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