Lst_AtEnd(jobs, job);
Lst_AtEnd(&jobs, job);
JobFinish(job, status)    Job         *job;	      	  /* job to finish */    int	  	*status;     	  /* sub-why job went away */{    Boolean 	 done;    if ((WIFEXITED(*status) &&	 (((WEXITSTATUS(*status) != 0) && !(job->flags & JOB_IGNERR)))) ||	(WIFSIGNALED(*status) && (WTERMSIG(*status) != SIGCONT)))    {	/*	 * If it exited non-zero and either we're doing things our	 * way or we're not ignoring errors, the job is finished.	 * Similarly, if the shell died because of a signal	 * the job is also finished. In these	 * cases, finish out the job's output before printing the exit	 * status...	 */#ifdef REMOTE	KILL(job->pid, SIGCONT);#endif	JobClose(job);	if (job->cmdFILE != NULL && job->cmdFILE != stdout) {	   (void) fclose(job->cmdFILE);	}	done = TRUE;#ifdef REMOTE	if (job->flags & JOB_REMOTE)	    Rmt_Done(job->rmtID, job->node);#endif    } else if (WIFEXITED(*status)) {	/*	 * Deal with ignored errors in -B mode. We need to print a message	 * telling of the ignored error as well as setting status.w_status	 * to 0 so the next command gets run. To do this, we set done to be	 * TRUE if in -B mode and the job exited non-zero.	 */	done = WEXITSTATUS(*status) != 0;	/*	 * Old comment said: "Note we don't	 * want to close down any of the streams until we know we're at the	 * end."	 * But we do. Otherwise when are we going to print the rest of the	 * stuff?	 */	JobClose(job);#ifdef REMOTE	if (job->flags & JOB_REMOTE)	    Rmt_Done(job->rmtID, job->node);#endif /* REMOTE */    } 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 {	    out = stdout;	}	if (WIFEXITED(*status)) {	    if (DEBUG(JOB)) {		(void) fprintf(stdout, "Process %d exited.\n", job->pid);		(void) fflush(stdout);	    }	    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) {		    *status = 0;		}	    } else if (DEBUG(JOB)) {		if (usePipes && job->node != lastNode) {		    MESSAGE(out, job->node);		    lastNode = job->node;		}		(void) fprintf(out, "*** Completed successfully\n");	    }	} else if (WIFSTOPPED(*status)) {	    if (DEBUG(JOB)) {		(void) fprintf(stdout, "Process %d stopped.\n", job->pid);		(void) fflush(stdout);	    }	    if (usePipes && job->node != lastNode) {		MESSAGE(out, job->node);		lastNode = job->node;	    }	    if (!(job->flags & JOB_REMIGRATE)) {		(void) fprintf(out, "*** Stopped -- signal %d\n",		    WSTOPSIG(*status));	    }	    job->flags |= JOB_RESUME;	    Lst_AtEnd(stoppedJobs, job);#ifdef REMOTE	    if (job->flags & JOB_REMIGRATE)		JobRestart(job);#endif	    (void) fflush(out);	    return;	} else if (WTERMSIG(*status) == SIGCONT) {	    /*	     * If the beastie has continued, shift the Job from the stopped	     * list to the running one (or re-stop it if concurrency is	     * exceeded) and go and get another child.	     */	    if (job->flags & (JOB_RESUME|JOB_REMIGRATE|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 %d was not continuing.\n",				   job->pid);		    (void) fflush(stdout);		}#ifdef notdef		/*		 * 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	    }	    job->flags &= ~JOB_CONTINUING; 	    Lst_AtEnd(jobs, job);	    nJobs += 1;	    if (!(job->flags & JOB_REMOTE)) {		if (DEBUG(JOB)) {		    (void) fprintf(stdout,				   "Process %d is continuing locally.\n",				   job->pid);		    (void) fflush(stdout);  		}		nLocal += 1;	    }	    if (nJobs == maxJobs) {		jobFull = TRUE;		if (DEBUG(JOB)) {		    (void) fprintf(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);    }    /*     * 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) &&	!Lst_IsAtEnd(job->node->commands))) {	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 &&	(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	 * wasn't given, we set the aborting flag so no more jobs get	 * started.	 */	aborting = ABORT_ERROR;    }    if ((aborting == ABORT_ERROR) && Job_Empty())	/*	 * If we are aborting and the job table is now empty, we finish.	 */	Finish(errors);}