GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
spawn.c
Go to the documentation of this file.
00001 
00017 #include <stdio.h>
00018 #include <stdlib.h>
00019 #include <string.h>
00020 #include <signal.h>
00021 #include <stdarg.h>
00022 #include <unistd.h>
00023 #include <fcntl.h>
00024 #include <errno.h>
00025 #include <sys/types.h>
00026 
00027 #ifndef __MINGW32__
00028 #include <sys/wait.h>
00029 #else
00030 #include <windows.h>
00031 #endif
00032 #include <grass/config.h>
00033 #include <grass/gis.h>
00034 #include <grass/glocale.h>
00035 #include <grass/spawn.h>
00036 
00044 #define MAX_ARGS 256
00045 #define MAX_BINDINGS 256
00046 #define MAX_SIGNALS 32
00047 #define MAX_REDIRECTS 32
00048 
00049 
00061 struct redirect
00062 {
00063     int dst_fd;
00064     int src_fd;
00065     const char *file;
00066     int mode;
00067 };
00068 
00069 struct signal
00070 {
00071     int which;
00072     int action;
00073     int signum;
00074     int valid;
00075 #ifndef __MINGW32__
00076     struct sigaction old_act;
00077     sigset_t old_mask;
00078 #endif
00079 };
00080 
00081 struct binding
00082 {
00083     const char *var;
00084     const char *val;
00085 };
00086 
00087 struct spawn
00088 {
00089     const char *args[MAX_ARGS];
00090     int num_args;
00091     struct redirect redirects[MAX_REDIRECTS];
00092     int num_redirects;
00093     struct signal signals[MAX_SIGNALS];
00094     int num_signals;
00095     struct binding bindings[MAX_BINDINGS];
00096     int num_bindings;
00097     int background;
00098     const char *directory;
00099 };
00100 
00101 static void parse_arglist(struct spawn *sp, va_list va);
00102 static void parse_argvec(struct spawn *sp, const char **va);
00103 
00104 #ifdef __MINGW32__
00105 
00106 struct buffer {
00107     char *str;
00108     size_t len;
00109     size_t size;
00110 };
00111 
00112 static const int INCREMENT = 50;
00113 
00114 static void clear(struct buffer *b)
00115 {
00116     b->len = 0;
00117     b->str[b->len] = '\0';
00118 }
00119 
00120 static void init(struct buffer *b)
00121 {
00122     b->str = G_malloc(1);
00123     b->size = 1;
00124     clear(b);
00125 }
00126 
00127 static char *release(struct buffer *b)
00128 {
00129     char *p = b->str;
00130 
00131     b->str = NULL;
00132     b->size = 0;
00133     b->len = 0;
00134 
00135     return p;
00136 }
00137 
00138 static void finish(struct buffer *b)
00139 {
00140     if (b->str)
00141         G_free(b->str);
00142     release(b);
00143 }
00144 
00145 static void ensure(struct buffer *b, size_t n)
00146 {
00147     if (b->size <= b->len + n + 1) {
00148         b->size = b->len + n + INCREMENT;
00149         b->str = G_realloc(b->str, b->size);
00150     }
00151 }
00152 
00153 static void append(struct buffer *b, const char *str)
00154 {
00155     size_t n = strlen(str);
00156 
00157     ensure(b, n);
00158     memcpy(&b->str[b->len], str, n);
00159     b->len += n;
00160     b->str[b->len] = '\0';
00161 }
00162 
00163 static void append_char(struct buffer *b, char c)
00164 {
00165     ensure(b, 1);
00166     b->str[b->len] = c;
00167     b->len++;
00168     b->str[b->len] = '\0';
00169 }
00170 
00171 static void escape_arg(struct buffer *result, const char *arg)
00172 {
00173     struct buffer buf;
00174     int quote, j;
00175 
00176     init(&buf);
00177 
00178     quote = arg[0] == '\0' || strchr(arg, ' ') || strchr(arg, '\t');
00179 
00180     if (quote)
00181         append_char(result, '\"');
00182 
00183     for (j = 0; arg[j]; j++) {
00184         int c = arg[j];
00185         int k;
00186 
00187         switch (c) {
00188         case '\\':
00189             append_char(&buf, '\\');
00190             break;
00191         case '\"':
00192             for (k = 0; k < buf.len; k++)
00193                 append(result, "\\\\");
00194             clear(&buf);
00195             append(result, "\\\"");
00196             break;
00197         default:
00198             if (buf.len > 0) {
00199                 append(result, buf.str);
00200                 clear(&buf);
00201             }
00202             append_char(result, c);
00203         }
00204     }
00205 
00206     if (buf.len > 0)
00207         append(result, buf.str);
00208 
00209     if (quote) {
00210         append(result, buf.str);
00211         append_char(result, '\"');
00212     }
00213 
00214     finish(&buf);
00215 }
00216 
00217 static char *check_program(const char *pgm, const char *dir, const char *ext)
00218 {
00219     char pathname[GPATH_MAX];
00220 
00221     sprintf(pathname, "%s%s%s%s", dir, *dir ? "\\" : "", pgm, ext);
00222     return access(pathname, 0) == 0
00223         ? G_store(pathname)
00224         : NULL;
00225 }
00226 
00227 static char *find_program_ext(const char *pgm, const char *dir, char **pathext)
00228 {
00229     char *result;
00230     int i;
00231 
00232     if (result = check_program(pgm, dir, ""), result)
00233         return result;
00234 
00235     for (i = 0; pathext[i]; i++) {
00236         const char *ext = pathext[i];
00237         if (result = check_program(pgm, dir, ext), result)
00238             return result;
00239     }
00240 
00241     return NULL;
00242 }
00243 
00244 static char *find_program_dir_ext(const char *pgm, char **path, char **pathext)
00245 {
00246     char *result = NULL;
00247     int i;
00248 
00249     if (strchr(pgm, '\\') || strchr(pgm, '/')) {
00250         if (result = find_program_ext(pgm, "", pathext), result)
00251             return result;
00252     }
00253     else {
00254         if (result = find_program_ext(pgm, ".", pathext), result)
00255             return result;
00256 
00257         for (i = 0; path[i]; i++) {
00258             const char *dir = path[i];
00259             if (result = find_program_ext(pgm, dir, pathext), result)
00260                 return result;
00261         }
00262     }
00263 
00264     return NULL;
00265 }
00266 
00267 static char *find_program(const char *pgm)
00268 {
00269     char **path = G_tokenize(getenv("PATH"), ";");
00270     char **pathext = G_tokenize(getenv("PATHEXT"), ";");
00271     char *result = find_program_dir_ext(pgm, path, pathext);
00272     G_free_tokens(path);
00273     G_free_tokens(pathext);
00274     return result;
00275 }
00276 
00277 static char *make_command_line(int shell, const char *cmd, const char **argv)
00278 {
00279     struct buffer result;
00280     int i;
00281 
00282     init(&result);
00283 
00284     if (shell) {
00285         const char *comspec = getenv("COMSPEC");
00286         append(&result, comspec ? comspec : "cmd.exe");
00287         append(&result, " /c \"");
00288         escape_arg(&result, cmd);
00289     }
00290 
00291     for (i = shell ? 1 : 0; argv[i]; i++) {
00292         if (result.len > 0)
00293             append_char(&result, ' ');
00294         escape_arg(&result, argv[i]);
00295     }
00296 
00297     append(&result, "\"");
00298 
00299     return release(&result);
00300 }
00301 
00302 static char *make_environment(const char **envp)
00303 {
00304     struct buffer result;
00305     int i;
00306 
00307     init(&result);
00308 
00309     for (i = 0; envp[i]; i++) {
00310         const char *env = envp[i];
00311 
00312         append(&result, env);
00313         append_char(&result, '\0');
00314     }
00315 
00316     return release(&result);
00317 }
00318 
00319 static HANDLE get_handle(int fd)
00320 {
00321     HANDLE h1, h2;
00322 
00323     if (fd < 0)
00324         return INVALID_HANDLE_VALUE;
00325 
00326     h1 = (HANDLE) _get_osfhandle(fd);
00327     if (!DuplicateHandle(GetCurrentProcess(), h1,
00328                          GetCurrentProcess(), &h2,
00329                          0, TRUE, DUPLICATE_SAME_ACCESS))
00330         return INVALID_HANDLE_VALUE;
00331 
00332     return h2;
00333 }
00334 
00335 static int win_spawn(const char *cmd, const char **argv, const char **envp,
00336                      const char *cwd, HANDLE handles[3], int background,
00337                      int shell)
00338 {
00339     char *args = make_command_line(shell, cmd, argv);
00340     char *env = make_environment(envp);
00341     char *program = shell ? NULL : find_program(cmd);
00342     STARTUPINFO si;
00343     PROCESS_INFORMATION pi;
00344     BOOL result;
00345     DWORD exitcode;
00346     int i;
00347 
00348     if (!shell) {
00349         G_debug(3, "win_spawn: program = %s", program);
00350 
00351         if (!program) {
00352             G_free(args);
00353             G_free(env);
00354             return -1;
00355         }
00356     }
00357 
00358     G_debug(3, "win_spawn: args = %s", args);
00359 
00360     memset(&si, 0, sizeof(si));
00361     si.cb = sizeof(si);
00362 
00363     si.dwFlags |= STARTF_USESTDHANDLES;
00364     si.hStdInput  = handles[0];
00365     si.hStdOutput = handles[1];
00366     si.hStdError  = handles[2];
00367 
00368     result = CreateProcess(
00369         program,        /* lpApplicationName */
00370         args,           /* lpCommandLine */
00371         NULL,           /* lpProcessAttributes */
00372         NULL,           /* lpThreadAttributes */
00373         1,              /* bInheritHandles */
00374         0,              /* dwCreationFlags */
00375         env,            /* lpEnvironment */
00376         cwd,            /* lpCurrentDirectory */
00377         &si,            /* lpStartupInfo */
00378         &pi             /* lpProcessInformation */
00379         );
00380 
00381     G_free(args);
00382     G_free(env);
00383     G_free(program);
00384 
00385     if (!result) {
00386         G_warning(_("CreateProcess() failed: error = %d"), GetLastError());
00387         return -1;
00388     }
00389 
00390     CloseHandle(pi.hThread);
00391 
00392     for (i = 0; i < 3; i++)
00393         if (handles[i] != INVALID_HANDLE_VALUE)
00394             CloseHandle(handles[i]);
00395 
00396     if (!background) {
00397         WaitForSingleObject(pi.hProcess, INFINITE);
00398         if (!GetExitCodeProcess(pi.hProcess, &exitcode))
00399             return -1;
00400         CloseHandle(pi.hProcess);
00401         return (int) exitcode;
00402     }
00403 
00404     CloseHandle(pi.hProcess);
00405 
00406     return pi.dwProcessId;
00407 }
00408 
00409 static void do_redirects(struct redirect *redirects, int num_redirects, HANDLE handles[3])
00410 {
00411     int i;
00412 
00413     for (i = 0; i < 3; i++)
00414         handles[i] = get_handle(i);
00415 
00416     for (i = 0; i < num_redirects; i++) {
00417         struct redirect *r = &redirects[i];
00418 
00419         if (r->dst_fd < 0 || r->dst_fd > 2) {
00420             if (r->file || r->src_fd >= 0)
00421                 G_warning(_("G_spawn: unable to redirect descriptor %d"), r->dst_fd);
00422             continue;
00423         }
00424 
00425         if (r->file) {
00426             r->src_fd = open(r->file, r->mode, 0666);
00427 
00428             if (r->src_fd < 0) {
00429                 G_warning(_("G_spawn: unable to open file %s"), r->file);
00430                 _exit(127);
00431             }
00432 
00433             handles[r->dst_fd] = get_handle(r->src_fd);
00434 
00435             close(r->src_fd);
00436 
00437         }
00438         else if (r->src_fd >= 0) {
00439             handles[r->dst_fd] = get_handle(r->src_fd);
00440         }
00441         else {
00442             if (r->dst_fd < 3) {
00443                 CloseHandle(handles[r->dst_fd]);
00444                 handles[r->dst_fd] = INVALID_HANDLE_VALUE;
00445             }
00446             close(r->dst_fd);
00447         }
00448     }
00449 }
00450 
00451 static void add_binding(const char **env, int *pnum, const struct binding *b)
00452 {
00453     char *str = G_malloc(strlen(b->var) + strlen(b->val) + 2);
00454     int n = *pnum;
00455     int i;
00456 
00457     sprintf(str, "%s=%s", b->var, b->val);
00458 
00459     for (i = 0; i < n; i++)
00460         if (G_strcasecmp(env[i], b->var) == 0) {
00461             env[i] = str;
00462             return;
00463         }
00464 
00465     env[n++] = str;
00466     *pnum = n;
00467 }
00468 
00469 static const char **do_bindings(const struct binding *bindings, int num_bindings)
00470 {
00471     const char **newenv;
00472     int i, n;
00473 
00474     for (i = 0; _environ[i]; i++)
00475         ;
00476     n = i;
00477 
00478     newenv = G_malloc((num_bindings + n + 1) * sizeof(char *));
00479 
00480     for (i = 0; i < n; i++)
00481         newenv[i] = _environ[i];
00482 
00483     for (i = 0; i < num_bindings; i++)
00484         add_binding(newenv, &n, &bindings[i]);
00485 
00486     newenv[num_bindings + n] = NULL;
00487 
00488     return newenv;
00489 }
00490 
00491 static int do_spawn(struct spawn *sp, const char *command)
00492 {
00493     HANDLE handles[3];
00494     const char **env;
00495     int status;
00496 
00497     do_redirects(sp->redirects, sp->num_redirects, handles);
00498     env = do_bindings(sp->bindings, sp->num_bindings);
00499 
00500     status = win_spawn(command, sp->args, env, sp->directory, handles, sp->background, 1);
00501 
00502     if (!sp->background && status < 0)
00503         G_warning(_("Unable to execute command"));
00504 
00505     return status;
00506 }
00507 
00508 #else /* __MINGW32__ */
00509 
00510 static int undo_signals(const struct signal *signals, int num_signals, int which)
00511 {
00512     int error = 0;
00513     int i;
00514 
00515     for (i = num_signals - 1; i >= 0; i--) {
00516         const struct signal *s = &signals[i];
00517 
00518         if (s->which != which)
00519             continue;
00520 
00521         if (!s->valid)
00522             continue;
00523 
00524         switch (s->action) {
00525         case SSA_IGNORE:
00526         case SSA_DEFAULT:
00527             if (sigaction(s->signum, &s->old_act, NULL) < 0) {
00528                 G_warning(_("G_spawn: unable to restore signal %d"),
00529                           s->signum);
00530                 error = 1;
00531             }
00532             break;
00533         case SSA_BLOCK:
00534         case SSA_UNBLOCK:
00535             if (sigprocmask(SIG_UNBLOCK, &s->old_mask, NULL) < 0) {
00536                 G_warning(_("G_spawn: unable to restore signal %d"),
00537                           s->signum);
00538                 error = 1;
00539             }
00540             break;
00541         }
00542     }
00543 
00544     return !error;
00545 }
00546 
00547 static int do_signals(struct signal *signals, int num_signals, int which)
00548 {
00549     struct sigaction act;
00550     sigset_t mask;
00551     int error = 0;
00552     int i;
00553 
00554     sigemptyset(&act.sa_mask);
00555     act.sa_flags = SA_RESTART;
00556 
00557     for (i = 0; i < num_signals; i++) {
00558         struct signal *s = &signals[i];
00559 
00560         if (s->which != which)
00561             continue;
00562 
00563         switch (s->action) {
00564         case SSA_IGNORE:
00565             act.sa_handler = SIG_IGN;
00566             if (sigaction(s->signum, &act, &s->old_act) < 0) {
00567                 G_warning(_("G_spawn: unable to reset signal %d"), s->signum);
00568                 error = 1;
00569             }
00570             else
00571                 s->valid = 1;
00572             break;
00573         case SSA_DEFAULT:
00574             act.sa_handler = SIG_DFL;
00575             if (sigaction(s->signum, &act, &s->old_act) < 0) {
00576                 G_warning(_("G_spawn: unable to ignore signal %d"),
00577                           s->signum);
00578                 error = 1;
00579             }
00580             else
00581                 s->valid = 1;
00582             break;
00583         case SSA_BLOCK:
00584             sigemptyset(&mask);
00585             sigaddset(&mask, s->signum);
00586             if (sigprocmask(SIG_BLOCK, &mask, &s->old_mask) < 0) {
00587                 G_warning(_("G_spawn: unable to block signal %d"), s->signum);
00588                 error = 1;
00589             }
00590             break;
00591         case SSA_UNBLOCK:
00592             sigemptyset(&mask);
00593             sigaddset(&mask, s->signum);
00594             if (sigprocmask(SIG_UNBLOCK, &mask, &s->old_mask) < 0) {
00595                 G_warning(_("G_spawn: unable to unblock signal %d"),
00596                           s->signum);
00597                 error = 1;
00598             }
00599             else
00600                 s->valid = 1;
00601             break;
00602         }
00603     }
00604 
00605     return !error;
00606 }
00607 
00608 static void do_redirects(struct redirect *redirects, int num_redirects)
00609 {
00610     int i;
00611 
00612     for (i = 0; i < num_redirects; i++) {
00613         struct redirect *r = &redirects[i];
00614 
00615         if (r->file) {
00616             r->src_fd = open(r->file, r->mode, 0666);
00617 
00618             if (r->src_fd < 0) {
00619                 G_warning(_("G_spawn: unable to open file %s"), r->file);
00620                 _exit(127);
00621             }
00622 
00623             if (dup2(r->src_fd, r->dst_fd) < 0) {
00624                 G_warning(_("G_spawn: unable to duplicate descriptor %d to %d"),
00625                           r->src_fd, r->dst_fd);
00626                 _exit(127);
00627             }
00628 
00629             close(r->src_fd);
00630         }
00631         else if (r->src_fd >= 0) {
00632             if (dup2(r->src_fd, r->dst_fd) < 0) {
00633                 G_warning(_("G_spawn: unable to duplicate descriptor %d to %d"),
00634                           r->src_fd, r->dst_fd);
00635                 _exit(127);
00636             }
00637         }
00638         else
00639             close(r->dst_fd);
00640     }
00641 }
00642 
00643 static void do_bindings(const struct binding *bindings, int num_bindings)
00644 {
00645     int i;
00646 
00647     for (i = 0; i < num_bindings; i++) {
00648         const struct binding *b = &bindings[i];
00649         char *str = G_malloc(strlen(b->var) + strlen(b->val) + 2);
00650 
00651         sprintf(str, "%s=%s", b->var, b->val);
00652         putenv(str);
00653     }
00654 }
00655 
00656 static int do_spawn(struct spawn *sp, const char *command)
00657 {
00658     int status = -1;
00659     pid_t pid;
00660 
00661     if (!do_signals(sp->signals, sp->num_signals, SST_PRE))
00662         return status;
00663 
00664     pid = fork();
00665     if (pid < 0) {
00666         G_warning(_("Unable to create a new process"));
00667         undo_signals(sp->signals, sp->num_signals, SST_PRE);
00668 
00669         return status;
00670     }
00671 
00672     if (pid == 0) {
00673         if (!undo_signals(sp->signals, sp->num_signals, SST_PRE))
00674             _exit(127);
00675 
00676         if (!do_signals(sp->signals, sp->num_signals, SST_CHILD))
00677             _exit(127);
00678 
00679         if (sp->directory)
00680             if (chdir(sp->directory) < 0) {
00681                 G_warning(_("Unable to change directory to %s"), sp->directory);
00682                 _exit(127);
00683             }
00684 
00685         do_redirects(sp->redirects, sp->num_redirects);
00686         do_bindings(sp->bindings, sp->num_bindings);
00687 
00688         execvp(command, (char **)sp->args);
00689         G_warning(_("Unable to execute command"));
00690         _exit(127);
00691     }
00692 
00693     do_signals(sp->signals, sp->num_signals, SST_POST);
00694 
00695     if (sp->background)
00696         status = (int)pid;
00697     else {
00698         pid_t n;
00699 
00700         do
00701             n = waitpid(pid, &status, 0);
00702         while (n == (pid_t) - 1 && errno == EINTR);
00703 
00704         if (n != pid)
00705             status = -1;
00706         else {
00707             if (WIFEXITED(status))
00708                 status = WEXITSTATUS(status);
00709             else if (WIFSIGNALED(status))
00710                 status = WTERMSIG(status);
00711             else
00712                 status = -0x100;
00713         }
00714     }
00715 
00716     undo_signals(sp->signals, sp->num_signals, SST_POST);
00717     undo_signals(sp->signals, sp->num_signals, SST_PRE);
00718 
00719     return status;
00720 }
00721 
00722 #endif /* __MINGW32__ */
00723 
00724 static void begin_spawn(struct spawn *sp)
00725 {
00726     sp->num_args = 0;
00727     sp->num_redirects = 0;
00728     sp->num_signals = 0;
00729     sp->num_bindings = 0;
00730     sp->background = 0;
00731     sp->directory = NULL;
00732 }
00733 
00734 #define NEXT_ARG(var, type) ((type) *(var)++)
00735 
00736 static void parse_argvec(struct spawn *sp, const char **va)
00737 {
00738     for (;;) {
00739         const char *arg = NEXT_ARG(va, const char *);
00740         const char *var, *val;
00741 
00742         if (!arg) {
00743             sp->args[sp->num_args++] = NULL;
00744             break;
00745         }
00746         else if (arg == SF_REDIRECT_FILE) {
00747             sp->redirects[sp->num_redirects].dst_fd = NEXT_ARG(va, int);
00748 
00749             sp->redirects[sp->num_redirects].src_fd = -1;
00750             sp->redirects[sp->num_redirects].mode = NEXT_ARG(va, int);
00751             sp->redirects[sp->num_redirects].file = NEXT_ARG(va, const char *);
00752 
00753             sp->num_redirects++;
00754         }
00755         else if (arg == SF_REDIRECT_DESCRIPTOR) {
00756             sp->redirects[sp->num_redirects].dst_fd = NEXT_ARG(va, int);
00757             sp->redirects[sp->num_redirects].src_fd = NEXT_ARG(va, int);
00758 
00759             sp->redirects[sp->num_redirects].file = NULL;
00760             sp->num_redirects++;
00761         }
00762         else if (arg == SF_CLOSE_DESCRIPTOR) {
00763             sp->redirects[sp->num_redirects].dst_fd = NEXT_ARG(va, int);
00764 
00765             sp->redirects[sp->num_redirects].src_fd = -1;
00766             sp->redirects[sp->num_redirects].file = NULL;
00767             sp->num_redirects++;
00768         }
00769         else if (arg == SF_SIGNAL) {
00770             sp->signals[sp->num_signals].which = NEXT_ARG(va, int);
00771             sp->signals[sp->num_signals].action = NEXT_ARG(va, int);
00772             sp->signals[sp->num_signals].signum = NEXT_ARG(va, int);
00773 
00774             sp->signals[sp->num_signals].valid = 0;
00775             sp->num_signals++;
00776         }
00777         else if (arg == SF_VARIABLE) {
00778             var = NEXT_ARG(va, const char *);
00779 
00780             val = getenv(var);
00781             sp->args[sp->num_args++] = val ? val : "";
00782         }
00783         else if (arg == SF_BINDING) {
00784             sp->bindings[sp->num_bindings].var = NEXT_ARG(va, const char *);
00785             sp->bindings[sp->num_bindings].val = NEXT_ARG(va, const char *);
00786 
00787             sp->num_bindings++;
00788         }
00789         else if (arg == SF_BACKGROUND) {
00790             sp->background = 1;
00791         }
00792         else if (arg == SF_DIRECTORY) {
00793             sp->directory = NEXT_ARG(va, const char *);
00794 
00795         }
00796         else if (arg == SF_ARGVEC) {
00797             parse_argvec(sp, NEXT_ARG(va, const char **));
00798         }
00799         else
00800             sp->args[sp->num_args++] = arg;
00801     }
00802 }
00803 
00804 static void parse_arglist(struct spawn *sp, va_list va)
00805 {
00806     for (;;) {
00807         const char *arg = va_arg(va, const char *);
00808         const char *var, *val;
00809 
00810         if (!arg) {
00811             sp->args[sp->num_args++] = NULL;
00812             break;
00813         }
00814         else if (arg == SF_REDIRECT_FILE) {
00815             sp->redirects[sp->num_redirects].dst_fd = va_arg(va, int);
00816 
00817             sp->redirects[sp->num_redirects].src_fd = -1;
00818             sp->redirects[sp->num_redirects].mode = va_arg(va, int);
00819             sp->redirects[sp->num_redirects].file = va_arg(va, const char *);
00820 
00821             sp->num_redirects++;
00822         }
00823         else if (arg == SF_REDIRECT_DESCRIPTOR) {
00824             sp->redirects[sp->num_redirects].dst_fd = va_arg(va, int);
00825             sp->redirects[sp->num_redirects].src_fd = va_arg(va, int);
00826 
00827             sp->redirects[sp->num_redirects].file = NULL;
00828             sp->num_redirects++;
00829         }
00830         else if (arg == SF_CLOSE_DESCRIPTOR) {
00831             sp->redirects[sp->num_redirects].dst_fd = va_arg(va, int);
00832 
00833             sp->redirects[sp->num_redirects].src_fd = -1;
00834             sp->redirects[sp->num_redirects].file = NULL;
00835             sp->num_redirects++;
00836         }
00837         else if (arg == SF_SIGNAL) {
00838             sp->signals[sp->num_signals].which = va_arg(va, int);
00839             sp->signals[sp->num_signals].action = va_arg(va, int);
00840             sp->signals[sp->num_signals].signum = va_arg(va, int);
00841 
00842             sp->signals[sp->num_signals].valid = 0;
00843             sp->num_signals++;
00844         }
00845         else if (arg == SF_VARIABLE) {
00846             var = va_arg(va, char *);
00847 
00848             val = getenv(var);
00849             sp->args[sp->num_args++] = val ? val : "";
00850         }
00851         else if (arg == SF_BINDING) {
00852             sp->bindings[sp->num_bindings].var = va_arg(va, const char *);
00853             sp->bindings[sp->num_bindings].val = va_arg(va, const char *);
00854 
00855             sp->num_bindings++;
00856         }
00857         else if (arg == SF_BACKGROUND) {
00858             sp->background = 1;
00859         }
00860         else if (arg == SF_DIRECTORY) {
00861             sp->directory = va_arg(va, const char *);
00862         }
00863         else if (arg == SF_ARGVEC) {
00864             parse_argvec(sp, va_arg(va, const char **));
00865         }
00866         else
00867             sp->args[sp->num_args++] = arg;
00868     }
00869 }
00870 
00881 int G_vspawn_ex(const char *command, const char **args)
00882 {
00883     struct spawn sp;
00884 
00885     begin_spawn(&sp);
00886 
00887     parse_argvec(&sp, args);
00888 
00889     return do_spawn(&sp, command);
00890 }
00891 
00902 int G_spawn_ex(const char *command, ...)
00903 {
00904     struct spawn sp;
00905     va_list va;
00906 
00907     begin_spawn(&sp);
00908 
00909     va_start(va, command);
00910     parse_arglist(&sp, va);
00911     va_end(va);
00912 
00913     return do_spawn(&sp, command);
00914 }
00915 
00924 int G_spawn(const char *command, ...)
00925 {
00926     const char *args[MAX_ARGS];
00927     int num_args = 0, i;
00928     va_list va;
00929     int status = -1;
00930 
00931     va_start(va, command);
00932 
00933     for (i = 0; ; i++) {
00934         const char *arg = va_arg(va, const char *);
00935         args[num_args++] = arg;
00936         if (!arg)
00937             break;
00938     }
00939 
00940     va_end(va);
00941 
00942     status = G_spawn_ex(
00943         command,
00944 #ifndef __MINGW32__
00945         SF_SIGNAL, SST_PRE, SSA_IGNORE, SIGINT,
00946         SF_SIGNAL, SST_PRE, SSA_IGNORE, SIGQUIT,
00947         SF_SIGNAL, SST_PRE, SSA_BLOCK, SIGCHLD,
00948 #endif
00949         SF_ARGVEC, args,
00950         NULL);
00951 
00952     return status;
00953 }
00954 
00955 int G_wait(int i_pid)
00956 {
00957 #ifdef __MINGW32__
00958     DWORD rights = PROCESS_QUERY_INFORMATION | SYNCHRONIZE;
00959     HANDLE hProcess = OpenProcess(rights, FALSE, (DWORD) i_pid);
00960     DWORD exitcode;
00961 
00962     if (!hProcess)
00963         return -1;
00964 
00965     WaitForSingleObject(hProcess, INFINITE);
00966     if (!GetExitCodeProcess(hProcess, &exitcode))
00967         exitcode = (DWORD) -1;
00968 
00969     CloseHandle(hProcess);
00970 
00971     return (int) exitcode;
00972 #else
00973     pid_t pid = (pid_t) i_pid;
00974     int status = -1;
00975     pid_t n;
00976 
00977     do
00978         n = waitpid(pid, &status, 0);
00979     while (n == (pid_t) - 1 && errno == EINTR);
00980 
00981     if (n != pid)
00982         return -1;
00983     else {
00984         if (WIFEXITED(status))
00985             return WEXITSTATUS(status);
00986         else if (WIFSIGNALED(status))
00987             return WTERMSIG(status);
00988         else
00989             return -0x100;
00990     }
00991 #endif
00992 }
00993