home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.muug.mb.ca
/
2014.06.ftp.muug.mb.ca.tar
/
ftp.muug.mb.ca
/
pub
/
src
/
top
/
display.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-02-01
|
19KB
|
990 lines
/*
* Top - a top users display for Berkeley Unix
*
* 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. NOTE: it is
* assumed that 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.
*/
#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#ifdef sunos4
#define KERNEL /* to get definition for PZERO */
#include <sys/param.h>
#undef KERNEL
#else
#include <sys/param.h>
#endif
#include <sys/dir.h>
#include <sys/user.h>
#ifdef scs
# define FLOAT /* for pctcpu in proc.h */
# include <sys/vm.h> /* for struct spt */
#endif
#include <sys/proc.h>
#include <sys/dk.h>
#include "screen.h" /* interface to screen package */
#include "layout.h" /* defines for screen position layout */
#include "top.h"
#include "top.local.h"
#include "boolean.h"
/* imported from screen.c */
extern int overstrike;
static int lmpid = 0;
static struct user u;
#ifdef scs
static struct spt pspt; /* for get_spt call */
#endif scs
char *sprintf();
char *printable();
/* Verbose process state names */
char *state_name[] =
{
"", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped"
};
/* process state names for the "STATE" column of the display */
char *state_abbrev[] =
{
"", "sleep", "WAIT", "run", "start", "zomb", "stop"
};
/* cpu state names for percentages */
char *cpu_state[] =
{
"user", "nice", "system", "idle"
};
/* screen positions for cpustate figures */
char x_cpustates[] = { 12, 24, 36, 50 };
i_loadave(mpid, avenrun)
int mpid;
#ifdef sun
long *avenrun;
#else
double *avenrun;
#endif sun
{
register int i;
/* i_loadave also clears the screen, since it is first */
clear();
printf("last pid: %5d; load averages", mpid);
for (i = 0; i < 3; i++)
{
printf("%c %5.2f",
i == 0 ? ':' : ',',
#ifdef sun
(double)avenrun[i] / FSCALE);
#else
avenrun[i]);
#endif
}
lmpid = mpid;
}
u_loadave(mpid, avenrun)
int mpid;
#ifdef sun
long *avenrun;
#else
double *avenrun;
#endif sun
{
register int i;
if (mpid != lmpid);
{
Move_to(x_lastpid, y_lastpid);
printf("%5d", mpid);
lmpid = mpid;
}
Move_to(x_loadave, y_loadave);
for (i = 0; i < 3; i++)
{
printf("%s%5.2f",
i == 0 ? "" : ", ",
#ifdef sun
(double)avenrun[i] / FSCALE);
#else
avenrun[i]);
#endif
}
}
static int ltotal = 0;
static int llength = 0;
static int lbrkdn[7];
i_procstates(total, brkdn)
int total;
int *brkdn;
{
register int i;
register int position;
/* write current number of processes and remember the value */
printf("%d processes:", total);
ltotal = total;
/* put out enough spaces to get to column 15 */
i = digits(total);
while (i++ < 4)
{
putchar(' ');
}
/* remember where we are to get length of the breakdown area */
position = fileptr(stdout);
/* write the breakdowns */
for (i = 1; i < 7; i++)
{
if (brkdn[i] != 0)
{
printf("%s%d %s%s",
i == 1 ? "" : ", ",
brkdn[i],
state_name[i],
(i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
}
}
/* calculate length of breakdown area */
llength = fileptr(stdout) - position;
/* remember breakdown figures */
bcopy((char *)brkdn, (char *)lbrkdn, sizeof(lbrkdn));
}
u_procstates(total, brkdn)
int total;
int *brkdn;
{
register int i;
register int position = No; /* does double duty */
/* update number of processes only if it has changed */
if (ltotal != total)
{
/* move and overwrite */
Move_to(x_procstate, y_procstate);
printf("%d", total);
/* if number of digits differs, rewrite the label */
if (digits(total) != digits(ltotal))
{
fputs(" processes:", stdout);
/* put out enough spaces to get to column 15 */
i = digits(total);
while (i++ < 4)
{
putchar(' ');
}
/* and remember that we are already there */
position = Yes;
}
/* save new total */
ltotal = total;
}
else if (bcmp((char *)brkdn, (char *)lbrkdn, sizeof(lbrkdn)) == 0)
{
/* nothing has changed -- don't bother */
return;
}
/* move to the right screen position for breakdown list */
if (!position)
{
Move_to(x_brkdn, y_brkdn);
}
/* keep track of how many characters are written */
position = fileptr(stdout);
/* write summary line */
for (i = 1; i < 7; i++)
{
if (brkdn[i] != 0)
{
printf("%s%d %s%s",
i == 1 ? "" : ", ",
brkdn[i],
state_name[i],
(i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
}
}
/* calculate new length of line */
position = fileptr(stdout) - position;
/* clear to end and remember new line length */
(void) clear_eol(llength - position);
llength = position;
/* save new data for next time through */
bcopy((char *)brkdn, (char *)lbrkdn, sizeof(lbrkdn));
}
i_cpustates(changes, total)
int *changes;
int total;
{
register int i;
register double percent;
printf("\nCpu states: ");
for (i = 0; i < CPUSTATES; i++)
{
percent = ((double)changes[i] / (double)total) * 100.0;
/* if (100.0 - percent <= 0.05) then it will print as 100% */
/* so we select the format accordingly */
printf((100.0 - percent <= 0.05) ? "%s%4.0f%% %s" : "%s%4.1f%% %s",
i == 0 ? "" : ", ",
percent,
cpu_state[i]);
}
printf("\n");
}
u_cpustates(changes, total)
int *changes;
int total;
{
register int i;
register double percent;
for (i = 0; i < CPUSTATES; i++)
{
Move_to(x_cpustates[i], y_cpustates);
percent = ((double)changes[i] / (double)total) * 100.0;
/* if (100.0 - percent <= 0.05) then it will print as 100% */
/* so we select the format accordingly */
printf((100.0 - percent <= 0.05) ? "%4.0f" : "%4.1f", percent);
}
}
z_cpustates()
{
register int i;
printf("\nCpu states: ");
for (i = 0; i < CPUSTATES; i++)
{
printf("%s %% %s", i == 0 ? "" : ", ", cpu_state[i]);
}
printf("\n");
}
static int lmem1, lmem2, lmem3, lmem4, lmem5;
i_memory(mem1, mem2, mem3, mem4, mem5)
int mem1, mem2, mem3, mem4, mem5;
{
register char *p;
/* mem2 and mem4 will be in parentheses and need special handling */
p = itoa(mem2);
*--p = '(';
/* print first half of the line */
printf("Memory: %5dK %6sK) real, ", mem1, p);
#ifndef sunos4
/* format mem4 for parentheses */
p = itoa(mem4);
*--p = '(';
/* print second half of line */
printf("%5dK %6sK) virtual, %5dK free", mem3, p, mem5);
#else
printf("%5dK free", mem5);
#endif
/* save "last" values */
lmem1 = mem1;
lmem2 = mem2;
lmem3 = mem3;
lmem4 = mem4;
lmem5 = mem5;
}
u_memory(mem1, mem2, mem3, mem4, mem5)
int mem1, mem2, mem3, mem4, mem5;
{
register int newp;
register char *p;
register char cursor_on_line = No;
/* preformat string if mem2 is different */
if ((newp = lmem2 != mem2))
{
p = itoa(mem2);
*--p = '(';
lmem2 = mem2;
}
/* handle mem1 and mem2 */
if (lmem1 != mem1)
{
Move_to(x_realmem, y_mem);
cursor_on_line = Yes;
printf("%5d", mem1);
lmem1 = mem1;
if (newp)
{
printf("K %6s", p);
}
}
else if (newp)
{
Move_to(x_realmem + 7, y_mem);
cursor_on_line = Yes;
printf("%6s", p);
}
#ifndef sunos4
/* preformat mem4 if different */
if ((newp = lmem4 != mem4))
{
p = itoa(mem4);
*--p = '(';
lmem4 = mem4;
}
/* handle mem3 and mem4 */
if (lmem3 != mem3)
{
Move_to(x_virtmem, y_mem);
cursor_on_line = Yes;
printf("%5d", mem3);
lmem3 = mem3;
if (newp)
{
printf("K %6s", p);
}
}
else if (newp)
{
Move_to(x_virtmem + 7, y_mem);
cursor_on_line = Yes;
printf("%6s", p);
}
#endif
/* handle mem5 */
if (lmem5 != mem5)
{
Move_to(x_free, y_mem);
cursor_on_line = Yes;
printf("%5d", mem5);
lmem5 = mem5;
}
/* make sure the cursor is on this line in an optimal way */
if (!cursor_on_line)
{
putchar('\n');
}
}
/*
* i_message is funny because it gets its message asynchrnously (with
* respect to screen updates).
*/
static char next_msg[85];
static int msglen = 0;
i_message()
{
putchar('\n');
if (next_msg[0] != '\0')
{
standout(next_msg);
next_msg[0] = '\0';
}
else if (msglen > 0)
{
(void) clear_eol(msglen);
msglen = 0;
}
}
u_message()
{
putchar('\n');
if (msglen > 0)
{
(void) clear_eol(msglen);
msglen = 0;
}
}
static char header_text[] =
"\n PID XXXXXXXX PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
/* 01234567 -- starts at header_text+7 */
#define header_length (sizeof(header_text) - 2)
i_header(part2)
char *part2;
{
/* copy parameter into appropriate place in the header */
(void) strncpy(header_text+7, part2, 8);
fputs(header_text, stdout);
}
/*ARGSUSED*/
u_header(part2)
char *part2; /* ignored */
{
Move_to(0, y_header);
}
#ifdef sun
#define percent_cpu(pp) ((double)(pp)->p_pctcpu / FSCALE)
#else
#define percent_cpu(pp) ((pp)->p_pctcpu)
#endif
#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
((pct) / (1.0 - exp((pp)->p_time * logcpu))))
#ifdef DEBUG
FILE *debug;
#endif
static void
fmt_proc(thisline, pp, get_userid)
char *thisline;
struct proc *pp;
char *(*get_userid)();
{
register long cputime;
register double pctcpu;
/* get the cpu usage and calculate the cpu percentages */
cputime = get_ucpu(pp);
pctcpu = percent_cpu(pp);
/* format for the line */
#define Proc_format \
"%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s"
/* memory usage is calculated differently for different architectures, */
/* we define Size and Resident appropriately: */
#ifdef pyr
#define Size pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize
#define Resident pp->p_rssize
#else
#ifdef scs
get_spt(pp->p_spti, &pspt); /* scs also needs this */
#define Size pspt.spt_usedpages
#define Resident pspt.spt_mempages
#else (everything else)
#define Size pp->p_tsize + pp->p_dsize + pp->p_ssize
#define Resident pp->p_rssize
#endif !scs
#endif !pyr
sprintf(thisline, Proc_format,
pp->p_pid,
(*get_userid)(pp->p_uid),
pp->p_pri - PZERO,
pp->p_nice - NZERO,
pagetok(Size),
pagetok(Resident),
state_abbrev[pp->p_stat],
cputime / 60l,
cputime % 60l,
100.0 * weighted_cpu(pctcpu, pp),
100.0 * pctcpu,
printable(u.u_comm));
}
i_process(line, pp, get_userid)
int line;
struct proc *pp;
char *(*get_userid)();
{
register char *thisline;
int len;
#ifdef DEBUG
debug = fopen("debug", "w");
#endif
/* calculate a pointer to the buffer for this line */
thisline = screenbuf[line];
/* format the line into our buffer */
fmt_proc(thisline, pp, get_userid);
/* write the line out */
putchar('\n');
fputs(thisline, stdout);
/* zero fill the rest of it */
len = strlen(thisline);
bzero(thisline + len, Display_width - len);
}
static int lastline = 0;
u_process(line, pp, get_userid)
int line;
struct proc *pp;
char *(*get_userid)();
{
register char *optr;
register char *nptr;
register int ch;
register int diff;
register int newcol = 1;
register int lastcol = 0;
char cursor_on_line = No;
char *thisline;
int screen_line = line + Header_lines;
static char newline[Display_width];
/* get a pointer to the old text for this line */
optr = thisline = screenbuf[line];
/* format the line into a temporary buffer */
fmt_proc(newline, pp, get_userid);
/* compare the two strings and only rewrite what has changed */
nptr = newline;
#ifdef DEBUG
fputs(optr, debug);
fputc('\n', debug);
fputs(nptr, 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 = *nptr++) != *optr)
{
if (screen_line - lastline == 1)
{
putchar('\n');
}
else
{
Move_to(0, screen_line);
}
cursor_on_line = Yes;
putchar(ch);
*optr = ch;
lastcol = 1;
}
optr++;
/*
* 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 = *nptr++) != *optr)
{
/* 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 screen buffer */
printf("%.*s", diff, &thisline[lastcol]);
}
else
{
/* use cursor addressing */
Move_to(newcol, screen_line);
cursor_on_line = Yes;
}
/* remember where the cursor is */
lastcol = newcol + 1;
}
else
{
/* already there, update position */
lastcol++;
}
/* write what we need to */
if (ch == '\0')
{
/* at the end--terminate with a clear-to-end-of-line */
(void) clear_eol(strlen(optr));
}
else
{
/* write the new character */
putchar(ch);
}
/* put the new character in the screen buffer */
*optr = ch;
}
/* update working column and screen buffer pointer */
newcol++;
optr++;
} while (ch != '\0');
/* zero out the rest of the line buffer -- MUST BE DONE! */
bzero(optr, Display_width - newcol);
/* remember where the current line is */
if (cursor_on_line)
{
lastline = screen_line;
}
}
static int last_hi = 0;
u_endscreen(hi)
register int hi;
{
register int screen_line = hi + Header_lines;
register int i;
if (smart_terminal)
{
if (hi < last_hi)
{
if (clear_to_end)
{
/* we can do this the easy way */
if (hi == 0)
{
/* extra positioning required */
putchar('\n');
}
else if (screen_line - lastline == 1)
{
putchar('\n');
}
else
{
Move_to(0, screen_line);
}
putcap(clear_to_end);
}
else
{
if (hi == 0)
{
putchar('\n');
(void) clear_eol(header_length);
putchar('\n');
}
else if (screen_line - lastline == 1)
{
putchar('\n');
}
else
{
Move_to(0, screen_line);
}
i = hi;
while ((void) clear_eol(strlen(screenbuf[i++])), i < last_hi)
{
putchar('\n');
}
}
}
last_hi = hi;
/* move the cursor to a pleasant place */
Move_to(x_idlecursor, y_idlecursor);
}
else
{
/* separate this display from the next with some vertical room */
fputs("\n\n", stdout);
}
}
/*VARARGS2*/
new_message(type, msgfmt, a1, a2, a3)
int type;
char *msgfmt;
int a1, a2, a3;
{
register int i;
/* first, format the message */
(void) sprintf(next_msg, msgfmt, a1, a2, a3);
if (msglen > 0)
{
/* message there already -- can we clear it? */
if (!overstrike)
{
/* yes -- write it and clear to end */
type ? standout(next_msg) : fputs(next_msg, stdout);
i = strlen(next_msg);
(void) clear_eol(msglen - i);
msglen = i;
next_msg[0] = '\0';
}
}
else
{
type ? standout(next_msg) : fputs(next_msg, stdout);
msglen = strlen(next_msg);
next_msg[0] = '\0';
}
}
clear_message()
{
if (clear_eol(msglen) == 1)
{
putchar('\r');
}
}
readline(buffer, size, numeric)
char *buffer;
int size;
int numeric;
{
register char *ptr = buffer;
register char ch;
register char cnt = 0;
register char maxcnt = 0;
/* allow room for null terminator */
size -= 1;
/* read loop */
while ((fflush(stdout), read(0, ptr, 1) > 0))
{
/* newline means we are done */
if ((ch = *ptr) == '\n')
{
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);
}
/*
* get_ucpu(pp) - retrieve the user structure associated with the proc
* structure pointed to by pp and return the cpu usage. The user
* structure is stored in the global structure "u" for later use.
* Since the user structure isn't always set up well when we try to get
* it, this routine also makes sure that the values it gets are
* reasonably sane so that they don't overflow any sprintf's.
*/
#define Max_cputime (10000*60 - 1) /* format for cputime is %4d:%02d */
get_ucpu(pp)
struct proc *pp;
{
register int retval;
#ifdef scs
(void) strcpy(u.u_comm, pp->p_infoname);
retval = pp->p_infotime.tv_sec;
#else !scs
if (getu(pp, &u) == -1)
{
(void) strcpy(u.u_comm, "<swapped>");
retval = 0;
}
else
{
/* set u_comm for system processes */
if (u.u_comm[0] == '\0')
{
if (pp->p_pid == 0)
{
(void) strcpy(u.u_comm, "Swapper");
}
else if (pp->p_pid == 2)
{
(void) strcpy(u.u_comm, "Pager");
}
}
#ifdef FOUR_ONE
retval = (int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz);
#else
retval = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec;
#endif
}
#endif !scs
/* make sure cputime returned is a reasonable value */
if (retval > Max_cputime)
{
retval = Max_cputime;
}
return (retval);
}
/*
* 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);
}