aboutsummaryrefslogtreecommitdiffstats
path: root/display.c
diff options
context:
space:
mode:
Diffstat (limited to 'display.c')
-rw-r--r--display.c2030
1 files changed, 1419 insertions, 611 deletions
diff --git a/display.c b/display.c
index 13cecc00ca22..2330ca422ec3 100644
--- a/display.c
+++ b/display.c
@@ -1,12 +1,38 @@
/*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
* 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
*/
/*
@@ -21,80 +47,631 @@
* 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.
+ * The routines should be called in this order: *_loadave, *_uptime,
+ * i_timeofday, *_procstates, *_cpustates, *_memory, *_swap,
+ * *_message, *_header, *_process, *_endscreen.
*/
#include "os.h"
#include <ctype.h>
-#include <time.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.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 "top.h"
-#include "top.local.h"
#include "boolean.h"
-#include "machine.h" /* we should eliminate this!!! */
#include "utils.h"
-#ifdef DEBUG
-FILE *debug;
+#ifdef ENABLE_COLOR
+#include "color.h"
#endif
+#define CURSOR_COST 8
+
+#define MESSAGE_DISPLAY_TIME 5
+
/* imported from screen.c */
extern int overstrike;
-static int lmpid = 0;
-static int last_hi = 0; /* used in u_process and u_endscreen */
-static int lastline = 0;
+static int lmpid = -1;
static int display_width = MAX_COLS;
-#define lineindex(l) ((l)*display_width)
-
-char *printable();
-
-/* things initialized by display_init and used thruout */
-
-/* buffer of proc information lines for display updating */
-char *screenbuf = NULL;
+/* 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_cpustates = X_CPUSTATES;
+static int y_cpustates = Y_CPUSTATES;
+static int x_kernel = X_KERNEL;
+static int y_kernel = Y_KERNEL;
+static int x_mem = X_MEM;
+static int y_mem = Y_MEM;
+static int x_swap = X_SWAP;
+static int y_swap = Y_SWAP;
+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 char **kernel_names;
static int num_procstates;
static int num_cpustates;
static int num_memory;
+static int num_swap;
+static int num_kernel;
static int *lprocstates;
static int *lcpustates;
-static int *lmemory;
static int *cpustate_columns;
static int cpustate_total_length;
-static enum { OFF, ON, ERASE } header_status = ON;
+static int header_status = Yes;
+
+/* pending messages are stored in a circular buffer, where message_first
+ is the next one to display, and message_last is the last one
+ in the buffer. Counters wrap around at MAX_MESSAGES. The buffer is
+ empty when message_first == message_last and full when
+ message_last + 1 == message_first. The pointer message_current holds
+ the message currently being displayed, or "" if there is none.
+*/
+#define MAX_MESSAGES 16
+static char *message_buf[MAX_MESSAGES];
+static int message_first = 0;
+static int message_last = 0;
+static struct timeval message_time = {0, 0};
+static char *message_current = NULL;
+static int message_length = 0;
+static int message_hold = 1;
+static int message_barrier = No;
+
+#ifdef ENABLE_COLOR
+static int load_cidx[3];
+static int header_cidx;
+static int *cpustate_cidx;
+static int *memory_cidx;
+static int *swap_cidx;
+static int *kernel_cidx;
+#else
+#define memory_cidx NULL
+#define swap_cidx NULL
+#define kernel_cidx NULL
+#endif
+
+
+/* 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");
+ screen_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)
+ {
+#ifdef ENABLE_COLOR
+ if (color != 0)
+ {
+ p = strcpyend(p, color_setstr(0));
+ color = 0;
+ cnt -= 5;
+ }
+#endif
+ *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)
+ {
+#ifdef ENABLE_COLOR
+ if (color != *colorp)
+ {
+ color = *colorp;
+ p = strcpyend(p, color_setstr(color));
+ cnt -= 5;
+ }
+#endif
+ 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
+ {
+ screen_move(x, y);
+ }
-static int string_count();
-static void summary_format();
-static void line_update();
+ /* update our position */
+ curr_x = x;
+ curr_y = y;
+}
-int display_resize()
+/*
+ * 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)
{
- register int lines;
+ char *bufp;
+ char *colorp;
+ int ch;
+ int diff;
+
+ dprintf("display_write(%d, %d, %d, %d, \"%s\")\n",
+ x, y, newcolor, eol, new);
- /* first, deallocate any previous buffer that may have been there */
- if (screenbuf != NULL)
+ /* dumb terminal handling here */
+ if (!smart_terminal)
{
- free(screenbuf);
+ 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 */
+#ifdef ENABLE_COLOR
+ if (curr_color != newcolor)
+ {
+ fputs(color_setstr(newcolor), stdout);
+ curr_color = newcolor;
+ }
+#endif
+ 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 */
+#ifdef ENABLE_COLOR
+ if (curr_color != 0)
+ {
+ fputs(color_setstr(0), stdout);
+ curr_color = 0;
+ }
+#endif
+
+ /* make sure we are at the end */
+ if (x != curr_x || y != curr_y)
+ {
+ screen_move(x, y);
+ curr_x = x;
+ curr_y = y;
+ }
+
+ /* clear to end */
+ screen_cleareol(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");
+
+ /* we will need this later */
+ len = lineindex(virt_y) + virt_x;
+
+ /* move to x and y, then clear to end */
+ display_move(virt_x, virt_y);
+ if (!screen_cte())
+ {
+ /* screen has no clear to end, so do it by hand */
+ p = &screenbuf[len];
+ len = strlen(p);
+ if (len > 0)
+ {
+ screen_cleareol(len);
+ }
+ while (++virt_y < screen_length)
+ {
+ display_move(0, virt_y);
+ p = &screenbuf[lineindex(virt_y)];
+ len = strlen(p);
+ if (len > 0)
+ {
+ screen_cleareol(len);
+ }
+ }
+ }
+
+ /* clear the screenbuf */
+ memzero(&screenbuf[len], bufsize - len);
+ memzero(&colorbuf[len], bufsize - len);
+ }
+}
+
+static void
+summary_format(int x, int y, int *numbers, char **names, int *cidx)
+
+{
+ register int num;
+ register char *thisname;
+ register char *lastname = NULL;
+ register int color;
+
+ /* 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);
+ }
+
+#ifdef ENABLE_COLOR
+ if (cidx != NULL)
+ {
+ /* choose a color */
+ color = color_test(*cidx++, num);
+ }
+#endif
+
+ /* write this number if positive */
+ if (num > 0)
+ {
+ display_write(x, y, color, 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 (lastname != NULL)
+ {
+ 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 (lastname != NULL)
+ {
+ 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 top_lines;
+ register int newsize;
+
/* calculate the current dimensions */
/* if operating in "dumb" mode, we only need one line */
- lines = smart_terminal ? screen_length - Header_lines : 1;
+ top_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
@@ -105,48 +682,123 @@ int display_resize()
display_width = MAX_COLS - 1;
}
- /* now, allocate space for the screen buffer */
- screenbuf = (char *)malloc(lines * display_width);
- if (screenbuf == (char *)NULL)
+ /* see how much space we need */
+ newsize = top_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
{
- /* oops! */
- return(-1);
+ /* just clear them out */
+ memzero(screenbuf, bufsize);
+ memzero(colorbuf, bufsize);
}
+ /* adjust total lines on screen to lines available for procs */
+ top_lines -= y_procs;
+
/* return number of lines available */
/* for dumb terminals, pretend like we can show any amount */
- return(smart_terminal ? lines : Largest);
+ return(smart_terminal ? top_lines : Largest);
+}
+
+int
+display_lines()
+
+{
+ return(smart_terminal ? screen_length : Largest);
+}
+
+int
+display_columns()
+
+{
+ return(display_width);
}
-int display_init(statics)
+/*
+ * 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.
+ */
-struct statics *statics;
+int
+display_init(struct statics *statics)
{
- register int lines;
+ register int top_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 kernel line shifts parts of the display down */
+ kernel_names = statics->kernel_names;
+ if ((num_kernel = string_count(kernel_names)) > 0)
+ {
+ /* adjust screen placements */
+ y_mem++;
+ y_swap++;
+ y_message++;
+ y_header++;
+ y_idlecursor++;
+ y_procs++;
+ }
+
+ /* 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++;
+ }
+
/* call resize to do the dirty work */
- lines = display_resize();
+ top_lines = display_resize();
/* only do the rest if we need to */
- if (lines > -1)
+ if (top_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));
+ lprocstates = (int *)calloc(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));
-
+ lcpustates = (int *)calloc(num_cpustates, sizeof(int));
+ cpustate_columns = (int *)calloc(num_cpustates, sizeof(int));
memory_names = statics->memory_names;
num_memory = string_count(memory_names);
- lmemory = (int *)malloc(num_memory * sizeof(int));
/* calculate starting columns where needed */
cpustate_total_length = 0;
@@ -162,42 +814,108 @@ struct statics *statics;
}
}
- /* return number of lines available */
- return(lines);
+#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");
+
+ /* color tags for cpu states */
+ cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int));
+ i = 0;
+ p = strcpyend(scratchbuf, "cpu.");
+ while (i < num_cpustates)
+ {
+ strcpy(p, cpustate_names[i]);
+ cpustate_cidx[i++] = color_tag(scratchbuf);
+ }
+
+ /* color tags for kernel */
+ if (num_kernel > 0)
+ {
+ kernel_cidx = (int *)malloc(num_kernel * sizeof(int));
+ i = 0;
+ p = strcpyend(scratchbuf, "kernel.");
+ while (i < num_kernel)
+ {
+ strcpy(p, homogenize(kernel_names[i]+1));
+ kernel_cidx[i++] = color_tag(scratchbuf);
+ }
+ }
+
+ /* color tags for memory */
+ memory_cidx = (int *)malloc(num_memory * sizeof(int));
+ i = 0;
+ p = strcpyend(scratchbuf, "memory.");
+ while (i < num_memory)
+ {
+ strcpy(p, homogenize(memory_names[i]+1));
+ memory_cidx[i++] = color_tag(scratchbuf);
+ }
+
+ /* color tags for swap */
+ if (num_swap > 0)
+ {
+ swap_cidx = (int *)malloc(num_swap * sizeof(int));
+ i = 0;
+ p = strcpyend(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(top_lines);
}
-i_loadave(mpid, avenrun)
+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 ? "," : ";"));
+}
-int mpid;
-double *avenrun;
+void
+i_loadave(int mpid, double *avenrun)
{
register int i;
- /* i_loadave also clears the screen, since it is first */
- clear();
-
/* mpid == -1 implies this system doesn't have an _mpid */
if (mpid != -1)
{
- printf("last pid: %5d; ", mpid);
+ 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;
}
-
- printf("load averages");
-
for (i = 0; i < 3; i++)
{
- printf("%c %5.2f",
- i == 0 ? ':' : ',',
- avenrun[i]);
+ pr_loadavg(avenrun[i], i);
}
+
lmpid = mpid;
}
-u_loadave(mpid, avenrun)
-
-int mpid;
-double *avenrun;
+void
+u_loadave(int mpid, double *avenrun)
{
register int i;
@@ -207,35 +925,80 @@ double *avenrun;
/* change screen only when value has really changed */
if (mpid != lmpid)
{
- Move_to(x_lastpid, y_lastpid);
- printf("%5d", mpid);
+ display_fmt(x_lastpid, y_lastpid, 0, 0,
+ "%5d", mpid);
lmpid = mpid;
}
-
- /* i remembers x coordinate to move to */
- i = x_loadave;
}
- else
+
+ /* display new load averages */
+ for (i = 0; i < 3; i++)
{
- i = x_loadave_nompid;
+ pr_loadavg(avenrun[i], i);
}
+}
- /* move into position for load averages */
- Move_to(i, y_loadave);
+static char minibar_buffer[64];
+#define MINIBAR_WIDTH 20
- /* display new load averages */
- /* we should optimize this and only display changes */
- for (i = 0; i < 3; i++)
+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)
{
- printf("%s%5.2f",
- i == 0 ? "" : ", ",
- avenrun[i]);
+ 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);
}
}
-i_timeofday(tod)
+void
+u_uptime(time_t *bt, time_t *tod)
+
+{
+ i_uptime(bt, tod);
+}
+
-time_t *tod;
+void
+i_timeofday(time_t *tod)
{
/*
@@ -243,100 +1006,80 @@ time_t *tod;
* "ctime" always returns a string that looks like this:
*
* Sun Sep 16 01:03:52 1973
- * 012345678901234567890123
+ * 012345678901234567890123
* 1 2
*
* We want indices 11 thru 18 (length 8).
*/
- if (smart_terminal)
- {
- Move_to(screen_width - 8, 0);
- }
- else
- {
- fputs(" ", stdout);
- }
-#ifdef DEBUG
+ int x;
+
+ /* where on the screen do we start? */
+ x = (smart_terminal ? screen_width : 79) - 8;
+
+ /* but don't bump in to uptime */
+ if (x < x_uptime + 19)
{
- char *foo;
- foo = ctime(tod);
- fputs(foo, stdout);
+ x = x_uptime + 19;
}
-#endif
- printf("%-8.8s\n", &(ctime(tod)[11]));
- lastline = 1;
+
+ /* display it */
+ display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11]));
}
static int ltotal = 0;
-static char procstates_buffer[MAX_COLS];
+static int lthreads = 0;
/*
* *_procstates(total, brkdn, names) - print the process summary line
- *
- * Assumptions: cursor is at the beginning of the line on entry
- * lastline is valid
*/
-i_procstates(total, brkdn)
-int total;
-int *brkdn;
+void
+i_procstates(int total, int *brkdn, int threads)
{
- register int i;
-
/* write current number of processes and remember the value */
- printf("%d processes:", total);
+ display_fmt(0, y_procstate, 0, 0,
+ "%d %s: ", total, threads ? "threads" : "processes");
ltotal = total;
- /* put out enough spaces to get to column 15 */
- i = digits(total);
- while (i++ < 4)
- {
- putchar(' ');
- }
+ /* remember where the summary starts */
+ x_procstate = virt_x;
- /* format and print the process state summary */
- summary_format(procstates_buffer, brkdn, procstate_names);
- fputs(procstates_buffer, stdout);
+ if (total > 0)
+ {
+ /* format and print the process state summary */
+ summary_format(-1, -1, brkdn, procstate_names, NULL);
- /* save the numbers for next time */
- memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
+ /* save the numbers for next time */
+ memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
+ lthreads = threads;
+ }
}
-u_procstates(total, brkdn)
-
-int total;
-int *brkdn;
+void
+u_procstates(int total, int *brkdn, int threads)
{
- static char new[MAX_COLS];
- register int i;
+ /* if threads state has changed, do a full update */
+ if (lthreads != threads)
+ {
+ i_procstates(total, brkdn, threads);
+ return;
+ }
/* update number of processes only if it has changed */
if (ltotal != total)
{
- /* move and overwrite */
-#if (x_procstate == 0)
- Move_to(x_procstate, y_procstate);
-#else
- /* cursor is already there...no motion needed */
- /* assert(lastline == 1); */
-#endif
- printf("%d", total);
+ display_fmt(0, y_procstate, 0, 0,
+ "%d", total);
/* if number of digits differs, rewrite the label */
if (digits(total) != digits(ltotal))
{
- fputs(" processes:", stdout);
- /* put out enough spaces to get to column 15 */
- i = digits(total);
- while (i++ < 4)
- {
- putchar(' ');
- }
- /* cursor may end up right where we want it!!! */
+ display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes");
+ x_procstate = virt_x;
}
/* save new total */
@@ -344,26 +1087,22 @@ int *brkdn;
}
/* see if any of the state numbers has changed */
- if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
+ if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
{
/* format and update the line */
- summary_format(new, brkdn, procstate_names);
- line_update(procstates_buffer, new, x_brkdn, y_brkdn);
+ summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL);
memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
}
}
/*
* *_cpustates(states, names) - print the cpu state percentages
- *
- * Assumptions: cursor is on the PREVIOUS line
*/
-static int cpustates_column;
-
/* cpustates_tag() calculates the correct tag to use to label the line */
-char *cpustates_tag()
+char *
+cpustates_tag()
{
register char *use;
@@ -382,24 +1121,30 @@ char *cpustates_tag()
use = long_tag;
}
- /* set cpustates_column accordingly then return result */
- cpustates_column = strlen(use);
+ /* set x_cpustates accordingly then return result */
+ x_cpustates = strlen(use);
return(use);
}
-i_cpustates(states)
-
-register int *states;
+void
+i_cpustates(int *states)
{
- register int i = 0;
- register int value;
- register char **names = cpustate_names;
- register char *thisname;
+ 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 and bump lastline */
- printf("\n%s", cpustates_tag());
- lastline++;
+ /* print tag */
+ display_write(0, y_cpustates, 0, 0, cpustates_tag());
/* now walk thru the names and print the line */
while ((thisname = *names++) != NULL)
@@ -407,33 +1152,45 @@ register int *states;
if (*thisname != '\0')
{
/* retrieve the value and remember it */
- value = *states++;
+ 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% */
- printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
- i++ == 0 ? "" : ", ",
- ((float)value)/10.,
- thisname);
+ 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));
}
-u_cpustates(states)
-
-register int *states;
+void
+u_cpustates(int *states)
{
- register int value;
- register char **names = cpustate_names;
- register char *thisname;
- register int *lp;
- register int *colp;
+ int value;
+ char **names = cpustate_names;
+ char *thisname;
+ int *lp;
+ int *colp;
+ int color = 0;
+#ifdef ENABLE_COLOR
+ int *cidx = cpustate_cidx;
+#endif
- Move_to(cpustates_column, y_cpustates);
- lastline = y_cpustates;
lp = lcpustates;
colp = cpustate_columns;
@@ -445,20 +1202,26 @@ register int *states;
/* did the value change since last time? */
if (*lp != *states)
{
- /* yes, move and change */
- Move_to(cpustates_column + *colp, y_cpustates);
- lastline = y_cpustates;
-
+ /* 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% */
- printf((value >= 1000 ? "%4.0f" : "%4.1f"),
- ((double)value)/10.);
+ 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 */
@@ -468,6 +1231,7 @@ register int *states;
}
}
+void
z_cpustates()
{
@@ -476,15 +1240,15 @@ z_cpustates()
register char *thisname;
register int *lp;
- /* show tag and bump lastline */
- printf("\n%s", cpustates_tag());
- lastline++;
+ /* print tag */
+ display_write(0, y_cpustates, 0, 0, cpustates_tag());
while ((thisname = *names++) != NULL)
{
if (*thisname != '\0')
{
- printf("%s %% %s", i++ == 0 ? "" : ", ", thisname);
+ display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ",
+ thisname);
}
}
@@ -498,37 +1262,90 @@ z_cpustates()
}
/*
- * *_memory(stats) - print "Memory: " followed by the memory summary string
+ * *_kernel(stats) - print "Kernel: " followed by the kernel summary string
*
- * Assumptions: cursor is on "lastline"
- * for i_memory ONLY: cursor is on the previous line
+ * Assumptions: cursor is on "lastline", the previous line
*/
-char memory_buffer[MAX_COLS];
+void
+i_kernel(int *stats)
+
+{
+ if (num_kernel > 0)
+ {
+ display_write(0, y_kernel, 0, 0, "Kernel: ");
+
+ /* format and print the kernel summary */
+ summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
+ }
+}
-i_memory(stats)
+void
+u_kernel(int *stats)
-int *stats;
+{
+ if (num_kernel > 0)
+ {
+ /* format the new line */
+ summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
+ }
+}
+
+/*
+ * *_memory(stats) - print "Memory: " followed by the memory summary string
+ *
+ * Assumptions: cursor is on "lastline", the previous line
+ */
+
+void
+i_memory(long *stats)
{
- fputs("\nMemory: ", stdout);
- lastline++;
+ display_write(0, y_mem, 0, 0, "Memory: ");
/* format and print the memory summary */
- summary_format(memory_buffer, stats, memory_names);
- fputs(memory_buffer, stdout);
+ 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);
}
-u_memory(stats)
+/*
+ * *_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
+ */
-int *stats;
+void
+i_swap(long *stats)
{
- static char new[MAX_COLS];
+ if (num_swap > 0)
+ {
+ /* print the tag */
+ display_write(0, y_swap, 0, 0, "Swap: ");
- /* format the new line */
- summary_format(new, stats, memory_names);
- line_update(memory_buffer, new, x_mem, y_mem);
+ /* 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);
+ }
}
/*
@@ -542,39 +1359,89 @@ int *stats;
/*
* i_message is funny because it gets its message asynchronously (with
- * respect to screen updates).
+ * respect to screen updates). Messages are taken out of the
+ * circular message_buf and displayed one at a time.
*/
-static char next_msg[MAX_COLS + 5];
-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). */
-
-i_message()
+void
+i_message(struct timeval *now)
{
- while (lastline < y_message)
- {
- fputc('\n', stdout);
- lastline++;
- }
- if (next_msg[0] != '\0')
+ struct timeval my_now;
+ int i = 0;
+
+ dprintf("i_message(%08x)\n", now);
+
+ /* if now is NULL we have to get it ourselves */
+ if (now == NULL)
{
- standout(next_msg);
- msglen = strlen(next_msg);
- next_msg[0] = '\0';
+ time_get(&my_now);
+ now = &my_now;
}
- else if (msglen > 0)
+
+ /* now that we have been called, messages no longer need to be held */
+ message_hold = 0;
+
+ dprintf("i_message: now %d, message_time %d\n",
+ now->tv_sec, message_time.tv_sec);
+
+ if (smart_terminal)
{
- (void) clear_eol(msglen);
- msglen = 0;
+ /* is it time to change the message? */
+ if (timercmp(now, &message_time, > ))
+ {
+ /* yes, free the current message */
+ dprintf("i_message: timer expired\n");
+ if (message_current != NULL)
+ {
+ free(message_current);
+ message_current = NULL;
+ }
+
+ /* is there a new message to be displayed? */
+ if (message_first != message_last)
+ {
+ /* move index to next message */
+ if (++message_first == MAX_MESSAGES) message_first = 0;
+
+ /* make the next message the current one */
+ message_current = message_buf[message_first];
+
+ /* show it */
+ dprintf("i_message: showing \"%s\"\n", message_current);
+ display_move(0, y_message);
+ screen_standout(message_current);
+ i = strlen(message_current);
+
+ /* set the expiration timer */
+ message_time = *now;
+ message_time.tv_sec += MESSAGE_DISPLAY_TIME;
+
+ /* clear the rest of the line */
+ screen_cleareol(message_length - i);
+ putchar('\r');
+ message_length = i;
+ }
+ else
+ {
+ /* just clear what was there before, if anything */
+ if (message_length > 0)
+ {
+ display_move(0, y_message);
+ screen_cleareol(message_length);
+ putchar('\r');
+ message_length = 0;
+ }
+ }
+ }
}
}
-u_message()
+void
+u_message(struct timeval *now)
{
- i_message();
+ i_message(now);
}
static int header_length;
@@ -585,37 +1452,34 @@ static int header_length;
* Assumptions: cursor is on the previous line and lastline is consistent
*/
-i_header(text)
-
-char *text;
+void
+i_header(char *text)
{
+ int header_color = 0;
+
+#ifdef ENABLE_COLOR
+ header_color = color_test(header_cidx, 0);
+#endif
header_length = strlen(text);
- if (header_status == ON)
+ if (header_status)
{
- putchar('\n');
- fputs(text, stdout);
- lastline++;
- }
- else if (header_status == ERASE)
- {
- header_status = OFF;
+ display_write(x_header, y_header, header_color, 1, text);
}
}
/*ARGSUSED*/
-u_header(text)
-
-char *text; /* ignored */
+void
+u_header(char *text)
{
- if (header_status == ERASE)
- {
- putchar('\n');
- lastline++;
- clear_eol(header_length);
- header_status = OFF;
- }
+ int header_color = 0;
+
+#ifdef ENABLE_COLOR
+ header_color = color_test(header_cidx, 0);
+#endif
+ display_write(x_header, y_header, header_color, 1,
+ header_status ? text : "");
}
/*
@@ -624,135 +1488,53 @@ char *text; /* ignored */
* Assumptions: lastline is consistent
*/
-i_process(line, thisline)
-
-int line;
-char *thisline;
+void
+i_process(int line, char *thisline)
{
- register char *p;
- register char *base;
-
- /* make sure we are on the correct line */
- while (lastline < y_procs + line)
- {
- putchar('\n');
- lastline++;
- }
-
/* truncate the line to conform to our current screen width */
thisline[display_width] = '\0';
/* write the line out */
- fputs(thisline, stdout);
-
- /* copy it in to our buffer */
- base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
- p = strecpy(base, thisline);
-
- /* zero fill the rest of it */
- memzero(p, display_width - (p - base));
+ display_write(0, y_procs + line, 0, 1, thisline);
}
-u_process(line, newline)
-
-int line;
-char *newline;
+void
+u_process(int line, char *new_line)
{
- register char *optr;
- register int screen_line = line + Header_lines;
- register char *bufferline;
-
- /* remember a pointer to the current line in the screen buffer */
- bufferline = &screenbuf[lineindex(line)];
+ i_process(line, new_line);
+}
- /* truncate the line to conform to our current screen width */
- newline[display_width] = '\0';
+void
+i_endscreen()
- /* is line higher than we went on the last display? */
- if (line >= last_hi)
+{
+ if (smart_terminal)
{
- /* yes, just ignore screenbuf and write it out directly */
- /* get positioned on the correct line */
- if (screen_line - lastline == 1)
- {
- putchar('\n');
- lastline++;
- }
- else
- {
- Move_to(0, screen_line);
- lastline = screen_line;
- }
-
- /* now write the line */
- fputs(newline, stdout);
-
- /* copy it in to the buffer */
- optr = strecpy(bufferline, newline);
-
- /* zero fill the rest of it */
- memzero(optr, display_width - (optr - bufferline));
+ /* move the cursor to a pleasant place */
+ display_move(x_idlecursor, y_idlecursor);
}
else
{
- line_update(bufferline, newline, 0, line + Header_lines);
+ /* separate this display from the next with some vertical room */
+ fputs("\n\n", stdout);
}
+ fflush(stdout);
}
-u_endscreen(hi)
-
-register int hi;
+void
+u_endscreen()
{
- register int screen_line = hi + Header_lines;
- register int i;
-
if (smart_terminal)
{
- if (hi < last_hi)
- {
- /* need to blank the remainder of the screen */
- /* but only if there is any screen left below this line */
- if (lastline + 1 < screen_length)
- {
- /* efficiently move to the end of currently displayed info */
- if (screen_line - lastline < 5)
- {
- while (lastline < screen_line)
- {
- putchar('\n');
- lastline++;
- }
- }
- else
- {
- Move_to(0, screen_line);
- lastline = screen_line;
- }
-
- if (clear_to_end)
- {
- /* we can do this the easy way */
- putcap(clear_to_end);
- }
- else
- {
- /* use clear_eol on each line */
- i = hi;
- while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
- {
- putchar('\n');
- }
- }
- }
- }
- last_hi = hi;
+ /* clear-to-end the display */
+ display_cte();
/* move the cursor to a pleasant place */
- Move_to(x_idlecursor, y_idlecursor);
- lastline = y_idlecursor;
+ display_move(x_idlecursor, y_idlecursor);
+ fflush(stdout);
}
else
{
@@ -761,82 +1543,244 @@ register int hi;
}
}
-display_header(t)
+void
+display_header(int t)
-int t;
+{
+ header_status = t != 0;
+}
+
+void
+message_mark()
{
- if (t)
- {
- header_status = ON;
- }
- else if (header_status == ON)
- {
- header_status = ERASE;
- }
+ message_barrier = Yes;
}
-/*VARARGS2*/
-new_message(type, msgfmt, a1, a2, a3)
+void
+message_expire()
-int type;
-char *msgfmt;
-caddr_t a1, a2, a3;
+{
+ message_time.tv_sec = 0;
+ message_time.tv_usec = 0;
+}
+
+void
+message_flush()
{
- register int i;
+ message_first = message_last;
+ message_time.tv_sec = 0;
+ message_time.tv_usec = 0;
+}
+
+/*
+ * void new_message_v(char *msgfmt, va_list ap)
+ *
+ * Display a message in the message area. This function takes a va_list for
+ * the arguments. Safe to call before display_init. This function only
+ * queues a message for display, and allowed for multiple messages to be
+ * queued. The i_message function drains the queue and actually writes the
+ * messages on the display.
+ */
+
+
+void
+new_message_v(char *msgfmt, va_list ap)
+
+{
+ int i;
+ int empty;
+ char msg[MAX_COLS];
+
+ /* if message_barrier is active, remove all pending messages */
+ if (message_barrier)
+ {
+ message_flush();
+ message_barrier = No;
+ }
/* first, format the message */
- (void) sprintf(next_msg, msgfmt, a1, a2, a3);
+ (void) vsnprintf(msg, sizeof(msg), msgfmt, ap);
+
+ /* where in the buffer will it go? */
+ i = message_last + 1;
+ if (i >= MAX_MESSAGES) i = 0;
- if (msglen > 0)
+ /* make sure the buffer is not full */
+ if (i != message_first)
{
- /* message there already -- can we clear it? */
- if (!overstrike)
+ /* insert it in to message_buf */
+ message_buf[i] = strdup(msg);
+ dprintf("new_message_v: new message inserted in slot %d\n", i);
+
+ /* remember if the buffer is empty and set the index */
+ empty = message_last == message_first;
+ message_last = i;
+
+ /* is message_buf otherwise empty and have we started displaying? */
+ if (empty && !message_hold)
{
- /* yes -- write it and clear to end */
- i = strlen(next_msg);
- if ((type & MT_delayed) == 0)
- {
- type & MT_standout ? standout(next_msg) :
- fputs(next_msg, stdout);
- (void) clear_eol(msglen - i);
- msglen = i;
- next_msg[0] = '\0';
- }
+ /* we can display the message now */
+ i_message(NULL);
}
}
- else
+}
+
+/*
+ * void new_message(int type, char *msgfmt, ...)
+ *
+ * Display a message in the message area. It is safe to call this function
+ * before display_init. Messages logged before the display is drawn will be
+ * held and displayed later.
+ */
+
+void
+new_message(char *msgfmt, ...)
+
+{
+ va_list ap;
+
+ va_start(ap, msgfmt);
+ new_message_v(msgfmt, ap);
+ va_end(ap);
+}
+
+/*
+ * void message_error(char *msgfmt, ...)
+ *
+ * Put an error message in the message area. It is safe to call this function
+ * before display_init. Messages logged before the display is drawn will be
+ * held and displayed later.
+ */
+
+void
+message_error(char *msgfmt, ...)
+
+{
+ va_list ap;
+
+ va_start(ap, msgfmt);
+ new_message_v(msgfmt, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+/*
+ * void message_clear()
+ *
+ * Clear message area and flush all pending messages.
+ */
+
+void
+message_clear()
+
+{
+ /* remove any existing message */
+ if (message_current != NULL)
{
- if ((type & MT_delayed) == 0)
- {
- type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
- msglen = strlen(next_msg);
- next_msg[0] = '\0';
- }
+ display_move(0, y_message);
+ screen_cleareol(message_length);
+ free(message_current);
+ message_current = 0;
}
+
+ /* flush all pending messages */
+ message_flush();
}
-clear_message()
+/*
+ * void message_prompt_v(int so, char *msgfmt, va_list ap)
+ *
+ * Place a prompt in the message area. A prompt is different from a
+ * message as follows: it is displayed immediately, overwriting any
+ * message that may already be there, it may be highlighted in standout
+ * mode (if "so" is true), the cursor is left to rest at the end of the
+ * prompt. This call causes all pending messages to be flushed.
+ */
+
+void
+message_prompt_v(int so, char *msgfmt, va_list ap)
{
- if (clear_eol(msglen) == 1)
+ char msg[MAX_COLS];
+ int i;
+
+ /* clear out the message buffer */
+ message_flush();
+
+ /* format the message */
+ i = vsnprintf(msg, sizeof(msg), msgfmt, ap);
+
+ /* this goes over any existing message */
+ display_move(0, y_message);
+
+ /* clear the entire line */
+ screen_cleareol(message_length);
+
+ /* show the prompt */
+ if (so)
+ {
+ screen_standout(msg);
+ }
+ else
{
- putchar('\r');
+ fputs(msg, stdout);
}
+
+ /* make it all visible */
+ fflush(stdout);
+
+ /* even though we dont keep a copy of the prompt, track its length */
+ message_length = i < MAX_COLS ? i : MAX_COLS;
}
-readline(buffer, size, numeric)
+/*
+ * void message_prompt(char *msgfmt, ...)
+ *
+ * Place a prompt in the message area (see message_prompt_v).
+ */
-char *buffer;
-int size;
-int numeric;
+void
+message_prompt(char *msgfmt, ...)
+
+{
+ va_list ap;
+
+ va_start(ap, msgfmt);
+ message_prompt_v(Yes, msgfmt, ap);
+ va_end(ap);
+}
+
+void
+message_prompt_plain(char *msgfmt, ...)
+
+{
+ va_list ap;
+
+ va_start(ap, msgfmt);
+ message_prompt_v(No, msgfmt, ap);
+ va_end(ap);
+}
+
+/*
+ * int readline(char *buffer, int size, int numeric)
+ *
+ * Read a line of input from the terminal. The line is placed in
+ * "buffer" not to exceed "size". If "numeric" is true then the input
+ * can only consist of digits. This routine handles all character
+ * editing while keeping the terminal in cbreak mode. If "numeric"
+ * is true then the number entered is returned. Otherwise the number
+ * of character read in to "buffer" is returned.
+ */
+
+int
+readline(char *buffer, int size, int numeric)
{
register char *ptr = buffer;
register char ch;
register char cnt = 0;
- register char maxcnt = 0;
/* allow room for null terminator */
size -= 1;
@@ -844,8 +1788,8 @@ int numeric;
/* read loop */
while ((fflush(stdout), read(0, ptr, 1) > 0))
{
- /* newline means we are done */
- if ((ch = *ptr) == '\n')
+ /* newline or return means we are done */
+ if ((ch = *ptr) == '\n' || ch == '\r')
{
break;
}
@@ -853,17 +1797,39 @@ int numeric;
/* handle special editing characters */
if (ch == ch_kill)
{
- /* kill line -- account for overstriking */
- if (overstrike)
- {
- msglen += maxcnt;
- }
-
/* return null string */
*buffer = '\0';
putchar('\r');
return(-1);
}
+ else if (ch == ch_werase)
+ {
+ /* erase previous word */
+ if (cnt <= 0)
+ {
+ /* none to erase! */
+ putchar('\7');
+ }
+ else
+ {
+ /*
+ * First: remove all spaces till the first-non-space
+ * Second: remove all non-spaces till the first-space
+ */
+ while(cnt > 0 && ptr[-1] == ' ')
+ {
+ fputs("\b \b", stdout);
+ ptr--;
+ cnt--;
+ }
+ while(cnt > 0 && ptr[-1] != ' ')
+ {
+ fputs("\b \b", stdout);
+ ptr--;
+ cnt--;
+ }
+ }
+ }
else if (ch == ch_erase)
{
/* erase previous character */
@@ -880,8 +1846,8 @@ int numeric;
}
}
/* check for character validity and buffer overflow */
- else if (cnt == size || (numeric && !isdigit(ch)) ||
- !isprint(ch))
+ else if (cnt == size || (numeric && !isdigit((int)ch)) ||
+ !isprint((int)ch))
{
/* not legal */
putchar('\7');
@@ -892,238 +1858,80 @@ int numeric;
putchar(ch);
ptr++;
cnt++;
- if (cnt > maxcnt)
- {
- maxcnt = cnt;
- }
}
}
/* all done -- null terminate the string */
*ptr = '\0';
- /* account for the extra characters in the message area */
- /* (if terminal overstrikes, remember the furthest they went) */
- msglen += overstrike ? maxcnt : cnt;
+ /* add response length to message_length */
+ message_length += cnt;
/* return either inputted number or string length */
putchar('\r');
return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
}
-/* internal support routines */
-
-static int string_count(pp)
-
-register char **pp;
+void
+display_pagerstart()
{
- register int cnt;
-
- cnt = 0;
- while (*pp++ != NULL)
- {
- cnt++;
- }
- return(cnt);
+ display_clear();
}
-static void summary_format(str, numbers, names)
-
-char *str;
-int *numbers;
-register char **names;
+void
+display_pagerend()
{
- register char *p;
- register int num;
- register char *thisname;
- register int useM = No;
-
- /* format each number followed by its string */
- p = str;
- while ((thisname = *names++) != NULL)
- {
- /* get the number to format */
- num = *numbers++;
-
- /* display only non-zero numbers */
- if (num > 0)
- {
- /* is this number in kilobytes? */
- if (thisname[0] == 'K')
- {
- /* yes: format it as a memory value */
- p = strecpy(p, format_k(num));
-
- /* skip over the K, since it was included by format_k */
- p = strecpy(p, thisname+1);
- }
- else
- {
- p = strecpy(p, itoa(num));
- p = strecpy(p, thisname);
- }
- }
+ char ch;
- /* ignore negative numbers, but display corresponding string */
- else if (num < 0)
- {
- p = strecpy(p, thisname);
- }
- }
-
- /* if the last two characters in the string are ", ", delete them */
- p -= 2;
- if (p >= str && p[0] == ',' && p[1] == ' ')
- {
- *p = '\0';
- }
+ screen_standout("Hit any key to continue: ");
+ fflush(stdout);
+ (void) read(0, &ch, 1);
}
-static void line_update(old, new, start, line)
-
-register char *old;
-register char *new;
-int start;
-int line;
+void
+display_pager(char *fmt, ...)
{
- register int ch;
- register int diff;
- register int newcol = start + 1;
- register int lastcol = start;
- char cursor_on_line = No;
- char *current;
-
- /* compare the two strings and only rewrite what has changed */
- current = old;
-#ifdef DEBUG
- fprintf(debug, "line_update, starting at %d\n", start);
- fputs(old, debug);
- fputc('\n', debug);
- fputs(new, debug);
- fputs("\n-\n", debug);
-#endif
+ va_list ap;
+
+ int ch;
+ char readch;
+ char buffer[MAX_COLS];
+ char *data;
- /* start things off on the right foot */
- /* this is to make sure the invariants get set up right */
- if ((ch = *new++) != *old)
+ /* format into buffer */
+ va_start(ap, fmt);
+ (void) vsnprintf(buffer, MAX_COLS, fmt, ap);
+ va_end(ap);
+ data = buffer;
+
+ while ((ch = *data++) != '\0')
{
- if (line - lastline == 1 && start == 0)
- {
- putchar('\n');
- }
- else
- {
- Move_to(start, line);
- }
- cursor_on_line = Yes;
putchar(ch);
- *old = ch;
- lastcol = 1;
- }
- old++;
-
- /*
- * main loop -- check each character. If the old and new aren't the
- * same, then update the display. When the distance from the
- * current cursor position to the new change is small enough,
- * the characters that belong there are written to move the
- * cursor over.
- *
- * Invariants:
- * lastcol is the column where the cursor currently is sitting
- * (always one beyond the end of the last mismatch).
- */
- do /* yes, a do...while */
- {
- if ((ch = *new++) != *old)
+ if (ch == '\n')
{
- /* new character is different from old */
- /* make sure the cursor is on top of this character */
- diff = newcol - lastcol;
- if (diff > 0)
+ if (++curr_y >= screen_length - 1)
{
- /* some motion is required--figure out which is shorter */
- if (diff < 6 && cursor_on_line)
+ screen_standout("...More...");
+ fflush(stdout);
+ (void) read(0, &readch, 1);
+ putchar('\r');
+ switch(readch)
{
- /* overwrite old stuff--get it out of the old buffer */
- printf("%.*s", diff, &current[lastcol-start]);
- }
- else
- {
- /* use cursor addressing */
- Move_to(newcol, line);
- cursor_on_line = Yes;
- }
- /* remember where the cursor is */
- lastcol = newcol + 1;
- }
- else
- {
- /* already there, update position */
- lastcol++;
- }
-
- /* write what we need to */
- if (ch == '\0')
- {
- /* at the end--terminate with a clear-to-end-of-line */
- (void) clear_eol(strlen(old));
- }
- else
- {
- /* write the new character */
- putchar(ch);
- }
- /* put the new character in the screen buffer */
- *old = ch;
- }
-
- /* update working column and screen buffer pointer */
- newcol++;
- old++;
-
- } while (ch != '\0');
-
- /* zero out the rest of the line buffer -- MUST BE DONE! */
- diff = display_width - newcol;
- if (diff > 0)
- {
- memzero(old, diff);
- }
-
- /* remember where the current line is */
- if (cursor_on_line)
- {
- lastline = line;
- }
-}
-
-/*
- * printable(str) - make the string pointed to by "str" into one that is
- * printable (i.e.: all ascii), by converting all non-printable
- * characters into '?'. Replacements are done in place and a pointer
- * to the original buffer is returned.
- */
-
-char *printable(str)
+ case '\r':
+ case '\n':
+ curr_y--;
+ break;
-char *str;
-
-{
- register char *ptr;
- register char ch;
+ case 'q':
+ return;
- ptr = str;
- while ((ch = *ptr) != '\0')
- {
- if (!isprint(ch))
- {
- *ptr = '?';
+ default:
+ curr_y = 0;
+ }
+ }
}
- ptr++;
}
- return(str);
}