/* fx.c 2.9.0 92/07/06 - the simple fork-and-exec function used in mff.c * * The only f&x needed is calling up METAFONT and GFtoPK, * and these programs take only one argument. It is convenient * to allow the templates given to include spaces, e.g., * `-M "jobserver -q cmmf"' actually runs `jobserver'. * * I have ignored the "struct wait" nonsense -- Edition 7 all the way! :-) * * ATARI ST (and presumablu MS-DOS?) modification -- pdc 25 Jul 91 * There is no fork-&-exec in TOS; Use spawnvp instead. * ----------------------------------------------------------------------- This software module copyright (c) 1990, 1991 Damian Cugley. It is provided for free on an "as-is" basis. See the file COPYING for more information. ----------------------------------------------------------------------- * Created - pdc Mon 15 Jan 1990 */ #include "config.h" #ifndef NO_EXEC_USE_SPAWN # include /* NSIG */ #else # include /* P_WAIT */ #endif /* no_exec_use_spawn */ #include #include "stdc.h" #include "xstdio.h" /* plus prototypes */ #include "fatal.h" /* how to error messages */ #include "strmisc.h" /* incl. */ #include "fx.h" NONRETURNING void exit ARGS((int)); #define TRUE 1 #define FALSE 0 #ifndef lint addr realloc ARGS((addr, sizeof_t)); #else # define realloc(P,S) 0 #endif #define MORE 16 /* * generate more argv slots in batches of 16 * (I will be surprised if 16 are needed!) */ /* * Construct an argv vector for the child process. * * |command| is pointer to zero-terminated string of characters; * it is broken up as per strword(3) into separate arguments. * |arg| is a final argument to append to the |argv| vector; * it is *not* broken into words. */ static inline char ** construct_args(command, argc, argv) int argc; char *command, **argv; { int num_args = 0, max_args = MORE + argc; char **nargv = (char **)xmalloc(max_args * sizeof(char *)), *p, *strword_state; if (!(p = strword(command, &strword_state))) syntaxf("command string is empty!"); /* There should always be at least one word */ do { if (max_args - num_args == 1 + argc) /* always two spare arg slots */ { /* try to make some more room */ nargv = (char **) xrealloc(nargv, (max_args += MORE) * sizeof (char *)); } nargv[num_args++] = p; p = strword((char *)NULL, &strword_state); } while (p); /* There are always two free slots in the |nargv| array: */ while (argc-- > 0) nargv[num_args++] = *argv++; nargv[num_args] = (char *)NULL; return nargv; } /* * fx -- run a subprocess and wait for it to finish */ #ifdef NO_EXEC_USE_SPAWN void fx(char *command, int argc, char **argv) { char **argv = construct_args(command, argc, argv); int exit_code = spawnvp(P_WAIT, argv[0], argv); xfree((addr)argv); if (exit_code < 0) /* I am guessing positive exit codes are OK */ fatalf("%s terminated with exit code %d\n", command, exit_code); } #else /* EXEC is available: */ #ifdef BSD # ifndef vfork # define fork vfork /* * use vfork in preference to fork -- unless vfork has been #defined * to fork (which might be expected on post-VM-problems UNIXes */ # endif #endif int wait ARGS((int *)); int fork ARGS((void)); int execvp ARGS((const char *, const char **)); void NONRETURNING _exit ARGS((int)); #ifdef NO_SIGLIST /* * Must supply own list of signal names: */ static const char *sys_siglist[] = { "shookum hip RALLO dine!", /* there is no signal 0 */ "hangup", "interrupt", "quit", "illegal instruction", "trace trap", /* 5 */ "IOT Trap", "EMT trap", "arithmetic exception", "kill -KILL", "bus error", /* 10 */ "violation of segments", "bad argument to system call", "unrequited pipe", "alarm clock", "software terminate -- from kill(2)", /* 15 */ /* that was the Edition 7 signals (0-15)... */ #ifdef USG "user-defined signal number 1", /* 16 */ "user-defined signal number 2", /* 17 */ "lonely child process", /* 18 */ "death! death! death!", /* 19 -- power failure */ /* * For other systems (4.1 BSD?), signals > 15 * will be reported numerically. */ #endif /* USG */ }; /* define NSIG to be # of messages in above: */ # undef NSIG # define NSIG ((sizeof sys_siglist)/sizeof (char *)) #else /* have siglist */ extern const char *sys_siglist[]; #endif extern char *sys_errlist[]; extern int sys_nerr; /* * classic fork and exec -- |command| and |arg| as above. * Waits for child to finish. */ void /* aborts if can't exec */ fx(command, argc, argv) int argc; char *command, **argv; { int pid, t; int status, /* status word from wait() */ exit_code; /* exit code of child */ argv = construct_args(command, argc, argv); if ((pid = fork()) < 0) pfatalf("fork"); else if (pid == 0) { /* This is the child */ (void)execvp((const char *)argv[0], (const char **)argv); /* If we get here, something's gone wrong: */ perrorf(argv[0]); _exit(1); /* don't use exit() in child */ } /* This is the parent process */ #ifdef BSD t = wait(&status); #else while ((t = wait(&status)) < 0 && errno == EINTR) ; #endif if (t != pid) pfatalf("very surprised because wait returned %d", t); xfree((addr)argv); if ((status & 0xFF) != 0) { /* Terminated by signal: */ int sig = status & 0x7F; fprintf(stderr, "%s killed by signal %d", command, sig); if (sig < NSIG) fprintf(stderr, " (%s)\n", sys_siglist[sig]); if (status & 0x80) fprintf(stderr, "Wow, coredump city.\n"); exit(1); } else if (exit_code = status >> 8 & 0xFF) { fprintf(stderr, "%s terminated with exit code %d.\n", command, exit_code); exit(1); } } #ifdef NO_EXECVP /* * This is intended as a version of execvp for systems that don't have it. * (V7, BSD 4.2?) * * If an executable file is found for which execv() fails, * it is assumed to be a shellscript and /bin/sh is invoked with * its full pathname as first argument. * * pdc Thur. 27 June 1991 */ #include #ifndef MAXPATHLEN # define MAXPATHLEN 1024 #endif #include "strmisc.h" #include "searchpath.h" /* might as well use that */ int /* returns -1 if fails */ execvp(progname, argv) const char *progname; /* name of prog to search for */ const char *argv[]; /* vector of arguments */ { const char *getenv ARGS((const char *)); int execv ARGS((const char *, const char *[])); char scratch[MAXPATHLEN]; const char *path = getenv("PATH"); if (!path) path = ""; /* for mad people who like PATH-free shells */ if (findfile(scratch, progname, path, (char *)0)) { if (execv(scratch, argv), errno == ENOEXEC) { /* * a /bin/sh command file * Substitute pathname of file as 1st argument to sh: * execvp("fo", {"fo","arg","brg",0}) -> * /bin/sh /usr/local/bin/fo arg brg */ const char **nargv; /* ptr to vect of ptrs to vects of const char */ int argc; /* * count args: */ { register const char **t = argv; while (*t) ++t; argc = (t - argv); } /* allocate room for 1 more arg & the NULL */ /* copy the NULL and the args except the 0'th arg */ nargv = (const char **)xmalloc(sizeof (char *) * (argc + 2)); bcopy((addr)(argv + 1), (addr)(nargv + 2), argc*(sizeof (char *))); nargv[0] = "/bin/sh"; nargv[1] = scratch; execv(nargv[0], nargv); /* failed -- */ xfree((addr)nargv); } } else { /* findfile returned FALSE */ errno = ENOENT; } return -1; } #endif /* NO_EXECVP */ #endif /* NO_EXEC_USE_SPAWN */