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