Changeset View
Changeset View
Standalone View
Standalone View
contrib/top/display.c
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/* | /* | ||||
* Top users/processes display for Unix | * Copyright (c) 1984 through 2008, William LeFebvre | ||||
* Version 3 | * All rights reserved. | ||||
* | * | ||||
* This program may be freely redistributed, | * Redistribution and use in source and binary forms, with or without | ||||
* but this entire comment MUST remain intact. | * modification, are permitted provided that the following conditions are met: | ||||
* | * | ||||
* Copyright (c) 1984, 1989, William LeFebvre, Rice University | * * Redistributions of source code must retain the above copyright | ||||
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University | * 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. | |||||
* | |||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
/* | /* | ||||
* Top users/processes display for Unix | |||||
* Version 3 | |||||
*/ | |||||
/* | |||||
* This file contains the routines that display information on the screen. | * This file contains the routines that display information on the screen. | ||||
* Each section of the screen has two routines: one for initially writing | * Each section of the screen has two routines: one for initially writing | ||||
* all constant and dynamic text, and one for only updating the text that | * all constant and dynamic text, and one for only updating the text that | ||||
* changes. The prefix "i_" is used on all the "initial" routines and the | * changes. The prefix "i_" is used on all the "initial" routines and the | ||||
* prefix "u_" is used for all the "updating" routines. | * prefix "u_" is used for all the "updating" routines. | ||||
* | * | ||||
* ASSUMPTIONS: | * ASSUMPTIONS: | ||||
* None of the "i_" routines use any of the termcap capabilities. | * None of the "i_" routines use any of the termcap capabilities. | ||||
* In this way, those routines can be safely used on terminals that | * In this way, those routines can be safely used on terminals that | ||||
* have minimal (or nonexistant) terminal capabilities. | * have minimal (or nonexistant) terminal capabilities. | ||||
* | * | ||||
* The routines are called in this order: *_loadave, i_timeofday, | * The routines should be called in this order: *_loadave, *_uptime, | ||||
* *_procstates, *_cpustates, *_memory, *_message, *_header, | * i_timeofday, *_procstates, *_cpustates, *_memory, *_swap, | ||||
* *_process, u_endscreen. | * *_message, *_header, *_process, *_endscreen. | ||||
*/ | */ | ||||
#include "os.h" | #include "os.h" | ||||
#include <sys/time.h> | |||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <time.h> | #include <stdarg.h> | ||||
#include <sys/types.h> | |||||
#include <sys/uio.h> | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#include "top.h" | |||||
#include "machine.h" | |||||
#include "screen.h" /* interface to screen package */ | #include "screen.h" /* interface to screen package */ | ||||
#include "layout.h" /* defines for screen position layout */ | #include "layout.h" /* defines for screen position layout */ | ||||
#include "display.h" | #include "display.h" | ||||
#include "top.h" | |||||
#include "top.local.h" | |||||
#include "boolean.h" | #include "boolean.h" | ||||
#include "machine.h" /* we should eliminate this!!! */ | |||||
#include "utils.h" | #include "utils.h" | ||||
#ifdef DEBUG | #ifdef ENABLE_COLOR | ||||
FILE *debug; | #include "color.h" | ||||
#endif | #endif | ||||
#define CURSOR_COST 8 | |||||
#define MESSAGE_DISPLAY_TIME 5 | |||||
/* imported from screen.c */ | /* imported from screen.c */ | ||||
extern int overstrike; | extern int overstrike; | ||||
static int lmpid = 0; | static int lmpid = -1; | ||||
static int last_hi = 0; /* used in u_process and u_endscreen */ | |||||
static int lastline = 0; | |||||
static int display_width = MAX_COLS; | static int display_width = MAX_COLS; | ||||
#define lineindex(l) ((l)*display_width) | /* 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_arc = X_ARC; | |||||
static int y_arc = Y_ARC; | |||||
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; | |||||
/* things initialized by display_init and used thruout */ | /* 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; | |||||
/* buffer of proc information lines for display updating */ | /* lineindex tells us where the beginning of a line is in the buffer */ | ||||
char *screenbuf = NULL; | #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 **procstate_names; | ||||
static char **cpustate_names; | static char **cpustate_names; | ||||
static char **memory_names; | static char **memory_names; | ||||
static char **arc_names; | |||||
static char **swap_names; | static char **swap_names; | ||||
static char **kernel_names; | |||||
static char **arc_names; | |||||
static int num_procstates; | static int num_procstates; | ||||
static int num_cpustates; | static int num_cpustates; | ||||
static int num_memory; | static int num_memory; | ||||
static int num_swap; | static int num_swap; | ||||
static int num_kernel; | |||||
static int num_arc; | |||||
static int *lprocstates; | static int *lprocstates; | ||||
static int *lcpustates; | static int *lcpustates; | ||||
static int *lmemory; | |||||
static int *lswap; | |||||
static int num_cpus; | |||||
static int *cpustate_columns; | static int *cpustate_columns; | ||||
static int cpustate_total_length; | static int cpustate_total_length; | ||||
static int cpustates_column; | |||||
static enum { OFF, ON, ERASE } header_status = ON; | static int header_status = Yes; | ||||
static int string_count(); | /* pending messages are stored in a circular buffer, where message_first | ||||
static void summary_format(); | is the next one to display, and message_last is the last one | ||||
static void line_update(); | 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; | |||||
int x_lastpid = 10; | #ifdef ENABLE_COLOR | ||||
int y_lastpid = 0; | static int load_cidx[3]; | ||||
int x_loadave = 33; | static int header_cidx; | ||||
int x_loadave_nompid = 15; | static int *cpustate_cidx; | ||||
int y_loadave = 0; | static int *memory_cidx; | ||||
int x_procstate = 0; | static int *swap_cidx; | ||||
int y_procstate = 1; | static int *kernel_cidx; | ||||
int x_brkdn = 15; | static int *arc_cidx; | ||||
int y_brkdn = 1; | #else | ||||
int x_mem = 5; | #define memory_cidx NULL | ||||
int y_mem = 3; | #define swap_cidx NULL | ||||
int x_arc = 5; | #define kernel_cidx NULL | ||||
int y_arc = 4; | #define arc_cidx NULL | ||||
int x_swap = 6; | #endif | ||||
int y_swap = 4; | |||||
int y_message = 5; | |||||
int x_header = 0; | |||||
int y_header = 6; | |||||
int x_idlecursor = 0; | |||||
int y_idlecursor = 5; | |||||
int y_procs = 7; | |||||
int y_cpustates = 2; | |||||
int Header_lines = 7; | |||||
int display_resize() | /* 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 lines; | register int cnt = 0; | ||||
/* first, deallocate any previous buffer that may have been there */ | if (pp != NULL) | ||||
if (screenbuf != NULL) | |||||
{ | { | ||||
free(screenbuf); | 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); | |||||
} | |||||
/* update our position */ | |||||
curr_x = x; | |||||
curr_y = y; | |||||
} | |||||
/* | |||||
* display_write(int x, int y, int newcolor, int eol, char *new) | |||||
* | |||||
* Optimized write to the display. This writes characters to the | |||||
* screen in a way that optimizes the number of characters actually | |||||
* sent, by comparing what is being written to what is already on | |||||
* the screen (according to screenbuf and colorbuf). The string to | |||||
* write is "new", the first character of "new" should appear at | |||||
* screen position x, y. If x is -1 then "new" begins wherever the | |||||
* cursor is currently positioned. The string is written with color | |||||
* "newcolor". If "eol" is true then the remainder of the line is | |||||
* cleared. It is expected that "new" will have no newlines and no | |||||
* escape sequences. | |||||
*/ | |||||
void | |||||
display_write(int x, int y, int newcolor, int eol, char *new) | |||||
{ | |||||
char *bufp; | |||||
char *colorp; | |||||
int ch; | |||||
int diff; | |||||
dprintf("display_write(%d, %d, %d, %d, \"%s\")\n", | |||||
x, y, newcolor, eol, new); | |||||
/* dumb terminal handling here */ | |||||
if (!smart_terminal) | |||||
{ | |||||
if (x != -1) | |||||
{ | |||||
/* make sure we are on the right line */ | |||||
while (curr_y < y) | |||||
{ | |||||
putchar('\n'); | |||||
curr_y++; | |||||
curr_x = 0; | |||||
} | |||||
/* make sure we are on the right column */ | |||||
while (curr_x < x) | |||||
{ | |||||
putchar(' '); | |||||
curr_x++; | |||||
} | |||||
} | |||||
/* write */ | |||||
fputs(new, stdout); | |||||
curr_x += strlen(new); | |||||
return; | |||||
} | |||||
/* adjust for "here" */ | |||||
if (x == -1) | |||||
{ | |||||
x = virt_x; | |||||
y = virt_y; | |||||
} | |||||
else | |||||
{ | |||||
virt_x = x; | |||||
virt_y = y; | |||||
} | |||||
/* a pointer to where we start */ | |||||
bufp = &screenbuf[lineindex(y) + x]; | |||||
colorp = &colorbuf[lineindex(y) + x]; | |||||
/* main loop */ | |||||
while ((ch = *new++) != '\0') | |||||
{ | |||||
/* if either character or color are different, an update is needed */ | |||||
/* but only when the screen is wide enough */ | |||||
if (x < display_width && (ch != *bufp || newcolor != *colorp)) | |||||
{ | |||||
/* check cursor */ | |||||
if (y != curr_y || x != curr_x) | |||||
{ | |||||
/* have to move the cursor */ | |||||
display_move(x, y); | |||||
} | |||||
/* write character */ | |||||
#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 */ | /* calculate the current dimensions */ | ||||
/* if operating in "dumb" mode, we only need one line */ | /* 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; | ||||
if (lines < 0) | |||||
lines = 0; | |||||
/* we don't want more than MAX_COLS columns, since the machine-dependent | /* 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 | modules make static allocations based on MAX_COLS and we don't want | ||||
to run off the end of their buffers */ | to run off the end of their buffers */ | ||||
display_width = screen_width; | display_width = screen_width; | ||||
if (display_width >= MAX_COLS) | if (display_width >= MAX_COLS) | ||||
{ | { | ||||
display_width = MAX_COLS - 1; | display_width = MAX_COLS - 1; | ||||
} | } | ||||
/* now, allocate space for the screen buffer */ | /* see how much space we need */ | ||||
screenbuf = (char *)malloc(lines * display_width); | newsize = top_lines * (MAX_COLS + 1); | ||||
if (screenbuf == (char *)NULL) | |||||
/* 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! */ | /* oops! */ | ||||
return(-1); | return(-1); | ||||
} | } | ||||
} | |||||
else | |||||
{ | |||||
/* 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 */ | /* return number of lines available */ | ||||
/* for dumb terminals, pretend like we can show any amount */ | /* for dumb terminals, pretend like we can show any amount */ | ||||
return(smart_terminal ? lines : Largest); | return(smart_terminal ? top_lines : Largest); | ||||
} | } | ||||
int display_updatecpus(statics) | int | ||||
display_lines() | |||||
struct statics *statics; | |||||
{ | { | ||||
register int *lp; | return(smart_terminal ? screen_length : Largest); | ||||
register int lines; | } | ||||
register int i; | |||||
/* call resize to do the dirty work */ | int | ||||
lines = display_resize(); | display_columns() | ||||
if (pcpu_stats) | |||||
num_cpus = statics->ncpus; | |||||
else | |||||
num_cpus = 1; | |||||
cpustates_column = 5; /* CPU: */ | |||||
if (num_cpus != 1) | |||||
cpustates_column += 2; /* CPU 0: */ | |||||
for (i = num_cpus; i > 9; i /= 10) | |||||
cpustates_column++; | |||||
/* fill the "last" array with all -1s, to insure correct updating */ | |||||
lp = lcpustates; | |||||
i = num_cpustates * num_cpus; | |||||
while (--i >= 0) | |||||
{ | { | ||||
*lp++ = -1; | return(display_width); | ||||
} | } | ||||
return(lines); | /* | ||||
} | * int display_init(struct statics *statics) | ||||
* | |||||
* Initialize the display system based on information in the statics | |||||
* structure. Returns the number of lines available for displaying | |||||
* processes or -1 if there was an error. | |||||
*/ | |||||
int display_init(statics) | int | ||||
display_init(struct statics *statics) | |||||
struct statics *statics; | |||||
{ | { | ||||
register int lines; | register int top_lines; | ||||
register char **pp; | register char **pp; | ||||
register char *p; | |||||
register int *ip; | register int *ip; | ||||
register int i; | register int i; | ||||
lines = display_updatecpus(statics); | /* 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_arc++; | |||||
y_swap++; | |||||
y_message++; | |||||
y_header++; | |||||
y_idlecursor++; | |||||
y_procs++; | |||||
} | |||||
/* An ARC line shifts parts of the display down. */ | |||||
arc_names = statics->arc_names; | |||||
if ((num_arc = string_count(arc_names)) > 0) | |||||
{ | |||||
/* Adjust screen placements. */ | |||||
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 */ | |||||
top_lines = display_resize(); | |||||
/* only do the rest if we need to */ | /* only do the rest if we need to */ | ||||
if (lines > -1) | if (top_lines > -1) | ||||
{ | { | ||||
/* save pointers and allocate space for names */ | /* save pointers and allocate space for names */ | ||||
procstate_names = statics->procstate_names; | procstate_names = statics->procstate_names; | ||||
num_procstates = string_count(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; | cpustate_names = statics->cpustate_names; | ||||
swap_names = statics->swap_names; | |||||
num_swap = string_count(swap_names); | |||||
lswap = (int *)malloc(num_swap * sizeof(int)); | |||||
num_cpustates = string_count(cpustate_names); | num_cpustates = string_count(cpustate_names); | ||||
lcpustates = (int *)malloc(num_cpustates * sizeof(int) * statics->ncpus); | lcpustates = (int *)calloc(num_cpustates, sizeof(int)); | ||||
cpustate_columns = (int *)malloc(num_cpustates * sizeof(int)); | cpustate_columns = (int *)calloc(num_cpustates, sizeof(int)); | ||||
memory_names = statics->memory_names; | memory_names = statics->memory_names; | ||||
num_memory = string_count(memory_names); | num_memory = string_count(memory_names); | ||||
lmemory = (int *)malloc(num_memory * sizeof(int)); | |||||
arc_names = statics->arc_names; | |||||
/* calculate starting columns where needed */ | /* calculate starting columns where needed */ | ||||
cpustate_total_length = 0; | cpustate_total_length = 0; | ||||
pp = cpustate_names; | pp = cpustate_names; | ||||
ip = cpustate_columns; | ip = cpustate_columns; | ||||
while (*pp != NULL) | while (*pp != NULL) | ||||
{ | { | ||||
*ip++ = cpustate_total_length; | *ip++ = cpustate_total_length; | ||||
if ((i = strlen(*pp++)) > 0) | if ((i = strlen(*pp++)) > 0) | ||||
{ | { | ||||
cpustate_total_length += i + 8; | cpustate_total_length += i + 8; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* return number of lines available */ | #ifdef ENABLE_COLOR | ||||
return(lines); | /* 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); | |||||
} | } | ||||
void | /* color tags for kernel */ | ||||
i_loadave(mpid, avenrun) | 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); | |||||
} | |||||
} | |||||
int mpid; | /* color tags for memory */ | ||||
double *avenrun; | 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 ARC */ | |||||
if (num_arc > 0) | |||||
{ | { | ||||
register int i; | arc_cidx = (int *)malloc(num_arc * sizeof(int)); | ||||
i = 0; | |||||
p = strcpyend(scratchbuf, "arc."); | |||||
while (i < num_arc) | |||||
{ | |||||
strcpy(p, homogenize(arc_names[i]+1)); | |||||
arc_cidx[i++] = color_tag(scratchbuf); | |||||
} | |||||
} | |||||
/* i_loadave also clears the screen, since it is first */ | /* color tags for swap */ | ||||
clear(); | 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 | |||||
/* mpid == -1 implies this system doesn't have an _mpid */ | /* return number of lines available (or error) */ | ||||
if (mpid != -1) | return(top_lines); | ||||
} | |||||
static void | |||||
pr_loadavg(double avg, int i) | |||||
{ | { | ||||
printf("last pid: %5d; ", mpid); | 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 ? "," : ";")); | |||||
} | } | ||||
printf("load averages"); | void | ||||
i_loadave(int mpid, double *avenrun) | |||||
{ | |||||
register int i; | |||||
/* mpid == -1 implies this system doesn't have an _mpid */ | |||||
if (mpid != -1) | |||||
{ | |||||
display_fmt(0, 0, 0, 0, | |||||
"last pid: %5d; load avg:", mpid); | |||||
x_loadave = X_LOADAVE; | |||||
} | |||||
else | |||||
{ | |||||
display_write(0, 0, 0, 0, "load averages:"); | |||||
x_loadave = X_LOADAVE - X_LASTPIDWIDTH; | |||||
} | |||||
for (i = 0; i < 3; i++) | for (i = 0; i < 3; i++) | ||||
{ | { | ||||
printf("%c %5.2f", | pr_loadavg(avenrun[i], i); | ||||
i == 0 ? ':' : ',', | |||||
avenrun[i]); | |||||
} | } | ||||
lmpid = mpid; | lmpid = mpid; | ||||
} | } | ||||
void | void | ||||
u_loadave(mpid, avenrun) | u_loadave(int mpid, double *avenrun) | ||||
int mpid; | |||||
double *avenrun; | |||||
{ | { | ||||
register int i; | register int i; | ||||
if (mpid != -1) | if (mpid != -1) | ||||
{ | { | ||||
/* change screen only when value has really changed */ | /* change screen only when value has really changed */ | ||||
if (mpid != lmpid) | if (mpid != lmpid) | ||||
{ | { | ||||
Move_to(x_lastpid, y_lastpid); | display_fmt(x_lastpid, y_lastpid, 0, 0, | ||||
printf("%5d", mpid); | "%5d", mpid); | ||||
lmpid = mpid; | lmpid = mpid; | ||||
} | } | ||||
} | |||||
/* i remembers x coordinate to move to */ | /* display new load averages */ | ||||
i = x_loadave; | for (i = 0; i < 3; i++) | ||||
{ | |||||
pr_loadavg(avenrun[i], i); | |||||
} | } | ||||
else | } | ||||
static char minibar_buffer[64]; | |||||
#define MINIBAR_WIDTH 20 | |||||
void | |||||
i_minibar(int (*formatter)(char *, int)) | |||||
{ | { | ||||
i = x_loadave_nompid; | (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); | ||||
display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); | |||||
} | } | ||||
/* move into position for load averages */ | void | ||||
Move_to(i, y_loadave); | u_minibar(int (*formatter)(char *, int)) | ||||
{ | |||||
(void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); | |||||
/* display new load averages */ | display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); | ||||
/* we should optimize this and only display changes */ | } | ||||
for (i = 0; i < 3; i++) | |||||
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) | |||||
{ | { | ||||
printf("%s%5.2f", | time_t uptime; | ||||
i == 0 ? "" : ", ", | |||||
avenrun[i]); | if (*bt != -1) | ||||
{ | |||||
uptime = *tod - *bt; | |||||
uptime += 30; | |||||
uptime_days = uptime / 86400; | |||||
uptime %= 86400; | |||||
uptime_hours = uptime / 3600; | |||||
uptime %= 3600; | |||||
uptime_mins = uptime / 60; | |||||
uptime_secs = uptime % 60; | |||||
/* | |||||
* Display the uptime. | |||||
*/ | |||||
display_fmt(x_uptime, y_uptime, 0, 0, | |||||
" up %d+%02d:%02d:%02d", | |||||
uptime_days, uptime_hours, uptime_mins, uptime_secs); | |||||
} | } | ||||
} | } | ||||
void | void | ||||
i_timeofday(tod) | u_uptime(time_t *bt, time_t *tod) | ||||
time_t *tod; | { | ||||
i_uptime(bt, tod); | |||||
} | |||||
void | |||||
i_timeofday(time_t *tod) | |||||
{ | { | ||||
/* | /* | ||||
* Display the current time. | * Display the current time. | ||||
* "ctime" always returns a string that looks like this: | * "ctime" always returns a string that looks like this: | ||||
* | * | ||||
* Sun Sep 16 01:03:52 1973 | * Sun Sep 16 01:03:52 1973 | ||||
* 012345678901234567890123 | * 012345678901234567890123 | ||||
* 1 2 | * 1 2 | ||||
* | * | ||||
* We want indices 11 thru 18 (length 8). | * We want indices 11 thru 18 (length 8). | ||||
*/ | */ | ||||
if (smart_terminal) | 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) | |||||
{ | { | ||||
Move_to(screen_width - 8, 0); | x = x_uptime + 19; | ||||
} | } | ||||
else | |||||
{ | /* display it */ | ||||
fputs(" ", stdout); | display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11])); | ||||
} | } | ||||
#ifdef DEBUG | |||||
{ | |||||
char *foo; | |||||
foo = ctime(tod); | |||||
fputs(foo, stdout); | |||||
} | |||||
#endif | |||||
printf("%-8.8s\n", &(ctime(tod)[11])); | |||||
lastline = 1; | |||||
} | |||||
static int ltotal = 0; | static int ltotal = 0; | ||||
static char procstates_buffer[MAX_COLS]; | static int lthreads = 0; | ||||
/* | /* | ||||
* *_procstates(total, brkdn, names) - print the process summary line | * *_procstates(total, brkdn, names) - print the process summary line | ||||
* | |||||
* Assumptions: cursor is at the beginning of the line on entry | |||||
* lastline is valid | |||||
*/ | */ | ||||
void | void | ||||
i_procstates(total, brkdn) | i_procstates(int total, int *brkdn, int threads) | ||||
int total; | |||||
int *brkdn; | |||||
{ | { | ||||
register int i; | |||||
/* write current number of processes and remember the value */ | /* 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; | ltotal = total; | ||||
/* put out enough spaces to get to column 15 */ | /* remember where the summary starts */ | ||||
i = digits(total); | x_procstate = virt_x; | ||||
while (i++ < 4) | |||||
{ | |||||
putchar(' '); | |||||
} | |||||
if (total > 0) | |||||
{ | |||||
/* format and print the process state summary */ | /* format and print the process state summary */ | ||||
summary_format(procstates_buffer, brkdn, procstate_names); | summary_format(-1, -1, brkdn, procstate_names, NULL); | ||||
fputs(procstates_buffer, stdout); | |||||
/* save the numbers for next time */ | /* save the numbers for next time */ | ||||
memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); | memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); | ||||
lthreads = threads; | |||||
} | } | ||||
} | |||||
void | void | ||||
u_procstates(total, brkdn) | u_procstates(int total, int *brkdn, int threads) | ||||
int total; | |||||
int *brkdn; | |||||
{ | { | ||||
static char new[MAX_COLS]; | /* if threads state has changed, do a full update */ | ||||
register int i; | if (lthreads != threads) | ||||
{ | |||||
i_procstates(total, brkdn, threads); | |||||
return; | |||||
} | |||||
/* update number of processes only if it has changed */ | /* update number of processes only if it has changed */ | ||||
if (ltotal != total) | if (ltotal != total) | ||||
{ | { | ||||
/* move and overwrite */ | display_fmt(0, y_procstate, 0, 0, | ||||
#if (x_procstate == 0) | "%d", total); | ||||
Move_to(x_procstate, y_procstate); | |||||
#else | |||||
/* cursor is already there...no motion needed */ | |||||
/* assert(lastline == 1); */ | |||||
#endif | |||||
printf("%d", total); | |||||
/* if number of digits differs, rewrite the label */ | /* if number of digits differs, rewrite the label */ | ||||
if (digits(total) != digits(ltotal)) | if (digits(total) != digits(ltotal)) | ||||
{ | { | ||||
fputs(" processes:", stdout); | display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes"); | ||||
/* put out enough spaces to get to column 15 */ | x_procstate = virt_x; | ||||
i = digits(total); | |||||
while (i++ < 4) | |||||
{ | |||||
putchar(' '); | |||||
} | } | ||||
/* cursor may end up right where we want it!!! */ | |||||
} | |||||
/* save new total */ | /* save new total */ | ||||
ltotal = total; | ltotal = total; | ||||
} | } | ||||
/* see if any of the state numbers has changed */ | /* 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 */ | /* format and update the line */ | ||||
summary_format(new, brkdn, procstate_names); | summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL); | ||||
line_update(procstates_buffer, new, x_brkdn, y_brkdn); | |||||
memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); | memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); | ||||
} | } | ||||
} | } | ||||
#ifdef no_more | |||||
/* | /* | ||||
* *_cpustates(states, names) - print the cpu state percentages | * *_cpustates(states, names) - print the cpu state percentages | ||||
* | |||||
* Assumptions: cursor is on the PREVIOUS line | |||||
*/ | */ | ||||
/* cpustates_tag() calculates the correct tag to use to label the line */ | /* cpustates_tag() calculates the correct tag to use to label the line */ | ||||
char *cpustates_tag() | char * | ||||
cpustates_tag() | |||||
{ | { | ||||
register char *use; | register char *use; | ||||
static char *short_tag = "CPU: "; | static char *short_tag = "CPU: "; | ||||
static char *long_tag = "CPU states: "; | static char *long_tag = "CPU states: "; | ||||
/* if length + strlen(long_tag) >= screen_width, then we have to | /* if length + strlen(long_tag) >= screen_width, then we have to | ||||
use the shorter tag (we subtract 2 to account for ": ") */ | use the shorter tag (we subtract 2 to account for ": ") */ | ||||
if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width) | if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width) | ||||
{ | { | ||||
use = short_tag; | use = short_tag; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
use = long_tag; | use = long_tag; | ||||
} | } | ||||
/* set cpustates_column accordingly then return result */ | /* set x_cpustates accordingly then return result */ | ||||
cpustates_column = strlen(use); | x_cpustates = strlen(use); | ||||
return(use); | return(use); | ||||
} | } | ||||
#endif | |||||
void | void | ||||
i_cpustates(states) | i_cpustates(int *states) | ||||
int *states; | |||||
{ | { | ||||
register int i = 0; | int value; | ||||
register int value; | char **names; | ||||
register char **names; | char *thisname; | ||||
register char *thisname; | int *colp; | ||||
int cpu; | int color = 0; | ||||
#ifdef ENABLE_COLOR | |||||
int *cidx = cpustate_cidx; | |||||
#endif | |||||
for (cpu = 0; cpu < num_cpus; cpu++) { | /* initialize */ | ||||
names = cpustate_names; | names = cpustate_names; | ||||
colp = cpustate_columns; | |||||
/* print tag and bump lastline */ | /* print tag */ | ||||
if (num_cpus == 1) | display_write(0, y_cpustates, 0, 0, cpustates_tag()); | ||||
printf("\nCPU: "); | |||||
else { | |||||
value = printf("\nCPU %d: ", cpu); | |||||
while (value++ <= cpustates_column) | |||||
printf(" "); | |||||
} | |||||
lastline++; | |||||
/* now walk thru the names and print the line */ | /* now walk thru the names and print the line */ | ||||
while ((thisname = *names++) != NULL) | while ((thisname = *names++) != NULL) | ||||
{ | { | ||||
if (*thisname != '\0') | if (*thisname != '\0') | ||||
{ | { | ||||
/* retrieve the value and remember it */ | /* 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% */ | /* if percentage is >= 1000, print it as 100% */ | ||||
printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), | display_fmt(x_cpustates + *colp, y_cpustates, | ||||
(i++ % num_cpustates) == 0 ? "" : ", ", | color, 0, | ||||
(value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), | |||||
((float)value)/10., | ((float)value)/10., | ||||
thisname); | thisname, | ||||
*names != NULL ? ", " : ""); | |||||
} | } | ||||
/* increment */ | |||||
colp++; | |||||
states++; | |||||
} | } | ||||
} | |||||
/* copy over values into "last" array */ | /* copy over values into "last" array */ | ||||
memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus); | memcpy(lcpustates, states, num_cpustates * sizeof(int)); | ||||
} | } | ||||
void | void | ||||
u_cpustates(states) | u_cpustates(int *states) | ||||
int *states; | |||||
{ | { | ||||
register int value; | int value; | ||||
register char **names; | char **names = cpustate_names; | ||||
register char *thisname; | char *thisname; | ||||
register int *lp; | int *lp; | ||||
register int *colp; | int *colp; | ||||
int cpu; | int color = 0; | ||||
#ifdef ENABLE_COLOR | |||||
int *cidx = cpustate_cidx; | |||||
#endif | |||||
for (cpu = 0; cpu < num_cpus; cpu++) { | lp = lcpustates; | ||||
names = cpustate_names; | |||||
Move_to(cpustates_column, y_cpustates + cpu); | |||||
lastline = y_cpustates + cpu; | |||||
lp = lcpustates + (cpu * num_cpustates); | |||||
colp = cpustate_columns; | colp = cpustate_columns; | ||||
/* we could be much more optimal about this */ | /* we could be much more optimal about this */ | ||||
while ((thisname = *names++) != NULL) | while ((thisname = *names++) != NULL) | ||||
{ | { | ||||
if (*thisname != '\0') | if (*thisname != '\0') | ||||
{ | { | ||||
/* did the value change since last time? */ | /* did the value change since last time? */ | ||||
if (*lp != *states) | if (*lp != *states) | ||||
{ | { | ||||
/* yes, move and change */ | /* yes, change it */ | ||||
Move_to(cpustates_column + *colp, y_cpustates + cpu); | |||||
lastline = y_cpustates + cpu; | |||||
/* retrieve value and remember it */ | /* retrieve 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% */ | /* if percentage is >= 1000, print it as 100% */ | ||||
printf((value >= 1000 ? "%4.0f" : "%4.1f"), | display_fmt(x_cpustates + *colp, y_cpustates, color, 0, | ||||
(value >= 1000 ? "%4.0f" : "%4.1f"), | |||||
((double)value)/10.); | ((double)value)/10.); | ||||
/* remember it for next time */ | /* remember it for next time */ | ||||
*lp = value; | *lp = value; | ||||
} | } | ||||
#ifdef ENABLE_COLOR | |||||
cidx++; | |||||
#endif | |||||
} | } | ||||
/* increment and move on */ | /* increment and move on */ | ||||
lp++; | lp++; | ||||
states++; | states++; | ||||
colp++; | colp++; | ||||
} | } | ||||
} | } | ||||
} | |||||
void | void | ||||
z_cpustates() | z_cpustates() | ||||
{ | { | ||||
register int i = 0; | register int i = 0; | ||||
register char **names; | register char **names = cpustate_names; | ||||
register char *thisname; | register char *thisname; | ||||
register int *lp; | register int *lp; | ||||
int cpu, value; | |||||
for (cpu = 0; cpu < num_cpus; cpu++) { | /* print tag */ | ||||
names = cpustate_names; | display_write(0, y_cpustates, 0, 0, cpustates_tag()); | ||||
/* show tag and bump lastline */ | |||||
if (num_cpus == 1) | |||||
printf("\nCPU: "); | |||||
else { | |||||
value = printf("\nCPU %d: ", cpu); | |||||
while (value++ <= cpustates_column) | |||||
printf(" "); | |||||
} | |||||
lastline++; | |||||
while ((thisname = *names++) != NULL) | while ((thisname = *names++) != NULL) | ||||
{ | { | ||||
if (*thisname != '\0') | if (*thisname != '\0') | ||||
{ | { | ||||
printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname); | display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ", | ||||
thisname); | |||||
} | } | ||||
} | } | ||||
} | |||||
/* fill the "last" array with all -1s, to insure correct updating */ | /* fill the "last" array with all -1s, to insure correct updating */ | ||||
lp = lcpustates; | lp = lcpustates; | ||||
i = num_cpustates * num_cpus; | i = num_cpustates; | ||||
while (--i >= 0) | while (--i >= 0) | ||||
{ | { | ||||
*lp++ = -1; | *lp++ = -1; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* *_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" | * Assumptions: cursor is on "lastline", the previous line | ||||
* for i_memory ONLY: cursor is on the previous line | |||||
*/ | */ | ||||
char memory_buffer[MAX_COLS]; | |||||
void | void | ||||
i_memory(stats) | i_kernel(int *stats) | ||||
int *stats; | |||||
{ | { | ||||
fputs("\nMem: ", stdout); | if (num_kernel > 0) | ||||
lastline++; | { | ||||
display_write(0, y_kernel, 0, 0, "Kernel: "); | |||||
/* format and print the memory summary */ | /* format and print the kernel summary */ | ||||
summary_format(memory_buffer, stats, memory_names); | summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); | ||||
fputs(memory_buffer, stdout); | |||||
} | } | ||||
} | |||||
void | void | ||||
u_memory(stats) | u_kernel(int *stats) | ||||
int *stats; | |||||
{ | { | ||||
static char new[MAX_COLS]; | if (num_kernel > 0) | ||||
{ | |||||
/* format the new line */ | /* format the new line */ | ||||
summary_format(new, stats, memory_names); | summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); | ||||
line_update(memory_buffer, new, x_mem, y_mem); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* *_arc(stats) - print "ARC: " followed by the ARC summary string | * *_memory(stats) - print "Memory: " followed by the memory summary string | ||||
* | * | ||||
* Assumptions: cursor is on "lastline" | * Assumptions: cursor is on "lastline", the previous line | ||||
* for i_arc ONLY: cursor is on the previous line | |||||
*/ | */ | ||||
char arc_buffer[MAX_COLS]; | |||||
void | void | ||||
i_arc(stats) | i_memory(long *stats) | ||||
int *stats; | |||||
{ | { | ||||
if (arc_names == NULL) | display_write(0, y_mem, 0, 0, "Memory: "); | ||||
return; | |||||
fputs("\nARC: ", stdout); | |||||
lastline++; | |||||
/* format and print the memory summary */ | /* format and print the memory summary */ | ||||
summary_format(arc_buffer, stats, arc_names); | summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); | ||||
fputs(arc_buffer, stdout); | |||||
} | } | ||||
void | void | ||||
u_arc(stats) | u_memory(long *stats) | ||||
int *stats; | |||||
{ | { | ||||
static char new[MAX_COLS]; | |||||
if (arc_names == NULL) | |||||
return; | |||||
/* format the new line */ | /* format the new line */ | ||||
summary_format(new, stats, arc_names); | summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); | ||||
line_update(arc_buffer, new, x_arc, y_arc); | |||||
} | } | ||||
/* | /* | ||||
* *_swap(stats) - print "Swap: " followed by the swap summary string | * *_swap(stats) - print "Swap: " followed by the swap summary string | ||||
* | * | ||||
* Assumptions: cursor is on "lastline" | * Assumptions: cursor is on "lastline", the previous line | ||||
* for i_swap ONLY: cursor is on the previous line | * | ||||
* These functions only print something when num_swap > 0 | |||||
*/ | */ | ||||
char swap_buffer[MAX_COLS]; | |||||
void | void | ||||
i_swap(stats) | i_swap(long *stats) | ||||
int *stats; | |||||
{ | { | ||||
fputs("\nSwap: ", stdout); | if (num_swap > 0) | ||||
lastline++; | { | ||||
/* print the tag */ | |||||
display_write(0, y_swap, 0, 0, "Swap: "); | |||||
/* format and print the swap summary */ | /* format and print the swap summary */ | ||||
summary_format(swap_buffer, stats, swap_names); | summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); | ||||
fputs(swap_buffer, stdout); | |||||
} | } | ||||
} | |||||
void | void | ||||
u_swap(stats) | u_swap(long *stats) | ||||
int *stats; | { | ||||
if (num_swap > 0) | |||||
{ | |||||
/* format the new line */ | |||||
summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); | |||||
} | |||||
} | |||||
/* | |||||
* *_arc(stats) - print "ARC: " followed by the ARC summary string | |||||
* | |||||
* Assumptions: cursor is on "lastline", the previous line | |||||
* | |||||
* These functions only print something when num_arc > 0 | |||||
*/ | |||||
void | |||||
i_arc(long *stats) | |||||
{ | { | ||||
static char new[MAX_COLS]; | if (num_arc > 0) | ||||
{ | |||||
/* print the tag */ | |||||
display_write(0, y_arc, 0, 0, "ARC: "); | |||||
/* format and print the ARC summary */ | |||||
summary_format_memory(x_arc, y_arc, stats, arc_names, arc_cidx); | |||||
} | |||||
} | |||||
void | |||||
u_arc(long *stats) | |||||
{ | |||||
if (num_arc > 0) | |||||
{ | |||||
/* format the new line */ | /* format the new line */ | ||||
summary_format(new, stats, swap_names); | summary_format_memory(x_arc, y_arc, stats, arc_names, arc_cidx); | ||||
line_update(swap_buffer, new, x_swap, y_swap); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* *_message() - print the next pending message line, or erase the one | * *_message() - print the next pending message line, or erase the one | ||||
* that is there. | * that is there. | ||||
* | * | ||||
* Note that u_message is (currently) the same as i_message. | * Note that u_message is (currently) the same as i_message. | ||||
* | * | ||||
* Assumptions: lastline is consistent | * Assumptions: lastline is consistent | ||||
*/ | */ | ||||
/* | /* | ||||
* i_message is funny because it gets its message asynchronously (with | * 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). */ | |||||
void | void | ||||
i_message() | i_message(struct timeval *now) | ||||
{ | { | ||||
while (lastline < y_message) | 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) | |||||
{ | { | ||||
fputc('\n', stdout); | time_get(&my_now); | ||||
lastline++; | now = &my_now; | ||||
} | } | ||||
if (next_msg[0] != '\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) | |||||
{ | { | ||||
standout(next_msg); | /* is it time to change the message? */ | ||||
msglen = strlen(next_msg); | if (timercmp(now, &message_time, > )) | ||||
next_msg[0] = '\0'; | { | ||||
/* yes, free the current message */ | |||||
dprintf("i_message: timer expired\n"); | |||||
if (message_current != NULL) | |||||
{ | |||||
free(message_current); | |||||
message_current = NULL; | |||||
} | } | ||||
else if (msglen > 0) | |||||
/* is there a new message to be displayed? */ | |||||
if (message_first != message_last) | |||||
{ | { | ||||
(void) clear_eol(msglen); | /* move index to next message */ | ||||
msglen = 0; | 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; | |||||
} | } | ||||
} | |||||
} | |||||
} | |||||
} | |||||
void | void | ||||
u_message() | u_message(struct timeval *now) | ||||
{ | { | ||||
i_message(); | i_message(now); | ||||
} | } | ||||
static int header_length; | static int header_length; | ||||
/* | /* | ||||
* Trim a header string to the current display width and return a newly | |||||
* allocated area with the trimmed header. | |||||
*/ | |||||
char * | |||||
trim_header(text) | |||||
char *text; | |||||
{ | |||||
char *s; | |||||
int width; | |||||
s = NULL; | |||||
width = display_width; | |||||
header_length = strlen(text); | |||||
if (header_length >= width) { | |||||
s = malloc((width + 1) * sizeof(char)); | |||||
if (s == NULL) | |||||
return (NULL); | |||||
strncpy(s, text, width); | |||||
s[width] = '\0'; | |||||
} | |||||
return (s); | |||||
} | |||||
/* | |||||
* *_header(text) - print the header for the process area | * *_header(text) - print the header for the process area | ||||
* | * | ||||
* Assumptions: cursor is on the previous line and lastline is consistent | * Assumptions: cursor is on the previous line and lastline is consistent | ||||
*/ | */ | ||||
void | void | ||||
i_header(text) | i_header(char *text) | ||||
char *text; | |||||
{ | { | ||||
char *s; | int header_color = 0; | ||||
s = trim_header(text); | #ifdef ENABLE_COLOR | ||||
if (s != NULL) | header_color = color_test(header_cidx, 0); | ||||
text = s; | #endif | ||||
header_length = strlen(text); | |||||
if (header_status == ON) | if (header_status) | ||||
{ | { | ||||
putchar('\n'); | display_write(x_header, y_header, header_color, 1, text); | ||||
fputs(text, stdout); | |||||
lastline++; | |||||
} | } | ||||
else if (header_status == ERASE) | |||||
{ | |||||
header_status = OFF; | |||||
} | } | ||||
free(s); | |||||
} | |||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
void | void | ||||
u_header(text) | u_header(char *text) | ||||
char *text __unused; /* ignored */ | |||||
{ | { | ||||
int header_color = 0; | |||||
if (header_status == ERASE) | #ifdef ENABLE_COLOR | ||||
{ | header_color = color_test(header_cidx, 0); | ||||
putchar('\n'); | #endif | ||||
lastline++; | display_write(x_header, y_header, header_color, 1, | ||||
clear_eol(header_length); | header_status ? text : ""); | ||||
header_status = OFF; | |||||
} | } | ||||
} | |||||
/* | /* | ||||
* *_process(line, thisline) - print one process line | * *_process(line, thisline) - print one process line | ||||
* | * | ||||
* Assumptions: lastline is consistent | * Assumptions: lastline is consistent | ||||
*/ | */ | ||||
void | void | ||||
i_process(line, thisline) | i_process(int line, char *thisline) | ||||
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 */ | /* truncate the line to conform to our current screen width */ | ||||
thisline[display_width] = '\0'; | thisline[display_width] = '\0'; | ||||
/* write the line out */ | /* write the line out */ | ||||
fputs(thisline, stdout); | display_write(0, y_procs + line, 0, 1, thisline); | ||||
/* 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)); | |||||
} | } | ||||
void | void | ||||
u_process(line, newline) | u_process(int line, char *new_line) | ||||
int line; | |||||
char *newline; | |||||
{ | { | ||||
register char *optr; | i_process(line, new_line); | ||||
register int screen_line = line + Header_lines; | } | ||||
register char *bufferline; | |||||
/* remember a pointer to the current line in the screen buffer */ | void | ||||
bufferline = &screenbuf[lineindex(line)]; | i_endscreen() | ||||
/* truncate the line to conform to our current screen width */ | |||||
newline[display_width] = '\0'; | |||||
/* is line higher than we went on the last display? */ | |||||
if (line >= last_hi) | |||||
{ | { | ||||
/* yes, just ignore screenbuf and write it out directly */ | if (smart_terminal) | ||||
/* get positioned on the correct line */ | |||||
if (screen_line - lastline == 1) | |||||
{ | { | ||||
putchar('\n'); | /* move the cursor to a pleasant place */ | ||||
lastline++; | display_move(x_idlecursor, y_idlecursor); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
Move_to(0, screen_line); | /* separate this display from the next with some vertical room */ | ||||
lastline = screen_line; | fputs("\n\n", stdout); | ||||
} | } | ||||
fflush(stdout); | |||||
} | |||||
/* now write the line */ | void | ||||
fputs(newline, stdout); | u_endscreen() | ||||
/* copy it in to the buffer */ | { | ||||
optr = strecpy(bufferline, newline); | if (smart_terminal) | ||||
{ | |||||
/* clear-to-end the display */ | |||||
display_cte(); | |||||
/* zero fill the rest of it */ | /* move the cursor to a pleasant place */ | ||||
memzero(optr, display_width - (optr - bufferline)); | display_move(x_idlecursor, y_idlecursor); | ||||
fflush(stdout); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
line_update(bufferline, newline, 0, line + Header_lines); | /* separate this display from the next with some vertical room */ | ||||
fputs("\n\n", stdout); | |||||
} | } | ||||
} | } | ||||
void | void | ||||
u_endscreen(hi) | display_header(int t) | ||||
int hi; | { | ||||
header_status = t != 0; | |||||
} | |||||
void | |||||
message_mark() | |||||
{ | { | ||||
register int screen_line = hi + Header_lines; | message_barrier = Yes; | ||||
register int i; | } | ||||
if (smart_terminal) | void | ||||
message_expire() | |||||
{ | { | ||||
if (hi < last_hi) | message_time.tv_sec = 0; | ||||
{ | message_time.tv_usec = 0; | ||||
/* 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 | void | ||||
message_flush() | |||||
{ | { | ||||
Move_to(0, screen_line); | message_first = message_last; | ||||
lastline = screen_line; | message_time.tv_sec = 0; | ||||
message_time.tv_usec = 0; | |||||
} | } | ||||
if (clear_to_end) | /* | ||||
* 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) | |||||
{ | { | ||||
/* we can do this the easy way */ | int i; | ||||
putcap(clear_to_end); | int empty; | ||||
char msg[MAX_COLS]; | |||||
/* if message_barrier is active, remove all pending messages */ | |||||
if (message_barrier) | |||||
{ | |||||
message_flush(); | |||||
message_barrier = No; | |||||
} | } | ||||
else | |||||
/* first, format the message */ | |||||
(void) vsnprintf(msg, sizeof(msg), msgfmt, ap); | |||||
/* where in the buffer will it go? */ | |||||
i = message_last + 1; | |||||
if (i >= MAX_MESSAGES) i = 0; | |||||
/* make sure the buffer is not full */ | |||||
if (i != message_first) | |||||
{ | { | ||||
/* use clear_eol on each line */ | /* insert it in to message_buf */ | ||||
i = hi; | message_buf[i] = strdup(msg); | ||||
while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) | 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) | |||||
{ | { | ||||
putchar('\n'); | /* we can display the message now */ | ||||
i_message(NULL); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
last_hi = hi; | |||||
/* move the cursor to a pleasant place */ | /* | ||||
Move_to(x_idlecursor, y_idlecursor); | * void new_message(int type, char *msgfmt, ...) | ||||
lastline = y_idlecursor; | * | ||||
} | * Display a message in the message area. It is safe to call this function | ||||
else | * before display_init. Messages logged before the display is drawn will be | ||||
* held and displayed later. | |||||
*/ | |||||
void | |||||
new_message(char *msgfmt, ...) | |||||
{ | { | ||||
/* separate this display from the next with some vertical room */ | va_list ap; | ||||
fputs("\n\n", stdout); | |||||
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 | void | ||||
display_header(t) | message_error(char *msgfmt, ...) | ||||
int t; | { | ||||
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() | |||||
{ | { | ||||
if (t) | /* remove any existing message */ | ||||
if (message_current != NULL) | |||||
{ | { | ||||
header_status = ON; | display_move(0, y_message); | ||||
screen_cleareol(message_length); | |||||
free(message_current); | |||||
message_current = 0; | |||||
} | } | ||||
else if (header_status == ON) | |||||
{ | /* flush all pending messages */ | ||||
header_status = ERASE; | message_flush(); | ||||
} | } | ||||
} | |||||
/*VARARGS2*/ | /* | ||||
* 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 | void | ||||
new_message(type, msgfmt, a1, a2, a3) | message_prompt_v(int so, char *msgfmt, va_list ap) | ||||
int type; | |||||
char *msgfmt; | |||||
caddr_t a1, a2, a3; | |||||
{ | { | ||||
register int i; | char msg[MAX_COLS]; | ||||
int i; | |||||
/* first, format the message */ | /* clear out the message buffer */ | ||||
(void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); | message_flush(); | ||||
if (msglen > 0) | /* 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) | |||||
{ | { | ||||
/* message there already -- can we clear it? */ | screen_standout(msg); | ||||
if (!overstrike) | |||||
{ | |||||
/* 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'; | |||||
} | } | ||||
} | |||||
} | |||||
else | else | ||||
{ | { | ||||
if ((type & MT_delayed) == 0) | fputs(msg, stdout); | ||||
{ | |||||
type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); | |||||
msglen = strlen(next_msg); | |||||
next_msg[0] = '\0'; | |||||
} | } | ||||
/* 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; | |||||
} | } | ||||
/* | |||||
* void message_prompt(char *msgfmt, ...) | |||||
* | |||||
* Place a prompt in the message area (see message_prompt_v). | |||||
*/ | |||||
void | |||||
message_prompt(char *msgfmt, ...) | |||||
{ | |||||
va_list ap; | |||||
va_start(ap, msgfmt); | |||||
message_prompt_v(Yes, msgfmt, ap); | |||||
va_end(ap); | |||||
} | } | ||||
void | void | ||||
clear_message() | message_prompt_plain(char *msgfmt, ...) | ||||
{ | { | ||||
if (clear_eol(msglen) == 1) | va_list ap; | ||||
{ | |||||
putchar('\r'); | 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 | int | ||||
readline(buffer, size, numeric) | readline(char *buffer, int size, int numeric) | ||||
char *buffer; | |||||
int size; | |||||
int numeric; | |||||
{ | { | ||||
register char *ptr = buffer; | register char *ptr = buffer; | ||||
register char ch; | register char ch; | ||||
register char cnt = 0; | register char cnt = 0; | ||||
register char maxcnt = 0; | |||||
/* allow room for null terminator */ | /* allow room for null terminator */ | ||||
size -= 1; | size -= 1; | ||||
/* read loop */ | /* read loop */ | ||||
while ((fflush(stdout), read(0, ptr, 1) > 0)) | while ((fflush(stdout), read(0, ptr, 1) > 0)) | ||||
{ | { | ||||
/* newline means we are done */ | /* newline or return means we are done */ | ||||
if ((ch = *ptr) == '\n' || ch == '\r') | if ((ch = *ptr) == '\n' || ch == '\r') | ||||
{ | { | ||||
break; | break; | ||||
} | } | ||||
/* handle special editing characters */ | /* handle special editing characters */ | ||||
if (ch == ch_kill) | if (ch == ch_kill) | ||||
{ | { | ||||
/* kill line -- account for overstriking */ | |||||
if (overstrike) | |||||
{ | |||||
msglen += maxcnt; | |||||
} | |||||
/* return null string */ | /* return null string */ | ||||
*buffer = '\0'; | *buffer = '\0'; | ||||
putchar('\r'); | putchar('\r'); | ||||
return(-1); | 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) | else if (ch == ch_erase) | ||||
{ | { | ||||
/* erase previous character */ | /* erase previous character */ | ||||
if (cnt <= 0) | if (cnt <= 0) | ||||
{ | { | ||||
/* none to erase! */ | /* none to erase! */ | ||||
putchar('\7'); | putchar('\7'); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
fputs("\b \b", stdout); | fputs("\b \b", stdout); | ||||
ptr--; | ptr--; | ||||
cnt--; | cnt--; | ||||
} | } | ||||
} | } | ||||
/* check for character validity and buffer overflow */ | /* check for character validity and buffer overflow */ | ||||
else if (cnt == size || (numeric && !isdigit(ch)) || | else if (cnt == size || (numeric && !isdigit((int)ch)) || | ||||
!isprint(ch)) | !isprint((int)ch)) | ||||
{ | { | ||||
/* not legal */ | /* not legal */ | ||||
putchar('\7'); | putchar('\7'); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
/* echo it and store it in the buffer */ | /* echo it and store it in the buffer */ | ||||
putchar(ch); | putchar(ch); | ||||
ptr++; | ptr++; | ||||
cnt++; | cnt++; | ||||
if (cnt > maxcnt) | |||||
{ | |||||
maxcnt = cnt; | |||||
} | } | ||||
} | } | ||||
} | |||||
/* all done -- null terminate the string */ | /* all done -- null terminate the string */ | ||||
*ptr = '\0'; | *ptr = '\0'; | ||||
/* account for the extra characters in the message area */ | /* add response length to message_length */ | ||||
/* (if terminal overstrikes, remember the furthest they went) */ | message_length += cnt; | ||||
msglen += overstrike ? maxcnt : cnt; | |||||
/* return either inputted number or string length */ | /* return either inputted number or string length */ | ||||
putchar('\r'); | putchar('\r'); | ||||
return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); | return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); | ||||
} | } | ||||
/* internal support routines */ | void | ||||
display_pagerstart() | |||||
static int string_count(pp) | |||||
register char **pp; | |||||
{ | { | ||||
register int cnt; | display_clear(); | ||||
cnt = 0; | |||||
while (*pp++ != NULL) | |||||
{ | |||||
cnt++; | |||||
} | } | ||||
return(cnt); | |||||
} | |||||
static void summary_format(str, numbers, names) | void | ||||
display_pagerend() | |||||
char *str; | |||||
int *numbers; | |||||
register char **names; | |||||
{ | { | ||||
register char *p; | char ch; | ||||
register int num; | |||||
register char *thisname; | |||||
register int useM = No; | |||||
/* format each number followed by its string */ | screen_standout("Hit any key to continue: "); | ||||
p = str; | fflush(stdout); | ||||
while ((thisname = *names++) != NULL) | (void) read(0, &ch, 1); | ||||
{ | |||||
/* 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); | |||||
} | |||||
} | |||||
/* ignore negative numbers, but display corresponding string */ | void | ||||
else if (num < 0) | display_pager(char *fmt, ...) | ||||
{ | |||||
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'; | va_list ap; | ||||
} | |||||
} | |||||
static void line_update(old, new, start, line) | int ch; | ||||
char readch; | |||||
char buffer[MAX_COLS]; | |||||
char *data; | |||||
register char *old; | /* format into buffer */ | ||||
register char *new; | va_start(ap, fmt); | ||||
int start; | (void) vsnprintf(buffer, MAX_COLS, fmt, ap); | ||||
int line; | va_end(ap); | ||||
data = buffer; | |||||
while ((ch = *data++) != '\0') | |||||
{ | { | ||||
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 | |||||
/* start things off on the right foot */ | |||||
/* this is to make sure the invariants get set up right */ | |||||
if ((ch = *new++) != *old) | |||||
{ | |||||
if (line - lastline == 1 && start == 0) | |||||
{ | |||||
putchar('\n'); | |||||
} | |||||
else | |||||
{ | |||||
Move_to(start, line); | |||||
} | |||||
cursor_on_line = Yes; | |||||
putchar(ch); | putchar(ch); | ||||
*old = ch; | if (ch == '\n') | ||||
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 (++curr_y >= screen_length - 1) | ||||
{ | { | ||||
/* new character is different from old */ | screen_standout("...More..."); | ||||
/* make sure the cursor is on top of this character */ | fflush(stdout); | ||||
diff = newcol - lastcol; | (void) read(0, &readch, 1); | ||||
if (diff > 0) | putchar('\r'); | ||||
switch(readch) | |||||
{ | { | ||||
/* some motion is required--figure out which is shorter */ | case '\r': | ||||
if (diff < 6 && cursor_on_line) | case '\n': | ||||
{ | curr_y--; | ||||
/* overwrite old stuff--get it out of the old buffer */ | break; | ||||
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 */ | case 'q': | ||||
if (ch == '\0') | return; | ||||
{ | |||||
/* 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 */ | default: | ||||
newcol++; | curr_y = 0; | ||||
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) | |||||
char *str; | |||||
{ | |||||
register char *ptr; | |||||
register char ch; | |||||
ptr = str; | |||||
while ((ch = *ptr) != '\0') | |||||
{ | |||||
if (!isprint(ch)) | |||||
{ | |||||
*ptr = '?'; | |||||
} | |||||
ptr++; | |||||
} | |||||
return(str); | |||||
} | |||||
void | |||||
i_uptime(bt, tod) | |||||
struct timeval* bt; | |||||
time_t *tod; | |||||
{ | |||||
time_t uptime; | |||||
int days, hrs, mins, secs; | |||||
if (bt->tv_sec != -1) { | |||||
uptime = *tod - bt->tv_sec; | |||||
days = uptime / 86400; | |||||
uptime %= 86400; | |||||
hrs = uptime / 3600; | |||||
uptime %= 3600; | |||||
mins = uptime / 60; | |||||
secs = uptime % 60; | |||||
/* | |||||
* Display the uptime. | |||||
*/ | |||||
if (smart_terminal) | |||||
{ | |||||
Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); | |||||
} | |||||
else | |||||
{ | |||||
fputs(" ", stdout); | |||||
} | |||||
printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); | |||||
} | } | ||||
} | } |