home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Utilities / top-0.5-MI / display.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-25  |  21.4 KB  |  1,071 lines

  1. /*
  2.  *  Top users/processes display for Unix
  3.  *  Version 3
  4.  *
  5.  *  This program may be freely redistributed,
  6.  *  but this entire comment MUST remain intact.
  7.  *
  8.  *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
  9.  *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
  10.  */
  11.  
  12. /*
  13.  *  This file contains the routines that display information on the screen.
  14.  *  Each section of the screen has two routines:  one for initially writing
  15.  *  all constant and dynamic text, and one for only updating the text that
  16.  *  changes.  The prefix "i_" is used on all the "initial" routines and the
  17.  *  prefix "u_" is used for all the "updating" routines.
  18.  *
  19.  *  ASSUMPTIONS:
  20.  *        None of the "i_" routines use any of the termcap capabilities.
  21.  *        In this way, those routines can be safely used on terminals that
  22.  *        have minimal (or nonexistant) terminal capabilities.
  23.  *
  24.  *        The routines are called in this order:  *_loadave, i_timeofday,
  25.  *        *_procstates, *_cpustates, *_memory, *_message, *_header,
  26.  *        *_process, u_endscreen.
  27.  *
  28.  *
  29.  *    8/5/95 tpugh@oce.orst.edu
  30.  *    Modified routine summary_format(str, numbers, names, nonzero)
  31.  *    Affects routines: i_procstates(), u_procstates(), i_memory(), u_memory()
  32.  *    Added an additional variable "nonzero" to call arguments.  "nonzero" is
  33.  *  boolean value and determines whether values equal to zero will be displayed.
  34.  *  Acceptable Values for "nonzero" variable.
  35.  *  Yes = display values greater than or equal to 1.
  36.  *   No = display values greater than or equal to zero.
  37.  *
  38.  */
  39.  
  40. #include "os.h"
  41. #include <ctype.h>
  42. #include <time.h>
  43.  
  44. #include "screen.h"        /* interface to screen package */
  45. #include "layout.h"        /* defines for screen position layout */
  46. #include "display.h"
  47. #include "top.h"
  48. #include "top.local.h"
  49. #include "boolean.h"
  50. #include "machine.h"        /* we should eliminate this!!! */
  51. #include "utils.h"
  52.  
  53. #ifdef DEBUG
  54. FILE *debug;
  55. #endif
  56.  
  57. /* imported from screen.c */
  58. extern int overstrike;
  59.  
  60. static int lmpid = 0;
  61. static int last_hi = 0;        /* used in u_process and u_endscreen */
  62. static int lastline = 0;
  63. static int display_width = MAX_COLS;
  64.  
  65. #define lineindex(l) ((l)*display_width)
  66.  
  67. char *printable();
  68.  
  69. /* things initialized by display_init and used thruout */
  70.  
  71. /* buffer of proc information lines for display updating */
  72. char *screenbuf = NULL;
  73.  
  74. static char **procstate_names;
  75. static char **cpustate_names;
  76. static char **memory_names;
  77.  
  78. static int num_procstates;
  79. static int num_cpustates;
  80. static int num_memory;
  81.  
  82. static int *lprocstates;
  83. static int *lcpustates;
  84. static int *lmemory;
  85.  
  86. static enum { OFF, ON, ERASE } header_status = ON;
  87.  
  88. static int string_count();
  89. static void summary_format();
  90. static void line_update();
  91.  
  92. int display_resize()
  93.  
  94. {
  95.     register int lines;
  96.  
  97.     /* first, deallocate any previous buffer that may have been there */
  98.     if (screenbuf != NULL)
  99.     {
  100.     free(screenbuf);
  101.     }
  102.  
  103.     /* calculate the current dimensions */
  104.     /* if operating in "dumb" mode, we only need one line */
  105.     lines = smart_terminal ? screen_length - Header_lines : 1;
  106.  
  107.     /* we don't want more than MAX_COLS columns, since the machine-dependent
  108.        modules make static allocations based on MAX_COLS and we don't want
  109.        to run off the end of their buffers */
  110.     display_width = screen_width;
  111.     if (display_width >= MAX_COLS)
  112.     {
  113.     display_width = MAX_COLS - 1;
  114.     }
  115.  
  116.     /* now, allocate space for the screen buffer */
  117.     screenbuf = (char *)malloc(lines * display_width);
  118.     if (screenbuf == (char *)NULL)
  119.     {
  120.     /* oops! */
  121.     return(-1);
  122.     }
  123.  
  124.     /* return number of lines available */
  125.     /* for dumb terminals, pretend like we can show any amount */
  126.     return(smart_terminal ? lines : Largest);
  127. }
  128.  
  129. int display_init(statics)
  130.  
  131. struct statics *statics;
  132.  
  133. {
  134.     register int lines;
  135.  
  136.     /* call resize to do the dirty work */
  137.     lines = display_resize();
  138.  
  139.     /* only do the rest if we need to */
  140.     if (lines > -1)
  141.     {
  142.     /* save pointers and allocate space for procstate and cpustate names */
  143.     procstate_names = statics->procstate_names;
  144.     num_procstates = string_count(procstate_names);
  145.     lprocstates = (int *)malloc(num_procstates * sizeof(int));
  146.  
  147.     cpustate_names = statics->cpustate_names;
  148.     num_cpustates = string_count(cpustate_names);
  149.     lcpustates = (int *)malloc(num_cpustates * sizeof(int));
  150.  
  151.     memory_names = statics->memory_names;
  152.     num_memory = string_count(memory_names);
  153.     lmemory = (int *)malloc(num_memory * sizeof(int));
  154.     }
  155.  
  156.     /* return number of lines available */
  157.     return(lines);
  158. }
  159.  
  160. i_loadave(mpid, avenrun)
  161.  
  162. int mpid;
  163. double *avenrun;
  164.  
  165. {
  166.     register int i;
  167.  
  168.     /* i_loadave also clears the screen, since it is first */
  169.     clear();
  170.  
  171.     /* mpid == -1 implies this system doesn't have an _mpid */
  172.     if (mpid != -1)
  173.     {
  174.     printf("last pid: %5d;  ", mpid);
  175.     }
  176.  
  177.     printf("load averages");
  178.  
  179.     for (i = 0; i < 3; i++)
  180.     {
  181.     printf("%c %5.2f",
  182.         i == 0 ? ':' : ',',
  183.         avenrun[i]);
  184.     }
  185.     lmpid = mpid;
  186. }
  187.  
  188. u_loadave(mpid, avenrun)
  189.  
  190. int mpid;
  191. double *avenrun;
  192.  
  193. {
  194.     register int i;
  195.  
  196.     if (mpid != -1)
  197.     {
  198.     /* change screen only when value has really changed */
  199.     if (mpid != lmpid)
  200.     {
  201.         Move_to(x_lastpid, y_lastpid);
  202.         printf("%5d", mpid);
  203.         lmpid = mpid;
  204.     }
  205.  
  206.     /* i remembers x coordinate to move to */
  207.     i = x_loadave;
  208.     }
  209.     else
  210.     {
  211.     i = x_loadave_nompid;
  212.     }
  213.  
  214.     /* move into position for load averages */
  215.     Move_to(i, y_loadave);
  216.  
  217.     /* display new load averages */
  218.     /* we should optimize this and only display changes */
  219.     for (i = 0; i < 3; i++)
  220.     {
  221.     printf("%s%5.2f",
  222.         i == 0 ? "" : ", ",
  223.         avenrun[i]);
  224.     }
  225. }
  226.  
  227. i_timeofday(tod)
  228.  
  229. time_t *tod;
  230.  
  231. {
  232.     /*
  233.      *  Display the current time.
  234.      *  "ctime" always returns a string that looks like this:
  235.      *  
  236.      *    Sun Sep 16 01:03:52 1973
  237.      *      012345678901234567890123
  238.      *              1         2
  239.      *
  240.      *  We want indices 11 thru 18 (length 8).
  241.      */
  242.  
  243.     if (smart_terminal)
  244.     {
  245.     Move_to(screen_width - 8, 0);
  246.     }
  247.     else
  248.     {
  249.     fputs("    ", stdout);
  250.     }
  251. #ifdef DEBUG
  252.     {
  253.     char *foo;
  254.     foo = ctime(tod);
  255.     fputs(foo, stdout);
  256.     }
  257. #endif
  258.     printf("%-8.8s\n", &(ctime(tod)[11]));
  259.     lastline = 1;
  260. }
  261.  
  262. static int ltotal = 0;
  263. static char procstates_buffer[128];
  264.  
  265. /*
  266.  *  *_procstates(total, brkdn, names) - print the process summary line
  267.  *
  268.  *  Assumptions:  cursor is at the beginning of the line on entry
  269.  *          lastline is valid
  270.  */
  271.  
  272. i_procstates(total, brkdn)
  273.  
  274. int total;
  275. int *brkdn;
  276.  
  277. {
  278.     register int i;
  279.  
  280.     /* write current number of processes and remember the value */
  281.     printf("%d processes:", total);
  282.     ltotal = total;
  283.  
  284.     /* put out enough spaces to get to column 15 */
  285.     i = digits(total);
  286.     while (i++ < 4)
  287.     {
  288.     putchar(' ');
  289.     }
  290.  
  291.     /* format and print the process state summary */
  292.     summary_format(procstates_buffer, brkdn, procstate_names, Yes);
  293.     fputs(procstates_buffer, stdout);
  294.  
  295.     /* save the numbers for next time */
  296.     memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
  297. }
  298.  
  299. u_procstates(total, brkdn)
  300.  
  301. int total;
  302. int *brkdn;
  303.  
  304. {
  305.     static char new[128];
  306.     register int i;
  307.  
  308.     /* update number of processes only if it has changed */
  309.     if (ltotal != total)
  310.     {
  311.     /* move and overwrite */
  312. #if (x_procstate == 0)
  313.     Move_to(x_procstate, y_procstate);
  314. #else
  315.     /* cursor is already there...no motion needed */
  316.     /* assert(lastline == 1); */
  317. #endif
  318.     printf("%d", total);
  319.  
  320.     /* if number of digits differs, rewrite the label */
  321.     if (digits(total) != digits(ltotal))
  322.     {
  323.         fputs(" processes:", stdout);
  324.         /* put out enough spaces to get to column 15 */
  325.         i = digits(total);
  326.         while (i++ < 4)
  327.         {
  328.         putchar(' ');
  329.         }
  330.         /* cursor may end up right where we want it!!! */
  331.     }
  332.  
  333.     /* save new total */
  334.     ltotal = total;
  335.     }
  336.  
  337.     /* see if any of the state numbers has changed */
  338.     if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
  339.     {
  340.     /* format and update the line */
  341.     summary_format(new, brkdn, procstate_names, Yes);
  342.     line_update(procstates_buffer, new, x_brkdn, y_brkdn);
  343.     }
  344. }
  345.  
  346. /*
  347.  *  *_cpustates(states, names) - print the cpu state percentages
  348.  *
  349.  *  Assumptions:  cursor is on the PREVIOUS line
  350.  */
  351.  
  352. i_cpustates(states)
  353.  
  354. register int *states;
  355.  
  356. {
  357.     register int i = 0;
  358.     register int value;
  359.     register char **names = cpustate_names;
  360.     register char *thisname;
  361.  
  362.     printf("\nCpu states: ");
  363.     lastline++;
  364.     while ((thisname = *names++) != NULL)
  365.     {
  366.     if (*thisname != '\0')
  367.     {
  368.         /* retrieve the value and remember it */
  369.         value = *states++;
  370.  
  371.         /* if percentage is >= 1000, print it as 100% */
  372.         printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
  373.            i++ == 0 ? "" : ", ",
  374.            ((float)value)/10.,
  375.            thisname);
  376.     }
  377.     }
  378. }
  379.  
  380. u_cpustates(states)
  381.  
  382. register int *states;
  383.  
  384. {
  385.     register int i = 0;
  386.     register int value;
  387.     register char **names = cpustate_names;
  388.     register char *thisname;
  389.  
  390.     Move_to(12, y_cpustates);
  391.     lastline = y_cpustates;
  392.  
  393.     /* we could be much more optimal about this */
  394.     while ((thisname = *names++) != NULL)
  395.     {
  396.     if (*thisname != '\0')
  397.     {
  398.         /* retrieve value and remember it */
  399.         value = *states++;
  400.  
  401.         /* if percentage is >= 1000, print it as 100% */
  402.         printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
  403.            i++ == 0 ? "" : ", ",
  404.            ((double)value)/10.,
  405.            thisname);
  406.     }
  407.     }
  408. }
  409.  
  410. z_cpustates()
  411.  
  412. {
  413.     register int i = 0;
  414.     register char **names = cpustate_names;
  415.     register char *thisname;
  416.  
  417.     printf("\nCpu states: ");
  418.     lastline++;
  419.  
  420.     while ((thisname = *names++) != NULL)
  421.     {
  422.     if (*thisname != '\0')
  423.     {
  424.         printf("%s    %% %s", i++ == 0 ? "" : ", ", thisname);
  425.     }
  426.     }
  427. }
  428.  
  429. /*
  430.  *  *_memory(stats) - print "Memory: " followed by the memory summary string
  431.  *
  432.  *  Assumptions:  cursor is on "lastline"
  433.  *                for i_memory ONLY: cursor is on the previous line
  434.  */
  435.  
  436. char memory_buffer[MAX_COLS];
  437.  
  438. i_memory(stats)
  439.  
  440. int *stats;
  441.  
  442. {
  443.     fputs("\nMemory: ", stdout);
  444.     lastline++;
  445.  
  446.     /* format and print the memory summary */
  447.     summary_format(memory_buffer, stats, memory_names, No);
  448.     fputs(memory_buffer, stdout);
  449. }
  450.  
  451. u_memory(stats)
  452.  
  453. int *stats;
  454.  
  455. {
  456.     static char new[MAX_COLS];
  457.  
  458.     /* format the new line */
  459.     summary_format(new, stats, memory_names, No);
  460.     line_update(memory_buffer, new, x_mem, y_mem);
  461. }
  462.  
  463. /*
  464.  *  *_message() - print the next pending message line, or erase the one
  465.  *                that is there.
  466.  *
  467.  *  Note that u_message is (currently) the same as i_message.
  468.  *
  469.  *  Assumptions:  lastline is consistent
  470.  */
  471.  
  472. /*
  473.  *  i_message is funny because it gets its message asynchronously (with
  474.  *    respect to screen updates).
  475.  */
  476.  
  477. static char next_msg[MAX_COLS + 5];
  478. static int msglen = 0;
  479. /* Invariant: msglen is always the length of the message currently displayed
  480.    on the screen (even when next_msg doesn't contain that message). */
  481.  
  482. i_message()
  483.  
  484. {
  485.     while (lastline < y_message)
  486.     {
  487.     fputc('\n', stdout);
  488.     lastline++;
  489.     }
  490.     if (next_msg[0] != '\0')
  491.     {
  492.     standout(next_msg);
  493.     msglen = strlen(next_msg);
  494.     next_msg[0] = '\0';
  495.     }
  496.     else if (msglen > 0)
  497.     {
  498.     (void) clear_eol(msglen);
  499.     msglen = 0;
  500.     }
  501. }
  502.  
  503. u_message()
  504.  
  505. {
  506. #ifdef notdef
  507.     putchar('\n');
  508.     if (msglen > 0)
  509.     {
  510.     (void) clear_eol(msglen);
  511.     msglen = 0;
  512.     }
  513. #endif
  514.     i_message();
  515. }
  516.  
  517. static int header_length;
  518.  
  519. /*
  520.  *  *_header(text) - print the header for the process area
  521.  *
  522.  *  Assumptions:  cursor is on the previous line and lastline is consistent
  523.  */
  524.  
  525. i_header(text)
  526.  
  527. char *text;
  528.  
  529. {
  530.     header_length = strlen(text);
  531.     if (header_status == ON)
  532.     {
  533.     putchar('\n');
  534.     fputs(text, stdout);
  535.     lastline++;
  536.     }
  537.     else if (header_status == ERASE)
  538.     {
  539.     header_status = OFF;
  540.     }
  541. }
  542.  
  543. /*ARGSUSED*/
  544. u_header(text)
  545.  
  546. char *text;        /* ignored */
  547.  
  548. {
  549.     if (header_status == ERASE)
  550.     {
  551.     putchar('\n');
  552.     lastline++;
  553.     clear_eol(header_length);
  554.     header_status = OFF;
  555.     }
  556. }
  557.  
  558. /*
  559.  *  *_process(line, thisline) - print one process line
  560.  *
  561.  *  Assumptions:  lastline is consistent
  562.  */
  563.  
  564. i_process(line, thisline)
  565.  
  566. int line;
  567. char *thisline;
  568.  
  569. {
  570.     register char *p;
  571.     register char *base;
  572.  
  573.     /* make sure we are on the correct line */
  574.     while (lastline < y_procs + line)
  575.     {
  576.     putchar('\n');
  577.     lastline++;
  578.     }
  579.  
  580.     /* truncate the line to conform to our current screen width */
  581.     thisline[display_width] = '\0';
  582.  
  583.     /* write the line out */
  584.     fputs(thisline, stdout);
  585.  
  586.     /* copy it in to our buffer */
  587.     base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
  588.     p = strecpy(base, thisline);
  589.  
  590.     /* zero fill the rest of it */
  591.     memzero(p, display_width - (p - base));
  592. }
  593.  
  594. u_process(line, newline)
  595.  
  596. int line;
  597. char *newline;
  598.  
  599. {
  600.     register char *optr;
  601.     register int screen_line = line + Header_lines;
  602.     register char *bufferline;
  603.  
  604.     /* remember a pointer to the current line in the screen buffer */
  605.     bufferline = &screenbuf[lineindex(line)];
  606.  
  607.     /* truncate the line to conform to our current screen width */
  608.     newline[display_width] = '\0';
  609.  
  610.     /* is line higher than we went on the last display? */
  611.     if (line >= last_hi)
  612.     {
  613.     /* yes, just ignore screenbuf and write it out directly */
  614.     /* get positioned on the correct line */
  615.     if (screen_line - lastline == 1)
  616.     {
  617.         putchar('\n');
  618.         lastline++;
  619.     }
  620.     else
  621.     {
  622.         Move_to(0, screen_line);
  623.         lastline = screen_line;
  624.     }
  625.  
  626.     /* now write the line */
  627.     fputs(newline, stdout);
  628.  
  629.     /* copy it in to the buffer */
  630.     optr = strecpy(bufferline, newline);
  631.  
  632.     /* zero fill the rest of it */
  633.     memzero(optr, display_width - (optr - bufferline));
  634.     }
  635.     else
  636.     {
  637.     line_update(bufferline, newline, 0, line + Header_lines);
  638.     }
  639. }
  640.  
  641. u_endscreen(hi)
  642.  
  643. register int hi;
  644.  
  645. {
  646.     register int screen_line = hi + Header_lines;
  647.     register int i;
  648.  
  649.     if (smart_terminal)
  650.     {
  651.     if (hi < last_hi)
  652.     {
  653.         /* need to blank the remainder of the screen */
  654.         /* but only if there is any screen left below this line */
  655.         if (lastline + 1 < screen_length)
  656.         {
  657.         /* efficiently move to the end of currently displayed info */
  658.         if (screen_line - lastline < 5)
  659.         {
  660.             while (lastline < screen_line)
  661.             {
  662.             putchar('\n');
  663.             lastline++;
  664.             }
  665.         }
  666.         else
  667.         {
  668.             Move_to(0, screen_line);
  669.             lastline = screen_line;
  670.         }
  671.  
  672.         if (clear_to_end)
  673.         {
  674.             /* we can do this the easy way */
  675.             putcap(clear_to_end);
  676.         }
  677.         else
  678.         {
  679.             /* use clear_eol on each line */
  680.             i = hi;
  681.             while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
  682.             {
  683.             putchar('\n');
  684.             }
  685.         }
  686.         }
  687.     }
  688.     last_hi = hi;
  689.  
  690.     /* move the cursor to a pleasant place */
  691.     Move_to(x_idlecursor, y_idlecursor);
  692.     lastline = y_idlecursor;
  693.     }
  694.     else
  695.     {
  696.     /* separate this display from the next with some vertical room */
  697.     fputs("\n\n", stdout);
  698.     }
  699. }
  700.  
  701. display_header(t)
  702.  
  703. int t;
  704.  
  705. {
  706.     if (t)
  707.     {
  708.     header_status = ON;
  709.     }
  710.     else if (header_status == ON)
  711.     {
  712.     header_status = ERASE;
  713.     }
  714. }
  715.  
  716. /*VARARGS2*/
  717. new_message(type, msgfmt, a1, a2, a3)
  718.  
  719. int type;
  720. char *msgfmt;
  721. caddr_t a1, a2, a3;
  722.  
  723. {
  724.     register int i;
  725.  
  726.     /* first, format the message */
  727.     (void) sprintf(next_msg, msgfmt, a1, a2, a3);
  728.  
  729.     if (msglen > 0)
  730.     {
  731.     /* message there already -- can we clear it? */
  732.     if (!overstrike)
  733.     {
  734.         /* yes -- write it and clear to end */
  735.         i = strlen(next_msg);
  736.         if ((type & MT_delayed) == 0)
  737.         {
  738.         type & MT_standout ? standout(next_msg) :
  739.                              fputs(next_msg, stdout);
  740.         (void) clear_eol(msglen - i);
  741.         msglen = i;
  742.         next_msg[0] = '\0';
  743.         }
  744.     }
  745.     }
  746.     else
  747.     {
  748.     if ((type & MT_delayed) == 0)
  749.     {
  750.         type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
  751.         msglen = strlen(next_msg);
  752.         next_msg[0] = '\0';
  753.     }
  754.     }
  755. }
  756.  
  757. clear_message()
  758.  
  759. {
  760.     if (clear_eol(msglen) == 1)
  761.     {
  762.     putchar('\r');
  763.     }
  764. }
  765.  
  766. readline(buffer, size, numeric)
  767.  
  768. char *buffer;
  769. int  size;
  770. int  numeric;
  771.  
  772. {
  773.     register char *ptr = buffer;
  774.     register char ch;
  775.     register char cnt = 0;
  776.     register char maxcnt = 0;
  777.  
  778.     /* allow room for null terminator */
  779.     size -= 1;
  780.  
  781.     /* read loop */
  782.     while ((fflush(stdout), read(0, ptr, 1) > 0))
  783.     {
  784.     /* newline means we are done */
  785.     if ((ch = *ptr) == '\n')
  786.     {
  787.         break;
  788.     }
  789.  
  790.     /* handle special editing characters */
  791.     if (ch == ch_kill)
  792.     {
  793.         /* kill line -- account for overstriking */
  794.         if (overstrike)
  795.         {
  796.         msglen += maxcnt;
  797.         }
  798.  
  799.         /* return null string */
  800.         *buffer = '\0';
  801.         putchar('\r');
  802.         return(-1);
  803.     }
  804.     else if (ch == ch_erase)
  805.     {
  806.         /* erase previous character */
  807.         if (cnt <= 0)
  808.         {
  809.         /* none to erase! */
  810.         putchar('\7');
  811.         }
  812.         else
  813.         {
  814.         fputs("\b \b", stdout);
  815.         ptr--;
  816.         cnt--;
  817.         }
  818.     }
  819.     /* check for character validity and buffer overflow */
  820.     else if (cnt == size || (numeric && !isdigit(ch)) ||
  821.         !isprint(ch))
  822.     {
  823.         /* not legal */
  824.         putchar('\7');
  825.     }
  826.     else
  827.     {
  828.         /* echo it and store it in the buffer */
  829.         putchar(ch);
  830.         ptr++;
  831.         cnt++;
  832.         if (cnt > maxcnt)
  833.         {
  834.         maxcnt = cnt;
  835.         }
  836.     }
  837.     }
  838.  
  839.     /* all done -- null terminate the string */
  840.     *ptr = '\0';
  841.  
  842.     /* account for the extra characters in the message area */
  843.     /* (if terminal overstrikes, remember the furthest they went) */
  844.     msglen += overstrike ? maxcnt : cnt;
  845.  
  846.     /* return either inputted number or string length */
  847.     putchar('\r');
  848.     return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
  849. }
  850.  
  851. /* internal support routines */
  852.  
  853. static int string_count(pp)
  854.  
  855. register char **pp;
  856.  
  857. {
  858.     register int cnt;
  859.  
  860.     cnt = 0;
  861.     while (*pp++ != NULL)
  862.     {
  863.     cnt++;
  864.     }
  865.     return(cnt);
  866. }
  867.  
  868. static void summary_format(str, numbers, names, nonzero)
  869.  
  870. char *str;
  871. int *numbers;
  872. register char **names;
  873. int nonzero;
  874.  
  875. {
  876.     register char *p;
  877.     register int num;
  878.     register char *thisname;
  879.     register int useM = No;
  880.     register int limit = (nonzero==Yes ? 1 : 0);
  881.  
  882.     /* format each number followed by its string */
  883.     p = str;
  884.     while ((thisname = *names++) != NULL)
  885.     {
  886.     /* get the number to format */
  887.     num = *numbers++;
  888.  
  889.     /* display only non-zero numbers */        
  890.     if (num >= limit)
  891.     {
  892.         /* is this number in kilobytes and it is larger than 9999? */
  893.         if (thisname[0] == 'K' && num >= 10000)
  894.         {
  895.         /* yes: format it as megabytes, rounded */
  896.         /* Change from 1000 to 1024 for correct KB to MB conversion. */
  897.         p = strecpy(p, itoa((num+500)/1024));
  898.  
  899.         /* replace 'K' with 'M' in string to display */
  900.         *p++ = 'M';
  901.         p = strecpy(p, thisname+1);
  902.         }
  903.         else
  904.         {
  905.         p = strecpy(p, itoa(num));
  906.         p = strecpy(p, thisname);
  907.         }
  908.     }
  909.  
  910.     /* ignore negative numbers, but display corresponding string */
  911.     else if (num < 0)
  912.     {
  913.         p = strecpy(p, thisname);
  914.     }
  915.     }
  916.  
  917.     /* if the last two characters in the string are ", ", delete them */
  918.     p -= 2;
  919.     if (p >= str && p[0] == ',' && p[1] == ' ')
  920.     {
  921.     *p = '\0';
  922.     }
  923. }
  924.  
  925. static void line_update(old, new, start, line)
  926.  
  927. register char *old;
  928. register char *new;
  929. int start;
  930. int line;
  931.  
  932. {
  933.     register int ch;
  934.     register int diff;
  935.     register int newcol = start + 1;
  936.     register int lastcol = start;
  937.     char cursor_on_line = No;
  938.     char *current;
  939.  
  940.     /* compare the two strings and only rewrite what has changed */
  941.     current = old;
  942. #ifdef DEBUG
  943.     fprintf(debug, "line_update, starting at %d\n", start);
  944.     fputs(old, debug);
  945.     fputc('\n', debug);
  946.     fputs(new, debug);
  947.     fputs("\n-\n", debug);
  948. #endif
  949.  
  950.     /* start things off on the right foot            */
  951.     /* this is to make sure the invariants get set up right */
  952.     if ((ch = *new++) != *old)
  953.     {
  954.     if (line - lastline == 1 && start == 0)
  955.     {
  956.         putchar('\n');
  957.     }
  958.     else
  959.     {
  960.         Move_to(start, line);
  961.     }
  962.     cursor_on_line = Yes;
  963.     putchar(ch);
  964.     *old = ch;
  965.     lastcol = 1;
  966.     }
  967.     old++;
  968.     
  969.     /*
  970.      *  main loop -- check each character.  If the old and new aren't the
  971.      *    same, then update the display.  When the distance from the
  972.      *    current cursor position to the new change is small enough,
  973.      *    the characters that belong there are written to move the
  974.      *    cursor over.
  975.      *
  976.      *    Invariants:
  977.      *        lastcol is the column where the cursor currently is sitting
  978.      *        (always one beyond the end of the last mismatch).
  979.      */
  980.     do        /* yes, a do...while */
  981.     {
  982.     if ((ch = *new++) != *old)
  983.     {
  984.         /* new character is different from old      */
  985.         /* make sure the cursor is on top of this character */
  986.         diff = newcol - lastcol;
  987.         if (diff > 0)
  988.         {
  989.         /* some motion is required--figure out which is shorter */
  990.         if (diff < 6 && cursor_on_line)
  991.         {
  992.             /* overwrite old stuff--get it out of the old buffer */
  993.             printf("%.*s", diff, ¤t[lastcol-start]);
  994.         }
  995.         else
  996.         {
  997.             /* use cursor addressing */
  998.             Move_to(newcol, line);
  999.             cursor_on_line = Yes;
  1000.         }
  1001.         /* remember where the cursor is */
  1002.         lastcol = newcol + 1;
  1003.         }
  1004.         else
  1005.         {
  1006.         /* already there, update position */
  1007.         lastcol++;
  1008.         }
  1009.         
  1010.         /* write what we need to */
  1011.         if (ch == '\0')
  1012.         {
  1013.         /* at the end--terminate with a clear-to-end-of-line */
  1014.         (void) clear_eol(strlen(old));
  1015.         }
  1016.         else
  1017.         {
  1018.         /* write the new character */
  1019.         putchar(ch);
  1020.         }
  1021.         /* put the new character in the screen buffer */
  1022.         *old = ch;
  1023.     }
  1024.         
  1025.     /* update working column and screen buffer pointer */
  1026.     newcol++;
  1027.     old++;
  1028.         
  1029.     } while (ch != '\0');
  1030.  
  1031.     /* zero out the rest of the line buffer -- MUST BE DONE! */
  1032.     diff = display_width - newcol;
  1033.     if (diff > 0)
  1034.     {
  1035.     memzero(old, diff);
  1036.     }
  1037.  
  1038.     /* remember where the current line is */
  1039.     if (cursor_on_line)
  1040.     {
  1041.     lastline = line;
  1042.     }
  1043. }
  1044.  
  1045. /*
  1046.  *  printable(str) - make the string pointed to by "str" into one that is
  1047.  *    printable (i.e.: all ascii), by converting all non-printable
  1048.  *    characters into '?'.  Replacements are done in place and a pointer
  1049.  *    to the original buffer is returned.
  1050.  */
  1051.  
  1052. char *printable(str)
  1053.  
  1054. char *str;
  1055.  
  1056. {
  1057.     register char *ptr;
  1058.     register char ch;
  1059.  
  1060.     ptr = str;
  1061.     while ((ch = *ptr) != '\0')
  1062.     {
  1063.     if (!isprint(ch))
  1064.     {
  1065.         *ptr = '?';
  1066.     }
  1067.     ptr++;
  1068.     }
  1069.     return(str);
  1070. }
  1071.