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