summaryrefslogtreecommitdiffstats
path: root/display.c
diff options
context:
space:
mode:
authorWilliam LeFebvre <bill@lefebvre.org>2007-04-20 06:31:10 -0700
committerWilliam LeFebvre <bill@lefebvre.org>2007-04-20 06:31:10 -0700
commitbc66647b3bf3fc5a270dac14f491c1b4eafbb90f (patch)
treea27b914b2ae86604fb8491767e02db6fc2f5ec6d /display.c
[svn] Create release 3.6.1
Diffstat (limited to 'display.c')
-rw-r--r--display.c1532
1 files changed, 1532 insertions, 0 deletions
diff --git a/display.c b/display.c
new file mode 100644
index 0000000..4081061
--- /dev/null
+++ b/display.c
@@ -0,0 +1,1532 @@
+/*
+ * Top users/processes display for Unix
+ * Version 3
+ *
+ * This program may be freely redistributed,
+ * but this entire comment MUST remain intact.
+ *
+ * Copyright (c) 1984, 1989, William LeFebvre, Rice University
+ * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
+ */
+
+/*
+ * This file contains the routines that display information on the screen.
+ * Each section of the screen has two routines: one for initially writing
+ * all constant and dynamic text, and one for only updating the text that
+ * changes. The prefix "i_" is used on all the "initial" routines and the
+ * prefix "u_" is used for all the "updating" routines.
+ *
+ * ASSUMPTIONS:
+ * None of the "i_" routines use any of the termcap capabilities.
+ * In this way, those routines can be safely used on terminals that
+ * have minimal (or nonexistant) terminal capabilities.
+ *
+ * The routines are called in this order: *_loadave, i_timeofday,
+ * *_procstates, *_cpustates, *_memory, *_message, *_header,
+ * *_process, u_endscreen.
+ */
+
+#include "os.h"
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "top.h"
+#include "machine.h"
+#include "screen.h" /* interface to screen package */
+#include "layout.h" /* defines for screen position layout */
+#include "display.h"
+#include "boolean.h"
+#include "utils.h"
+
+#ifdef ENABLE_COLOR
+#include "color.h"
+#endif
+
+#define CURSOR_COST 8
+
+/* imported from screen.c */
+extern int overstrike;
+
+static int lmpid = -1;
+static int display_width = MAX_COLS;
+
+/* cursor positions of key points on the screen are maintained here */
+/* layout.h has static definitions, but we may change our minds on some
+ of the positions as we make decisions about what needs to be displayed */
+
+static int x_lastpid = X_LASTPID;
+static int y_lastpid = Y_LASTPID;
+static int x_loadave = X_LOADAVE;
+static int y_loadave = Y_LOADAVE;
+static int x_minibar = X_MINIBAR;
+static int y_minibar = Y_MINIBAR;
+static int x_uptime = X_UPTIME;
+static int y_uptime = Y_UPTIME;
+static int x_procstate = X_PROCSTATE;
+static int y_procstate = Y_PROCSTATE;
+static int x_brkdn = X_BRKDN;
+static int y_brkdn = Y_BRKDN;
+static int x_cpustates = X_CPUSTATES;
+static int y_cpustates = Y_CPUSTATES;
+static int x_mem = X_MEM;
+static int y_mem = Y_MEM;
+static int x_swap = -1;
+static int y_swap = -1;
+static int y_message = Y_MESSAGE;
+static int x_header = X_HEADER;
+static int y_header = Y_HEADER;
+static int x_idlecursor = X_IDLECURSOR;
+static int y_idlecursor = Y_IDLECURSOR;
+static int y_procs = Y_PROCS;
+
+/* buffer and colormask that describes the content of the screen */
+/* these are singly dimensioned arrays -- the row boundaries are
+ determined on the fly.
+*/
+static char *screenbuf = NULL;
+static char *colorbuf = NULL;
+static char scratchbuf[MAX_COLS];
+static int bufsize = 0;
+
+/* lineindex tells us where the beginning of a line is in the buffer */
+#define lineindex(l) ((l)*MAX_COLS)
+
+/* screen's cursor */
+static int curr_x, curr_y;
+static int curr_color;
+
+/* virtual cursor */
+static int virt_x, virt_y;
+
+static char **procstate_names;
+static char **cpustate_names;
+static char **memory_names;
+static char **swap_names;
+
+static int num_procstates;
+static int num_cpustates;
+static int num_memory;
+static int num_swap;
+
+static int *lprocstates;
+static int *lcpustates;
+
+static int *cpustate_columns;
+static int cpustate_total_length;
+
+static enum { OFF, ON, ERASE } header_status = ON;
+
+#ifdef ENABLE_COLOR
+static int load_cidx[3];
+static int header_cidx;
+static int *cpustate_cidx;
+static int *memory_cidx;
+static int *swap_cidx;
+#endif
+static int header_color = 0;
+
+
+/* internal support routines */
+
+/*
+ * static int string_count(char **pp)
+ *
+ * Pointer "pp" points to an array of string pointers, which is
+ * terminated by a NULL. Return the number of string pointers in
+ * this array.
+ */
+
+static int
+string_count(char **pp)
+
+{
+ register int cnt = 0;
+
+ if (pp != NULL)
+ {
+ while (*pp++ != NULL)
+ {
+ cnt++;
+ }
+ }
+ return(cnt);
+}
+
+void
+display_clear()
+
+{
+ dprintf("display_clear\n");
+ clear();
+ memzero(screenbuf, bufsize);
+ memzero(colorbuf, bufsize);
+ curr_x = curr_y = 0;
+}
+
+/*
+ * void display_move(int x, int y)
+ *
+ * Efficiently move the cursor to x, y. This assumes the cursor is
+ * currently located at curr_x, curr_y, and will only use cursor
+ * addressing when it is less expensive than overstriking what's
+ * already on the screen.
+ */
+
+void
+display_move(int x, int y)
+
+{
+ char buff[128];
+ char *p;
+ char *bufp;
+ char *colorp;
+ int cnt = 0;
+ int color = curr_color;
+
+ dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y);
+
+ /* are we in a position to do this without cursor addressing? */
+ if (curr_y < y || (curr_y == y && curr_x <= x))
+ {
+ /* start buffering up what it would take to move there by rewriting
+ what's on the screen */
+ cnt = CURSOR_COST;
+ p = buff;
+
+ /* one newline for every line */
+ while (cnt > 0 && curr_y < y)
+ {
+ if (color != 0)
+ {
+ p = strecpy(p, color_set(0));
+ color = 0;
+ cnt -= 5;
+ }
+ *p++ = '\n';
+ curr_y++;
+ curr_x = 0;
+ cnt--;
+ }
+
+ /* write whats in the screenbuf */
+ bufp = &screenbuf[lineindex(curr_y) + curr_x];
+ colorp = &colorbuf[lineindex(curr_y) + curr_x];
+ while (cnt > 0 && curr_x < x)
+ {
+ if (color != *colorp)
+ {
+ color = *colorp;
+ p = strecpy(p, color_set(color));
+ cnt -= 5;
+ }
+ if ((*p = *bufp) == '\0')
+ {
+ /* somwhere on screen we haven't been before */
+ *p = *bufp = ' ';
+ }
+ p++;
+ bufp++;
+ colorp++;
+ curr_x++;
+ cnt--;
+ }
+ }
+
+ /* move the cursor */
+ if (cnt > 0)
+ {
+ /* screen rewrite is cheaper */
+ *p = '\0';
+ fputs(buff, stdout);
+ curr_color = color;
+ }
+ else
+ {
+ Move_to(x, y);
+ }
+
+ /* update our position */
+ curr_x = x;
+ curr_y = y;
+}
+
+/*
+ * display_write(int x, int y, int newcolor, int eol, char *new)
+ *
+ * Optimized write to the display. This writes characters to the
+ * screen in a way that optimizes the number of characters actually
+ * sent, by comparing what is being written to what is already on
+ * the screen (according to screenbuf and colorbuf). The string to
+ * write is "new", the first character of "new" should appear at
+ * screen position x, y. If x is -1 then "new" begins wherever the
+ * cursor is currently positioned. The string is written with color
+ * "newcolor". If "eol" is true then the remainder of the line is
+ * cleared. It is expected that "new" will have no newlines and no
+ * escape sequences.
+ */
+
+void
+display_write(int x, int y, int newcolor, int eol, char *new)
+
+{
+ char *bufp;
+ char *colorp;
+ int ch;
+ int diff;
+
+ dprintf("display_write(%d, %d, %d, %d, \"%s\")\n",
+ x, y, newcolor, eol, new);
+
+ /* dumb terminal handling here */
+ if (!smart_terminal)
+ {
+ if (x != -1)
+ {
+ /* make sure we are on the right line */
+ while (curr_y < y)
+ {
+ putchar('\n');
+ curr_y++;
+ curr_x = 0;
+ }
+
+ /* make sure we are on the right column */
+ while (curr_x < x)
+ {
+ putchar(' ');
+ curr_x++;
+ }
+ }
+
+ /* write */
+ fputs(new, stdout);
+ curr_x += strlen(new);
+
+ return;
+ }
+
+ /* adjust for "here" */
+ if (x == -1)
+ {
+ x = virt_x;
+ y = virt_y;
+ }
+ else
+ {
+ virt_x = x;
+ virt_y = y;
+ }
+
+ /* a pointer to where we start */
+ bufp = &screenbuf[lineindex(y) + x];
+ colorp = &colorbuf[lineindex(y) + x];
+
+ /* main loop */
+ while ((ch = *new++) != '\0')
+ {
+ /* if either character or color are different, an update is needed */
+ /* but only when the screen is wide enough */
+ if (x < display_width && (ch != *bufp || newcolor != *colorp))
+ {
+ /* check cursor */
+ if (y != curr_y || x != curr_x)
+ {
+ /* have to move the cursor */
+ display_move(x, y);
+ }
+
+ /* write character */
+ if (curr_color != newcolor)
+ {
+ fputs(color_set(newcolor), stdout);
+ curr_color = newcolor;
+ }
+ putchar(ch);
+ *bufp = ch;
+ *colorp = curr_color;
+ curr_x++;
+ }
+
+ /* move */
+ x++;
+ virt_x++;
+ bufp++;
+ colorp++;
+ }
+
+ /* eol handling */
+ if (eol && *bufp != '\0')
+ {
+ dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp);
+ /* make sure we are color 0 */
+ if (curr_color != 0)
+ {
+ fputs(color_set(0), stdout);
+ curr_color = 0;
+ }
+
+ /* make sure we are at the end */
+ if (x != curr_x || y != curr_y)
+ {
+ Move_to(x, y);
+ curr_x = x;
+ curr_y = y;
+ }
+
+ /* clear to end */
+ clear_eol(strlen(bufp));
+
+ /* clear out whats left of this line's buffer */
+ diff = display_width - x;
+ if (diff > 0)
+ {
+ memzero(bufp, diff);
+ memzero(colorp, diff);
+ }
+ }
+}
+
+void
+display_fmt(int x, int y, int newcolor, int eol, char *fmt, ...)
+
+{
+ va_list argp;
+
+ va_start(argp, fmt);
+
+ vsnprintf(scratchbuf, MAX_COLS, fmt, argp);
+ display_write(x, y, newcolor, eol, scratchbuf);
+}
+
+void
+display_cte()
+
+{
+ int len;
+ int y;
+ char *p;
+ int need_clear = 0;
+
+ /* is there anything out there that needs to be cleared? */
+ p = &screenbuf[lineindex(virt_y) + virt_x];
+ if (*p != '\0')
+ {
+ need_clear = 1;
+ }
+ else
+ {
+ /* this line is clear, what about the rest? */
+ y = virt_y;
+ while (++y < screen_length)
+ {
+ if (screenbuf[lineindex(y)] != '\0')
+ {
+ need_clear = 1;
+ break;
+ }
+ }
+ }
+
+ if (need_clear)
+ {
+ dprintf("display_cte: clearing\n");
+
+ /* different method when there's no clear_to_end */
+ if (clear_to_end)
+ {
+ display_move(virt_x, virt_y);
+ putcap(clear_to_end);
+ }
+ else
+ {
+ if (++virt_y < screen_length)
+ {
+ display_move(0, virt_y);
+ virt_x = 0;
+ while (virt_y < screen_length)
+ {
+ p = &screenbuf[lineindex(virt_y)];
+ len = strlen(p);
+ if (len > 0)
+ {
+ clear_eol(len);
+ }
+ virt_y++;
+ }
+ }
+ }
+
+ /* clear the screenbuf */
+ len = lineindex(virt_y) + virt_x;
+ memzero(&screenbuf[len], bufsize -len);
+ memzero(&colorbuf[len], bufsize -len);
+ }
+}
+
+static void
+summary_format(int x, int y, int *numbers, char **names)
+
+{
+ register int num;
+ register char *thisname;
+ register char *lastname = NULL;
+
+ /* format each number followed by its string */
+ while ((thisname = *names++) != NULL)
+ {
+ /* get the number to format */
+ num = *numbers++;
+
+ /* display only non-zero numbers */
+ if (num != 0)
+ {
+ /* write the previous name */
+ if (lastname != NULL)
+ {
+ display_write(-1, -1, 0, 0, lastname);
+ }
+
+ /* write this number if positive */
+ if (num > 0)
+ {
+ display_write(x, y, 0, 0, itoa(num));
+ }
+
+ /* defer writing this name */
+ lastname = thisname;
+
+ /* next iteration will not start at x, y */
+ x = y = -1;
+ }
+ }
+
+ /* if the last string has a separator on the end, it has to be
+ written with care */
+ if ((num = strlen(lastname)) > 1 &&
+ lastname[num-2] == ',' && lastname[num-1] == ' ')
+ {
+ display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname);
+ }
+ else
+ {
+ display_write(-1, -1, 0, 1, lastname);
+ }
+}
+
+static void
+summary_format_memory(int x, int y, long *numbers, char **names, int *cidx)
+
+{
+ register long num;
+ register int color;
+ register char *thisname;
+ register char *lastname = NULL;
+
+ /* format each number followed by its string */
+ while ((thisname = *names++) != NULL)
+ {
+ /* get the number to format */
+ num = *numbers++;
+ color = 0;
+
+ /* display only non-zero numbers */
+ if (num != 0)
+ {
+ /* write the previous name */
+ if (lastname != NULL)
+ {
+ display_write(-1, -1, 0, 0, lastname);
+ }
+
+ /* defer writing this name */
+ lastname = thisname;
+
+#ifdef ENABLE_COLOR
+ /* choose a color */
+ color = color_test(*cidx++, num);
+#endif
+
+ /* is this number in kilobytes? */
+ if (thisname[0] == 'K')
+ {
+ display_write(x, y, color, 0, format_k(num));
+ lastname++;
+ }
+ else
+ {
+ display_write(x, y, color, 0, itoa((int)num));
+ }
+
+ /* next iteration will not start at x, y */
+ x = y = -1;
+ }
+ }
+
+ /* if the last string has a separator on the end, it has to be
+ written with care */
+ if ((num = strlen(lastname)) > 1 &&
+ lastname[num-2] == ',' && lastname[num-1] == ' ')
+ {
+ display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname);
+ }
+ else
+ {
+ display_write(-1, -1, 0, 1, lastname);
+ }
+}
+
+/*
+ * int display_resize()
+ *
+ * Reallocate buffer space needed by the display package to accomodate
+ * a new screen size. Must be called whenever the screen's size has
+ * changed. Returns the number of lines available for displaying
+ * processes or -1 if there was a problem allocating space.
+ */
+
+int
+display_resize()
+
+{
+ register int lines;
+ register int newsize;
+
+ /* calculate the current dimensions */
+ /* if operating in "dumb" mode, we only need one line */
+ lines = smart_terminal ? screen_length : 1;
+
+ /* we don't want more than MAX_COLS columns, since the machine-dependent
+ modules make static allocations based on MAX_COLS and we don't want
+ to run off the end of their buffers */
+ display_width = screen_width;
+ if (display_width >= MAX_COLS)
+ {
+ display_width = MAX_COLS - 1;
+ }
+
+ /* see how much space we need */
+ newsize = lines * (MAX_COLS + 1);
+
+ /* reallocate only if we need more than we already have */
+ if (newsize > bufsize)
+ {
+ /* deallocate any previous buffer that may have been there */
+ if (screenbuf != NULL)
+ {
+ free(screenbuf);
+ }
+ if (colorbuf != NULL)
+ {
+ free(colorbuf);
+ }
+
+ /* allocate space for the screen and color buffers */
+ bufsize = newsize;
+ screenbuf = (char *)calloc(bufsize, sizeof(char));
+ colorbuf = (char *)calloc(bufsize, sizeof(char));
+ if (screenbuf == NULL || colorbuf == NULL)
+ {
+ /* oops! */
+ return(-1);
+ }
+ }
+ else
+ {
+ /* just clear them out */
+ memzero(screenbuf, bufsize);
+ memzero(colorbuf, bufsize);
+ }
+
+ /* adjust total lines on screen to lines available for procs */
+ lines -= y_procs;
+
+ /* return number of lines available */
+ /* for dumb terminals, pretend like we can show any amount */
+ return(smart_terminal ? lines : Largest);
+}
+
+/*
+ * int display_init(struct statics *statics)
+ *
+ * Initialize the display system based on information in the statics
+ * structure. Returns the number of lines available for displaying
+ * processes or -1 if there was an error.
+ */
+
+int
+display_init(struct statics *statics)
+
+{
+ register int lines;
+ register char **pp;
+ register char *p;
+ register int *ip;
+ register int i;
+
+ /* certain things may influence the screen layout,
+ so look at those first */
+ /* a swap line shifts parts of the display down one */
+ swap_names = statics->swap_names;
+ if ((num_swap = string_count(swap_names)) > 0)
+ {
+ /* adjust screen placements */
+ y_message++;
+ y_header++;
+ y_idlecursor++;
+ y_procs++;
+ x_swap = X_SWAP;
+ y_swap = Y_SWAP;
+ }
+
+ /* call resize to do the dirty work */
+ lines = display_resize();
+
+ /* only do the rest if we need to */
+ if (lines > -1)
+ {
+ /* save pointers and allocate space for names */
+ procstate_names = statics->procstate_names;
+ num_procstates = string_count(procstate_names);
+ lprocstates = (int *)malloc(num_procstates * sizeof(int));
+
+ cpustate_names = statics->cpustate_names;
+ num_cpustates = string_count(cpustate_names);
+ lcpustates = (int *)malloc(num_cpustates * sizeof(int));
+ cpustate_columns = (int *)malloc(num_cpustates * sizeof(int));
+ memory_names = statics->memory_names;
+ num_memory = string_count(memory_names);
+
+ /* calculate starting columns where needed */
+ cpustate_total_length = 0;
+ pp = cpustate_names;
+ ip = cpustate_columns;
+ while (*pp != NULL)
+ {
+ *ip++ = cpustate_total_length;
+ if ((i = strlen(*pp++)) > 0)
+ {
+ cpustate_total_length += i + 8;
+ }
+ }
+ }
+
+#ifdef ENABLE_COLOR
+ /* set up color tags for loadavg */
+ load_cidx[0] = color_tag("1min");
+ load_cidx[1] = color_tag("5min");
+ load_cidx[2] = color_tag("15min");
+
+ /* find header color */
+ header_cidx = color_tag("header");
+ header_color = color_test(header_cidx, 0);
+
+ /* color tags for cpu states */
+ cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int));
+ i = 0;
+ p = strecpy(scratchbuf, "cpu.");
+ while (i < num_cpustates)
+ {
+ strcpy(p, cpustate_names[i]);
+ cpustate_cidx[i++] = color_tag(scratchbuf);
+ }
+
+ /* color tags for memory */
+ memory_cidx = (int *)malloc(num_memory * sizeof(int));
+ i = 0;
+ p = strecpy(scratchbuf, "memory.");
+ while (i < num_memory)
+ {
+ strcpy(p, homogenize(memory_names[i]+1));
+ memory_cidx[i++] = color_tag(scratchbuf);
+ }
+
+ /* color tags for swap */
+ swap_cidx = (int *)malloc(num_swap * sizeof(int));
+ i = 0;
+ p = strecpy(scratchbuf, "swap.");
+ while (i < num_swap)
+ {
+ strcpy(p, homogenize(swap_names[i]+1));
+ swap_cidx[i++] = color_tag(scratchbuf);
+ }
+#endif
+
+ /* return number of lines available (or error) */
+ return(lines);
+}
+
+static void
+pr_loadavg(double avg, int i)
+
+{
+ int color = 0;
+
+#ifdef ENABLE_COLOR
+ color = color_test(load_cidx[i], (int)(avg * 100));
+#endif
+ display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0,
+ avg < 10.0 ? " %5.2f" : " %5.1f", avg);
+ display_write(-1, -1, 0, 0, (i < 2 ? "," : ";"));
+}
+
+void
+i_loadave(int mpid, double *avenrun)
+
+{
+ register int i;
+
+ /* i_loadave also clears the screen, since it is first */
+ display_clear();
+
+ /* mpid == -1 implies this system doesn't have an _mpid */
+ if (mpid != -1)
+ {
+ display_fmt(0, 0, 0, 0,
+ "last pid: %5d; load avg:", mpid);
+ x_loadave = X_LOADAVE;
+ }
+ else
+ {
+ display_write(0, 0, 0, 0, "load averages:");
+ x_loadave = X_LOADAVE - X_LASTPIDWIDTH;
+ }
+ for (i = 0; i < 3; i++)
+ {
+ pr_loadavg(avenrun[i], i);
+ }
+
+ lmpid = mpid;
+}
+
+void
+u_loadave(int mpid, double *avenrun)
+
+{
+ register int i;
+
+ if (mpid != -1)
+ {
+ /* change screen only when value has really changed */
+ if (mpid != lmpid)
+ {
+ display_fmt(x_lastpid, y_lastpid, 0, 0,
+ "%5d", mpid);
+ lmpid = mpid;
+ }
+ }
+
+ /* display new load averages */
+ for (i = 0; i < 3; i++)
+ {
+ pr_loadavg(avenrun[i], i);
+ }
+}
+
+static char minibar_buffer[64];
+#define MINIBAR_WIDTH 20
+
+void
+i_minibar(int (*formatter)(char *, int))
+{
+ (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
+
+ display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
+}
+
+void
+u_minibar(int (*formatter)(char *, int))
+{
+ (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
+
+ display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
+}
+
+static int uptime_days;
+static int uptime_hours;
+static int uptime_mins;
+static int uptime_secs;
+
+void
+i_uptime(time_t *bt, time_t *tod)
+
+{
+ time_t uptime;
+
+ if (*bt != -1)
+ {
+ uptime = *tod - *bt;
+ uptime += 30;
+ uptime_days = uptime / 86400;
+ uptime %= 86400;
+ uptime_hours = uptime / 3600;
+ uptime %= 3600;
+ uptime_mins = uptime / 60;
+ uptime_secs = uptime % 60;
+
+ /*
+ * Display the uptime.
+ */
+
+ display_fmt(x_uptime, y_uptime, 0, 0,
+ " up %d+%02d:%02d:%02d",
+ uptime_days, uptime_hours, uptime_mins, uptime_secs);
+ }
+}
+
+void
+u_uptime(time_t *bt, time_t *tod)
+
+{
+ i_uptime(bt, tod);
+}
+
+
+void
+i_timeofday(time_t *tod)
+
+{
+ /*
+ * Display the current time.
+ * "ctime" always returns a string that looks like this:
+ *
+ * Sun Sep 16 01:03:52 1973
+ * 012345678901234567890123
+ * 1 2
+ *
+ * We want indices 11 thru 18 (length 8).
+ */
+
+ display_fmt((smart_terminal ? screen_width : 79) - 8, 0, 0, 1,
+ "%-8.8s", &(ctime(tod)[11]));
+}
+
+static int ltotal = 0;
+
+/*
+ * *_procstates(total, brkdn, names) - print the process summary line
+ */
+
+
+void
+i_procstates(int total, int *brkdn)
+
+{
+ /* write current number of processes and remember the value */
+ display_fmt(0, y_procstate, 0, 0,
+ "%d processes: ", total);
+ ltotal = total;
+
+ /* remember where the summary starts */
+ x_procstate = virt_x;
+
+ /* format and print the process state summary */
+ summary_format(-1, -1, brkdn, procstate_names);
+
+ /* save the numbers for next time */
+ memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
+}
+
+void
+u_procstates(int total, int *brkdn)
+
+{
+ /* update number of processes only if it has changed */
+ if (ltotal != total)
+ {
+ display_fmt(0, y_procstate, 0, 0,
+ "%d", total);
+
+ /* if number of digits differs, rewrite the label */
+ if (digits(total) != digits(ltotal))
+ {
+ display_write(-1, -1, 0, 0, " processes: ");
+ x_procstate = virt_x;
+ }
+
+ /* save new total */
+ ltotal = total;
+ }
+
+ /* see if any of the state numbers has changed */
+ if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
+ {
+ /* format and update the line */
+ summary_format(x_procstate, y_procstate, brkdn, procstate_names);
+ memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
+ }
+}
+
+/*
+ * *_cpustates(states, names) - print the cpu state percentages
+ */
+
+/* cpustates_tag() calculates the correct tag to use to label the line */
+
+char *
+cpustates_tag()
+
+{
+ register char *use;
+
+ static char *short_tag = "CPU: ";
+ static char *long_tag = "CPU states: ";
+
+ /* if length + strlen(long_tag) >= screen_width, then we have to
+ use the shorter tag (we subtract 2 to account for ": ") */
+ if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
+ {
+ use = short_tag;
+ }
+ else
+ {
+ use = long_tag;
+ }
+
+ /* set x_cpustates accordingly then return result */
+ x_cpustates = strlen(use);
+ return(use);
+}
+
+void
+i_cpustates(int *states)
+
+{
+ int value;
+ char **names;
+ char *thisname;
+ int *colp;
+ int color = 0;
+#ifdef ENABLE_COLOR
+ int *cidx = cpustate_cidx;
+#endif
+
+ /* initialize */
+ names = cpustate_names;
+ colp = cpustate_columns;
+
+ /* print tag */
+ display_write(0, y_cpustates, 0, 0, cpustates_tag());
+
+ /* now walk thru the names and print the line */
+ while ((thisname = *names++) != NULL)
+ {
+ if (*thisname != '\0')
+ {
+ /* retrieve the value and remember it */
+ value = *states;
+
+#ifdef ENABLE_COLOR
+ /* determine color number to use */
+ color = color_test(*cidx++, value/10);
+#endif
+
+ /* if percentage is >= 1000, print it as 100% */
+ display_fmt(x_cpustates + *colp, y_cpustates,
+ color, 0,
+ (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
+ ((float)value)/10.,
+ thisname,
+ *names != NULL ? ", " : "");
+
+ }
+ /* increment */
+ colp++;
+ states++;
+ }
+
+ /* copy over values into "last" array */
+ memcpy(lcpustates, states, num_cpustates * sizeof(int));
+}
+
+void
+u_cpustates(int *states)
+
+{
+ int value;
+ char **names = cpustate_names;
+ char *thisname;
+ int *lp;
+ int *colp;
+ int color = 0;
+#ifdef ENABLE_COLOR
+ int *cidx = cpustate_cidx;
+#endif
+
+ lp = lcpustates;
+ colp = cpustate_columns;
+
+ /* we could be much more optimal about this */
+ while ((thisname = *names++) != NULL)
+ {
+ if (*thisname != '\0')
+ {
+ /* did the value change since last time? */
+ if (*lp != *states)
+ {
+ /* yes, change it */
+ /* retrieve value and remember it */
+ value = *states;
+
+#ifdef ENABLE_COLOR
+ /* determine color number to use */
+ color = color_test(*cidx, value/10);
+#endif
+
+ /* if percentage is >= 1000, print it as 100% */
+ display_fmt(x_cpustates + *colp, y_cpustates, color, 0,
+ (value >= 1000 ? "%4.0f" : "%4.1f"),
+ ((double)value)/10.);
+
+ /* remember it for next time */
+ *lp = value;
+ }
+#ifdef ENABLE_COLOR
+ cidx++;
+#endif
+ }
+
+ /* increment and move on */
+ lp++;
+ states++;
+ colp++;
+ }
+}
+
+void
+z_cpustates()
+
+{
+ register int i = 0;
+ register char **names = cpustate_names;
+ register char *thisname;
+ register int *lp;
+
+ /* print tag */
+ display_write(0, y_cpustates, 0, 0, cpustates_tag());
+
+ while ((thisname = *names++) != NULL)
+ {
+ if (*thisname != '\0')
+ {
+ display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ",
+ thisname);
+ }
+ }
+
+ /* fill the "last" array with all -1s, to insure correct updating */
+ lp = lcpustates;
+ i = num_cpustates;
+ while (--i >= 0)
+ {
+ *lp++ = -1;
+ }
+}
+
+/*
+ * *_memory(stats) - print "Memory: " followed by the memory summary string
+ *
+ * Assumptions: cursor is on "lastline", the previous line
+ */
+
+void
+i_memory(long *stats)
+
+{
+ display_write(0, y_mem, 0, 0, "Memory: ");
+
+ /* format and print the memory summary */
+ summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
+}
+
+void
+u_memory(long *stats)
+
+{
+ /* format the new line */
+ summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
+}
+
+/*
+ * *_swap(stats) - print "Swap: " followed by the swap summary string
+ *
+ * Assumptions: cursor is on "lastline", the previous line
+ *
+ * These functions only print something when num_swap > 0
+ */
+
+void
+i_swap(long *stats)
+
+{
+ if (num_swap > 0)
+ {
+ /* print the tag */
+ display_write(0, y_swap, 0, 0, "Swap: ");
+
+ /* format and print the swap summary */
+ summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
+ }
+}
+
+void
+u_swap(long *stats)
+
+{
+ if (num_swap > 0)
+ {
+ /* format the new line */
+ summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
+ }
+}
+
+/*
+ * *_message() - print the next pending message line, or erase the one
+ * that is there.
+ *
+ * Note that u_message is (currently) the same as i_message.
+ *
+ * Assumptions: lastline is consistent
+ */
+
+/*
+ * i_message is funny because it gets its message asynchronously (with
+ * respect to screen updates).
+ */
+
+static char next_msg[MAX_COLS + 8];
+static int msglen = 0;
+/* Invariant: msglen is always the length of the message currently displayed
+ on the screen (even when next_msg doesn't contain that message). */
+
+void
+i_message()
+
+{
+ if (smart_terminal)
+ {
+ if (next_msg[0] != '\0')
+ {
+ display_move(0, y_message);
+ standout(next_msg);
+ msglen = strlen(next_msg);
+ next_msg[0] = '\0';
+ }
+ else if (msglen > 0)
+ {
+ display_move(0, y_message);
+ (void) clear_eol(msglen);
+ msglen = 0;
+ }
+ }
+}
+
+void
+u_message()
+
+{
+ i_message();
+}
+
+static int header_length;
+
+/*
+ * *_header(text) - print the header for the process area
+ *
+ * Assumptions: cursor is on the previous line and lastline is consistent
+ */
+
+void
+i_header(char *text)
+
+{
+ header_length = strlen(text);
+ if (header_status == ON)
+ {
+ display_write(x_header, y_header, header_color, 1, text);
+ }
+ else if (header_status == ERASE)
+ {
+ header_status = OFF;
+ }
+}
+
+/*ARGSUSED*/
+void
+u_header(char *text)
+
+{
+ if (header_status == ERASE)
+ {
+ display_write(x_header, y_header, header_color, 1, "");
+ header_status = OFF;
+ }
+}
+
+/*
+ * *_process(line, thisline) - print one process line
+ *
+ * Assumptions: lastline is consistent
+ */
+
+void
+i_process(int line, char *thisline)
+
+{
+ /* truncate the line to conform to our current screen width */
+ thisline[display_width] = '\0';
+
+ /* write the line out */
+ display_write(0, y_procs + line, 0, 1, thisline);
+}
+
+void
+u_process(int line, char *newline)
+
+{
+ i_process(line, newline);
+}
+
+void
+u_endscreen(int hi)
+
+{
+ if (smart_terminal)
+ {
+ /* clear-to-end the display */
+ display_cte();
+
+ /* move the cursor to a pleasant place */
+ /* does this need to be a display_move??? */
+ Move_to(x_idlecursor, y_idlecursor);
+ }
+ else
+ {
+ /* separate this display from the next with some vertical room */
+ fputs("\n\n", stdout);
+ }
+}
+
+void
+display_header(int t)
+
+{
+ if (t)
+ {
+ header_status = ON;
+ }
+ else if (header_status == ON)
+ {
+ header_status = ERASE;
+ }
+}
+
+void
+new_message_v(int type, char *msgfmt, va_list ap)
+
+{
+ register int i;
+
+ /* first, format the message */
+ (void) vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);
+
+ if (msglen > 0)
+ {
+ /* message there already -- can we clear it? */
+ if (!overstrike)
+ {
+ /* yes -- write it and clear to end */