home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume13 / preroff / part01 / preroff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-24  |  17.5 KB  |  822 lines

  1. /* @(#)preroff.c    1.6 90/06/11 */
  2. /* copyright Mike Howard, 1990 - all rights reserved */
  3.  
  4. #ifdef BSD
  5. # ifdef SYSV
  6. this is an error - cannot define both BSD and SYSV
  7. # endif /* SYSV */
  8. #endif /* BSD */
  9.  
  10. #include <stdio.h>
  11. #include <fcntl.h>
  12. #include <signal.h>
  13. #include <ctype.h>
  14. #include <errno.h>
  15. #include <string.h>
  16.  
  17. #ifdef BSD
  18. # include <sys/types.h>
  19. # include <sys/time.h>
  20. #endif /* BSD */
  21.  
  22. char *shell_path = "/bin/sh";
  23. char *shell_name = "sh";
  24. #define SO          ".so "
  25. #define SHELL_START ".shell-start"
  26. #define SHELL_END   ".shell-end"
  27. #define EXIT_PREROFF ".exit-preroff"
  28. #define EXEC_CMD     ".exec-cmd"
  29. #define SO_LEN            4
  30. #define SHELL_START_LEN  12
  31. #define SHELL_END_LEN    10
  32. #define EXIT_PREROFF_LEN 13
  33. #define EXEC_CMD_LEN      9
  34.  
  35.  
  36. #ifdef _NFILE
  37. # define STACK_SIZE _NFILE - 5
  38. #else
  39. # define STACK_SIZE    15
  40. #endif
  41.  
  42. int stack_top;
  43. struct stack_item {
  44.   struct stack_item *next;
  45.   char *file_name;
  46.   int line_number;
  47.   int fd;
  48.   int pid;
  49.   char *buffer;
  50.   char *next_cp;
  51.   char *last_cp;
  52. } stack[STACK_SIZE];
  53.  
  54. int save_fd;
  55. #define current_fname    stack[stack_top].file_name
  56. #define current_line    stack[stack_top].line_number
  57.  
  58. /* pid of current child process */
  59. #define child_pid   stack[stack_top].pid
  60. #define current_pid stack[stack_top - 1].pid
  61.  
  62. #define current_buffer   stack[stack_top].buffer
  63. #define current_next_cp  stack[stack_top].next_cp
  64. #define current_last_cp  stack[stack_top].last_cp
  65.  
  66. #define BUF_SIZE  1024
  67. char buf[BUF_SIZE];
  68.  
  69. int verbose;
  70. int delete_failed_so_flag;
  71. int timeout = 300;    /* everything should send some input within 5 minutes*/
  72.  
  73. /* dirty.c */
  74. #define VOID void /* */
  75. #define VOIDARG void /* */
  76. /* #define VOID int /* */
  77. /* #define VOIDARG  /* */
  78.  
  79. #ifdef __STDC__
  80. int main(int  /* argc */ , char ** /* argv */ );
  81. int open_a_file(char * /* fname */ );
  82. VOID doit_dirty(VOIDARG);
  83. VOID gather_command(VOIDARG);
  84. VOID do_shell_command(VOIDARG);
  85. VOID do_exec_command(VOIDARG);
  86. char *collect_args(char * /* bp */ );
  87. VOID clean_up(int  /* sig */ );
  88. int get_line(VOIDARG);
  89. VOID read_alarm(VOIDARG);
  90. VOID kill_current_child(VOIDARG);
  91. VOID push_name(char * /* fname */ , int  /* fd */ );
  92. VOID pop_name(VOIDARG);
  93. char *Malloc(unsigned  /* size */ );
  94. char *Realloc(char * /* ptr */ , unsigned  /* size */ );
  95. VOID wait_for_child(VOIDARG);
  96. VOID err_msg(char * /* s */ );
  97. #else /* __STDC__ */
  98. int main( /* int argc, char **argv */ );
  99. int open_a_file( /* char *fname */ );
  100. VOID doit_dirty( /* void */ );
  101. VOID gather_command( /* void */ );
  102. VOID do_shell_command( /* void */ );
  103. VOID do_exec_command( /* void */ );
  104. char *collect_args( /* char *bp */ );
  105. VOID clean_up( /* int sig */ );
  106. int get_line( /* void */ );
  107. VOID read_alarm( /* void */ );
  108. VOID kill_current_child( /* void */ );
  109. VOID push_name( /* char *fname, int fd */ );
  110. VOID pop_name( /* void */ );
  111. char *Malloc( /* unsigned size */ );
  112. char *Realloc( /* char *ptr, unsigned size */ );
  113. VOID wait_for_child( /* void */ );
  114. VOID err_msg( /* char *s */ );
  115. #endif /* __STDC__ */
  116.  
  117. char *hlp[] = {
  118. "Option              Effect",
  119. "-v(vvv)             increment verbosity - for debugging documents",
  120. "-t timeout          change timeout value from default (5 minutes)",
  121. "-d                  discard failed .so directives rather than passing",
  122. "                    them thru (default)",
  123. (char *)0};
  124.  
  125. #define USE_MSG  "usage: %s(1.6) [-h | option(s)] [file or '-' ...]\n"
  126.  
  127. main(argc, argv)
  128. int argc;
  129. char **argv;
  130. {
  131.   int i;
  132.   extern int optind, opterr;
  133.   extern char *optarg;
  134.  
  135.   while ((i = getopt(argc, argv, "hdvt:")) != EOF) {
  136.     switch (i) {
  137.     case 'h':
  138.       for (i=0;hlp[i];i++)
  139.     fprintf(stderr, "%s\n", hlp[i]);
  140.       fprintf(stderr, USE_MSG, argv[0]);
  141.       exit(0);
  142.     case 'd':
  143.       delete_failed_so_flag++;
  144.       break;
  145.     case 't':
  146.       if ((timeout = atoi(optarg)) <= 0)
  147.     timeout = 0;
  148.       break;
  149.     case 'v':
  150.       verbose++;
  151.       break;
  152.     case '?':
  153.       fprintf(stderr, "Illegal argument: %c\n", i);
  154.       fprintf(stderr, USE_MSG, argv[0]);
  155.       exit(1);
  156.     }
  157.   }
  158.   
  159.  
  160.   signal(SIGHUP, clean_up);
  161.   signal(SIGINT, clean_up);
  162.   signal(SIGQUIT, clean_up);
  163.   signal(SIGTERM, clean_up);
  164.   stack_top = 0;
  165.   save_fd = dup(0);
  166.  
  167.   if (argc == optind) {
  168.     push_name("stdin", save_fd);
  169.     doit_dirty();
  170.  
  171.     /* return to the prior file */
  172.     pop_name();
  173.     exit(0);
  174.   }
  175.  
  176.   for (i=optind;i<argc;i++) {
  177.     if (!open_a_file(argv[i])) {
  178.       doit_dirty();
  179.       pop_name();
  180.     }
  181.   }
  182.  
  183.   exit(0);
  184. }
  185.  
  186. int open_a_file(fname)
  187. char *fname;
  188. {
  189.   int fd;
  190.  
  191.   if (!strcmp(fname, "-")) {
  192.     if (save_fd >= 0) {
  193.       push_name("stdin", save_fd);
  194.       save_fd = -1;
  195.       return 0;
  196.     }
  197.     else {
  198.       err_msg("Attempt to re-scan stdin");
  199.       dup(stack[stack_top].fd);
  200.       close(stack[stack_top].fd);
  201.       return -1;
  202.     }
  203.   }
  204.   else if ((fd = open(fname, O_RDONLY)) < 0) {
  205.     char err_buf[256];
  206.     sprintf(err_buf, "unable to open %s", fname);
  207.     err_msg(err_buf);
  208.     return -1;
  209.   }
  210.  
  211.   push_name(fname, fd);
  212.  
  213.   return 0;
  214. }
  215.  
  216. VOID doit_dirty()
  217. {
  218.   if (verbose)
  219.     fprintf(stderr, "Starting to process %s\n", current_fname);
  220.  
  221.   fflush(stdout);
  222.   while (get_line() > 0) {
  223.     if (verbose > 1)
  224.       fprintf(stderr, "%s:%d: %s", current_fname, current_line,
  225.           buf);
  226.     if (!strncmp(buf, SO, SO_LEN)) {
  227.       char *fname = &buf[4];
  228.       char *cp;
  229.  
  230.       /* find and bound file name */
  231.       while (isspace(*fname))
  232.     fname++;
  233.       for (cp=fname;!isspace(*cp);cp++)
  234.     ;
  235.       *cp = '\0';
  236.  
  237.       if (!open_a_file(fname)) {
  238.     doit_dirty();
  239.     pop_name();
  240.       }
  241.       else if (!delete_failed_so_flag)
  242.     fputs(buf, stdout);
  243.     }
  244.     else if (!strncmp(buf, SHELL_START, SHELL_START_LEN)) {
  245.       gather_command();
  246.       do_shell_command();
  247.     }
  248.     else if (!strncmp(buf, EXEC_CMD, EXEC_CMD_LEN)) {
  249.       do_exec_command();
  250.     }
  251.     else if (!strncmp(buf, EXIT_PREROFF, EXIT_PREROFF_LEN)) {
  252.       break;
  253.     }
  254.     else
  255.       fputs(buf, stdout);
  256.   }
  257.  
  258.   if (verbose)
  259.     fprintf(stderr, "Finished processing %s\n", current_fname);
  260. }
  261.  
  262. char *tmp_fname;
  263.  
  264. VOID gather_command()
  265. {
  266.   int fd;
  267.  
  268.   /* open a temp file */
  269.   do {
  270.     tmp_fname = tmpnam((char *)0);
  271.   } while ((fd = open(tmp_fname, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0);
  272.  
  273.   if (verbose > 1)
  274.     write(fd, "set -v\n", 7);
  275.   if (verbose > 2)
  276.     write(fd, "set -x\n", 7);
  277.  
  278.   while (get_line() > 0) {
  279.     if (!strncmp(buf, SHELL_END, SHELL_END_LEN))
  280.       break;
  281.  
  282.     write(fd, buf, strlen(buf));
  283.   }
  284.  
  285.   close(fd);
  286. }
  287.  
  288. VOID do_shell_command()
  289. {
  290.   int pipe_fd[2];
  291.   int fd;
  292.  
  293.   if (pipe(pipe_fd) < 0) {
  294.     err_msg("unable to open pipe to command");
  295.     unlink(tmp_fname);
  296.     tmp_fname = (char *)0;
  297.     return;
  298.   }
  299.  
  300.   if (child_pid = fork()) {
  301.     if (verbose)
  302.       fprintf(stderr, "forking child process: %d\n", child_pid);
  303.  
  304.     close(pipe_fd[1]);
  305.     push_name("shell-process", pipe_fd[0]);
  306.     close(pipe_fd[0]);
  307.  
  308.     doit_dirty();
  309.     wait_for_child();
  310.     pop_name();
  311.   }
  312.   else {
  313.     /* direct output to pipe */
  314.     close(1);
  315.     dup(pipe_fd[1]);
  316.     close(pipe_fd[0]);
  317.     close(pipe_fd[1]);
  318.  
  319.     close(0);
  320.     open("/dev/null", O_RDONLY);
  321.  
  322.     execl(shell_path, shell_name, tmp_fname, (char *)0);
  323.     err_msg("exec of shell failed");
  324.   }
  325.  
  326.   unlink(tmp_fname);
  327.   tmp_fname = (char *)0;
  328. }
  329.  
  330. /* the command line(s) are scanned and arguments are found.
  331.    the scanner recognizes '...' and "..." strings - the enclosing
  332.     quote marks are stripped, but no /bin/sh interpretations are performed
  333.    \x escapes are processed by stripping the \ character
  334.    and recognizes \{white-space}*\n as a continuation to the next line
  335. */
  336.  
  337. VOID do_exec_command()
  338. {
  339.   int pipe_fd[2];
  340.   char *args[100];
  341.   int arg_count = 0;
  342.  
  343.   while (args[arg_count] =
  344.      collect_args(arg_count ? (char *)0 : &buf[EXEC_CMD_LEN]))
  345.     arg_count++;
  346.  
  347.   if (!arg_count) {
  348.     err_msg("No arguments for system command");
  349.     return;
  350.   }
  351.  
  352.   if (verbose > 1) {
  353.     int i;
  354.  
  355.     fprintf(stderr, "%s arg dump:\n", EXEC_CMD);
  356.     for (i=0;i<arg_count;i++)
  357.       fprintf(stderr, "arg[%2d]: %s\n", i, args[i]);
  358.   }
  359.  
  360.   if (pipe(pipe_fd) < 0) {
  361.     err_msg("unable to open pipe to command");
  362.     return;
  363.   }
  364.  
  365.   if (child_pid = fork()) {
  366.     if (verbose)
  367.       fprintf(stderr, "forking child process: %d\n", child_pid);
  368.     close(pipe_fd[1]);
  369.     push_name("exec-cmd", pipe_fd[0]);
  370.     close(pipe_fd[0]);
  371.  
  372.     doit_dirty();
  373.     wait_for_child();
  374.     pop_name();
  375.   }
  376.   else {
  377.     int local_errno;
  378.  
  379.     /* direct output to pipe */
  380.     close(1);
  381.     dup(pipe_fd[1]);
  382.     close(pipe_fd[0]);
  383.     close(pipe_fd[1]);
  384.  
  385.     close(0);
  386.     open("/dev/null", O_RDONLY);
  387.  
  388.     execvp(args[0], args);
  389.     local_errno = errno;
  390.     {
  391.       char err_buf[256];
  392.       extern char *sys_errlist[];
  393.  
  394.       sprintf(err_buf, "exec of %s failed\n%d - %s", args[0], local_errno,
  395.           sys_errlist[local_errno]);
  396.       err_msg(err_buf);
  397.     }
  398.     exit(1);
  399.   }
  400.  
  401.   unlink(tmp_fname);
  402.   tmp_fname = (char *)0;
  403. }
  404.  
  405. char *collect_args(bp)
  406. char *bp;
  407. {
  408.   static char *next_char;
  409.   char *base;
  410.   int c;
  411.   int in_quoted_string;
  412.   char *cp;
  413.   int len;
  414.  
  415.   /* initialize next_char pointers on first call */
  416.   if (bp)
  417.     next_char = bp;
  418.  
  419.   /* strip internal white space and test for end of list */
  420.   do {
  421. if (verbose > 2)
  422. fprintf(stderr, "skipping %lx: %c %2x\n", next_char, *next_char, *next_char);
  423.     while (isspace(*next_char))
  424.       next_char++;
  425.     /* if there is no next token then we are done */
  426.     if (!*next_char)
  427.       return (char *)0;
  428.  
  429.     /* if we see \ \n seqence, then this is a continuation line */
  430.     if (next_char[0] == '\\' && next_char[1] == '\n') {
  431.       if (get_line() <= 0)
  432.     return (char *)0;
  433.       next_char = buf;
  434.     }
  435.   } while (isspace(*next_char));
  436.  
  437.   /* allocate sufficient room */
  438.   base =
  439.     cp = Malloc(strlen(next_char));
  440.  
  441.   /* detect quoted string */
  442.   if ((c = *next_char) == '\'' || c == '"') {
  443.     next_char++;
  444.     in_quoted_string = c;
  445.   }
  446.   else
  447.     in_quoted_string = 0;
  448.  
  449.   /* collect this argument */
  450.   while (1) {
  451. if (verbose > 2)
  452. fprintf(stderr, "scanning %lx: %c %2x\n", next_char, *next_char, *next_char);
  453.     switch (c = *next_char++) {
  454.     case '\'':
  455.     case '"':
  456.       if (in_quoted_string) {
  457.     if (c == in_quoted_string)
  458.       in_quoted_string = 0;
  459.     else
  460.       *cp++ = c;
  461.       }
  462.       else
  463.     in_quoted_string = c;
  464.       break;
  465.     case '\\':
  466.       if ((*cp++ = *next_char++) == '\n') {
  467.     cp--;
  468.     if (get_line() <= 0) {
  469.       *cp = '\0';
  470.       return Realloc(base, strlen(base));
  471.     }
  472.     len = cp - base;
  473.     base = Realloc(base, strlen(base) + strlen(buf));
  474.     cp = base + len;
  475.     next_char = buf;
  476.       }
  477.       break;
  478.     case ' ':
  479.     case '\t':
  480.       if (in_quoted_string) {
  481.     *cp++ = c;
  482.       }
  483.       else {
  484.     *cp = '\0';
  485.     return Realloc(base, strlen(base));
  486.       }
  487.       break;
  488.     case '\n':
  489.       if (!in_quoted_string) {
  490.     *cp = '\0';
  491.     return Realloc(base, strlen(base));
  492.       }
  493.       *cp++ = '\n';
  494.       if (get_line() <= 0) {
  495.     *cp = '\0';
  496.     return Realloc(base, strlen(base));
  497.       }
  498.       len = cp - base;
  499.       base = Realloc(base, strlen(base) + strlen(buf));
  500.       cp = base + len;
  501.       next_char = buf;
  502.       break;
  503.     case '\0':
  504.       err_msg("null encountered in .exec-cmd arg list");
  505.       *cp = '\0';
  506.       return Realloc(base, strlen(base));
  507.     default:
  508.       *cp++ = c;
  509.       break;
  510.     }
  511.   }
  512. }
  513.  
  514. VOID clean_up(sig)
  515. int sig;
  516. {
  517.   if (tmp_fname)
  518.     unlink(tmp_fname);
  519.  
  520.   switch (sig) {
  521.   case SIGQUIT:
  522.     abort();
  523.   default:
  524.     exit(sig);
  525.   }
  526. }
  527.  
  528. #ifdef BSD
  529. int get_line()
  530. {
  531.   char *cp = buf;
  532.   char *ncp = current_next_cp;
  533.   char *lcp = current_last_cp;
  534.   int len;
  535.   int local_errno;
  536.   fd_set read_fdset;
  537.   struct timeval select_timeout;
  538.  
  539.   while (1) {
  540.     while (ncp < lcp) {
  541.       if ((*cp++ = *ncp++) == '\n') {
  542.     current_next_cp = ncp;
  543.     current_line++;
  544.     *cp = '\0';
  545.     return cp - buf;
  546.       }
  547.     }
  548.  
  549.     /* test for ready-to-read */
  550.     FD_ZERO(&read_fdset);
  551.     FD_SET(0, &read_fdset);
  552.     select_timeout.tv_sec = timeout;
  553.     select_timeout.tv_usec = 0;
  554.     if (select(1, &read_fdset, (fd_set *)0, (fd_set *)0, &timeout) <= 0) {
  555.       int old_timeout = timeout;
  556.  
  557.       err_msg("Input timeout - probable infinite loop");
  558.       timeout = 5;
  559.       signal(SIGALRM, kill_current_child);
  560.       alarm(timeout);
  561.       kill_current_child();
  562.       wait_for_child();
  563.       timeout = old_timeout;
  564.  
  565.       local_errno = errno;
  566.       alarm(0);
  567.       signal(SIGALRM, SIG_IGN);
  568.     }
  569.  
  570.     /* input ready - so read stdin */
  571.     len = read(0, current_buffer, BUF_SIZE);
  572.  
  573.     /* initialize input buffer pointers */
  574.     ncp =
  575.       current_next_cp = current_buffer;
  576.     lcp =
  577.       current_last_cp = current_buffer + len;
  578.  
  579.     if (len < 0 && local_errno == EINTR) {
  580.       fprintf(stderr, "errno: %d: %s, %d\n", local_errno,
  581.           current_fname, current_line);
  582.       errno = 0;
  583.     }
  584.  
  585.     if (len <= 0) {
  586.       *cp = '\0';
  587.       return cp - buf;
  588.     }
  589.   }
  590. }
  591.  
  592. #else /* BSD */
  593.  
  594. int get_line()
  595. {
  596.   char *cp = buf;
  597.   char *ncp = current_next_cp;
  598.   char *lcp = current_last_cp;
  599.   int len;
  600.   int local_errno;
  601.  
  602.   while (1) {
  603.     while (ncp < lcp) {
  604.       if ((*cp++ = *ncp++) == '\n') {
  605.     current_next_cp = ncp;
  606.     current_line++;
  607.     *cp = '\0';
  608.     return cp - buf;
  609.       }
  610.     }
  611.  
  612.     signal(SIGALRM, read_alarm);
  613.     alarm(timeout);
  614.     len = read(0, current_buffer, BUF_SIZE);
  615.     local_errno = errno;
  616.     alarm(0);
  617.     signal(SIGALRM, SIG_IGN);
  618.  
  619.     ncp =
  620.       current_next_cp = current_buffer;
  621.     lcp =
  622.       current_last_cp = current_buffer + len;
  623.  
  624.     if (len < 0 && local_errno == EINTR) {
  625.       int old_timeout = timeout;
  626.  
  627.       err_msg("Input timeout - probable infinite loop");
  628.       timeout = 5;
  629.       signal(SIGALRM, kill_current_child);
  630.       alarm(timeout);
  631.       kill_current_child();
  632.       wait_for_child();
  633.       timeout = old_timeout;
  634.     }
  635.     if (verbose > 1 && local_errno) {
  636.       fprintf(stderr, "errno: %d: %s, %d\n", local_errno,
  637.           current_fname, current_line);
  638.       errno = 0;
  639.     }
  640.  
  641.     if (len <= 0) {
  642.       *cp = '\0';
  643.       return cp - buf;
  644.     }
  645.   }
  646. }
  647.  
  648. VOID read_alarm()
  649. {
  650.   return;
  651. }
  652. #endif /* BSD */
  653.  
  654. /* this routine attempts to kill `current_pid' with a SIGTERM.  If
  655.    that fails,(causing an alarm signal) it sends a SIGKILL */
  656.  
  657. VOID kill_current_child()
  658. {
  659.   static int last_pid_killed = 0;
  660.  
  661.   if (verbose)
  662.     fprintf(stderr, "Alarm Signal - sending %d to %d\n",
  663.         last_pid_killed == current_pid ? SIGKILL : SIGTERM, current_pid);
  664.   if (current_pid) {
  665.     kill(current_pid, last_pid_killed == current_pid ? SIGKILL : SIGTERM);
  666.     last_pid_killed = current_pid;
  667.   }
  668.   signal(SIGALRM, kill_current_child);
  669.   alarm(timeout);
  670.  
  671.   return;
  672. }
  673.  
  674. VOID push_name(fname, fd)
  675. char *fname;
  676. int fd;
  677. {
  678.   if (stack_top >= STACK_SIZE) {
  679.     char err_buf[80];
  680.  
  681.     sprintf(err_buf, "attempt to open more than %d inputs\n", STACK_SIZE -1);
  682.     err_msg(err_buf);
  683.     return;
  684.   }
  685.  
  686.   /* redirect stdin - saving old file descriptor */
  687.   stack[stack_top].fd = dup(0);
  688.   close(0);
  689.   dup(fd);
  690.   close(fd);
  691.  
  692.   /* push new file name and initialize line counter */
  693.   stack[++stack_top].file_name = strcpy(Malloc(strlen(fname)), fname);
  694.   stack[stack_top].line_number = 0;
  695.   if (!stack[stack_top].buffer)
  696.     stack[stack_top].buffer = Malloc(BUF_SIZE);
  697.   stack[stack_top].next_cp =
  698.     stack[stack_top].last_cp = stack[stack_top].buffer;
  699. }
  700.  
  701. /* pops stack and restores stdin */
  702.  
  703. VOID pop_name()
  704. {
  705.   if (stack_top < 0) {
  706.     err_msg("fatal error - pop_name()\n");
  707.     abort();
  708.   }
  709.   free(stack[stack_top--].file_name);
  710.   close(0);
  711.   if (stack_top >= 0) {
  712.     dup(stack[stack_top].fd);
  713.     close(stack[stack_top].fd);
  714.   }
  715. }
  716.  
  717. /* like malloc EXCEPT that it abort()s if malloc() fails and it allocates
  718.    size+1 bytes */
  719.  
  720. char *Malloc(size)
  721. unsigned size;
  722. {
  723.   char *malloc();
  724.   char *ret;
  725.  
  726.   if (ret = malloc(++size))
  727.     return ret;
  728.  
  729.   err_msg("Memory Allocation Failure in Malloc");
  730.   abort();
  731. }
  732.  
  733. /* like realloc EXCEPT that it abort()s if malloc() fails and it allocates
  734.    size+1 bytes */
  735.  
  736. char *Realloc(ptr, size)
  737. char *ptr;
  738. unsigned size;
  739. {
  740.   char *realloc();
  741.   char *ret;
  742.  
  743.   if (ret = realloc(ptr, ++size))
  744.     return ret;
  745.  
  746.   err_msg("Memory Allocation Failure in Realloc");
  747.   abort();
  748. }
  749.  
  750. /* This is messy - I'm not all that sure I trust it.
  751.  
  752.    The hair comes in when we have a couple of child processes running.
  753.    Exprience says that they may return their pid's to the wait() call
  754.    out of order, so we have to test all valid wait returns to see
  755.    if they were some of our children and clear their slot in the stack.
  756.    Also, wait_for_child() has to make sure that the child it is waiting
  757.    for has not already gone away.
  758.  
  759.    for the curious: this sort of thing happens when a child process
  760.    produces output which contains .shell-start/end or .exec-cmd directives:
  761.      the first child spits out the output and then dies.
  762.      preroff reads the stream and then fires up a new child to process
  763.      the directive - the first child's output is still in the pipe.
  764.      the second child croaks and the doit_dirty() exits - which starts
  765.      the wait_for_child() loop - which receives the pid's of the dead
  766.      processes in the order they died, but not in the order of completing
  767.      processing their output.
  768. */
  769.  
  770. VOID wait_for_child()
  771. {
  772.   int wait_ret;
  773.   int status;
  774.   int counter = 0;
  775.   int i;
  776.  
  777.   if (!current_pid)
  778.     return;
  779.  
  780.   signal(SIGALRM, kill_current_child);
  781.   alarm(timeout);
  782.   while (1) {
  783.     wait_ret = wait(&status);
  784.     if (verbose)
  785.       fprintf(stderr, "wait_for_child: wait returned %d\n", wait_ret);
  786.     if (wait_ret == current_pid) {
  787.       current_pid = 0;
  788.       break;
  789.     }
  790.     if (wait_ret > 0)
  791.       for (i=0;i<stack_top;i++) {
  792.     if (wait_ret == stack[i].pid) {
  793.       stack[i].pid = 0;
  794.       break;
  795.     }
  796.       }
  797.       
  798.     
  799.     /* if it was an interupt, then assume it was an alarm */
  800.     if (wait_ret == -1 && counter++ > 2 && errno == EINTR)
  801.       break;
  802.  
  803.     if (counter > 4)
  804.       break;
  805.   }
  806.  
  807.   signal(SIGALRM, SIG_IGN);
  808.   alarm(0);
  809. }
  810.  
  811. VOID err_msg(s)
  812. char *s;
  813. {
  814.   int i;
  815.  
  816.   fprintf(stderr, "%s\nStack Trace\n", s ? s : "Error Detected");
  817.  
  818.   for (i=stack_top;i>0;i--)
  819.     fprintf(stderr, "line %4d: %s \n", stack[i].line_number,
  820.         stack[i].file_name);
  821. }
  822.