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