revert most files from origtgz deleted in MirBSD base to origtgz
[alioth/cvs.git] / os2 / run.c
1 /* run.c --- routines for executing subprocesses under OS/2.
2    
3    This file is part of GNU CVS.
4
5    GNU CVS is free software; you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by the
7    Free Software Foundation; either version 2, or (at your option) any
8    later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.  */
14
15 #include "cvs.h"
16
17 #include "os2inc.h"
18
19 #include <process.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <io.h>
29
30 #define STDIN       0
31 #define STDOUT      1
32 #define STDERR      2
33
34 static void run_add_arg( const char *s );
35 static void run_init_prog( void );
36
37 extern char *strtok ();
38
39 /*
40  * To exec a program under CVS, first call run_setup() to setup any initial
41  * arguments.  The options to run_setup are essentially like printf(). The
42  * arguments will be parsed into whitespace separated words and added to the
43  * global run_argv list.
44  * 
45  * Then, optionally call run_arg() for each additional argument that you'd like
46  * to pass to the executed program.
47  * 
48  * Finally, call run_exec() to execute the program with the specified
49  * arguments. 
50  * The execvp() syscall will be used, so that the PATH is searched correctly.
51  * File redirections can be performed in the call to run_exec().
52  */
53 static char **run_argv;
54 static int run_argc;
55 static int run_argc_allocated;
56
57 void 
58 run_setup (const char *prog)
59 {
60     int i;
61     char *run_prog;
62     char *buf, *d, *s;
63     size_t length;
64     size_t doff;
65     char inquotes;
66     int dolastarg;
67
68     /* clean out any malloc'ed values from run_argv */
69     for (i = 0; i < run_argc; i++)
70     {
71         if (run_argv[i])
72         {
73             free (run_argv[i]);
74             run_argv[i] = (char *) 0;
75         }
76     }
77     run_argc = 0;
78
79     run_prog = xstrdup (prog);
80
81     s = run_prog;
82     d = buf = NULL;
83     length = 0;
84     dolastarg = 1;
85     inquotes = '\0';
86     doff = d - buf;
87     expand_string(&buf, &length, doff + 1);
88     d = buf + doff;
89     while (*d = *s++)
90     {
91         switch (*d)
92         {
93             case '\\':
94                 if (*s) *d = *s++;
95                 d++;
96                 break;
97             case '"':
98             case '\'':
99                 if (inquotes == *d) inquotes = '\0';
100                 else inquotes = *d;
101                 break;
102             case ' ':
103             case '\t':
104                 if (inquotes) d++;
105                 else
106                 {
107                     *d = '\0';
108                     run_add_arg (buf);
109                     d = buf;
110                     while (isspace(*s)) s++;
111                     if (!*s) dolastarg = 0;
112                 }
113                 break;
114             default:
115                 d++;
116                 break;
117         }
118         doff = d - buf;
119         expand_string(&buf, &length, doff + 1);
120         d = buf + doff;
121     }
122     if (dolastarg) run_add_arg (buf);
123     /* put each word into run_argv, allocating it as we go */
124     if (buf) free (buf);
125     free (run_prog);
126
127     free (run_prog);
128     if (buf) free (buf);
129 }
130
131 void
132 run_arg (s)
133     const char *s;
134 {
135     run_add_arg (s);
136 }
137
138 /* Return a malloc'd copy of s, with double quotes around it.  */
139 /* FIXME - this should replace " with \" as it copies.  or something.
140  * depends where it's used, I would suppose.
141  */
142 static char *
143 quote (const char *s)
144 {
145     size_t s_len = strlen (s);
146     char *copy = xmalloc (s_len + 3);
147     char *scan = copy;
148
149     *scan++ = '"';
150     strcpy (scan, s);
151     scan += s_len;
152     *scan++ = '"';
153     *scan++ = '\0';
154
155     return copy;
156 }
157
158 static void
159 run_add_arg (s)
160     const char *s;
161 {
162     /* allocate more argv entries if we've run out */
163     if (run_argc >= run_argc_allocated)
164     {
165         run_argc_allocated += 50;
166         run_argv = (char **) xrealloc ((char *) run_argv,
167                                      run_argc_allocated * sizeof (char **));
168     }
169
170     if (s)
171     {
172         run_argv[run_argc] = (run_argc ? quote (s) : xstrdup (s));
173         run_argc++;
174     }
175     else
176         /* not post-incremented on purpose! */
177         run_argv[run_argc] = (char *) 0;
178 }
179
180 int
181 run_exec (stin, stout, sterr, flags)
182     char *stin;
183     char *stout;
184     char *sterr;
185     int flags;
186 {
187     int shin, shout, sherr;
188     int sain, saout, saerr;     /* saved handles */
189     int mode_out, mode_err;
190     int status = -1;
191     int rerrno = 0;
192     int rval   = -1;
193     void (*old_sigint) (int);
194
195     if (trace)                  /* if in trace mode */
196     {
197         (void) fprintf (stderr, "-> system(");
198         run_print (stderr);
199         (void) fprintf (stderr, ")\n");
200     }
201     if (noexec && (flags & RUN_REALLY) == 0) /* if in noexec mode */
202         return (0);
203
204     /*
205      * start the engine and take off
206      */
207
208     /* make sure that we are null terminated, since we didn't calloc */
209     run_add_arg ((char *) 0);
210
211     /* setup default file descriptor numbers */
212     shin = 0;
213     shout = 1;
214     sherr = 2;
215
216     /* set the file modes for stdout and stderr */
217     mode_out = mode_err = O_WRONLY | O_CREAT;
218     mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
219     mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
220
221     /* open the files as required, shXX are shadows of stdin... */
222     if (stin && (shin = open (stin, O_RDONLY)) == -1)
223     {
224         rerrno = errno;
225         error (0, errno, "cannot open %s for reading (prog %s)",
226                stin, run_argv[0]);
227         goto out0;
228     }
229     if (stout && (shout = open (stout, mode_out, 0666)) == -1)
230     {
231         rerrno = errno;
232         error (0, errno, "cannot open %s for writing (prog %s)",
233                stout, run_argv[0]);
234         goto out1;
235     }
236     if (sterr && (flags & RUN_COMBINED) == 0)
237     {
238         if ((sherr = open (sterr, mode_err, 0666)) == -1)
239         {
240             rerrno = errno;
241             error (0, errno, "cannot open %s for writing (prog %s)",
242                    sterr, run_argv[0]);
243             goto out2;
244         }
245     }
246     /* now save the standard handles */
247     sain = saout = saerr = -1;
248     sain  = dup( 0); /* dup stdin  */
249     saout = dup( 1); /* dup stdout */
250     saerr = dup( 2); /* dup stderr */
251
252     /* the new handles will be dup'd to the standard handles
253      * for the spawn.
254      */
255
256     if (shin != 0)
257       {
258         (void) dup2 (shin, 0);
259         (void) close (shin);
260       }
261     if (shout != 1)
262       {
263         (void) dup2 (shout, 1);
264         (void) close (shout);
265       }
266     if (flags & RUN_COMBINED)
267       (void) dup2 (1, 2);
268     else if (sherr != 2)
269       {
270         (void) dup2 (sherr, 2);
271         (void) close (sherr);
272       }
273
274     /* Ignore signals while we're running this.  */
275     old_sigint = signal (SIGINT, SIG_IGN);
276
277     /* dup'ing is done.  try to run it now */
278     rval = spawnvp ( P_WAIT, run_argv[0], run_argv);
279
280     /* Restore signal handling.  */
281     signal (SIGINT, old_sigint);
282
283     /* restore the original file handles   */
284     if (sain  != -1) {
285       (void) dup2( sain, 0);    /* re-connect stdin  */
286       (void) close( sain);
287     }
288     if (saout != -1) {
289       (void) dup2( saout, 1);   /* re-connect stdout */
290       (void) close( saout);
291     }
292     if (saerr != -1) {
293       (void) dup2( saerr, 2);   /* re-connect stderr */
294       (void) close( saerr);
295     }
296
297     /* Recognize the return code for a failed subprocess.  */
298     if (rval == -1)
299         return 2;
300     else
301         return rval;            /* return child's exit status */
302
303     /* error cases */
304     /* cleanup the open file descriptors */
305   out2:
306     if (stout)
307         (void) close (shout);
308   out1:
309     if (stin)
310         (void) close (shin);
311
312   out0:
313     if (rerrno)
314         errno = rerrno;
315     return (status);
316 }
317
318
319 void
320 run_print (fp)
321     FILE *fp;
322 {
323     int i;
324
325     for (i = 0; i < run_argc; i++)
326     {
327         (void) fprintf (fp, "'%s'", run_argv[i]);
328         if (i != run_argc - 1)
329             (void) fprintf (fp, " ");
330     }
331 }
332
333 static char *
334 requote (const char *cmd)
335 {
336     char *requoted = xmalloc (strlen (cmd) + 1);
337     char *p = requoted;
338
339     strcpy (requoted, cmd);
340     while ((p = strchr (p, '\'')) != NULL)
341     {
342         *p++ = '"';
343     }
344
345     return requoted;
346 }
347
348 FILE *
349 run_popen (cmd, mode)
350     const char *cmd;
351     const char *mode;
352 {
353     TRACE( TRACE_FUNCTION, "run_popen(%s,%s)", cmd, mode );
354
355     if (noexec)
356         return (NULL);
357
358     /* If the command string uses single quotes, turn them into
359        double quotes.  */
360     {
361         char *requoted = requote (cmd);
362         TRACE( TRACE_DATA, "Executing popen(%s,%s)", requoted, mode );
363         FILE *result = popen (requoted, mode);
364         free (requoted);
365         return result;
366     }
367 }
368
369
370 /* Running children with pipes connected to them.  */
371
372 /* Create a pipe.  Set READWRITE[0] to its reading end, and 
373    READWRITE[1] to its writing end.  */
374
375 static int
376 my_pipe (int *readwrite)
377 {
378     fprintf (stderr,
379              "Error: my_pipe() is unimplemented.\n");
380     exit (1);
381 }
382
383
384 /* Create a child process running COMMAND with IN as its standard input,
385    and OUT as its standard output.  Return a handle to the child, or
386    INVALID_HANDLE_VALUE.  */
387 static int
388 start_child (char *command, int in, int out)
389 {
390     fprintf (stderr,
391              "Error: start_child() is unimplemented.\n");
392     exit (1);
393 }
394
395
396 /* Given an array of arguments that one might pass to spawnv,
397    construct a command line that one might pass to CreateProcess.
398    Try to quote things appropriately.  */
399 static char *
400 build_command (char **argv)
401 {
402     int len;
403
404     /* Compute the total length the command will have.  */
405     {
406         int i;
407
408         len = 0;
409         for (i = 0; argv[i]; i++)
410         {
411             char *p;
412
413             len += 2;  /* for the double quotes */
414
415             for (p = argv[i]; *p; p++)
416             {
417                 if (*p == '"')
418                     len += 2;
419                 else
420                     len++;
421             }
422         }
423         len++;  /* for the space or the '\0'  */
424     }
425
426     {
427         char *command = (char *) xmalloc (len);
428         int i;
429         char *p;
430
431         if (! command)
432         {
433             errno = ENOMEM;
434             return command;
435         }
436
437         p = command;
438         /* copy each element of argv to command, putting each command
439            in double quotes, and backslashing any quotes that appear
440            within an argument.  */
441         for (i = 0; argv[i]; i++)
442         {
443             char *a;
444             *p++ = '"';
445             for (a = argv[i]; *a; a++)
446             {
447                 if (*a == '"')
448                     *p++ = '\\', *p++ = '"';
449                 else
450                     *p++ = *a;
451             }
452             *p++ = '"';
453             *p++ = ' ';
454         }
455         p[-1] = '\0';
456
457         return command;
458     }
459 }
460
461
462 /* Create an asynchronous child process executing ARGV,
463    with its standard input and output connected to the 
464    parent with pipes.  Set *TO to the file descriptor on
465    which one writes data for the child; set *FROM to
466    the file descriptor from which one reads data from the child.
467    Return the handle of the child process (this is what
468    _cwait and waitpid expect).  */
469 int
470 piped_child (char **argv, int *to, int *from)
471 {
472     fprintf (stderr,
473              "Error: piped_child() is unimplemented.\n");
474     exit (1);
475 }
476
477 /*
478  * dir = 0 : main proc writes to new proc, which writes to oldfd
479  * dir = 1 : main proc reads from new proc, which reads from oldfd
480  *
481  * If this returns at all, then it was successful and the return value
482  * is a file descriptor; else it errors and exits.
483  */
484 int
485 filter_stream_through_program (int oldfd, int dir,
486                            char **prog, int *pidp)
487 {
488         int newfd;  /* Gets set to one end of the pipe and returned. */
489     HFILE from, to;
490         HFILE Old0 = -1, Old1 = -1, Old2 = -1, Tmp;
491
492     if (DosCreatePipe (&from, &to, 4096))
493         return FALSE;
494
495     /* Save std{in,out,err} */
496     DosDupHandle (STDIN, &Old0);
497     DosSetFHState (Old1, OPEN_FLAGS_NOINHERIT);
498     DosDupHandle (STDOUT, &Old1);
499     DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
500     DosDupHandle (STDERR, &Old2);
501     DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
502
503     /* Redirect std{in,out,err} */
504         if (dir)    /* Who goes where? */
505         {
506                 Tmp = STDIN;
507                 DosDupHandle (oldfd, &Tmp);
508                 Tmp = STDOUT;
509                 DosDupHandle (to, &Tmp);
510                 Tmp = STDERR;
511                 DosDupHandle (to, &Tmp);
512
513                 newfd = from;
514                 _setmode (newfd, O_BINARY);
515
516                 DosClose (oldfd);
517                 DosClose (to);
518                 DosSetFHState (from, OPEN_FLAGS_NOINHERIT);
519         }
520         else
521         {
522                 Tmp = STDIN;
523                 DosDupHandle (from, &Tmp);
524                 Tmp = STDOUT;
525                 DosDupHandle (oldfd, &Tmp);
526                 Tmp = STDERR;
527                 DosDupHandle (oldfd, &Tmp);
528
529                 newfd = to;
530                 _setmode (newfd, O_BINARY);
531
532                 DosClose (oldfd);
533                 DosClose (from);
534                 DosSetFHState (to, OPEN_FLAGS_NOINHERIT);
535         }
536
537     /* Spawn we now our hoary brood. */
538         *pidp = spawnvp (P_NOWAIT, prog[0], prog);
539
540     /* Restore std{in,out,err} */
541     Tmp = STDIN;
542     DosDupHandle (Old0, &Tmp);
543     DosClose (Old0);
544     Tmp = STDOUT;
545     DosDupHandle (Old1, &Tmp);
546     DosClose (Old1);
547     Tmp = STDERR;
548     DosDupHandle (Old2, &Tmp);
549     DosClose (Old2);
550
551     if(*pidp < 0) 
552     {
553         DosClose (from);
554         DosClose (to);
555         error (1, 0, "error spawning %s", prog[0]);
556     }
557
558     return newfd;
559 }
560
561
562 int
563 pipe (int *filedesc)
564 {
565   /* todo: actually, we can use DosCreatePipe().  Fix this. */
566   fprintf (stderr,
567            "Error: pipe() should not have been called in client.\n");
568   exit (1);
569 }
570
571
572 void
573 close_on_exec (int fd)
574 {
575   /* Just does nothing for now... */
576
577   /* Actually, we probably *can* implement this one.  Let's see... */
578   /* Nope.  OS/2 has <fcntl.h>, but no fcntl() !  Wow. */
579   /* Well, I'll leave this stuff in for future reference. */
580 }
581
582
583 /* Actually, we #define sleep() in config.h now. */
584 #ifndef sleep
585 unsigned int
586 sleep (unsigned int seconds)
587 {
588   /* I don't want to interfere with alarm signals, so I'm going to do
589      this the nasty way. */
590
591   time_t base;
592   time_t tick;
593   int i;
594
595   /* Init. */
596   time (&base);
597   time (&tick);
598
599   /* Loop until time has passed. */
600   while (difftime (tick, base) < seconds)
601     {
602       /* This might be more civilized than calling time over and over
603          again. */
604       for (i = 0; i < 10000; i++)
605         ;
606       time (&tick);
607     }
608
609   return 0;
610 }
611 #endif /* sleep */