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