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