[BACK]Return to job.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

Diff for /src/usr.bin/make/job.c between version 1.65 and 1.66

version 1.65, 2007/09/16 12:01:11 version 1.66, 2007/09/16 15:12:12
Line 525 
Line 525 
 JobCondPassSig(void *jobp,      /* Job to biff */  JobCondPassSig(void *jobp,      /* Job to biff */
     void *signop)               /* Signal to send it */      void *signop)               /* Signal to send it */
 {  {
     Job *job = (Job *)jobp;          Job *job = (Job *)jobp;
     int signo = *(int *)signop;          int signo = *(int *)signop;
     if (DEBUG(JOB)) {          if (DEBUG(JOB)) {
         (void)fprintf(stdout,                  (void)fprintf(stdout,
                        "JobCondPassSig passing signal %d to child %ld.\n",                      "JobCondPassSig passing signal %d to child %ld.\n",
                        signo, (long)job->pid);                      signo, (long)job->pid);
         (void)fflush(stdout);                  (void)fflush(stdout);
     }          }
     KILL(job->pid, signo);          KILL(job->pid, signo);
 }  }
   
 /*-  /*-
Line 549 
Line 549 
 static void  static void
 JobPassSig(int signo) /* The signal number we've received */  JobPassSig(int signo) /* The signal number we've received */
 {  {
     sigset_t nmask, omask;          sigset_t nmask, omask;
     struct sigaction act;          struct sigaction act;
   
     if (DEBUG(JOB)) {          if (DEBUG(JOB)) {
         (void)fprintf(stdout, "JobPassSig(%d) called.\n", signo);                  (void)fprintf(stdout, "JobPassSig(%d) called.\n", signo);
         (void)fflush(stdout);                  (void)fflush(stdout);
     }          }
     Lst_ForEach(&jobs, JobCondPassSig, &signo);          Lst_ForEach(&jobs, JobCondPassSig, &signo);
   
     /*          /*
      * Deal with proper cleanup based on the signal received. We only run           * Deal with proper cleanup based on the signal received. We only run
      * the .INTERRUPT target if the signal was in fact an interrupt. The other           * the .INTERRUPT target if the signal was in fact an interrupt. The
      * three termination signals are more of a "get out *now*" command.           * other three termination signals are more of a "get out *now*"
      */           * command.
     if (signo == SIGINT) {           */
         JobInterrupt(true, signo);          if (signo == SIGINT) {
     } else if (signo == SIGHUP || signo == SIGTERM || signo == SIGQUIT) {                  JobInterrupt(true, signo);
         JobInterrupt(false, signo);          } else if (signo == SIGHUP || signo == SIGTERM || signo == SIGQUIT) {
     }                  JobInterrupt(false, signo);
           }
   
     /*          /*
      * Leave gracefully if SIGQUIT, rather than core dumping.           * Leave gracefully if SIGQUIT, rather than core dumping.
      */           */
     if (signo == SIGQUIT) {          if (signo == SIGQUIT) {
         Finish(0);                  Finish(0);
     }          }
   
     /*          /*
      * Send ourselves the signal now we've given the message to everyone else.           * Send ourselves the signal now we've given the message to everyone
      * Note we block everything else possible while we're getting the signal.           * else.  Note we block everything else possible while we're getting
      * This ensures that all our jobs get continued when we wake up before           * the signal.  This ensures that all our jobs get continued when we
      * we take any other signal.           * wake up before we take any other signal.
      */           */
     sigemptyset(&nmask);          sigemptyset(&nmask);
     sigaddset(&nmask, signo);          sigaddset(&nmask, signo);
     sigprocmask(SIG_SETMASK, &nmask, &omask);          sigprocmask(SIG_SETMASK, &nmask, &omask);
     memset(&act, 0, sizeof act);          memset(&act, 0, sizeof act);
     act.sa_handler = SIG_DFL;          act.sa_handler = SIG_DFL;
     sigemptyset(&act.sa_mask);          sigemptyset(&act.sa_mask);
     act.sa_flags = 0;          act.sa_flags = 0;
     sigaction(signo, &act, NULL);          sigaction(signo, &act, NULL);
   
     if (DEBUG(JOB)) {          if (DEBUG(JOB)) {
         (void)fprintf(stdout,                  (void)fprintf(stdout,
                        "JobPassSig passing signal to self, mask = %x.\n",                      "JobPassSig passing signal to self, mask = %x.\n",
                        ~0 & ~(1 << (signo-1)));                      ~0 & ~(1 << (signo-1)));
         (void)fflush(stdout);                  (void)fflush(stdout);
     }          }
     (void)signal(signo, SIG_DFL);          (void)signal(signo, SIG_DFL);
   
     (void)KILL(getpid(), signo);          (void)KILL(getpid(), signo);
   
     signo = SIGCONT;          signo = SIGCONT;
     Lst_ForEach(&jobs, JobCondPassSig, &signo);          Lst_ForEach(&jobs, JobCondPassSig, &signo);
   
     (void)sigprocmask(SIG_SETMASK, &omask, NULL);          (void)sigprocmask(SIG_SETMASK, &omask, NULL);
     sigprocmask(SIG_SETMASK, &omask, NULL);          sigprocmask(SIG_SETMASK, &omask, NULL);
     act.sa_handler = SigHandler;          act.sa_handler = SigHandler;
     sigaction(signo, &act, NULL);          sigaction(signo, &act, NULL);
 }  }
   
 /*-  /*-
Line 625 
Line 626 
 JobCmpPid(void *job,    /* job to examine */  JobCmpPid(void *job,    /* job to examine */
     void *pid)          /* process id desired */      void *pid)          /* process id desired */
 {  {
     return *(pid_t *)pid - ((Job *)job)->pid;          return *(pid_t *)pid - ((Job *)job)->pid;
 }  }
   
 /*-  /*-
Line 659 
Line 660 
 JobPrintCommand(LstNode cmdNode,    /* command string to print */  JobPrintCommand(LstNode cmdNode,    /* command string to print */
     void *jobp)                     /* job for which to print it */      void *jobp)                     /* job for which to print it */
 {  {
     bool          noSpecials;       /* true if we shouldn't worry about          bool noSpecials;        /* true if we shouldn't worry about
                                      * inserting special commands into                                   * inserting special commands into
                                      * the input stream. */                                   * the input stream. */
     bool          shutUp = false;   /* true if we put a no echo command          bool shutUp = false;    /* true if we put a no echo command
                                      * into the command file */                                   * into the command file */
     bool          errOff = false;   /* true if we turned error checking          bool errOff = false;    /* true if we turned error checking
                                      * off before printing the command                                   * off before printing the command
                                      * and need to turn it back on */                                   * and need to turn it back on */
     char          *cmdTemplate;     /* Template to use when printing the          char *cmdTemplate;      /* Template to use when printing the
                                      * command */                                   * command */
     char          *cmdStart;        /* Start of expanded command */          char *cmdStart;         /* Start of expanded command */
     char          *cmd = (char *)Lst_Datum(cmdNode);          char *cmd = (char *)Lst_Datum(cmdNode);
     Job           *job = (Job *)jobp;          Job *job = (Job *)jobp;
   
     noSpecials = (noExecute && !(job->node->type & OP_MAKE));          noSpecials = (noExecute && !(job->node->type & OP_MAKE));
   
     if (strcmp(cmd, "...") == 0) {          if (strcmp(cmd, "...") == 0) {
         job->node->type |= OP_SAVE_CMDS;                  job->node->type |= OP_SAVE_CMDS;
         if ((job->flags & JOB_IGNDOTS) == 0) {                  if ((job->flags & JOB_IGNDOTS) == 0) {
             job->tailCmds = Lst_Succ(cmdNode);                          job->tailCmds = Lst_Succ(cmdNode);
             return 0;                          return 0;
                   }
                   return 1;
         }          }
         return 1;  
     }  
   
 #define DBPRINTF(fmt, arg) if (DEBUG(JOB)) {    \  #define DBPRINTF(fmt, arg) if (DEBUG(JOB)) {    \
         (void)fprintf(stdout, fmt, arg);        \          (void)fprintf(stdout, fmt, arg);        \
Line 691 
Line 692 
    (void)fprintf(job->cmdFILE, fmt, arg);       \     (void)fprintf(job->cmdFILE, fmt, arg);       \
    (void)fflush(job->cmdFILE);     (void)fflush(job->cmdFILE);
   
     numCommands += 1;          numCommands += 1;
   
     /* For debugging, we replace each command with the result of expanding          /* For debugging, we replace each command with the result of expanding
      * the variables in the command.  */           * the variables in the command.  */
     cmdStart = cmd = Var_Subst(cmd, &job->node->context, false);          cmdStart = cmd = Var_Subst(cmd, &job->node->context, false);
     Lst_Replace(cmdNode, cmdStart);          Lst_Replace(cmdNode, cmdStart);
   
     cmdTemplate = "%s\n";          cmdTemplate = "%s\n";
   
     /*          /*
      * Check for leading @' and -'s to control echoing and error checking.           * Check for leading @' and -'s to control echoing and error checking.
      */           */
     for (;; cmd++) {          for (;; cmd++) {
         if (*cmd == '@')                  if (*cmd == '@')
             shutUp = DEBUG(LOUD) ? false : true;                          shutUp = DEBUG(LOUD) ? false : true;
         else if (*cmd == '-')                  else if (*cmd == '-')
             errOff = true;                          errOff = true;
         else if (*cmd != '+')                  else if (*cmd != '+')
             break;                          break;
     }  
   
     while (isspace(*cmd))  
         cmd++;  
   
     if (shutUp) {  
         if (!(job->flags & JOB_SILENT) && !noSpecials &&  
             commandShell->hasEchoCtl) {  
                 DBPRINTF("%s\n", commandShell->echoOff);  
         } else {  
             shutUp = false;  
         }          }
     }  
   
     if (errOff) {          while (isspace(*cmd))
         if ( !(job->flags & JOB_IGNERR) && !noSpecials) {                  cmd++;
             if (commandShell->hasErrCtl) {  
                 /*          if (shutUp) {
                  * we don't want the error-control commands showing                  if (!(job->flags & JOB_SILENT) && !noSpecials &&
                  * up either, so we turn off echoing while executing  
                  * them. We could put another field in the shell  
                  * structure to tell JobDoOutput to look for this  
                  * string too, but why make it any more complex than  
                  * it already is?  
                  */  
                 if (!(job->flags & JOB_SILENT) && !shutUp &&  
                     commandShell->hasEchoCtl) {                      commandShell->hasEchoCtl) {
                         DBPRINTF("%s\n", commandShell->echoOff);                          DBPRINTF("%s\n", commandShell->echoOff);
                         DBPRINTF("%s\n", commandShell->ignErr);  
                         DBPRINTF("%s\n", commandShell->echoOn);  
                 } else {                  } else {
                     DBPRINTF("%s\n", commandShell->ignErr);                          shutUp = false;
                 }                  }
             } else if (commandShell->ignErr &&          }
                       (*commandShell->ignErr != '\0'))  
             {          if (errOff) {
                   if ( !(job->flags & JOB_IGNERR) && !noSpecials) {
                           if (commandShell->hasErrCtl) {
                                   /*
                                    * we don't want the error-control commands
                                    * showing up either, so we turn off echoing
                                    * while executing them. We could put another
                                    * field in the shell structure to tell
                                    * JobDoOutput to look for this string too, but
                                    * why make it any more complex than it already
                                    * is?
                                    */
                                   if (!(job->flags & JOB_SILENT) && !shutUp &&
                                       commandShell->hasEchoCtl) {
                                           DBPRINTF("%s\n", commandShell->echoOff);
                                           DBPRINTF("%s\n", commandShell->ignErr);
                                           DBPRINTF("%s\n", commandShell->echoOn);
                                   } else {
                                           DBPRINTF("%s\n", commandShell->ignErr);
                                   }
                           } else if (commandShell->ignErr &&
                               (*commandShell->ignErr != '\0')) {
                                   /*
                                    * The shell has no error control, so we need
                                    * to be weird to get it to ignore any errors
                                    * from the command.  If echoing is turned on,
                                    * we turn it off and use the errCheck template
                                    * to echo the command. Leave echoing off so
                                    * the user doesn't see the weirdness we go
                                    * through to ignore errors. Set cmdTemplate to
                                    * use the weirdness instead of the simple
                                    * "%s\n" template.
                                    */
                                   if (!(job->flags & JOB_SILENT) && !shutUp &&
                                       commandShell->hasEchoCtl) {
                                           DBPRINTF("%s\n", commandShell->echoOff);
                                           DBPRINTF(commandShell->errCheck, cmd);
                                           shutUp = true;
                                   }
                                   cmdTemplate = commandShell->ignErr;
                                   /*
                                    * The error ignoration (hee hee) is already
                                    * taken care of by the ignErr template, so
                                    * pretend error checking is still on.
                                    */
                                   errOff = false;
                           } else {
                                   errOff = false;
                           }
                   } else {
                           errOff = false;
                   }
           }
   
           DBPRINTF(cmdTemplate, cmd);
   
           if (errOff) {
                 /*                  /*
                  * The shell has no error control, so we need to be                   * If echoing is already off, there's no point in issuing the
                  * weird to get it to ignore any errors from the command.                   * echoOff command. Otherwise we issue it and pretend it was on
                  * If echoing is turned on, we turn it off and use the                   * for the whole command...
                  * errCheck template to echo the command. Leave echoing  
                  * off so the user doesn't see the weirdness we go through  
                  * to ignore errors. Set cmdTemplate to use the weirdness  
                  * instead of the simple "%s\n" template.  
                  */                   */
                 if (!(job->flags & JOB_SILENT) && !shutUp &&                  if (!shutUp && !(job->flags & JOB_SILENT) &&
                     commandShell->hasEchoCtl) {                      commandShell->hasEchoCtl){
                         DBPRINTF("%s\n", commandShell->echoOff);                          DBPRINTF("%s\n", commandShell->echoOff);
                         DBPRINTF(commandShell->errCheck, cmd);  
                         shutUp = true;                          shutUp = true;
                 }                  }
                 cmdTemplate = commandShell->ignErr;                  DBPRINTF("%s\n", commandShell->errCheck);
                 /*  
                  * The error ignoration (hee hee) is already taken care  
                  * of by the ignErr template, so pretend error checking  
                  * is still on.  
                  */  
                 errOff = false;  
             } else {  
                 errOff = false;  
             }  
         } else {  
             errOff = false;  
         }          }
     }          if (shutUp) {
                   DBPRINTF("%s\n", commandShell->echoOn);
     DBPRINTF(cmdTemplate, cmd);  
   
     if (errOff) {  
         /*  
          * If echoing is already off, there's no point in issuing the  
          * echoOff command. Otherwise we issue it and pretend it was on  
          * for the whole command...  
          */  
         if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){  
             DBPRINTF("%s\n", commandShell->echoOff);  
             shutUp = true;  
         }          }
         DBPRINTF("%s\n", commandShell->errCheck);          return 1;
     }  
     if (shutUp) {  
         DBPRINTF("%s\n", commandShell->echoOn);  
     }  
     return 1;  
 }  }
   
 /*-  /*-
Line 809 
Line 813 
 static void  static void
 JobSaveCommand(void *cmd, void *gn)  JobSaveCommand(void *cmd, void *gn)
 {  {
     GNode       *g = (GNode *)gn;          GNode *g = (GNode *)gn;
     char        *result;          char *result;
   
     result = Var_Subst((char *)cmd, &g->context, false);          result = Var_Subst((char *)cmd, &g->context, false);
     Lst_AtEnd(&postCommands->commands, result);          Lst_AtEnd(&postCommands->commands, result);
 }  }
   
   
Line 829 
Line 833 
 static void  static void
 JobClose(Job *job)  JobClose(Job *job)
 {  {
     if (usePipes) {          if (usePipes) {
         FD_CLR(job->inPipe, outputsp);                  FD_CLR(job->inPipe, outputsp);
         if (job->outPipe != job->inPipe) {                  if (job->outPipe != job->inPipe) {
            (void)close(job->outPipe);                         (void)close(job->outPipe);
                   }
                   JobDoOutput(job, true);
                   (void)close(job->inPipe);
           } else {
                   (void)close(job->outFd);
                   JobDoOutput(job, true);
         }          }
         JobDoOutput(job, true);  
         (void)close(job->inPipe);  
     } else {  
         (void)close(job->outFd);  
         JobDoOutput(job, true);  
     }  
 }  }
   
 /*-  /*-
Line 867 
Line 871 
 JobFinish(Job *job,             /* job to finish */  JobFinish(Job *job,             /* job to finish */
     int *status)                /* sub-why job went away */      int *status)                /* sub-why job went away */
 {  {
     bool         done;          bool done;
   
     if ((WIFEXITED(*status) &&          if ((WIFEXITED(*status) &&
          WEXITSTATUS(*status) != 0 && !(job->flags & JOB_IGNERR)) ||               WEXITSTATUS(*status) != 0 && !(job->flags & JOB_IGNERR)) ||
         (WIFSIGNALED(*status) && WTERMSIG(*status) != SIGCONT))              (WIFSIGNALED(*status) && WTERMSIG(*status) != SIGCONT)) {
     {                  /*
         /*                   * If it exited non-zero and either we're doing things our
          * If it exited non-zero and either we're doing things our                   * way or we're not ignoring errors, the job is finished.
          * way or we're not ignoring errors, the job is finished.                   * Similarly, if the shell died because of a signal
          * Similarly, if the shell died because of a signal                   * the job is also finished. In these
          * the job is also finished. In these                   * cases, finish out the job's output before printing the exit
          * cases, finish out the job's output before printing the exit                   * status...
          * status...                   */
          */                  JobClose(job);
         JobClose(job);                  if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
         if (job->cmdFILE != NULL && job->cmdFILE != stdout) {                         (void)fclose(job->cmdFILE);
            (void)fclose(job->cmdFILE);                  }
         }                  done = true;
         done = true;          } else if (WIFEXITED(*status)) {
     } else if (WIFEXITED(*status)) {                  /*
         /*                   * Deal with ignored errors in -B mode. We need to print a
          * Deal with ignored errors in -B mode. We need to print a message                   * message telling of the ignored error as well as setting
          * telling of the ignored error as well as setting status.w_status                   * status.w_status to 0 so the next command gets run. To do
          * to 0 so the next command gets run. To do this, we set done to be                   * this, we set done to be true if in -B mode and the job
          * true if in -B mode and the job exited non-zero.                   * exited non-zero.
          */                   */
         done = WEXITSTATUS(*status) != 0;                  done = WEXITSTATUS(*status) != 0;
         /*                  /*
          * Old comment said: "Note we don't                   * Old comment said: "Note we don't want to close down any of
          * want to close down any of the streams until we know we're at the                   * the streams until we know we're at the end." But we do.
          * end."                   * Otherwise when are we going to print the rest of the stuff?
          * But we do. Otherwise when are we going to print the rest of the                   */
          * stuff?                  JobClose(job);
          */  
         JobClose(job);  
     } else {  
         /*  
          * No need to close things down or anything.  
          */  
         done = false;  
     }  
   
     if (done ||  
         WIFSTOPPED(*status) ||  
         (WIFSIGNALED(*status) && WTERMSIG(*status) == SIGCONT) ||  
         DEBUG(JOB))  
     {  
         FILE      *out;  
   
         if (compatMake && !usePipes && (job->flags & JOB_IGNERR)) {  
             /*  
              * If output is going to a file and this job is ignoring  
              * errors, arrange to have the exit status sent to the  
              * output file as well.  
              */  
             out = fdopen(job->outFd, "w");  
         } else {          } else {
             out = stdout;                  /*
                    * No need to close things down or anything.
                    */
                   done = false;
         }          }
   
         if (WIFEXITED(*status)) {          if (done ||
             if (DEBUG(JOB)) {              WIFSTOPPED(*status) ||
                 (void)fprintf(stdout, "Process %ld exited.\n", (long)job->pid);              (WIFSIGNALED(*status) && WTERMSIG(*status) == SIGCONT) ||
                 (void)fflush(stdout);              DEBUG(JOB)) {
             }                  FILE *out;
             if (WEXITSTATUS(*status) != 0) {  
                 if (usePipes && job->node != lastNode) {  
                     MESSAGE(out, job->node);  
                     lastNode = job->node;  
                 }  
                 (void)fprintf(out, "*** Error code %d%s\n",  
                                WEXITSTATUS(*status),  
                                (job->flags & JOB_IGNERR) ? "(ignored)" : "");  
   
                 if (job->flags & JOB_IGNERR) {                  if (compatMake && !usePipes && (job->flags & JOB_IGNERR)) {
                     *status = 0;                          /*
                            * If output is going to a file and this job is ignoring
                            * errors, arrange to have the exit status sent to the
                            * output file as well.
                            */
                           out = fdopen(job->outFd, "w");
                   } else {
                           out = stdout;
                 }                  }
             } else if (DEBUG(JOB)) {  
                 if (usePipes && job->node != lastNode) {                  if (WIFEXITED(*status)) {
                     MESSAGE(out, job->node);                          if (DEBUG(JOB)) {
                     lastNode = job->node;                                  (void)fprintf(stdout, "Process %ld exited.\n",
                 }                                      (long)job->pid);
                 (void)fprintf(out, "*** Completed successfully\n");                                  (void)fflush(stdout);
             }                          }
         } else if (WIFSTOPPED(*status)) {                          if (WEXITSTATUS(*status) != 0) {
             if (DEBUG(JOB)) {                                  if (usePipes && job->node != lastNode) {
                 (void)fprintf(stdout, "Process %ld stopped.\n", (long)job->pid);                                          MESSAGE(out, job->node);
                 (void)fflush(stdout);                                          lastNode = job->node;
             }                                  }
             if (usePipes && job->node != lastNode) {                                  (void)fprintf(out, "*** Error code %d%s\n",
                 MESSAGE(out, job->node);                                      WEXITSTATUS(*status),
                 lastNode = job->node;                                      (job->flags & JOB_IGNERR) ? "(ignored)" :
             }                                      "");
             (void)fprintf(out, "*** Stopped -- signal %d\n",  
                 WSTOPSIG(*status));                                  if (job->flags & JOB_IGNERR) {
             job->flags |= JOB_RESUME;                                          *status = 0;
             Lst_AtEnd(&stoppedJobs, job);                                  }
             (void)fflush(out);                          } else if (DEBUG(JOB)) {
             return;                                  if (usePipes && job->node != lastNode) {
         } else if (WTERMSIG(*status) == SIGCONT) {                                          MESSAGE(out, job->node);
             /*                                          lastNode = job->node;
              * If the beastie has continued, shift the Job from the stopped                                  }
              * list to the running one (or re-stop it if concurrency is                                  (void)fprintf(out,
              * exceeded) and go and get another child.                                      "*** Completed successfully\n");
              */                          }
             if (job->flags & (JOB_RESUME|JOB_RESTART)) {                  } else if (WIFSTOPPED(*status)) {
                 if (usePipes && job->node != lastNode) {                          if (DEBUG(JOB)) {
                     MESSAGE(out, job->node);                                  (void)fprintf(stdout, "Process %ld stopped.\n",
                     lastNode = job->node;                                      (long)job->pid);
                 }                                  (void)fflush(stdout);
                 (void)fprintf(out, "*** Continued\n");                          }
             }                          if (usePipes && job->node != lastNode) {
             if (!(job->flags & JOB_CONTINUING)) {                                  MESSAGE(out, job->node);
                 if (DEBUG(JOB)) {                                  lastNode = job->node;
                     (void)fprintf(stdout,                          }
                                    "Warning: process %ld was not continuing.\n",                          (void)fprintf(out, "*** Stopped -- signal %d\n",
                                    (long)job->pid);                              WSTOPSIG(*status));
                     (void)fflush(stdout);                          job->flags |= JOB_RESUME;
                 }                          Lst_AtEnd(&stoppedJobs, job);
 #ifdef notdef                          (void)fflush(out);
                 /*                          return;
                  * We don't really want to restart a job from scratch just                  } else if (WTERMSIG(*status) == SIGCONT) {
                  * because it continued, especially not without killing the                          /*
                  * continuing process!  That's why this is ifdef'ed out.                           * If the beastie has continued, shift the Job from the
                  * FD - 9/17/90                           * stopped list to the running one (or re-stop it if
                  */                           * concurrency is exceeded) and go and get another
                 JobRestart(job);                           * child.
                            */
                           if (job->flags & (JOB_RESUME|JOB_RESTART)) {
                                   if (usePipes && job->node != lastNode) {
                                           MESSAGE(out, job->node);
                                           lastNode = job->node;
                                   }
                                   (void)fprintf(out, "*** Continued\n");
                           }
                           if (!(job->flags & JOB_CONTINUING)) {
                                   if (DEBUG(JOB)) {
                                           (void)fprintf(stdout,
                                   "Warning: process %ld was not continuing.\n",
                                               (long)job->pid);
                                           (void)fflush(stdout);
                                   }
   #if 0
                                   /*
                                    * We don't really want to restart a job from
                                    * scratch just because it continued,
                                    * especially not without killing the
                                    * continuing process!  That's why this is
                                    * ifdef'ed out.  FD - 9/17/90
                                    */
                                   JobRestart(job);
 #endif  #endif
             }                          }
             job->flags &= ~JOB_CONTINUING;                          job->flags &= ~JOB_CONTINUING;
             Lst_AtEnd(&jobs, job);                          Lst_AtEnd(&jobs, job);
             nJobs += 1;                          nJobs += 1;
             if (DEBUG(JOB)) {                          if (DEBUG(JOB)) {
                 (void)fprintf(stdout,                                  (void)fprintf(stdout,
                                "Process %ld is continuing locally.\n",                                      "Process %ld is continuing locally.\n",
                                (long)job->pid);                                      (long)job->pid);
                 (void)fflush(stdout);                                  (void)fflush(stdout);
             }                          }
             nLocal += 1;                          nLocal += 1;
             if (nJobs == maxJobs) {                          if (nJobs == maxJobs) {
                 jobFull = true;                                  jobFull = true;
                 if (DEBUG(JOB)) {                                  if (DEBUG(JOB)) {
                     (void)fprintf(stdout, "Job queue is full.\n");                                          (void)fprintf(stdout,
                     (void)fflush(stdout);                                              "Job queue is full.\n");
                                           (void)fflush(stdout);
                                   }
                           }
                           (void)fflush(out);
                           return;
                   } else {
                           if (usePipes && job->node != lastNode) {
                                   MESSAGE(out, job->node);
                                   lastNode = job->node;
                           }
                           (void)fprintf(out, "*** Signal %d\n",
                               WTERMSIG(*status));
                 }                  }
             }  
             (void)fflush(out);                  (void)fflush(out);
             return;  
         } else {  
             if (usePipes && job->node != lastNode) {  
                 MESSAGE(out, job->node);  
                 lastNode = job->node;  
             }  
             (void)fprintf(out, "*** Signal %d\n", WTERMSIG(*status));  
         }          }
   
         (void)fflush(out);          /*
     }           * Now handle the -B-mode stuff. If the beast still isn't finished,
            * try and restart the job on the next command. If JobStart says it's
            * ok, it's ok. If there's an error, this puppy is done.
            */
           if (compatMake && WIFEXITED(*status) && job->node->current != NULL) {
                   switch (JobStart(job->node, job->flags & JOB_IGNDOTS, job)) {
                   case JOB_RUNNING:
                           done = false;
                           break;
                   case JOB_ERROR:
                           done = true;
                           W_SETEXITSTATUS(status, 1);
                           break;
                   case JOB_FINISHED:
                           /*
                            * If we got back a JOB_FINISHED code, JobStart has
                            * already called Make_Update and freed the job
                            * descriptor. We set done to false here to avoid fake
                            * cycles and double frees.  JobStart needs to do the
                            * update so we can proceed up the graph when given the
                            * -n flag..
                            */
                           done = false;
                           break;
                   }
           } else
                   done = true;
   
     /*          if (done &&
      * Now handle the -B-mode stuff. If the beast still isn't finished,              aborting != ABORT_ERROR &&
      * try and restart the job on the next command. If JobStart says it's              aborting != ABORT_INTERRUPT &&
      * ok, it's ok. If there's an error, this puppy is done.              *status == 0) {
      */                  /* As long as we aren't aborting and the job didn't return a
     if (compatMake && WIFEXITED(*status) && job->node->current != NULL) {                   * non-zero status that we shouldn't ignore, we call
         switch (JobStart(job->node, job->flags & JOB_IGNDOTS, job)) {                   * Make_Update to update the parents. In addition, any saved
         case JOB_RUNNING:                   * commands for the node are placed on the .END target. */
             done = false;                  Lst_ForEachFrom(job->tailCmds, JobSaveCommand, job->node);
             break;                  job->node->made = MADE;
         case JOB_ERROR:                  Make_Update(job->node);
             done = true;                  free(job);
             W_SETEXITSTATUS(status, 1);          } else if (*status != 0) {
             break;                  errors += 1;
         case JOB_FINISHED:                  free(job);
             /*  
              * If we got back a JOB_FINISHED code, JobStart has already  
              * called Make_Update and freed the job descriptor. We set  
              * done to false here to avoid fake cycles and double frees.  
              * JobStart needs to do the update so we can proceed up the  
              * graph when given the -n flag..  
              */  
             done = false;  
             break;  
         }          }
     } else  
         done = true;  
   
     if (done &&          JobRestartJobs();
         aborting != ABORT_ERROR &&  
         aborting != ABORT_INTERRUPT &&  
         *status == 0) {  
         /* As long as we aren't aborting and the job didn't return a non-zero  
          * status that we shouldn't ignore, we call Make_Update to update  
          * the parents. In addition, any saved commands for the node are placed  
          * on the .END target.  */  
         Lst_ForEachFrom(job->tailCmds, JobSaveCommand, job->node);  
         job->node->made = MADE;  
         Make_Update(job->node);  
         free(job);  
     } else if (*status != 0) {  
         errors += 1;  
         free(job);  
     }  
   
     JobRestartJobs();  
   
     /*  
      * Set aborting if any error.  
      */  
     if (errors && !keepgoing && aborting != ABORT_INTERRUPT) {  
         /*          /*
          * If we found any errors in this batch of children and the -k flag           * Set aborting if any error.
          * wasn't given, we set the aborting flag so no more jobs get  
          * started.  
          */           */
         aborting = ABORT_ERROR;          if (errors && !keepgoing && aborting != ABORT_INTERRUPT) {
     }                  /*
                    * If we found any errors in this batch of children and the -k
                    * flag wasn't given, we set the aborting flag so no more jobs
                    * get started.
                    */
                   aborting = ABORT_ERROR;
           }
   
     if (aborting == ABORT_ERROR && Job_Empty()) {          if (aborting == ABORT_ERROR && Job_Empty()) {
         /*                  /*
          * If we are aborting and the job table is now empty, we finish.                   * If we are aborting and the job table is now empty, we finish.
          */                   */
         (void)eunlink(tfile);                  (void)eunlink(tfile);
         Finish(errors);                  Finish(errors);
     }          }
 }  }
   
 /*-  /*-
Line 1108 
Line 1118 
 static void  static void
 JobExec(Job *job, char **argv)  JobExec(Job *job, char **argv)
 {  {
     pid_t         cpid;         /* ID of new child */          pid_t cpid;     /* ID of new child */
   
     if (DEBUG(JOB)) {          if (DEBUG(JOB)) {
         int       i;                  int       i;
   
         (void)fprintf(stdout, "Running %s\n", job->node->name);                  (void)fprintf(stdout, "Running %s\n", job->node->name);
         (void)fprintf(stdout, "\tCommand: ");                  (void)fprintf(stdout, "\tCommand: ");
         for (i = 0; argv[i] != NULL; i++) {                  for (i = 0; argv[i] != NULL; i++) {
             (void)fprintf(stdout, "%s ", argv[i]);                          (void)fprintf(stdout, "%s ", argv[i]);
                   }
                   (void)fprintf(stdout, "\n");
                   (void)fflush(stdout);
         }          }
         (void)fprintf(stdout, "\n");  
         (void)fflush(stdout);  
     }  
   
     /*  
      * Some jobs produce no output and it's disconcerting to have  
      * no feedback of their running (since they produce no output, the  
      * banner with their name in it never appears). This is an attempt to  
      * provide that feedback, even if nothing follows it.  
      */  
     if (lastNode != job->node && (job->flags & JOB_FIRST) &&  
         !(job->flags & JOB_SILENT)) {  
         MESSAGE(stdout, job->node);  
         lastNode = job->node;  
     }  
   
     if ((cpid = fork()) == -1) {  
         Punt("Cannot fork");  
     } else if (cpid == 0) {  
   
         /*          /*
          * Must duplicate the input stream down to the child's input and           * Some jobs produce no output and it's disconcerting to have
          * reset it to the beginning (again). Since the stream was marked           * no feedback of their running (since they produce no output, the
          * close-on-exec, we must clear that bit in the new input.           * banner with their name in it never appears). This is an attempt to
            * provide that feedback, even if nothing follows it.
          */           */
         if (dup2(FILENO(job->cmdFILE), 0) == -1)          if (lastNode != job->node && (job->flags & JOB_FIRST) &&
             Punt("Cannot dup2: %s", strerror(errno));              !(job->flags & JOB_SILENT)) {
         (void)fcntl(0, F_SETFD, 0);                  MESSAGE(stdout, job->node);
         (void)lseek(0, 0, SEEK_SET);                  lastNode = job->node;
   
         if (usePipes) {  
             /*  
              * Set up the child's output to be routed through the pipe  
              * we've created for it.  
              */  
             if (dup2(job->outPipe, 1) == -1)  
                 Punt("Cannot dup2: %s", strerror(errno));  
         } else {  
             /*  
              * We're capturing output in a file, so we duplicate the  
              * descriptor to the temporary file into the standard  
              * output.  
              */  
             if (dup2(job->outFd, 1) == -1)  
                 Punt("Cannot dup2: %s", strerror(errno));  
         }          }
         /*  
          * The output channels are marked close on exec. This bit was  
          * duplicated by the dup2 (on some systems), so we have to clear  
          * it before routing the shell's error output to the same place as  
          * its standard output.  
          */  
         (void)fcntl(1, F_SETFD, 0);  
         if (dup2(1, 2) == -1)  
             Punt("Cannot dup2: %s", strerror(errno));  
   
           if ((cpid = fork()) == -1) {
                   Punt("Cannot fork");
           } else if (cpid == 0) {
   
                   /*
                    * Must duplicate the input stream down to the child's input
                    * and reset it to the beginning (again). Since the stream was
                    * marked close-on-exec, we must clear that bit in the new
                    * input.
                    */
                   if (dup2(FILENO(job->cmdFILE), 0) == -1)
                           Punt("Cannot dup2: %s", strerror(errno));
                   (void)fcntl(0, F_SETFD, 0);
                   (void)lseek(0, 0, SEEK_SET);
   
                   if (usePipes) {
                           /*
                            * Set up the child's output to be routed through the
                            * pipe we've created for it.
                            */
                           if (dup2(job->outPipe, 1) == -1)
                                   Punt("Cannot dup2: %s", strerror(errno));
                   } else {
                           /*
                            * We're capturing output in a file, so we duplicate the
                            * descriptor to the temporary file into the standard
                            * output.
                            */
                           if (dup2(job->outFd, 1) == -1)
                                   Punt("Cannot dup2: %s", strerror(errno));
                   }
                   /*
                    * The output channels are marked close on exec. This bit was
                    * duplicated by the dup2 (on some systems), so we have to
                    * clear it before routing the shell's error output to the same
                    * place as its standard output.
                    */
                   (void)fcntl(1, F_SETFD, 0);
                   if (dup2(1, 2) == -1)
                           Punt("Cannot dup2: %s", strerror(errno));
   
 #ifdef USE_PGRP  #ifdef USE_PGRP
         /*                  /*
          * We want to switch the child into a different process family so                   * We want to switch the child into a different process family
          * we can kill it and all its descendants in one fell swoop,                   * so we can kill it and all its descendants in one fell swoop,
          * by killing its process family, but not commit suicide.                   * by killing its process family, but not commit suicide.
          */                   */
 # if defined(SYSV)  # if defined(SYSV)
         (void)setsid();                  (void)setsid();
 # else  # else
         (void)setpgid(0, getpid());                  (void)setpgid(0, getpid());
 # endif  # endif
 #endif /* USE_PGRP */  #endif /* USE_PGRP */
   
            (void)execv(shellPath, argv);                  (void)execv(shellPath, argv);
   
         (void)write(STDERR_FILENO, "Could not execute shell\n",                  (void)write(STDERR_FILENO, "Could not execute shell\n",
                      sizeof("Could not execute shell"));                      sizeof("Could not execute shell"));
         _exit(1);                  _exit(1);
     } else {          } else {
         job->pid = cpid;                  job->pid = cpid;
   
         if (usePipes && (job->flags & JOB_FIRST) ) {                  if (usePipes && (job->flags & JOB_FIRST) ) {
             /*                          /*
              * The first time a job is run for a node, we set the current                           * The first time a job is run for a node, we set the
              * position in the buffer to the beginning and mark another                           * current position in the buffer to the beginning and
              * stream to watch in the outputs mask                           * mark another stream to watch in the outputs mask
              */                           */
             job->curPos = 0;                          job->curPos = 0;
   
             if (outputsp == NULL || job->inPipe > outputsn) {                          if (outputsp == NULL || job->inPipe > outputsn) {
                 int bytes, obytes;                                  int bytes, obytes;
                 char *tmp;                                  char *tmp;
   
                 bytes = howmany(job->inPipe+1, NFDBITS) * sizeof(fd_mask);                                  bytes = howmany(job->inPipe+1, NFDBITS) *
                 obytes = outputsn ?                                      sizeof(fd_mask);
                     howmany(outputsn+1, NFDBITS) * sizeof(fd_mask) : 0;                                  obytes = outputsn ?
                                       howmany(outputsn+1, NFDBITS) *
                                       sizeof(fd_mask) : 0;
   
                 if (bytes != obytes) {                                  if (bytes != obytes) {
                         tmp = realloc(outputsp, bytes);                                          tmp = realloc(outputsp, bytes);
                         if (tmp == NULL)                                          if (tmp == NULL)
                                 return;                                                  return;
                         memset(tmp + obytes, 0, bytes - obytes);                                          memset(tmp + obytes, 0, bytes - obytes);
                         outputsp = (fd_set *)tmp;                                          outputsp = (fd_set *)tmp;
                                   }
                                   outputsn = job->inPipe;
                           }
                           FD_SET(job->inPipe, outputsp);
                 }                  }
                 outputsn = job->inPipe;  
             }                  nLocal += 1;
             FD_SET(job->inPipe, outputsp);                  /*
                    * XXX: Used to not happen if REMOTE. Why?
                    */
                   if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
                           (void)fclose(job->cmdFILE);
                           job->cmdFILE = NULL;
                   }
         }          }
   
         nLocal += 1;  
         /*          /*
          * XXX: Used to not happen if REMOTE. Why?           * Now the job is actually running, add it to the table.
          */           */
         if (job->cmdFILE != NULL && job->cmdFILE != stdout) {          nJobs += 1;
             (void)fclose(job->cmdFILE);          Lst_AtEnd(&jobs, job);
             job->cmdFILE = NULL;          if (nJobs == maxJobs) {
                   jobFull = true;
         }          }
     }  
   
     /*  
      * Now the job is actually running, add it to the table.  
      */  
     nJobs += 1;  
     Lst_AtEnd(&jobs, job);  
     if (nJobs == maxJobs) {  
         jobFull = true;  
     }  
 }  }
   
 /*-  /*-
Line 1252 
Line 1265 
 static void  static void
 JobMakeArgv(Job *job, char **argv)  JobMakeArgv(Job *job, char **argv)
 {  {
     int           argc;          int argc;
     static char   args[10];     /* For merged arguments */          static char args[10];   /* For merged arguments */
   
     argv[0] = shellName;          argv[0] = shellName;
     argc = 1;          argc = 1;
   
     if ((commandShell->exit && *commandShell->exit != '-') ||          if ((commandShell->exit && *commandShell->exit != '-') ||
         (commandShell->echo && *commandShell->echo != '-'))              (commandShell->echo && *commandShell->echo != '-')) {
     {                  /*
         /*                   * At least one of the flags doesn't have a minus before it, so
          * At least one of the flags doesn't have a minus before it, so                   * merge them together. Have to do this because the
          * merge them together. Have to do this because the *(&(@*#*&#$#                   * *(&(@*#*&#$# Bourne shell thinks its second argument is a
          * Bourne shell thinks its second argument is a file to source.                   * file to source.  Grrrr. Note the ten-character limitation on
          * Grrrr. Note the ten-character limitation on the combined arguments.                   * the combined arguments.
          */                   */
         (void)snprintf(args, sizeof(args), "-%s%s",                  (void)snprintf(args, sizeof(args), "-%s%s",
                       ((job->flags & JOB_IGNERR) ? "" :                      ((job->flags & JOB_IGNERR) ? "" :
                        (commandShell->exit ? commandShell->exit : "")),                      (commandShell->exit ? commandShell->exit : "")),
                       ((job->flags & JOB_SILENT) ? "" :                      ((job->flags & JOB_SILENT) ? "" :
                        (commandShell->echo ? commandShell->echo : "")));                      (commandShell->echo ? commandShell->echo : "")));
   
         if (args[1]) {                  if (args[1]) {
             argv[argc] = args;                          argv[argc] = args;
             argc++;                          argc++;
                   }
           } else {
                   if (!(job->flags & JOB_IGNERR) && commandShell->exit) {
                           argv[argc] = commandShell->exit;
                           argc++;
                   }
                   if (!(job->flags & JOB_SILENT) && commandShell->echo) {
                           argv[argc] = commandShell->echo;
                           argc++;
                   }
         }          }
     } else {          argv[argc] = NULL;
         if (!(job->flags & JOB_IGNERR) && commandShell->exit) {  
             argv[argc] = commandShell->exit;  
             argc++;  
         }  
         if (!(job->flags & JOB_SILENT) && commandShell->echo) {  
             argv[argc] = commandShell->echo;  
             argc++;  
         }  
     }  
     argv[argc] = NULL;  
 }  }
   
 /*-  /*-
Line 1302 
Line 1315 
 static void  static void
 JobRestart(Job *job)  JobRestart(Job *job)
 {  {
     if (job->flags & JOB_RESTART) {          if (job->flags & JOB_RESTART) {
         /*  
          * Set up the control arguments to the shell. This is based on the  
          * flags set earlier for this job. If the JOB_IGNERR flag is clear,  
          * the 'exit' flag of the commandShell is used to cause it to exit  
          * upon receiving an error. If the JOB_SILENT flag is clear, the  
          * 'echo' flag of the commandShell is used to get it to start echoing  
          * as soon as it starts processing commands.  
          */  
         char      *argv[4];  
   
         JobMakeArgv(job, argv);  
   
         if (DEBUG(JOB)) {  
             (void)fprintf(stdout, "Restarting %s...", job->node->name);  
             (void)fflush(stdout);  
         }  
         {  
             if (nLocal >= maxLocal && !(job->flags & JOB_SPECIAL)) {  
                 /*                  /*
                  * Can't be exported and not allowed to run locally -- put it                   * Set up the control arguments to the shell. This is based on
                  * back on the hold queue and mark the table full                   * the flags set earlier for this job. If the JOB_IGNERR flag
                    * is clear, the 'exit' flag of the commandShell is used to
                    * cause it to exit upon receiving an error. If the JOB_SILENT
                    * flag is clear, the 'echo' flag of the commandShell is used
                    * to get it to start echoing as soon as it starts processing
                    * commands.
                  */                   */
                   char *argv[4];
   
                   JobMakeArgv(job, argv);
   
                 if (DEBUG(JOB)) {                  if (DEBUG(JOB)) {
                     (void)fprintf(stdout, "holding\n");                          (void)fprintf(stdout, "Restarting %s...",
                     (void)fflush(stdout);                              job->node->name);
                           (void)fflush(stdout);
                 }                  }
                 Lst_AtFront(&stoppedJobs, job);                  if (nLocal >= maxLocal && !(job->flags & JOB_SPECIAL)) {
                 jobFull = true;                          /*
                 if (DEBUG(JOB)) {                           * Can't be exported and not allowed to run locally --
                     (void)fprintf(stdout, "Job queue is full.\n");                           * put it back on the hold queue and mark the table
                     (void)fflush(stdout);                           * full
                            */
                           if (DEBUG(JOB)) {
                                   (void)fprintf(stdout, "holding\n");
                                   (void)fflush(stdout);
                           }
                           Lst_AtFront(&stoppedJobs, job);
                           jobFull = true;
                           if (DEBUG(JOB)) {
                                   (void)fprintf(stdout, "Job queue is full.\n");
                                   (void)fflush(stdout);
                           }
                           return;
                   } else {
                           /*
                            * Job may be run locally.
                            */
                           if (DEBUG(JOB)) {
                                   (void)fprintf(stdout, "running locally\n");
                                   (void)fflush(stdout);
                           }
                 }                  }
                 return;                  JobExec(job, argv);
             } else {          } else {
                 /*                  /*
                  * Job may be run locally.                   * The job has stopped and needs to be restarted. Why it
                    * stopped, we don't know...
                  */                   */
                 if (DEBUG(JOB)) {                  if (DEBUG(JOB)) {
                     (void)fprintf(stdout, "running locally\n");                         (void)fprintf(stdout, "Resuming %s...", job->node->name);
                     (void)fflush(stdout);                         (void)fflush(stdout);
                 }                  }
             }                  if ((nLocal < maxLocal ||
         }                      ((job->flags & JOB_SPECIAL) &&
         JobExec(job, argv);                       maxLocal == 0)
     } else {                     ) && nJobs != maxJobs) {
         /*                          /*
          * The job has stopped and needs to be restarted. Why it stopped,                           * If we haven't reached the concurrency limit already
          * we don't know...                           * (or maxLocal is 0), it's ok to resume the job.
          */                           */
         if (DEBUG(JOB)) {                          bool error;
            (void)fprintf(stdout, "Resuming %s...", job->node->name);                          int status;
            (void)fflush(stdout);  
         }  
         if ((nLocal < maxLocal ||  
             ((job->flags & JOB_SPECIAL) &&  
              maxLocal == 0)  
            ) && nJobs != maxJobs)  
         {  
             /*  
              * If we haven't reached the concurrency limit already (or  
              * maxLocal is 0), it's ok to resume the job.  
              */  
             bool error;  
             int status;  
   
                 error = KILL(job->pid, SIGCONT) != 0;                          error = KILL(job->pid, SIGCONT) != 0;
   
             if (!error) {                          if (!error) {
                 /*                                  /*
                  * Make sure the user knows we've continued the beast and                                   * Make sure the user knows we've continued the
                  * actually put the thing in the job table.                                   * beast and actually put the thing in the job
                  */                                   * table.
                 job->flags |= JOB_CONTINUING;                                   */
                 W_SETTERMSIG(&status, SIGCONT);                                  job->flags |= JOB_CONTINUING;
                 JobFinish(job, &status);                                  W_SETTERMSIG(&status, SIGCONT);
                                   JobFinish(job, &status);
   
                 job->flags &= ~(JOB_RESUME|JOB_CONTINUING);                                  job->flags &= ~(JOB_RESUME|JOB_CONTINUING);
                 if (DEBUG(JOB)) {                                  if (DEBUG(JOB)) {
                    (void)fprintf(stdout, "done\n");                                         (void)fprintf(stdout, "done\n");
                    (void)fflush(stdout);                                         (void)fflush(stdout);
                                   }
                           } else {
                                   Error("couldn't resume %s: %s",
                                       job->node->name, strerror(errno));
                                   status = 0;
                                   W_SETEXITSTATUS(&status, 1);
                                   JobFinish(job, &status);
                           }
                   } else {
                           /*
                            * Job cannot be restarted. Mark the table as full and
                            * place the job back on the list of stopped jobs.
                            */
                           if (DEBUG(JOB)) {
                                   (void)fprintf(stdout, "table full\n");
                                   (void)fflush(stdout);
                           }
                           Lst_AtFront(&stoppedJobs, job);
                           jobFull = true;
                           if (DEBUG(JOB)) {
                                   (void)fprintf(stdout, "Job queue is full.\n");
                                   (void)fflush(stdout);
                           }
                 }                  }
             } else {  
                 Error("couldn't resume %s: %s",  
                     job->node->name, strerror(errno));  
                 status = 0;  
                 W_SETEXITSTATUS(&status, 1);  
                 JobFinish(job, &status);  
             }  
         } else {  
             /*  
              * Job cannot be restarted. Mark the table as full and  
              * place the job back on the list of stopped jobs.  
              */  
             if (DEBUG(JOB)) {  
                 (void)fprintf(stdout, "table full\n");  
                 (void)fflush(stdout);  
             }  
             Lst_AtFront(&stoppedJobs, job);  
             jobFull = true;  
             if (DEBUG(JOB)) {  
                 (void)fprintf(stdout, "Job queue is full.\n");  
                 (void)fflush(stdout);  
             }  
         }          }
     }  
 }  }
   
 /*-  /*-
Line 1433 
Line 1447 
     Job           *previous)  /* The previous Job structure for this node,      Job           *previous)  /* The previous Job structure for this node,
                                * if any. */                                 * if any. */
 {  {
     Job           *job;       /* new job descriptor */          Job *job;       /* new job descriptor */
     char          *argv[4];   /* Argument vector to shell */          char *argv[4];  /* Argument vector to shell */
     bool          cmdsOK;     /* true if the nodes commands were all right */          bool cmdsOK;    /* true if the nodes commands were all right */
     bool          local;      /* Set true if the job was run locally */          bool local;     /* Set true if the job was run locally */
     bool          noExec;     /* Set true if we decide not to run the job */          bool noExec;    /* Set true if we decide not to run the job */
   
     if (previous != NULL) {          if (previous != NULL) {
         previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT);                  previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT);
         job = previous;                  job = previous;
     } else {          } else {
         job = emalloc(sizeof(Job));                  job = emalloc(sizeof(Job));
         if (job == NULL) {                  if (job == NULL) {
             Punt("JobStart out of memory");                          Punt("JobStart out of memory");
                   }
                   flags |= JOB_FIRST;
         }          }
         flags |= JOB_FIRST;  
     }  
   
     job->node = gn;          job->node = gn;
     job->tailCmds = NULL;          job->tailCmds = NULL;
   
     /*  
      * Set the initial value of the flags for this job based on the global  
      * ones and the node's attributes... Any flags supplied by the caller  
      * are also added to the field.  
      */  
     job->flags = 0;  
     if (Targ_Ignore(gn)) {  
         job->flags |= JOB_IGNERR;  
     }  
     if (Targ_Silent(gn)) {  
         job->flags |= JOB_SILENT;  
     }  
     job->flags |= flags;  
   
     /*  
      * Check the commands now so any attributes from .DEFAULT have a chance  
      * to migrate to the node  
      */  
     if (!compatMake && job->flags & JOB_FIRST) {  
         cmdsOK = Job_CheckCommands(gn, Error);  
     } else {  
         cmdsOK = true;  
     }  
   
     /*  
      * If the -n flag wasn't given, we open up OUR (not the child's)  
      * temporary file to stuff commands in it. The thing is rd/wr so we don't  
      * need to reopen it to feed it to the shell. If the -n flag *was* given,  
      * we just set the file to be stdout. Cute, huh?  
      */  
     if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) {  
         /*          /*
          * We're serious here, but if the commands were bogus, we're           * Set the initial value of the flags for this job based on the global
          * also dead...           * ones and the node's attributes... Any flags supplied by the caller
            * are also added to the field.
          */           */
         if (!cmdsOK) {          job->flags = 0;
             DieHorribly();          if (Targ_Ignore(gn)) {
                   job->flags |= JOB_IGNERR;
         }          }
           if (Targ_Silent(gn)) {
         job->cmdFILE = fopen(tfile, "w+");                  job->flags |= JOB_SILENT;
         if (job->cmdFILE == NULL) {  
             Punt("Could not open %s", tfile);  
         }          }
         (void)fcntl(FILENO(job->cmdFILE), F_SETFD, 1);          job->flags |= flags;
   
         /*          /*
          * Send the commands to the command file, flush all its buffers then           * Check the commands now so any attributes from .DEFAULT have a chance
          * rewind and remove the thing.           * to migrate to the node
          */           */
         noExec = false;          if (!compatMake && job->flags & JOB_FIRST) {
                   cmdsOK = Job_CheckCommands(gn, Error);
           } else {
                   cmdsOK = true;
           }
   
         /*          /*
          * used to be backwards; replace when start doing multiple commands           * If the -n flag wasn't given, we open up OUR (not the child's)
          * per shell.           * temporary file to stuff commands in it. The thing is rd/wr so we
            * don't need to reopen it to feed it to the shell. If the -n flag
            * *was* given, we just set the file to be stdout. Cute, huh?
          */           */
         if (compatMake) {          if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) {
             /*                  /*
              * Be compatible: If this is the first time for this node,                   * We're serious here, but if the commands were bogus, we're
              * verify its commands are ok and open the commands list for                   * also dead...
              * sequential access by later invocations of JobStart.                   */
              * Once that is done, we take the next command off the list                  if (!cmdsOK) {
              * and print it to the command file. If the command was an                          DieHorribly();
              * ellipsis, note that there's nothing more to execute.                  }
              */  
             if ((job->flags&JOB_FIRST))  
                 gn->current = Lst_First(&gn->commands);  
             else  
                 gn->current = Lst_Succ(gn->current);  
   
             if (gn->current == NULL ||                  job->cmdFILE = fopen(tfile, "w+");
                 !JobPrintCommand(gn->current, job)) {                  if (job->cmdFILE == NULL) {
                 noExec = true;                          Punt("Could not open %s", tfile);
                 gn->current = NULL;                  }
             }                  (void)fcntl(FILENO(job->cmdFILE), F_SETFD, 1);
             if (noExec && !(job->flags & JOB_FIRST)) {  
                 /*                  /*
                  * If we're not going to execute anything, the job                   * Send the commands to the command file, flush all its buffers
                  * is done and we need to close down the various                   * then rewind and remove the thing.
                  * file descriptors we've opened for output, then  
                  * call JobDoOutput to catch the final characters or  
                  * send the file to the screen... Note that the i/o streams  
                  * are only open if this isn't the first job.  
                  * Note also that this could not be done in  
                  * Job_CatchChildren b/c it wasn't clear if there were  
                  * more commands to execute or not...  
                  */                   */
                 JobClose(job);                  noExec = false;
             }  
         } else {  
             /*  
              * We can do all the commands at once. hooray for sanity  
              */  
             numCommands = 0;  
             Lst_ForEachNodeWhile(&gn->commands, JobPrintCommand, job);  
   
             /*                  /*
              * If we didn't print out any commands to the shell script,                   * used to be backwards; replace when start doing multiple
              * there's not much point in executing the shell, is there?                   * commands per shell.
              */                   */
             if (numCommands == 0) {                  if (compatMake) {
                           /*
                            * Be compatible: If this is the first time for this
                            * node, verify its commands are ok and open the
                            * commands list for sequential access by later
                            * invocations of JobStart.  Once that is done, we take
                            * the next command off the list and print it to the
                            * command file. If the command was an ellipsis, note
                            * that there's nothing more to execute.
                            */
                           if ((job->flags&JOB_FIRST))
                                   gn->current = Lst_First(&gn->commands);
                           else
                                   gn->current = Lst_Succ(gn->current);
   
                           if (gn->current == NULL ||
                               !JobPrintCommand(gn->current, job)) {
                                   noExec = true;
                                   gn->current = NULL;
                           }
                           if (noExec && !(job->flags & JOB_FIRST)) {
                                   /*
                                    * If we're not going to execute anything, the
                                    * job is done and we need to close down the
                                    * various file descriptors we've opened for
                                    * output, then call JobDoOutput to catch the
                                    * final characters or send the file to the
                                    * screen... Note that the i/o streams are only
                                    * open if this isn't the first job.  Note also
                                    * that this could not be done in
                                    * Job_CatchChildren b/c it wasn't clear if
                                    * there were more commands to execute or
                                    * not...
                                    */
                                   JobClose(job);
                           }
                   } else {
                           /*
                            * We can do all the commands at once. hooray for
                            * sanity
                            */
                           numCommands = 0;
                           Lst_ForEachNodeWhile(&gn->commands, JobPrintCommand,
                               job);
   
                           /*
                            * If we didn't print out any commands to the shell
                            * script, there's not much point in executing the
                            * shell, is there?
                            */
                           if (numCommands == 0) {
                                   noExec = true;
                           }
                   }
           } else if (noExecute) {
                   /*
                    * Not executing anything -- just print all the commands to
                    * stdout in one fell swoop. This will still set up
                    * job->tailCmds correctly.
                    */
                   if (lastNode != gn) {
                           MESSAGE(stdout, gn);
                           lastNode = gn;
                   }
                   job->cmdFILE = stdout;
                   /*
                    * Only print the commands if they're ok, but don't die if
                    * they're not -- just let the user know they're bad and keep
                    * going. It doesn't do any harm in this case and may do some
                    * good.
                    */
                   if (cmdsOK) {
                           Lst_ForEachNodeWhile(&gn->commands, JobPrintCommand,
                               job);
                   }
                   /*
                    * Don't execute the shell, thank you.
                    */
                 noExec = true;                  noExec = true;
             }          } else {
                   /*
                    * Just touch the target and note that no shell should be
                    * executed.  Set cmdFILE to stdout to make life easier. Check
                    * the commands, too, but don't die if they're no good -- it
                    * does no harm to keep working up the graph.
                    */
                   job->cmdFILE = stdout;
                   Job_Touch(gn, job->flags&JOB_SILENT);
                   noExec = true;
         }          }
     } else if (noExecute) {  
         /*          /*
          * Not executing anything -- just print all the commands to stdout           * If we're not supposed to execute a shell, don't.
          * in one fell swoop. This will still set up job->tailCmds correctly.  
          */           */
         if (lastNode != gn) {          if (noExec) {
             MESSAGE(stdout, gn);                  /*
             lastNode = gn;                   * Unlink and close the command file if we opened one
                    */
                   if (job->cmdFILE != stdout) {
                           (void)eunlink(tfile);
                           if (job->cmdFILE != NULL)
                                   (void)fclose(job->cmdFILE);
                   } else {
                            (void)fflush(stdout);
                   }
   
                   /*
                    * We only want to work our way up the graph if we aren't here
                    * because the commands for the job were no good.
                    */
                   if (cmdsOK) {
                           if (aborting == 0) {
                                   Lst_ForEachFrom(job->tailCmds, JobSaveCommand,
                                       job->node);
                                   Make_Update(job->node);
                           }
                           free(job);
                           return JOB_FINISHED;
                   } else {
                           free(job);
                           return JOB_ERROR;
                   }
           } else {
                   (void)fflush(job->cmdFILE);
                   (void)eunlink(tfile);
         }          }
         job->cmdFILE = stdout;  
         /*  
          * Only print the commands if they're ok, but don't die if they're  
          * not -- just let the user know they're bad and keep going. It  
          * doesn't do any harm in this case and may do some good.  
          */  
         if (cmdsOK) {  
             Lst_ForEachNodeWhile(&gn->commands, JobPrintCommand, job);  
         }  
         /*  
          * Don't execute the shell, thank you.  
          */  
         noExec = true;  
     } else {  
         /*  
          * Just touch the target and note that no shell should be executed.  
          * Set cmdFILE to stdout to make life easier. Check the commands, too,  
          * but don't die if they're no good -- it does no harm to keep working  
          * up the graph.  
          */  
         job->cmdFILE = stdout;  
         Job_Touch(gn, job->flags&JOB_SILENT);  
         noExec = true;  
     }  
   
     /*  
      * If we're not supposed to execute a shell, don't.  
      */  
     if (noExec) {  
         /*          /*
          * Unlink and close the command file if we opened one           * Set up the control arguments to the shell. This is based on the flags
            * set earlier for this job.
          */           */
         if (job->cmdFILE != stdout) {          JobMakeArgv(job, argv);
             (void)eunlink(tfile);  
             if (job->cmdFILE != NULL)  
                 (void)fclose(job->cmdFILE);  
         } else {  
              (void)fflush(stdout);  
         }  
   
         /*          /*
          * We only want to work our way up the graph if we aren't here because           * If we're using pipes to catch output, create the pipe by which we'll
          * the commands for the job were no good.           * get the shell's output. If we're using files, print out that we're
            * starting a job and then set up its temporary-file name.
          */           */
         if (cmdsOK) {          if (!compatMake || (job->flags & JOB_FIRST)) {
             if (aborting == 0) {                  if (usePipes) {
                 Lst_ForEachFrom(job->tailCmds, JobSaveCommand, job->node);                          int fd[2];
                 Make_Update(job->node);                          if (pipe(fd) == -1)
             }                                  Punt("Cannot create pipe: %s", strerror(errno));
             free(job);                          job->inPipe = fd[0];
             return JOB_FINISHED;                          job->outPipe = fd[1];
         } else {                          (void)fcntl(job->inPipe, F_SETFD, 1);
             free(job);                          (void)fcntl(job->outPipe, F_SETFD, 1);
             return JOB_ERROR;                  } else {
                           (void)fprintf(stdout, "Remaking `%s'\n", gn->name);
                           (void)fflush(stdout);
                           (void)strlcpy(job->outFile, TMPPAT,
                               sizeof(job->outFile));
                           if ((job->outFd = mkstemp(job->outFile)) == -1)
                                   Punt("Cannot create temp file: %s",
                                       strerror(errno));
                           (void)fcntl(job->outFd, F_SETFD, 1);
                   }
         }          }
     } else {  
         (void)fflush(job->cmdFILE);  
         (void)eunlink(tfile);  
     }  
   
     /*  
      * Set up the control arguments to the shell. This is based on the flags  
      * set earlier for this job.  
      */  
     JobMakeArgv(job, argv);  
   
     /*  
      * If we're using pipes to catch output, create the pipe by which we'll  
      * get the shell's output. If we're using files, print out that we're  
      * starting a job and then set up its temporary-file name.  
      */  
     if (!compatMake || (job->flags & JOB_FIRST)) {  
         if (usePipes) {  
             int fd[2];  
             if (pipe(fd) == -1)  
                 Punt("Cannot create pipe: %s", strerror(errno));  
             job->inPipe = fd[0];  
             job->outPipe = fd[1];  
             (void)fcntl(job->inPipe, F_SETFD, 1);  
             (void)fcntl(job->outPipe, F_SETFD, 1);  
         } else {  
             (void)fprintf(stdout, "Remaking `%s'\n", gn->name);  
             (void)fflush(stdout);  
             (void)strlcpy(job->outFile, TMPPAT, sizeof(job->outFile));  
             if ((job->outFd = mkstemp(job->outFile)) == -1)  
                 Punt("Cannot create temp file: %s", strerror(errno));  
             (void)fcntl(job->outFd, F_SETFD, 1);  
         }  
     }  
   
         local = true;          local = true;
   
     if (local && nLocal >= maxLocal &&          if (local && nLocal >= maxLocal &&
         !(job->flags & JOB_SPECIAL) &&              !(job->flags & JOB_SPECIAL) &&
         maxLocal != 0              maxLocal != 0
         )              ) {
     {                  /*
         /*                   * The job can only be run locally, but we've hit the limit of
          * The job can only be run locally, but we've hit the limit of                   * local concurrency, so put the job on hold until some other
          * local concurrency, so put the job on hold until some other job                   * job finishes. Note that the special jobs (.BEGIN, .INTERRUPT
          * finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END)                   * and .END) may be run locally even when the local limit has
          * may be run locally even when the local limit has been reached                   * been reached (e.g. when maxLocal == 0), though they will be
          * (e.g. when maxLocal == 0), though they will be exported if at                   * exported if at all possible. In addition, any target marked
          * all possible. In addition, any target marked with .NOEXPORT will                   * with .NOEXPORT will be run locally if maxLocal is 0.
          * be run locally if maxLocal is 0.                   */
          */                  jobFull = true;
         jobFull = true;  
   
         if (DEBUG(JOB)) {                  if (DEBUG(JOB)) {
            (void)fprintf(stdout, "Can only run job locally.\n");                         (void)fprintf(stdout, "Can only run job locally.\n");
            (void)fflush(stdout);                         (void)fflush(stdout);
                   }
                   job->flags |= JOB_RESTART;
                   Lst_AtEnd(&stoppedJobs, job);
           } else {
                   if (nLocal >= maxLocal && local) {
                           /*
                            * If we're running this job locally as a special case
                            * (see above), at least say the table is full.
                            */
                           jobFull = true;
                           if (DEBUG(JOB)) {
                                   (void)fprintf(stdout,
                                       "Local job queue is full.\n");
                                   (void)fflush(stdout);
                           }
                   }
                   JobExec(job, argv);
         }          }
         job->flags |= JOB_RESTART;          return JOB_RUNNING;
         Lst_AtEnd(&stoppedJobs, job);  
     } else {  
         if (nLocal >= maxLocal && local) {  
             /*  
              * If we're running this job locally as a special case (see above),  
              * at least say the table is full.  
              */  
             jobFull = true;  
             if (DEBUG(JOB)) {  
                 (void)fprintf(stdout, "Local job queue is full.\n");  
                 (void)fflush(stdout);  
             }  
         }  
         JobExec(job, argv);  
     }  
     return JOB_RUNNING;  
 }  }
   
 static char *  static char *
 JobOutput(Job *job, char *cp, char *endp, int msg)  JobOutput(Job *job, char *cp, char *endp, int msg)
 {  {
     char *ecp;          char *ecp;
   
     if (commandShell->noPrint) {          if (commandShell->noPrint) {
         ecp = strstr(cp, commandShell->noPrint);  
         while (ecp != NULL) {  
             if (cp != ecp) {  
                 *ecp = '\0';  
                 if (msg && job->node != lastNode) {  
                     MESSAGE(stdout, job->node);  
                     lastNode = job->node;  
                 }  
                 /*  
                  * The only way there wouldn't be a newline after  
                  * this line is if it were the last in the buffer.  
                  * however, since the non-printable comes after it,  
                  * there must be a newline, so we don't print one.  
                  */  
                 (void)fprintf(stdout, "%s", cp);  
                 (void)fflush(stdout);  
             }  
             cp = ecp + commandShell->noPLen;  
             if (cp != endp) {  
                 /*  
                  * Still more to print, look again after skipping  
                  * the whitespace following the non-printable  
                  * command....  
                  */  
                 cp++;  
                 while (*cp == ' ' || *cp == '\t' || *cp == '\n') {  
                     cp++;  
                 }  
                 ecp = strstr(cp, commandShell->noPrint);                  ecp = strstr(cp, commandShell->noPrint);
             } else {                  while (ecp != NULL) {
                 return cp;                          if (cp != ecp) {
             }                                  *ecp = '\0';
                                   if (msg && job->node != lastNode) {
                                           MESSAGE(stdout, job->node);
                                           lastNode = job->node;
                                   }
                                   /*
                                    * The only way there wouldn't be a newline
                                    * after this line is if it were the last in
                                    * the buffer.  however, since the
                                    * non-printable comes after it, there must be
                                    * a newline, so we don't print one.
                                    */
                                   (void)fprintf(stdout, "%s", cp);
                                   (void)fflush(stdout);
                           }
                           cp = ecp + commandShell->noPLen;
                           if (cp != endp) {
                                   /*
                                    * Still more to print, look again after
                                    * skipping the whitespace following the
                                    * non-printable command....
                                    */
                                   cp++;
                                   while (*cp == ' ' || *cp == '\t' ||
                                       *cp == '\n') {
                                           cp++;
                                   }
                                   ecp = strstr(cp, commandShell->noPrint);
                           } else {
                                   return cp;
                           }
                   }
         }          }
     }          return cp;
     return cp;  
 }  }
   
 /*-  /*-
Line 1763 
Line 1791 
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
 JobDoOutput(Job           *job,   /* the job whose output needs printing */  JobDoOutput(Job *job,           /* the job whose output needs printing */
     bool           finish)        /* true if this is the last time we'll be      bool finish)                /* true if this is the last time we'll be
                                    * called for this job */                                   * called for this job */
 {  {
     bool          gotNL = false;  /* true if got a newline */          bool gotNL = false;     /* true if got a newline */
     bool          fbuf;           /* true if our buffer filled up */          bool fbuf;              /* true if our buffer filled up */
     int           nr;             /* number of bytes read */          int nr;                 /* number of bytes read */
     int           i;              /* auxiliary index into outBuf */          int i;                  /* auxiliary index into outBuf */
     int           max;            /* limit for i (end of current data) */          int max;                /* limit for i (end of current data) */
     int           nRead;          /* (Temporary) number of bytes read */          int nRead;              /* (Temporary) number of bytes read */
   
     FILE          *oFILE;         /* Stream pointer to shell's output file */          FILE *oFILE;            /* Stream pointer to shell's output file */
     char          inLine[132];          char inLine[132];
   
   
     if (usePipes) {          if (usePipes) {
         /*  
          * Read as many bytes as will fit in the buffer.  
          */  
 end_loop:  
         gotNL = false;  
         fbuf = false;  
   
         nRead = read(job->inPipe, &job->outBuf[job->curPos],  
                          JOB_BUFSIZE - job->curPos);  
         if (nRead == -1) {  
             if (DEBUG(JOB)) {  
                 perror("JobDoOutput(piperead)");  
             }  
             nr = 0;  
         } else {  
             nr = nRead;  
         }  
   
         /*  
          * If we hit the end-of-file (the job is dead), we must flush its  
          * remaining output, so pretend we read a newline if there's any  
          * output remaining in the buffer.  
          * Also clear the 'finish' flag so we stop looping.  
          */  
         if (nr == 0 && job->curPos != 0) {  
             job->outBuf[job->curPos] = '\n';  
             nr = 1;  
             finish = false;  
         } else if (nr == 0) {  
             finish = false;  
         }  
   
         /*  
          * Look for the last newline in the bytes we just got. If there is  
          * one, break out of the loop with 'i' as its index and gotNL set  
          * true.  
          */  
         max = job->curPos + nr;  
         for (i = job->curPos + nr - 1; i >= job->curPos; i--) {  
             if (job->outBuf[i] == '\n') {  
                 gotNL = true;  
                 break;  
             } else if (job->outBuf[i] == '\0') {  
                 /*                  /*
                  * Why?                   * Read as many bytes as will fit in the buffer.
                  */                   */
                 job->outBuf[i] = ' ';  end_loop:
             }                  gotNL = false;
         }                  fbuf = false;
   
         if (!gotNL) {                  nRead = read(job->inPipe, &job->outBuf[job->curPos],
             job->curPos += nr;                      JOB_BUFSIZE - job->curPos);
             if (job->curPos == JOB_BUFSIZE) {                  if (nRead == -1) {
                 /*                          if (DEBUG(JOB)) {
                  * If we've run out of buffer space, we have no choice                                  perror("JobDoOutput(piperead)");
                  * but to print the stuff. sigh.                          }
                  */                          nr = 0;
                 fbuf = true;                  } else {
                 i = job->curPos;                          nr = nRead;
             }                  }
         }  
         if (gotNL || fbuf) {  
             /*  
              * Need to send the output to the screen. Null terminate it  
              * first, overwriting the newline character if there was one.  
              * So long as the line isn't one we should filter (according  
              * to the shell description), we print the line, preceded  
              * by a target banner if this target isn't the same as the  
              * one for which we last printed something.  
              * The rest of the data in the buffer are then shifted down  
              * to the start of the buffer and curPos is set accordingly.  
              */  
             job->outBuf[i] = '\0';  
             if (i >= job->curPos) {  
                 char *cp;  
   
                 cp = JobOutput(job, job->outBuf, &job->outBuf[i], false);  
   
                 /*                  /*
                  * There's still more in that thar buffer. This time, though,                   * If we hit the end-of-file (the job is dead), we must flush
                  * we know there's no newline at the end, so we add one of                   * its remaining output, so pretend we read a newline if
                  * our own free will.                   * there's any output remaining in the buffer.  Also clear the
                    * 'finish' flag so we stop looping.
                  */                   */
                 if (*cp != '\0') {                  if (nr == 0 && job->curPos != 0) {
                     if (job->node != lastNode) {                          job->outBuf[job->curPos] = '\n';
                         MESSAGE(stdout, job->node);                          nr = 1;
                         lastNode = job->node;                          finish = false;
                     }                  } else if (nr == 0) {
                     (void)fprintf(stdout, "%s%s", cp, gotNL ? "\n" : "");                          finish = false;
                     (void)fflush(stdout);  
                 }                  }
             }  
             if (i < max - 1) {  
                 /* shift the remaining characters down */  
                 (void)memcpy(job->outBuf, &job->outBuf[i + 1], max - (i + 1));  
                 job->curPos = max - (i + 1);  
   
             } else {  
                 /*                  /*
                  * We have written everything out, so we just start over                   * Look for the last newline in the bytes we just got. If there
                  * from the start of the buffer. No copying. No nothing.                   * is one, break out of the loop with 'i' as its index and
                    * gotNL set true.
                  */                   */
                 job->curPos = 0;                  max = job->curPos + nr;
             }                  for (i = job->curPos + nr - 1; i >= job->curPos; i--) {
         }                          if (job->outBuf[i] == '\n') {
         if (finish) {                                  gotNL = true;
             /*                                  break;
              * If the finish flag is true, we must loop until we hit                          } else if (job->outBuf[i] == '\0') {
              * end-of-file on the pipe. This is guaranteed to happen                                  /*
              * eventually since the other end of the pipe is now closed                                   * Why?
              * (we closed it explicitly and the child has exited). When                                   */
              * we do get an EOF, finish will be set false and we'll fall                                  job->outBuf[i] = ' ';
              * through and out.                          }
              */                  }
             goto end_loop;  
         }  
     } else {  
         /*  
          * We've been called to retrieve the output of the job from the  
          * temporary file where it's been squirreled away. This consists of  
          * opening the file, reading the output line by line, being sure not  
          * to print the noPrint line for the shell we used, then close and  
          * remove the temporary file. Very simple.  
          *  
          * Change to read in blocks and do FindSubString type things as for  
          * pipes? That would allow for "@echo -n..."  
          */  
         oFILE = fopen(job->outFile, "r");  
         if (oFILE != NULL) {  
             (void)fprintf(stdout, "Results of making %s:\n", job->node->name);  
             (void)fflush(stdout);  
             while (fgets(inLine, sizeof(inLine), oFILE) != NULL) {  
                 char    *cp, *endp, *oendp;  
   
                 cp = inLine;                  if (!gotNL) {
                 oendp = endp = inLine + strlen(inLine);                          job->curPos += nr;
                 if (endp != inLine && endp[-1] == '\n') {                          if (job->curPos == JOB_BUFSIZE) {
                     *--endp = '\0';                                  /*
                                    * If we've run out of buffer space, we have no
                                    * choice but to print the stuff. sigh.
                                    */
                                   fbuf = true;
                                   i = job->curPos;
                           }
                 }                  }
                 cp = JobOutput(job, inLine, endp, false);                  if (gotNL || fbuf) {
                           /*
                            * Need to send the output to the screen. Null
                            * terminate it first, overwriting the newline
                            * character if there was one.  So long as the line
                            * isn't one we should filter (according to the shell
                            * description), we print the line, preceded by a
                            * target banner if this target isn't the same as the
                            * one for which we last printed something.  The rest
                            * of the data in the buffer are then shifted down to
                            * the start of the buffer and curPos is set
                            * accordingly.
                            */
                           job->outBuf[i] = '\0';
                           if (i >= job->curPos) {
                                   char *cp;
   
                                   cp = JobOutput(job, job->outBuf,
                                       &job->outBuf[i], false);
   
                                   /*
                                    * There's still more in that thar buffer. This
                                    * time, though, we know there's no newline at
                                    * the end, so we add one of our own free will.
                                    */
                                   if (*cp != '\0') {
                                           if (job->node != lastNode) {
                                                   MESSAGE(stdout, job->node);
                                                   lastNode = job->node;
                                           }
                                           (void)fprintf(stdout, "%s%s", cp,
                                               gotNL ? "\n" : "");
                                           (void)fflush(stdout);
                                   }
                           }
                           if (i < max - 1) {
                                   /* shift the remaining characters down */
                                   (void)memcpy(job->outBuf, &job->outBuf[i + 1],
                                       max - (i + 1));
                                   job->curPos = max - (i + 1);
   
                           } else {
                                   /*
                                    * We have written everything out, so we just
                                    * start over from the start of the buffer. No
                                    * copying.  No nothing.
                                    */
                                   job->curPos = 0;
                           }
                   }
                   if (finish) {
                           /*
                            * If the finish flag is true, we must loop until we
                            * hit end-of-file on the pipe. This is guaranteed to
                            * happen eventually since the other end of the pipe is
                            * now closed (we closed it explicitly and the child
                            * has exited). When we do get an EOF, finish will be
                            * set false and we'll fall through and out.
                            */
                           goto end_loop;
                   }
           } else {
                 /*                  /*
                  * There's still more in that thar buffer. This time, though,                   * We've been called to retrieve the output of the job from the
                  * we know there's no newline at the end, so we add one of                   * temporary file where it's been squirreled away. This
                  * our own free will.                   * consists of opening the file, reading the output line by
                    * line, being sure not to print the noPrint line for the shell
                    * we used, then close and remove the temporary file. Very
                    * simple.
                    *
                    * Change to read in blocks and do FindSubString type things as
                    * for pipes? That would allow for "@echo -n..."
                  */                   */
                 (void)fprintf(stdout, "%s", cp);                  oFILE = fopen(job->outFile, "r");
                 (void)fflush(stdout);                  if (oFILE != NULL) {
                 if (endp != oendp) {                          (void)fprintf(stdout, "Results of making %s:\n",
                     (void)fprintf(stdout, "\n");                              job->node->name);
                     (void)fflush(stdout);                          (void)fflush(stdout);
                           while (fgets(inLine, sizeof(inLine), oFILE) != NULL) {
                                   char *cp, *endp, *oendp;
   
                                   cp = inLine;
                                   oendp = endp = inLine + strlen(inLine);
                                   if (endp != inLine && endp[-1] == '\n') {
                                           *--endp = '\0';
                                   }
                                   cp = JobOutput(job, inLine, endp, false);
   
                                   /*
                                    * There's still more in that thar buffer. This
                                    * time, though, we know there's no newline at
                                    * the end, so we add one of our own free will.
                                    */
                                   (void)fprintf(stdout, "%s", cp);
                                   (void)fflush(stdout);
                                   if (endp != oendp) {
                                           (void)fprintf(stdout, "\n");
                                           (void)fflush(stdout);
                                   }
                           }
                           (void)fclose(oFILE);
                           (void)eunlink(job->outFile);
                 }                  }
             }  
             (void)fclose(oFILE);  
             (void)eunlink(job->outFile);  
         }          }
     }  
 }  }
   
 /*-  /*-
Line 1956 
Line 1992 
 void  void
 Job_CatchChildren(bool block)   /* true if should block on the wait. */  Job_CatchChildren(bool block)   /* true if should block on the wait. */
 {  {
     pid_t         pid;          /* pid of dead child */          pid_t pid;              /* pid of dead child */
     Job           *job;         /* job descriptor for dead child */          Job *job;               /* job descriptor for dead child */
     LstNode       jnode;        /* list element for finding job */          LstNode jnode;          /* list element for finding job */
     int           status;       /* Exit/termination status */          int status;             /* Exit/termination status */
   
     /*          /*
      * Don't even bother if we know there's no one around.           * Don't even bother if we know there's no one around.
      */           */
     if (nLocal == 0) {          if (nLocal == 0) {
         return;                  return;
     }  
   
     while ((pid = waitpid((pid_t) -1, &status,  
                           (block?0:WNOHANG)|WUNTRACED)) > 0)  
     {  
         HandleSigs();  
         if (DEBUG(JOB)) {  
             (void)fprintf(stdout, "Process %ld exited or stopped.\n", (long)pid);  
             (void)fflush(stdout);  
         }          }
   
           while ((pid = waitpid((pid_t) -1, &status,
               (block?0:WNOHANG)|WUNTRACED)) > 0) {
                   HandleSigs();
                   if (DEBUG(JOB)) {
                           (void)fprintf(stdout,
                               "Process %ld exited or stopped.\n", (long)pid);
                           (void)fflush(stdout);
                   }
   
         jnode = Lst_Find(&jobs, JobCmpPid, &pid);  
   
         if (jnode == NULL) {                  jnode = Lst_Find(&jobs, JobCmpPid, &pid);
             if (WIFSIGNALED(status) && (WTERMSIG(status) == SIGCONT)) {  
                 jnode = Lst_Find(&stoppedJobs, JobCmpPid, &pid);  
                 if (jnode == NULL) {                  if (jnode == NULL) {
                     Error("Resumed child (%ld) not in table", (long)pid);                          if (WIFSIGNALED(status) &&
                     continue;                              (WTERMSIG(status) == SIGCONT)) {
                                   jnode = Lst_Find(&stoppedJobs, JobCmpPid, &pid);
                                   if (jnode == NULL) {
                                           Error("Resumed child (%ld) not in table", (long)pid);
                                           continue;
                                   }
                                   job = (Job *)Lst_Datum(jnode);
                                   Lst_Remove(&stoppedJobs, jnode);
                           } else {
                                   Error("Child (%ld) not in table?", (long)pid);
                                   continue;
                           }
                   } else {
                           job = (Job *)Lst_Datum(jnode);
                           Lst_Remove(&jobs, jnode);
                           nJobs -= 1;
                           if (jobFull && DEBUG(JOB)) {
                                   (void)fprintf(stdout,
                                       "Job queue is no longer full.\n");
                                   (void)fflush(stdout);
                           }
                           jobFull = false;
                           nLocal -= 1;
                 }                  }
                 job = (Job *)Lst_Datum(jnode);  
                 Lst_Remove(&stoppedJobs, jnode);  
             } else {  
                 Error("Child (%ld) not in table?", (long)pid);  
                 continue;  
             }  
         } else {  
             job = (Job *)Lst_Datum(jnode);  
             Lst_Remove(&jobs, jnode);  
             nJobs -= 1;  
             if (jobFull && DEBUG(JOB)) {  
                 (void)fprintf(stdout, "Job queue is no longer full.\n");  
                 (void)fflush(stdout);  
             }  
             jobFull = false;  
             nLocal -= 1;  
         }  
   
         JobFinish(job, &status);                  JobFinish(job, &status);
     }          }
 }  }
   
 /*-  /*-
Line 2025 
Line 2063 
 void  void
 Job_CatchOutput(void)  Job_CatchOutput(void)
 {  {
     int                   nfds;          int nfds;
     struct timeval        timeout;          struct timeval timeout;
     LstNode               ln;          LstNode ln;
     Job                   *job;          Job *job;
   
     (void)fflush(stdout);          (void)fflush(stdout);
     if (usePipes) {          if (usePipes) {
         int count = howmany(outputsn+1, NFDBITS) * sizeof(fd_mask);                  int count = howmany(outputsn+1, NFDBITS) * sizeof(fd_mask);
         fd_set *readfdsp = malloc(count);                  fd_set *readfdsp = malloc(count);
         if (readfdsp == NULL)                  if (readfdsp == NULL)
             return;                          return;
   
         memcpy(readfdsp, outputsp, count);                  memcpy(readfdsp, outputsp, count);
         timeout.tv_sec = SEL_SEC;                  timeout.tv_sec = SEL_SEC;
         timeout.tv_usec = SEL_USEC;                  timeout.tv_usec = SEL_USEC;
   
         if ((nfds = select(outputsn+1, readfdsp, (fd_set *) 0,                  if ((nfds = select(outputsn+1, readfdsp, (fd_set *) 0,
                            (fd_set *) 0, &timeout)) <= 0) {                      (fd_set *) 0, &timeout)) <= 0) {
             HandleSigs();                          HandleSigs();
             free(readfdsp);                          free(readfdsp);
             return;                          return;
         } else {                  } else {
             HandleSigs();                          HandleSigs();
             for (ln = Lst_First(&jobs); nfds && ln != NULL; ln = Lst_Adv(ln)) {                          for (ln = Lst_First(&jobs); nfds && ln != NULL;
                 job = (Job *)Lst_Datum(ln);                              ln = Lst_Adv(ln)) {
                 if (FD_ISSET(job->inPipe, readfdsp)) {                                  job = (Job *)Lst_Datum(ln);
                     JobDoOutput(job, false);                                  if (FD_ISSET(job->inPipe, readfdsp)) {
                     nfds -= 1;                                          JobDoOutput(job, false);
                                           nfds -= 1;
                                   }
                           }
                 }                  }
             }                  free(readfdsp);
         }          }
         free(readfdsp);  
     }  
 }  }
   
 /*-  /*-
Line 2073 
Line 2112 
 void  void
 Job_Make(GNode *gn)  Job_Make(GNode *gn)
 {  {
     (void)JobStart(gn, 0, NULL);          (void)JobStart(gn, 0, NULL);
 }  }
   
 /*-  /*-
Line 2091 
Line 2130 
     int           maxlocal) /* the greatest number of local jobs which may      int           maxlocal) /* the greatest number of local jobs which may
                              * be running at once. */                               * be running at once. */
 {  {
     GNode         *begin;     /* node for commands to do at the very start */          GNode *begin;     /* node for commands to do at the very start */
     int           tfd;          int tfd;
   
     (void)strlcpy(tfile, TMPPAT, sizeof(tfile));          (void)strlcpy(tfile, TMPPAT, sizeof(tfile));
     if ((tfd = mkstemp(tfile)) == -1)          if ((tfd = mkstemp(tfile)) == -1)
         Punt("Cannot create temp file: %s", strerror(errno));                  Punt("Cannot create temp file: %s", strerror(errno));
     else          else
         (void)close(tfd);                  (void)close(tfd);
   
     Static_Lst_Init(&jobs);          Static_Lst_Init(&jobs);
     Static_Lst_Init(&stoppedJobs);          Static_Lst_Init(&stoppedJobs);
     maxJobs =     maxproc;          maxJobs =         maxproc;
     maxLocal =    maxlocal;          maxLocal =        maxlocal;
     nJobs =       0;          nJobs =   0;
     nLocal =      0;          nLocal =          0;
     jobFull =     false;          jobFull =         false;
   
     aborting =    0;          aborting =        0;
     errors =      0;          errors =          0;
   
     lastNode =    NULL;          lastNode =        NULL;
   
     if (maxJobs == 1) {          if (maxJobs == 1) {
                   /*
                    * If only one job can run at a time, there's no need for a
                    * banner, no is there?
                    */
                   targFmt = "";
           } else {
                   targFmt = TARG_FMT;
           }
   
           if (shellPath == NULL) {
                   /*
                    * The user didn't specify a shell to use, so we are using the
                    * default one... Both the absolute path and the last component
                    * must be set. The last component is taken from the 'name'
                    * field of the default shell description pointed-to by
                    * commandShell.  All default shells are located in
                    * _PATH_DEFSHELLDIR.
                    */
                   shellName = commandShell->name;
                   shellPath = Str_concat(_PATH_DEFSHELLDIR, shellName, '/');
           }
   
           if (commandShell->exit == NULL) {
                   commandShell->exit = "";
           }
           if (commandShell->echo == NULL) {
                   commandShell->echo = "";
           }
   
         /*          /*
          * If only one job can run at a time, there's no need for a banner,           * Catch the four signals that POSIX specifies if they aren't ignored.
          * no is there?           * JobPassSig will take care of calling JobInterrupt if appropriate.
          */           */
         targFmt = "";          if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
     } else {                  (void)signal(SIGINT, SigHandler);
         targFmt = TARG_FMT;          }
     }          if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
                   (void)signal(SIGHUP, SigHandler);
     if (shellPath == NULL) {          }
           if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
                   (void)signal(SIGQUIT, SigHandler);
           }
           if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
                   (void)signal(SIGTERM, SigHandler);
           }
         /*          /*
          * The user didn't specify a shell to use, so we are using the           * There are additional signals that need to be caught and passed if
          * default one... Both the absolute path and the last component           * either the export system wants to be told directly of signals or if
          * must be set. The last component is taken from the 'name' field           * we're giving each job its own process group (since then it won't get
          * of the default shell description pointed-to by commandShell.           * signals from the terminal driver as we own the terminal)
          * All default shells are located in _PATH_DEFSHELLDIR.  
          */           */
         shellName = commandShell->name;  
         shellPath = Str_concat(_PATH_DEFSHELLDIR, shellName, '/');  
     }  
   
     if (commandShell->exit == NULL) {  
         commandShell->exit = "";  
     }  
     if (commandShell->echo == NULL) {  
         commandShell->echo = "";  
     }  
   
     /*  
      * Catch the four signals that POSIX specifies if they aren't ignored.  
      * JobPassSig will take care of calling JobInterrupt if appropriate.  
      */  
     if (signal(SIGINT, SIG_IGN) != SIG_IGN) {  
         (void)signal(SIGINT, SigHandler);  
     }  
     if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {  
         (void)signal(SIGHUP, SigHandler);  
     }  
     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {  
         (void)signal(SIGQUIT, SigHandler);  
     }  
     if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {  
         (void)signal(SIGTERM, SigHandler);  
     }  
     /*  
      * There are additional signals that need to be caught and passed if  
      * either the export system wants to be told directly of signals or if  
      * we're giving each job its own process group (since then it won't get  
      * signals from the terminal driver as we own the terminal)  
      */  
 #if defined(USE_PGRP)  #if defined(USE_PGRP)
     if (signal(SIGTSTP, SIG_IGN) != SIG_IGN) {          if (signal(SIGTSTP, SIG_IGN) != SIG_IGN) {
         (void)signal(SIGTSTP, SigHandler);                  (void)signal(SIGTSTP, SigHandler);
     }          }
     if (signal(SIGTTOU, SIG_IGN) != SIG_IGN) {          if (signal(SIGTTOU, SIG_IGN) != SIG_IGN) {
         (void)signal(SIGTTOU, SigHandler);                  (void)signal(SIGTTOU, SigHandler);
     }          }
     if (signal(SIGTTIN, SIG_IGN) != SIG_IGN) {          if (signal(SIGTTIN, SIG_IGN) != SIG_IGN) {
         (void)signal(SIGTTIN, SigHandler);                  (void)signal(SIGTTIN, SigHandler);
     }          }
     if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) {          if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) {
         (void)signal(SIGWINCH, SigHandler);                  (void)signal(SIGWINCH, SigHandler);
     }          }
 #endif  #endif
   
     begin = Targ_FindNode(".BEGIN", TARG_NOCREATE);          begin = Targ_FindNode(".BEGIN", TARG_NOCREATE);
   
     if (begin != NULL) {          if (begin != NULL) {
         JobStart(begin, JOB_SPECIAL, (Job *)0);                  JobStart(begin, JOB_SPECIAL, (Job *)0);
         while (nJobs) {                  while (nJobs) {
             Job_CatchOutput();                          Job_CatchOutput();
             Job_CatchChildren(!usePipes);                          Job_CatchChildren(!usePipes);
                   }
         }          }
     }          postCommands = Targ_FindNode(".END", TARG_CREATE);
     postCommands = Targ_FindNode(".END", TARG_CREATE);  
 }  }
   
 /*-  /*-
Line 2206 
Line 2246 
 bool  bool
 Job_Full(void)  Job_Full(void)
 {  {
     return aborting || jobFull;          return aborting || jobFull;
 }  }
   
 /*-  /*-
Line 2224 
Line 2264 
 bool  bool
 Job_Empty(void)  Job_Empty(void)
 {  {
     if (nJobs == 0) {          if (nJobs == 0) {
         if (!Lst_IsEmpty(&stoppedJobs) && !aborting) {                  if (!Lst_IsEmpty(&stoppedJobs) && !aborting) {
             /*                          /*
              * The job table is obviously not full if it has no jobs in                           * The job table is obviously not full if it has no
              * it...Try and restart the stopped jobs.                           * jobs in it...Try and restart the stopped jobs.
              */                           */
             jobFull = false;                          jobFull = false;
             JobRestartJobs();                          JobRestartJobs();
             return false;                          return false;
                   } else {
                           return true;
                   }
         } else {          } else {
             return true;                  return false;
         }          }
     } else {  
         return false;  
     }  
 }  }
   
 /*-  /*-
Line 2254 
Line 2294 
 static void  static void
 JobInterrupt(int runINTERRUPT,  /* Non-zero if commands for the .INTERRUPT  JobInterrupt(int runINTERRUPT,  /* Non-zero if commands for the .INTERRUPT
                                  * target should be executed */                                   * target should be executed */
     int     signo)              /* signal received */      int signo)                  /* signal received */
 {  {
     LstNode       ln;           /* element in job table */          LstNode ln;             /* element in job table */
     Job           *job;         /* job descriptor in that element */          Job  *job;              /* job descriptor in that element */
     GNode         *interrupt;   /* the node describing the .INTERRUPT target */          GNode *interrupt;       /* the node describing the .INTERRUPT target */
   
     aborting = ABORT_INTERRUPT;          aborting = ABORT_INTERRUPT;
   
     for (ln = Lst_First(&jobs); ln != NULL; ln = Lst_Adv(ln)) {          for (ln = Lst_First(&jobs); ln != NULL; ln = Lst_Adv(ln)) {
         job = (Job *)Lst_Datum(ln);                  job = (Job *)Lst_Datum(ln);
   
         if (!Targ_Precious(job->node)) {                  if (!Targ_Precious(job->node)) {
             const char  *file = job->node->path == NULL ?                          const char *file = job->node->path == NULL ?
                                  job->node->name :                              job->node->name : job->node->path;
                                  job->node->path;                          if (!noExecute && eunlink(file) != -1) {
             if (!noExecute && eunlink(file) != -1) {                                  Error("*** %s removed", file);
                 Error("*** %s removed", file);                          }
             }                  }
                   if (job->pid) {
                           if (DEBUG(JOB)) {
                                   (void)fprintf(stdout,
                                       "JobInterrupt passing signal to child %ld.\n",
                                       (long)job->pid);
                                   (void)fflush(stdout);
                           }
                           KILL(job->pid, signo);
                   }
         }          }
         if (job->pid) {  
             if (DEBUG(JOB)) {  
                 (void)fprintf(stdout,  
                                "JobInterrupt passing signal to child %ld.\n",  
                                (long)job->pid);  
                 (void)fflush(stdout);  
             }  
             KILL(job->pid, signo);  
         }  
     }  
   
     if (runINTERRUPT && !touchFlag) {          if (runINTERRUPT && !touchFlag) {
         interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);                  interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
         if (interrupt != NULL) {                  if (interrupt != NULL) {
             ignoreErrors = false;                          ignoreErrors = false;
   
             JobStart(interrupt, JOB_IGNDOTS, (Job *)0);                          JobStart(interrupt, JOB_IGNDOTS, (Job *)0);
             while (nJobs) {                          while (nJobs) {
                 Job_CatchOutput();                                  Job_CatchOutput();
                 Job_CatchChildren(!usePipes);                                  Job_CatchChildren(!usePipes);
             }                          }
                   }
         }          }
     }          (void)eunlink(tfile);
     (void)eunlink(tfile);          exit(signo);
     exit(signo);  
 }  }
   
 /*  /*
Line 2317 
Line 2356 
 int  int
 Job_Finish(void)  Job_Finish(void)
 {  {
     if (postCommands != NULL && !Lst_IsEmpty(&postCommands->commands)) {          if (postCommands != NULL && !Lst_IsEmpty(&postCommands->commands)) {
         if (errors) {                  if (errors) {
             Error("Errors reported so .END ignored");                          Error("Errors reported so .END ignored");
         } else {                  } else {
             JobStart(postCommands, JOB_SPECIAL | JOB_IGNDOTS, NULL);                          JobStart(postCommands, JOB_SPECIAL | JOB_IGNDOTS, NULL);
   
             while (nJobs) {                          while (nJobs) {
                 Job_CatchOutput();                                  Job_CatchOutput();
                 Job_CatchChildren(!usePipes);                                  Job_CatchChildren(!usePipes);
             }                          }
                   }
         }          }
     }          (void)eunlink(tfile);
     (void)eunlink(tfile);          return errors;
     return errors;  
 }  }
   
 /*-  /*-
Line 2346 
Line 2385 
 void  void
 Job_End(void)  Job_End(void)
 {  {
     efree(shellArgv);          efree(shellArgv);
 }  }
 #endif  #endif
   
Line 2364 
Line 2403 
 void  void
 Job_Wait(void)  Job_Wait(void)
 {  {
     aborting = ABORT_WAIT;          aborting = ABORT_WAIT;
     while (nJobs != 0) {          while (nJobs != 0) {
         Job_CatchOutput();                  Job_CatchOutput();
         Job_CatchChildren(!usePipes);                  Job_CatchChildren(!usePipes);
     }          }
     aborting = 0;          aborting = 0;
 }  }
   
 /*-  /*-
Line 2386 
Line 2425 
 void  void
 Job_AbortAll(void)  Job_AbortAll(void)
 {  {
     LstNode             ln;     /* element in job table */          LstNode ln;     /* element in job table */
     Job                 *job;   /* the job descriptor in that element */          Job *job;       /* the job descriptor in that element */
     int                 foo;          int foo;
   
     aborting = ABORT_ERROR;          aborting = ABORT_ERROR;
   
     if (nJobs) {          if (nJobs) {
         for (ln = Lst_First(&jobs); ln != NULL; ln = Lst_Adv(ln)) {                  for (ln = Lst_First(&jobs); ln != NULL; ln = Lst_Adv(ln)) {
             job = (Job *)Lst_Datum(ln);                          job = (Job *)Lst_Datum(ln);
   
             /*                          /*
              * kill the child process with increasingly drastic signals to make                           * kill the child process with increasingly drastic
              * darn sure it's dead.                           * signals to make darn sure it's dead.
              */                           */
             KILL(job->pid, SIGINT);                          KILL(job->pid, SIGINT);
             KILL(job->pid, SIGKILL);                          KILL(job->pid, SIGKILL);
                   }
         }          }
     }  
   
     /*          /*
      * Catch as many children as want to report in at first, then give up           * Catch as many children as want to report in at first, then give up
      */           */
     while (waitpid(-1, &foo, WNOHANG) > 0)          while (waitpid(-1, &foo, WNOHANG) > 0)
         continue;                  continue;
     (void)eunlink(tfile);          (void)eunlink(tfile);
 }  }
   
 /*-  /*-
Line 2427 
Line 2466 
 static void  static void
 JobRestartJobs(void)  JobRestartJobs(void)
 {  {
     Job *job;          Job *job;
   
     while (!jobFull && (job = (Job *)Lst_DeQueue(&stoppedJobs)) != NULL) {          while (!jobFull && (job = (Job *)Lst_DeQueue(&stoppedJobs)) != NULL) {
         if (DEBUG(JOB)) {                  if (DEBUG(JOB)) {
             (void)fprintf(stdout,                          (void)fprintf(stdout,
                        "Job queue is not full. Restarting a stopped job.\n");                              "Job queue is not full. Restarting a stopped job.\n");
             (void)fflush(stdout);                          (void)fflush(stdout);
                   }
                   JobRestart(job);
         }          }
         JobRestart(job);  
     }  
 }  }

Legend:
Removed from v.1.65  
changed lines
  Added in v.1.66