diff options
Diffstat (limited to 'display.c')
-rw-r--r-- | display.c | 2030 |
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, ¤t[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); } |