
Due to differences in (mainly) measuring and accumulating CPU times, the two top programs end up serving different purposes: the NetBSD top is a system administration tool, while the MINIX3 top (now mtop) is a performance debugging tool. Therefore, we keep both. The newly imported BSD top has a few MINIX3-specific changes. CPU statistics separate system time from kernel time, rather than kernel time from time spent on handling interrupts. Memory statistics show numbers that are currently relevant for MINIX3. Swap statistics are disabled entirely. All of these changes effectively bring it closer to how mtop already worked as well. Change-Id: I9611917cb03e164ddf012c5def6da0e7fede826d
2022 lines
42 KiB
C
2022 lines
42 KiB
C
/*
|
|
* 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 file contains the routines that display information on the screen.
|
|
* Each section of the screen has two routines: one for initially writing
|
|
* all constant and dynamic text, and one for only updating the text that
|
|
* changes. The prefix "i_" is used on all the "initial" routines and the
|
|
* prefix "u_" is used for all the "updating" routines.
|
|
*
|
|
* ASSUMPTIONS:
|
|
* None of the "i_" routines use any of the termcap capabilities.
|
|
* In this way, those routines can be safely used on terminals that
|
|
* have minimal (or nonexistant) terminal capabilities.
|
|
*
|
|
* The routines 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 <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 "boolean.h"
|
|
#include "utils.h"
|
|
|
|
#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 = -1;
|
|
static int display_width = MAX_COLS;
|
|
static int ncpu = 0;
|
|
|
|
/* 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;
|
|
static int multi = 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 const char **procstate_names;
|
|
static const char **cpustate_names;
|
|
static const char **memory_names;
|
|
static const char **swap_names;
|
|
static const 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 *cpustate_columns;
|
|
static int cpustate_total_length;
|
|
|
|
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(const char **pp)
|
|
|
|
{
|
|
register int cnt = 0;
|
|
|
|
if (pp != NULL)
|
|
{
|
|
while (*pp++ != NULL)
|
|
{
|
|
cnt++;
|
|
}
|
|
}
|
|
return(cnt);
|
|
}
|
|
|
|
void
|
|
display_clear(void)
|
|
|
|
{
|
|
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.
|
|
*/
|
|
|
|
static 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.
|
|
*/
|
|
|
|
static void
|
|
display_write(int x, int y, int newcolor, int eol, const 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
display_fmt(int x, int y, int newcolor, int eol, const char *fmt, ...)
|
|
|
|
{
|
|
va_list argp;
|
|
|
|
va_start(argp, fmt);
|
|
|
|
vsnprintf(scratchbuf, MAX_COLS, fmt, argp);
|
|
display_write(x, y, newcolor, eol, scratchbuf);
|
|
}
|
|
|
|
static void
|
|
display_cte(void)
|
|
|
|
{
|
|
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, const char **names, int *cidx)
|
|
|
|
{
|
|
register int num;
|
|
register const char *thisname;
|
|
register const 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, const char **names, int *cidx)
|
|
|
|
{
|
|
register long num;
|
|
register int color;
|
|
register const char *thisname;
|
|
register const 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 */
|
|
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
|
|
to run off the end of their buffers */
|
|
display_width = screen_width;
|
|
if (display_width >= MAX_COLS)
|
|
{
|
|
display_width = MAX_COLS - 1;
|
|
}
|
|
|
|
/* see how much space we need */
|
|
newsize = 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 = ecalloc(bufsize, sizeof(char));
|
|
colorbuf = ecalloc(bufsize, sizeof(char));
|
|
if (screenbuf == NULL || colorbuf == NULL)
|
|
{
|
|
/* oops! */
|
|
return(-1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* just clear them out */
|
|
memzero(screenbuf, bufsize);
|
|
memzero(colorbuf, bufsize);
|
|
}
|
|
|
|
/* for dumb terminals, pretend like we can show any amount */
|
|
if (!smart_terminal)
|
|
return Largest;
|
|
|
|
/* adjust total lines on screen to lines available for procs */
|
|
if (top_lines < y_procs)
|
|
top_lines = 0;
|
|
else
|
|
top_lines -= y_procs;
|
|
|
|
/* return number of lines available */
|
|
return top_lines;
|
|
}
|
|
|
|
int
|
|
display_lines()
|
|
|
|
{
|
|
return(smart_terminal ? screen_length : Largest);
|
|
}
|
|
|
|
int
|
|
display_columns()
|
|
|
|
{
|
|
return(display_width);
|
|
}
|
|
|
|
/*
|
|
* 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_setmulti(int m)
|
|
{
|
|
int i;
|
|
if (m == multi)
|
|
return 0;
|
|
if ((multi = m) != 0) {
|
|
for (i = 1; i < ncpu; i++)
|
|
{
|
|
/* adjust screen placements */
|
|
y_kernel++;
|
|
y_mem++;
|
|
y_swap++;
|
|
y_message++;
|
|
y_header++;
|
|
y_idlecursor++;
|
|
y_procs++;
|
|
}
|
|
return -(ncpu - 1);
|
|
} else {
|
|
for (i = 1; i < ncpu; i++)
|
|
{
|
|
/* adjust screen placements */
|
|
y_kernel--;
|
|
y_mem--;
|
|
y_swap--;
|
|
y_message--;
|
|
y_header--;
|
|
y_idlecursor--;
|
|
y_procs--;
|
|
}
|
|
return (ncpu - 1);
|
|
}
|
|
}
|
|
|
|
int
|
|
display_init(struct statics *statics, int percpuinfo)
|
|
|
|
{
|
|
register int top_lines;
|
|
register const char **pp;
|
|
register char *p;
|
|
register int *ip;
|
|
register int i;
|
|
|
|
/* certain things may influence the screen layout,
|
|
so look at those first */
|
|
|
|
ncpu = statics->ncpu ? statics->ncpu : 1;
|
|
/* 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++;
|
|
}
|
|
|
|
(void)display_setmulti(percpuinfo);
|
|
|
|
/* 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 */
|
|
if (top_lines > -1)
|
|
{
|
|
/* save pointers and allocate space for names */
|
|
procstate_names = statics->procstate_names;
|
|
num_procstates = string_count(procstate_names);
|
|
lprocstates = ecalloc(num_procstates, sizeof(int));
|
|
|
|
cpustate_names = statics->cpustate_names;
|
|
num_cpustates = string_count(cpustate_names);
|
|
lcpustates = ecalloc(num_cpustates, sizeof(int) * ncpu);
|
|
cpustate_columns = ecalloc(num_cpustates, sizeof(int));
|
|
memory_names = statics->memory_names;
|
|
num_memory = string_count(memory_names);
|
|
|
|
/* calculate starting columns where needed */
|
|
cpustate_total_length = 0;
|
|
pp = cpustate_names;
|
|
ip = cpustate_columns;
|
|
while (*pp != NULL)
|
|
{
|
|
*ip++ = cpustate_total_length;
|
|
if ((i = strlen(*pp++)) > 0)
|
|
{
|
|
cpustate_total_length += i + 8;
|
|
}
|
|
}
|
|
cpustate_total_length -= 2;
|
|
}
|
|
|
|
#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 = emalloc(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 = emalloc(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 = emalloc(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 = emalloc(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);
|
|
}
|
|
|
|
static void
|
|
pr_loadavg(double avg, int i)
|
|
|
|
{
|
|
int color = 0;
|
|
|
|
#ifdef ENABLE_COLOR
|
|
color = color_test(load_cidx[i], (int)(avg * 100));
|
|
#endif
|
|
display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0,
|
|
avg < 10.0 ? " %5.2f" : " %5.1f", avg);
|
|
display_write(-1, -1, 0, 0, (i < 2 ? "," : ";"));
|
|
}
|
|
|
|
void
|
|
i_loadave(int mpid, double *avenrun)
|
|
|
|
{
|
|
register int i;
|
|
|
|
/* mpid == -1 implies this system doesn't have an _mpid */
|
|
if (mpid != -1)
|
|
{
|
|
display_fmt(0, 0, 0, 0,
|
|
"last pid: %5d; load avg:", mpid);
|
|
x_loadave = X_LOADAVE;
|
|
}
|
|
else
|
|
{
|
|
display_write(0, 0, 0, 0, "load averages:");
|
|
x_loadave = X_LOADAVE - X_LASTPIDWIDTH;
|
|
}
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
pr_loadavg(avenrun[i], i);
|
|
}
|
|
|
|
lmpid = mpid;
|
|
}
|
|
|
|
void
|
|
u_loadave(int mpid, double *avenrun)
|
|
|
|
{
|
|
register int i;
|
|
|
|
if (mpid != -1)
|
|
{
|
|
/* change screen only when value has really changed */
|
|
if (mpid != lmpid)
|
|
{
|
|
display_fmt(x_lastpid, y_lastpid, 0, 0,
|
|
"%5d", mpid);
|
|
lmpid = mpid;
|
|
}
|
|
}
|
|
|
|
/* display new load averages */
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
pr_loadavg(avenrun[i], i);
|
|
}
|
|
}
|
|
|
|
static char minibar_buffer[64];
|
|
#define MINIBAR_WIDTH 20
|
|
|
|
void
|
|
i_minibar(int (*formatter)(char *, int))
|
|
{
|
|
(void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
|
|
|
|
display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
|
|
}
|
|
|
|
void
|
|
u_minibar(int (*formatter)(char *, int))
|
|
{
|
|
(void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
|
|
|
|
display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
|
|
}
|
|
|
|
static int uptime_days;
|
|
static int uptime_hours;
|
|
static int uptime_mins;
|
|
static int uptime_secs;
|
|
|
|
void
|
|
i_uptime(time_t *bt, time_t *tod)
|
|
|
|
{
|
|
time_t uptime;
|
|
|
|
if (*bt != -1)
|
|
{
|
|
uptime = *tod - *bt;
|
|
uptime += 30;
|
|
uptime_days = uptime / 86400;
|
|
uptime %= 86400;
|
|
uptime_hours = uptime / 3600;
|
|
uptime %= 3600;
|
|
uptime_mins = uptime / 60;
|
|
uptime_secs = uptime % 60;
|
|
|
|
/*
|
|
* Display the uptime.
|
|
*/
|
|
|
|
display_fmt(x_uptime, y_uptime, 0, 0,
|
|
" up %d+%02d:%02d:%02d",
|
|
uptime_days, uptime_hours, uptime_mins, uptime_secs);
|
|
}
|
|
}
|
|
|
|
void
|
|
u_uptime(time_t *bt, time_t *tod)
|
|
|
|
{
|
|
i_uptime(bt, tod);
|
|
}
|
|
|
|
|
|
void
|
|
i_timeofday(time_t *tod)
|
|
|
|
{
|
|
/*
|
|
* Display the current time.
|
|
* "ctime" always returns a string that looks like this:
|
|
*
|
|
* Sun Sep 16 01:03:52 1973
|
|
* 012345678901234567890123
|
|
* 1 2
|
|
*
|
|
* We want indices 11 thru 18 (length 8).
|
|
*/
|
|
|
|
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)
|
|
{
|
|
x = x_uptime + 19;
|
|
}
|
|
|
|
/* display it */
|
|
display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11]));
|
|
}
|
|
|
|
static int ltotal = 0;
|
|
static int lthreads = 0;
|
|
|
|
/*
|
|
* *_procstates(total, brkdn, names) - print the process summary line
|
|
*/
|
|
|
|
|
|
void
|
|
i_procstates(int total, int *brkdn, int threads)
|
|
|
|
{
|
|
/* write current number of processes and remember the value */
|
|
display_fmt(0, y_procstate, 0, 0,
|
|
"%d %s: ", total, threads ? "threads" : "processes");
|
|
ltotal = total;
|
|
|
|
/* remember where the summary starts */
|
|
x_procstate = virt_x;
|
|
|
|
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));
|
|
lthreads = threads;
|
|
}
|
|
}
|
|
|
|
void
|
|
u_procstates(int total, int *brkdn, int threads)
|
|
|
|
{
|
|
/* 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)
|
|
{
|
|
display_fmt(0, y_procstate, 0, 0,
|
|
"%d", total);
|
|
|
|
/* if number of digits differs, rewrite the label */
|
|
if (digits(total) != digits(ltotal))
|
|
{
|
|
display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes");
|
|
x_procstate = virt_x;
|
|
}
|
|
|
|
/* save new total */
|
|
ltotal = total;
|
|
}
|
|
|
|
/* see if any of the state numbers has changed */
|
|
if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
|
|
{
|
|
/* format and update the line */
|
|
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
|
|
*/
|
|
|
|
/* cpustates_tag() calculates the correct tag to use to label the line */
|
|
|
|
static char *
|
|
cpustates_tag(int c)
|
|
|
|
{
|
|
unsigned width, u;
|
|
|
|
static char fmttag[100];
|
|
|
|
const char *short_tag = !multi || ncpu <= 1 ? "CPU: " : "CPU%0*d: ";
|
|
const char *long_tag = !multi || ncpu <= 1 ?
|
|
"CPU states: " : "CPU%0*d states: ";
|
|
|
|
for (width = 0, u = ncpu - 1; u > 0; u /= 10) {
|
|
++width;
|
|
}
|
|
/* if length + strlen(long_tag) > screen_width, then we have to
|
|
use the shorter tag */
|
|
|
|
snprintf(fmttag, sizeof(fmttag), long_tag, width, c);
|
|
|
|
if (cpustate_total_length + (signed)strlen(fmttag) > screen_width) {
|
|
snprintf(fmttag, sizeof(fmttag), short_tag, width, c);
|
|
}
|
|
|
|
/* set x_cpustates accordingly then return result */
|
|
x_cpustates = strlen(fmttag);
|
|
return(fmttag);
|
|
}
|
|
|
|
void
|
|
i_cpustates(int *states)
|
|
|
|
{
|
|
int value;
|
|
const char **names;
|
|
const char *thisname;
|
|
int *colp;
|
|
int color = 0;
|
|
#ifdef ENABLE_COLOR
|
|
int *cidx;
|
|
#endif
|
|
int c, i;
|
|
|
|
if (multi == 0 && ncpu > 1)
|
|
{
|
|
for (c = 1; c < ncpu; c++)
|
|
for (i = 0; i < num_cpustates; i++)
|
|
states[i] += states[c * num_cpustates + i];
|
|
for (i = 0; i < num_cpustates; i++)
|
|
states[i] /= ncpu;
|
|
}
|
|
|
|
for (c = 0; c < (multi ? ncpu : 1); c++)
|
|
{
|
|
#ifdef ENABLE_COLOR
|
|
cidx = cpustate_cidx;
|
|
#endif
|
|
|
|
/* print tag */
|
|
display_write(0, y_cpustates + c, 0, 0, cpustates_tag(c));
|
|
colp = cpustate_columns;
|
|
|
|
/* now walk thru the names and print the line */
|
|
for (i = 0, names = cpustate_names; ((thisname = *names++) != NULL);)
|
|
{
|
|
if (*thisname != '\0')
|
|
{
|
|
/* retrieve the value and remember it */
|
|
value = *states;
|
|
|
|
#ifdef ENABLE_COLOR
|
|
/* determine color number to use */
|
|
color = color_test(*cidx++, value/10);
|
|
#endif
|
|
|
|
/* if percentage is >= 1000, print it as 100% */
|
|
display_fmt(x_cpustates + *colp, y_cpustates + c,
|
|
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) * ncpu);
|
|
}
|
|
|
|
void
|
|
u_cpustates(int *states)
|
|
|
|
{
|
|
int value;
|
|
const char **names;
|
|
const char *thisname;
|
|
int *lp;
|
|
int *colp;
|
|
int color = 0;
|
|
#ifdef ENABLE_COLOR
|
|
int *cidx;
|
|
#endif
|
|
int c, i;
|
|
|
|
lp = lcpustates;
|
|
|
|
if (multi == 0 && ncpu > 1)
|
|
{
|
|
for (c = 1; c < ncpu; c++)
|
|
for (i = 0; i < num_cpustates; i++)
|
|
states[i] += states[c * num_cpustates + i];
|
|
for (i = 0; i < num_cpustates; i++)
|
|
states[i] /= ncpu;
|
|
}
|
|
|
|
for (c = 0; c < (multi ? ncpu : 1); c++)
|
|
{
|
|
#ifdef ENABLE_COLOR
|
|
cidx = cpustate_cidx;
|
|
#endif
|
|
colp = cpustate_columns;
|
|
/* we could be much more optimal about this */
|
|
for (names = cpustate_names; (thisname = *names++) != NULL;)
|
|
{
|
|
if (*thisname != '\0')
|
|
{
|
|
/* did the value change since last time? */
|
|
if (*lp != *states)
|
|
{
|
|
/* yes, change it */
|
|
/* retrieve value and remember it */
|
|
value = *states;
|
|
|
|
#ifdef ENABLE_COLOR
|
|
/* determine color number to use */
|
|
color = color_test(*cidx, value/10);
|
|
#endif
|
|
|
|
/* if percentage is >= 1000, print it as 100% */
|
|
display_fmt(x_cpustates + *colp, y_cpustates + c, color, 0,
|
|
(value >= 1000 ? "%4.0f" : "%4.1f"),
|
|
((double)value)/10.);
|
|
|
|
/* remember it for next time */
|
|
*lp = value;
|
|
}
|
|
#ifdef ENABLE_COLOR
|
|
cidx++;
|
|
#endif
|
|
}
|
|
|
|
/* increment and move on */
|
|
lp++;
|
|
states++;
|
|
colp++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
z_cpustates()
|
|
|
|
{
|
|
register int i, c;
|
|
register const char **names = cpustate_names;
|
|
register const char *thisname;
|
|
register int *lp;
|
|
|
|
/* print tag */
|
|
for (c = 0; c < (multi ? ncpu : 1); c++)
|
|
{
|
|
display_write(0, y_cpustates + c, 0, 0, cpustates_tag(c));
|
|
|
|
for (i = 0, names = cpustate_names; (thisname = *names++) != NULL;)
|
|
{
|
|
if (*thisname != '\0')
|
|
{
|
|
display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ",
|
|
thisname);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* fill the "last" array with all -1s, to insure correct updating */
|
|
lp = lcpustates;
|
|
i = num_cpustates * ncpu;
|
|
while (--i >= 0)
|
|
{
|
|
*lp++ = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* *_kernel(stats) - print "Kernel: " followed by the kernel summary string
|
|
*
|
|
* Assumptions: cursor is on "lastline", the previous line
|
|
*/
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void
|
|
u_kernel(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)
|
|
|
|
{
|
|
display_write(0, y_mem, 0, 0, "Memory: ");
|
|
|
|
/* format and print the memory summary */
|
|
summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
|
|
}
|
|
|
|
void
|
|
u_memory(long *stats)
|
|
|
|
{
|
|
/* format the new line */
|
|
summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
|
|
}
|
|
|
|
/*
|
|
* *_swap(stats) - print "Swap: " followed by the swap summary string
|
|
*
|
|
* Assumptions: cursor is on "lastline", the previous line
|
|
*
|
|
* These functions only print something when num_swap > 0
|
|
*/
|
|
|
|
void
|
|
i_swap(long *stats)
|
|
|
|
{
|
|
if (num_swap > 0)
|
|
{
|
|
/* print the tag */
|
|
display_write(0, y_swap, 0, 0, "Swap: ");
|
|
|
|
/* format and print the swap summary */
|
|
summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
|
|
}
|
|
}
|
|
|
|
void
|
|
u_swap(long *stats)
|
|
|
|
{
|
|
if (num_swap > 0)
|
|
{
|
|
/* format the new line */
|
|
summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* *_message() - print the next pending message line, or erase the one
|
|
* that is there.
|
|
*
|
|
* Note that u_message is (currently) the same as i_message.
|
|
*
|
|
* Assumptions: lastline is consistent
|
|
*/
|
|
|
|
/*
|
|
* i_message is funny because it gets its message asynchronously (with
|
|
* respect to screen updates). Messages are taken out of the
|
|
* circular message_buf and displayed one at a time.
|
|
*/
|
|
|
|
void
|
|
i_message(struct timeval *now)
|
|
|
|
{
|
|
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)
|
|
{
|
|
time_get(&my_now);
|
|
now = &my_now;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
u_message(struct timeval *now)
|
|
|
|
{
|
|
i_message(now);
|
|
}
|
|
|
|
static int header_length;
|
|
|
|
/*
|
|
* *_header(text) - print the header for the process area
|
|
*
|
|
* Assumptions: cursor is on the previous line and lastline is consistent
|
|
*/
|
|
|
|
void
|
|
i_header(char *text)
|
|
|
|
{
|
|
int header_color = 0;
|
|
|
|
#ifdef ENABLE_COLOR
|
|
header_color = color_test(header_cidx, 0);
|
|
#endif
|
|
header_length = strlen(text);
|
|
if (header_status)
|
|
{
|
|
display_write(x_header, y_header, header_color, 1, text);
|
|
}
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
void
|
|
u_header(char *text)
|
|
|
|
{
|
|
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 : "");
|
|
}
|
|
|
|
/*
|
|
* *_process(line, thisline) - print one process line
|
|
*
|
|
* Assumptions: lastline is consistent
|
|
*/
|
|
|
|
void
|
|
i_process(int line, char *thisline)
|
|
|
|
{
|
|
/* truncate the line to conform to our current screen width */
|
|
thisline[display_width] = '\0';
|
|
|
|
/* write the line out */
|
|
display_write(0, y_procs + line, 0, 1, thisline);
|
|
}
|
|
|
|
void
|
|
u_process(int line, char *new_line)
|
|
|
|
{
|
|
i_process(line, new_line);
|
|
}
|
|
|
|
void
|
|
i_endscreen()
|
|
|
|
{
|
|
if (smart_terminal)
|
|
{
|
|
/* move the cursor to a pleasant place */
|
|
display_move(x_idlecursor, y_idlecursor);
|
|
}
|
|
else
|
|
{
|
|
/* separate this display from the next with some vertical room */
|
|
fputs("\n\n", stdout);
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
|
|
void
|
|
u_endscreen()
|
|
|
|
{
|
|
if (smart_terminal)
|
|
{
|
|
/* clear-to-end the display */
|
|
display_cte();
|
|
|
|
/* move the cursor to a pleasant place */
|
|
display_move(x_idlecursor, y_idlecursor);
|
|
fflush(stdout);
|
|
}
|
|
else
|
|
{
|
|
/* separate this display from the next with some vertical room */
|
|
fputs("\n\n", stdout);
|
|
}
|
|
}
|
|
|
|
void
|
|
display_header(int t)
|
|
|
|
{
|
|
header_status = t != 0;
|
|
}
|
|
|
|
void
|
|
message_mark(void)
|
|
|
|
{
|
|
message_barrier = Yes;
|
|
}
|
|
|
|
void
|
|
message_expire(void)
|
|
|
|
{
|
|
message_time.tv_sec = 0;
|
|
message_time.tv_usec = 0;
|
|
}
|
|
|
|
static void
|
|
message_flush(void)
|
|
|
|
{
|
|
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.
|
|
*/
|
|
|
|
|
|
static void
|
|
new_message_v(const 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) 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)
|
|
{
|
|
/* insert it in to message_buf */
|
|
message_buf[i] = estrdup(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)
|
|
{
|
|
/* we can display the message now */
|
|
i_message(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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(const 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(const 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)
|
|
{
|
|
display_move(0, y_message);
|
|
screen_cleareol(message_length);
|
|
free(message_current);
|
|
message_current = 0;
|
|
}
|
|
|
|
/* flush all pending messages */
|
|
message_flush();
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
static void
|
|
message_prompt_v(int so, const char *msgfmt, va_list ap)
|
|
|
|
{
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* void message_prompt(char *msgfmt, ...)
|
|
*
|
|
* Place a prompt in the message area (see message_prompt_v).
|
|
*/
|
|
|
|
void
|
|
message_prompt(const char *msgfmt, ...)
|
|
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, msgfmt);
|
|
message_prompt_v(Yes, msgfmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
message_prompt_plain(const 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;
|
|
|
|
/* allow room for null terminator */
|
|
size -= 1;
|
|
|
|
/* read loop */
|
|
while ((fflush(stdout), read(0, ptr, 1) > 0))
|
|
{
|
|
/* newline or return means we are done */
|
|
if ((ch = *ptr) == '\n' || ch == '\r')
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* handle special editing characters */
|
|
if (ch == ch_kill)
|
|
{
|
|
/* 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 */
|
|
if (cnt <= 0)
|
|
{
|
|
/* none to erase! */
|
|
putchar('\7');
|
|
}
|
|
else
|
|
{
|
|
fputs("\b \b", stdout);
|
|
ptr--;
|
|
cnt--;
|
|
}
|
|
}
|
|
/* check for character validity and buffer overflow */
|
|
else if (cnt == size || (numeric && !isdigit((int)ch)) ||
|
|
!isprint((int)ch))
|
|
{
|
|
/* not legal */
|
|
putchar('\7');
|
|
}
|
|
else
|
|
{
|
|
/* echo it and store it in the buffer */
|
|
putchar(ch);
|
|
ptr++;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
/* all done -- null terminate the string */
|
|
*ptr = '\0';
|
|
|
|
/* 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);
|
|
}
|
|
|
|
void
|
|
display_pagerstart()
|
|
|
|
{
|
|
display_clear();
|
|
}
|
|
|
|
void
|
|
display_pagerend()
|
|
|
|
{
|
|
char ch;
|
|
|
|
screen_standout("Hit any key to continue: ");
|
|
fflush(stdout);
|
|
(void) read(0, &ch, 1);
|
|
}
|
|
|
|
void
|
|
display_pager(const char *fmt, ...)
|
|
|
|
{
|
|
va_list ap;
|
|
|
|
int ch;
|
|
char readch;
|
|
char buffer[MAX_COLS];
|
|
char *data;
|
|
|
|
/* format into buffer */
|
|
va_start(ap, fmt);
|
|
(void) vsnprintf(buffer, MAX_COLS, fmt, ap);
|
|
va_end(ap);
|
|
data = buffer;
|
|
|
|
while ((ch = *data++) != '\0')
|
|
{
|
|
putchar(ch);
|
|
if (ch == '\n')
|
|
{
|
|
if (++curr_y >= screen_length - 1)
|
|
{
|
|
screen_standout("...More...");
|
|
fflush(stdout);
|
|
(void) read(0, &readch, 1);
|
|
putchar('\r');
|
|
switch(readch)
|
|
{
|
|
case '\r':
|
|
case '\n':
|
|
curr_y--;
|
|
break;
|
|
|
|
case 'q':
|
|
return;
|
|
|
|
default:
|
|
curr_y = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|