fc8ae86a8f
Submitted by: Antranig Vartanian <antranigv@freebsd.am> Reviewed by: imp, philip Differential Revision: https://reviews.freebsd.org/D22871
1364 lines
27 KiB
C
1364 lines
27 KiB
C
/*
|
|
* Top users/processes display for Unix
|
|
* Version 3
|
|
*
|
|
* This program may be freely redistributed,
|
|
* but this entire comment MUST remain intact.
|
|
*
|
|
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
|
|
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
* This file contains the routines that display information on the screen.
|
|
* Each section of the screen has two routines: one for initially writing
|
|
* all constant and dynamic text, and one for only updating the text that
|
|
* changes. The prefix "i_" is used on all the "initial" routines and the
|
|
* prefix "u_" is used for all the "updating" routines.
|
|
*
|
|
* ASSUMPTIONS:
|
|
* None of the "i_" routines use any of the termcap capabilities.
|
|
* In this way, those routines can be safely used on terminals that
|
|
* have minimal (or nonexistant) terminal capabilities.
|
|
*
|
|
* The routines are called in this order: *_loadave, i_timeofday,
|
|
* *_procstates, *_cpustates, *_memory, *_message, *_header,
|
|
* *_process, u_endscreen.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <termcap.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "screen.h" /* interface to screen package */
|
|
#include "layout.h" /* defines for screen position layout */
|
|
#include "display.h"
|
|
#include "top.h"
|
|
#include "machine.h" /* we should eliminate this!!! */
|
|
#include "utils.h"
|
|
|
|
#ifdef DEBUG
|
|
FILE *debug;
|
|
#endif
|
|
|
|
static int lmpid = 0;
|
|
static int last_hi = 0; /* used in u_process and u_endscreen */
|
|
static int lastline = 0;
|
|
|
|
#define lineindex(l) ((l)*screen_width)
|
|
|
|
|
|
/* things initialized by display_init and used thruout */
|
|
|
|
/* buffer of proc information lines for display updating */
|
|
static char *screenbuf = NULL;
|
|
|
|
static const char * const *procstate_names;
|
|
static const char * const *cpustate_names;
|
|
static const char * const *memory_names;
|
|
static const char * const *arc_names;
|
|
static const char * const *carc_names;
|
|
static const char * const *swap_names;
|
|
|
|
static int num_procstates;
|
|
static int num_cpustates;
|
|
static int num_memory;
|
|
static int num_swap;
|
|
|
|
static int *lprocstates;
|
|
static int *lcpustates;
|
|
static int *lmemory;
|
|
static int *lswap;
|
|
|
|
static int num_cpus;
|
|
static int *cpustate_columns;
|
|
static int cpustate_total_length;
|
|
static int cpustates_column;
|
|
|
|
static enum { OFF, ON, ERASE } header_status = ON;
|
|
|
|
static void summary_format(char *, int *, const char * const *);
|
|
static void line_update(char *, char *, int, int);
|
|
|
|
static int setup_buffer_bufsiz = 0;
|
|
static char * setup_buffer(char *, int);
|
|
|
|
int x_lastpid = 10;
|
|
int y_lastpid = 0;
|
|
int x_loadave = 33;
|
|
int x_loadave_nompid = 15;
|
|
int y_loadave = 0;
|
|
int x_procstate = 0;
|
|
int y_procstate = 1;
|
|
int x_brkdn = 15;
|
|
int y_brkdn = 1;
|
|
int x_mem = 5;
|
|
int y_mem = 3;
|
|
int x_arc = 5;
|
|
int y_arc = 4;
|
|
int x_carc = 5;
|
|
int y_carc = 5;
|
|
int x_swap = 6;
|
|
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(void)
|
|
{
|
|
int lines;
|
|
|
|
/* first, deallocate any previous buffer that may have been there */
|
|
if (screenbuf != NULL)
|
|
{
|
|
free(screenbuf);
|
|
}
|
|
|
|
/* calculate the current dimensions */
|
|
/* if operating in "dumb" mode, we only need one line */
|
|
lines = smart_terminal ? screen_length - Header_lines : 1;
|
|
|
|
if (lines < 0)
|
|
lines = 0;
|
|
|
|
/* now, allocate space for the screen buffer */
|
|
screenbuf = calloc(lines, screen_width);
|
|
if (screenbuf == NULL)
|
|
{
|
|
/* oops! */
|
|
return(-1);
|
|
}
|
|
|
|
/* return number of lines available */
|
|
/* for dumb terminals, pretend like we can show any amount */
|
|
return(smart_terminal ? lines : Largest);
|
|
}
|
|
|
|
int
|
|
display_updatecpus(struct statics *statics)
|
|
{
|
|
int lines;
|
|
int i;
|
|
|
|
/* call resize to do the dirty work */
|
|
lines = display_resize();
|
|
if (pcpu_stats)
|
|
num_cpus = statics->ncpus;
|
|
else
|
|
num_cpus = 1;
|
|
cpustates_column = 5; /* CPU: */
|
|
if (num_cpus > 1) {
|
|
cpustates_column += 1 + digits(num_cpus); /* CPU #: */
|
|
}
|
|
|
|
/* fill the "last" array with all -1s, to insure correct updating */
|
|
for (i = 0; i < num_cpustates * num_cpus; ++i) {
|
|
lcpustates[i] = -1;
|
|
}
|
|
|
|
return(lines);
|
|
}
|
|
|
|
int
|
|
display_init(struct statics * statics)
|
|
{
|
|
int lines;
|
|
const char * const *pp;
|
|
int *ip;
|
|
int i;
|
|
|
|
lines = display_updatecpus(statics);
|
|
|
|
/* only do the rest if we need to */
|
|
if (lines > -1)
|
|
{
|
|
/* save pointers and allocate space for names */
|
|
procstate_names = statics->procstate_names;
|
|
num_procstates = 8;
|
|
assert(num_procstates > 0);
|
|
lprocstates = calloc(num_procstates, sizeof(int));
|
|
|
|
cpustate_names = statics->cpustate_names;
|
|
|
|
swap_names = statics->swap_names;
|
|
num_swap = 7;
|
|
assert(num_swap > 0);
|
|
lswap = calloc(num_swap, sizeof(int));
|
|
num_cpustates = CPUSTATES;
|
|
assert(num_cpustates > 0);
|
|
lcpustates = calloc(num_cpustates * sizeof(int), statics->ncpus);
|
|
cpustate_columns = calloc(num_cpustates, sizeof(int));
|
|
|
|
memory_names = statics->memory_names;
|
|
num_memory = 7;
|
|
assert(num_memory > 0);
|
|
lmemory = calloc(num_memory, sizeof(int));
|
|
|
|
arc_names = statics->arc_names;
|
|
carc_names = statics->carc_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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* return number of lines available */
|
|
return(lines);
|
|
}
|
|
|
|
void
|
|
i_loadave(int mpid, double avenrun[])
|
|
{
|
|
int i;
|
|
|
|
/* i_loadave also clears the screen, since it is first */
|
|
top_clear();
|
|
|
|
/* mpid == -1 implies this system doesn't have an _mpid */
|
|
if (mpid != -1)
|
|
{
|
|
printf("last pid: %5d; ", mpid);
|
|
}
|
|
|
|
printf("load averages");
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
printf("%c %5.2f",
|
|
i == 0 ? ':' : ',',
|
|
avenrun[i]);
|
|
}
|
|
lmpid = mpid;
|
|
}
|
|
|
|
void
|
|
u_loadave(int mpid, double *avenrun)
|
|
{
|
|
int i;
|
|
|
|
if (mpid != -1)
|
|
{
|
|
/* change screen only when value has really changed */
|
|
if (mpid != lmpid)
|
|
{
|
|
Move_to(x_lastpid, y_lastpid);
|
|
printf("%5d", mpid);
|
|
lmpid = mpid;
|
|
}
|
|
|
|
/* i remembers x coordinate to move to */
|
|
i = x_loadave;
|
|
}
|
|
else
|
|
{
|
|
i = x_loadave_nompid;
|
|
}
|
|
|
|
/* move into position for load averages */
|
|
Move_to(i, y_loadave);
|
|
|
|
/* display new load averages */
|
|
/* we should optimize this and only display changes */
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
printf("%s%5.2f",
|
|
i == 0 ? "" : ", ",
|
|
avenrun[i]);
|
|
}
|
|
}
|
|
|
|
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).
|
|
*/
|
|
|
|
if (smart_terminal)
|
|
{
|
|
Move_to(screen_width - 8, 0);
|
|
}
|
|
else
|
|
{
|
|
fputs(" ", stdout);
|
|
}
|
|
#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 char *procstates_buffer = NULL;
|
|
|
|
/*
|
|
* *_procstates(total, brkdn, names) - print the process summary line
|
|
*
|
|
* Assumptions: cursor is at the beginning of the line on entry
|
|
* lastline is valid
|
|
*/
|
|
|
|
void
|
|
i_procstates(int total, int *brkdn)
|
|
{
|
|
int i;
|
|
|
|
procstates_buffer = setup_buffer(procstates_buffer, 0);
|
|
|
|
/* write current number of processes and remember the value */
|
|
printf("%d %s:", total, ps.thread ? "threads" : "processes");
|
|
ltotal = total;
|
|
|
|
/* put out enough spaces to get to column 15 */
|
|
i = digits(total);
|
|
while (i++ < (ps.thread ? 6 : 4))
|
|
{
|
|
putchar(' ');
|
|
}
|
|
|
|
/* format and print the process state summary */
|
|
summary_format(procstates_buffer, brkdn, procstate_names);
|
|
fputs(procstates_buffer, stdout);
|
|
|
|
/* save the numbers for next time */
|
|
memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
|
|
}
|
|
|
|
void
|
|
u_procstates(int total, int *brkdn)
|
|
{
|
|
static char *new = NULL;
|
|
int i;
|
|
|
|
new = setup_buffer(new, 0);
|
|
|
|
/* update number of processes only if it has changed */
|
|
if (ltotal != total)
|
|
{
|
|
/* move and overwrite */
|
|
if (x_procstate == 0) {
|
|
Move_to(x_procstate, y_procstate);
|
|
}
|
|
else {
|
|
/* cursor is already there...no motion needed */
|
|
assert(lastline == 1);
|
|
}
|
|
printf("%d", total);
|
|
|
|
/* if number of digits differs, rewrite the label */
|
|
if (digits(total) != digits(ltotal))
|
|
{
|
|
printf(" %s:", ps.thread ? "threads" : "processes");
|
|
/* put out enough spaces to get to column 15 */
|
|
i = digits(total);
|
|
while (i++ < (ps.thread ? 6 : 4))
|
|
{
|
|
putchar(' ');
|
|
}
|
|
/* cursor may end up right where we want it!!! */
|
|
}
|
|
|
|
/* save new total */
|
|
ltotal = total;
|
|
}
|
|
|
|
/* see if any of the state numbers has changed */
|
|
if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
|
|
{
|
|
/* format and update the line */
|
|
summary_format(new, brkdn, procstate_names);
|
|
line_update(procstates_buffer, new, x_brkdn, y_brkdn);
|
|
memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
|
|
}
|
|
}
|
|
|
|
void
|
|
i_cpustates(int *states)
|
|
{
|
|
int i = 0;
|
|
int value;
|
|
const char * const *names;
|
|
const char *thisname;
|
|
int *hstates = states;
|
|
int cpu;
|
|
|
|
for (cpu = 0; cpu < num_cpus; cpu++) {
|
|
names = cpustate_names;
|
|
|
|
/* print tag and bump lastline */
|
|
if (num_cpus == 1)
|
|
printf("\nCPU: ");
|
|
else {
|
|
value = printf("\nCPU %d: ", cpu);
|
|
while (value++ <= cpustates_column)
|
|
printf(" ");
|
|
}
|
|
lastline++;
|
|
|
|
/* now walk thru the names and print the line */
|
|
while ((thisname = *names++) != NULL)
|
|
{
|
|
if (*thisname != '\0')
|
|
{
|
|
/* retrieve the value and remember it */
|
|
value = *states++;
|
|
|
|
/* if percentage is >= 1000, print it as 100% */
|
|
printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
|
|
(i++ % num_cpustates) == 0 ? "" : ", ",
|
|
((float)value)/10.,
|
|
thisname);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* copy over values into "last" array */
|
|
states = hstates;
|
|
memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
|
|
}
|
|
|
|
void
|
|
u_cpustates(int *states)
|
|
{
|
|
int value;
|
|
const char * const *names;
|
|
const char *thisname;
|
|
int *hstates = states;
|
|
int *lp;
|
|
int *colp;
|
|
int cpu;
|
|
|
|
for (cpu = 0; cpu < num_cpus; cpu++) {
|
|
names = cpustate_names;
|
|
|
|
Move_to(cpustates_column, y_cpustates + cpu);
|
|
lastline = y_cpustates + cpu;
|
|
lp = lcpustates + (cpu * num_cpustates);
|
|
colp = cpustate_columns;
|
|
|
|
/* we could be much more optimal about this */
|
|
while ((thisname = *names++) != NULL)
|
|
{
|
|
if (*thisname != '\0')
|
|
{
|
|
/* did the value change since last time? */
|
|
if (*lp != *states)
|
|
{
|
|
/* yes, move and change */
|
|
Move_to(cpustates_column + *colp, y_cpustates + cpu);
|
|
lastline = y_cpustates + cpu;
|
|
|
|
/* retrieve value and remember it */
|
|
value = *states;
|
|
|
|
/* if percentage is >= 1000, print it as 100% */
|
|
printf((value >= 1000 ? "%4.0f" : "%4.1f"),
|
|
((double)value)/10.);
|
|
|
|
/* remember it for next time */
|
|
*lp = value;
|
|
}
|
|
}
|
|
|
|
/* increment and move on */
|
|
lp++;
|
|
states++;
|
|
colp++;
|
|
}
|
|
}
|
|
|
|
states = hstates;
|
|
}
|
|
|
|
void
|
|
z_cpustates(void)
|
|
{
|
|
int i = 0;
|
|
const char * const *names;
|
|
const char *thisname;
|
|
int cpu, value;
|
|
|
|
for (cpu = 0; cpu < num_cpus; cpu++) {
|
|
names = cpustate_names;
|
|
|
|
/* 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)
|
|
{
|
|
if (*thisname != '\0')
|
|
{
|
|
printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* fill the "last" array with all -1s, to insure correct updating */
|
|
for (i = 0; i < num_cpustates * num_cpus; ++i) {
|
|
lcpustates[i] = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* *_memory(stats) - print "Memory: " followed by the memory summary string
|
|
*
|
|
* Assumptions: cursor is on "lastline"
|
|
* for i_memory ONLY: cursor is on the previous line
|
|
*/
|
|
|
|
static char *memory_buffer = NULL;
|
|
|
|
void
|
|
i_memory(int *stats)
|
|
{
|
|
memory_buffer = setup_buffer(memory_buffer, 0);
|
|
|
|
fputs("\nMem: ", stdout);
|
|
lastline++;
|
|
|
|
/* format and print the memory summary */
|
|
summary_format(memory_buffer, stats, memory_names);
|
|
fputs(memory_buffer, stdout);
|
|
}
|
|
|
|
void
|
|
u_memory(int *stats)
|
|
{
|
|
static char *new = NULL;
|
|
|
|
new = setup_buffer(new, 0);
|
|
|
|
/* format the new line */
|
|
summary_format(new, stats, memory_names);
|
|
line_update(memory_buffer, new, x_mem, y_mem);
|
|
}
|
|
|
|
/*
|
|
* *_arc(stats) - print "ARC: " followed by the ARC summary string
|
|
*
|
|
* Assumptions: cursor is on "lastline"
|
|
* for i_arc ONLY: cursor is on the previous line
|
|
*/
|
|
static char *arc_buffer = NULL;
|
|
|
|
void
|
|
i_arc(int *stats)
|
|
{
|
|
arc_buffer = setup_buffer(arc_buffer, 0);
|
|
|
|
if (arc_names == NULL)
|
|
return;
|
|
|
|
fputs("\nARC: ", stdout);
|
|
lastline++;
|
|
|
|
/* format and print the memory summary */
|
|
summary_format(arc_buffer, stats, arc_names);
|
|
fputs(arc_buffer, stdout);
|
|
}
|
|
|
|
void
|
|
u_arc(int *stats)
|
|
{
|
|
static char *new = NULL;
|
|
|
|
new = setup_buffer(new, 0);
|
|
|
|
if (arc_names == NULL)
|
|
return;
|
|
|
|
/* format the new line */
|
|
summary_format(new, stats, arc_names);
|
|
line_update(arc_buffer, new, x_arc, y_arc);
|
|
}
|
|
|
|
|
|
/*
|
|
* *_carc(stats) - print "Compressed ARC: " followed by the summary string
|
|
*
|
|
* Assumptions: cursor is on "lastline"
|
|
* for i_carc ONLY: cursor is on the previous line
|
|
*/
|
|
static char *carc_buffer = NULL;
|
|
|
|
void
|
|
i_carc(int *stats)
|
|
{
|
|
carc_buffer = setup_buffer(carc_buffer, 0);
|
|
|
|
if (carc_names == NULL)
|
|
return;
|
|
|
|
fputs("\n ", stdout);
|
|
lastline++;
|
|
|
|
/* format and print the memory summary */
|
|
summary_format(carc_buffer, stats, carc_names);
|
|
fputs(carc_buffer, stdout);
|
|
}
|
|
|
|
void
|
|
u_carc(int *stats)
|
|
{
|
|
static char *new = NULL;
|
|
|
|
new = setup_buffer(new, 0);
|
|
|
|
if (carc_names == NULL)
|
|
return;
|
|
|
|
/* format the new line */
|
|
summary_format(new, stats, carc_names);
|
|
line_update(carc_buffer, new, x_carc, y_carc);
|
|
}
|
|
|
|
/*
|
|
* *_swap(stats) - print "Swap: " followed by the swap summary string
|
|
*
|
|
* Assumptions: cursor is on "lastline"
|
|
* for i_swap ONLY: cursor is on the previous line
|
|
*/
|
|
|
|
static char *swap_buffer = NULL;
|
|
|
|
void
|
|
i_swap(int *stats)
|
|
{
|
|
swap_buffer = setup_buffer(swap_buffer, 0);
|
|
|
|
if (swap_names == NULL)
|
|
return;
|
|
|
|
fputs("\nSwap: ", stdout);
|
|
lastline++;
|
|
|
|
/* format and print the swap summary */
|
|
summary_format(swap_buffer, stats, swap_names);
|
|
fputs(swap_buffer, stdout);
|
|
}
|
|
|
|
void
|
|
u_swap(int *stats)
|
|
{
|
|
static char *new = NULL;
|
|
|
|
new = setup_buffer(new, 0);
|
|
|
|
if (swap_names == NULL)
|
|
return;
|
|
|
|
/* format the new line */
|
|
summary_format(new, stats, swap_names);
|
|
line_update(swap_buffer, new, x_swap, y_swap);
|
|
}
|
|
|
|
/*
|
|
* *_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).
|
|
*/
|
|
|
|
#define NEXT_MSG_ADDLEN 5
|
|
static char *next_msg = NULL;
|
|
static int msglen = 0;
|
|
/* Invariant: msglen is always the length of the message currently displayed
|
|
on the screen (even when next_msg doesn't contain that message). */
|
|
|
|
void
|
|
i_message(void)
|
|
{
|
|
next_msg = setup_buffer(next_msg, NEXT_MSG_ADDLEN);
|
|
|
|
while (lastline < y_message)
|
|
{
|
|
fputc('\n', stdout);
|
|
lastline++;
|
|
}
|
|
if (next_msg[0] != '\0')
|
|
{
|
|
top_standout(next_msg);
|
|
msglen = strlen(next_msg);
|
|
next_msg[0] = '\0';
|
|
}
|
|
else if (msglen > 0)
|
|
{
|
|
(void) clear_eol(msglen);
|
|
msglen = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
u_message(void)
|
|
{
|
|
i_message();
|
|
}
|
|
|
|
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(const char *text)
|
|
{
|
|
char *s;
|
|
int width;
|
|
|
|
s = NULL;
|
|
width = screen_width;
|
|
header_length = strlen(text);
|
|
if (header_length >= width) {
|
|
s = strndup(text, width);
|
|
if (s == NULL)
|
|
return (NULL);
|
|
}
|
|
return (s);
|
|
}
|
|
|
|
/*
|
|
* *_header(text) - print the header for the process area
|
|
*
|
|
* Assumptions: cursor is on the previous line and lastline is consistent
|
|
*/
|
|
|
|
void
|
|
i_header(const char *text)
|
|
{
|
|
char *s;
|
|
|
|
s = trim_header(text);
|
|
if (s != NULL)
|
|
text = s;
|
|
|
|
if (header_status == ON)
|
|
{
|
|
putchar('\n');
|
|
fputs(text, stdout);
|
|
lastline++;
|
|
}
|
|
else if (header_status == ERASE)
|
|
{
|
|
header_status = OFF;
|
|
}
|
|
free(s);
|
|
}
|
|
|
|
void
|
|
u_header(const char *text __unused)
|
|
{
|
|
|
|
if (header_status == ERASE)
|
|
{
|
|
putchar('\n');
|
|
lastline++;
|
|
clear_eol(header_length);
|
|
header_status = OFF;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* *_process(line, thisline) - print one process line
|
|
*
|
|
* Assumptions: lastline is consistent
|
|
*/
|
|
|
|
void
|
|
i_process(int line, char *thisline)
|
|
{
|
|
char *p;
|
|
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 */
|
|
int len = strlen(thisline);
|
|
if (screen_width < len)
|
|
{
|
|
thisline[screen_width] = '\0';
|
|
}
|
|
|
|
/* write the line out */
|
|
fputs(thisline, stdout);
|
|
|
|
/* copy it in to our buffer */
|
|
base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
|
|
p = stpcpy(base, thisline);
|
|
|
|
/* zero fill the rest of it */
|
|
if (p - base < screen_width)
|
|
{
|
|
memset(p, 0, screen_width - (p - base));
|
|
}
|
|
}
|
|
|
|
void
|
|
u_process(int line, char *newline)
|
|
{
|
|
char *optr;
|
|
int screen_line = line + Header_lines;
|
|
char *bufferline;
|
|
|
|
/* remember a pointer to the current line in the screen buffer */
|
|
bufferline = &screenbuf[lineindex(line)];
|
|
|
|
/* truncate the line to conform to our current screen width */
|
|
int len = strlen(newline);
|
|
if (screen_width < len)
|
|
{
|
|
newline[screen_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 */
|
|
/* get positioned on the correct line */
|
|
if (screen_line - lastline == 1)
|
|
{
|
|
putchar('\n');
|
|
lastline++;
|
|
}
|
|
else
|
|
{
|
|
Move_to(0, screen_line);
|
|
lastline = screen_line;
|
|
}
|
|
|
|
/* now write the line */
|
|
fputs(newline, stdout);
|
|
|
|
/* copy it in to the buffer */
|
|
optr = stpcpy(bufferline, newline);
|
|
|
|
/* zero fill the rest of it */
|
|
if (optr - bufferline < screen_width)
|
|
{
|
|
memset(optr, 0, screen_width - (optr - bufferline));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
line_update(bufferline, newline, 0, line + Header_lines);
|
|
}
|
|
}
|
|
|
|
void
|
|
u_endscreen(int hi)
|
|
{
|
|
int screen_line = hi + Header_lines;
|
|
int i;
|
|
|
|
if (smart_terminal)
|
|
{
|
|
if (hi < last_hi)
|
|
{
|
|
/* need to blank the remainder of the screen */
|
|
/* but only if there is any screen left below this line */
|
|
if (lastline + 1 < screen_length)
|
|
{
|
|
/* efficiently move to the end of currently displayed info */
|
|
if (screen_line - lastline < 5)
|
|
{
|
|
while (lastline < screen_line)
|
|
{
|
|
putchar('\n');
|
|
lastline++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Move_to(0, screen_line);
|
|
lastline = screen_line;
|
|
}
|
|
|
|
if (clear_to_end)
|
|
{
|
|
/* we can do this the easy way */
|
|
putcap(clear_to_end);
|
|
}
|
|
else
|
|
{
|
|
/* use clear_eol on each line */
|
|
i = hi;
|
|
while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
|
|
{
|
|
putchar('\n');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
last_hi = hi;
|
|
|
|
/* move the cursor to a pleasant place */
|
|
Move_to(x_idlecursor, y_idlecursor);
|
|
lastline = y_idlecursor;
|
|
}
|
|
else
|
|
{
|
|
/* separate this display from the next with some vertical room */
|
|
fputs("\n\n", stdout);
|
|
}
|
|
}
|
|
|
|
void
|
|
display_header(int t)
|
|
{
|
|
|
|
if (t)
|
|
{
|
|
header_status = ON;
|
|
}
|
|
else if (header_status == ON)
|
|
{
|
|
header_status = ERASE;
|
|
}
|
|
}
|
|
|
|
void
|
|
new_message(int type, const char *msgfmt, ...)
|
|
{
|
|
va_list args;
|
|
size_t i;
|
|
|
|
va_start(args, msgfmt);
|
|
|
|
/* first, format the message */
|
|
vsnprintf(next_msg, setup_buffer_bufsiz + NEXT_MSG_ADDLEN,
|
|
msgfmt, args);
|
|
|
|
va_end(args);
|
|
|
|
if (msglen > 0)
|
|
{
|
|
/* message there already -- can we clear it? */
|
|
if (!overstrike)
|
|
{
|
|
/* yes -- write it and clear to end */
|
|
i = strlen(next_msg);
|
|
if ((type & MT_delayed) == 0)
|
|
{
|
|
if (type & MT_standout) {
|
|
top_standout(next_msg);
|
|
} else {
|
|
fputs(next_msg, stdout);
|
|
}
|
|
clear_eol(msglen - i);
|
|
msglen = i;
|
|
next_msg[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((type & MT_delayed) == 0)
|
|
{
|
|
if (type & MT_standout) {
|
|
top_standout(next_msg);
|
|
} else {
|
|
fputs(next_msg, stdout);
|
|
}
|
|
msglen = strlen(next_msg);
|
|
next_msg[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
clear_message(void)
|
|
{
|
|
if (clear_eol(msglen) == 1)
|
|
{
|
|
putchar('\r');
|
|
}
|
|
}
|
|
|
|
int
|
|
readline(char *buffer, int size, int numeric)
|
|
{
|
|
char *ptr = buffer;
|
|
char ch;
|
|
char cnt = 0;
|
|
char maxcnt = 0;
|
|
|
|
/* allow room for null terminator */
|
|
size -= 1;
|
|
|
|
/* read loop */
|
|
while ((fflush(stdout), read(0, ptr, 1) > 0))
|
|
{
|
|
/* newline means we are done */
|
|
if ((ch = *ptr) == '\n' || ch == '\r')
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* handle special editing characters */
|
|
if (ch == ch_kill)
|
|
{
|
|
/* kill line -- account for overstriking */
|
|
if (overstrike)
|
|
{
|
|
msglen += maxcnt;
|
|
}
|
|
|
|
/* return null string */
|
|
*buffer = '\0';
|
|
putchar('\r');
|
|
return(-1);
|
|
}
|
|
else if (ch == ch_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(ch)) ||
|
|
!isprint(ch))
|
|
{
|
|
/* not legal */
|
|
putchar('\7');
|
|
}
|
|
else
|
|
{
|
|
/* echo it and store it in the buffer */
|
|
putchar(ch);
|
|
ptr++;
|
|
cnt++;
|
|
if (cnt > maxcnt)
|
|
{
|
|
maxcnt = cnt;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* all done -- null terminate the string */
|
|
*ptr = '\0';
|
|
|
|
/* account for the extra characters in the message area */
|
|
/* (if terminal overstrikes, remember the furthest they went) */
|
|
msglen += overstrike ? maxcnt : cnt;
|
|
|
|
/* return either inputted number or string length */
|
|
putchar('\r');
|
|
return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
|
|
}
|
|
|
|
/* internal support routines */
|
|
|
|
static void
|
|
summary_format(char *str, int *numbers, const char * const *names)
|
|
{
|
|
char *p;
|
|
int num;
|
|
const char *thisname;
|
|
char rbuf[6];
|
|
|
|
/* format each number followed by its string */
|
|
p = str;
|
|
while ((thisname = *names++) != NULL)
|
|
{
|
|
/* get the number to format */
|
|
num = *numbers++;
|
|
|
|
/* display only non-zero numbers */
|
|
if (num > 0)
|
|
{
|
|
/* is this number in kilobytes? */
|
|
if (thisname[0] == 'K')
|
|
{
|
|
/* yes: format it as a memory value */
|
|
p = stpcpy(p, format_k(num));
|
|
|
|
/* skip over the K, since it was included by format_k */
|
|
p = stpcpy(p, thisname+1);
|
|
}
|
|
/* is this number a ratio? */
|
|
else if (thisname[0] == ':')
|
|
{
|
|
(void) snprintf(rbuf, sizeof(rbuf), "%.2f",
|
|
(float)*(numbers - 2) / (float)num);
|
|
p = stpcpy(p, rbuf);
|
|
p = stpcpy(p, thisname);
|
|
}
|
|
else
|
|
{
|
|
p = stpcpy(p, itoa(num));
|
|
p = stpcpy(p, thisname);
|
|
}
|
|
}
|
|
|
|
/* ignore negative numbers, but display corresponding string */
|
|
else if (num < 0)
|
|
{
|
|
p = stpcpy(p, thisname);
|
|
}
|
|
}
|
|
|
|
/* if the last two characters in the string are ", ", delete them */
|
|
p -= 2;
|
|
if (p >= str && p[0] == ',' && p[1] == ' ')
|
|
{
|
|
*p = '\0';
|
|
}
|
|
}
|
|
|
|
static void
|
|
line_update(char *old, char *new, int start, int line)
|
|
{
|
|
int ch;
|
|
int diff;
|
|
int newcol = start + 1;
|
|
int lastcol = start;
|
|
char cursor_on_line = false;
|
|
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 = true;
|
|
putchar(ch);
|
|
*old = ch;
|
|
lastcol = start + 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)
|
|
{
|
|
/* new character is different from old */
|
|
/* make sure the cursor is on top of this character */
|
|
diff = newcol - lastcol;
|
|
if (diff > 0)
|
|
{
|
|
/* some motion is required--figure out which is shorter */
|
|
if (diff < 6 && cursor_on_line)
|
|
{
|
|
/* overwrite old stuff--get it out of the old buffer */
|
|
printf("%.*s", diff, ¤t[lastcol-start]);
|
|
}
|
|
else
|
|
{
|
|
/* use cursor addressing */
|
|
Move_to(newcol, line);
|
|
cursor_on_line = true;
|
|
}
|
|
/* remember where the cursor is */
|
|
lastcol = newcol + 1;
|
|
}
|
|
else
|
|
{
|
|
/* already there, update position */
|
|
lastcol++;
|
|
}
|
|
|
|
/* write what we need to */
|
|
if (ch == '\0')
|
|
{
|
|
/* at the end--terminate with a clear-to-end-of-line */
|
|
(void) clear_eol(strlen(old));
|
|
}
|
|
else
|
|
{
|
|
/* write the new character */
|
|
putchar(ch);
|
|
}
|
|
/* put the new character in the screen buffer */
|
|
*old = ch;
|
|
}
|
|
|
|
/* update working column and screen buffer pointer */
|
|
newcol++;
|
|
old++;
|
|
|
|
} while (ch != '\0');
|
|
|
|
/* zero out the rest of the line buffer -- MUST BE DONE! */
|
|
diff = screen_width - newcol;
|
|
if (diff > 0)
|
|
{
|
|
memset(old, 0, diff);
|
|
}
|
|
|
|
/* remember where the current line is */
|
|
if (cursor_on_line)
|
|
{
|
|
lastline = line;
|
|
}
|
|
}
|
|
|
|
void
|
|
i_uptime(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);
|
|
}
|
|
}
|
|
|
|
void
|
|
i_battery(int nbat, int batt)
|
|
{
|
|
|
|
if (nbat > 0) {
|
|
printf("; battery: %d%%", batt);
|
|
}
|
|
}
|
|
|
|
#define SETUPBUFFER_MIN_SCREENWIDTH 80
|
|
#define SETUPBUFFER_REQUIRED_ADDBUFSIZ 2
|
|
|
|
static char *
|
|
setup_buffer(char *buffer, int addlen)
|
|
{
|
|
size_t len, old_len;
|
|
char *new_buffer;
|
|
|
|
setup_buffer_bufsiz = screen_width;
|
|
if (setup_buffer_bufsiz < SETUPBUFFER_MIN_SCREENWIDTH)
|
|
{
|
|
setup_buffer_bufsiz = SETUPBUFFER_MIN_SCREENWIDTH;
|
|
}
|
|
|
|
len = setup_buffer_bufsiz + addlen + SETUPBUFFER_REQUIRED_ADDBUFSIZ;
|
|
new_buffer = calloc(len, sizeof(char));
|
|
if (new_buffer == NULL)
|
|
{
|
|
errx(4, "can't allocate sufficient memory");
|
|
}
|
|
if (buffer != NULL)
|
|
{
|
|
old_len = strlen(buffer);
|
|
memcpy(new_buffer, buffer, old_len < len - 1 ? old_len : len - 1);
|
|
free(buffer);
|
|
}
|
|
|
|
return new_buffer;
|
|
}
|