home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Shells / zsh-3.0.5-MIHS / src / Src / jobs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-25  |  19.8 KB  |  860 lines

  1. /*
  2.  * $Id: jobs.c,v 2.17 1996/10/15 20:16:35 hzoli Exp $
  3.  *
  4.  * jobs.c - job control
  5.  *
  6.  * This file is part of zsh, the Z shell.
  7.  *
  8.  * Copyright (c) 1992-1996 Paul Falstad
  9.  * All rights reserved.
  10.  *
  11.  * Permission is hereby granted, without written agreement and without
  12.  * license or royalty fees, to use, copy, modify, and distribute this
  13.  * software and to distribute modified versions of this software for any
  14.  * purpose, provided that the above copyright notice and the following
  15.  * two paragraphs appear in all copies of this software.
  16.  *
  17.  * In no event shall Paul Falstad or the Zsh Development Group be liable
  18.  * to any party for direct, indirect, special, incidental, or consequential
  19.  * damages arising out of the use of this software and its documentation,
  20.  * even if Paul Falstad and the Zsh Development Group have been advised of
  21.  * the possibility of such damage.
  22.  *
  23.  * Paul Falstad and the Zsh Development Group specifically disclaim any
  24.  * warranties, including, but not limited to, the implied warranties of
  25.  * merchantability and fitness for a particular purpose.  The software
  26.  * provided hereunder is on an "as is" basis, and Paul Falstad and the
  27.  * Zsh Development Group have no obligation to provide maintenance,
  28.  * support, updates, enhancements, or modifications.
  29.  *
  30.  */
  31.  
  32. #include "zsh.h"
  33.  
  34. /* empty job structure for quick clearing of jobtab entries */
  35.  
  36. static struct job zero;        /* static variables are initialized to zero */
  37.  
  38. struct timeval dtimeval, now;
  39.  
  40. /* Diff two timevals for elapsed-time computations */
  41.  
  42. /**/
  43. struct timeval *
  44. dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2)
  45. {
  46.     dt->tv_sec = t2->tv_sec - t1->tv_sec;
  47.     dt->tv_usec = t2->tv_usec - t1->tv_usec;
  48.     if (dt->tv_usec < 0) {
  49.     dt->tv_usec += 1000000.0;
  50.     dt->tv_sec -= 1.0;
  51.     }
  52.     return dt;
  53. }
  54.  
  55. /* change job table entry from stopped to running */
  56.  
  57. /**/
  58. void
  59. makerunning(Job jn)
  60. {
  61.     Process pn;
  62.  
  63.     jn->stat &= ~STAT_STOPPED;
  64.     for (pn = jn->procs; pn; pn = pn->next)
  65.     if (WIFSTOPPED(pn->status) && 
  66.         (!(jn->stat & STAT_SUPERJOB) || pn->next))
  67.         pn->status = SP_RUNNING;
  68.  
  69.     if (jn->stat & STAT_SUPERJOB)
  70.     makerunning(jobtab + jn->other);
  71. }
  72.  
  73. /* Find process and job associated with pid.         *
  74.  * Return 1 if search was successful, else return 0. */
  75.  
  76. /**/
  77. int
  78. findproc(pid_t pid, Job *jptr, Process *pptr)
  79. {
  80.     Process pn;
  81.     int i;
  82.  
  83.     for (i = 1; i < MAXJOB; i++)
  84.     for (pn = jobtab[i].procs; pn; pn = pn->next)
  85.         if (pn->pid == pid) {
  86.         *pptr = pn;
  87.         *jptr = jobtab + i;
  88.         return 1;
  89.         }
  90.  
  91.     return 0;
  92. }
  93.  
  94. /* Update status of process that we have just WAIT'ed for */
  95.  
  96. /**/
  97. void
  98. update_process(Process pn, int status)
  99. {
  100.     struct timezone dummy_tz;
  101.     long childs, childu;
  102.  
  103.     childs = shtms.tms_cstime;
  104.     childu = shtms.tms_cutime;
  105.     times(&shtms);                          /* get time-accounting info          */
  106.  
  107.     pn->status = status;                    /* save the status returned by WAIT  */
  108.     pn->ti.st  = shtms.tms_cstime - childs; /* compute process system space time */
  109.     pn->ti.ut  = shtms.tms_cutime - childu; /* compute process user space time   */
  110.  
  111.     gettimeofday(&pn->endtime, &dummy_tz);  /* record time process exited        */
  112. }
  113.  
  114. /* Update status of job, possibly printing it */
  115.  
  116. /**/
  117. void
  118. update_job(Job jn)
  119. {
  120.     Process pn;
  121.     int job;
  122.     int val = 0, status = 0;
  123.     int somestopped = 0, inforeground = 0;
  124.  
  125.     for (pn = jn->procs; pn; pn = pn->next) {
  126.     if (pn->status == SP_RUNNING)      /* some processes in this job are running       */
  127.         return;                        /* so no need to update job table entry         */
  128.     if (WIFSTOPPED(pn->status))        /* some processes are stopped                   */
  129.         somestopped = 1;               /* so job is not done, but entry needs updating */
  130.     if (!pn->next)                     /* last job in pipeline determines exit status  */
  131.         val = (WIFSIGNALED(pn->status)) ? 0200 | WTERMSIG(pn->status) :
  132.         WEXITSTATUS(pn->status);
  133.     if (pn->pid == jn->gleader)        /* if this process is process group leader      */
  134.         status = pn->status;
  135.     }
  136.  
  137.     job = jn - jobtab;   /* compute job number */
  138.  
  139.     if (somestopped) {
  140.     if (shout && job == thisjob) {
  141.         if (!jn->ty)
  142.         jn->ty = (struct ttyinfo *) zalloc(sizeof(struct ttyinfo));
  143.         gettyinfo(jn->ty);
  144.     }
  145.     if (jn->stat & STAT_STOPPED)
  146.         return;
  147.     } else {                   /* job is done, so remember return value */
  148.     lastval2 = val;
  149.     /* If last process was run in the current shell, keep old status
  150.      * and let it handle its own traps
  151.      */
  152.     if (job == thisjob && !(jn->stat & STAT_CURSH)) {
  153.       lastval = val;
  154.       inforeground = 1;
  155.     }
  156.     }
  157.  
  158.     if (shout && !ttyfrozen && !jn->stty_in_env &&
  159.     job == thisjob && !somestopped && !(jn->stat & STAT_NOSTTY))
  160.     gettyinfo(&shttyinfo);
  161.  
  162.     if (isset(MONITOR)) {
  163.     pid_t pgrp = gettygrp();           /* get process group of tty      */
  164.  
  165.     /* is this job in the foreground of an interactive shell? */
  166.     if (mypgrp != pgrp && inforeground &&
  167.         (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) {
  168.         attachtty(mypgrp);
  169.         adjustwinsize(0);   /* check window size and adjust if necessary */
  170.     }
  171.     }
  172.  
  173.     if (somestopped && jn->stat & STAT_SUPERJOB)
  174.     return;
  175.     jn->stat |= (somestopped) ? STAT_CHANGED | STAT_STOPPED :
  176.     STAT_CHANGED | STAT_DONE;
  177.     if ((jn->stat & (STAT_DONE | STAT_STOPPED)) == STAT_STOPPED) {
  178.     prevjob = curjob;
  179.     curjob = job;
  180.     }
  181.     if ((isset(NOTIFY) || job == thisjob) && (jn->stat & STAT_LOCKED)) {
  182.     printjob(jn, !!isset(LONGLISTJOBS), 0);
  183.     if (zleactive)
  184.         refresh();
  185.     }
  186.     if (sigtrapped[SIGCHLD] && job != thisjob)
  187.     dotrap(SIGCHLD);
  188.  
  189.     /* When MONITOR is set, the foreground process runs in a different *
  190.      * process group from the shell, so the shell will not receive     *
  191.      * terminal signals, therefore we we pretend that the shell got    *
  192.      * the signal too.                                                 */
  193.     if (inforeground && isset(MONITOR) && WIFSIGNALED(status)) {
  194.     int sig = WTERMSIG(status);
  195.  
  196.     if (sig == SIGINT || sig == SIGQUIT) {
  197.         if (sigtrapped[sig]) {
  198.         dotrap(sig);
  199.         /* We keep the errflag as set or not by dotrap.
  200.          * This is to fulfil the promise to carry on
  201.          * with the jobs if trap returns zero.
  202.          * Setting breaks = loops ensures a consistent return
  203.          * status if inside a loop.  Maybe the code in loops
  204.          * should be changed.
  205.          */
  206.         if (errflag)
  207.             breaks = loops;
  208.         } else {
  209.         breaks = loops;
  210.         errflag = 1;
  211.         }
  212.     }
  213.     }
  214. }
  215.  
  216. /* lng = 0 means jobs    *
  217.  * lng = 1 means jobs -l *
  218.  * lng = 2 means jobs -p 
  219.  *
  220.  * synch = 0 means asynchronous
  221.  * synch = 1 means synchronous
  222.  * synch = 2 means called synchronously from jobs
  223. */
  224.  
  225. /**/
  226. void
  227. printjob(Job jn, int lng, int synch)
  228. {
  229.     Process pn;
  230.     int job = jn - jobtab, len = 9, sig, sflag = 0, llen;
  231.     int conted = 0, lineleng = columns, skip = 0, doputnl = 0;
  232.     FILE *fout = (synch == 2) ? stdout : shout;
  233.  
  234.     if (jn->stat & STAT_NOPRINT)
  235.     return;
  236.  
  237.     if (lng < 0) {
  238.     conted = 1;
  239.     lng = 0;
  240.     }
  241.  
  242. /* find length of longest signame, check to see */
  243. /* if we really need to print this job          */
  244.  
  245.     for (pn = jn->procs; pn; pn = pn->next) {
  246.     if (jn->stat & STAT_SUPERJOB &&
  247.         jn->procs->status == SP_RUNNING && !pn->next)
  248.         pn->status = SP_RUNNING;
  249.     if (pn->status != SP_RUNNING)
  250.         if (WIFSIGNALED(pn->status)) {
  251.         sig = WTERMSIG(pn->status);
  252.         llen = strlen(sigmsg[sig]);
  253.         if (WCOREDUMP(pn->status))
  254.             llen += 14;
  255.         if (llen > len)
  256.             len = llen;
  257.         if (sig != SIGINT && sig != SIGPIPE)
  258.             sflag = 1;
  259.         if (job == thisjob && sig == SIGINT)
  260.             doputnl = 1;
  261.         } else if (WIFSTOPPED(pn->status)) {
  262.         sig = WSTOPSIG(pn->status);
  263.         if ((int)strlen(sigmsg[sig]) > len)
  264.             len = strlen(sigmsg[sig]);
  265.         if (job == thisjob && sig == SIGTSTP)
  266.             doputnl = 1;
  267.         } else if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
  268.                WEXITSTATUS(pn->status))
  269.         sflag = 1;
  270.     }
  271.  
  272. /* print if necessary */
  273.  
  274.     if (interact && jobbing && ((jn->stat & STAT_STOPPED) || sflag ||
  275.                 job != thisjob)) {
  276.     int len2, fline = 1;
  277.     Process qn;
  278.  
  279.     if (!synch)
  280.         trashzle();
  281.     if (doputnl && !synch)
  282.         putc('\n', fout);
  283.     for (pn = jn->procs; pn;) {
  284.         len2 = ((job == thisjob) ? 5 : 10) + len;    /* 2 spaces */
  285.         if (lng)
  286.         qn = pn->next;
  287.         else
  288.         for (qn = pn->next; qn; qn = qn->next) {
  289.             if (qn->status != pn->status)
  290.             break;
  291.             if ((int)strlen(qn->text) + len2 + ((qn->next) ? 3 : 0) > lineleng)
  292.             break;
  293.             len2 += strlen(qn->text) + 2;
  294.         }
  295.         if (job != thisjob)
  296.         if (fline)
  297.             fprintf(fout, "[%ld]  %c ",
  298.                 (long)(jn - jobtab),
  299.                 (job == curjob) ? '+'
  300.                 : (job == prevjob) ? '-' : ' ');
  301.         else
  302.             fprintf(fout, (job > 9) ? "        " : "       ");
  303.         else
  304.         fprintf(fout, "zsh: ");
  305.         if (lng)
  306.         if (lng == 1)
  307.             fprintf(fout, "%ld ", (long) pn->pid);
  308.         else {
  309.             pid_t x = jn->gleader;
  310.  
  311.             fprintf(fout, "%ld ", (long) x);
  312.             do
  313.             skip++;
  314.             while ((x /= 10));
  315.             skip++;
  316.             lng = 0;
  317.         } else
  318.         fprintf(fout, "%*s", skip, "");
  319.         if (pn->status == SP_RUNNING)
  320.         if (!conted)
  321.             fprintf(fout, "running%*s", len - 7 + 2, "");
  322.         else
  323.             fprintf(fout, "continued%*s", len - 9 + 2, "");
  324.         else if (WIFEXITED(pn->status))
  325.         if (WEXITSTATUS(pn->status))
  326.             fprintf(fout, "exit %-4d%*s", WEXITSTATUS(pn->status),
  327.                 len - 9 + 2, "");
  328.         else
  329.             fprintf(fout, "done%*s", len - 4 + 2, "");
  330.         else if (WIFSTOPPED(pn->status))
  331.         fprintf(fout, "%-*s", len + 2, sigmsg[WSTOPSIG(pn->status)]);
  332.         else if (WCOREDUMP(pn->status))
  333.         fprintf(fout, "%s (core dumped)%*s",
  334.             sigmsg[WTERMSIG(pn->status)],
  335.             (int)(len - 14 + 2 - strlen(sigmsg[WTERMSIG(pn->status)])), "");
  336.         else
  337.         fprintf(fout, "%-*s", len + 2, sigmsg[WTERMSIG(pn->status)]);
  338.         for (; pn != qn; pn = pn->next)
  339.         fprintf(fout, (pn->next) ? "%s | " : "%s", pn->text);
  340.         putc('\n', fout);
  341.         fline = 0;
  342.     }
  343.     fflush(fout);
  344.     } else if (doputnl && interact && !synch) {
  345.     putc('\n', fout);
  346.     fflush(fout);
  347.     }
  348.  
  349. /* print "(pwd now: foo)" messages */
  350.  
  351.     if (interact && job == thisjob && strcmp(jn->pwd, pwd)) {
  352.     fprintf(shout, "(pwd now: ");
  353.     fprintdir(pwd, shout);
  354.     fprintf(shout, ")\n");
  355.     fflush(shout);
  356.     }
  357. /* delete job if done */
  358.  
  359.     if (jn->stat & STAT_DONE) {
  360.     if (should_report_time(jn))
  361.         dumptime(jn);
  362.     deletejob(jn);
  363.     if (job == curjob) {
  364.         curjob = prevjob;
  365.         prevjob = job;
  366.     }
  367.     if (job == prevjob)
  368.         setprevjob();
  369.     } else
  370.     jn->stat &= ~STAT_CHANGED;
  371. }
  372.  
  373. /**/
  374. void
  375. deletefilelist(LinkList file_list)
  376. {
  377.     char *s;
  378.     if (file_list) {
  379.     while ((s = (char *)getlinknode(file_list))) {
  380.         unlink(s);
  381.         zsfree(s);
  382.     }
  383.     zfree(file_list, sizeof(struct linklist));
  384.     }
  385. }
  386.  
  387. /**/
  388. void
  389. deletejob(Job jn)
  390. {
  391.     struct process *pn, *nx;
  392.  
  393.     for (pn = jn->procs; pn; pn = nx) {
  394.     nx = pn->next;
  395.     zfree(pn, sizeof(struct process));
  396.     }
  397.     zsfree(jn->pwd);
  398.  
  399.     deletefilelist(jn->filelist);
  400.  
  401.     if (jn->ty)
  402.     zfree(jn->ty, sizeof(struct ttyinfo));
  403.  
  404.     *jn = zero;
  405. }
  406.  
  407. /* set the previous job to something reasonable */
  408.  
  409. /**/
  410. void
  411. setprevjob(void)
  412. {
  413.     int i;
  414.  
  415.     for (i = MAXJOB - 1; i; i--)
  416.     if ((jobtab[i].stat & STAT_INUSE) && (jobtab[i].stat & STAT_STOPPED) &&
  417.         i != curjob && i != thisjob) {
  418.         prevjob = i;
  419.         return;
  420.     }
  421.  
  422.     for (i = MAXJOB - 1; i; i--)
  423.     if ((jobtab[i].stat & STAT_INUSE) && i != curjob && i != thisjob) {
  424.         prevjob = i;
  425.         return;
  426.     }
  427.  
  428.     prevjob = -1;
  429. }
  430.  
  431. /* add a process to the current job */
  432.  
  433. /**/
  434. void
  435. addproc(pid_t pid, char *text)
  436. {
  437.     Process pn;
  438.     struct timezone dummy_tz;
  439.  
  440.     pn = (Process) zcalloc(sizeof *pn);
  441.     pn->pid = pid;
  442.     if (text)
  443.     strcpy(pn->text, text);
  444.     else
  445.     *pn->text = '\0';
  446.     gettimeofday(&pn->bgtime, &dummy_tz);
  447.     pn->status = SP_RUNNING;
  448.     pn->next = NULL;
  449.  
  450.     /* if this is the first process we are adding to *
  451.      * the job, then it's the group leader.          */
  452.     if (!jobtab[thisjob].gleader)
  453.     jobtab[thisjob].gleader = pid;
  454.  
  455.     /* attach this process to end of process list of current job */
  456.     if (jobtab[thisjob].procs) {
  457.     Process n;
  458.  
  459.     for (n = jobtab[thisjob].procs; n->next; n = n->next);
  460.     pn->next = NULL;
  461.     n->next = pn;
  462.     } else {
  463.     /* first process for this job */
  464.     jobtab[thisjob].procs = pn;
  465.     }
  466. }
  467.  
  468. /* Check if we have files to delete.  We need to check this to see *
  469.  * if it's all right to exec a command without forking in the last *
  470.  * component of subshells or after the `-c' option.                */
  471.  
  472. /**/
  473. int
  474. havefiles(void)
  475. {
  476.     int i;
  477.  
  478.     for (i = 1; i < MAXJOB; i++)
  479.     if (jobtab[i].stat && jobtab[i].filelist)
  480.         return 1;
  481.     return 0;
  482.  
  483. }
  484.  
  485. /* wait for a particular process */
  486.  
  487. /**/
  488. void
  489. waitforpid(pid_t pid)
  490. {
  491.     int first = 1;
  492.  
  493.     /* child_block() around this loop in case #ifndef WNOHANG */
  494.     child_block();        /* unblocked in child_suspend() */
  495.     while (!errflag && (kill(pid, 0) >= 0 || errno != ESRCH)) {
  496.     if (first)
  497.         first = 0;
  498.     else
  499.         kill(pid, SIGCONT);
  500.  
  501.     child_suspend(SIGINT);
  502.     child_block();
  503.     }
  504.     child_unblock();
  505. }
  506.  
  507. /* wait for a job to finish */
  508.  
  509. /**/
  510. void
  511. waitjob(int job, int sig)
  512. {
  513.     Job jn = jobtab + job;
  514.  
  515.     child_block();         /* unblocked during child_suspend() */
  516.     if (jn->procs) {         /* if any forks were done         */
  517.     jn->stat |= STAT_LOCKED;
  518.     if (jn->stat & STAT_CHANGED)
  519.         printjob(jn, !!isset(LONGLISTJOBS), 1);
  520.     while (!errflag && jn->stat &&
  521.            !(jn->stat & STAT_DONE) &&
  522.            !(interact && (jn->stat & STAT_STOPPED))) {
  523.         child_suspend(sig);
  524.         /* Commenting this out makes ^C-ing a job started by a function
  525.            stop the whole function again.  But I guess it will stop
  526.            something else from working properly, we have to find out
  527.            what this might be.  --oberon
  528.  
  529.         errflag = 0; */
  530.         if (jn->stat & STAT_SUPERJOB) {
  531.         Job sj = jobtab + jn->other;
  532.         if (sj->stat & STAT_DONE) {
  533.             struct process *p;
  534.             
  535.             for (p = sj->procs; p; p = p->next)
  536.             if (WIFSIGNALED(p->status)) {
  537.                 killpg(jn->gleader, WTERMSIG(p->status));
  538.                 kill(sj->other, SIGCONT);
  539.                 kill(sj->other, WTERMSIG(p->status));
  540.                 break;
  541.             }
  542.             if (!p) {
  543.             jn->stat &= ~STAT_SUPERJOB;
  544.             kill(sj->other, SIGCONT);
  545.             deletejob(sj);
  546.             }
  547.             curjob = jn - jobtab;
  548.         }
  549.         else if (sj->stat & STAT_STOPPED) {
  550.             struct process *p;
  551.  
  552.             jn->stat |= STAT_STOPPED;
  553.             for (p = jn->procs; p; p = p->next)
  554.             p->status = sj->procs->status;
  555.             curjob = jn - jobtab;
  556.             printjob(jn, !!isset(LONGLISTJOBS), 1);
  557.             break;
  558.         }
  559.         }
  560.         child_block();
  561.     }
  562.     } else
  563.     deletejob(jn);
  564.     child_unblock();
  565. }
  566.  
  567. /* wait for running job to finish */
  568.  
  569. /**/
  570. void
  571. waitjobs(void)
  572. {
  573.     waitjob(thisjob, 0);
  574.     thisjob = -1;
  575. }
  576.  
  577. /* clear job table when entering subshells */
  578.  
  579. /**/
  580. void
  581. clearjobtab(void)
  582. {
  583.     int i;
  584.  
  585.     for (i = 1; i < MAXJOB; i++) {
  586.     if (jobtab[i].pwd)
  587.         zsfree(jobtab[i].pwd);
  588.     if (jobtab[i].ty)
  589.         zfree(jobtab[i].ty, sizeof(struct ttyinfo));
  590.     }
  591.  
  592.     memset(jobtab, 0, sizeof(jobtab)); /* zero out table */
  593. }
  594.  
  595. /* Get a free entry in the job table and initialize it. */
  596.  
  597. /**/
  598. int
  599. initjob(void)
  600. {
  601.     int i;
  602.  
  603.     for (i = 1; i < MAXJOB; i++)
  604.     if (!jobtab[i].stat) {
  605.         jobtab[i].stat = STAT_INUSE;
  606.         jobtab[i].pwd = ztrdup(pwd);
  607.         jobtab[i].gleader = 0;
  608.         return i;
  609.     }
  610.  
  611.     zerr("job table full or recursion limit exceeded", NULL, 0);
  612.     return -1;
  613. }
  614.  
  615. /* print pids for & */
  616.  
  617. /**/
  618. void
  619. spawnjob(void)
  620. {
  621.     Process pn;
  622.  
  623.     /* if we are not in a subshell */
  624.     if (!subsh) {
  625.     if (curjob == -1 || !(jobtab[curjob].stat & STAT_STOPPED)) {
  626.         curjob = thisjob;
  627.         setprevjob();
  628.     } else if (prevjob == -1 || !(jobtab[prevjob].stat & STAT_STOPPED))
  629.         prevjob = thisjob;
  630.     if (interact && jobbing && jobtab[thisjob].procs) {
  631.         fprintf(stderr, "[%d]", thisjob);
  632.         for (pn = jobtab[thisjob].procs; pn; pn = pn->next)
  633.         fprintf(stderr, " %ld", (long) pn->pid);
  634.         fprintf(stderr, "\n");
  635.         fflush(stderr);
  636.     }
  637.     }
  638.     if (!jobtab[thisjob].procs)
  639.     deletejob(jobtab + thisjob);
  640.     else
  641.     jobtab[thisjob].stat |= STAT_LOCKED;
  642.     thisjob = -1;
  643. }
  644.  
  645. static long clktck = 0;
  646.  
  647. static void
  648. set_clktck(void)
  649. {
  650. #ifdef _SC_CLK_TCK
  651.     if (!clktck)
  652.     /* fetch clock ticks per second from *
  653.      * sysconf only the first time       */
  654.     clktck = sysconf(_SC_CLK_TCK);
  655. #else
  656. # ifdef __NeXT__
  657.     /* NeXTStep 3.3 defines CLK_TCK wrongly */
  658.     clktck = 60;
  659. # else
  660. #  ifdef CLK_TCK
  661.     clktck = CLK_TCK;
  662. #  else
  663. #   ifdef HZ
  664.      clktck = HZ;
  665. #   else
  666.      clktck = 60;
  667. #   endif
  668. #  endif
  669. # endif
  670. #endif
  671. }
  672.  
  673. /* Check whether shell should report the amount of time consumed   *
  674.  * by job.  This will be the case if we have preceded the command  *
  675.  * with the keyword time, or if REPORTTIME is non-negative and the *
  676.  * amount of time consumed by the job is greater than REPORTTIME   */
  677.  
  678. /**/
  679. int
  680. should_report_time(Job j)
  681. {
  682.     Value v;
  683.     char *s = "REPORTTIME";
  684.     int reporttime;
  685.  
  686.     /* if the time keyword was used */
  687.     if (j->stat & STAT_TIMED)
  688.     return 1;
  689.  
  690.     if (!(v = getvalue(&s, 0)) || (reporttime = getintvalue(v)) < 0)
  691.     return 0;
  692.  
  693.     /* can this ever happen? */
  694.     if (!j->procs)
  695.     return 0;
  696.  
  697.     set_clktck();
  698.     return ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime);
  699. }
  700.  
  701. /**/
  702. void
  703. printhhmmss(double secs)
  704. {
  705.     int mins = (int) secs / 60;
  706.     int hours = mins / 60;
  707.  
  708.     secs -= 60 * mins;
  709.     mins -= 60 * hours;
  710.     if (hours)
  711.     fprintf(stderr, "%d:%02d:%05.2f", hours, mins, secs);
  712.     else if (mins)
  713.     fprintf(stderr,      "%d:%05.2f",        mins, secs);
  714.     else
  715.     fprintf(stderr,           "%.3f",              secs);
  716. }
  717.  
  718. /**/
  719. void
  720. printtime(struct timeval *real, struct timeinfo *ti, char *desc)
  721. {
  722.     char *s;
  723.     double elapsed_time, user_time, system_time;
  724.     int percent;
  725.  
  726.     if (!desc)
  727.     desc = "";
  728.  
  729.     set_clktck();
  730.     /* go ahead and compute these, since almost every TIMEFMT will have them */
  731.     elapsed_time = real->tv_sec + real->tv_usec / 1000000.0;
  732.     user_time    = ti->ut / (double) clktck;
  733.     system_time  = ti->st / (double) clktck;
  734.     percent      =  100.0 * (ti->ut + ti->st)
  735.     / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0);
  736.  
  737.     if (!(s = getsparam("TIMEFMT")))
  738.     s = DEFAULT_TIMEFMT;
  739.  
  740.     for (; *s; s++)
  741.     if (*s == '%')
  742.         switch (*++s) {
  743.         case 'E':
  744.         fprintf(stderr, "%4.2fs", elapsed_time);
  745.         break;
  746.         case 'U':
  747.         fprintf(stderr, "%4.2fs", user_time);
  748.         break;
  749.         case 'S':
  750.         fprintf(stderr, "%4.2fs", system_time);
  751.         break;
  752.         case '*':
  753.         switch (*++s) {
  754.         case 'E':
  755.             printhhmmss(elapsed_time);
  756.             break;
  757.         case 'U':
  758.             printhhmmss(user_time);
  759.             break;
  760.         case 'S':
  761.             printhhmmss(system_time);
  762.             break;
  763.         default:
  764.             fprintf(stderr, "%%*");
  765.             s--;
  766.             break;
  767.         }
  768.         break;
  769.         case 'P':
  770.         fprintf(stderr, "%d%%", percent);
  771.         break;
  772.         case 'J':
  773.         fprintf(stderr, "%s", desc);
  774.         break;
  775.         case '%':
  776.         putc('%', stderr);
  777.         break;
  778.         case '\0':
  779.         s--;
  780.         break;
  781.         default:
  782.         fprintf(stderr, "%%%c", *s);
  783.         break;
  784.     } else
  785.         putc(*s, stderr);
  786.     putc('\n', stderr);
  787.     fflush(stderr);
  788. }
  789.  
  790. /**/
  791. void
  792. dumptime(Job jn)
  793. {
  794.     Process pn;
  795.  
  796.     if (!jn->procs)
  797.     return;
  798.     for (pn = jn->procs; pn; pn = pn->next)
  799.     printtime(dtime(&dtimeval, &pn->bgtime, &pn->endtime), &pn->ti, pn->text);
  800. }
  801.  
  802. /**/
  803. void
  804. shelltime(void)
  805. {
  806.     struct timeinfo ti;
  807.     struct timezone dummy_tz;
  808.     struct tms buf;
  809.  
  810.     times(&buf);
  811.     ti.ut = buf.tms_utime;
  812.     ti.st = buf.tms_stime;
  813.     gettimeofday(&now, &dummy_tz);
  814.     printtime(dtime(&dtimeval, &shtimer, &now), &ti, "shell");
  815.     ti.ut = buf.tms_cutime;
  816.     ti.st = buf.tms_cstime;
  817.     printtime(dtime(&dtimeval, &shtimer, &now), &ti, "children");
  818. }
  819.  
  820. /* see if jobs need printing */
  821.  
  822. /**/
  823. void
  824. scanjobs(void)
  825. {
  826.     int i;
  827.  
  828.     for (i = 1; i < MAXJOB; i++)
  829.         if (jobtab[i].stat & STAT_CHANGED)
  830.             printjob(jobtab + i, 0, 1);
  831. }
  832.  
  833. /* check to see if user has jobs running/stopped */
  834.  
  835. /**/
  836. void
  837. checkjobs(void)
  838. {
  839.     int i;
  840.  
  841.     for (i = 1; i < MAXJOB; i++)
  842.     if (i != thisjob && (jobtab[i].stat & STAT_LOCKED) &&
  843.         !(jobtab[i].stat & STAT_NOPRINT))
  844.         break;
  845.     if (i < MAXJOB) {
  846.     if (jobtab[i].stat & STAT_STOPPED) {
  847.  
  848. #ifdef USE_SUSPENDED
  849.         zerr("you have suspended jobs.", NULL, 0);
  850. #else
  851.         zerr("you have stopped jobs.", NULL, 0);
  852. #endif
  853.  
  854.     } else
  855.         zerr("you have running jobs.", NULL, 0);
  856.     stopmsg = 1;
  857.     }
  858. }
  859.  
  860.