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 fputs("\nSwap: ", stdout);
679 lastline++;
680
681 /* format and print the swap summary */
682 summary_format(swap_buffer, stats, swap_names);
683 fputs(swap_buffer, stdout);
684 }
685
686 void
u_swap(int * stats)687 u_swap(int *stats)
688 {
689 static char *new = NULL;
690
691 new = setup_buffer(new, 0);
692
693 /* format the new line */
694 summary_format(new, stats, swap_names);
695 line_update(swap_buffer, new, x_swap, y_swap);
696 }
697
698 /*
699 * *_message() - print the next pending message line, or erase the one
700 * that is there.
701 *
702 * Note that u_message is (currently) the same as i_message.
703 *
704 * Assumptions: lastline is consistent
705 */
706
707 /*
708 * i_message is funny because it gets its message asynchronously (with
709 * respect to screen updates).
710 */
711
712 #define NEXT_MSG_ADDLEN 5
713 static char *next_msg = NULL;
714 static int msglen = 0;
715 /* Invariant: msglen is always the length of the message currently displayed
716 on the screen (even when next_msg doesn't contain that message). */
717
718 void
i_message(void)719 i_message(void)
720 {
721 next_msg = setup_buffer(next_msg, NEXT_MSG_ADDLEN);
722
723 while (lastline < y_message)
724 {
725 fputc('\n', stdout);
726 lastline++;
727 }
728 if (next_msg[0] != '\0')
729 {
730 top_standout(next_msg);
731 msglen = strlen(next_msg);
732 next_msg[0] = '\0';
733 }
734 else if (msglen > 0)
735 {
736 (void) clear_eol(msglen);
737 msglen = 0;
738 }
739 }
740
741 void
u_message(void)742 u_message(void)
743 {
744 i_message();
745 }
746
747 static int header_length;
748
749 /*
750 * Trim a header string to the current display width and return a newly
751 * allocated area with the trimmed header.
752 */
753
754 char *
trim_header(const char * text)755 trim_header(const char *text)
756 {
757 char *s;
758 int width;
759
760 s = NULL;
761 width = screen_width;
762 header_length = strlen(text);
763 if (header_length >= width) {
764 s = strndup(text, width);
765 if (s == NULL)
766 return (NULL);
767 }
768 return (s);
769 }
770
771 /*
772 * *_header(text) - print the header for the process area
773 *
774 * Assumptions: cursor is on the previous line and lastline is consistent
775 */
776
777 void
i_header(const char * text)778 i_header(const char *text)
779 {
780 char *s;
781
782 s = trim_header(text);
783 if (s != NULL)
784 text = s;
785
786 if (header_status == ON)
787 {
788 putchar('\n');
789 fputs(text, stdout);
790 lastline++;
791 }
792 else if (header_status == ERASE)
793 {
794 header_status = OFF;
795 }
796 free(s);
797 }
798
799 void
u_header(const char * text __unused)800 u_header(const char *text __unused)
801 {
802
803 if (header_status == ERASE)
804 {
805 putchar('\n');
806 lastline++;
807 clear_eol(header_length);
808 header_status = OFF;
809 }
810 }
811
812 /*
813 * *_process(line, thisline) - print one process line
814 *
815 * Assumptions: lastline is consistent
816 */
817
818 void
i_process(int line,char * thisline)819 i_process(int line, char *thisline)
820 {
821 char *p;
822 char *base;
823
824 /* make sure we are on the correct line */
825 while (lastline < y_procs + line)
826 {
827 putchar('\n');
828 lastline++;
829 }
830
831 /* truncate the line to conform to our current screen width */
832 int len = strlen(thisline);
833 if (screen_width < len)
834 {
835 thisline[screen_width] = '\0';
836 }
837
838 /* write the line out */
839 fputs(thisline, stdout);
840
841 /* copy it in to our buffer */
842 base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
843 p = stpcpy(base, thisline);
844
845 /* zero fill the rest of it */
846 if (p - base < screen_width)
847 {
848 memset(p, 0, screen_width - (p - base));
849 }
850 }
851
852 void
u_process(int line,char * newline)853 u_process(int line, char *newline)
854 {
855 char *optr;
856 int screen_line = line + Header_lines;
857 char *bufferline;
858
859 /* remember a pointer to the current line in the screen buffer */
860 bufferline = &screenbuf[lineindex(line)];
861
862 /* truncate the line to conform to our current screen width */
863 int len = strlen(newline);
864 if (screen_width < len)
865 {
866 newline[screen_width] = '\0';
867 }
868
869 /* is line higher than we went on the last display? */
870 if (line >= last_hi)
871 {
872 /* yes, just ignore screenbuf and write it out directly */
873 /* get positioned on the correct line */
874 if (screen_line - lastline == 1)
875 {
876 putchar('\n');
877 lastline++;
878 }
879 else
880 {
881 Move_to(0, screen_line);
882 lastline = screen_line;
883 }
884
885 /* now write the line */
886 fputs(newline, stdout);
887
888 /* copy it in to the buffer */
889 optr = stpcpy(bufferline, newline);
890
891 /* zero fill the rest of it */
892 if (optr - bufferline < screen_width)
893 {
894 memset(optr, 0, screen_width - (optr - bufferline));
895 }
896 }
897 else
898 {
899 line_update(bufferline, newline, 0, line + Header_lines);
900 }
901 }
902
903 void
u_endscreen(int hi)904 u_endscreen(int hi)
905 {
906 int screen_line = hi + Header_lines;
907 int i;
908
909 if (smart_terminal)
910 {
911 if (hi < last_hi)
912 {
913 /* need to blank the remainder of the screen */
914 /* but only if there is any screen left below this line */
915 if (lastline + 1 < screen_length)
916 {
917 /* efficiently move to the end of currently displayed info */
918 if (screen_line - lastline < 5)
919 {
920 while (lastline < screen_line)
921 {
922 putchar('\n');
923 lastline++;
924 }
925 }
926 else
927 {
928 Move_to(0, screen_line);
929 lastline = screen_line;
930 }
931
932 if (clear_to_end)
933 {
934 /* we can do this the easy way */
935 putcap(clear_to_end);
936 }
937 else
938 {
939 /* use clear_eol on each line */
940 i = hi;
941 while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
942 {
943 putchar('\n');
944 }
945 }
946 }
947 }
948 last_hi = hi;
949
950 /* move the cursor to a pleasant place */
951 Move_to(x_idlecursor, y_idlecursor);
952 lastline = y_idlecursor;
953 }
954 else
955 {
956 /* separate this display from the next with some vertical room */
957 fputs("\n\n", stdout);
958 }
959 }
960
961 void
display_header(int t)962 display_header(int t)
963 {
964
965 if (t)
966 {
967 header_status = ON;
968 }
969 else if (header_status == ON)
970 {
971 header_status = ERASE;
972 }
973 }
974
975 void
new_message(int type,const char * msgfmt,...)976 new_message(int type, const char *msgfmt, ...)
977 {
978 va_list args;
979 size_t i;
980
981 va_start(args, msgfmt);
982
983 /* first, format the message */
984 vsnprintf(next_msg, setup_buffer_bufsiz + NEXT_MSG_ADDLEN,
985 msgfmt, args);
986
987 va_end(args);
988
989 if (msglen > 0)
990 {
991 /* message there already -- can we clear it? */
992 if (!overstrike)
993 {
994 /* yes -- write it and clear to end */
995 i = strlen(next_msg);
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 clear_eol(msglen - i);
1004 msglen = i;
1005 next_msg[0] = '\0';
1006 }
1007 }
1008 }
1009 else
1010 {
1011 if ((type & MT_delayed) == 0)
1012 {
1013 if (type & MT_standout) {
1014 top_standout(next_msg);
1015 } else {
1016 fputs(next_msg, stdout);
1017 }
1018 msglen = strlen(next_msg);
1019 next_msg[0] = '\0';
1020 }
1021 }
1022 }
1023
1024 void
clear_message(void)1025 clear_message(void)
1026 {
1027 if (clear_eol(msglen) == 1)
1028 {
1029 putchar('\r');
1030 }
1031 }
1032
1033 int
readline(char * buffer,int size,int numeric)1034 readline(char *buffer, int size, int numeric)
1035 {
1036 char *ptr = buffer;
1037 char ch;
1038 char cnt = 0;
1039 char maxcnt = 0;
1040
1041 /* allow room for null terminator */
1042 size -= 1;
1043
1044 /* read loop */
1045 while ((fflush(stdout), read(0, ptr, 1) > 0))
1046 {
1047 /* newline means we are done */
1048 if ((ch = *ptr) == '\n' || ch == '\r')
1049 {
1050 break;
1051 }
1052
1053 /* handle special editing characters */
1054 if (ch == ch_kill)
1055 {
1056 /* kill line -- account for overstriking */
1057 if (overstrike)
1058 {
1059 msglen += maxcnt;
1060 }
1061
1062 /* return null string */
1063 *buffer = '\0';
1064 putchar('\r');
1065 return(-1);
1066 }
1067 else if (ch == ch_erase)
1068 {
1069 /* erase previous character */
1070 if (cnt <= 0)
1071 {
1072 /* none to erase! */
1073 putchar('\7');
1074 }
1075 else
1076 {
1077 fputs("\b \b", stdout);
1078 ptr--;
1079 cnt--;
1080 }
1081 }
1082 /* check for character validity and buffer overflow */
1083 else if (cnt == size || (numeric && !isdigit(ch)) ||
1084 !isprint(ch))
1085 {
1086 /* not legal */
1087 putchar('\7');
1088 }
1089 else
1090 {
1091 /* echo it and store it in the buffer */
1092 putchar(ch);
1093 ptr++;
1094 cnt++;
1095 if (cnt > maxcnt)
1096 {
1097 maxcnt = cnt;
1098 }
1099 }
1100 }
1101
1102 /* all done -- null terminate the string */
1103 *ptr = '\0';
1104
1105 /* account for the extra characters in the message area */
1106 /* (if terminal overstrikes, remember the furthest they went) */
1107 msglen += overstrike ? maxcnt : cnt;
1108
1109 /* return either inputted number or string length */
1110 putchar('\r');
1111 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
1112 }
1113
1114 /* internal support routines */
1115
1116 static void
summary_format(char * str,int * numbers,const char * const * names)1117 summary_format(char *str, int *numbers, const char * const *names)
1118 {
1119 char *p;
1120 int num;
1121 const char *thisname;
1122 char rbuf[6];
1123
1124 /* format each number followed by its string */
1125 p = str;
1126 while ((thisname = *names++) != NULL)
1127 {
1128 /* get the number to format */
1129 num = *numbers++;
1130
1131 /* display only non-zero numbers */
1132 if (num > 0)
1133 {
1134 /* is this number in kilobytes? */
1135 if (thisname[0] == 'K')
1136 {
1137 /* yes: format it as a memory value */
1138 p = stpcpy(p, format_k(num));
1139
1140 /* skip over the K, since it was included by format_k */
1141 p = stpcpy(p, thisname+1);
1142 }
1143 /* is this number a ratio? */
1144 else if (thisname[0] == ':')
1145 {
1146 (void) snprintf(rbuf, sizeof(rbuf), "%.2f",
1147 (float)*(numbers - 2) / (float)num);
1148 p = stpcpy(p, rbuf);
1149 p = stpcpy(p, thisname);
1150 }
1151 else
1152 {
1153 p = stpcpy(p, itoa(num));
1154 p = stpcpy(p, thisname);
1155 }
1156 }
1157
1158 /* ignore negative numbers, but display corresponding string */
1159 else if (num < 0)
1160 {
1161 p = stpcpy(p, thisname);
1162 }
1163 }
1164
1165 /* if the last two characters in the string are ", ", delete them */
1166 p -= 2;
1167 if (p >= str && p[0] == ',' && p[1] == ' ')
1168 {
1169 *p = '\0';
1170 }
1171 }
1172
1173 static void
line_update(char * old,char * new,int start,int line)1174 line_update(char *old, char *new, int start, int line)
1175 {
1176 int ch;
1177 int diff;
1178 int newcol = start + 1;
1179 int lastcol = start;
1180 char cursor_on_line = false;
1181 char *current;
1182
1183 /* compare the two strings and only rewrite what has changed */
1184 current = old;
1185 #ifdef DEBUG
1186 fprintf(debug, "line_update, starting at %d\n", start);
1187 fputs(old, debug);
1188 fputc('\n', debug);
1189 fputs(new, debug);
1190 fputs("\n-\n", debug);
1191 #endif
1192
1193 /* start things off on the right foot */
1194 /* this is to make sure the invariants get set up right */
1195 if ((ch = *new++) != *old)
1196 {
1197 if (line - lastline == 1 && start == 0)
1198 {
1199 putchar('\n');
1200 }
1201 else
1202 {
1203 Move_to(start, line);
1204 }
1205 cursor_on_line = true;
1206 putchar(ch);
1207 *old = ch;
1208 lastcol = start + 1;
1209 }
1210 old++;
1211
1212 /*
1213 * main loop -- check each character. If the old and new aren't the
1214 * same, then update the display. When the distance from the
1215 * current cursor position to the new change is small enough,
1216 * the characters that belong there are written to move the
1217 * cursor over.
1218 *
1219 * Invariants:
1220 * lastcol is the column where the cursor currently is sitting
1221 * (always one beyond the end of the last mismatch).
1222 */
1223 do /* yes, a do...while */
1224 {
1225 if ((ch = *new++) != *old)
1226 {
1227 /* new character is different from old */
1228 /* make sure the cursor is on top of this character */
1229 diff = newcol - lastcol;
1230 if (diff > 0)
1231 {
1232 /* some motion is required--figure out which is shorter */
1233 if (diff < 6 && cursor_on_line)
1234 {
1235 /* overwrite old stuff--get it out of the old buffer */
1236 printf("%.*s", diff, ¤t[lastcol-start]);
1237 }
1238 else
1239 {
1240 /* use cursor addressing */
1241 Move_to(newcol, line);
1242 cursor_on_line = true;
1243 }
1244 /* remember where the cursor is */
1245 lastcol = newcol + 1;
1246 }
1247 else
1248 {
1249 /* already there, update position */
1250 lastcol++;
1251 }
1252
1253 /* write what we need to */
1254 if (ch == '\0')
1255 {
1256 /* at the end--terminate with a clear-to-end-of-line */
1257 (void) clear_eol(strlen(old));
1258 }
1259 else
1260 {
1261 /* write the new character */
1262 putchar(ch);
1263 }
1264 /* put the new character in the screen buffer */
1265 *old = ch;
1266 }
1267
1268 /* update working column and screen buffer pointer */
1269 newcol++;
1270 old++;
1271
1272 } while (ch != '\0');
1273
1274 /* zero out the rest of the line buffer -- MUST BE DONE! */
1275 diff = screen_width - newcol;
1276 if (diff > 0)
1277 {
1278 memset(old, 0, diff);
1279 }
1280
1281 /* remember where the current line is */
1282 if (cursor_on_line)
1283 {
1284 lastline = line;
1285 }
1286 }
1287
1288 /*
1289 * printable(str) - make the string pointed to by "str" into one that is
1290 * printable (i.e.: all ascii), by converting all non-printable
1291 * characters into '?'. Replacements are done in place and a pointer
1292 * to the original buffer is returned.
1293 */
1294
1295 char *
printable(char str[])1296 printable(char str[])
1297 {
1298 char *ptr;
1299 char ch;
1300
1301 ptr = str;
1302 while ((ch = *ptr) != '\0')
1303 {
1304 if (!isprint(ch))
1305 {
1306 *ptr = '?';
1307 }
1308 ptr++;
1309 }
1310 return(str);
1311 }
1312
1313 void
i_uptime(struct timeval * bt,time_t * tod)1314 i_uptime(struct timeval *bt, time_t *tod)
1315 {
1316 time_t uptime;
1317 int days, hrs, mins, secs;
1318
1319 if (bt->tv_sec != -1) {
1320 uptime = *tod - bt->tv_sec;
1321 days = uptime / 86400;
1322 uptime %= 86400;
1323 hrs = uptime / 3600;
1324 uptime %= 3600;
1325 mins = uptime / 60;
1326 secs = uptime % 60;
1327
1328 /*
1329 * Display the uptime.
1330 */
1331
1332 if (smart_terminal)
1333 {
1334 Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
1335 }
1336 else
1337 {
1338 fputs(" ", stdout);
1339 }
1340 printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
1341 }
1342 }
1343
1344 #define SETUPBUFFER_MIN_SCREENWIDTH 80
1345 #define SETUPBUFFER_REQUIRED_ADDBUFSIZ 2
1346
1347 static char *
setup_buffer(char * buffer,int addlen)1348 setup_buffer(char *buffer, int addlen)
1349 {
1350 size_t len, old_len;
1351 char *new_buffer;
1352
1353 setup_buffer_bufsiz = screen_width;
1354 if (setup_buffer_bufsiz < SETUPBUFFER_MIN_SCREENWIDTH)
1355 {
1356 setup_buffer_bufsiz = SETUPBUFFER_MIN_SCREENWIDTH;
1357 }
1358
1359 len = setup_buffer_bufsiz + addlen + SETUPBUFFER_REQUIRED_ADDBUFSIZ;
1360 new_buffer = calloc(len, sizeof(char));
1361 if (new_buffer == NULL)
1362 {
1363 errx(4, "can't allocate sufficient memory");
1364 }
1365 if (buffer != NULL)
1366 {
1367 old_len = strlen(buffer);
1368 memcpy(new_buffer, buffer, old_len < len - 1 ? old_len : len - 1);
1369 free(buffer);
1370 }
1371
1372 return new_buffer;
1373 }
1374