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 (y
< (smart_terminal
? screen_length
: Largest
) && x
< display_width
&&
388 (ch
!= *bufp
|| newcolor
!= *colorp
))
391 if (y
!= curr_y
|| x
!= curr_x
)
393 /* have to move the cursor */
397 /* write character */
399 if (curr_color
!= newcolor
)
401 fputs(color_setstr(newcolor
), stdout
);
402 curr_color
= newcolor
;
407 *colorp
= curr_color
;
419 if (eol
&& *bufp
!= '\0')
421 dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp
);
422 /* make sure we are color 0 */
426 fputs(color_setstr(0), stdout
);
431 /* make sure we are at the end */
432 if (x
!= curr_x
|| y
!= curr_y
)
440 screen_cleareol(strlen(bufp
));
442 /* clear out whats left of this line's buffer */
443 diff
= display_width
- x
;
447 memzero(colorp
, diff
);
453 display_fmt(int x
, int y
, int newcolor
, int eol
, char *fmt
, ...)
460 vsnprintf(scratchbuf
, MAX_COLS
, fmt
, argp
);
461 display_write(x
, y
, newcolor
, eol
, scratchbuf
);
473 /* is there anything out there that needs to be cleared? */
474 p
= &screenbuf
[lineindex(virt_y
) + virt_x
];
481 /* this line is clear, what about the rest? */
483 while (++y
< screen_length
)
485 if (screenbuf
[lineindex(y
)] != '\0')
495 dprintf("display_cte: clearing\n");
497 /* we will need this later */
498 len
= lineindex(virt_y
) + virt_x
;
500 /* move to x and y, then clear to end */
501 display_move(virt_x
, virt_y
);
504 /* screen has no clear to end, so do it by hand */
509 screen_cleareol(len
);
511 while (++virt_y
< screen_length
)
513 display_move(0, virt_y
);
514 p
= &screenbuf
[lineindex(virt_y
)];
518 screen_cleareol(len
);
523 /* clear the screenbuf */
524 memzero(&screenbuf
[len
], bufsize
- len
);
525 memzero(&colorbuf
[len
], bufsize
- len
);
530 summary_format(int x
, int y
, int *numbers
, char **names
, int *cidx
)
534 register char *thisname
;
535 register char *lastname
= NULL
;
538 /* format each number followed by its string */
539 while ((thisname
= *names
++) != NULL
)
541 /* get the number to format */
545 /* display only non-zero numbers */
548 /* write the previous name */
549 if (lastname
!= NULL
)
551 display_write(-1, -1, 0, 0, lastname
);
558 color
= color_test(*cidx
++, num
);
562 /* write this number if positive */
565 display_write(x
, y
, color
, 0, itoa(num
));
568 /* defer writing this name */
571 /* next iteration will not start at x, y */
576 /* if the last string has a separator on the end, it has to be
578 if (lastname
!= NULL
)
580 if ((num
= strlen(lastname
)) > 1 &&
581 lastname
[num
-2] == ',' && lastname
[num
-1] == ' ')
583 display_fmt(-1, -1, 0, 1, "%.*s", num
-2, lastname
);
587 display_write(-1, -1, 0, 1, lastname
);
593 summary_format_memory(int x
, int y
, long *numbers
, char **names
, int *cidx
)
598 register char *thisname
;
599 register char *lastname
= NULL
;
601 /* format each number followed by its string */
602 while ((thisname
= *names
++) != NULL
)
604 /* get the number to format */
608 /* display only non-zero numbers */
611 /* write the previous name */
612 if (lastname
!= NULL
)
614 display_write(-1, -1, 0, 0, lastname
);
617 /* defer writing this name */
622 color
= color_test(*cidx
++, num
);
625 /* is this number in kilobytes? */
626 if (thisname
[0] == 'K')
628 display_write(x
, y
, color
, 0, format_k(num
));
633 display_write(x
, y
, color
, 0, itoa((int)num
));
636 /* next iteration will not start at x, y */
641 /* if the last string has a separator on the end, it has to be
643 if (lastname
!= NULL
)
645 if ((num
= strlen(lastname
)) > 1 &&
646 lastname
[num
-2] == ',' && lastname
[num
-1] == ' ')
648 display_fmt(-1, -1, 0, 1, "%.*s", num
-2, lastname
);
652 display_write(-1, -1, 0, 1, lastname
);
658 * int display_resize()
660 * Reallocate buffer space needed by the display package to accomodate
661 * a new screen size. Must be called whenever the screen's size has
662 * changed. Returns the number of lines available for displaying
663 * processes or -1 if there was a problem allocating space.
670 register int top_lines
;
671 register int newsize
;
673 /* calculate the current dimensions */
674 /* if operating in "dumb" mode, we only need one line */
675 top_lines
= smart_terminal
? screen_length
: 1;
677 /* we don't want more than MAX_COLS columns, since the machine-dependent
678 modules make static allocations based on MAX_COLS and we don't want
679 to run off the end of their buffers */
680 display_width
= screen_width
;
681 if (display_width
>= MAX_COLS
)
683 display_width
= MAX_COLS
- 1;
686 /* see how much space we need */
687 newsize
= top_lines
* (MAX_COLS
+ 1);
689 /* reallocate only if we need more than we already have */
690 if (newsize
> bufsize
)
692 /* deallocate any previous buffer that may have been there */
693 if (screenbuf
!= NULL
)
697 if (colorbuf
!= NULL
)
702 /* allocate space for the screen and color buffers */
704 screenbuf
= (char *)calloc(bufsize
, sizeof(char));
705 colorbuf
= (char *)calloc(bufsize
, sizeof(char));
706 if (screenbuf
== NULL
|| colorbuf
== NULL
)
714 /* just clear them out */
715 memzero(screenbuf
, bufsize
);
716 memzero(colorbuf
, bufsize
);
719 /* adjust total lines on screen to lines available for procs */
720 if (top_lines
> y_procs
)
721 top_lines
-= y_procs
;
725 /* return number of lines available */
726 /* for dumb terminals, pretend like we can show any amount */
727 return(smart_terminal
? top_lines
: Largest
);
734 return(smart_terminal
? screen_length
: Largest
);
741 return(display_width
);
745 * int display_init(struct statics *statics)
747 * Initialize the display system based on information in the statics
748 * structure. Returns the number of lines available for displaying
749 * processes or -1 if there was an error.
753 display_init(struct statics
*statics
)
756 register int top_lines
;
762 /* certain things may influence the screen layout,
763 so look at those first */
765 /* More than one core will shif the parts of the display down */
766 if (enable_ncpus
!= 0 && n_cpus
> 1)
768 /* adjust screen placements */
769 y_mem
= y_mem
+ n_cpus
-1;
770 y_swap
= y_swap
+ n_cpus
-1;
771 y_message
= y_message
+ n_cpus
-1;
772 y_header
= y_header
+ n_cpus
-1;
773 y_idlecursor
= y_idlecursor
+ n_cpus
-1;
774 y_procs
= y_procs
+ n_cpus
-1;
777 /* a kernel line shifts parts of the display down */
778 kernel_names
= statics
->kernel_names
;
779 if ((num_kernel
= string_count(kernel_names
)) > 0)
781 /* adjust screen placements */
790 /* a swap line shifts parts of the display down one */
791 swap_names
= statics
->swap_names
;
792 if ((num_swap
= string_count(swap_names
)) > 0)
794 /* adjust screen placements */
801 /* call resize to do the dirty work */
802 top_lines
= display_resize();
805 * save pointers and allocate space for names. Even if top_lines <= -1
806 * the code will dereference many of these pointers and arrays.
808 procstate_names
= statics
->procstate_names
;
809 num_procstates
= string_count(procstate_names
);
811 lprocstates
= (int *)calloc(num_procstates
, sizeof(int));
813 cpustate_names
= statics
->cpustate_names
;
814 num_cpustates
= string_count(cpustate_names
);
815 lcpustates
= (int *)calloc(num_cpustates
, sizeof(int));
816 cpustate_columns
= (int *)calloc(num_cpustates
, sizeof(int));
817 memory_names
= statics
->memory_names
;
818 num_memory
= string_count(memory_names
);
820 /* calculate starting columns where needed */
821 cpustate_total_length
= 0;
823 ip
= cpustate_columns
;
826 *ip
++ = cpustate_total_length
;
827 if ((i
= strlen(*pp
++)) > 0)
829 cpustate_total_length
+= i
+ 8;
834 /* set up color tags for loadavg */
835 load_cidx
[0] = color_tag("1min");
836 load_cidx
[1] = color_tag("5min");
837 load_cidx
[2] = color_tag("15min");
839 /* find header color */
840 header_cidx
= color_tag("header");
842 /* color tags for cpu states */
843 cpustate_cidx
= (int *)malloc(num_cpustates
* sizeof(int));
845 p
= strcpyend(scratchbuf
, "cpu.");
846 while (i
< num_cpustates
)
848 strcpy(p
, cpustate_names
[i
]);
849 cpustate_cidx
[i
++] = color_tag(scratchbuf
);
852 /* color tags for kernel */
855 kernel_cidx
= (int *)malloc(num_kernel
* sizeof(int));
857 p
= strcpyend(scratchbuf
, "kernel.");
858 while (i
< num_kernel
)
860 strcpy(p
, homogenize(kernel_names
[i
]+1));
861 kernel_cidx
[i
++] = color_tag(scratchbuf
);
865 /* color tags for memory */
866 memory_cidx
= (int *)malloc(num_memory
* sizeof(int));
868 p
= strcpyend(scratchbuf
, "memory.");
869 while (i
< num_memory
)
871 strcpy(p
, homogenize(memory_names
[i
]+1));
872 memory_cidx
[i
++] = color_tag(scratchbuf
);
875 /* color tags for swap */
878 swap_cidx
= (int *)malloc(num_swap
* sizeof(int));
880 p
= strcpyend(scratchbuf
, "swap.");
883 strcpy(p
, homogenize(swap_names
[i
]+1));
884 swap_cidx
[i
++] = color_tag(scratchbuf
);
889 /* return number of lines available (or error) */
894 pr_loadavg(double avg
, int i
)
900 color
= color_test(load_cidx
[i
], (int)(avg
* 100));
902 display_fmt(x_loadave
+ X_LOADAVEWIDTH
* i
, y_loadave
, color
, 0,
903 avg
< 10.0 ? " %5.2f" : " %5.1f", avg
);
904 display_write(-1, -1, 0, 0, (i
< 2 ? "," : ";"));
908 i_loadave(int mpid
, double *avenrun
)
913 /* mpid == -1 implies this system doesn't have an _mpid */
916 display_fmt(0, 0, 0, 0,
917 "last pid: %5d; load avg:", mpid
);
918 x_loadave
= X_LOADAVE
;
922 display_write(0, 0, 0, 0, "load averages:");
923 x_loadave
= X_LOADAVE
- X_LASTPIDWIDTH
;
925 for (i
= 0; i
< 3; i
++)
927 pr_loadavg(avenrun
[i
], i
);
934 u_loadave(int mpid
, double *avenrun
)
941 /* change screen only when value has really changed */
944 display_fmt(x_lastpid
, y_lastpid
, 0, 0,
950 /* display new load averages */
951 for (i
= 0; i
< 3; i
++)
953 pr_loadavg(avenrun
[i
], i
);
957 static char minibar_buffer
[64];
958 #define MINIBAR_WIDTH 20
961 i_minibar(int (*formatter
)(char *, int))
963 (void)((*formatter
)(minibar_buffer
, MINIBAR_WIDTH
));
965 display_write(x_minibar
, y_minibar
, 0, 0, minibar_buffer
);
969 u_minibar(int (*formatter
)(char *, int))
971 (void)((*formatter
)(minibar_buffer
, MINIBAR_WIDTH
));
973 display_write(x_minibar
, y_minibar
, 0, 0, minibar_buffer
);
976 static int uptime_days
;
977 static int uptime_hours
;
978 static int uptime_mins
;
979 static int uptime_secs
;
982 i_uptime(time_t *bt
, time_t *tod
)
991 uptime_days
= uptime
/ 86400;
993 uptime_hours
= uptime
/ 3600;
995 uptime_mins
= uptime
/ 60;
996 uptime_secs
= uptime
% 60;
999 * Display the uptime.
1002 display_fmt(x_uptime
, y_uptime
, 0, 0,
1003 " up %d+%02d:%02d:%02d",
1004 uptime_days
, uptime_hours
, uptime_mins
, uptime_secs
);
1009 u_uptime(time_t *bt
, time_t *tod
)
1017 i_timeofday(time_t *tod
)
1021 * Display the current time.
1022 * "ctime" always returns a string that looks like this:
1024 * Sun Sep 16 01:03:52 1973
1025 * 012345678901234567890123
1028 * We want indices 11 thru 18 (length 8).
1033 /* where on the screen do we start? */
1034 x
= (smart_terminal
? screen_width
: 79) - 8;
1036 /* but don't bump in to uptime */
1037 if (x
< x_uptime
+ 19)
1043 display_fmt(x
, 0, 0, 1, "%-8.8s", &(ctime(tod
)[11]));
1046 static int ltotal
= 0;
1047 static int lthreads
= 0;
1050 * *_procstates(total, brkdn, names) - print the process summary line
1055 i_procstates(int total
, int *brkdn
, int threads
)
1058 /* write current number of processes and remember the value */
1059 display_fmt(0, y_procstate
, 0, 0,
1060 "%d %s: ", total
, threads
? "threads" : "processes");
1063 /* remember where the summary starts */
1064 x_procstate
= virt_x
;
1068 /* format and print the process state summary */
1069 summary_format(-1, -1, brkdn
, procstate_names
, NULL
);
1071 /* save the numbers for next time */
1072 memcpy(lprocstates
, brkdn
, num_procstates
* sizeof(int));
1078 u_procstates(int total
, int *brkdn
, int threads
)
1081 /* if threads state has changed, do a full update */
1082 if (lthreads
!= threads
)
1084 i_procstates(total
, brkdn
, threads
);
1088 /* update number of processes only if it has changed */
1089 if (ltotal
!= total
)
1091 display_fmt(0, y_procstate
, 0, 0,
1094 /* if number of digits differs, rewrite the label */
1095 if (digits(total
) != digits(ltotal
))
1097 display_fmt(-1, -1, 0, 0, " %s: ", threads
? "threads" : "processes");
1098 x_procstate
= virt_x
;
1101 /* save new total */
1105 /* see if any of the state numbers has changed */
1106 if (total
> 0 && memcmp(lprocstates
, brkdn
, num_procstates
* sizeof(int)) != 0)
1108 /* format and update the line */
1109 summary_format(x_procstate
, y_procstate
, brkdn
, procstate_names
, NULL
);
1110 memcpy(lprocstates
, brkdn
, num_procstates
* sizeof(int));
1115 * *_cpustates(states, names) - print the cpu state percentages
1118 /* cpustates_tag() calculates the correct tag to use to label the line */
1126 static char *short_tag
= "CPU: ";
1127 static char *long_tag
= "CPU states: ";
1129 /* if length + strlen(long_tag) >= screen_width, then we have to
1130 use the shorter tag (we subtract 2 to account for ": ") */
1131 if (cpustate_total_length
+ (int)strlen(long_tag
) - 2 >= screen_width
)
1140 /* set x_cpustates accordingly then return result */
1141 x_cpustates
= strlen(use
);
1146 i_cpustates(int *states
)
1156 int *cidx
= cpustate_cidx
;
1160 names
= cpustate_names
;
1161 colp
= cpustate_columns
;
1164 if (enable_ncpus
!=0 && n_cpus
> 1) {
1165 for (cpu
= 0; cpu
< n_cpus
; ++cpu
) {
1166 int y_pos
= y_cpustates
;
1167 y_pos
= y_pos
+ cpu
;
1168 colp
= cpustate_columns
;
1169 names
= cpustate_names
;
1170 display_write(0, y_cpustates
+cpu
, 0, 0, cpustates_tag());
1172 /* now walk thru the names and print the line */
1173 while ((thisname
= *names
++) != NULL
) {
1174 if (*thisname
!= '\0') {
1175 /* retrieve the value and remember it */
1179 /* determine color number to use */
1180 color
= color_test(*cidx
++, value
/10);
1182 /* if percentage is >= 1000, print it as 100% */
1183 display_fmt(x_cpustates
+ *colp
, y_pos
,
1185 (value
>= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
1188 *names
!= NULL
? ", " : "");
1195 /* copy over values into "last" array */
1196 memcpy(lcpustates
, states
, num_cpustates
* sizeof(int));
1199 display_write(0, y_cpustates
, 0, 0, cpustates_tag());
1201 /* now walk thru the names and print the line */
1202 while ((thisname
= *names
++) != NULL
)
1204 if (*thisname
!= '\0')
1206 /* retrieve the 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
,
1217 (value
>= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
1220 *names
!= NULL
? ", " : "");
1228 /* copy over values into "last" array */
1229 memcpy(lcpustates
, states
, num_cpustates
* sizeof(int));
1235 u_cpustates(int *states
)
1239 char **names
= cpustate_names
;
1246 int *cidx
= cpustate_cidx
;
1250 if (enable_ncpus
!= 0 && n_cpus
> 1 ) {
1251 for (cpu
= 0; cpu
< n_cpus
; ++cpu
) {
1253 int y_pos
= y_cpustates
;
1254 y_pos
= y_pos
+ cpu
;
1255 colp
= cpustate_columns
;
1256 char **names
= cpustate_names
;
1257 /* we could be much more optimal about this */
1258 while ((thisname
= *names
++) != NULL
) {
1259 if (*thisname
!= '\0') {
1260 /* yes, change it */
1261 /* retrieve value and remember it */
1265 /* determine color number to use */
1266 color
= color_test(*cidx
, value
/10);
1268 /* if percentage is >= 1000, print it as 100% */
1269 display_fmt(x_cpustates
+ *colp
, y_pos
, color
, 0,
1270 (value
>= 1000 ? "%4.0f" : "%4.1f"),
1271 ((double)value
)/10.);
1277 /* increment and move on */
1285 colp
= cpustate_columns
;
1287 /* we could be much more optimal about this */
1288 while ((thisname
= *names
++) != NULL
)
1290 if (*thisname
!= '\0')
1292 /* did the value change since last time? */
1295 /* yes, change it */
1296 /* retrieve value and remember it */
1300 /* determine color number to use */
1301 color
= color_test(*cidx
, value
/10);
1304 /* if percentage is >= 1000, print it as 100% */
1305 display_fmt(x_cpustates
+ *colp
, y_cpustates
, color
, 0,
1306 (value
>= 1000 ? "%4.0f" : "%4.1f"),
1307 ((double)value
)/10.);
1309 /* remember it for next time */
1317 /* increment and move on */
1330 register char **names
= cpustate_names
;
1331 register char *thisname
;
1336 if (enable_ncpus
!= 0 && n_cpus
> 1) {
1337 for (cpu
= 0; cpu
< n_cpus
; ++cpu
) {
1338 display_write(0, y_cpustates
+ cpu
, 0, 0, cpustates_tag());
1339 char **names
= cpustate_names
;
1341 while ((thisname
= *names
++) != NULL
) {
1342 if (*thisname
!= '\0') {
1343 display_fmt(-1, -1, 0, 0, "%s %% %s", i
++ == 0 ? "" : ", ",
1347 /* fill the "last" array with all -1s, to insure correct updating */
1355 display_write(0, y_cpustates
, 0, 0, cpustates_tag());
1357 while ((thisname
= *names
++) != NULL
)
1359 if (*thisname
!= '\0')
1361 display_fmt(-1, -1, 0, 0, "%s %% %s", i
++ == 0 ? "" : ", ",
1366 /* fill the "last" array with all -1s, to insure correct updating */
1377 * *_kernel(stats) - print "Kernel: " followed by the kernel summary string
1379 * Assumptions: cursor is on "lastline", the previous line
1383 i_kernel(int *stats
)
1388 display_write(0, y_kernel
, 0, 0, "Kernel: ");
1390 /* format and print the kernel summary */
1391 summary_format(x_kernel
, y_kernel
, stats
, kernel_names
, kernel_cidx
);
1396 u_kernel(int *stats
)
1401 /* format the new line */
1402 summary_format(x_kernel
, y_kernel
, stats
, kernel_names
, kernel_cidx
);
1407 * *_memory(stats) - print "Memory: " followed by the memory summary string
1409 * Assumptions: cursor is on "lastline", the previous line
1413 i_memory(long *stats
)
1416 display_write(0, y_mem
, 0, 0, "Memory: ");
1418 /* format and print the memory summary */
1419 summary_format_memory(x_mem
, y_mem
, stats
, memory_names
, memory_cidx
);
1423 u_memory(long *stats
)
1426 /* format the new line */
1427 summary_format_memory(x_mem
, y_mem
, stats
, memory_names
, memory_cidx
);
1431 * *_swap(stats) - print "Swap: " followed by the swap summary string
1433 * Assumptions: cursor is on "lastline", the previous line
1435 * These functions only print something when num_swap > 0
1445 display_write(0, y_swap
, 0, 0, "Swap: ");
1447 /* format and print the swap summary */
1448 summary_format_memory(x_swap
, y_swap
, stats
, swap_names
, swap_cidx
);
1458 /* format the new line */
1459 summary_format_memory(x_swap
, y_swap
, stats
, swap_names
, swap_cidx
);
1464 * *_message() - print the next pending message line, or erase the one
1467 * Note that u_message is (currently) the same as i_message.
1469 * Assumptions: lastline is consistent
1473 * i_message is funny because it gets its message asynchronously (with
1474 * respect to screen updates). Messages are taken out of the
1475 * circular message_buf and displayed one at a time.
1479 i_message(struct timeval
*now
)
1482 struct timeval my_now
;
1485 dprintf("i_message(%08x)\n", now
);
1487 /* if now is NULL we have to get it ourselves */
1494 /* now that we have been called, messages no longer need to be held */
1497 dprintf("i_message: now %d, message_time %d\n",
1498 now
->tv_sec
, message_time
.tv_sec
);
1502 /* is it time to change the message? */
1503 if (timercmp(now
, &message_time
, > ))
1505 /* yes, free the current message */
1506 dprintf("i_message: timer expired\n");
1507 if (message_current
!= NULL
)
1509 free(message_current
);
1510 message_current
= NULL
;
1513 /* is there a new message to be displayed? */
1514 if (message_first
!= message_last
)
1516 /* move index to next message */
1517 if (++message_first
== MAX_MESSAGES
) message_first
= 0;
1519 /* make the next message the current one */
1520 message_current
= message_buf
[message_first
];
1523 dprintf("i_message: showing \"%s\"\n", message_current
);
1524 display_move(0, y_message
);
1525 screen_standout(message_current
);
1526 i
= strlen(message_current
);
1528 /* set the expiration timer */
1529 message_time
= *now
;
1530 message_time
.tv_sec
+= MESSAGE_DISPLAY_TIME
;
1532 /* clear the rest of the line */
1533 screen_cleareol(message_length
- i
);
1539 /* just clear what was there before, if anything */
1540 if (message_length
> 0)
1542 display_move(0, y_message
);
1543 screen_cleareol(message_length
);
1553 u_message(struct timeval
*now
)
1559 static int header_length
;
1562 * *_header(text) - print the header for the process area
1564 * Assumptions: cursor is on the previous line and lastline is consistent
1568 i_header(char *text
)
1571 int header_color
= 0;
1574 header_color
= color_test(header_cidx
, 0);
1576 header_length
= strlen(text
);
1579 display_write(x_header
, y_header
, header_color
, 1, text
);
1585 u_header(char *text
)
1588 int header_color
= 0;
1591 header_color
= color_test(header_cidx
, 0);
1593 display_write(x_header
, y_header
, header_color
, 1,
1594 header_status
? text
: "");
1598 * *_process(line, thisline) - print one process line
1600 * Assumptions: lastline is consistent
1604 i_process(int line
, char *thisline
)
1607 /* truncate the line to conform to our current screen width */
1608 thisline
[display_width
] = '\0';
1610 /* write the line out */
1611 display_write(0, y_procs
+ line
, 0, 1, thisline
);
1615 u_process(int line
, char *new_line
)
1618 i_process(line
, new_line
);
1627 /* move the cursor to a pleasant place */
1628 display_move(x_idlecursor
, y_idlecursor
);
1632 /* separate this display from the next with some vertical room */
1633 fputs("\n\n", stdout
);
1644 /* clear-to-end the display */
1647 /* move the cursor to a pleasant place */
1648 display_move(x_idlecursor
, y_idlecursor
);
1653 /* separate this display from the next with some vertical room */
1654 fputs("\n\n", stdout
);
1659 display_header(int t
)
1662 header_status
= t
!= 0;
1669 message_barrier
= Yes
;
1676 message_time
.tv_sec
= 0;
1677 message_time
.tv_usec
= 0;
1684 message_first
= message_last
;
1685 message_time
.tv_sec
= 0;
1686 message_time
.tv_usec
= 0;
1690 * void new_message_v(char *msgfmt, va_list ap)
1692 * Display a message in the message area. This function takes a va_list for
1693 * the arguments. Safe to call before display_init. This function only
1694 * queues a message for display, and allowed for multiple messages to be
1695 * queued. The i_message function drains the queue and actually writes the
1696 * messages on the display.
1701 new_message_v(char *msgfmt
, va_list ap
)
1708 /* if message_barrier is active, remove all pending messages */
1709 if (message_barrier
)
1712 message_barrier
= No
;
1715 /* first, format the message */
1716 (void) vsnprintf(msg
, sizeof(msg
), msgfmt
, ap
);
1718 /* where in the buffer will it go? */
1719 i
= message_last
+ 1;
1720 if (i
>= MAX_MESSAGES
) i
= 0;
1722 /* make sure the buffer is not full */
1723 if (i
!= message_first
)
1725 /* insert it in to message_buf */
1726 message_buf
[i
] = strdup(msg
);
1727 dprintf("new_message_v: new message inserted in slot %d\n", i
);
1729 /* remember if the buffer is empty and set the index */
1730 empty
= message_last
== message_first
;
1733 /* is message_buf otherwise empty and have we started displaying? */
1734 if (empty
&& !message_hold
)
1736 /* we can display the message now */
1743 * void new_message(int type, char *msgfmt, ...)
1745 * Display a message in the message area. It is safe to call this function
1746 * before display_init. Messages logged before the display is drawn will be
1747 * held and displayed later.
1751 new_message(char *msgfmt
, ...)
1756 va_start(ap
, msgfmt
);
1757 new_message_v(msgfmt
, ap
);
1762 * void message_error(char *msgfmt, ...)
1764 * Put an error message in the message area. It is safe to call this function
1765 * before display_init. Messages logged before the display is drawn will be
1766 * held and displayed later.
1770 message_error(char *msgfmt
, ...)
1775 va_start(ap
, msgfmt
);
1776 new_message_v(msgfmt
, ap
);
1782 * void message_clear()
1784 * Clear message area and flush all pending messages.
1791 /* remove any existing message */
1792 if (message_current
!= NULL
)
1794 display_move(0, y_message
);
1795 screen_cleareol(message_length
);
1796 free(message_current
);
1797 message_current
= 0;
1800 /* flush all pending messages */
1805 * void message_prompt_v(int so, char *msgfmt, va_list ap)
1807 * Place a prompt in the message area. A prompt is different from a
1808 * message as follows: it is displayed immediately, overwriting any
1809 * message that may already be there, it may be highlighted in standout
1810 * mode (if "so" is true), the cursor is left to rest at the end of the
1811 * prompt. This call causes all pending messages to be flushed.
1815 message_prompt_v(int so
, char *msgfmt
, va_list ap
)
1821 /* clear out the message buffer */
1824 /* format the message */
1825 i
= vsnprintf(msg
, sizeof(msg
), msgfmt
, ap
);
1827 /* this goes over any existing message */
1828 display_move(0, y_message
);
1830 /* clear the entire line */
1831 screen_cleareol(message_length
);
1833 /* show the prompt */
1836 screen_standout(msg
);
1843 /* make it all visible */
1846 /* even though we dont keep a copy of the prompt, track its length */
1847 message_length
= i
< MAX_COLS
? i
: MAX_COLS
;
1851 * void message_prompt(char *msgfmt, ...)
1853 * Place a prompt in the message area (see message_prompt_v).
1857 message_prompt(char *msgfmt
, ...)
1862 va_start(ap
, msgfmt
);
1863 message_prompt_v(Yes
, msgfmt
, ap
);
1868 message_prompt_plain(char *msgfmt
, ...)
1873 va_start(ap
, msgfmt
);
1874 message_prompt_v(No
, msgfmt
, ap
);
1879 * int readline(char *buffer, int size, int numeric)
1881 * Read a line of input from the terminal. The line is placed in
1882 * "buffer" not to exceed "size". If "numeric" is true then the input
1883 * can only consist of digits. This routine handles all character
1884 * editing while keeping the terminal in cbreak mode. If "numeric"
1885 * is true then the number entered is returned. Otherwise the number
1886 * of character read in to "buffer" is returned.
1890 readline(char *buffer
, int size
, int numeric
)
1893 register char *ptr
= buffer
;
1895 register char cnt
= 0;
1897 /* allow room for null terminator */
1901 while ((fflush(stdout
), read(0, ptr
, 1) > 0))
1903 /* newline or return means we are done */
1904 if ((ch
= *ptr
) == '\n' || ch
== '\r')
1909 /* handle special editing characters */
1912 /* return null string */
1917 else if (ch
== ch_werase
)
1919 /* erase previous word */
1922 /* none to erase! */
1928 * First: remove all spaces till the first-non-space
1929 * Second: remove all non-spaces till the first-space
1931 while(cnt
> 0 && ptr
[-1] == ' ')
1933 fputs("\b \b", stdout
);
1937 while(cnt
> 0 && ptr
[-1] != ' ')
1939 fputs("\b \b", stdout
);
1945 else if (ch
== ch_erase
)
1947 /* erase previous character */
1950 /* none to erase! */
1955 fputs("\b \b", stdout
);
1960 /* check for character validity and buffer overflow */
1961 else if (cnt
== size
|| (numeric
&& !isdigit((int)ch
)) ||
1969 /* echo it and store it in the buffer */
1976 /* all done -- null terminate the string */
1979 /* add response length to message_length */
1980 message_length
+= cnt
;
1982 /* return either inputted number or string length */
1984 return(cnt
== 0 ? -1 : numeric
? atoi(buffer
) : cnt
);
1988 display_pagerstart()
2000 screen_standout("Hit any key to continue: ");
2002 (void) read(0, &ch
, 1);
2006 display_pager(char *fmt
, ...)
2013 char buffer
[MAX_COLS
];
2016 /* format into buffer */
2018 (void) vsnprintf(buffer
, MAX_COLS
, fmt
, ap
);
2022 while ((ch
= *data
++) != '\0')
2027 if (++curr_y
>= screen_length
- 1)
2029 screen_standout("...More...");
2031 (void) read(0, &readch
, 1);