summaryrefslogtreecommitdiff
path: root/os2/run.c
diff options
context:
space:
mode:
Diffstat (limited to 'os2/run.c')
-rw-r--r--os2/run.c611
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 */