home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume7 / top2 / part02 / top.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  25.6 KB  |  1,258 lines

  1. char *copyright =
  2.     "Top, version 2.0, copyright (c) 1984, 1986, William LeFebvre";
  3.  
  4. /*
  5.  *  Top users display for Berkeley Unix
  6.  *  Version 2.0
  7.  *
  8.  *  This program may be freely redistributed to other Unix sites, but this
  9.  *  entire comment MUST remain intact.
  10.  *
  11.  *  Copyright (c) 1984, 1986, William LeFebvre, Rice University
  12.  *
  13.  *  This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
  14.  *  Compile with the preprocessor constant "FOUR_ONE" set to get an
  15.  *  executable that will run on Berkeley 4.1 Unix.
  16.  *
  17.  *  The Sun kernel uses scaled integers instead of floating point.
  18.  *  Compilation with the preprocessor variable "sun" gets an executable
  19.  *  that will run on Sun Unix version 1.1 or later ("sun" is automatically
  20.  *  set by the Sun C compiler).
  21.  *
  22.  *  The Pyramid splits the stack size (p_ssize) into control stack and user
  23.  *  stack sizes.  Compilation with the preprocessor variable "pyr" gets an
  24.  *  executable that will run on Pyramids ("pyr" is automatically set by the
  25.  *  Pyramid C compiler).
  26.  *
  27.  *  See the file "Changes" for more information on version-to-version changes.
  28.  */
  29.  
  30. #include <stdio.h>
  31. #include <pwd.h>
  32. #include <nlist.h>
  33. #include <signal.h>
  34. #include <setjmp.h>
  35. #include <sys/param.h>
  36. #include <sys/dir.h>
  37. #include <sys/user.h>
  38. #include <sys/proc.h>
  39. #include <sys/dk.h>
  40. #include <sys/vm.h>
  41.  
  42. /* includes specific to top */
  43. #include "layout.h"
  44. #include "screen.h"        /* interface to screen package */
  45. #include "top.h"
  46. #include "top.local.h"
  47. #include "boolean.h"
  48.  
  49. /* Special atoi routine returns either a non-negative number or one of: */
  50. #define Infinity    -1
  51. #define Invalid        -2
  52.  
  53. /* Size of the stdio buffer given to stdout */
  54. #define Buffersize    2048
  55.  
  56. /* The buffer the stdio will use */
  57. char stdoutbuf[Buffersize];
  58.  
  59. /* wish list for kernel symbols */
  60.  
  61. struct nlist nlst[] = {
  62.     { "_avenrun" },
  63. #define X_AVENRUN    0
  64.     { "_ccpu" },
  65. #define X_CCPU        1
  66.     { "_cp_time" },
  67. #define X_CP_TIME    2
  68.     { "_hz" },
  69. #define X_HZ        3
  70.     { "_mpid" },
  71. #define X_MPID        4
  72.     { "_nproc" },
  73. #define X_NPROC        5
  74.     { "_proc" },
  75. #define X_PROC        6
  76.     { "_total" },
  77. #define X_TOTAL        7
  78.     { 0 },
  79. };
  80.  
  81. /* build Signal masks */
  82. #define Smask(s)    (1 << ((s) - 1))
  83.  
  84. /* for system errors */
  85. extern int errno;
  86.  
  87. /* for getopt: */
  88. extern int  optind;
  89. extern char *optarg;
  90.  
  91. /* signal handling routines */
  92. int leave();
  93. int onalrm();
  94. int tstop();
  95.  
  96. int nproc;
  97. int mpid;
  98.  
  99. /* kernel "hz" variable -- clock rate */
  100. long hz;
  101.  
  102. /* All this is to calculate the cpu state percentages */
  103.  
  104. long cp_time[CPUSTATES];
  105. long cp_old[CPUSTATES];
  106. long cp_change[CPUSTATES];
  107. long total_change;
  108. long mpid_offset;
  109. long avenrun_offset;
  110. long cp_time_offset;
  111. long total_offset;
  112.  
  113. #ifdef sun
  114. long ccpu;
  115. long avenrun[3];
  116. #else
  117. double ccpu;
  118. double avenrun[3];
  119. #endif
  120. double logcpu;
  121.  
  122. struct vmtotal total;
  123.  
  124. struct proc *proc;
  125. struct proc *pbase;
  126. int bytes;
  127. int initialized = No;
  128. char *myname = "top";
  129. jmp_buf jmp_int;
  130. char input_waiting = No;
  131.  
  132. /* routines that don't return int */
  133.  
  134. struct passwd *getpwent();
  135. char *username();
  136. char *itoa();
  137. char *ctime();
  138. char *rindex();
  139. char *kill_procs();
  140. char *renice_procs();
  141.  
  142. int proc_compar();
  143. long time();
  144.  
  145. /* different routines for displaying the user's identification */
  146. /* (values assigned to get_userid) */
  147. char *username();
  148. char *itoa7();
  149.  
  150. /* display routines that need to be predeclared */
  151. int i_loadave();
  152. int u_loadave();
  153. int i_procstates();
  154. int u_procstates();
  155. int i_cpustates();
  156. int u_cpustates();
  157. int i_memory();
  158. int u_memory();
  159. int i_header();
  160. int u_header();
  161. int i_process();
  162. int u_process();
  163.  
  164. /* pointers to display routines */
  165. int (*d_loadave)() = i_loadave;
  166. int (*d_procstates)() = i_procstates;
  167. int (*d_cpustates)() = i_cpustates;
  168. int (*d_memory)() = i_memory;
  169. int (*d_header)() = i_header;
  170. int (*d_process)() = i_process;
  171.  
  172. /* buffer of proc information lines for display updating */
  173. /* unfortunate that this must be declared globally */
  174. char (* screenbuf)[Display_width];
  175.  
  176. main(argc, argv)
  177.  
  178. int  argc;
  179. char *argv[];
  180.  
  181. {
  182.     register struct proc *pp;
  183.     register struct proc **prefp;
  184.     register int i;
  185.     register int active_procs;
  186.     register int change;
  187.  
  188.     static struct proc **pref;
  189.     static char tempbuf1[50];
  190.     static char tempbuf2[50];
  191.     int total_procs;
  192.     int old_sigmask;
  193.     int proc_brkdn[7];
  194.     int topn = Default_TOPN;
  195.     int delay = Default_DELAY;
  196.     int displays = 0;        /* indicates unspecified */
  197.     long curr_time;
  198.     char *(*get_userid)() = username;
  199.     char *uname_field = "USERNAME";
  200. #ifndef FOUR_ONE
  201.     char ch;
  202.     char msg_showing = 0;
  203.     int readfds;
  204.     struct timeval timeout;
  205. #endif    
  206.     char dostates = No;
  207.     char do_unames = Yes;
  208.     char do_init = No;
  209.     char interactive = Maybe;
  210.     char show_sysprocs = No;
  211.     char topn_specified = No;
  212.     char warnings = 0;
  213.  
  214.     /* set the buffer for stdout */
  215.     setbuffer(stdout, stdoutbuf, Buffersize);
  216.  
  217.     /* get our name */
  218.     if (argc > 0)
  219.     {
  220.     if ((myname = rindex(argv[0], '/')) == 0)
  221.     {
  222.         myname = argv[0];
  223.     }
  224.     else
  225.     {
  226.         myname++;
  227.     }
  228.     }
  229.  
  230.     /* process options */
  231.     while ((i = getopt(argc, argv, "Sbinus:d:")) != EOF)
  232.     {
  233.     switch(i)
  234.     {
  235.         case 'u':            /* display uid instead of name */
  236.         do_unames = No;
  237.         uname_field = "   UID  ";
  238.         get_userid = itoa7;
  239.         break;
  240.  
  241.         case 'S':            /* show system processes */
  242.         show_sysprocs = Yes;
  243.         break;
  244.  
  245.         case 'i':            /* go interactive regardless */
  246.         interactive = Yes;
  247.         break;
  248.  
  249.         case 'n':            /* batch, or non-interactive */
  250.         case 'b':
  251.         interactive = No;
  252.         break;
  253.  
  254.         case 'd':            /* number of displays to show */
  255.         if ((i = atoiwi(optarg)) == Invalid || i == 0)
  256.         {
  257.             fprintf(stderr,
  258.             "%s: warning: display count should be positive -- option ignored\n",
  259.             myname);
  260.             warnings++;
  261.         }
  262.         else
  263.         {
  264.             displays = i;
  265.         }
  266.         break;
  267.  
  268.         case 's':
  269.         if ((delay = atoi(optarg)) < 0)
  270.         {
  271.             fprintf(stderr,
  272.             "%s: warning: seconds delay should be non-negative -- using default\n",
  273.             myname);
  274.             delay = Default_DELAY;
  275.             warnings++;
  276.         }
  277.         break;
  278.  
  279.         default:
  280.         fprintf(stderr,
  281.             "Usage: %s [-Sbinu] [-d x] [-s x] [number]\n",
  282.             myname);
  283.         exit(1);
  284.     }
  285.     }
  286.  
  287.     /* get count of top processes to display (if any) */
  288.     if (optind < argc)
  289.     {
  290.     if ((topn = atoiwi(argv[optind])) == Invalid)
  291.     {
  292.         fprintf(stderr,
  293.         "%s: warning: process display count should be non-negative -- using default\n",
  294.         myname);
  295.         topn = Default_TOPN;
  296.         warnings++;
  297.     }
  298.     else
  299.     {
  300.         topn_specified = Yes;
  301.     }
  302.     }
  303.  
  304.     /* initialize the kernel memory interface */
  305.     init_kernel();
  306.  
  307.     if (initialized != 1)
  308.     {
  309.     /* get the list of symbols we want to access in the kernel */
  310.     /* errno = 0; ??? */
  311.     nlist(VMUNIX, nlst);
  312.     if (nlst[0].n_type == 0)
  313.     {
  314.         fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
  315.         exit(2);
  316.     }
  317.     
  318.     /* get the symbol values out of kmem */
  319.     getkval(nlst[X_PROC].n_value,  &proc,  sizeof(int),
  320.         nlst[X_PROC].n_name);
  321.     getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int),
  322.         nlst[X_NPROC].n_name);
  323.     getkval(nlst[X_HZ].n_value,    &hz,    sizeof(int),
  324.         nlst[X_HZ].n_name);
  325.     getkval(nlst[X_CCPU].n_value,  &ccpu,  sizeof(int),
  326.         nlst[X_CCPU].n_name);
  327.     
  328.     /* some calculations we use later */
  329.     
  330.     mpid_offset = nlst[X_MPID].n_value;
  331.     avenrun_offset = nlst[X_AVENRUN].n_value;
  332.     cp_time_offset = nlst[X_CP_TIME].n_value;
  333.     total_offset = nlst[X_TOTAL].n_value;
  334.     
  335.     /* this is used in calculating WCPU -- calculate it ahead of time */
  336. #ifdef sun
  337.     logcpu = log((double)ccpu / FSCALE);
  338. #else
  339.     logcpu = log(ccpu);
  340. #endif
  341.     
  342.     /* allocate space for proc structure array and array of pointers */
  343.     bytes  = nproc * sizeof(struct proc);
  344.     pbase  = (struct proc *)sbrk(bytes);
  345.     pref   = (struct proc **)sbrk(nproc * sizeof(struct proc *));
  346.  
  347.     /* Just in case ... */
  348.     if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
  349.     {
  350.         fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
  351.         exit(3);
  352.     }
  353.     
  354.     /* initialize the hashing stuff */
  355.     if (do_unames)
  356.     {
  357.         init_hash();
  358.     }
  359.     
  360.     if (do_init)
  361.     {
  362.         initialized = 1;
  363.         kill(0, SIGQUIT);
  364.         exit(99);
  365.     }
  366.     }
  367.  
  368.     /* initialize termcap */
  369.     init_termcap();
  370.  
  371.     /*
  372.      *  Smart terminals can only display so many processes, precisely
  373.      *    "screen_length - Header_lines".  When run on dumb terminals, nothing
  374.      *    fancy is done anyway, so we can display as many processes as the
  375.      *    system can make.  But since we never need to remember what is on the
  376.      *    screen, we only allocate a buffer for one screen line.
  377.      */
  378.     if (smart_terminal)
  379.     {
  380.     /* can only display (screen_length - Header_lines) processes */
  381.     i = screen_length - Header_lines;
  382.     if (topn > i)        /* false even when topn == Infinity */
  383.     {
  384.         fprintf(stderr,
  385.         "%s: warning: this terminal can only display %d processes.\n",
  386.         myname, screen_length - Header_lines);
  387.         topn = i;
  388.         warnings++;
  389.     }
  390.     }
  391.     else
  392.     {
  393.     i = 1;
  394.     screen_length = nproc + Header_lines;
  395.     }
  396.  
  397.     /* allocate space for the screen buffer */
  398.     screenbuf = (char (*)[])sbrk(i * Display_width);
  399.     if (screenbuf == (char (*)[])NULL)
  400.     {
  401.     fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
  402.     exit(4);
  403.     }
  404.  
  405.     /* adjust for topn == Infinity */
  406.     if (topn == Infinity)
  407.     {
  408.     /*
  409.      *  For smart terminals, infinity really means everything that can
  410.      *  be displayed (which just happens to be "i" at this point).
  411.      *  On dumb terminals, infinity means every process in the system!
  412.      *  We only really want to do that if it was explicitly specified.
  413.      *  This is always the case when "Default_TOPN != Infinity".  But if
  414.      *  topn wasn't explicitly specified and we are on a dumb terminal
  415.      *  and the default is Infinity, then (and only then) we use
  416.      *  "Nominal_TOPN" instead.
  417.      */
  418. #if Default_TOPN == Infinity
  419.     topn = smart_terminal ? i :
  420.             (topn_specified ? nproc : Nominal_TOPN);
  421. #else
  422.     topn = smart_terminal ? i : nproc;
  423. #endif
  424.     }
  425.  
  426.     /* determine interactive state */
  427.     if (interactive == Maybe)
  428.     {
  429.     interactive = smart_terminal;
  430.     }
  431.  
  432.     /* if # of displays not specified, fill it in */
  433.     if (displays == 0)
  434.     {
  435.     displays = smart_terminal ? Infinity : 1;
  436.     }
  437.  
  438.     /* hold interrupt signals while setting up the screen and the handlers */
  439. #ifndef FOUR_ONE
  440.     old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
  441. #endif
  442.     init_screen();
  443.     signal(SIGINT, leave);
  444.     signal(SIGQUIT, leave);
  445.     signal(SIGTSTP, tstop);
  446. #ifndef FOUR_ONE
  447.     sigsetmask(old_sigmask);
  448. #endif
  449.     if (warnings)
  450.     {
  451.     fprintf(stderr, "....");
  452.     fflush(stderr);            /* why must I do this? */
  453.     sleep(3 * warnings);
  454.     }
  455.     clear();
  456.  
  457.     /* setup the jump buffer for stops */
  458.     if (setjmp(jmp_int) != 0)
  459.     {
  460.     /* control ends up here after an interrupt */
  461.     clear();
  462.     reset_display();
  463.     }
  464.  
  465.     /*
  466.      *  main loop -- repeat while display count is positive or while it
  467.      *        indicates infinity (by being -1)
  468.      */
  469.  
  470.     while ((displays == -1) || (displays-- > 0))
  471.     {
  472.     /* read all the proc structures in one fell swoop */
  473.     getkval(proc, pbase, bytes, "proc array");
  474.  
  475.     /* get the cp_time array */
  476.     getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time");
  477.  
  478.     /* get load average array */
  479.     getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun");
  480.  
  481.     /* get mpid -- process id of last process */
  482.     getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
  483.  
  484.     /* get total -- systemwide main memory usage structure */
  485.     getkval(total_offset, &total, sizeof(total), "_total");
  486.  
  487.     /* count up process states and get pointers to interesting procs */
  488.     total_procs = 0;
  489.     active_procs = 0;
  490.     bzero(proc_brkdn, sizeof(proc_brkdn));
  491.     prefp = pref;
  492.     for (pp = pbase, i = 0; i < nproc; pp++, i++)
  493.     {
  494.         /*
  495.          *  Place pointers to each valid proc structure in pref[].
  496.          *  Process slots that are actually in use have a non-zero
  497.          *  status field.  Processes with SSYS set are system
  498.          *  processes---these get ignored unless show_sysprocs is set.
  499.          */
  500.         if (pp->p_stat != 0 &&
  501.         (show_sysprocs || ((pp->p_flag & SSYS) == 0)))
  502.         {
  503.         total_procs++;
  504.         proc_brkdn[pp->p_stat]++;
  505.         if (pp->p_stat != SZOMB)
  506.         {
  507.             *prefp++ = pp;
  508.             active_procs++;
  509.         }
  510.         }
  511.     }
  512.  
  513.     /* display the load averages */
  514.     (*d_loadave)(mpid, avenrun);
  515.  
  516.     /*
  517.      *  Display the current time.
  518.      *  "ctime" always returns a string that looks like this:
  519.      *  
  520.      *    Sun Sep 16 01:03:52 1973
  521.      *      012345678901234567890123
  522.      *              1         2
  523.      *
  524.      *  We want indices 11 thru 18 (length 8).
  525.      */
  526.  
  527.     curr_time = time(0);
  528.     if (smart_terminal)
  529.     {
  530.         Move_to(screen_width - 8, 0);
  531.     }
  532.     else
  533.     {
  534.         fputs("    ", stdout);
  535.     }
  536.     printf("%-8.8s\n", &(ctime(&curr_time)[11]));
  537.  
  538.     /* display process state breakdown */
  539.     (*d_procstates)(total_procs, proc_brkdn);
  540.  
  541.     /* calculate percentage time in each cpu state */
  542.     if (dostates)    /* but not the first time */
  543.     {
  544.         total_change = 0;
  545.         for (i = 0; i < CPUSTATES; i++)
  546.         {
  547.         /* calculate changes for each state and overall change */
  548.         if (cp_time[i] < cp_old[i])
  549.         {
  550.             /* this only happens when the counter wraps */
  551.             change = (int)
  552.             ((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
  553.         }
  554.         else
  555.         {
  556.             change = cp_time[i] - cp_old[i];
  557.         }
  558.         total_change += (cp_change[i] = change);
  559.         cp_old[i] = cp_time[i];
  560.         }
  561.         (*d_cpustates)(cp_change, total_change);
  562.     }
  563.     else
  564.     {
  565.         /* we'll do it next time */
  566.         if (smart_terminal)
  567.         {
  568.         z_cpustates();
  569.         }
  570.         else
  571.         {
  572.         putchar('\n');
  573.         }
  574.         dostates = Yes;
  575.         bzero(cp_old, sizeof(cp_old));
  576.     }
  577.  
  578.     /* display main memory statistics */
  579.     (*d_memory)(
  580.         pagetok(total.t_rm), pagetok(total.t_arm),
  581.         pagetok(total.t_vm), pagetok(total.t_avm),
  582.         pagetok(total.t_free));
  583.  
  584.     i = 0;
  585.     if (topn > 0)
  586.     {
  587.         /* update the header area */
  588.         (*d_header)(uname_field);
  589.     
  590.         /* sort by cpu percentage (pctcpu) */
  591.         qsort(pref, active_procs, sizeof(struct proc *), proc_compar);
  592.     
  593.         /* adjust for a lack of processes */
  594.         if (active_procs > topn)
  595.         {
  596.         active_procs = topn;
  597.         }
  598.  
  599.         /*
  600.          *  Now, show the top "n" processes.  The method is slightly
  601.          *    different for dumb terminals, so we will just use two very
  602.          *    similar loops; this increases speed but also code size.
  603.          */
  604.         if (smart_terminal)
  605.         {
  606.         for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
  607.         {
  608.             pp = *prefp;
  609.             (*d_process)(i, pp, get_userid);
  610.         }
  611.         }
  612.         else for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
  613.         {
  614.         pp = *prefp;
  615.         /* (only one buffer lien with dumb terminals) */
  616.         (*d_process)(0, pp, get_userid);
  617.         }
  618.     }
  619.  
  620.     /* do end-screen processing */
  621.     u_endscreen(i);
  622.  
  623.     /* now, flush the output buffer */
  624.     fflush(stdout);
  625.  
  626.     /* only do the rest if we have more displays to show */
  627.     if (displays)
  628.     {
  629.         /* switch out for new display on smart terminals */
  630.         if (smart_terminal)
  631.         {
  632.         d_loadave = u_loadave;
  633.         d_procstates = u_procstates;
  634.         d_cpustates = u_cpustates;
  635.         d_memory = u_memory;
  636.         d_header = u_header;
  637.         d_process = u_process;
  638.         }
  639.     
  640. #ifndef FOUR_ONE
  641.         if (!interactive)
  642. #endif
  643.         {
  644.         /* set up alarm */
  645.         signal(SIGALRM, onalrm);
  646.         alarm(delay);
  647.     
  648.         /* wait for the rest of it .... */
  649.         pause();
  650.         }
  651. #ifndef FOUR_ONE
  652.         else
  653.         {
  654.         /* wait for either input or the end of the delay period */
  655.         readfds = 1;            /* for standard input */
  656.         timeout.tv_sec  = delay;
  657.         timeout.tv_usec = 0;
  658.         if (select(32, &readfds, 0, 0, &timeout) > 0)
  659.         {
  660.             int newval;
  661.             char *errmsg;
  662.     
  663.             /* something to read -- clear the message area first */
  664.             if (msg_showing)
  665.             {
  666.             if (smart_terminal)
  667.             {
  668.                 putcap(clear_line);
  669.             }
  670.             msg_showing = No;
  671.             }
  672.  
  673.             /* now read it and act on it */
  674.             read(0, &ch, 1);
  675.             switch(ch)
  676.             {
  677.             case '\f':        /* redraw screen */
  678.                 reset_display();
  679.                 clear();
  680.                 break;
  681.  
  682.             case ' ':        /* merely update display */
  683.                 break;
  684.     
  685.             case 'q':        /* quit */
  686.                 quit(0);
  687.                 break;
  688.     
  689.             case 'h':        /* help */
  690.             case '?':
  691.                 reset_display();
  692.                 clear();
  693.                 show_help();
  694.                 standout("Hit any key to continue: ");
  695.                 fflush(stdout);
  696.                     read(0, &ch, 1);
  697.                 clear();
  698.                 break;
  699.     
  700.             case 'e':        /* show errors */
  701.                 if (error_count() == 0)
  702.                 {
  703.                 standout(" Currently no errors to report.");
  704.                 msg_showing = Yes;
  705.                 }
  706.                 else
  707.                 {
  708.                 reset_display();
  709.                 clear();
  710.                 show_errors();
  711.                 standout("Hit any key to continue: ");
  712.                 fflush(stdout);
  713.                 read(0, &ch, 1);
  714.                 clear();
  715.                 }
  716.                 break;
  717.     
  718.             case 'n':        /* new number */
  719.             case '#':
  720.                 standout("Number of processes to show: ");
  721.                 newval = readline(tempbuf1, 8, Yes);
  722.                 putchar('\r');
  723.                 if (newval > -1)
  724.                 {
  725.                 if (newval > (i = screen_length - Header_lines))
  726.                 {
  727.                     standout(
  728.                       " This terminal can only display %d processes.",
  729.                       i);
  730.                     newval = i;
  731.                     msg_showing = Yes;
  732.                     break;
  733.                 }
  734.     
  735.                 if (newval > topn)
  736.                 {
  737.                     /* zero fill appropriate part of screenbuf */
  738.                     bzero(screenbuf[topn],
  739.                     (newval - topn) * Display_width);
  740.     
  741.                     /* redraw header if need be */
  742.                     if (topn == 0)
  743.                     {
  744.                     d_header = i_header;
  745.                     }
  746.                 }
  747.                 topn = newval;
  748.                 }
  749.                 putcap(clear_line);
  750.                 break;
  751.     
  752.             case 's':        /* new seconds delay */
  753.                 standout("Seconds to delay: ");
  754.                 if ((i = readline(tempbuf1, 8, Yes)) > -1)
  755.                 {
  756.                 delay = i;
  757.                 }
  758.                 putchar('\r');
  759.                 putcap(clear_line);
  760.                 break;
  761.     
  762.             case 'd':        /* change display count */
  763.                 standout("Displays to show (currently %s): ",
  764.                     displays == -1 ? "infinite" :
  765.                              itoa(displays));
  766.                 if ((i = readline(tempbuf1, 10, Yes)) > 0)
  767.                 {
  768.                 displays = i;
  769.                 }
  770.                 else if (i == 0)
  771.                 {
  772.                 quit(0);
  773.                 }
  774.                 putchar('\r');
  775.                 putcap(clear_line);
  776.                 break;
  777.  
  778.             case 'k':        /* kill program */
  779.                 fputs("kill ", stdout);
  780.                 if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
  781.                 {
  782.                 if ((errmsg = kill_procs(tempbuf2)) != NULL)
  783.                 {
  784.                     putchar('\r');
  785.                     standout(errmsg);
  786.                 }
  787.                 msg_showing = Yes;
  788.                 }
  789.                 else
  790.                 {
  791.                 putchar('\r');
  792.                 }
  793.                 putcap(clear_line);
  794.                 break;
  795.     
  796.             case 'r':        /* renice program */
  797.                 fputs("renice ", stdout);
  798.                 if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
  799.                 {
  800.                 if ((errmsg = renice_procs(tempbuf2)) != NULL)
  801.                 {
  802.                     putchar('\r');
  803.                     standout(errmsg);
  804.                     msg_showing = Yes;
  805.                 }
  806.                 }
  807.                 else
  808.                 {
  809.                 putchar('\r');
  810.                 }
  811.                 putcap(clear_line);
  812.                 break;
  813.     
  814.             default:
  815.                 standout(" Command not understood");
  816.                 msg_showing = Yes;
  817.             }
  818.         }
  819.         else if (msg_showing)
  820.         {
  821.             if (smart_terminal)
  822.             {
  823.             putcap(clear_line);
  824.             }
  825.             msg_showing = No;
  826.         }
  827.         }
  828. #endif
  829.     }
  830.     }
  831.  
  832.     quit(0);
  833. }
  834.  
  835. /*
  836.  *  reset_display() - reset all the display routine pointers so that entire
  837.  *    screen will get redrawn.
  838.  */
  839.  
  840. reset_display()
  841.  
  842. {
  843.     d_loadave    = i_loadave;
  844.     d_procstates = i_procstates;
  845.     d_cpustates  = i_cpustates;
  846.     d_memory     = i_memory;
  847.     d_header     = i_header;
  848.     d_process     = i_process;
  849. }
  850.  
  851. readline(buffer, size, numeric)
  852.  
  853. char *buffer;
  854. int  size;
  855. int  numeric;
  856.  
  857. {
  858.     register char *ptr = buffer;
  859.     register char ch;
  860.     register char cnt = 0;
  861.  
  862.     size -= 1;
  863.     while ((fflush(stdout), read(0, ptr, 1) > 0))
  864.     {
  865.     if ((ch = *ptr) == '\n')
  866.     {
  867.         break;
  868.     }
  869.  
  870.     if (ch == ch_kill)
  871.     {
  872.         *buffer = '\0';
  873.         return(-1);
  874.     }
  875.     else if (ch == ch_erase)
  876.     {
  877.         if (cnt <= 0)
  878.         {
  879.         putchar('\7');
  880.         }
  881.         else
  882.         {
  883.         fputs("\b \b", stdout);
  884.         ptr--;
  885.         cnt--;
  886.         }
  887.     }
  888.     else if (cnt == size || (numeric && (ch < '0' || ch > '9')))
  889.     {
  890.         putchar('\7');
  891.     }
  892.     else
  893.     {
  894.         putchar(ch);
  895.         ptr++;
  896.         cnt++;
  897.     }
  898.     }
  899.     *ptr = '\0';
  900.     return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
  901. }
  902.  
  903. /*
  904.  *  signal handlers
  905.  */
  906.  
  907. leave()            /* exit under normal conditions -- INT handler */
  908.  
  909. {
  910.     end_screen();
  911.     exit(0);
  912. }
  913.  
  914. tstop()
  915.  
  916. {
  917.     /* move to the lower left */
  918.     end_screen();
  919.     fflush(stdout);
  920.  
  921. #ifdef FOUR_ONE        /* a 4.1 system */
  922.  
  923.     /* send a STOP (uncatchable) to everyone in the process group */
  924.     kill(0, SIGSTOP);
  925.  
  926.     /* reset the signal handler */
  927.     signal(SIGTSTP, tstop);
  928.  
  929. #else            /* assume it is a 4.2 system */
  930.  
  931.     /* default the signal handler action */
  932.     signal(SIGTSTP, SIG_DFL);
  933.  
  934.     /* unblock the signal and send ourselves one */
  935.     sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
  936.     kill(0, SIGTSTP);
  937.  
  938.     /* reset the signal handler */
  939.     signal(SIGTSTP, tstop);
  940.  
  941. #endif
  942.     /* reinit screen */
  943.     reinit_screen();
  944.  
  945.     /* jump to appropriate place */
  946.     longjmp(jmp_int, 1);
  947.  
  948.     /*NOTREACHED*/
  949. }
  950.  
  951. quit(status)        /* exit under duress */
  952.  
  953. int status;
  954.  
  955. {
  956.     end_screen();
  957.     exit(status);
  958. }
  959.  
  960. onalrm()
  961.  
  962. {
  963.     return(0);
  964. }
  965.  
  966. /*
  967.  *  proc_compar - comparison function for "qsort"
  968.  *    Compares the resource consumption of two processes using five
  969.  *      distinct keys.  The keys (in descending order of importance) are:
  970.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  971.  *      memory usage.  The process states are ordered as follows (from least
  972.  *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
  973.  *      array declaration below maps a process state index into a number
  974.  *      that reflects this ordering.
  975.  */
  976.  
  977. unsigned char sorted_state[] =
  978. {
  979.     0,    /* not used        */
  980.     3,    /* sleep        */
  981.     1,    /* ABANDONED (WAIT)    */
  982.     6,    /* run            */
  983.     5,    /* start        */
  984.     2,    /* zombie        */
  985.     4    /* stop            */
  986. };
  987.  
  988. proc_compar(pp1, pp2)
  989.  
  990. struct proc **pp1;
  991. struct proc **pp2;
  992.  
  993. {
  994.     register struct proc *p1;
  995.     register struct proc *p2;
  996.     register int result;
  997. #ifndef sun
  998.     register double dresult;
  999. #endif
  1000.  
  1001.     /* remove one level of indirection */
  1002.     p1 = *pp1;
  1003.     p2 = *pp2;
  1004.  
  1005.     /* compare percent cpu (pctcpu) */
  1006. #ifdef sun
  1007.     if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0)
  1008. #else
  1009.     if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
  1010. #endif
  1011.     {
  1012.     /* use cpticks to break the tie */
  1013.     if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
  1014.     {
  1015.         /* use process state to break the tie */
  1016.         if ((result = sorted_state[p2->p_stat] -
  1017.               sorted_state[p1->p_stat])  == 0)
  1018.         {
  1019.         /* use priority to break the tie */
  1020.         if ((result = p2->p_pri - p1->p_pri) == 0)
  1021.         {
  1022.             /* use resident set size (rssize) to break the tie */
  1023.             if ((result = p2->p_rssize - p1->p_rssize) == 0)
  1024.             {
  1025.             /* use total memory to break the tie */
  1026. #ifdef pyr
  1027.             result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) -
  1028.                  (p1->p_tsize + p1->p_dsize + p1->p_ussize);
  1029. #else
  1030.             result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) -
  1031.                  (p1->p_tsize + p1->p_dsize + p1->p_ssize);
  1032. #endif
  1033.             }
  1034.         }
  1035.         }
  1036.     }
  1037.     }
  1038. #ifndef sun
  1039.     else
  1040.     {
  1041.     result = dresult < 0.0 ? -1 : 1;
  1042.     }
  1043. #endif
  1044.  
  1045.     return(result);
  1046. }
  1047.  
  1048. /* routines to translate uids into a string */
  1049.  
  1050. char *user_name(euid, ruid)
  1051.  
  1052. int euid, ruid;
  1053.  
  1054. {
  1055.     return(username(euid));
  1056. }
  1057.  
  1058. char *user_uid(euid, ruid)
  1059.  
  1060. int euid, ruid;
  1061.  
  1062. {
  1063.     return(itoa7(euid));
  1064. }
  1065.  
  1066. /*
  1067.  *  These routines handle uid to username mapping.
  1068.  *  They use a hashing table scheme to reduce reading overhead.
  1069.  */
  1070.  
  1071. struct hash_el {
  1072.     int  uid;
  1073.     char name[8];
  1074. };
  1075.  
  1076. #define    H_empty    -1
  1077.  
  1078. /* simple minded hashing function */
  1079. #define    hashit(i)    ((i) % Table_size)
  1080.  
  1081. struct hash_el hash_table[Table_size];
  1082.  
  1083. init_hash()
  1084.  
  1085. {
  1086.     register int i;
  1087.     register struct hash_el *h;
  1088.  
  1089.     for (h = hash_table, i = 0; i < Table_size; h++, i++)
  1090.     {
  1091.     h->uid = H_empty;
  1092.     }
  1093. }
  1094.  
  1095. char *username(uid)
  1096.  
  1097. register int uid;
  1098.  
  1099. {
  1100.     register int index;
  1101.     register int found;
  1102.     register char *name;
  1103.  
  1104.     /* This is incredibly naive, but it'll probably get changed anyway */
  1105.     index = hashit(uid);
  1106.     while ((found = hash_table[index].uid) != uid)
  1107.     {
  1108.     if (found == H_empty)
  1109.     {
  1110.         /* not here -- get it out of passwd */
  1111.         index = get_user(uid);
  1112.         break;        /* out of while */
  1113.     }
  1114.     index = (index + 1) % Table_size;
  1115.     }
  1116.     return(hash_table[index].name);
  1117. }
  1118.  
  1119. enter_user(uid, name)
  1120.  
  1121. register int  uid;
  1122. register char *name;
  1123.  
  1124. {
  1125.     register int length;
  1126.     register int index;
  1127.     register int try;
  1128.     static int uid_count = 0;
  1129.  
  1130.     /* avoid table overflow -- insure at least one empty slot */
  1131.     if (++uid_count >= Table_size)
  1132.     {
  1133.     fprintf(stderr, "table overflow: too many users\n");
  1134.     quit(10);
  1135.     }
  1136.  
  1137.     index = hashit(uid);
  1138.     while ((try = hash_table[index].uid) != H_empty)
  1139.     {
  1140.     if (try == uid)
  1141.     {
  1142.         return(index);
  1143.     }
  1144.     index = (index + 1) % Table_size;
  1145.     }
  1146.     hash_table[index].uid = uid;
  1147.     strncpy(hash_table[index].name, name, 8);
  1148.     return(index);
  1149. }
  1150.  
  1151. get_user(uid)
  1152.  
  1153. register int uid;
  1154.  
  1155. {
  1156.     struct passwd *pwd;
  1157.     register int last_index;
  1158.  
  1159.     while ((pwd = getpwent()) != NULL)
  1160.     {
  1161.     last_index = enter_user(pwd->pw_uid, pwd->pw_name);
  1162.     if (pwd->pw_uid == uid)
  1163.     {
  1164.         return(last_index);
  1165.     }
  1166.     }
  1167.     return(enter_user(uid, itoa7(uid)));
  1168. }
  1169.  
  1170. atoiwi(str)
  1171.  
  1172. char *str;
  1173.  
  1174. {
  1175.     register int len;
  1176.  
  1177.     len = strlen(str);
  1178.     if (len != 0)
  1179.     {
  1180.     if (strncmp(str, "infinity", len) == 0 ||
  1181.         strncmp(str, "all",      len) == 0 ||
  1182.         strncmp(str, "maximum",  len) == 0)
  1183.     {
  1184.         return(Infinity);
  1185.     }
  1186.     else if (str[0] == '-')
  1187.     {
  1188.         return(Invalid);
  1189.     }
  1190.     else
  1191.     {
  1192.         return(atoi(str));
  1193.     }
  1194.     }
  1195.     return(0);
  1196. }
  1197.  
  1198. /*
  1199.  *  itoa - convert integer (decimal) to ascii string for positive numbers
  1200.  *         only (we don't bother with negative numbers since we know we
  1201.  *       don't use them).
  1202.  */
  1203.  
  1204. static char buffer[16];        /* shared by the next two routines */
  1205.  
  1206. char *itoa(val)
  1207.  
  1208. register int val;
  1209.  
  1210. {
  1211.     register char *ptr;
  1212.  
  1213.     ptr = buffer + sizeof(buffer);
  1214.     *--ptr = '\0';
  1215.     if (val == 0)
  1216.     {
  1217.     *--ptr = '0';
  1218.     }
  1219.     else while (val != 0)
  1220.     {
  1221.     *--ptr = (val % 10) + '0';
  1222.     val /= 10;
  1223.     }
  1224.     return(ptr);
  1225. }
  1226.  
  1227. /*
  1228.  *  itoa7(val) - like itoa, except the number is right justified in a 7
  1229.  *    character field.  This code is a duplication of itoa instead of
  1230.  *    a front end to a more general routine for efficiency.
  1231.  */
  1232.  
  1233. char *itoa7(val)
  1234.  
  1235. register int val;
  1236.  
  1237. {
  1238.     register char *ptr;
  1239.  
  1240.     ptr = buffer + sizeof(buffer);
  1241.     *--ptr = '\0';
  1242.     if (val == 0)
  1243.     {
  1244.     *--ptr = '0';
  1245.     }
  1246.     else while (val != 0)
  1247.     {
  1248.     *--ptr = (val % 10) + '0';
  1249.     val /= 10;
  1250.     }
  1251.     while (ptr > buffer + sizeof(buffer) - 7)
  1252.     {
  1253.     *--ptr = ' ';
  1254.     }
  1255.     return(ptr);
  1256. }
  1257.  
  1258.