diff options
Diffstat (limited to 'os2/run.c')
-rw-r--r-- | os2/run.c | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/os2/run.c b/os2/run.c new file mode 100644 index 0000000..6a4179b --- /dev/null +++ b/os2/run.c @@ -0,0 +1,611 @@ +/* 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 <process.h> + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <io.h> + +#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 <fcntl.h>, 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 */ |