geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

spawn.c
Go to the documentation of this file.
1/*
2 * spawn.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2013 The Geany contributors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/* An ongoing effort to improve the tool spawning situation under Windows.
22 * In particular:
23 * - There is no g_shell_parse_argv() for windows. It's not hard to write one,
24 * but the command line recreated by mscvrt may be wrong.
25 * - GLib converts the argument vector to UNICODE. For non-UTF8 arguments,
26 * the result is often "Invalid string in argument vector at %d: %s: Invalid
27 * byte sequence in conversion input" (YMMV). Our tools (make, grep, gcc, ...)
28 * are "ANSI", so converting to UNICODE and then back only causes problems.
29 * - For various reasons, GLib uses an intermediate program to start children
30 * (see gspawn-win32.c), the result being that the grandchildren output (such
31 * as make -> gcc) is not captured.
32 * - With non-blocking pipes, the g_io_add_watch() callbacks are never invoked,
33 * while with blocking pipes, g_io_channel_read_line() blocks.
34 * - Some other problems are explained in separate comments below.
35 *
36 * Even under Unix, using g_io_channel_read_line() is not a good idea, since it may
37 * buffer lines of unlimited length.
38 *
39 * This module does not depend on Geany when compiled for testing (-DSPAWN_TEST).
40 */
41
42/** @file spawn.h
43 * Portable and convenient process spawning and communication.
44 */
45
46#ifdef HAVE_CONFIG_H
47# include "config.h"
48#endif
49
50#include <errno.h>
51#include <string.h>
52
53#include "spawn.h"
54
55#ifdef G_OS_WIN32
56# include <ctype.h> /* isspace() */
57# include <fcntl.h> /* _O_RDONLY, _O_WRONLY */
58# include <io.h> /* _open_osfhandle, _close */
59# include <windows.h>
60#else /* G_OS_WIN32 */
61# include <signal.h>
62#endif /* G_OS_WIN32 */
63
64#ifdef SPAWN_TEST
65# define _
66# define GEANY_API_SYMBOL
67# define geany_debug g_debug
68#else
69# include "support.h"
70# include "geany.h"
71#endif
72
73#if ! GLIB_CHECK_VERSION(2, 31, 20) && ! defined(G_SPAWN_ERROR_TOO_BIG)
74# define G_SPAWN_ERROR_TOO_BIG G_SPAWN_ERROR_2BIG
75#endif
76
77#ifdef G_OS_WIN32
78/* Each 4KB under Windows seem to come in 2 portions, so 2K + 2K is more
79 balanced than 4095 + 1. May be different on the latest Windows/glib? */
80# define DEFAULT_IO_LENGTH 2048
81#else
82# define DEFAULT_IO_LENGTH 4096
83
84/* helper function that cuts glib citing of the original text on bad quoting: it may be long,
85 and only the caller knows whether it's UTF-8. Thought we lose the ' or " failed info. */
86static gboolean spawn_parse_argv(const gchar *command_line, gint *argcp, gchar ***argvp,
87 GError **error)
88{
89 GError *gerror = NULL;
90
91 if (g_shell_parse_argv(command_line, argcp, argvp, &gerror))
92 return TRUE;
93
94 g_set_error_literal(error, gerror->domain, gerror->code,
95 gerror->code == G_SHELL_ERROR_BAD_QUOTING ?
96 _("Text ended before matching quote was found") : gerror->message);
97 g_error_free(gerror);
98 return FALSE;
99}
100#endif
101
102#define SPAWN_IO_FAILURE (G_IO_ERR | G_IO_HUP | G_IO_NVAL) /* always used together */
103
104
105/*
106 * Checks whether a command line is syntactically valid and extracts the program name from it.
107 *
108 * See @c spawn_check_command() for details.
109 *
110 * @param command_line the command line to check and get the program name from.
111 * @param error return location for error.
112 *
113 * @return allocated string with the program name on success, @c NULL on error.
114 */
115static gchar *spawn_get_program_name(const gchar *command_line, GError **error)
116{
117 gchar *program;
118
119#ifdef G_OS_WIN32
120 gboolean open_quote = FALSE;
121 const gchar *s, *arguments;
122
123 g_return_val_if_fail(command_line != NULL, FALSE);
124
125 while (*command_line && strchr(" \t\r\n", *command_line))
126 command_line++;
127
128 if (!*command_line)
129 {
130 g_set_error_literal(error, G_SHELL_ERROR, G_SHELL_ERROR_EMPTY_STRING,
131 /* TL note: from glib */
132 _("Text was empty (or contained only whitespace)"));
133 return FALSE;
134 }
135
136 /* To prevent Windows from doing something weird, we want to be 100% sure that the
137 character after the program name is a delimiter, so we allow space and tab only. */
138
139 if (*command_line == '"')
140 {
141 command_line++;
142 /* Windows allows "foo.exe, but we may have extra arguments */
143 if ((s = strchr(command_line, '"')) == NULL)
144 {
145 g_set_error_literal(error, G_SHELL_ERROR, G_SHELL_ERROR_BAD_QUOTING,
146 _("Text ended before matching quote was found"));
147 return FALSE;
148 }
149
150 if (!strchr(" \t", s[1])) /* strchr() catches s[1] == '\0' */
151 {
152 g_set_error_literal(error, G_SHELL_ERROR, G_SHELL_ERROR_BAD_QUOTING,
153 _("A quoted Windows program name must be entirely inside the quotes"));
154 return FALSE;
155 }
156 }
157 else
158 {
159 const gchar *quote = strchr(command_line, '"');
160
161 /* strchr() catches *s == '\0', and the for body is empty */
162 for (s = command_line; !strchr(" \t", *s); s++);
163
164 if (quote && quote < s)
165 {
166 g_set_error_literal(error, G_SHELL_ERROR, G_SHELL_ERROR_BAD_QUOTING,
167 _("A quoted Windows program name must be entirely inside the quotes"));
168 return FALSE;
169 }
170 }
171
172 program = g_strndup(command_line, s - command_line);
173 arguments = s + (*s == '"');
174
175 for (s = arguments; *s; s++)
176 {
177 if (*s == '"')
178 {
179 const char *slash;
180
181 for (slash = s; slash > arguments && slash[-1] == '\\'; slash--);
182 if ((s - slash) % 2 == 0)
183 open_quote ^= TRUE;
184 }
185 }
186
187 if (open_quote)
188 {
189 g_set_error_literal(error, G_SHELL_ERROR, G_SHELL_ERROR_BAD_QUOTING,
190 _("Text ended before matching quote was found"));
191 g_free(program);
192 return FALSE;
193 }
194#else /* G_OS_WIN32 */
195 int argc;
196 char **argv;
197
198 if (!spawn_parse_argv(command_line, &argc, &argv, error))
199 return FALSE;
200
201 /* empty string results in parse error, so argv[0] is not NULL */
202 program = g_strdup(argv[0]);
203 g_strfreev(argv);
204#endif /* G_OS_WIN32 */
205
206 return program;
207}
208
209
210/**
211 * Checks whether a command line is valid.
212 *
213 * Checks if @a command_line is syntactically valid.
214 *
215 * All OS:
216 * - any leading spaces, tabs and new lines are skipped
217 * - an empty command is invalid
218 *
219 * Unix:
220 * - the standard shell quoting and escaping rules are used, see @c g_shell_parse_argv()
221 * - as a consequence, an unqouted # at the start of an argument comments to the end of line
222 *
223 * Windows:
224 * - leading carriage returns are skipped too
225 * - a quoted program name must be entirely inside the quotes. No "C:\Foo\Bar".pdf or
226 * "C:\Foo\Bar".bat, which would be executed by Windows as `C:\Foo\Bar.exe`
227 * - an unquoted program name may not contain spaces. `Foo Bar Qux` will not be considered
228 * `"Foo Bar.exe" Qux` or `"Foo Bar Qux.exe"`, depending on what executables exist, as
229 * Windows normally does.
230 * - the program name must be separated from the arguments by at least one space or tab
231 * - the standard Windows quoting and escaping rules are used: double quote is escaped with
232 * backslash, and any literal backslashes before a double quote must be duplicated.
233 *
234 * If @a execute is TRUE, also checks, using @c g_find_program_in_path(), if the program
235 * specified in @a command_line exists and is executable.
236 *
237 * @param command_line the command line to check.
238 * @param execute whether to check if the command line is really executable.
239 * @param error return location for error.
240 *
241 * @return @c TRUE on success, @c FALSE on error.
242 *
243 * @since 1.25
244 **/
245GEANY_API_SYMBOL
246gboolean spawn_check_command(const gchar *command_line, gboolean execute, GError **error)
247{
248 gchar *program = spawn_get_program_name(command_line, error);
249
250 if (!program)
251 return FALSE;
252
253 if (execute)
254 {
255 gchar *executable = g_find_program_in_path(program);
256
257 if (!executable)
258 {
259 g_set_error_literal(error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
260 _("Program not found"));
261 g_free(program);
262 return FALSE;
263 }
264
265 g_free(executable);
266 }
267
268 g_free(program);
269 return TRUE;
270}
271
272
273/**
274 * Kills a process.
275 *
276 * @param pid id of the process to kill.
277 * @param error return location for error.
278 *
279 * On Unix, sends a SIGTERM to the process.
280 *
281 * On Windows, terminates the process with exit code 255 (used sometimes as "generic"
282 * error code, or for programs terminated with Ctrl+C / Ctrl+Break).
283 *
284 * @return @c TRUE on success, @c FALSE on error.
285 *
286 * @since 1.25
287 **/
288GEANY_API_SYMBOL
289gboolean spawn_kill_process(GPid pid, GError **error)
290{
291#ifdef G_OS_WIN32
292 if (!TerminateProcess(pid, 255))
293 {
294 gchar *message = g_win32_error_message(GetLastError());
295
296 g_set_error_literal(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, message);
297 g_free(message);
298 return FALSE;
299 }
300#else
301 if (kill(pid, SIGTERM))
302 {
303 g_set_error_literal(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, g_strerror(errno));
304 return FALSE;
305 }
306#endif
307 return TRUE;
308}
309
310
311#ifdef G_OS_WIN32
312static gchar *spawn_create_process_with_pipes(wchar_t *w_command_line, const wchar_t *w_working_directory,
313 void *w_environment, HANDLE *hprocess, int *stdin_fd, int *stdout_fd, int *stderr_fd)
314{
315 enum { WRITE_STDIN, READ_STDOUT, READ_STDERR, READ_STDIN, WRITE_STDOUT, WRITE_STDERR };
316 STARTUPINFOW startup;
317 PROCESS_INFORMATION process;
318 HANDLE hpipe[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
319 int *fd[3] = { stdin_fd, stdout_fd, stderr_fd };
320 const char *failed; /* failed WIN32/CRTL function, if any */
321 gchar *message = NULL; /* glib WIN32/CTRL error message */
322 gchar *failure = NULL; /* full error text */
323 gboolean pipe_io;
324 int i;
325
326 ZeroMemory(&startup, sizeof startup);
327 startup.cb = sizeof startup;
328 pipe_io = stdin_fd || stdout_fd || stderr_fd;
329
330 if (pipe_io)
331 {
332 startup.dwFlags |= STARTF_USESTDHANDLES;
333
334 /* not all programs accept mixed NULL and non-NULL hStd*, so we create all */
335 for (i = 0; i < 3; i++)
336 {
337 static int pindex[3][2] = { { READ_STDIN, WRITE_STDIN },
338 { READ_STDOUT, WRITE_STDOUT }, { READ_STDERR, WRITE_STDERR } };
339
340 if (!CreatePipe(&hpipe[pindex[i][0]], &hpipe[pindex[i][1]], NULL, 0))
341 {
342 hpipe[pindex[i][0]] = hpipe[pindex[i][1]] = NULL;
343 failed = "create pipe";
344 goto leave;
345 }
346
347 if (fd[i])
348 {
349 static int mode[3] = { _O_WRONLY, _O_RDONLY, _O_RDONLY };
350
351 if ((*fd[i] = _open_osfhandle((intptr_t) hpipe[i], mode[i])) == -1)
352 {
353 failed = "convert pipe handle to file descriptor";
354 message = g_strdup(g_strerror(errno));
355 goto leave;
356 }
357 }
358 else if (!CloseHandle(hpipe[i]))
359 {
360 failed = "close pipe";
361 goto leave;
362 }
363
364 if (!SetHandleInformation(hpipe[i + 3], HANDLE_FLAG_INHERIT,
365 HANDLE_FLAG_INHERIT))
366 {
367 failed = "set pipe handle to inheritable";
368 goto leave;
369 }
370 }
371 }
372
373 startup.hStdInput = hpipe[READ_STDIN];
374 startup.hStdOutput = hpipe[WRITE_STDOUT];
375 startup.hStdError = hpipe[WRITE_STDERR];
376
377 if (!CreateProcessW(NULL, w_command_line, NULL, NULL, TRUE,
378 CREATE_UNICODE_ENVIRONMENT | (pipe_io ? CREATE_NO_WINDOW : 0),
379 w_environment, w_working_directory, &startup, &process))
380 {
381 failed = ""; /* report the message only */
382 /* further errors will not be reported */
383 }
384 else
385 {
386 failed = NULL;
387 CloseHandle(process.hThread); /* we don't need this */
388
389 if (hprocess)
390 *hprocess = process.hProcess;
391 else
392 CloseHandle(process.hProcess);
393 }
394
395leave:
396 if (failed)
397 {
398 if (!message)
399 {
400 size_t len;
401
402 message = g_win32_error_message(GetLastError());
403 len = strlen(message);
404
405 /* unlike g_strerror(), the g_win32_error messages may include a final '.' */
406 if (len > 0 && message[len - 1] == '.')
407 message[len - 1] = '\0';
408 }
409
410 if (*failed == '\0')
411 failure = message;
412 else
413 {
414 failure = g_strdup_printf("Failed to %s (%s)", failed, message);
415 g_free(message);
416 }
417 }
418
419 if (pipe_io)
420 {
421 for (i = 0; i < 3; i++)
422 {
423 if (failed)
424 {
425 if (fd[i] && *fd[i] != -1)
426 _close(*fd[i]);
427 else if (hpipe[i])
428 CloseHandle(hpipe[i]);
429 }
430
431 if (hpipe[i + 3])
432 CloseHandle(hpipe[i + 3]);
433 }
434 }
435
436 return failure;
437}
438
439
440static void spawn_append_argument(GString *command, const char *text)
441{
442 const char *s;
443
444 if (command->len)
445 g_string_append_c(command, ' ');
446
447 for (s = text; *s; s++)
448 {
449 /* g_ascii_isspace() fails for '\v', and locale spaces (if any) will do no harm */
450 if (*s == '"' || isspace(*s))
451 break;
452 }
453
454 if (*text && !*s)
455 g_string_append(command, text);
456 else
457 {
458 g_string_append_c(command, '"');
459
460 for (s = text; *s; s++)
461 {
462 const char *slash;
463
464 for (slash = s; *slash == '\\'; slash++);
465
466 if (slash > s)
467 {
468 g_string_append_len(command, s, slash - s);
469
470 if (!*slash || *slash == '"')
471 {
472 g_string_append_len(command, s, slash - s);
473
474 if (!*slash)
475 break;
476 }
477
478 s = slash;
479 }
480
481 if (*s == '"')
482 g_string_append_c(command, '\\');
483
484 g_string_append_c(command, *s);
485 }
486
487 g_string_append_c(command, '"');
488 }
489}
490#endif /* G_OS_WIN32 */
491
492
493/*
494 * Executes a child program asynchronously and setups pipes.
495 *
496 * This is the low-level spawning function. Please use @c spawn_with_callbacks() unless
497 * you need to setup specific event source(s).
498 *
499 * A command line or an argument vector must be passed. If both are present, the argument
500 * vector is appended to the command line. An empty command line is not allowed.
501 *
502 * Under Windows, if the child is a console application, and at least one file descriptor is
503 * specified, the new child console (if any) will be hidden.
504 *
505 * If a @a child_pid is passed, it's your responsibility to invoke @c g_spawn_close_pid().
506 *
507 * @param working_directory child's current working directory, or @c NULL.
508 * @param command_line child program and arguments, or @c NULL.
509 * @param argv child's argument vector, or @c NULL.
510 * @param envp child's environment, or @c NULL.
511 * @param child_pid return location for child process ID, or @c NULL.
512 * @param stdin_fd return location for file descriptor to write to child's stdin, or @c NULL.
513 * @param stdout_fd return location for file descriptor to read child's stdout, or @c NULL.
514 * @param stderr_fd return location for file descriptor to read child's stderr, or @c NULL.
515 * @param error return location for error.
516 *
517 * @return @c TRUE on success, @c FALSE on error.
518 */
519static gboolean spawn_async_with_pipes(const gchar *working_directory, const gchar *command_line,
520 gchar **argv, gchar **envp, GPid *child_pid, gint *stdin_fd, gint *stdout_fd,
521 gint *stderr_fd, GError **error)
522{
523 g_return_val_if_fail(command_line != NULL || argv != NULL, FALSE);
524
525#ifdef G_OS_WIN32
526 GString *command;
527 GArray *w_environment;
528 wchar_t *w_working_directory = NULL;
529 wchar_t *w_command = NULL;
530 gboolean success = TRUE;
531
532 if (command_line)
533 {
534 gchar *program = spawn_get_program_name(command_line, error);
535 const gchar *arguments;
536
537 if (!program)
538 return FALSE;
539
540 command = g_string_new(NULL);
541 arguments = strstr(command_line, program) + strlen(program);
542
543 if (*arguments == '"')
544 {
545 g_string_append(command, program);
546 arguments++;
547 }
548 else
549 {
550 /* quote the first token, to avoid Windows attemps to run two or more
551 unquoted tokens as a program until an existing file name is found */
552 g_string_printf(command, "\"%s\"", program);
553 }
554
555 g_string_append(command, arguments);
556 g_free(program);
557 }
558 else
559 command = g_string_new(NULL);
560
561 w_environment = g_array_new(TRUE, FALSE, sizeof(wchar_t));
562
563 while (argv && *argv)
564 spawn_append_argument(command, *argv++);
565
566#if defined(SPAWN_TEST) || defined(GEANY_DEBUG)
567 g_message("full spawn command line: %s", command->str);
568#endif
569
570 while (envp && *envp && success)
571 {
572 glong w_entry_len;
573 wchar_t *w_entry;
574 gchar *tmp = NULL;
575
576 // FIXME: remove this and rely on UTF-8 input
577 if (! g_utf8_validate(*envp, -1, NULL))
578 {
579 tmp = g_locale_to_utf8(*envp, -1, NULL, NULL, NULL);
580 if (tmp)
581 *envp = tmp;
582 }
583 /* TODO: better error message */
584 w_entry = g_utf8_to_utf16(*envp, -1, NULL, &w_entry_len, error);
585
586 if (! w_entry)
587 success = FALSE;
588 else
589 {
590 /* copy the entry, including NUL terminator */
591 g_array_append_vals(w_environment, w_entry, w_entry_len + 1);
592 g_free(w_entry);
593 }
594
595 g_free(tmp);
596 envp++;
597 }
598
599 /* convert working directory into locale encoding */
600 if (success && working_directory)
601 {
602 GError *gerror = NULL;
603 const gchar *utf8_working_directory;
604 gchar *tmp = NULL;
605
606 // FIXME: remove this and rely on UTF-8 input
607 if (! g_utf8_validate(working_directory, -1, NULL))
608 {
609 tmp = g_locale_to_utf8(working_directory, -1, NULL, NULL, NULL);
610 if (tmp)
611 utf8_working_directory = tmp;
612 }
613 else
614 utf8_working_directory = working_directory;
615
616 w_working_directory = g_utf8_to_utf16(utf8_working_directory, -1, NULL, NULL, &gerror);
617 if (! w_working_directory)
618 {
619 /* TODO use the code below post-1.28 as it introduces a new string
620 g_set_error(error, gerror->domain, gerror->code,
621 _("Failed to convert working directory into locale encoding: %s"), gerror->message);
622 */
623 g_propagate_error(error, gerror);
624 success = FALSE;
625 }
626 g_free(tmp);
627 }
628 /* convert command into locale encoding */
629 if (success)
630 {
631 GError *gerror = NULL;
632 const gchar *utf8_cmd;
633 gchar *tmp = NULL;
634
635 // FIXME: remove this and rely on UTF-8 input
636 if (! g_utf8_validate(command->str, -1, NULL))
637 {
638 tmp = g_locale_to_utf8(command->str, -1, NULL, NULL, NULL);
639 if (tmp)
640 utf8_cmd = tmp;
641 }
642 else
643 utf8_cmd = command->str;
644
645 w_command = g_utf8_to_utf16(utf8_cmd, -1, NULL, NULL, &gerror);
646 if (! w_command)
647 {
648 /* TODO use the code below post-1.28 as it introduces a new string
649 g_set_error(error, gerror->domain, gerror->code,
650 _("Failed to convert command into locale encoding: %s"), gerror->message);
651 */
652 g_propagate_error(error, gerror);
653 success = FALSE;
654 }
655 }
656
657 if (success)
658 {
659 gchar *failure;
660
661 failure = spawn_create_process_with_pipes(w_command, w_working_directory,
662 envp ? w_environment->data : NULL, child_pid, stdin_fd, stdout_fd, stderr_fd);
663
664 if (failure)
665 {
666 g_set_error_literal(error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, failure);
667 g_free(failure);
668 }
669
670 success = failure == NULL;
671 }
672
673 g_string_free(command, TRUE);
674 g_array_free(w_environment, TRUE);
675 g_free(w_working_directory);
676 g_free(w_command);
677
678 return success;
679#else /* G_OS_WIN32 */
680 int cl_argc;
681 char **full_argv;
682 gboolean spawned;
683 GError *gerror = NULL;
684
685 if (command_line)
686 {
687 int argc = 0;
688 char **cl_argv;
689
690 if (!spawn_parse_argv(command_line, &cl_argc, &cl_argv, error))
691 return FALSE;
692
693 if (argv)
694 for (argc = 0; argv[argc]; argc++);
695
696 full_argv = g_renew(gchar *, cl_argv, cl_argc + argc + 1);
697 memcpy(full_argv + cl_argc, argv, argc * sizeof(gchar *));
698 full_argv[cl_argc + argc] = NULL;
699 }
700 else
701 full_argv = argv;
702
703 spawned = g_spawn_async_with_pipes(working_directory, full_argv, envp,
704 G_SPAWN_SEARCH_PATH | (child_pid ? G_SPAWN_DO_NOT_REAP_CHILD : 0), NULL, NULL,
705 child_pid, stdin_fd, stdout_fd, stderr_fd, &gerror);
706
707 if (!spawned)
708 {
709 gint en = 0;
710 const gchar *message = gerror->message;
711
712 /* try to cut glib citing of the program name or working directory: they may be long,
713 and only the caller knows whether they're UTF-8. We lose the exact chdir error. */
714 switch (gerror->code)
715 {
716 #ifdef EACCES
717 case G_SPAWN_ERROR_ACCES : en = EACCES; break;
718 #endif
719 #ifdef EPERM
720 case G_SPAWN_ERROR_PERM : en = EPERM; break;
721 #endif
722 #ifdef E2BIG
723 case G_SPAWN_ERROR_TOO_BIG : en = E2BIG; break;
724 #endif
725 #ifdef ENOEXEC
726 case G_SPAWN_ERROR_NOEXEC : en = ENOEXEC; break;
727 #endif
728 #ifdef ENAMETOOLONG
729 case G_SPAWN_ERROR_NAMETOOLONG : en = ENAMETOOLONG; break;
730 #endif
731 #ifdef ENOENT
732 case G_SPAWN_ERROR_NOENT : en = ENOENT; break;
733 #endif
734 #ifdef ENOMEM
735 case G_SPAWN_ERROR_NOMEM : en = ENOMEM; break;
736 #endif
737 #ifdef ENOTDIR
738 case G_SPAWN_ERROR_NOTDIR : en = ENOTDIR; break;
739 #endif
740 #ifdef ELOOP
741 case G_SPAWN_ERROR_LOOP : en = ELOOP; break;
742 #endif
743 #ifdef ETXTBUSY
744 case G_SPAWN_ERROR_TXTBUSY : en = ETXTBUSY; break;
745 #endif
746 #ifdef EIO
747 case G_SPAWN_ERROR_IO : en = EIO; break;
748 #endif
749 #ifdef ENFILE
750 case G_SPAWN_ERROR_NFILE : en = ENFILE; break;
751 #endif
752 #ifdef EMFILE
753 case G_SPAWN_ERROR_MFILE : en = EMFILE; break;
754 #endif
755 #ifdef EINVAL
756 case G_SPAWN_ERROR_INVAL : en = EINVAL; break;
757 #endif
758 #ifdef EISDIR
759 case G_SPAWN_ERROR_ISDIR : en = EISDIR; break;
760 #endif
761 #ifdef ELIBBAD
762 case G_SPAWN_ERROR_LIBBAD : en = ELIBBAD; break;
763 #endif
764 case G_SPAWN_ERROR_CHDIR :
765 {
766 message = _("Failed to change to the working directory");
767 break;
768 }
769 case G_SPAWN_ERROR_FAILED :
770 {
771 message = _("Unknown error executing child process");
772 break;
773 }
774 }
775
776 if (en)
777 message = g_strerror(en);
778
779 g_set_error_literal(error, gerror->domain, gerror->code, message);
780 g_error_free(gerror);
781 }
782
783 if (full_argv != argv)
784 {
785 full_argv[cl_argc] = NULL;
786 g_strfreev(full_argv);
787 }
788
789 return spawned;
790#endif /* G_OS_WIN32 */
791}
792
793
794/**
795 * Executes a child asynchronously.
796 *
797 * A command line or an argument vector must be passed. If both are present, the argument
798 * vector is appended to the command line. An empty command line is not allowed.
799 *
800 * If a @a child_pid is passed, it's your responsibility to invoke @c g_spawn_close_pid().
801 *
802 * @param working_directory @nullable child's current working directory, or @c NULL.
803 * @param command_line @nullable child program and arguments, or @c NULL.
804 * @param argv @nullable child's argument vector, or @c NULL.
805 * @param envp @nullable child's environment, or @c NULL.
806 * @param child_pid @out @optional return location for child process ID, or @c NULL.
807 * @param error return location for error.
808 *
809 * @return @c TRUE on success, @c FALSE on error.
810 *
811 * @since 1.25
812 **/
813GEANY_API_SYMBOL
814gboolean spawn_async(const gchar *working_directory, const gchar *command_line, gchar **argv,
815 gchar **envp, GPid *child_pid, GError **error)
816{
817 return spawn_async_with_pipes(working_directory, command_line, argv, envp, child_pid,
818 NULL, NULL, NULL, error);
819}
820
821
822/*
823 * Spawn with callbacks - general event sequence:
824 *
825 * - Launch the child.
826 * - Setup any I/O callbacks and a child watch callback.
827 * - On sync execution, run a main loop.
828 * - Wait for the child to terminate.
829 * - Check for active I/O sources. If any, add a timeout source to watch them, they should
830 * become inactive real soon now that the child is dead. Otherwise, finalize immediately.
831 * - In the timeout source: check for active I/O sources and finalize if none.
832 */
833
834typedef struct _SpawnChannelData
835{
836 GIOChannel *channel; /* NULL if not created / already destroyed */
837 union
838 {
839 GIOFunc write;
841 } cb;
842 gpointer cb_data;
843 /* stdout/stderr only */
844 GString *buffer; /* NULL if recursive */
845 GString *line_buffer; /* NULL if char buffered */
847 /* stdout/stderr: fix continuous empty G_IO_IN-s for recursive channels */
850
851#define SPAWN_CHANNEL_GIO_WATCH(sc) ((sc)->empty_gio_ins < 200)
852
853
855{
856 g_io_channel_shutdown(sc->channel, FALSE, NULL);
857
858 if (sc->buffer)
859 g_string_free(sc->buffer, TRUE);
860
861 if (sc->line_buffer)
862 g_string_free(sc->line_buffer, TRUE);
863}
864
865
866static void spawn_timeout_destroy_cb(gpointer data)
867{
868 SpawnChannelData *sc = (SpawnChannelData *) data;
869
871 g_io_channel_unref(sc->channel);
872 sc->channel = NULL;
873}
874
875
876static void spawn_destroy_cb(gpointer data)
877{
878 SpawnChannelData *sc = (SpawnChannelData *) data;
879
881 {
883 sc->channel = NULL;
884 }
885}
886
887
888static gboolean spawn_write_cb(GIOChannel *channel, GIOCondition condition, gpointer data)
889{
890 SpawnChannelData *sc = (SpawnChannelData *) data;
891
892 if (!sc->cb.write(channel, condition, sc->cb_data))
893 return FALSE;
894
895 return !(condition & SPAWN_IO_FAILURE);
896}
897
898
899static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpointer data);
900
901static gboolean spawn_timeout_read_cb(gpointer data)
902{
903 SpawnChannelData *sc = (SpawnChannelData *) data;
904
905 /* The solution for continuous empty G_IO_IN-s is to generate them outselves. :) */
906 return spawn_read_cb(sc->channel, G_IO_IN, data);
907}
908
909static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpointer data)
910{
911 SpawnChannelData *sc = (SpawnChannelData *) data;
912 GString *line_buffer = sc->line_buffer;
913 GString *buffer = sc->buffer ? sc->buffer : g_string_sized_new(sc->max_length);
914 GIOCondition input_cond = condition & (G_IO_IN | G_IO_PRI);
915 GIOCondition failure_cond = condition & SPAWN_IO_FAILURE;
916 GIOStatus status = G_IO_STATUS_NORMAL;
917 /*
918 * - Normally, read only once. With IO watches, our data processing must be immediate,
919 * which may give the child time to emit more data, and a read loop may combine it into
920 * large stdout and stderr portions. Under Windows, looping blocks.
921 * - On failure, read in a loop. It won't block now, there will be no more data, and the
922 * IO watch is not guaranteed to be called again (under Windows this is the last call).
923 * - When using timeout callbacks, read in a loop. Otherwise, the input processing will
924 * be limited to (1/0.050 * DEFAULT_IO_LENGTH) KB/sec, which is pretty low.
925 */
926 if (input_cond)
927 {
928 gsize chars_read;
929
930 if (line_buffer)
931 {
932 gsize n = line_buffer->len;
933
934 while ((status = g_io_channel_read_chars(channel, line_buffer->str + n,
935 DEFAULT_IO_LENGTH, &chars_read, NULL)) == G_IO_STATUS_NORMAL)
936 {
937 g_string_set_size(line_buffer, n + chars_read);
938
939 while (n < line_buffer->len)
940 {
941 gsize line_len = 0;
942
943 if (n == sc->max_length)
944 line_len = n;
945 else if (strchr("\n", line_buffer->str[n])) /* '\n' or '\0' */
946 line_len = n + 1;
947 else if (n < line_buffer->len - 1 && line_buffer->str[n] == '\r')
948 line_len = n + 1 + (line_buffer->str[n + 1] == '\n');
949
950 if (!line_len)
951 n++;
952 else
953 {
954 g_string_append_len(buffer, line_buffer->str, line_len);
955 g_string_erase(line_buffer, 0, line_len);
956 /* input only, failures are reported separately below */
957 sc->cb.read(buffer, input_cond, sc->cb_data);
958 g_string_truncate(buffer, 0);
959 n = 0;
960 }
961 }
962
963 if (SPAWN_CHANNEL_GIO_WATCH(sc) && !failure_cond)
964 break;
965 }
966 }
967 else
968 {
969 while ((status = g_io_channel_read_chars(channel, buffer->str, sc->max_length,
970 &chars_read, NULL)) == G_IO_STATUS_NORMAL)
971 {
972 g_string_set_size(buffer, chars_read);
973 /* input only, failures are reported separately below */
974 sc->cb.read(buffer, input_cond, sc->cb_data);
975
976 if (SPAWN_CHANNEL_GIO_WATCH(sc) && !failure_cond)
977 break;
978 }
979 }
980
981 /* Under OSX, after child death, the read watches receive input conditions instead
982 of error conditions, so we convert the termination statuses into conditions.
983 Should not hurt the other OS. */
984 if (status == G_IO_STATUS_ERROR)
985 failure_cond |= G_IO_ERR;
986 else if (status == G_IO_STATUS_EOF)
987 failure_cond |= G_IO_HUP;
988 }
989
990 if (failure_cond) /* we must signal the callback */
991 {
992 if (line_buffer && line_buffer->len) /* flush the line buffer */
993 {
994 g_string_append_len(buffer, line_buffer->str, line_buffer->len);
995 /* all data may be from a previous call */
996 if (!input_cond)
997 input_cond = G_IO_IN;
998 }
999 else
1000 {
1001 input_cond = 0;
1002 g_string_truncate(buffer, 0);
1003 }
1004
1005 sc->cb.read(buffer, input_cond | failure_cond, sc->cb_data);
1006 }
1007 /* Check for continuous activations with G_IO_IN | G_IO_PRI, without any
1008 data to read and without errors. If detected, switch to timeout source. */
1009 else if (SPAWN_CHANNEL_GIO_WATCH(sc) && status == G_IO_STATUS_AGAIN)
1010 {
1011 sc->empty_gio_ins++;
1012
1013 if (!SPAWN_CHANNEL_GIO_WATCH(sc))
1014 {
1015 GSource *old_source = g_main_current_source();
1016 GSource *new_source = g_timeout_source_new(50);
1017
1018 geany_debug("Switching spawn source %s ((GSource*)%p on (GIOChannel*)%p) to a timeout source",
1019 g_source_get_name(old_source), (gpointer) old_source, (gpointer)sc->channel);
1020
1021 g_io_channel_ref(sc->channel);
1022 g_source_set_can_recurse(new_source, g_source_get_can_recurse(old_source));
1023 g_source_set_callback(new_source, spawn_timeout_read_cb, data, spawn_timeout_destroy_cb);
1024 g_source_attach(new_source, g_source_get_context(old_source));
1025 g_source_unref(new_source);
1026 failure_cond |= G_IO_ERR;
1027 }
1028 }
1029
1030 if (buffer != sc->buffer)
1031 g_string_free(buffer, TRUE);
1032
1033 return !failure_cond;
1034}
1035
1036
1037typedef struct _SpawnWatcherData
1038{
1039 SpawnChannelData sc[3]; /* stdin, stdout, stderr */
1040 GChildWatchFunc exit_cb;
1041 gpointer exit_data;
1042 GPid pid;
1044 GMainContext *main_context; /* NULL if async execution */
1045 GMainLoop *main_loop; /* NULL if async execution */
1047
1048
1050{
1051 if (sw->exit_cb)
1052 sw->exit_cb(sw->pid, sw->exit_status, sw->exit_data);
1053
1054 if (sw->main_loop)
1055 {
1056 g_main_loop_quit(sw->main_loop);
1057 g_main_loop_unref(sw->main_loop);
1058 }
1059
1060 g_spawn_close_pid(sw->pid);
1061 g_slice_free(SpawnWatcherData, sw);
1062}
1063
1064
1065static gboolean spawn_timeout_watch_cb(gpointer data)
1066{
1067 SpawnWatcherData *sw = (SpawnWatcherData *) data;
1068 int i;
1069
1070 for (i = 0; i < 3; i++)
1071 if (sw->sc[i].channel)
1072 return TRUE;
1073
1074 spawn_finalize(sw);
1075 return FALSE;
1076}
1077
1078
1079static void spawn_watch_cb(GPid pid, gint status, gpointer data)
1080{
1081 SpawnWatcherData *sw = (SpawnWatcherData *) data;
1082 int i;
1083
1084 sw->pid = pid;
1085 sw->exit_status = status;
1086
1087 for (i = 0; i < 3; i++)
1088 {
1089 if (sw->sc[i].channel)
1090 {
1091 GSource *source = g_timeout_source_new(50);
1092
1093 g_source_set_callback(source, spawn_timeout_watch_cb, data, NULL);
1094 g_source_attach(source, sw->main_context);
1095 g_source_unref(source);
1096 return;
1097 }
1098 }
1099
1100 spawn_finalize(sw);
1101}
1102
1103
1104/** @girskip
1105 * Executes a child program and setups callbacks.
1106 *
1107 * A command line or an argument vector must be passed. If both are present, the argument
1108 * vector is appended to the command line. An empty command line is not allowed.
1109 *
1110 * The synchronous execution may not be combined with recursive callbacks.
1111 *
1112 * In line buffered mode, the child input is broken on `\n`, `\r\n`, `\r`, `\0` and max length.
1113 *
1114 * All I/O callbacks are guaranteed to be invoked at least once with @c G_IO_ERR, @c G_IO_HUP
1115 * or @c G_IO_NVAL set (except for a @a stdin_cb which returns @c FALSE before that). For the
1116 * non-recursive callbacks, this is guaranteed to be the last call, and may be used to free any
1117 * resources associated with the callback.
1118 *
1119 * The @a stdin_cb may write to @c channel only once per invocation, only if @c G_IO_OUT is
1120 * set, and only a non-zero number of characters.
1121 *
1122 * @c stdout_cb and @c stderr_cb may modify the received strings in any way, but must not
1123 * free them.
1124 *
1125 * The default max lengths are 24K for line buffered stdout, 8K for line buffered stderr,
1126 * 4K for unbuffered input under Unix, and 2K for unbuffered input under Windows.
1127 *
1128 * Due to a bug in some glib versions, the sources for recursive stdout/stderr callbacks may
1129 * be converted from child watch to timeout(50ms). No callback changes are required.
1130 *
1131 * @c exit_cb is always invoked last, after all I/O callbacks.
1132 *
1133 * The @a child_pid will be closed automatically, after @a exit_cb is invoked.
1134 *
1135 * @param working_directory @nullable child's current working directory, or @c NULL.
1136 * @param command_line @nullable child program and arguments, or @c NULL.
1137 * @param argv @nullable child's argument vector, or @c NULL.
1138 * @param envp @nullable child's environment, or @c NULL.
1139 * @param spawn_flags flags from SpawnFlags.
1140 * @param stdin_cb @nullable callback to send data to childs's stdin, or @c NULL.
1141 * @param stdin_data data to pass to @a stdin_cb.
1142 * @param stdout_cb @nullable callback to receive child's stdout, or @c NULL.
1143 * @param stdout_data data to pass to @a stdout_cb.
1144 * @param stdout_max_length maximum data length to pass to stdout_cb, @c 0 = default.
1145 * @param stderr_cb @nullable callback to receive child's stderr, or @c NULL.
1146 * @param stderr_data data to pass to @a stderr_cb.
1147 * @param stderr_max_length maximum data length to pass to stderr_cb, @c 0 = default.
1148 * @param exit_cb @nullable callback to invoke when the child exits, or @c NULL.
1149 * @param exit_data data to pass to @a exit_cb.
1150 * @param child_pid @out @optional return location for child process ID, or @c NULL.
1151 * @param error return location for error.
1152 *
1153 * @return @c TRUE on success, @c FALSE on error.
1154 *
1155 * @since 1.25
1156 **/
1157GEANY_API_SYMBOL
1158gboolean spawn_with_callbacks(const gchar *working_directory, const gchar *command_line,
1159 gchar **argv, gchar **envp, SpawnFlags spawn_flags, GIOFunc stdin_cb, gpointer stdin_data,
1160 SpawnReadFunc stdout_cb, gpointer stdout_data, gsize stdout_max_length,
1161 SpawnReadFunc stderr_cb, gpointer stderr_data, gsize stderr_max_length,
1162 GChildWatchFunc exit_cb, gpointer exit_data, GPid *child_pid, GError **error)
1163{
1164 GPid pid;
1165 int pipe[3] = { -1, -1, -1 };
1166
1167 g_return_val_if_fail(!(spawn_flags & SPAWN_RECURSIVE) || !(spawn_flags & SPAWN_SYNC),
1168 FALSE);
1169
1170 if (spawn_async_with_pipes(working_directory, command_line, argv, envp, &pid,
1171 stdin_cb ? &pipe[0] : NULL, stdout_cb ? &pipe[1] : NULL,
1172 stderr_cb ? &pipe[2] : NULL, error))
1173 {
1174 SpawnWatcherData *sw = g_slice_new0(SpawnWatcherData);
1175 gpointer cb_data[3] = { stdin_data, stdout_data, stderr_data };
1176 GSource *source;
1177 int i;
1178
1179 sw->main_context = spawn_flags & SPAWN_SYNC ? g_main_context_new() : NULL;
1180
1181 if (child_pid)
1182 *child_pid = pid;
1183
1184 for (i = 0; i < 3; i++)
1185 {
1186 SpawnChannelData *sc = &sw->sc[i];
1187 GIOCondition condition;
1188 GSourceFunc callback;
1189
1190 if (pipe[i] == -1)
1191 continue;
1192
1193 #ifdef G_OS_WIN32
1194 sc->channel = g_io_channel_win32_new_fd(pipe[i]);
1195 #else
1196 sc->channel = g_io_channel_unix_new(pipe[i]);
1197 g_io_channel_set_flags(sc->channel, G_IO_FLAG_NONBLOCK, NULL);
1198 #endif
1199 g_io_channel_set_encoding(sc->channel, NULL, NULL);
1200 /* we have our own buffers, and GIO buffering blocks under Windows */
1201 g_io_channel_set_buffered(sc->channel, FALSE);
1202 sc->cb_data = cb_data[i];
1203
1204 if (i == 0)
1205 {
1206 sc->cb.write = stdin_cb;
1207 condition = G_IO_OUT | SPAWN_IO_FAILURE;
1208 callback = (GSourceFunc) spawn_write_cb;
1209 }
1210 else
1211 {
1212 gboolean line_buffered = !(spawn_flags &
1213 ((SPAWN_STDOUT_UNBUFFERED >> 1) << i));
1214
1215 condition = G_IO_IN | G_IO_PRI | SPAWN_IO_FAILURE;
1216 callback = (GSourceFunc) spawn_read_cb;
1217
1218 if (i == 1)
1219 {
1220 sc->cb.read = stdout_cb;
1221 sc->max_length = stdout_max_length ? stdout_max_length :
1222 line_buffered ? 24576 : DEFAULT_IO_LENGTH;
1223 }
1224 else
1225 {
1226 sc->cb.read = stderr_cb;
1227 sc->max_length = stderr_max_length ? stderr_max_length :
1228 line_buffered ? 8192 : DEFAULT_IO_LENGTH;
1229 }
1230
1231 if (line_buffered)
1232 {
1233 sc->line_buffer = g_string_sized_new(sc->max_length +
1235 }
1236
1237 sc->empty_gio_ins = 0;
1238 }
1239
1240 source = g_io_create_watch(sc->channel, condition);
1241 g_io_channel_unref(sc->channel);
1242
1243 if (spawn_flags & (SPAWN_STDIN_RECURSIVE << i))
1244 g_source_set_can_recurse(source, TRUE);
1245 else if (i) /* to avoid new string on each call */
1246 sc->buffer = g_string_sized_new(sc->max_length);
1247
1248 g_source_set_callback(source, callback, sc, spawn_destroy_cb);
1249 g_source_attach(source, sw->main_context);
1250 g_source_unref(source);
1251 }
1252
1253 sw->exit_cb = exit_cb;
1254 sw->exit_data = exit_data;
1255 source = g_child_watch_source_new(pid);
1256 g_source_set_callback(source, (GSourceFunc) spawn_watch_cb, sw, NULL);
1257 g_source_attach(source, sw->main_context);
1258 g_source_unref(source);
1259
1260 if (spawn_flags & SPAWN_SYNC)
1261 {
1262 sw->main_loop = g_main_loop_new(sw->main_context, FALSE);
1263 g_main_context_unref(sw->main_context);
1264 g_main_loop_run(sw->main_loop);
1265 }
1266
1267 return TRUE;
1268 }
1269
1270 return FALSE;
1271}
1272
1273
1274/**
1275 * Writes (a portion of) the data pointed by @a data->ptr to the @a channel.
1276 *
1277 * If @c G_IO_OUT in @a condition is set, and the @a data->size is > 0, attempts to write
1278 * @a data->ptr (or a portion of it, depending on the size) to the @a channel. On success,
1279 * increases ptr and decreases size with the number of characters written.
1280 *
1281 * This function may converted to @c GIOFunc and passed to @c spawn_with_callbacks() as
1282 * @c stdin_cb, together with a @c SpawnWriteData for @c stdin_data. As with any other
1283 * callback data, make sure that @c stdin_data exists while the child is being executed.
1284 * (For example, on asynchronous execution, you can allocate the data in the heap, and free
1285 * it in your @c spawn_with_callbacks() @c exit_cb callback.)
1286 *
1287 * @param channel the channel to write data to.
1288 * @param condition condition to check for @c G_IO_OUT.
1289 * @param data @c SpawnWriteData to write to @a channel.
1290 *
1291 * @return @c TRUE if the remaining size is > 0 and @a condition does not indicate any error,
1292 * @c FALSE otherwise.
1293 *
1294 * @since 1.25
1295 **/
1296GEANY_API_SYMBOL
1297gboolean spawn_write_data(GIOChannel *channel, GIOCondition condition, SpawnWriteData *data)
1298{
1299 if ((condition & G_IO_OUT) && data->size)
1300 {
1301 gsize chars_written = 0;
1302
1303 g_io_channel_write_chars(channel, data->ptr, data->size < DEFAULT_IO_LENGTH ?
1304 data->size : DEFAULT_IO_LENGTH, &chars_written, NULL);
1305
1306 /* "This can be nonzero even if the return value is not G_IO_STATUS_NORMAL." */
1307 if (chars_written)
1308 {
1309 data->ptr += chars_written;
1310 data->size -= chars_written;
1311 }
1312 }
1313
1314 return data->size > 0 && !(condition & SPAWN_IO_FAILURE);
1315}
1316
1317
1318static void spawn_append_gstring_cb(GString *string, GIOCondition condition, gpointer data)
1319{
1320 if (condition & (G_IO_IN | G_IO_PRI))
1321 g_string_append_len((GString *) data, string->str, string->len);
1322}
1323
1324
1325static void spawn_get_exit_status_cb(G_GNUC_UNUSED GPid pid, gint status, gpointer exit_status)
1326{
1327 *(gint *) exit_status = status;
1328}
1329
1330
1331/**
1332 * Executes a child synchronously.
1333 *
1334 * A command line or an argument vector must be passed. If both are present, the argument
1335 * vector is appended to the command line. An empty command line is not allowed.
1336 *
1337 * The @a stdin_data is sent to the child with @c spawn_write_data().
1338 *
1339 * All output from the child, including the nul characters, is stored in @a stdout_data and
1340 * @a stderr_data (if non-NULL). Any existing data in these strings will be erased.
1341 *
1342 * @param working_directory @nullable child's current working directory, or @c NULL.
1343 * @param command_line @nullable child program and arguments, or @c NULL.
1344 * @param argv @nullable child's argument vector, or @c NULL.
1345 * @param envp @nullable child's environment, or @c NULL.
1346 * @param stdin_data @nullable data to send to childs's stdin, or @c NULL.
1347 * @param stdout_data @nullable GString location to receive the child's stdout, or @c NULL.
1348 * @param stderr_data @nullable GString location to receive the child's stderr, or @c NULL.
1349 * @param exit_status @out @optional return location for the child exit code, or @c NULL.
1350 * @param error return location for error.
1351 *
1352 * @return @c TRUE on success, @c FALSE on error.
1353 *
1354 * @since 1.25
1355 **/
1356GEANY_API_SYMBOL
1357gboolean spawn_sync(const gchar *working_directory, const gchar *command_line, gchar **argv,
1358 gchar **envp, SpawnWriteData *stdin_data, GString *stdout_data, GString *stderr_data,
1359 gint *exit_status, GError **error)
1360{
1361 if (stdout_data)
1362 g_string_truncate(stdout_data, 0);
1363 if (stderr_data)
1364 g_string_truncate(stderr_data, 0);
1365
1366 return spawn_with_callbacks(working_directory, command_line, argv, envp, SPAWN_SYNC |
1367 SPAWN_UNBUFFERED, stdin_data ? (GIOFunc) spawn_write_data : NULL, stdin_data,
1368 stdout_data ? spawn_append_gstring_cb : NULL, stdout_data, 0,
1369 stderr_data ? spawn_append_gstring_cb : NULL, stderr_data, 0,
1370 exit_status ? spawn_get_exit_status_cb : NULL, exit_status, NULL, error);
1371}
1372
1373
1374/* tests, not part of the API */
1375#ifdef SPAWN_TEST
1376#include <stdio.h>
1377
1378
1379static gboolean read_line(const char *prompt, char *buffer, size_t size)
1380{
1381 fputs(prompt, stderr);
1382 *buffer = '\0';
1383
1384 if (fgets(buffer, size, stdin))
1385 {
1386 char *s = strchr(buffer, '\n');
1387
1388 if (s)
1389 *s = '\0';
1390 }
1391
1392 return *buffer;
1393}
1394
1395
1396static GString *read_string(const char *prompt)
1397{
1398 char buffer[0x1000]; /* larger portions for spawn < file */
1399 GString *string = g_string_sized_new(sizeof buffer);
1400
1401 while (read_line(prompt, buffer, sizeof buffer))
1402 {
1403 if (string->len)
1404 g_string_append_c(string, '\n');
1405
1406 g_string_append(string, buffer);
1407 }
1408
1409 if (!string->len)
1410 {
1411 g_string_free(string, TRUE);
1412 string = NULL;
1413 }
1414
1415 return string;
1416}
1417
1418
1419static void print_cb(GString *string, GIOCondition condition, gpointer data)
1420{
1421 if (condition & (G_IO_IN | G_IO_PRI))
1422 {
1423 gsize i;
1424
1425 printf("%s: ", (const gchar *) data);
1426 /*fputs(string->str, stdout);*/
1427 for (i = 0; i < string->len; i++)
1428 {
1429 unsigned char c = (unsigned char) string->str[i];
1430 printf(c >= ' ' && c < 0x80 ? "%c" : "\\x%02x", c);
1431 }
1432 putchar('\n');
1433 }
1434}
1435
1436
1437static void print_status(gint status)
1438{
1439 fputs("finished, ", stderr);
1440
1441 if (SPAWN_WIFEXITED(status))
1442 fprintf(stderr, "exit code %d\n", SPAWN_WEXITSTATUS(status));
1443 else
1444 fputs("abnormal termination\n", stderr);
1445}
1446
1447
1448static void exit_cb(GPid pid, gint status, G_GNUC_UNUSED gpointer data)
1449{
1450 fprintf(stderr, "process %u ", (guint) pid);
1451 print_status(status);
1452}
1453
1454
1455static void watch_cb(GPid pid, gint status, gpointer data)
1456{
1457 g_spawn_close_pid(pid);
1458 exit_cb(pid, status, NULL);
1459 g_main_loop_quit((GMainLoop *) data);
1460}
1461
1462
1463int main(int argc, char **argv)
1464{
1465 char *test_type;
1466
1467 if (argc != 2)
1468 {
1469 fputs("usage: spawn <test-type>\n", stderr);
1470 return 1;
1471 }
1472
1473 test_type = argv[1];
1474
1475 if (!strcmp(test_type, "syntax") || !strcmp(test_type, "syntexec"))
1476 {
1477 char command_line[0x100];
1478
1479 while (read_line("command line: ", command_line, sizeof command_line))
1480 {
1481 GError *error = NULL;
1482
1483 if (spawn_check_command(command_line, argv[1][4] == 'e', &error))
1484 fputs("valid\n", stderr);
1485 else
1486 {
1487 fprintf(stderr, "error: %s\n", error->message);
1488 g_error_free(error);
1489 }
1490 }
1491 }
1492 else if (!strcmp(test_type, "execute"))
1493 {
1494 char command_line[0x100];
1495
1496 while (read_line("command line: ", command_line, sizeof command_line))
1497 {
1498 char working_directory[0x100];
1499 char args[4][0x100];
1500 char envs[4][0x100];
1501 char *argv[] = { args[0], args[1], args[2], args[3], NULL };
1502 char *envp[] = { envs[0], envs[1], envs[2], envs[3], NULL };
1503 int i;
1504 GPid pid;
1505 GError *error = NULL;
1506
1507 read_line("working directory: ", working_directory, sizeof working_directory);
1508
1509 fputs("up to 4 arguments\n", stderr);
1510 for (i = 0; i < 4 && read_line("argument: ", args[i], sizeof args[i]); i++);
1511 argv[i] = NULL;
1512
1513 fputs("up to 4 variables, or empty line for parent environment\n", stderr);
1514 for (i = 0; i < 4 && read_line("variable: ", envs[i], sizeof envs[i]); i++);
1515 envp[i] = NULL;
1516
1517 if (spawn_async_with_pipes(*working_directory ? working_directory : NULL,
1518 *command_line ? command_line : NULL, argv, i ? envp : NULL, &pid, NULL,
1519 NULL, NULL, &error))
1520 {
1521 GMainLoop *loop = g_main_loop_new(NULL, TRUE);
1522
1523 g_child_watch_add(pid, watch_cb, loop);
1524 g_main_loop_run(loop);
1525 g_main_loop_unref(loop);
1526 }
1527 else
1528 {
1529 fprintf(stderr, "error: %s\n", error->message);
1530 g_error_free(error);
1531 }
1532 }
1533 }
1534 else if (!strcmp(test_type, "redirect") || !strcmp(test_type, "redinput"))
1535 {
1536 char command_line[0x100];
1537 gboolean output = test_type[4] == 'r';
1538
1539 while (read_line("command line: ", command_line, sizeof command_line))
1540 {
1541 GString *stdin_text = read_string("text to send: ");
1542 SpawnWriteData stdin_data;
1543 GError *error = NULL;
1544
1545 if (stdin_text)
1546 {
1547 stdin_data.ptr = stdin_text->str;
1548 stdin_data.size = stdin_text->len;
1549 }
1550
1551 if (!spawn_with_callbacks(NULL, command_line, NULL, NULL, SPAWN_SYNC,
1552 stdin_text ? (GIOFunc) spawn_write_data : NULL, &stdin_data,
1553 output ? print_cb : NULL, "stdout", 0, output ? print_cb : NULL,
1554 "stderr", 0, exit_cb, NULL, NULL, &error))
1555 {
1556 fprintf(stderr, "error: %s\n", error->message);
1557 g_error_free(error);
1558 }
1559
1560 if (stdin_text)
1561 g_string_free(stdin_text, TRUE);
1562 }
1563 }
1564 else if (!strcmp(test_type, "capture"))
1565 {
1566 char command_line[0x100];
1567
1568 while (read_line("command line: ", command_line, sizeof command_line))
1569 {
1570 GString *stdin_text = read_string("text to send: ");
1571 SpawnWriteData stdin_data = { NULL, 0 };
1572 GString *stdout_data = g_string_sized_new(0x10000); /* may grow */
1573 GString *stderr_data = g_string_sized_new(0x1000); /* may grow */
1574 gint exit_status;
1575 GError *error = NULL;
1576
1577 if (stdin_text)
1578 {
1579 stdin_data.ptr = stdin_text->str;
1580 stdin_data.size = stdin_text->len;
1581 }
1582
1583 if (spawn_sync(NULL, command_line, NULL, NULL, &stdin_data, stdout_data,
1584 stderr_data, &exit_status, &error))
1585 {
1586 printf("stdout: %s\n", stdout_data->str);
1587 printf("stderr: %s\n", stderr_data->str);
1588 print_status(exit_status);
1589 }
1590 else
1591 {
1592 fprintf(stderr, "error: %s\n", error->message);
1593 g_error_free(error);
1594 }
1595
1596 if (stdin_text)
1597 g_string_free(stdin_text, TRUE);
1598
1599 g_string_free(stdout_data, TRUE);
1600 g_string_free(stderr_data, TRUE);
1601 }
1602 }
1603 else
1604 {
1605 fprintf(stderr, "spawn: unknown test type '%s'", argv[1]);
1606 return 1;
1607 }
1608
1609 return 0;
1610}
1611#endif /* SPAWN_TEST */
const gchar * command
Definition: build.c:2677
gchar * text
Definition: editor.c:83
void error(const errorSelection selection, const char *const format,...)
Definition: error.c:53
int errno
void geany_debug(gchar const *format,...)
Definition: log.c:67
#define NULL
Definition: rbtree.h:150
char * strstr(const char *str, const char *substr)
Definition: routines.c:304
gboolean spawn_with_callbacks(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, SpawnFlags spawn_flags, GIOFunc stdin_cb, gpointer stdin_data, SpawnReadFunc stdout_cb, gpointer stdout_data, gsize stdout_max_length, SpawnReadFunc stderr_cb, gpointer stderr_data, gsize stderr_max_length, GChildWatchFunc exit_cb, gpointer exit_data, GPid *child_pid, GError **error)
<simplesect kind="geany:skip"></simplesect> Executes a child program and setups callbacks.
Definition: spawn.c:1158
gboolean spawn_async(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, GPid *child_pid, GError **error)
Executes a child asynchronously.
Definition: spawn.c:814
static void spawn_finalize(SpawnWatcherData *sw)
Definition: spawn.c:1049
static gboolean spawn_parse_argv(const gchar *command_line, gint *argcp, gchar ***argvp, GError **error)
Definition: spawn.c:86
struct _SpawnWatcherData SpawnWatcherData
static void spawn_timeout_destroy_cb(gpointer data)
Definition: spawn.c:866
gboolean spawn_check_command(const gchar *command_line, gboolean execute, GError **error)
Checks whether a command line is valid.
Definition: spawn.c:246
static gboolean spawn_timeout_read_cb(gpointer data)
Definition: spawn.c:901
#define SPAWN_CHANNEL_GIO_WATCH(sc)
Definition: spawn.c:851
static gboolean spawn_write_cb(GIOChannel *channel, GIOCondition condition, gpointer data)
Definition: spawn.c:888
static void spawn_get_exit_status_cb(G_GNUC_UNUSED GPid pid, gint status, gpointer exit_status)
Definition: spawn.c:1325
static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpointer data)
Definition: spawn.c:909
static void spawn_watch_cb(GPid pid, gint status, gpointer data)
Definition: spawn.c:1079
struct _SpawnChannelData SpawnChannelData
#define SPAWN_IO_FAILURE
Definition: spawn.c:102
static void spawn_destroy_common(SpawnChannelData *sc)
Definition: spawn.c:854
gboolean spawn_sync(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, SpawnWriteData *stdin_data, GString *stdout_data, GString *stderr_data, gint *exit_status, GError **error)
Executes a child synchronously.
Definition: spawn.c:1357
gboolean spawn_write_data(GIOChannel *channel, GIOCondition condition, SpawnWriteData *data)
Writes (a portion of) the data pointed by data->ptr to the channel.
Definition: spawn.c:1297
static gboolean spawn_timeout_watch_cb(gpointer data)
Definition: spawn.c:1065
static gboolean spawn_async_with_pipes(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, GPid *child_pid, gint *stdin_fd, gint *stdout_fd, gint *stderr_fd, GError **error)
Definition: spawn.c:519
#define DEFAULT_IO_LENGTH
Definition: spawn.c:82
static void spawn_append_gstring_cb(GString *string, GIOCondition condition, gpointer data)
Definition: spawn.c:1318
static void spawn_destroy_cb(gpointer data)
Definition: spawn.c:876
#define G_SPAWN_ERROR_TOO_BIG
Definition: spawn.c:74
gboolean spawn_kill_process(GPid pid, GError **error)
Kills a process.
Definition: spawn.c:289
static gchar * spawn_get_program_name(const gchar *command_line, GError **error)
Definition: spawn.c:115
Portable and convenient process spawning and communication.
#define SPAWN_WIFEXITED(status)
non-zero if the child exited normally
Definition: spawn.h:34
void(* SpawnReadFunc)(GString *string, GIOCondition condition, gpointer data)
Specifies the type of function passed to spawn_with_callbacks() as stdout or stderr callback.
Definition: spawn.h:80
SpawnFlags
Flags passed to spawn_with_callbacks(), which see.
Definition: spawn.h:50
@ SPAWN_RECURSIVE
All callbacks are recursive.
Definition: spawn.h:62
@ SPAWN_STDOUT_UNBUFFERED
stdout is not buffered.
Definition: spawn.h:55
@ SPAWN_UNBUFFERED
stdout/stderr are not buffered.
Definition: spawn.h:57
@ SPAWN_STDIN_RECURSIVE
The stdin callback is recursive.
Definition: spawn.h:59
@ SPAWN_SYNC
Synchronous execution.
Definition: spawn.h:52
#define SPAWN_WEXITSTATUS(status)
exit status of a child if exited normally
Definition: spawn.h:35
GtkWidget * main
Definition: splitwindow.c:60
GIOFunc write
Definition: spawn.c:839
union _SpawnChannelData::@115 cb
guint empty_gio_ins
Definition: spawn.c:848
GIOChannel * channel
Definition: spawn.c:836
SpawnReadFunc read
Definition: spawn.c:840
gpointer cb_data
Definition: spawn.c:842
GString * line_buffer
Definition: spawn.c:845
gsize max_length
Definition: spawn.c:846
GString * buffer
Definition: spawn.c:844
GMainContext * main_context
Definition: spawn.c:1044
gpointer exit_data
Definition: spawn.c:1041
gint exit_status
Definition: spawn.c:1043
GChildWatchFunc exit_cb
Definition: spawn.c:1040
SpawnChannelData sc[3]
Definition: spawn.c:1039
GMainLoop * main_loop
Definition: spawn.c:1045
A simple structure used by spawn_write_data() to write data to a channel.
Definition: spawn.h:93
gsize size
Size of the data.
Definition: spawn.h:95
const gchar * ptr
Pointer to the data.
Definition: spawn.h:94
Defines internationalization macros.
#define _(String)
Definition: support.h:42