DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T t

⟦4070fcdf8⟧ TextFile

    Length: 26262 (0x6696)
    Types: TextFile
    Names: »top.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec8/top/top.c« 

TextFile

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 <sys/user.h>
#include <sys/proc.h>
#include <sys/dk.h>
#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 */

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 },
};

/* 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;

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;
    int proc_brkdn[7];
    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 */
    setbuffer(stdout, stdoutbuf, Buffersize);

    /* 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 */
	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);
	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;
	cp_time_offset = nlst[X_CP_TIME].n_value;
	total_offset = nlst[X_TOTAL].n_value;
    
	/* 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 */
	getkval(proc, pbase, bytes, "proc array");

	/* get the cp_time array */
	getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time");

	/* 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");

	/* get total -- systemwide main memory usage structure */
	getkval(total_offset, &total, sizeof(total), "_total");

	/* 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++;
		proc_brkdn[pp->p_stat]++;
		if (pp->p_stat != SZOMB)
		{
		    *prefp++ = pp;
		    active_procs++;
		}
	    }
	}

	/* 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.
 */

unsigned char sorted_state[] =
{
    0,	/* not used		*/
    3,	/* sleep		*/
    1,	/* ABANDONED (WAIT)	*/
    6,	/* run			*/
    5,	/* start		*/
    2,	/* zombie		*/
    4	/* stop			*/
};
 
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 */
	    if ((result = sorted_state[p2->p_stat] -
			  sorted_state[p1->p_stat])  == 0)
	    {
		/* 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);
}