2 * Copyright (c) 1984 through 2008, William LeFebvre
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
16 * * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * Top users/processes display for Unix
39 * This file contains the routines that display information on the screen.
40 * Each section of the screen has two routines: one for initially writing
41 * all constant and dynamic text, and one for only updating the text that
42 * changes. The prefix "i_" is used on all the "initial" routines and the
43 * prefix "u_" is used for all the "updating" routines.
46 * None of the "i_" routines use any of the termcap capabilities.
47 * In this way, those routines can be safely used on terminals that
48 * have minimal (or nonexistant) terminal capabilities.
50 * The routines should be called in this order: *_loadave, *_uptime,
51 * i_timeofday, *_procstates, *_cpustates, *_memory, *_swap,
52 * *_message, *_header, *_process, *_endscreen.
58 #include <sys/types.h>
64 #include "screen.h" /* interface to screen package */
65 #include "layout.h" /* defines for screen position layout */
76 #define MESSAGE_DISPLAY_TIME 5
78 /* imported from screen.c */
79 extern int overstrike;
81 static int lmpid = -1;
82 static int display_width = MAX_COLS;
84 /* cursor positions of key points on the screen are maintained here */
85 /* layout.h has static definitions, but we may change our minds on some
86 of the positions as we make decisions about what needs to be displayed */
88 static int x_lastpid = X_LASTPID;
89 static int y_lastpid = Y_LASTPID;
90 static int x_loadave = X_LOADAVE;
91 static int y_loadave = Y_LOADAVE;
92 static int x_minibar = X_MINIBAR;
93 static int y_minibar = Y_MINIBAR;
94 static int x_uptime = X_UPTIME;
95 static int y_uptime = Y_UPTIME;
96 static int x_procstate = X_PROCSTATE;
97 static int y_procstate = Y_PROCSTATE;
98 static int x_cpustates = X_CPUSTATES;
99 static int y_cpustates = Y_CPUSTATES;
100 static int x_kernel = X_KERNEL;
101 static int y_kernel = Y_KERNEL;
102 static int x_mem = X_MEM;
103 static int y_mem = Y_MEM;
104 static int x_swap = X_SWAP;
105 static int y_swap = Y_SWAP;
106 static int y_message = Y_MESSAGE;
107 static int x_header = X_HEADER;
108 static int y_header = Y_HEADER;
109 static int x_idlecursor = X_IDLECURSOR;
110 static int y_idlecursor = Y_IDLECURSOR;
111 static int y_procs = Y_PROCS;
113 /* buffer and colormask that describes the content of the screen */
114 /* these are singly dimensioned arrays -- the row boundaries are
115 determined on the fly.
117 static char *screenbuf = NULL;
118 static char *colorbuf = NULL;
119 static char scratchbuf[MAX_COLS];
120 static int bufsize = 0;
122 /* lineindex tells us where the beginning of a line is in the buffer */
123 #define lineindex(l) ((l)*MAX_COLS)
125 /* screen's cursor */
126 static int curr_x, curr_y;
127 static int curr_color;
130 static int virt_x, virt_y;
132 static char **procstate_names;
133 static char **cpustate_names;
134 static char **memory_names;
135 static char **swap_names;
136 static char **kernel_names;
138 static int num_procstates;
139 static int num_cpustates;
140 static int num_memory;
142 static int num_kernel;
144 static int *lprocstates;
145 static int *lcpustates;
147 static int *cpustate_columns;
148 static int cpustate_total_length;
150 static int header_status = Yes;
152 /* pending messages are stored in a circular buffer, where message_first
153 is the next one to display, and message_last is the last one
154 in the buffer. Counters wrap around at MAX_MESSAGES. The buffer is
155 empty when message_first == message_last and full when
156 message_last + 1 == message_first. The pointer message_current holds
157 the message currently being displayed, or "" if there is none.
159 #define MAX_MESSAGES 16
160 static char *message_buf[MAX_MESSAGES];
161 static int message_first = 0;
162 static int message_last = 0;
163 static struct timeval message_time = {0, 0};
164 static char *message_current = NULL;
165 static int message_length = 0;
166 static int message_hold = 1;
167 static int message_barrier = No;
170 static int load_cidx[3];
171 static int header_cidx;
172 static int *cpustate_cidx;
173 static int *memory_cidx;
174 static int *swap_cidx;
175 static int *kernel_cidx;
177 #define memory_cidx NULL
178 #define swap_cidx NULL
179 #define kernel_cidx NULL
183 /* internal support routines */
186 * static int string_count(char **pp)
188 * Pointer "pp" points to an array of string pointers, which is
189 * terminated by a NULL. Return the number of string pointers in
194 string_count(char **pp)
197 register int cnt = 0;
201 while (*pp++ != NULL)
213 dprintf("display_clear\n");
215 memzero(screenbuf, bufsize);
216 memzero(colorbuf, bufsize);
221 * void display_move(int x, int y)
223 * Efficiently move the cursor to x, y. This assumes the cursor is
224 * currently located at curr_x, curr_y, and will only use cursor
225 * addressing when it is less expensive than overstriking what's
226 * already on the screen.
230 display_move(int x, int y)
238 int color = curr_color;
240 dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y);
242 /* are we in a position to do this without cursor addressing? */
243 if (curr_y < y || (curr_y == y && curr_x <= x))
245 /* start buffering up what it would take to move there by rewriting
246 what's on the screen */
250 /* one newline for every line */
251 while (cnt > 0 && curr_y < y)
256 p = strcpyend(p, color_setstr(0));
267 /* write whats in the screenbuf */
268 bufp = &screenbuf[lineindex(curr_y) + curr_x];
269 colorp = &colorbuf[lineindex(curr_y) + curr_x];
270 while (cnt > 0 && curr_x < x)
273 if (color != *colorp)
276 p = strcpyend(p, color_setstr(color));
280 if ((*p = *bufp) == '\0')
282 /* somwhere on screen we haven't been before */
293 /* move the cursor */
296 /* screen rewrite is cheaper */
306 /* update our position */
312 * display_write(int x, int y, int newcolor, int eol, char *new)
314 * Optimized write to the display. This writes characters to the
315 * screen in a way that optimizes the number of characters actually
316 * sent, by comparing what is being written to what is already on
317 * the screen (according to screenbuf and colorbuf). The string to
318 * write is "new", the first character of "new" should appear at
319 * screen position x, y. If x is -1 then "new" begins wherever the
320 * cursor is currently positioned. The string is written with color
321 * "newcolor". If "eol" is true then the remainder of the line is
322 * cleared. It is expected that "new" will have no newlines and no
327 display_write(int x, int y, int newcolor, int eol, char *new)
335 dprintf("display_write(%d, %d, %d, %d, \"%s\")\n",
336 x, y, newcolor, eol, new);
338 /* dumb terminal handling here */
343 /* make sure we are on the right line */
351 /* make sure we are on the right column */
361 curr_x += strlen(new);
366 /* adjust for "here" */
378 /* a pointer to where we start */
379 bufp = &screenbuf[lineindex(y) + x];
380 colorp = &colorbuf[lineindex(y) + x];
383 while ((ch = *new++) != '\0')
385 /* if either character or color are different, an update is needed */
386 /* but only when the screen is wide enough */
387 if (x < display_width && (ch != *bufp || newcolor != *colorp))
390 if (y != curr_y || x != curr_x)
392 /* have to move the cursor */
396 /* write character */
398 if (curr_color != newcolor)
400 fputs(color_setstr(newcolor), stdout);
401 curr_color = newcolor;
406 *colorp = curr_color;
418 if (eol && *bufp != '\0')
420 dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp);
421 /* make sure we are color 0 */
425 fputs(color_setstr(0), stdout);
430 /* make sure we are at the end */
431 if (x != curr_x || y != curr_y)
439 screen_cleareol(strlen(bufp));
441 /* clear out whats left of this line's buffer */
442 diff = display_width - x;
446 memzero(colorp, diff);
452 display_fmt(int x, int y, int newcolor, int eol, char *fmt, ...)
459 vsnprintf(scratchbuf, MAX_COLS, fmt, argp);
460 display_write(x, y, newcolor, eol, scratchbuf);
472 /* is there anything out there that needs to be cleared? */
473 p = &screenbuf[lineindex(virt_y) + virt_x];
480 /* this line is clear, what about the rest? */
482 while (++y < screen_length)
484 if (screenbuf[lineindex(y)] != '\0')
494 dprintf("display_cte: clearing\n");
496 /* we will need this later */
497 len = lineindex(virt_y) + virt_x;
499 /* move to x and y, then clear to end */
500 display_move(virt_x, virt_y);
503 /* screen has no clear to end, so do it by hand */
508 screen_cleareol(len);
510 while (++virt_y < screen_length)
512 display_move(0, virt_y);
513 p = &screenbuf[lineindex(virt_y)];
517 screen_cleareol(len);
522 /* clear the screenbuf */
523 memzero(&screenbuf[len], bufsize - len);
524 memzero(&colorbuf[len], bufsize - len);
529 summary_format(int x, int y, int *numbers, char **names, int *cidx)
533 register char *thisname;
534 register char *lastname = NULL;
537 /* format each number followed by its string */
538 while ((thisname = *names++) != NULL)
540 /* get the number to format */
544 /* display only non-zero numbers */
547 /* write the previous name */
548 if (lastname != NULL)
550 display_write(-1, -1, 0, 0, lastname);
557 color = color_test(*cidx++, num);
561 /* write this number if positive */
564 display_write(x, y, color, 0, itoa(num));
567 /* defer writing this name */
570 /* next iteration will not start at x, y */
575 /* if the last string has a separator on the end, it has to be
577 if (lastname != NULL)
579 if ((num = strlen(lastname)) > 1 &&
580 lastname[num-2] == ',' && lastname[num-1] == ' ')
582 display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname);
586 display_write(-1, -1, 0, 1, lastname);
592 summary_format_memory(int x, int y, long *numbers, char **names, int *cidx)
597 register char *thisname;
598 register char *lastname = NULL;
600 /* format each number followed by its string */
601 while ((thisname = *names++) != NULL)
603 /* get the number to format */
607 /* display only non-zero numbers */
610 /* write the previous name */
611 if (lastname != NULL)
613 display_write(-1, -1, 0, 0, lastname);
616 /* defer writing this name */
621 color = color_test(*cidx++, num);
624 /* is this number in kilobytes? */
625 if (thisname[0] == 'K')
627 display_write(x, y, color, 0, format_k(num));
632 display_write(x, y, color, 0, itoa((int)num));
635 /* next iteration will not start at x, y */
640 /* if the last string has a separator on the end, it has to be
642 if (lastname != NULL)
644 if ((num = strlen(lastname)) > 1 &&
645 lastname[num-2] == ',' && lastname[num-1] == ' ')
647 display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname);
651 display_write(-1, -1, 0, 1, lastname);
657 * int display_resize()
659 * Reallocate buffer space needed by the display package to accomodate
660 * a new screen size. Must be called whenever the screen's size has
661 * changed. Returns the number of lines available for displaying
662 * processes or -1 if there was a problem allocating space.
669 register int top_lines;
670 register int newsize;
672 /* calculate the current dimensions */
673 /* if operating in "dumb" mode, we only need one line */
674 top_lines = smart_terminal ? screen_length : 1;
676 /* we don't want more than MAX_COLS columns, since the machine-dependent
677 modules make static allocations based on MAX_COLS and we don't want
678 to run off the end of their buffers */
679 display_width = screen_width;
680 if (display_width >= MAX_COLS)
682 display_width = MAX_COLS - 1;
685 /* see how much space we need */
686 newsize = top_lines * (MAX_COLS + 1);
688 /* reallocate only if we need more than we already have */
689 if (newsize > bufsize)
691 /* deallocate any previous buffer that may have been there */
692 if (screenbuf != NULL)
696 if (colorbuf != NULL)
701 /* allocate space for the screen and color buffers */
703 screenbuf = (char *)calloc(bufsize, sizeof(char));
704 colorbuf = (char *)calloc(bufsize, sizeof(char));
705 if (screenbuf == NULL || colorbuf == NULL)
713 /* just clear them out */
714 memzero(screenbuf, bufsize);
715 memzero(colorbuf, bufsize);
718 /* adjust total lines on screen to lines available for procs */
719 top_lines -= y_procs;
721 /* return number of lines available */
722 /* for dumb terminals, pretend like we can show any amount */
723 return(smart_terminal ? top_lines : Largest);
730 return(smart_terminal ? screen_length : Largest);
737 return(display_width);
741 * int display_init(struct statics *statics)
743 * Initialize the display system based on information in the statics
744 * structure. Returns the number of lines available for displaying
745 * processes or -1 if there was an error.
749 display_init(struct statics *statics)
752 register int top_lines;
758 /* certain things may influence the screen layout,
759 so look at those first */
761 /* a kernel line shifts parts of the display down */
762 kernel_names = statics->kernel_names;
763 if ((num_kernel = string_count(kernel_names)) > 0)
765 /* adjust screen placements */
774 /* a swap line shifts parts of the display down one */
775 swap_names = statics->swap_names;
776 if ((num_swap = string_count(swap_names)) > 0)
778 /* adjust screen placements */
785 /* call resize to do the dirty work */
786 top_lines = display_resize();
788 /* only do the rest if we need to */
791 /* save pointers and allocate space for names */
792 procstate_names = statics->procstate_names;
793 num_procstates = string_count(procstate_names);
794 lprocstates = (int *)calloc(num_procstates, sizeof(int));
796 cpustate_names = statics->cpustate_names;
797 num_cpustates = string_count(cpustate_names);
798 lcpustates = (int *)calloc(num_cpustates, sizeof(int));
799 cpustate_columns = (int *)calloc(num_cpustates, sizeof(int));
800 memory_names = statics->memory_names;
801 num_memory = string_count(memory_names);
803 /* calculate starting columns where needed */
804 cpustate_total_length = 0;
806 ip = cpustate_columns;
809 *ip++ = cpustate_total_length;
810 if ((i = strlen(*pp++)) > 0)
812 cpustate_total_length += i + 8;
818 /* set up color tags for loadavg */
819 load_cidx[0] = color_tag("1min");
820 load_cidx[1] = color_tag("5min");
821 load_cidx[2] = color_tag("15min");
823 /* find header color */
824 header_cidx = color_tag("header");
826 /* color tags for cpu states */
827 cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int));
829 p = strcpyend(scratchbuf, "cpu.");
830 while (i < num_cpustates)
832 strcpy(p, cpustate_names[i]);
833 cpustate_cidx[i++] = color_tag(scratchbuf);
836 /* color tags for kernel */
839 kernel_cidx = (int *)malloc(num_kernel * sizeof(int));
841 p = strcpyend(scratchbuf, "kernel.");
842 while (i < num_kernel)
844 strcpy(p, homogenize(kernel_names[i]+1));
845 kernel_cidx[i++] = color_tag(scratchbuf);
849 /* color tags for memory */
850 memory_cidx = (int *)malloc(num_memory * sizeof(int));
852 p = strcpyend(scratchbuf, "memory.");
853 while (i < num_memory)
855 strcpy(p, homogenize(memory_names[i]+1));
856 memory_cidx[i++] = color_tag(scratchbuf);
859 /* color tags for swap */
862 swap_cidx = (int *)malloc(num_swap * sizeof(int));
864 p = strcpyend(scratchbuf, "swap.");
867 strcpy(p, homogenize(swap_names[i]+1));
868 swap_cidx[i++] = color_tag(scratchbuf);
873 /* return number of lines available (or error) */
878 pr_loadavg(double avg, int i)
884 color = color_test(load_cidx[i], (int)(avg * 100));
886 display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0,
887 avg < 10.0 ? " %5.2f" : " %5.1f", avg);
888 display_write(-1, -1, 0, 0, (i < 2 ? "," : ";"));
892 i_loadave(int mpid, double *avenrun)
897 /* mpid == -1 implies this system doesn't have an _mpid */
900 display_fmt(0, 0, 0, 0,
901 "last pid: %5d; load avg:", mpid);
902 x_loadave = X_LOADAVE;
906 display_write(0, 0, 0, 0, "load averages:");
907 x_loadave = X_LOADAVE - X_LASTPIDWIDTH;
909 for (i = 0; i < 3; i++)
911 pr_loadavg(avenrun[i], i);
918 u_loadave(int mpid, double *avenrun)
925 /* change screen only when value has really changed */
928 display_fmt(x_lastpid, y_lastpid, 0, 0,
934 /* display new load averages */
935 for (i = 0; i < 3; i++)
937 pr_loadavg(avenrun[i], i);
941 static char minibar_buffer[64];
942 #define MINIBAR_WIDTH 20
945 i_minibar(int (*formatter)(char *, int))
947 (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
949 display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
953 u_minibar(int (*formatter)(char *, int))
955 (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
957 display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
960 static int uptime_days;
961 static int uptime_hours;
962 static int uptime_mins;
963 static int uptime_secs;
966 i_uptime(time_t *bt, time_t *tod)
975 uptime_days = uptime / 86400;
977 uptime_hours = uptime / 3600;
979 uptime_mins = uptime / 60;
980 uptime_secs = uptime % 60;
983 * Display the uptime.
986 display_fmt(x_uptime, y_uptime, 0, 0,
987 " up %d+%02d:%02d:%02d",
988 uptime_days, uptime_hours, uptime_mins, uptime_secs);
993 u_uptime(time_t *bt, time_t *tod)
1001 i_timeofday(time_t *tod)
1005 * Display the current time.
1006 * "ctime" always returns a string that looks like this:
1008 * Sun Sep 16 01:03:52 1973
1009 * 012345678901234567890123
1012 * We want indices 11 thru 18 (length 8).
1017 /* where on the screen do we start? */
1018 x = (smart_terminal ? screen_width : 79) - 8;
1020 /* but don't bump in to uptime */
1021 if (x < x_uptime + 19)
1027 display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11]));
1030 static int ltotal = 0;
1031 static int lthreads = 0;
1034 * *_procstates(total, brkdn, names) - print the process summary line
1039 i_procstates(int total, int *brkdn, int threads)
1042 /* write current number of processes and remember the value */
1043 display_fmt(0, y_procstate, 0, 0,
1044 "%d %s: ", total, threads ? "threads" : "processes");
1047 /* remember where the summary starts */
1048 x_procstate = virt_x;
1052 /* format and print the process state summary */
1053 summary_format(-1, -1, brkdn, procstate_names, NULL);
1055 /* save the numbers for next time */
1056 memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
1062 u_procstates(int total, int *brkdn, int threads)
1065 /* if threads state has changed, do a full update */
1066 if (lthreads != threads)
1068 i_procstates(total, brkdn, threads);
1072 /* update number of processes only if it has changed */
1073 if (ltotal != total)
1075 display_fmt(0, y_procstate, 0, 0,
1078 /* if number of digits differs, rewrite the label */
1079 if (digits(total) != digits(ltotal))
1081 display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes");
1082 x_procstate = virt_x;
1085 /* save new total */
1089 /* see if any of the state numbers has changed */
1090 if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
1092 /* format and update the line */
1093 summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL);
1094 memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
1099 * *_cpustates(states, names) - print the cpu state percentages
1102 /* cpustates_tag() calculates the correct tag to use to label the line */
1110 static char *short_tag = "CPU: ";
1111 static char *long_tag = "CPU states: ";
1113 /* if length + strlen(long_tag) >= screen_width, then we have to
1114 use the shorter tag (we subtract 2 to account for ": ") */
1115 if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
1124 /* set x_cpustates accordingly then return result */
1125 x_cpustates = strlen(use);
1130 i_cpustates(int *states)
1139 int *cidx = cpustate_cidx;
1143 names = cpustate_names;
1144 colp = cpustate_columns;
1147 display_write(0, y_cpustates, 0, 0, cpustates_tag());
1149 /* now walk thru the names and print the line */
1150 while ((thisname = *names++) != NULL)
1152 if (*thisname != '\0')
1154 /* retrieve the value and remember it */
1158 /* determine color number to use */
1159 color = color_test(*cidx++, value/10);
1162 /* if percentage is >= 1000, print it as 100% */
1163 display_fmt(x_cpustates + *colp, y_cpustates,
1165 (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
1168 *names != NULL ? ", " : "");
1176 /* copy over values into "last" array */
1177 memcpy(lcpustates, states, num_cpustates * sizeof(int));
1181 u_cpustates(int *states)
1185 char **names = cpustate_names;
1191 int *cidx = cpustate_cidx;
1195 colp = cpustate_columns;
1197 /* we could be much more optimal about this */
1198 while ((thisname = *names++) != NULL)
1200 if (*thisname != '\0')
1202 /* did the value change since last time? */
1205 /* yes, change it */
1206 /* retrieve value and remember it */
1210 /* determine color number to use */
1211 color = color_test(*cidx, value/10);
1214 /* if percentage is >= 1000, print it as 100% */
1215 display_fmt(x_cpustates + *colp, y_cpustates, color, 0,
1216 (value >= 1000 ? "%4.0f" : "%4.1f"),
1217 ((double)value)/10.);
1219 /* remember it for next time */
1227 /* increment and move on */
1239 register char **names = cpustate_names;
1240 register char *thisname;
1244 display_write(0, y_cpustates, 0, 0, cpustates_tag());
1246 while ((thisname = *names++) != NULL)
1248 if (*thisname != '\0')
1250 display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ",
1255 /* fill the "last" array with all -1s, to insure correct updating */
1265 * *_kernel(stats) - print "Kernel: " followed by the kernel summary string
1267 * Assumptions: cursor is on "lastline", the previous line
1271 i_kernel(int *stats)
1276 display_write(0, y_kernel, 0, 0, "Kernel: ");
1278 /* format and print the kernel summary */
1279 summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
1284 u_kernel(int *stats)
1289 /* format the new line */
1290 summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
1295 * *_memory(stats) - print "Memory: " followed by the memory summary string
1297 * Assumptions: cursor is on "lastline", the previous line
1301 i_memory(long *stats)
1304 display_write(0, y_mem, 0, 0, "Memory: ");
1306 /* format and print the memory summary */
1307 summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
1311 u_memory(long *stats)
1314 /* format the new line */
1315 summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
1319 * *_swap(stats) - print "Swap: " followed by the swap summary string
1321 * Assumptions: cursor is on "lastline", the previous line
1323 * These functions only print something when num_swap > 0
1333 display_write(0, y_swap, 0, 0, "Swap: ");
1335 /* format and print the swap summary */
1336 summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
1346 /* format the new line */
1347 summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
1352 * *_message() - print the next pending message line, or erase the one
1355 * Note that u_message is (currently) the same as i_message.
1357 * Assumptions: lastline is consistent
1361 * i_message is funny because it gets its message asynchronously (with
1362 * respect to screen updates). Messages are taken out of the
1363 * circular message_buf and displayed one at a time.
1367 i_message(struct timeval *now)
1370 struct timeval my_now;
1373 dprintf("i_message(%08x)\n", now);
1375 /* if now is NULL we have to get it ourselves */
1382 /* now that we have been called, messages no longer need to be held */
1385 dprintf("i_message: now %d, message_time %d\n",
1386 now->tv_sec, message_time.tv_sec);
1390 /* is it time to change the message? */
1391 if (timercmp(now, &message_time, > ))
1393 /* yes, free the current message */
1394 dprintf("i_message: timer expired\n");
1395 if (message_current != NULL)
1397 free(message_current);
1398 message_current = NULL;
1401 /* is there a new message to be displayed? */
1402 if (message_first != message_last)
1404 /* move index to next message */
1405 if (++message_first == MAX_MESSAGES) message_first = 0;
1407 /* make the next message the current one */
1408 message_current = message_buf[message_first];
1411 dprintf("i_message: showing \"%s\"\n", message_current);
1412 display_move(0, y_message);
1413 screen_standout(message_current);
1414 i = strlen(message_current);
1416 /* set the expiration timer */
1417 message_time = *now;
1418 message_time.tv_sec += MESSAGE_DISPLAY_TIME;
1420 /* clear the rest of the line */
1421 screen_cleareol(message_length - i);
1427 /* just clear what was there before, if anything */
1428 if (message_length > 0)
1430 display_move(0, y_message);
1431 screen_cleareol(message_length);
1441 u_message(struct timeval *now)
1447 static int header_length;
1450 * *_header(text) - print the header for the process area
1452 * Assumptions: cursor is on the previous line and lastline is consistent
1456 i_header(char *text)
1459 int header_color = 0;
1462 header_color = color_test(header_cidx, 0);
1464 header_length = strlen(text);
1467 display_write(x_header, y_header, header_color, 1, text);
1473 u_header(char *text)
1476 int header_color = 0;
1479 header_color = color_test(header_cidx, 0);
1481 display_write(x_header, y_header, header_color, 1,
1482 header_status ? text : "");
1486 * *_process(line, thisline) - print one process line
1488 * Assumptions: lastline is consistent
1492 i_process(int line, char *thisline)
1495 /* truncate the line to conform to our current screen width */
1496 thisline[display_width] = '\0';
1498 /* write the line out */
1499 display_write(0, y_procs + line, 0, 1, thisline);
1503 u_process(int line, char *new_line)
1506 i_process(line, new_line);
1515 /* move the cursor to a pleasant place */
1516 display_move(x_idlecursor, y_idlecursor);
1520 /* separate this display from the next with some vertical room */
1521 fputs("\n\n", stdout);
1532 /* clear-to-end the display */
1535 /* move the cursor to a pleasant place */
1536 display_move(x_idlecursor, y_idlecursor);
1541 /* separate this display from the next with some vertical room */
1542 fputs("\n\n", stdout);
1547 display_header(int t)
1550 header_status = t != 0;
1557 message_barrier = Yes;
1564 message_time.tv_sec = 0;
1565 message_time.tv_usec = 0;
1572 message_first = message_last;
1573 message_time.tv_sec = 0;
1574 message_time.tv_usec = 0;
1578 * void new_message_v(char *msgfmt, va_list ap)
1580 * Display a message in the message area. This function takes a va_list for
1581 * the arguments. Safe to call before display_init. This function only
1582 * queues a message for display, and allowed for multiple messages to be
1583 * queued. The i_message function drains the queue and actually writes the
1584 * messages on the display.
1589 new_message_v(char *msgfmt, va_list ap)
1596 /* if message_barrier is active, remove all pending messages */
1597 if (message_barrier)
1600 message_barrier = No;
1603 /* first, format the message */
1604 (void) vsnprintf(msg, sizeof(msg), msgfmt, ap);
1606 /* where in the buffer will it go? */
1607 i = message_last + 1;
1608 if (i >= MAX_MESSAGES) i = 0;
1610 /* make sure the buffer is not full */
1611 if (i != message_first)
1613 /* insert it in to message_buf */
1614 message_buf[i] = strdup(msg);
1615 dprintf("new_message_v: new message inserted in slot %d\n", i);
1617 /* remember if the buffer is empty and set the index */
1618 empty = message_last == message_first;
1621 /* is message_buf otherwise empty and have we started displaying? */
1622 if (empty && !message_hold)
1624 /* we can display the message now */
1631 * void new_message(int type, char *msgfmt, ...)
1633 * Display a message in the message area. It is safe to call this function
1634 * before display_init. Messages logged before the display is drawn will be
1635 * held and displayed later.
1639 new_message(char *msgfmt, ...)
1644 va_start(ap, msgfmt);
1645 new_message_v(msgfmt, ap);
1650 * void message_error(char *msgfmt, ...)
1652 * Put an error message in the message area. It is safe to call this function
1653 * before display_init. Messages logged before the display is drawn will be
1654 * held and displayed later.
1658 message_error(char *msgfmt, ...)
1663 va_start(ap, msgfmt);
1664 new_message_v(msgfmt, ap);
1670 * void message_clear()
1672 * Clear message area and flush all pending messages.
1679 /* remove any existing message */
1680 if (message_current != NULL)
1682 display_move(0, y_message);
1683 screen_cleareol(message_length);
1684 free(message_current);
1685 message_current = 0;
1688 /* flush all pending messages */
1693 * void message_prompt_v(int so, char *msgfmt, va_list ap)
1695 * Place a prompt in the message area. A prompt is different from a
1696 * message as follows: it is displayed immediately, overwriting any
1697 * message that may already be there, it may be highlighted in standout
1698 * mode (if "so" is true), the cursor is left to rest at the end of the
1699 * prompt. This call causes all pending messages to be flushed.
1703 message_prompt_v(int so, char *msgfmt, va_list ap)
1709 /* clear out the message buffer */
1712 /* format the message */
1713 i = vsnprintf(msg, sizeof(msg), msgfmt, ap);
1715 /* this goes over any existing message */
1716 display_move(0, y_message);
1718 /* clear the entire line */
1719 screen_cleareol(message_length);
1721 /* show the prompt */
1724 screen_standout(msg);
1731 /* make it all visible */
1734 /* even though we dont keep a copy of the prompt, track its length */
1735 message_length = i < MAX_COLS ? i : MAX_COLS;
1739 * void message_prompt(char *msgfmt, ...)
1741 * Place a prompt in the message area (see message_prompt_v).
1745 message_prompt(char *msgfmt, ...)
1750 va_start(ap, msgfmt);
1751 message_prompt_v(Yes, msgfmt, ap);
1756 message_prompt_plain(char *msgfmt, ...)
1761 va_start(ap, msgfmt);
1762 message_prompt_v(No, msgfmt, ap);
1767 * int readline(char *buffer, int size, int numeric)
1769 * Read a line of input from the terminal. The line is placed in
1770 * "buffer" not to exceed "size". If "numeric" is true then the input
1771 * can only consist of digits. This routine handles all character
1772 * editing while keeping the terminal in cbreak mode. If "numeric"
1773 * is true then the number entered is returned. Otherwise the number
1774 * of character read in to "buffer" is returned.
1778 readline(char *buffer, int size, int numeric)
1781 register char *ptr = buffer;
1783 register char cnt = 0;
1785 /* allow room for null terminator */
1789 while ((fflush(stdout), read(0, ptr, 1) > 0))
1791 /* newline or return means we are done */
1792 if ((ch = *ptr) == '\n' || ch == '\r')
1797 /* handle special editing characters */
1800 /* return null string */
1805 else if (ch == ch_werase)
1807 /* erase previous word */
1810 /* none to erase! */
1816 * First: remove all spaces till the first-non-space
1817 * Second: remove all non-spaces till the first-space
1819 while(cnt > 0 && ptr[-1] == ' ')
1821 fputs("\b \b", stdout);
1825 while(cnt > 0 && ptr[-1] != ' ')
1827 fputs("\b \b", stdout);
1833 else if (ch == ch_erase)
1835 /* erase previous character */
1838 /* none to erase! */
1843 fputs("\b \b", stdout);
1848 /* check for character validity and buffer overflow */
1849 else if (cnt == size || (numeric && !isdigit((int)ch)) ||
1857 /* echo it and store it in the buffer */
1864 /* all done -- null terminate the string */
1867 /* add response length to message_length */
1868 message_length += cnt;
1870 /* return either inputted number or string length */
1872 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
1876 display_pagerstart()
1888 screen_standout("Hit any key to continue: ");
1890 (void) read(0, &ch, 1);
1894 display_pager(char *fmt, ...)
1901 char buffer[MAX_COLS];
1904 /* format into buffer */
1906 (void) vsnprintf(buffer, MAX_COLS, fmt, ap);
1910 while ((ch = *data++) != '\0')
1915 if (++curr_y >= screen_length - 1)
1917 screen_standout("...More...");
1919 (void) read(0, &readch, 1);