/* run.c --- routines for executing subprocesses under OS/2. This file is part of GNU CVS. GNU CVS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include "cvs.h" #include "os2inc.h" #include #include #include #include #include #include #include #include #include #define STDIN 0 #define STDOUT 1 #define STDERR 2 static void run_add_arg( const char *s ); static void run_init_prog( void ); extern char *strtok (); /* * To exec a program under CVS, first call run_setup() to setup any initial * arguments. The options to run_setup are essentially like printf(). The * arguments will be parsed into whitespace separated words and added to the * global run_argv list. * * Then, optionally call run_arg() for each additional argument that you'd like * to pass to the executed program. * * Finally, call run_exec() to execute the program with the specified * arguments. * The execvp() syscall will be used, so that the PATH is searched correctly. * File redirections can be performed in the call to run_exec(). */ static char **run_argv; static int run_argc; static int run_argc_allocated; void run_setup (const char *prog) { int i; char *run_prog; char *buf, *d, *s; size_t length; size_t doff; char inquotes; int dolastarg; /* clean out any malloc'ed values from run_argv */ for (i = 0; i < run_argc; i++) { if (run_argv[i]) { free (run_argv[i]); run_argv[i] = (char *) 0; } } run_argc = 0; run_prog = xstrdup (prog); s = run_prog; d = buf = NULL; length = 0; dolastarg = 1; inquotes = '\0'; doff = d - buf; expand_string(&buf, &length, doff + 1); d = buf + doff; while (*d = *s++) { switch (*d) { case '\\': if (*s) *d = *s++; d++; break; case '"': case '\'': if (inquotes == *d) inquotes = '\0'; else inquotes = *d; break; case ' ': case '\t': if (inquotes) d++; else { *d = '\0'; run_add_arg (buf); d = buf; while (isspace(*s)) s++; if (!*s) dolastarg = 0; } break; default: d++; break; } doff = d - buf; expand_string(&buf, &length, doff + 1); d = buf + doff; } if (dolastarg) run_add_arg (buf); /* put each word into run_argv, allocating it as we go */ if (buf) free (buf); free (run_prog); free (run_prog); if (buf) free (buf); } void run_arg (s) const char *s; { run_add_arg (s); } /* Return a malloc'd copy of s, with double quotes around it. */ /* FIXME - this should replace " with \" as it copies. or something. * depends where it's used, I would suppose. */ static char * quote (const char *s) { size_t s_len = strlen (s); char *copy = xmalloc (s_len + 3); char *scan = copy; *scan++ = '"'; strcpy (scan, s); scan += s_len; *scan++ = '"'; *scan++ = '\0'; return copy; } static void run_add_arg (s) const char *s; { /* allocate more argv entries if we've run out */ if (run_argc >= run_argc_allocated) { run_argc_allocated += 50; run_argv = (char **) xrealloc ((char *) run_argv, run_argc_allocated * sizeof (char **)); } if (s) { run_argv[run_argc] = (run_argc ? quote (s) : xstrdup (s)); run_argc++; } else /* not post-incremented on purpose! */ run_argv[run_argc] = (char *) 0; } int run_exec (stin, stout, sterr, flags) char *stin; char *stout; char *sterr; int flags; { int shin, shout, sherr; int sain, saout, saerr; /* saved handles */ int mode_out, mode_err; int status = -1; int rerrno = 0; int rval = -1; void (*old_sigint) (int); if (trace) /* if in trace mode */ { (void) fprintf (stderr, "-> system("); run_print (stderr); (void) fprintf (stderr, ")\n"); } if (noexec && (flags & RUN_REALLY) == 0) /* if in noexec mode */ return (0); /* * start the engine and take off */ /* make sure that we are null terminated, since we didn't calloc */ run_add_arg ((char *) 0); /* setup default file descriptor numbers */ shin = 0; shout = 1; sherr = 2; /* set the file modes for stdout and stderr */ mode_out = mode_err = O_WRONLY | O_CREAT; mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC); mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC); /* open the files as required, shXX are shadows of stdin... */ if (stin && (shin = open (stin, O_RDONLY)) == -1) { rerrno = errno; error (0, errno, "cannot open %s for reading (prog %s)", stin, run_argv[0]); goto out0; } if (stout && (shout = open (stout, mode_out, 0666)) == -1) { rerrno = errno; error (0, errno, "cannot open %s for writing (prog %s)", stout, run_argv[0]); goto out1; } if (sterr && (flags & RUN_COMBINED) == 0) { if ((sherr = open (sterr, mode_err, 0666)) == -1) { rerrno = errno; error (0, errno, "cannot open %s for writing (prog %s)", sterr, run_argv[0]); goto out2; } } /* now save the standard handles */ sain = saout = saerr = -1; sain = dup( 0); /* dup stdin */ saout = dup( 1); /* dup stdout */ saerr = dup( 2); /* dup stderr */ /* the new handles will be dup'd to the standard handles * for the spawn. */ if (shin != 0) { (void) dup2 (shin, 0); (void) close (shin); } if (shout != 1) { (void) dup2 (shout, 1); (void) close (shout); } if (flags & RUN_COMBINED) (void) dup2 (1, 2); else if (sherr != 2) { (void) dup2 (sherr, 2); (void) close (sherr); } /* Ignore signals while we're running this. */ old_sigint = signal (SIGINT, SIG_IGN); /* dup'ing is done. try to run it now */ rval = spawnvp ( P_WAIT, run_argv[0], run_argv); /* Restore signal handling. */ signal (SIGINT, old_sigint); /* restore the original file handles */ if (sain != -1) { (void) dup2( sain, 0); /* re-connect stdin */ (void) close( sain); } if (saout != -1) { (void) dup2( saout, 1); /* re-connect stdout */ (void) close( saout); } if (saerr != -1) { (void) dup2( saerr, 2); /* re-connect stderr */ (void) close( saerr); } /* Recognize the return code for a failed subprocess. */ if (rval == -1) return 2; else return rval; /* return child's exit status */ /* error cases */ /* cleanup the open file descriptors */ out2: if (stout) (void) close (shout); out1: if (stin) (void) close (shin); out0: if (rerrno) errno = rerrno; return (status); } void run_print (fp) FILE *fp; { int i; for (i = 0; i < run_argc; i++) { (void) fprintf (fp, "'%s'", run_argv[i]); if (i != run_argc - 1) (void) fprintf (fp, " "); } } static char * requote (const char *cmd) { char *requoted = xmalloc (strlen (cmd) + 1); char *p = requoted; strcpy (requoted, cmd); while ((p = strchr (p, '\'')) != NULL) { *p++ = '"'; } return requoted; } FILE * run_popen (cmd, mode) const char *cmd; const char *mode; { TRACE( TRACE_FUNCTION, "run_popen(%s,%s)", cmd, mode ); if (noexec) return (NULL); /* If the command string uses single quotes, turn them into double quotes. */ { char *requoted = requote (cmd); TRACE( TRACE_DATA, "Executing popen(%s,%s)", requoted, mode ); FILE *result = popen (requoted, mode); free (requoted); return result; } } /* Running children with pipes connected to them. */ /* Create a pipe. Set READWRITE[0] to its reading end, and READWRITE[1] to its writing end. */ static int my_pipe (int *readwrite) { fprintf (stderr, "Error: my_pipe() is unimplemented.\n"); exit (1); } /* Create a child process running COMMAND with IN as its standard input, and OUT as its standard output. Return a handle to the child, or INVALID_HANDLE_VALUE. */ static int start_child (char *command, int in, int out) { fprintf (stderr, "Error: start_child() is unimplemented.\n"); exit (1); } /* Given an array of arguments that one might pass to spawnv, construct a command line that one might pass to CreateProcess. Try to quote things appropriately. */ static char * build_command (char **argv) { int len; /* Compute the total length the command will have. */ { int i; len = 0; for (i = 0; argv[i]; i++) { char *p; len += 2; /* for the double quotes */ for (p = argv[i]; *p; p++) { if (*p == '"') len += 2; else len++; } } len++; /* for the space or the '\0' */ } { char *command = (char *) xmalloc (len); int i; char *p; if (! command) { errno = ENOMEM; return command; } p = command; /* copy each element of argv to command, putting each command in double quotes, and backslashing any quotes that appear within an argument. */ for (i = 0; argv[i]; i++) { char *a; *p++ = '"'; for (a = argv[i]; *a; a++) { if (*a == '"') *p++ = '\\', *p++ = '"'; else *p++ = *a; } *p++ = '"'; *p++ = ' '; } p[-1] = '\0'; return command; } } /* Create an asynchronous child process executing ARGV, with its standard input and output connected to the parent with pipes. Set *TO to the file descriptor on which one writes data for the child; set *FROM to the file descriptor from which one reads data from the child. Return the handle of the child process (this is what _cwait and waitpid expect). */ int piped_child (char **argv, int *to, int *from) { fprintf (stderr, "Error: piped_child() is unimplemented.\n"); exit (1); } /* * dir = 0 : main proc writes to new proc, which writes to oldfd * dir = 1 : main proc reads from new proc, which reads from oldfd * * If this returns at all, then it was successful and the return value * is a file descriptor; else it errors and exits. */ int filter_stream_through_program (int oldfd, int dir, char **prog, int *pidp) { int newfd; /* Gets set to one end of the pipe and returned. */ HFILE from, to; HFILE Old0 = -1, Old1 = -1, Old2 = -1, Tmp; if (DosCreatePipe (&from, &to, 4096)) return FALSE; /* Save std{in,out,err} */ DosDupHandle (STDIN, &Old0); DosSetFHState (Old1, OPEN_FLAGS_NOINHERIT); DosDupHandle (STDOUT, &Old1); DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT); DosDupHandle (STDERR, &Old2); DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT); /* Redirect std{in,out,err} */ if (dir) /* Who goes where? */ { Tmp = STDIN; DosDupHandle (oldfd, &Tmp); Tmp = STDOUT; DosDupHandle (to, &Tmp); Tmp = STDERR; DosDupHandle (to, &Tmp); newfd = from; _setmode (newfd, O_BINARY); DosClose (oldfd); DosClose (to); DosSetFHState (from, OPEN_FLAGS_NOINHERIT); } else { Tmp = STDIN; DosDupHandle (from, &Tmp); Tmp = STDOUT; DosDupHandle (oldfd, &Tmp); Tmp = STDERR; DosDupHandle (oldfd, &Tmp); newfd = to; _setmode (newfd, O_BINARY); DosClose (oldfd); DosClose (from); DosSetFHState (to, OPEN_FLAGS_NOINHERIT); } /* Spawn we now our hoary brood. */ *pidp = spawnvp (P_NOWAIT, prog[0], prog); /* Restore std{in,out,err} */ Tmp = STDIN; DosDupHandle (Old0, &Tmp); DosClose (Old0); Tmp = STDOUT; DosDupHandle (Old1, &Tmp); DosClose (Old1); Tmp = STDERR; DosDupHandle (Old2, &Tmp); DosClose (Old2); if(*pidp < 0) { DosClose (from); DosClose (to); error (1, 0, "error spawning %s", prog[0]); } return newfd; } int pipe (int *filedesc) { /* todo: actually, we can use DosCreatePipe(). Fix this. */ fprintf (stderr, "Error: pipe() should not have been called in client.\n"); exit (1); } void close_on_exec (int fd) { /* Just does nothing for now... */ /* Actually, we probably *can* implement this one. Let's see... */ /* Nope. OS/2 has , but no fcntl() ! Wow. */ /* Well, I'll leave this stuff in for future reference. */ } /* Actually, we #define sleep() in config.h now. */ #ifndef sleep unsigned int sleep (unsigned int seconds) { /* I don't want to interfere with alarm signals, so I'm going to do this the nasty way. */ time_t base; time_t tick; int i; /* Init. */ time (&base); time (&tick); /* Loop until time has passed. */ while (difftime (tick, base) < seconds) { /* This might be more civilized than calling time over and over again. */ for (i = 0; i < 10000; i++) ; time (&tick); } return 0; } #endif /* sleep */