sob@soma.UUCP (Stan Barber) (09/30/86)
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# top.c
# commands.c
# display.c
# kernel.c
# screen.c
# This archive created: Mon Sep 29 16:14:10 1986
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'top.c'" '(29035 characters)'
if test -f 'top.c'
then
echo shar: "will not over-write existing file 'top.c'"
else
cat << \SHAR_EOF > 'top.c'
char *copyright =
"Top, version 2.0, copyright (c) 1984, 1986, William LeFebvre";
/*
* Top users display for Berkeley Unix
* Version 2.0
*
* This program may be freely redistributed to other Unix sites, but this
* entire comment MUST remain intact.
*
* Copyright (c) 1984, 1986, William LeFebvre, Rice University
*
* This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
* Compile with the preprocessor constant "FOUR_ONE" set to get an
* executable that will run on Berkeley 4.1 Unix.
*
* The Sun kernel uses scaled integers instead of floating point.
* Compilation with the preprocessor variable "sun" gets an executable
* that will run on Sun Unix version 1.1 or later ("sun" is automatically
* set by the Sun C compiler).
*
* The Pyramid splits the stack size (p_ssize) into control stack and user
* stack sizes. Compilation with the preprocessor variable "pyr" gets an
* executable that will run on Pyramids ("pyr" is automatically set by the
* Pyramid C compiler).
*
* See the file "Changes" for more information on version-to-version changes.
*/
#include <stdio.h>
#include <pwd.h>
#include <nlist.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/param.h>
#include <sys/dir.h>
#include "masscomp.h"
#include <sys/user.h>
#include <sys/proc.h>
#ifndef MASSCOMP
#include <sys/dk.h>
#endif
#include <sys/vm.h>
/* includes specific to top */
#include "layout.h"
#include "screen.h" /* interface to screen package */
#include "top.h"
#include "top.local.h"
#include "boolean.h"
/* Special atoi routine returns either a non-negative number or one of: */
#define Infinity -1
#define Invalid -2
/* Size of the stdio buffer given to stdout */
#define Buffersize 2048
/* The buffer the stdio will use */
char stdoutbuf[Buffersize];
/* wish list for kernel symbols */
#ifdef MASSCOMP
struct nlist nlst[] = {
{ "_avenrun" }, /* load averages vector */
#define X_AVENRUN 0
{ "_ccpu" },
#define X_CCPU 1
{ "_mpid" }, /* maximum process id */
#define X_MPID 2
{ "_physmem" }, /* total available physical memory */
#define X_PHYSMEM 3
{ "_nswap" }, /* swap device size */
#define X_NSWAP 4
{ "_freemem" }, /* current free memory size */
#define X_FREEMEM 5
{ 0 },
};
#else
struct nlist nlst[] = {
{ "_avenrun" },
#define X_AVENRUN 0
{ "_ccpu" },
#define X_CCPU 1
{ "_cp_time" },
#define X_CP_TIME 2
{ "_hz" },
#define X_HZ 3
{ "_mpid" },
#define X_MPID 4
{ "_nproc" },
#define X_NPROC 5
{ "_proc" },
#define X_PROC 6
{ "_total" },
#define X_TOTAL 7
{ 0 },
};
#endif
/* build Signal masks */
#define Smask(s) (1 << ((s) - 1))
/* for system errors */
extern int errno;
/* for getopt: */
extern int optind;
extern char *optarg;
/* signal handling routines */
int leave();
int onalrm();
int tstop();
int nproc;
int mpid;
/* kernel "hz" variable -- clock rate */
long hz;
/* All this is to calculate the cpu state percentages */
long cp_time[CPUSTATES];
long cp_old[CPUSTATES];
long cp_change[CPUSTATES];
long total_change;
long mpid_offset;
long avenrun_offset;
long cp_time_offset;
long total_offset;
#ifdef sun
long ccpu;
long avenrun[3];
#else
double ccpu;
double avenrun[3];
#endif
double logcpu;
struct vmtotal total;
#ifdef MASSCOMP
struct cu_des cu_des;
#endif
struct proc *proc;
struct proc *pbase;
int bytes;
int initialized = No;
char *myname = "top";
jmp_buf jmp_int;
char input_waiting = No;
/* routines that don't return int */
struct passwd *getpwent();
char *username();
char *itoa();
char *ctime();
char *rindex();
char *kill_procs();
char *renice_procs();
int proc_compar();
long time();
/* different routines for displaying the user's identification */
/* (values assigned to get_userid) */
char *username();
char *itoa7();
/* display routines that need to be predeclared */
int i_loadave();
int u_loadave();
int i_procstates();
int u_procstates();
int i_cpustates();
int u_cpustates();
int i_memory();
int u_memory();
int i_header();
int u_header();
int i_process();
int u_process();
/* pointers to display routines */
int (*d_loadave)() = i_loadave;
int (*d_procstates)() = i_procstates;
int (*d_cpustates)() = i_cpustates;
int (*d_memory)() = i_memory;
int (*d_header)() = i_header;
int (*d_process)() = i_process;
/* buffer of proc information lines for display updating */
/* unfortunate that this must be declared globally */
char (* screenbuf)[Display_width];
main(argc, argv)
int argc;
char *argv[];
{
register struct proc *pp;
register struct proc **prefp;
register int i;
register int active_procs;
register int change;
static struct proc **pref;
static char tempbuf1[50];
static char tempbuf2[50];
int total_procs;
int old_sigmask;
#ifdef MASSCOMP
int proc_brkdn[9];
#else
int proc_brkdn[7];
#endif
int topn = Default_TOPN;
int delay = Default_DELAY;
int displays = 0; /* indicates unspecified */
long curr_time;
char *(*get_userid)() = username;
char *uname_field = "USERNAME";
#ifndef FOUR_ONE
char ch;
char msg_showing = 0;
int readfds;
struct timeval timeout;
#endif
char dostates = No;
char do_unames = Yes;
char do_init = No;
char interactive = Maybe;
char show_sysprocs = No;
char topn_specified = No;
char warnings = 0;
/* set the buffer for stdout */
#ifndef MASSCOMP /* this should be there, but is not for some reason */
setbuffer(stdout, stdoutbuf, Buffersize);
#endif
#ifdef MASSCOMP
nproc = MAX_PROC;
#endif
/* get our name */
if (argc > 0)
{
if ((myname = rindex(argv[0], '/')) == 0)
{
myname = argv[0];
}
else
{
myname++;
}
}
/* process options */
while ((i = getopt(argc, argv, "Sbinus:d:")) != EOF)
{
switch(i)
{
case 'u': /* display uid instead of name */
do_unames = No;
uname_field = " UID ";
get_userid = itoa7;
break;
case 'S': /* show system processes */
show_sysprocs = Yes;
break;
case 'i': /* go interactive regardless */
interactive = Yes;
break;
case 'n': /* batch, or non-interactive */
case 'b':
interactive = No;
break;
case 'd': /* number of displays to show */
if ((i = atoiwi(optarg)) == Invalid || i == 0)
{
fprintf(stderr,
"%s: warning: display count should be positive -- option ignored\n",
myname);
warnings++;
}
else
{
displays = i;
}
break;
case 's':
if ((delay = atoi(optarg)) < 0)
{
fprintf(stderr,
"%s: warning: seconds delay should be non-negative -- using default\n",
myname);
delay = Default_DELAY;
warnings++;
}
break;
default:
fprintf(stderr,
"Usage: %s [-Sbinu] [-d x] [-s x] [number]\n",
myname);
exit(1);
}
}
/* get count of top processes to display (if any) */
if (optind < argc)
{
if ((topn = atoiwi(argv[optind])) == Invalid)
{
fprintf(stderr,
"%s: warning: process display count should be non-negative -- using default\n",
myname);
topn = Default_TOPN;
warnings++;
}
else
{
topn_specified = Yes;
}
}
/* initialize the kernel memory interface */
init_kernel();
if (initialized != 1)
{
/* get the list of symbols we want to access in the kernel */
/* errno = 0; ??? */
nlist(VMUNIX, nlst);
if (nlst[0].n_type == 0)
{
fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
exit(2);
}
/* get the symbol values out of kmem */
#ifndef MASSCOMP
getkval(nlst[X_PROC].n_value, &proc, sizeof(int),
nlst[X_PROC].n_name);
getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int),
nlst[X_NPROC].n_name);
getkval(nlst[X_HZ].n_value, &hz, sizeof(int),
nlst[X_HZ].n_name);
#endif
getkval(nlst[X_CCPU].n_value, &ccpu, sizeof(int),
nlst[X_CCPU].n_name);
/* some calculations we use later */
mpid_offset = nlst[X_MPID].n_value;
avenrun_offset = nlst[X_AVENRUN].n_value;
#ifndef MASSCOMP
cp_time_offset = nlst[X_CP_TIME].n_value;
total_offset = nlst[X_TOTAL].n_value;
#endif
/* this is used in calculating WCPU -- calculate it ahead of time */
#ifdef sun
logcpu = log((double)ccpu / FSCALE);
#else
logcpu = log(ccpu);
#endif
/* allocate space for proc structure array and array of pointers */
bytes = nproc * sizeof(struct proc);
pbase = (struct proc *)sbrk(bytes);
pref = (struct proc **)sbrk(nproc * sizeof(struct proc *));
/* Just in case ... */
if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
exit(3);
}
/* initialize the hashing stuff */
if (do_unames)
{
init_hash();
}
if (do_init)
{
initialized = 1;
kill(0, SIGQUIT);
exit(99);
}
}
/* initialize termcap */
init_termcap();
/*
* Smart terminals can only display so many processes, precisely
* "screen_length - Header_lines". When run on dumb terminals, nothing
* fancy is done anyway, so we can display as many processes as the
* system can make. But since we never need to remember what is on the
* screen, we only allocate a buffer for one screen line.
*/
if (smart_terminal)
{
/* can only display (screen_length - Header_lines) processes */
i = screen_length - Header_lines;
if (topn > i) /* false even when topn == Infinity */
{
fprintf(stderr,
"%s: warning: this terminal can only display %d processes.\n",
myname, screen_length - Header_lines);
topn = i;
warnings++;
}
}
else
{
i = 1;
screen_length = nproc + Header_lines;
}
/* allocate space for the screen buffer */
screenbuf = (char (*)[])sbrk(i * Display_width);
if (screenbuf == (char (*)[])NULL)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
exit(4);
}
/* adjust for topn == Infinity */
if (topn == Infinity)
{
/*
* For smart terminals, infinity really means everything that can
* be displayed (which just happens to be "i" at this point).
* On dumb terminals, infinity means every process in the system!
* We only really want to do that if it was explicitly specified.
* This is always the case when "Default_TOPN != Infinity". But if
* topn wasn't explicitly specified and we are on a dumb terminal
* and the default is Infinity, then (and only then) we use
* "Nominal_TOPN" instead.
*/
#if Default_TOPN == Infinity
topn = smart_terminal ? i :
(topn_specified ? nproc : Nominal_TOPN);
#else
topn = smart_terminal ? i : nproc;
#endif
}
/* determine interactive state */
if (interactive == Maybe)
{
interactive = smart_terminal;
}
/* if # of displays not specified, fill it in */
if (displays == 0)
{
displays = smart_terminal ? Infinity : 1;
}
/* hold interrupt signals while setting up the screen and the handlers */
#ifndef FOUR_ONE
old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
#endif
init_screen();
signal(SIGINT, leave);
signal(SIGQUIT, leave);
signal(SIGTSTP, tstop);
#ifndef FOUR_ONE
sigsetmask(old_sigmask);
#endif
if (warnings)
{
fprintf(stderr, "....");
fflush(stderr); /* why must I do this? */
sleep(3 * warnings);
}
clear();
/* setup the jump buffer for stops */
if (setjmp(jmp_int) != 0)
{
/* control ends up here after an interrupt */
clear();
reset_display();
}
/*
* main loop -- repeat while display count is positive or while it
* indicates infinity (by being -1)
*/
while ((displays == -1) || (displays-- > 0))
{
/* read all the proc structures in one fell swoop */
#ifdef MASSCOMP
cu_des.p_max = MAX_PROC;
cu_des.t_max = 0; /* not interested in text */
cu_des.c_max = 0; /* not interested in cmap */
cinfo(&cu_des,pbase,NULL,NULL);
if (cu_des.p_ret < 0 ) nproc = 0;
else nproc = cu_des.p_ret;
/* this is not really the correct way to do this
* but it will do
*/
bzero((char *)cp_time,sizeof(cp_time));
bzero((char *)cp_old,sizeof(cp_time));
#else
getkval(proc, pbase, bytes, "proc array");
/* get the cp_time array */
getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time");
#endif
/* get load average array */
getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun");
/* get mpid -- process id of last process */
getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
#ifdef MASSCOMP
/* get total -- systemwide main memory usage structure */
getmem(&total);
#else
getkval(total_offset, &total, sizeof(total), "_total");
#endif
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
bzero(proc_brkdn, sizeof(proc_brkdn));
prefp = pref;
for (pp = pbase, i = 0; i < nproc; pp++, i++)
{
/*
* Place pointers to each valid proc structure in pref[].
* Process slots that are actually in use have a non-zero
* status field. Processes with SSYS set are system
* processes---these get ignored unless show_sysprocs is set.
*/
if (pp->p_stat != 0 &&
(show_sysprocs || ((pp->p_flag & SSYS) == 0)))
{
total_procs++;
#ifdef MASSCOMP
proc_brkdn[bitmask(pp->p_stat)]++;
total.t_arm += pp->p_rssize;
total.t_avm += (pp->p_tsize + pp->p_dsize + pp->p_ssize
- pp->p_rssize);
#else
proc_brkdn[pp->p_stat]++;
#endif
if (pp->p_stat != SZOMB)
{
*prefp++ = pp;
active_procs++;
}
}
#ifdef MASSCOMP
if (((pp->p_flag) & SSYS) != 0
|| pp->p_pid == 0 || pp->p_uid == 0)
cp_time[2] += pp->p_pctcpu;
else
cp_time[0] += pp->p_pctcpu;
if (pp->p_nice - NZERO) cp_time[1] += pp->p_pctcpu;
#endif
}
#ifdef MASSCOMP
cp_time[3] = 100 - cp_time[2] - cp_time[0];
total.t_rm = total.t_free+total.t_arm; /* gag! */
#endif
/* display the load averages */
(*d_loadave)(mpid, avenrun);
/*
* 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).
*/
curr_time = time(0);
if (smart_terminal)
{
Move_to(screen_width - 8, 0);
}
else
{
fputs(" ", stdout);
}
printf("%-8.8s\n", &(ctime(&curr_time)[11]));
/* display process state breakdown */
(*d_procstates)(total_procs, proc_brkdn);
/* calculate percentage time in each cpu state */
if (dostates) /* but not the first time */
{
total_change = 0;
for (i = 0; i < CPUSTATES; i++)
{
/* calculate changes for each state and overall change */
if (cp_time[i] < cp_old[i])
{
/* this only happens when the counter wraps */
change = (int)
((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
}
else
{
change = cp_time[i] - cp_old[i];
}
total_change += (cp_change[i] = change);
cp_old[i] = cp_time[i];
}
(*d_cpustates)(cp_change, total_change);
}
else
{
/* we'll do it next time */
if (smart_terminal)
{
z_cpustates();
}
else
{
putchar('\n');
}
dostates = Yes;
bzero(cp_old, sizeof(cp_old));
}
/* display main memory statistics */
(*d_memory)(
pagetok(total.t_rm), pagetok(total.t_arm),
pagetok(total.t_vm), pagetok(total.t_avm),
pagetok(total.t_free));
i = 0;
if (topn > 0)
{
/* update the header area */
(*d_header)(uname_field);
/* sort by cpu percentage (pctcpu) */
qsort(pref, active_procs, sizeof(struct proc *), proc_compar);
/* adjust for a lack of processes */
if (active_procs > topn)
{
active_procs = topn;
}
/*
* Now, show the top "n" processes. The method is slightly
* different for dumb terminals, so we will just use two very
* similar loops; this increases speed but also code size.
*/
if (smart_terminal)
{
for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
{
pp = *prefp;
(*d_process)(i, pp, get_userid);
}
}
else for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
{
pp = *prefp;
/* (only one buffer lien with dumb terminals) */
(*d_process)(0, pp, get_userid);
}
}
/* do end-screen processing */
u_endscreen(i);
/* now, flush the output buffer */
fflush(stdout);
/* only do the rest if we have more displays to show */
if (displays)
{
/* switch out for new display on smart terminals */
if (smart_terminal)
{
d_loadave = u_loadave;
d_procstates = u_procstates;
d_cpustates = u_cpustates;
d_memory = u_memory;
d_header = u_header;
d_process = u_process;
}
#ifndef FOUR_ONE
if (!interactive)
#endif
{
/* set up alarm */
signal(SIGALRM, onalrm);
alarm(delay);
/* wait for the rest of it .... */
pause();
}
#ifndef FOUR_ONE
else
{
/* wait for either input or the end of the delay period */
readfds = 1; /* for standard input */
timeout.tv_sec = delay;
timeout.tv_usec = 0;
if (select(32, &readfds, 0, 0, &timeout) > 0)
{
int newval;
char *errmsg;
/* something to read -- clear the message area first */
if (msg_showing)
{
if (smart_terminal)
{
putcap(clear_line);
}
msg_showing = No;
}
/* now read it and act on it */
read(0, &ch, 1);
switch(ch)
{
case '\f': /* redraw screen */
reset_display();
clear();
break;
case ' ': /* merely update display */
break;
case 'q': /* quit */
quit(0);
break;
case 'h': /* help */
case '?':
reset_display();
clear();
show_help();
standout("Hit any key to continue: ");
fflush(stdout);
read(0, &ch, 1);
clear();
break;
case 'e': /* show errors */
if (error_count() == 0)
{
standout(" Currently no errors to report.");
msg_showing = Yes;
}
else
{
reset_display();
clear();
show_errors();
standout("Hit any key to continue: ");
fflush(stdout);
read(0, &ch, 1);
clear();
}
break;
case 'n': /* new number */
case '#':
standout("Number of processes to show: ");
newval = readline(tempbuf1, 8, Yes);
putchar('\r');
if (newval > -1)
{
if (newval > (i = screen_length - Header_lines))
{
standout(
" This terminal can only display %d processes.",
i);
newval = i;
msg_showing = Yes;
break;
}
if (newval > topn)
{
/* zero fill appropriate part of screenbuf */
bzero(screenbuf[topn],
(newval - topn) * Display_width);
/* redraw header if need be */
if (topn == 0)
{
d_header = i_header;
}
}
topn = newval;
}
putcap(clear_line);
break;
case 's': /* new seconds delay */
standout("Seconds to delay: ");
if ((i = readline(tempbuf1, 8, Yes)) > -1)
{
delay = i;
}
putchar('\r');
putcap(clear_line);
break;
case 'd': /* change display count */
standout("Displays to show (currently %s): ",
displays == -1 ? "infinite" :
itoa(displays));
if ((i = readline(tempbuf1, 10, Yes)) > 0)
{
displays = i;
}
else if (i == 0)
{
quit(0);
}
putchar('\r');
putcap(clear_line);
break;
case 'k': /* kill program */
fputs("kill ", stdout);
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
{
if ((errmsg = kill_procs(tempbuf2)) != NULL)
{
putchar('\r');
standout(errmsg);
}
msg_showing = Yes;
}
else
{
putchar('\r');
}
putcap(clear_line);
break;
case 'r': /* renice program */
fputs("renice ", stdout);
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
{
if ((errmsg = renice_procs(tempbuf2)) != NULL)
{
putchar('\r');
standout(errmsg);
msg_showing = Yes;
}
}
else
{
putchar('\r');
}
putcap(clear_line);
break;
default:
standout(" Command not understood");
msg_showing = Yes;
}
}
else if (msg_showing)
{
if (smart_terminal)
{
putcap(clear_line);
}
msg_showing = No;
}
}
#endif
}
}
quit(0);
}
/*
* reset_display() - reset all the display routine pointers so that entire
* screen will get redrawn.
*/
reset_display()
{
d_loadave = i_loadave;
d_procstates = i_procstates;
d_cpustates = i_cpustates;
d_memory = i_memory;
d_header = i_header;
d_process = i_process;
}
readline(buffer, size, numeric)
char *buffer;
int size;
int numeric;
{
register char *ptr = buffer;
register char ch;
register char cnt = 0;
size -= 1;
while ((fflush(stdout), read(0, ptr, 1) > 0))
{
if ((ch = *ptr) == '\n')
{
break;
}
if (ch == ch_kill)
{
*buffer = '\0';
return(-1);
}
else if (ch == ch_erase)
{
if (cnt <= 0)
{
putchar('\7');
}
else
{
fputs("\b \b", stdout);
ptr--;
cnt--;
}
}
else if (cnt == size || (numeric && (ch < '0' || ch > '9')))
{
putchar('\7');
}
else
{
putchar(ch);
ptr++;
cnt++;
}
}
*ptr = '\0';
return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
}
/*
* signal handlers
*/
leave() /* exit under normal conditions -- INT handler */
{
end_screen();
exit(0);
}
tstop()
{
/* move to the lower left */
end_screen();
fflush(stdout);
#ifdef FOUR_ONE /* a 4.1 system */
/* send a STOP (uncatchable) to everyone in the process group */
kill(0, SIGSTOP);
/* reset the signal handler */
signal(SIGTSTP, tstop);
#else /* assume it is a 4.2 system */
/* default the signal handler action */
signal(SIGTSTP, SIG_DFL);
/* unblock the signal and send ourselves one */
sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
kill(0, SIGTSTP);
/* reset the signal handler */
signal(SIGTSTP, tstop);
#endif
/* reinit screen */
reinit_screen();
/* jump to appropriate place */
longjmp(jmp_int, 1);
/*NOTREACHED*/
}
quit(status) /* exit under duress */
int status;
{
end_screen();
exit(status);
}
onalrm()
{
return(0);
}
/*
* proc_compar - comparison function for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys (in descending order of importance) are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered as follows (from least
* to most important): WAIT, zombie, sleep, stop, start, run. The
* array declaration below maps a process state index into a number
* that reflects this ordering.
*/
#ifdef MASSCOMP
unsigned char sorted_state[] =
{
6, /* awaiting memory */
0, /* sleep */
4, /* tired */
2, /* idle */
1, /* run */
3, /* start */
8, /* zombie */
5, /* alert */
7 /* stop */
};
#else
unsigned char sorted_state[] =
{
0, /* not used */
3, /* sleep */
1, /* ABANDONED (WAIT) */
6, /* run */
5, /* start */
2, /* zombie */
4 /* stop */
};
#endif
proc_compar(pp1, pp2)
struct proc **pp1;
struct proc **pp2;
{
register struct proc *p1;
register struct proc *p2;
register int result;
#ifndef sun
register double dresult;
#endif
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/* compare percent cpu (pctcpu) */
#ifdef sun
if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0)
#else
if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
#endif
{
/* use cpticks to break the tie */
if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
{
/* use process state to break the tie */
#ifdef MASSCOMP
if ((result = sorted_state[bitmask(p2->p_stat)] -
sorted_state[bitmask(p1->p_stat)]) == 0)
#else
if ((result = sorted_state[p2->p_stat] -
sorted_state[p1->p_stat]) == 0)
#endif
{
/* use priority to break the tie */
if ((result = p2->p_pri - p1->p_pri) == 0)
{
/* use resident set size (rssize) to break the tie */
if ((result = p2->p_rssize - p1->p_rssize) == 0)
{
/* use total memory to break the tie */
#ifdef pyr
result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) -
(p1->p_tsize + p1->p_dsize + p1->p_ussize);
#else
result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) -
(p1->p_tsize + p1->p_dsize + p1->p_ssize);
#endif
}
}
}
}
}
#ifndef sun
else
{
result = dresult < 0.0 ? -1 : 1;
}
#endif
return(result);
}
/* routines to translate uids into a string */
char *user_name(euid, ruid)
int euid, ruid;
{
return(username(euid));
}
char *user_uid(euid, ruid)
int euid, ruid;
{
return(itoa7(euid));
}
/*
* These routines handle uid to username mapping.
* They use a hashing table scheme to reduce reading overhead.
*/
struct hash_el {
int uid;
char name[8];
};
#define H_empty -1
/* simple minded hashing function */
#define hashit(i) ((i) % Table_size)
struct hash_el hash_table[Table_size];
init_hash()
{
register int i;
register struct hash_el *h;
for (h = hash_table, i = 0; i < Table_size; h++, i++)
{
h->uid = H_empty;
}
}
char *username(uid)
register int uid;
{
register int index;
register int found;
register char *name;
/* This is incredibly naive, but it'll probably get changed anyway */
index = hashit(uid);
while ((found = hash_table[index].uid) != uid)
{
if (found == H_empty)
{
/* not here -- get it out of passwd */
index = get_user(uid);
break; /* out of while */
}
index = (index + 1) % Table_size;
}
return(hash_table[index].name);
}
enter_user(uid, name)
register int uid;
register char *name;
{
register int length;
register int index;
register int try;
static int uid_count = 0;
/* avoid table overflow -- insure at least one empty slot */
if (++uid_count >= Table_size)
{
fprintf(stderr, "table overflow: too many users\n");
quit(10);
}
index = hashit(uid);
while ((try = hash_table[index].uid) != H_empty)
{
if (try == uid)
{
return(index);
}
index = (index + 1) % Table_size;
}
hash_table[index].uid = uid;
strncpy(hash_table[index].name, name, 8);
return(index);
}
get_user(uid)
register int uid;
{
struct passwd *pwd;
register int last_index;
while ((pwd = getpwent()) != NULL)
{
last_index = enter_user(pwd->pw_uid, pwd->pw_name);
if (pwd->pw_uid == uid)
{
return(last_index);
}
}
return(enter_user(uid, itoa7(uid)));
}
atoiwi(str)
char *str;
{
register int len;
len = strlen(str);
if (len != 0)
{
if (strncmp(str, "infinity", len) == 0 ||
strncmp(str, "all", len) == 0 ||
strncmp(str, "maximum", len) == 0)
{
return(Infinity);
}
else if (str[0] == '-')
{
return(Invalid);
}
else
{
return(atoi(str));
}
}
return(0);
}
/*
* itoa - convert integer (decimal) to ascii string for positive numbers
* only (we don't bother with negative numbers since we know we
* don't use them).
*/
static char buffer[16]; /* shared by the next two routines */
char *itoa(val)
register int val;
{
register char *ptr;
ptr = buffer + sizeof(buffer);
*--ptr = '\0';
if (val == 0)
{
*--ptr = '0';
}
else while (val != 0)
{
*--ptr = (val % 10) + '0';
val /= 10;
}
return(ptr);
}
/*
* itoa7(val) - like itoa, except the number is right justified in a 7
* character field. This code is a duplication of itoa instead of
* a front end to a more general routine for efficiency.
*/
char *itoa7(val)
register int val;
{
register char *ptr;
ptr = buffer + sizeof(buffer);
*--ptr = '\0';
if (val == 0)
{
*--ptr = '0';
}
else while (val != 0)
{
*--ptr = (val % 10) + '0';
val /= 10;
}
while (ptr > buffer + sizeof(buffer) - 7)
{
*--ptr = ' ';
}
return(ptr);
}
#ifdef MASSCOMP
getmem(val)
struct vmtotal * val;
{
int nswap;
int freemem;
int physmem;
getkval(nlst[X_NSWAP].n_value, &nswap, sizeof(int),
nlst[X_NSWAP].n_name);
getkval(nlst[X_FREEMEM].n_value, &freemem, sizeof(int),
nlst[X_FREEMEM].n_name);
getkval(nlst[X_PHYSMEM].n_value, &physmem, sizeof(int),
nlst[X_PHYSMEM].n_name);
val->t_free = freemem; /* free memory pages */
/* this does not work for some reason */
val->t_rm = physmem; /* total real memory in use */
val->t_arm= 0; /* active real memory */
val->t_vm = dtoc(nswap); /* total virtual memory */
val->t_avm = 0; /* active virtual memory */
}
#endif
SHAR_EOF
fi
echo shar: "extracting 'commands.c'" '(8905 characters)'
if test -f 'commands.c'
then
echo shar: "will not over-write existing file 'commands.c'"
else
cat << \SHAR_EOF > 'commands.c'
/*
* Top users display for Berkeley Unix
*
* This file contains the routines that implement some of the interactive
* mode commands. Note that some of the commands are implemented in-line
* in "main". This is necessary because they change the global state of
* "top" (i.e.: changing the number of processes to display).
*/
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "sigdesc.h" /* generated automatically */
#include "boolean.h"
extern int errno;
extern int sys_nerr;
extern char *sys_errlist[];
extern char *copyright;
int err_compar();
char *err_string();
char *index();
/*
* show_help() - display the help screen; invoked in response to
* either 'h' or '?'.
*/
show_help()
{
fputs(copyright, stdout);
fputs("\n\n\
A top users display for Unix\n\
\n\
These single-character commands are available:\n\
\n\
^L - redraw screen\n\
q - quit\n\
h or ? - help; show this text\n\
d - change number of displays to show\n\
e - list errors generated by last \"kill\" or \"renice\" command\n\
k - kill processes; send a signal to a list of processes\n\
n or # - change number of processes to display\n\
r - renice a process\n\
s - change number of seconds to delay between updates\n\
\n\
\n", stdout);
}
/*
* Utility routines that help with some of the commands.
*/
char *next_field(str)
register char *str;
{
register char *temp;
if ((str = index(str, ' ')) == NULL)
{
return(NULL);
}
*str = '\0';
while (*++str == ' ') /* loop */;
return(str);
}
scanint(str, intp)
char *str;
int *intp;
{
register int val = 0;
register char ch;
while ((ch = *str++) != '\0')
{
if (isdigit(ch))
{
val = val * 10 + (ch - '0');
}
else if (isspace(ch))
{
break;
}
else
{
return(-1);
}
}
*intp = val;
return(0);
}
/*
* Some of the commands make system calls that could generate errors.
* These errors are collected up in an array of structures for later
* contemplation and display. Such routines return a string containing an
* error message, or NULL if no errors occurred. The next few routines are
* for manipulating and displaying these errors. We need an upper limit on
* the number of errors, so we arbitrarily choose 20.
*/
#define ERRMAX 20
struct errs /* structure for a system-call error */
{
int errno; /* value of errno (that is, the actual error) */
char *arg; /* argument that caused the error */
};
static struct errs errs[ERRMAX];
static int errcnt;
static char *err_toomany = " too many errors occurred";
static char *err_listem =
" Many errors occurred. Press `e' to display the list of errors.";
/* These macros get used to reset and log the errors */
#define ERR_RESET errcnt = 0
#define ERROR(p, e) if (errcnt >= ERRMAX) \
{ \
return(err_toomany); \
} \
else \
{ \
errs[errcnt].arg = (p); \
errs[errcnt++].errno = (e); \
}
/*
* err_string() - return an appropriate error string. This is what the
* command will return for displaying. If no errors were logged, then
* return NULL. The maximum length of the error string is defined by
* "STRMAX".
*/
#define STRMAX 80
char *err_string()
{
register char *ptr;
register struct errs *errp;
register int cnt = 0;
register int first = Yes;
register int currerr = -1;
int stringlen; /* characters still available in "string" */
char string[STRMAX];
/* if there are no errors, return NULL */
if (errcnt == 0)
{
return(NULL);
}
/* sort the errors */
qsort(errs, errcnt, sizeof(struct errs), err_compar);
/* need a space at the from of the error string */
string[0] = ' ';
string[1] = '\0';
stringlen = STRMAX - 2;
/* loop thru the sorted list, building an error string */
ptr = string;
while (cnt < errcnt)
{
errp = &(errs[cnt++]);
if (errp->errno != currerr)
{
if (currerr != -1)
{
if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
{
return(err_listem);
}
strcat(string, "; "); /* we know there's more */
}
currerr = errp->errno;
first = Yes;
}
if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
{
return(err_listem);
}
first = No;
}
/* add final message */
stringlen = str_adderr(string, stringlen, currerr);
/* return the error string */
return(stringlen == 0 ? err_listem : string);
}
/*
* str_adderr(str, len, err) - add an explanation of error "err" to
* the string "str".
*/
str_adderr(str, len, err)
char *str;
int len;
int err;
{
register char *msg;
register int msglen;
msg = err == 0 ? "Not a number" : sys_errlist[err];
msglen = strlen(msg) + 2;
if (len <= msglen)
{
return(0);
}
strcat(str, ": ");
strcat(str, msg);
return(len - msglen);
}
/*
* str_addarg(str, len, arg, first) - add the string argument "arg" to
* the string "str". This is the first in the group when "first"
* is set (indicating that a comma should NOT be added to the front).
*/
str_addarg(str, len, arg, first)
char *str;
int len;
char *arg;
int first;
{
register int arglen;
arglen = strlen(arg);
if (!first)
{
arglen += 2;
}
if (len <= arglen)
{
return(0);
}
if (!first)
{
strcat(str, ", ");
}
strcat(str, arg);
return(len - arglen);
}
/*
* err_compar(p1, p2) - comparison routine used by "qsort"
* for sorting errors.
*/
err_compar(p1, p2)
register struct errs *p1, *p2;
{
register int result;
if ((result = p1->errno - p2->errno) == 0)
{
return(strcmp(p1->arg, p2->arg));
}
return(result);
}
/*
* error_count() - return the number of errors currently logged.
*/
error_count()
{
return(errcnt);
}
/*
* show_errors() - display on stdout the current log of errors.
*/
show_errors()
{
register int cnt = 0;
register struct errs *errp = errs;
printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
while (cnt++ < errcnt)
{
printf("%5s: %s\n", errp->arg,
errp->errno == 0 ? "Not a number" : sys_errlist[errp->errno]);
errp++;
}
}
/*
* kill_procs(str) - send signals to processes, much like the "kill"
* command does; invoked in response to 'k'.
*/
char *kill_procs(str)
char *str;
{
register char *nptr;
register char *optr;
int signum = SIGTERM; /* default */
int procnum;
char badnum = 0;
struct sigdesc *sigp;
ERR_RESET;
if (str[0] == '-')
{
/* explicit signal specified */
if ((optr = nptr = next_field(str)) == NULL)
{
return(" kill: no processes specified");
}
if (isdigit(str[1]))
{
scanint(str + 1, &signum);
if (signum <= 0 || signum >= NSIG)
{
return(" invalid signal number");
}
}
else
{
/* terminate the end of the signal name */
while (*--optr == ' ');
*++optr = '\0';
/* translate the name into a number */
for (sigp = sigdesc; sigp->name != NULL; sigp++)
{
if (strcmp(sigp->name, str + 1) == 0)
{
signum = sigp->number;
break;
}
}
/* was it ever found */
if (sigp->name == NULL)
{
return(" bad signal name");
}
}
/* put the new pointer in place */
str = nptr;
}
/* loop thru the string, killing processes */
do
{
if (scanint(str, &procnum) == -1)
{
ERROR(str, 0);
}
else if (kill(procnum, signum) == -1)
{
/* chalk up an error */
ERROR(str, errno);
}
} while ((str = next_field(str)) != NULL);
/* return appropriate error string */
return(err_string());
}
/*
* renice_procs(str) - change the "nice" of processes, much like the
* "renice" command does; invoked in response to 'r'.
*/
char *renice_procs(str)
char *str;
{
register char negate;
int prio;
int procnum;
ERR_RESET;
/* allow for negative priority values */
if ((negate = *str == '-'))
{
/* move past the minus sign */
str++;
}
/* use procnum as a temporary holding place and get the number */
procnum = scanint(str, &prio);
/* negate if necessary */
if (negate)
{
prio = -prio;
}
/* check for validity */
if (procnum == -1 || prio <= PRIO_MIN || prio >= PRIO_MAX)
{
return(" bad priority value");
}
/* move to the first process number */
if ((str = next_field(str)) == NULL)
{
return(" no processes specified");
}
/* loop thru the process numbers, renicing each one */
do
{
if (scanint(str, &procnum) == -1)
{
ERROR(str, 0);
}
else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
{
ERROR(str, errno);
}
} while ((str = next_field(str)) != NULL);
/* return appropriate error string */
return(err_string());
}
SHAR_EOF
fi
echo shar: "extracting 'display.c'" '(12497 characters)'
if test -f 'display.c'
then
echo shar: "will not over-write existing file 'display.c'"
else
cat << \SHAR_EOF > 'display.c'
/*
* 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 <sys/param.h>
#include <sys/dir.h>
#include "masscomp.h"
#include <sys/user.h>
#include <sys/proc.h>
#ifndef MASSCOMP
#include <sys/dk.h>
#endif
#include "screen.h" /* interface to screen package */
#include "layout.h" /* defines for screen position layout */
#include "top.h"
#include "boolean.h"
static int lmpid = 0;
static struct user u;
char *printable();
/* Verbose process state names */
#ifdef MASSCOMP
char *state_name[] = {
"sleeping", "running", "idle", "runnable","tired", "alert",
"awaiting memory", "stopped", "zombie"
};
#else
char *state_name[] =
{
"", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped"
};
#endif
/* process state names for the "STATE" column of the display */
#ifdef MASSCOMP
char *state_abbrev[] = {
"sleep", "run", "idle", "avail","tired", "alert",
"mem", "stop", "zomb"
};
#else
char *state_abbrev[] =
{
"", "sleep", "WAIT", "run", "start", "zomb", "stop"
};
#endif
/* 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;
printf("last pid: %5d; load averages", mpid);
for (i = 0; i < 3; i++)
{
printf("%c %4.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%4.2f",
i == 0 ? "" : ", ",
#ifdef sun
(double)avenrun[i] / FSCALE);
#else
avenrun[i]);
#endif
}
}
static int ltotal = 0;
#ifdef MASSCOMP
static int lbrkdn[9];
#else
static int lbrkdn[7];
#endif
i_procstates(total, brkdn)
int total;
int *brkdn;
{
register int i;
printf("%2d processes", total); /* ??? */
ltotal = total;
#ifdef MASSCOMP
for (i = 0; i < 9; i++)
#else
for (i = 1; i < 7; i++)
#endif
{
if (brkdn[i] != 0)
{
printf("%c %d %s%s",
i == 1 ? ':' : ',',
brkdn[i],
state_name[i],
(i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
}
}
bcopy(brkdn, lbrkdn, sizeof(lbrkdn));
}
u_procstates(total, brkdn)
int total;
int *brkdn;
{
register int i;
if (ltotal != total)
{
Move_to(x_procstate, y_procstate);
printf("%d ", total);
ltotal = total;
}
else if (bcmp(brkdn, lbrkdn, sizeof(lbrkdn)) == 0)
{
return;
}
Move_to(x_brkdn, y_brkdn);
#ifdef MASSCOMP
for (i = 0; i < 9; i++)
#else
for (i = 1; i < 7; i++)
#endif
{
if (brkdn[i] != 0)
{
printf("%s%d %s%s",
i == 1 ? ':' : ',',
brkdn[i],
state_name[i],
(i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
}
}
putcap(clear_line);
bcopy(brkdn, lbrkdn, sizeof(lbrkdn));
}
i_cpustates(changes, total)
int *changes;
int total;
{
register int i;
printf("\nCpu states: ");
if (total){
for (i = 0; i < CPUSTATES; i++)
{
printf("%s%4.1f%% %s",
i == 0 ? "" : ", ",
((float)changes[i] / (float)total) * 100.0,
cpu_state[i]);
}
}
printf("\n");
}
u_cpustates(changes, total)
int *changes;
int total;
{
register int i;
if (total){
for (i = 0; i < CPUSTATES; i++)
{
Move_to(x_cpustates[i], y_cpustates);
printf("%4.1f",
((float)changes[i] / (float)total) * 100.0);
}
}
}
z_cpustates()
{
register int i;
printf("\nCpu states: ");
for (i = 0; i < CPUSTATES; i++)
{
printf("%s %% %s", i == 0 ? "" : ", ", cpu_state[i]);
}
printf("\n");
}
i_memory(i1, i2, i3, i4, i5)
int i1, i2, i3, i4, i5;
{
printf("Memory: %4dK (%4dK) real, %5dK (%5dK) virtual, %4dK free",
i1, i2, i3, i4, i5);
}
u_memory(i1, i2, i3, i4, i5)
int i1, i2, i3, i4, i5;
{
Move_to(x_realmem, y_mem);
printf("%4dK (%4d", i1, i2);
Move_to(x_virtmem, y_mem);
printf("%5dK (%5d", i3, i4);
Move_to(x_free, y_mem);
printf("%4d", i5);
}
i_header(f2)
char *f2;
{
printf(
"\n\n PID %s PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND",
f2);
}
u_header()
{
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))))
#define Proc_format "%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s"
#ifdef DEBUG
FILE *debug;
#endif
i_process(line, pp, get_userid)
int line;
struct proc *pp;
char *(*get_userid)();
{
register long cputime;
register double pctcpu;
register char *thisline;
int len;
#ifdef DEBUG
debug = fopen("debug", "w");
#endif
/* calculate a pointer to the buffer for this line */
thisline = screenbuf[line];
/* get the cpu usage and calculate the cpu percentages */
cputime = get_ucpu(pp);
#ifdef MASSCOMP
if (cputime == 0)
cputime = (pp->p_ru.ru_utime.tv_sec + pp->p_ru.ru_stime.tv_sec);
#endif
pctcpu = percent_cpu(pp);
/* format the line */
sprintf(thisline, Proc_format,
pp->p_pid,
(*get_userid)(pp->p_uid),
pp->p_pri - PZERO,
pp->p_nice - NZERO,
#ifdef pyr
pagetok(pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize),
#else
pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize),
#endif
pagetok(pp->p_rssize),
#ifdef MASSCOMP
state_abbrev[bitmask(pp->p_stat)],
#else
state_abbrev[pp->p_stat],
#endif
cputime / 60l,
cputime % 60l,
#ifdef MASSCOMP
weighted_cpu(pctcpu,pp),
pctcpu,
#else
100.0 * weighted_cpu(pctcpu, pp),
100.0 * pctcpu,
#endif
printable(u.u_comm));
/* 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;
register long cputime;
register double pctcpu;
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];
/* get the cpu usage and calculate the cpu percentages */
cputime = get_ucpu(pp);
pctcpu = percent_cpu(pp);
/* format the line */
sprintf(newline, Proc_format,
pp->p_pid,
(*get_userid)(pp->p_uid),
pp->p_pri - PZERO,
pp->p_nice - NZERO,
#ifdef pyr
pagetok(pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize),
#else
pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize),
#endif
pagetok(pp->p_rssize),
#ifdef MASSCOMP
state_abbrev[bitmask(pp->p_stat)],
#else
state_abbrev[pp->p_stat],
#endif
cputime / 60l,
cputime % 60l,
#ifdef MASSCOMP
weighted_cpu(pctcpu,pp),
pctcpu,
#else
100.0 * weighted_cpu(pctcpu, pp),
100.0 * pctcpu,
#endif
printable(u.u_comm));
/* 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 */
/* put the new character in the screen buffer */
*optr = ch;
/* 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 */
putcap(clear_line);
}
else
{
/* write the new character */
putchar(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;
if (smart_terminal)
{
if (hi < last_hi)
{
if (hi == 0)
{
putchar('\n');
putchar('\n');
putcap(clear_line);
putchar('\n');
}
else if (screen_line - lastline == 1)
{
putchar('\n');
}
else
{
Move_to(0, screen_line);
}
while (--last_hi > hi)
{
putcap(clear_line);
putchar('\n');
}
putcap(clear_line);
}
else
{
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);
}
}
/*
* 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.
*/
get_ucpu(pp)
struct proc *pp;
{
if (getu(pp, &u) == -1)
{
strcpy(u.u_comm, "<swapped>");
return(0);
}
else
{
/* set u_comm for system processes */
if (u.u_comm[0] == '\0')
{
if (pp->p_pid == 0)
{
strcpy(u.u_comm, "Swapper");
}
else if (pp->p_pid == 2)
{
strcpy(u.u_comm, "Pager");
}
}
#ifdef FOUR_ONE
return((int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz));
#else
return(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec);
#endif
}
}
/*
* 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);
}
#ifdef MASSCOMP
/*
* bitmask returns an integer representing the first bit (from the right)
* detected on in the passed arguement...
*/
bitmask(x)
int x;
{
int i,j;
i = 1;
for (j=0;j<8;j++)
{
if (x & i<<j) return(j);
}
}
SHAR_EOF
fi
echo shar: "extracting 'kernel.c'" '(3138 characters)'
if test -f 'kernel.c'
then
echo shar: "will not over-write existing file 'kernel.c'"
else
cat << \SHAR_EOF > 'kernel.c'
/*
* Top - a top users display for Berkeley Unix
*
* This file contains all the routines that retrieve values from
* kernel and user memory.
*/
#include <stdio.h>
#include <sys/param.h>
#include <sys/dir.h>
#include "masscomp.h"
#ifndef MASSCOMP
#if defined(FOUR_ONE) || defined(pyr)
#include <sys/pte.h>
#else
#include <machine/pte.h>
#endif
#endif
#include <sys/user.h>
#include <sys/proc.h>
#include "top.local.h"
/* useful externals */
extern int errno;
extern char *sys_errlist[];
static int kmem = -1;
static int mem = -1;
init_kernel()
{
/* open kmem and mem */
if ((kmem = open(KMEM, 0)) < 0)
{
perror(KMEM);
exit(20);
}
if ((mem = open(MEM, 0)) < 0)
{
perror(MEM);
exit(21);
}
}
/*
* getu(p, u) - get the user structure for the process whose proc structure
* is pointed to by p. The user structure is put in the buffer pointed
* to by u. Return 0 if successful, -1 on failure (such as the process
* being swapped out).
*/
getu(p, u)
register struct proc *p;
struct user *u;
{
struct pte uptes[UPAGES];
register caddr_t upage;
register struct pte *pte;
register nbytes, n;
/*
* Check if the process is currently loaded or swapped out. The way we
* get the u area is totally different for the two cases. For this
* application, we just don't bother if the process is swapped out.
*/
if ((p->p_flag & SLOAD) == 0)
{
return(-1);
}
/*
* Process is currently in memory, we hope!
*/
if (!getkval(p->p_addr, uptes, sizeof(uptes), "!p->p_addr"))
{
/* we can't seem to get to it, so pretend it's swapped out */
return(-1);
}
upage = (caddr_t)u;
pte = uptes;
for (nbytes = sizeof(struct user); nbytes > 0; nbytes -= NBPG)
{
lseek(mem, pte++->pg_pfnum * NBPG, 0);
n = MIN(nbytes, NBPG);
if (read(mem, upage, n) != n)
{
/* we can't seem to get to it, so pretend it's swapped out */
return(-1);
}
upage += n;
}
return(0);
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
getkval(offset, ptr, size, refstr)
long offset;
int *ptr;
int size;
char *refstr;
{
if (lseek(kmem, offset, 0) == -1)
{
if (*refstr == '!')
{
refstr++;
}
fprintf(stderr, "%s: lseek to %s: %s\n",
KMEM, refstr, sys_errlist[errno]);
quit(22);
}
if (read(kmem, ptr, size) == -1)
{
if (*refstr == '!')
{
/* we lost the race with the kernel, process isn't in memory */
return(0);
}
else
{
fprintf(stderr, "%s: reading %s: %s\n",
KMEM, refstr, sys_errlist[errno]);
quit(23);
}
}
return(1);
}
SHAR_EOF
fi
echo shar: "extracting 'screen.c'" '(4485 characters)'
if test -f 'screen.c'
then
echo shar: "will not over-write existing file 'screen.c'"
else
cat << \SHAR_EOF > 'screen.c'
/*
* Top - a top users display for Berkeley Unix
*
* This file contains the routines that interface to termcap and stty/gtty.
*/
#include <stdio.h>
#include <sgtty.h>
#include "screen.h"
#include "boolean.h"
extern char *myname;
int putstdout();
int scrolls;
int hardcopy;
int screen_length;
int screen_width;
char ch_erase;
char ch_kill;
char smart_terminal;
char PC;
char *tgetstr();
char *tgoto();
char termcap_buf[1024];
char init_buf[1024];
char string_buffer[1024];
char home[15];
char lower_left[15];
char *clear_line;
char *clear_screen;
char *cursor_motion;
char *start_standout;
char *end_standout;
char *terminal_init;
char *terminal_end;
short ospeed;
static struct sgttyb old_settings;
static struct sgttyb new_settings;
static char is_a_terminal = No;
init_termcap()
{
char *bufptr;
char *PCptr;
char *term_name;
char *temp_ptr;
char *getenv();
int status;
/* assume we have a smart terminal until proven otherwise */
smart_terminal = Yes;
/* now get terminal name and termcap entry */
term_name = getenv("TERM");
if ((status = tgetent(termcap_buf, term_name)) != 1)
{
if (status == -1)
{
fprintf(stderr, "%s: can't open termcap file\n", myname);
}
else
{
fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
myname, getenv("TERM"));
}
/* pretend it's dumb and proceed */
smart_terminal = No;
return;
}
/* these immediately indicate a very stupid terminal */
if (tgetflag("hc") || tgetflag("os"))
{
smart_terminal = No;
return;
}
/* set up common terminal capabilities */
if ((screen_length = tgetnum("li")) <= 0)
{
screen_length = smart_terminal = 0;
return;
}
/* screen_width is a little different */
if ((screen_width = tgetnum("co")) == -1)
{
screen_width = 79;
}
else
{
screen_width -= 1;
}
/* initialize the pointer into the termcap string buffer */
bufptr = string_buffer;
/* get necessary capabilities */
if ((clear_line = tgetstr("ce", &bufptr)) == NULL ||
(clear_screen = tgetstr("cl", &bufptr)) == NULL ||
(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
{
smart_terminal = No;
return;
}
/* get some more sophisticated stuff -- these are optional */
terminal_init = tgetstr("ti", &bufptr);
terminal_end = tgetstr("te", &bufptr);
start_standout = tgetstr("so", &bufptr);
end_standout = tgetstr("se", &bufptr);
/* pad character */
PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
/* set convenience strings */
strcpy(home, tgoto(cursor_motion, 0, 0));
strcpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1));
/* if stdout is not a terminal, pretend we are a dumb terminal */
if (gtty(1, &old_settings) == -1)
{
smart_terminal = No;
}
}
init_screen()
{
/* get the old settings for safe keeping */
if (gtty(1, &old_settings) == 0)
{
/* copy the settings so we can modify them */
new_settings = old_settings;
/* turn on CBREAK and turn off character echo and tab expansion */
new_settings.sg_flags |= CBREAK;
new_settings.sg_flags &= ~(ECHO|XTABS);
stty(1, &new_settings);
/* remember the erase and kill characters */
ch_erase = old_settings.sg_erase;
ch_kill = old_settings.sg_kill;
/* remember that it really is a terminal */
is_a_terminal = Yes;
/* send the termcap initialization string */
putcap(terminal_init);
}
else
{
/* not a terminal at all---consider it dumb */
smart_terminal = No;
}
}
end_screen()
{
/* move to the lower left, clear the line and send "te" */
if (smart_terminal)
{
putcap(lower_left);
putcap(clear_line);
putcap(terminal_end);
}
/* if we have settings to reset, then do so */
if (is_a_terminal)
{
stty(1, &old_settings);
}
}
reinit_screen()
{
/* install our settings if it is a terminal */
if (is_a_terminal)
{
stty(1, &new_settings);
}
/* send init string */
if (smart_terminal)
{
putcap(terminal_init);
}
}
standout(fmt, a1, a2, a3)
char *fmt;
int a1, a2, a3;
{
if (smart_terminal)
{
putcap(start_standout);
printf(fmt, a1, a2, a3);
putcap(end_standout);
}
else
{
printf(fmt, a1, a2, a3);
}
}
clear()
{
if (smart_terminal)
{
putcap(clear_screen);
}
}
/* This has to be defined as a subroutine for tputs (instead of a macro) */
putstdout(ch)
char ch;
{
putchar(ch);
}
SHAR_EOF
fi
exit 0
# End of shell archive