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