[mod.computers.masscomp] Top for the masscomp Part 3 of 3

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