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