[comp.sources.unix] v10i064: Top users display, 2.1 with Symmetric changes, Part02/02

rs@uunet.UU.NET (Rich Salz) (07/23/87)

Submitted-by: hoptoad!vixie!paul (Paul Vixie Esq)
Posting-Number: Volume 10, Issue 64
Archive-name: top_s375/Part02

#! /bin/sh
##  This is a shell archive.  Remove anything before this line, then unpack
##  it by saving it into a file and typing "sh file".  To overwrite existing
##  files, type "sh file -c".  You can also feed this as standard input via
##  unshar, or by typing "sh <file".  If this archive is complete, you will
##  see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  display.c top.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f display.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"display.c\"
else
echo shar: Extracting \"display.c\" \(12060 characters\)
sed "s/^X//" >display.c <<'END_OF_display.c'
X/*
X *  Top - a top users display for Berkeley Unix
X *
X *  This file contains the routines that display information on the screen.
X *  Each section of the screen has two routines:  one for initially writing
X *  all constant and dynamic text, and one for only updating the text that
X *  changes.  The prefix "i_" is used on all the "initial" routines and the
X *  prefix "u_" is used for all the "updating" routines.  NOTE:  it is
X *  assumed that none of the "i_" routines use any of the termcap
X *  capabilities.  In this way, those routines can be safely used on
X *  terminals that have minimal (or nonexistant) terminal capabilities.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#if defined(scs)
X# define FLOAT		/* for pctcpu in proc.h */
X# include <sys/vm.h>	/* for struct spt */
X#endif
X#include <sys/proc.h>
X#include <sys/dk.h>
X#include "screen.h"		/* interface to screen package */
X#include "layout.h"		/* defines for screen position layout */
X#include "top.h"
X#include "boolean.h"
X
Xstatic int lmpid = 0;
Xstatic struct user u;
X
X#if defined(scs)
Xstatic struct spt	pspt;
X#endif scs
X
Xchar *printable();
X
X/* Verbose process state names */
X
Xchar *state_name[] =
X{
X    "", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped"
X};
X
X/* process state names for the "STATE" column of the display */
X
Xchar *state_abbrev[] =
X{
X    "", "sleep", "WAIT", "run", "start", "zomb", "stop"
X};
X
X/* cpu state names for percentages */
X
Xchar *cpu_state[] =
X{
X    "user", "nice", "system", "idle"
X};
X
X/* screen positions for cpustate figures */
Xchar x_cpustates[] = { 12, 24, 36, 50 };
X
Xi_loadave(mpid, avenrun)
X
Xint mpid;
X#if defined(sun)
Xlong *avenrun;
X#else
Xdouble *avenrun;
X#endif sun
X
X{
X    register int i;
X
X    printf("last pid: %5d;  load averages", mpid);
X
X    for (i = 0; i < 3; i++)
X    {
X	printf("%c %4.2f",
X	    i == 0 ? ':' : ',',
X#if defined(sun)
X	    (double)avenrun[i] / FSCALE);
X#else
X	    avenrun[i]);
X#endif
X    }
X    lmpid = mpid;
X}
X
Xu_loadave(mpid, avenrun)
X
Xint mpid;
X#if defined(sun)
Xlong *avenrun;
X#else
Xdouble *avenrun;
X#endif sun
X
X{
X    register int i;
X
X    if (mpid != lmpid);
X    {
X	Move_to(x_lastpid, y_lastpid);
X	printf("%5d", mpid);
X	lmpid = mpid;
X    }
X
X    Move_to(x_loadave, y_loadave);
X    for (i = 0; i < 3; i++)
X    {
X	printf("%s%4.2f",
X	    i == 0 ? "" : ", ",
X#if defined(sun)
X	    (double)avenrun[i] / FSCALE);
X#else
X	    avenrun[i]);
X#endif
X    }
X}
X
Xstatic int ltotal = 0;
Xstatic int lbrkdn[7];
X
Xi_procstates(total, brkdn)
X
Xint total;
Xint *brkdn;
X
X{
X    register int i;
X
X    printf("%2d processes", total);	/* ??? */
X    ltotal = total;
X    for (i = 1; i < 7; i++)
X    {
X	if (brkdn[i] != 0)
X	{
X	    printf("%c %d %s%s",
X		    i == 1 ? ':' : ',',
X		    brkdn[i],
X		    state_name[i],
X		    (i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
X	}
X    }
X    bcopy(brkdn, lbrkdn, sizeof(lbrkdn));
X}
X
Xu_procstates(total, brkdn)
X
Xint total;
Xint *brkdn;
X
X{
X    register int i;
X
X    if (ltotal != total)
X    {
X	Move_to(x_procstate, y_procstate);
X	printf("%d ", total);
X	ltotal = total;
X    }
X    else if (bcmp(brkdn, lbrkdn, sizeof(lbrkdn)) == 0)
X    {
X	return;
X    }
X
X    Move_to(x_brkdn, y_brkdn);
X    for (i = 1; i < 7; i++)
X    {
X	if (brkdn[i] != 0)
X	{
X	    printf("%s%d %s%s",
X		    i == 1 ? "" : ", ",
X		    brkdn[i],
X		    state_name[i],
X		    (i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
X	}
X    }
X    putcap(clear_line);
X    bcopy(brkdn, lbrkdn, sizeof(lbrkdn));
X}
X
Xi_cpustates(changes, total)
X
Xint *changes;
Xint total;
X
X{
X    register int i;
X
X    printf("\nCpu states: ");
X    for (i = 0; i < CPUSTATES; i++)
X    {
X	printf("%s%4.1f%% %s",
X		i == 0 ? "" : ", ",
X		((float)changes[i] / (float)total) * 100.0,
X		cpu_state[i]);
X    }
X    printf("\n");
X}
X
Xu_cpustates(changes, total)
X
Xint *changes;
Xint total;
X
X{
X    register int i;
X
X    for (i = 0; i < CPUSTATES; i++)
X    {
X	Move_to(x_cpustates[i], y_cpustates);
X	printf("%4.1f",
X		((float)changes[i] / (float)total) * 100.0);
X    }
X}
X
Xz_cpustates()
X
X{
X    register int i;
X
X    printf("\nCpu states: ");
X    for (i = 0; i < CPUSTATES; i++)
X    {
X	printf("%s    %% %s", i == 0 ? "" : ", ", cpu_state[i]);
X    }
X    printf("\n");
X}
X
Xi_memory(i1, i2, i3, i4, i5)
X
Xint i1, i2, i3, i4, i5;
X
X{
X    printf("Memory: %4dK (%4dK) real, %4dK (%4dK) virtual, %4dK free",
X	i1, i2, i3, i4, i5);
X}
X
Xu_memory(i1, i2, i3, i4, i5)
X
Xint i1, i2, i3, i4, i5;
X
X{
X    Move_to(x_realmem, y_mem);
X    printf("%4dK (%4d", i1, i2);
X    Move_to(x_virtmem, y_mem);
X    printf("%4dK (%4d", i3, i4);
X    Move_to(x_free, y_mem);
X    printf("%4d", i5);
X}
X
Xi_header(f2)
X
Xchar *f2;
X
X{
X    printf(
X      "\n\n  PID %s PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND", 
X      f2);
X}
X
Xu_header()
X
X{
X    Move_to(0, y_header);
X}
X
X#if defined(sun)
X#define percent_cpu(pp) ((double)(pp)->p_pctcpu / FSCALE)
X#else
X#define percent_cpu(pp) ((pp)->p_pctcpu)
X#endif sun
X
X#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
X			 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
X
X#ifdef DEBUG
XFILE *debug;
X#endif
X
Xstatic void
Xfmt_proc(thisline, pp, get_userid)
X	char		*thisline;
X	struct proc	*pp;
X	char		*(*get_userid)();
X{
X	register long cputime;
X	register double pctcpu;
X
X	/* get the cpu usage and calculate the cpu percentages */
X	cputime = get_ucpu(pp);
X	pctcpu = percent_cpu(pp);
X
X	/* format the line */
X
X#define Proc_format \
X	"%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s"
X
X#if !defined(pyr) && !defined(scs)
X	/* vax or sun, both are the same here */
X	sprintf(thisline, Proc_format,
X		pp->p_pid,
X		(*get_userid)(pp->p_uid),
X		pp->p_pri - PZERO,
X		pp->p_nice - NZERO,
X		pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize),
X		pagetok(pp->p_rssize),
X		state_abbrev[pp->p_stat],
X		cputime / 60l,
X		cputime % 60l,
X		100.0 * weighted_cpu(pctcpu, pp),
X		100.0 * pctcpu,
X		printable(u.u_comm));
X#else
X	/* pyr xor scs */
X#if defined(pyr)
X	sprintf(thisline, Proc_format,
X		pp->p_pid,
X		(*get_userid)(pp->p_uid),
X		pp->p_pri - PZERO,
X		pp->p_nice - NZERO,
X		pagetok(pp->p_tsize+pp->p_dsize+pp->p_cssize+pp->p_ussize),
X		pagetok(pp->p_rssize),
X		state_abbrev[pp->p_stat],
X		cputime / 60l,
X		cputime % 60l,
X		100.0 * weighted_cpu(pctcpu, pp),
X		100.0 * pctcpu,
X		printable(u.u_comm));
X#endif pyr
X#if defined(scs)
X	get_spt(pp->p_spti, &pspt);
X	sprintf(thisline, Proc_format,
X		pp->p_pid,
X		(*get_userid)(pp->p_uid),
X		pp->p_pri - PZERO,
X		pp->p_nice - NZERO,
X		pagetok(pspt.spt_usedpages),
X		pagetok(pspt.spt_mempages),
X		state_abbrev[pp->p_stat],
X		cputime / 60l,
X		cputime % 60l,
X		100.0 * weighted_cpu(pctcpu, pp),
X		100.0 * pctcpu,
X		printable(u.u_comm));
X#endif scs
X#endif /* for vax/sun / pyr/scs */
X}
X
Xi_process(line, pp, get_userid)
X	int line;
X	struct proc *pp;
X	char *(*get_userid)();
X{
X	register char *thisline;
X	int len;
X
X#ifdef DEBUG
X	debug = fopen("debug", "w");
X#endif
X
X	/* calculate a pointer to the buffer for this line */
X	thisline = screenbuf[line];
X
X	/* format the line into our buffer */
X	fmt_proc(thisline, pp, get_userid);
X
X	/* write the line out */
X	putchar('\n');
X	fputs(thisline, stdout);
X
X	/* zero fill the rest of it */
X	len = strlen(thisline);
X	bzero(thisline + len, Display_width - len);
X}
X
Xstatic int lastline = 0;
X
Xu_process(line, pp, get_userid)
X	int line;
X	struct proc *pp;
X	char *(*get_userid)();
X{
X    register char *optr;
X    register char *nptr;
X    register int ch;
X    register int diff;
X    register int newcol = 1;
X    register int lastcol = 0;
X    char cursor_on_line = No;
X    char *thisline;
X    int screen_line = line + Header_lines;
X    static char newline[Display_width];
X
X    /* get a pointer to the old text for this line */
X    optr = thisline = screenbuf[line];
X
X	/* format the line into our temporary buffer */
X	fmt_proc(newline, pp, get_userid);
X
X    /* compare the two strings and only rewrite what has changed */
X    nptr = newline;
X#ifdef DEBUG
X    fputs(optr, debug);
X    fputc('\n', debug);
X    fputs(nptr, debug);
X    fputs("\n-\n", debug);
X#endif
X
X    /* start things off on the right foot		    */
X    /* this is to make sure the invariants get set up right */
X    if ((ch = *nptr++) != *optr)
X    {
X	if (screen_line - lastline == 1)
X	{
X	    putchar('\n');
X	}
X	else
X	{
X	    Move_to(0, screen_line);
X	}
X	cursor_on_line = Yes;
X	putchar(ch);
X	*optr = ch;
X	lastcol = 1;
X    }
X    optr++;
X
X    /*
X     *  main loop -- check each character.  If the old and new aren't the
X     *	same, then update the display.  When the distance from the current
X     *	cursor position to the new change is small enough, the characters
X     *	that belong there are written to move the cursor over.
X     *
X     *	Invariants:
X     *	    lastcol is the column where the cursor currently is sitting
X     *		(always one beyond the end of the last mismatch).
X     */
X    do		/* yes, a do...while */
X    {
X	if ((ch = *nptr++) != *optr)
X	{
X	    /* new character is different from old	  */
X	    /* put the new character in the screen buffer */
X	    *optr = ch;
X
X	    /* make sure the cursor is on top of this character */
X	    diff = newcol - lastcol;
X	    if (diff > 0)
X	    {
X		/* some motion is required--figure out which is shorter */
X		if (diff < 6 && cursor_on_line)
X		{
X		    /* overwrite old stuff--get it out of the screen buffer */
X		    printf("%.*s", diff, &thisline[lastcol]);
X		}
X		else
X		{
X		    /* use cursor addressing */
X		    Move_to(newcol, screen_line);
X		    cursor_on_line = Yes;
X		}
X		/* remember where the cursor is */
X		lastcol = newcol + 1;
X	    }
X	    else
X	    {
X		/* already there, update position */
X		lastcol++;
X	    }
X
X	    /* write what we need to */
X	    if (ch == '\0')
X	    {
X		/* at the end--terminate with a clear-to-end-of-line */
X		putcap(clear_line);
X	    }
X	    else
X	    {
X		/* write the new character */
X		putchar(ch);
X	    }
X	}
X
X	/* update working column and screen buffer pointer */
X	newcol++;
X	optr++;
X
X    } while (ch != '\0');
X
X    /* zero out the rest of the line buffer -- MUST BE DONE! */
X    bzero(optr, Display_width - newcol);
X
X    /* remember where the current line is */
X    if (cursor_on_line)
X    {
X	lastline = screen_line;
X    }
X}
X
Xstatic int last_hi = 0;
X
Xu_endscreen(hi)
X
Xregister int hi;
X
X{
X    register int screen_line = hi + Header_lines;
X
X    if (smart_terminal)
X    {
X	if (hi < last_hi)
X	{
X	    if (hi == 0)
X	    {
X		putchar('\n');
X		putchar('\n');
X		putcap(clear_line);
X		putchar('\n');
X	    }
X	    else if (screen_line - lastline == 1)
X	    {
X		putchar('\n');
X	    }
X	    else
X	    {
X		Move_to(0, screen_line);
X	    }
X    
X	    while (--last_hi > hi)
X	    {
X		putcap(clear_line);
X		putchar('\n');
X	    }
X	    putcap(clear_line);
X	}
X	else
X	{
X	    last_hi = hi;
X	}
X
X	/* move the cursor to a pleasant place */
X	Move_to(x_idlecursor, y_idlecursor);
X    }
X    else
X    {
X	/* separate this display from the next with some vertical room */
X	fputs("\n\n", stdout);
X    }
X}
X
X/*
X *  get_ucpu(pp) - retrieve the user structure associated with the proc
X *	structure pointed to by pp and return the cpu usage.  The user
X *	structure is stored in the global structure "u" for later use.
X */
X
Xget_ucpu(pp)
X
Xstruct proc *pp;
X
X{
X#if defined(scs)
X
X	strcpy(u.u_comm, pp->p_infoname);
X	return pp->p_infotime.tv_sec;
X
X#else
X
X    if (getu(pp, &u) == -1)
X    {
X	strcpy(u.u_comm, "<swapped>");
X	return(0);
X    }
X    else
X    {
X	/* set u_comm for system processes */
X	if (u.u_comm[0] == '\0')
X	{
X	    if (pp->p_pid == 0)
X	    {
X		strcpy(u.u_comm, "Swapper");
X	    }
X	    else if (pp->p_pid == 2)
X	    {
X		strcpy(u.u_comm, "Pager");
X	    }
X	}
X
X#ifdef FOUR_ONE
X	return((int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz));
X#else
X	return(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec);
X#endif
X    }
X#endif scs
X}
X
X/*
X *  printable(str) - make the string pointed to by "str" into one that is
X *	printable (i.e.: all ascii), by converting all non-printable
X *	characters into '?'.  Replacements are done in place and a pointer
X *	to the original buffer is returned.
X */
X
Xchar *printable(str)
X
Xchar *str;
X
X{
X    register char *ptr;
X    register char ch;
X
X    ptr = str;
X    while ((ch = *ptr) != '\0')
X    {
X	if (!isprint(ch))
X	{
X	    *ptr = '?';
X	}
X	ptr++;
X    }
X    return(str);
X}
END_OF_display.c
if test 12060 -ne `wc -c <display.c`; then
    echo shar: \"display.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f top.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"top.c\"
else
echo shar: Extracting \"top.c\" \(26926 characters\)
sed "s/^X//" >top.c <<'END_OF_top.c'
Xchar *copyright =
X    "Top, version 2.1, copyright (c) 1984, 1986, William LeFebvre";
X
X/*
X *  Top users display for Berkeley Unix
X *  Version 2.1
X *
X *  This program may be freely redistributed to other Unix sites, but this
X *  entire comment MUST remain intact.
X *
X *  Copyright (c) 1984, 1986, William LeFebvre, Rice University
X *
X *  This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
X *  Compile with the preprocessor constant "FOUR_ONE" set to get an
X *  executable that will run on Berkeley 4.1 Unix.
X *
X *  The Sun kernel uses scaled integers instead of floating point.
X *  Compilation with the preprocessor variable "sun" gets an executable
X *  that will run on Sun Unix version 1.1 or later ("sun" is automatically
X *  set by the Sun C compiler).
X *
X *  The Pyramid splits the stack size (p_ssize) into control stack and user
X *  stack sizes.  Compilation with the preprocessor variable "pyr" gets an
X *  executable that will run on Pyramids ("pyr" is automatically set by the
X *  Pyramid C compiler).
X *
X *  The Symmetric 375 needs various goodies as well.  "scs" is defined in cc.
X *
X *  See the file "Changes" for more information on version-to-version changes.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <nlist.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#if defined(scs)
X# define FLOAT		/* for pctcpu in proc.h */
X#endif
X#include <sys/proc.h>
X#include <sys/dk.h>
X#include <sys/vm.h>
X
X/* includes specific to top */
X#include "layout.h"
X#include "screen.h"		/* interface to screen package */
X#include "top.h"
X#include "top.local.h"
X#include "boolean.h"
X
X/* Special atoi routine returns either a non-negative number or one of: */
X#define Infinity	-1
X#define Invalid		-2
X
X/* Size of the stdio buffer given to stdout */
X#define Buffersize	2048
X
X/* The buffer the stdio will use */
Xchar stdoutbuf[Buffersize];
X
X/* wish list for kernel symbols */
X
Xstruct nlist nlst[] = {
X    { "_avenrun" },
X#define X_AVENRUN	0
X    { "_ccpu" },
X#define X_CCPU		1
X    { "_cp_time" },
X#define X_CP_TIME	2
X    { "_hz" },
X#define X_HZ		3
X    { "_mpid" },
X#define X_MPID		4
X    { "_nproc" },
X#define X_NPROC		5
X    { "_proc" },
X#define X_PROC		6
X    { "_total" },
X#define X_TOTAL		7
X#if defined(scs)
X    { "_spt" },
X# define X_SPT		8
X#endif scs
X    { 0 },
X};
X
X/* build Signal masks */
X#define Smask(s)	(1 << ((s) - 1))
X
X/* for system errors */
Xextern int errno;
X
X/* for getopt: */
Xextern int  optind;
Xextern char *optarg;
X
X/* signal handling routines */
Xint leave();
Xint onalrm();
Xint tstop();
X
Xint nproc;
Xint mpid;
X
X/* kernel "hz" variable -- clock rate */
Xlong hz;
X
X/* All this is to calculate the cpu state percentages */
X
Xlong cp_time[CPUSTATES];
Xlong cp_old[CPUSTATES];
Xlong cp_change[CPUSTATES];
Xlong total_change;
Xlong mpid_offset;
Xlong avenrun_offset;
Xlong cp_time_offset;
Xlong total_offset;
X
X#if defined(sun)
Xlong ccpu;
Xlong avenrun[3];
X#else
Xdouble ccpu;
Xdouble avenrun[3];
X#endif
Xdouble logcpu;
X
Xstruct vmtotal total;
X
X#if defined(scs)
Xstruct spt *spt;
X#endif scs
X
Xstruct proc *proc;
Xstruct proc *pbase;
Xint bytes;
Xint initialized = No;
Xchar *myname = "top";
Xjmp_buf jmp_int;
Xchar input_waiting = No;
X
X/* routines that don't return int */
X
Xstruct passwd *getpwent();
Xchar *username();
Xchar *itoa();
Xchar *ctime();
Xchar *rindex();
Xchar *kill_procs();
Xchar *renice_procs();
X
Xint proc_compar();
Xlong time();
X
X/* different routines for displaying the user's identification */
X/* (values assigned to get_userid) */
Xchar *username();
Xchar *itoa7();
X
X/* display routines that need to be predeclared */
Xint i_loadave();
Xint u_loadave();
Xint i_procstates();
Xint u_procstates();
Xint i_cpustates();
Xint u_cpustates();
Xint i_memory();
Xint u_memory();
Xint i_header();
Xint u_header();
Xint i_process();
Xint u_process();
X
X/* pointers to display routines */
Xint (*d_loadave)() = i_loadave;
Xint (*d_procstates)() = i_procstates;
Xint (*d_cpustates)() = i_cpustates;
Xint (*d_memory)() = i_memory;
Xint (*d_header)() = i_header;
Xint (*d_process)() = i_process;
X
X/* buffer of proc information lines for display updating */
X/* unfortunate that this must be declared globally */
Xchar (* screenbuf)[Display_width];
X
Xmain(argc, argv)
X
Xint  argc;
Xchar *argv[];
X
X{
X    register struct proc *pp;
X    register struct proc **prefp;
X    register int i;
X    register int active_procs;
X    register int change;
X
X    static struct proc **pref;
X    static char tempbuf1[50];
X    static char tempbuf2[50];
X    int total_procs;
X    int old_sigmask;
X    int proc_brkdn[7];
X    int topn = Default_TOPN;
X    int delay = Default_DELAY;
X    int displays = 0;		/* indicates unspecified */
X    long curr_time;
X    char *(*get_userid)() = username;
X    char *uname_field = "USERNAME";
X#ifndef FOUR_ONE
X    char ch;
X    char msg_showing = 0;
X    int readfds;
X    struct timeval timeout;
X#endif    
X    char dostates = No;
X    char do_unames = Yes;
X    char do_init = No;
X    char interactive = Maybe;
X    char show_sysprocs = No;
X    char topn_specified = No;
X    char warnings = 0;
X
X    /* set the buffer for stdout */
X    setbuffer(stdout, stdoutbuf, Buffersize);
X
X    /* get our name */
X    if (argc > 0)
X    {
X	if ((myname = rindex(argv[0], '/')) == 0)
X	{
X	    myname = argv[0];
X	}
X	else
X	{
X	    myname++;
X	}
X    }
X
X    /* process options */
X    while ((i = getopt(argc, argv, "Sbinus:d:")) != EOF)
X    {
X	switch(i)
X	{
X	    case 'u':			/* display uid instead of name */
X		do_unames = No;
X		uname_field = "   UID  ";
X		get_userid = itoa7;
X		break;
X
X	    case 'S':			/* show system processes */
X		show_sysprocs = Yes;
X		break;
X
X	    case 'i':			/* go interactive regardless */
X		interactive = Yes;
X		break;
X
X	    case 'n':			/* batch, or non-interactive */
X	    case 'b':
X		interactive = No;
X		break;
X
X	    case 'd':			/* number of displays to show */
X		if ((i = atoiwi(optarg)) == Invalid || i == 0)
X		{
X		    fprintf(stderr,
X			"%s: warning: display count should be positive -- option ignored\n",
X			myname);
X		    warnings++;
X		}
X		else
X		{
X		    displays = i;
X		}
X		break;
X
X	    case 's':
X		if ((delay = atoi(optarg)) < 0)
X		{
X		    fprintf(stderr,
X			"%s: warning: seconds delay should be non-negative -- using default\n",
X			myname);
X		    delay = Default_DELAY;
X		    warnings++;
X		}
X		break;
X
X	    default:
X		fprintf(stderr,
X		    "Usage: %s [-Sbinu] [-d x] [-s x] [number]\n",
X		    myname);
X		exit(1);
X	}
X    }
X
X    /* get count of top processes to display (if any) */
X    if (optind < argc)
X    {
X	if ((topn = atoiwi(argv[optind])) == Invalid)
X	{
X	    fprintf(stderr,
X		"%s: warning: process display count should be non-negative -- using default\n",
X		myname);
X	    topn = Default_TOPN;
X	    warnings++;
X	}
X	else
X	{
X	    topn_specified = Yes;
X	}
X    }
X
X    /* initialize the kernel memory interface */
X    init_kernel();
X
X    if (initialized != 1)
X    {
X	/* get the list of symbols we want to access in the kernel */
X	/* errno = 0; ??? */
X	nlist(VMUNIX, nlst);
X	if (nlst[0].n_type == 0)
X	{
X	    fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
X	    exit(2);
X	}
X    
X	/* get the symbol values out of kmem */
X	getkval(nlst[X_PROC].n_value,  &proc,  sizeof(int),
X		nlst[X_PROC].n_name);
X	getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int),
X		nlst[X_NPROC].n_name);
X	getkval(nlst[X_HZ].n_value,    &hz,    sizeof(int),
X		nlst[X_HZ].n_name);
X	getkval(nlst[X_CCPU].n_value,  &ccpu,  sizeof ccpu,
X		nlst[X_CCPU].n_name);
X#if defined(scs)
X	getkval(nlst[X_SPT].n_value,   &spt,   sizeof(struct spt *),
X		nlst[X_SPT].n_name);
X#endif scs
X
X	/* some calculations we use later */
X    
X	mpid_offset = nlst[X_MPID].n_value;
X	avenrun_offset = nlst[X_AVENRUN].n_value;
X	cp_time_offset = nlst[X_CP_TIME].n_value;
X	total_offset = nlst[X_TOTAL].n_value;
X    
X	/* this is used in calculating WCPU -- calculate it ahead of time */
X#if defined(sun)
X	logcpu = log((double)ccpu / FSCALE);
X#else
X	logcpu = log(ccpu);
X#endif
X
X	/* allocate space for proc structure array and array of pointers */
X	bytes  = nproc * sizeof(struct proc);
X	pbase  = (struct proc *)sbrk(bytes);
X	pref   = (struct proc **)sbrk(nproc * sizeof(struct proc *));
X
X	/* Just in case ... */
X	if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
X	{
X	    fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
X	    exit(3);
X	}
X    
X	/* initialize the hashing stuff */
X	if (do_unames)
X	{
X	    init_hash();
X	}
X	
X	if (do_init)
X	{
X	    initialized = 1;
X	    kill(0, SIGQUIT);
X	    exit(99);
X	}
X    }
X
X    /* initialize termcap */
X    init_termcap();
X
X    /*
X     *  Smart terminals can only display so many processes, precisely
X     *	"screen_length - Header_lines".  When run on dumb terminals, nothing
X     *	fancy is done anyway, so we can display as many processes as the
X     *	system can make.  But since we never need to remember what is on the
X     *	screen, we only allocate a buffer for one screen line.
X     */
X    if (smart_terminal)
X    {
X	/* can only display (screen_length - Header_lines) processes */
X	i = screen_length - Header_lines;
X	if (topn > i)		/* false even when topn == Infinity */
X	{
X	    fprintf(stderr,
X		"%s: warning: this terminal can only display %d processes.\n",
X		myname, screen_length - Header_lines);
X	    topn = i;
X	    warnings++;
X	}
X    }
X    else
X    {
X	i = 1;
X	screen_length = nproc + Header_lines;
X    }
X
X    /* allocate space for the screen buffer */
X    screenbuf = (char (*)[])sbrk(i * Display_width);
X    if (screenbuf == (char (*)[])NULL)
X    {
X	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
X	exit(4);
X    }
X
X    /* adjust for topn == Infinity */
X    if (topn == Infinity)
X    {
X	/*
X	 *  For smart terminals, infinity really means everything that can
X	 *  be displayed (which just happens to be "i" at this point).
X	 *  On dumb terminals, infinity means every process in the system!
X	 *  We only really want to do that if it was explicitly specified.
X	 *  This is always the case when "Default_TOPN != Infinity".  But if
X	 *  topn wasn't explicitly specified and we are on a dumb terminal
X	 *  and the default is Infinity, then (and only then) we use
X	 *  "Nominal_TOPN" instead.
X	 */
X#if Default_TOPN == Infinity
X	topn = smart_terminal ? i :
X		    (topn_specified ? nproc : Nominal_TOPN);
X#else
X	topn = smart_terminal ? i : nproc;
X#endif
X    }
X
X    /* determine interactive state */
X    if (interactive == Maybe)
X    {
X	interactive = smart_terminal;
X    }
X
X    /* if # of displays not specified, fill it in */
X    if (displays == 0)
X    {
X	displays = smart_terminal ? Infinity : 1;
X    }
X
X    /* hold interrupt signals while setting up the screen and the handlers */
X#ifndef FOUR_ONE
X    old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
X#endif
X    init_screen();
X    signal(SIGINT, leave);
X    signal(SIGQUIT, leave);
X    signal(SIGTSTP, tstop);
X#ifndef FOUR_ONE
X    sigsetmask(old_sigmask);
X#endif
X    if (warnings)
X    {
X	fprintf(stderr, "....");
X	fflush(stderr);			/* why must I do this? */
X	sleep(3 * warnings);
X    }
X    clear();
X
X    /* setup the jump buffer for stops */
X    if (setjmp(jmp_int) != 0)
X    {
X	/* control ends up here after an interrupt */
X	clear();
X	reset_display();
X    }
X
X    /*
X     *  main loop -- repeat while display count is positive or while it
X     *		indicates infinity (by being -1)
X     */
X
X    while ((displays == -1) || (displays-- > 0))
X    {
X	/* read all the proc structures in one fell swoop */
X	getkval(proc, pbase, bytes, "proc array");
X
X	/* get the cp_time array */
X	getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time");
X
X	/* get load average array */
X	getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun");
X
X	/* get mpid -- process id of last process */
X	getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
X
X	/* get total -- systemwide main memory usage structure */
X	getkval(total_offset, &total, sizeof(total), "_total");
X
X	/* count up process states and get pointers to interesting procs */
X	total_procs = 0;
X	active_procs = 0;
X	bzero(proc_brkdn, sizeof(proc_brkdn));
X	prefp = pref;
X	for (pp = pbase, i = 0; i < nproc; pp++, i++)
X	{
X	    /*
X	     *  Place pointers to each valid proc structure in pref[].
X	     *  Process slots that are actually in use have a non-zero
X	     *  status field.  Processes with SSYS set are system
X	     *  processes---these get ignored unless show_sysprocs is set.
X	     */
X	    if (pp->p_stat != 0 &&
X		(show_sysprocs || ((pp->p_flag & SSYS) == 0)))
X	    {
X		total_procs++;
X		proc_brkdn[pp->p_stat]++;
X		if (pp->p_stat != SZOMB)
X		{
X		    *prefp++ = pp;
X		    active_procs++;
X		}
X	    }
X	}
X
X	/* display the load averages */
X	(*d_loadave)(mpid, avenrun);
X
X	/*
X	 *  Display the current time.
X	 *  "ctime" always returns a string that looks like this:
X	 *  
X	 *	Sun Sep 16 01:03:52 1973
X	 *      012345678901234567890123
X	 *	          1         2
X	 *
X	 *  We want indices 11 thru 18 (length 8).
X	 */
X
X	curr_time = time(0);
X	if (smart_terminal)
X	{
X	    Move_to(screen_width - 8, 0);
X	}
X	else
X	{
X	    fputs("    ", stdout);
X	}
X	printf("%-8.8s\n", &(ctime(&curr_time)[11]));
X
X	/* display process state breakdown */
X	(*d_procstates)(total_procs, proc_brkdn);
X
X	/* calculate percentage time in each cpu state */
X	if (dostates)	/* but not the first time */
X	{
X	    total_change = 0;
X	    for (i = 0; i < CPUSTATES; i++)
X	    {
X		/* calculate changes for each state and overall change */
X		if (cp_time[i] < cp_old[i])
X		{
X		    /* this only happens when the counter wraps */
X		    change = (int)
X			((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
X		}
X		else
X		{
X		    change = cp_time[i] - cp_old[i];
X		}
X		total_change += (cp_change[i] = change);
X		cp_old[i] = cp_time[i];
X	    }
X	    (*d_cpustates)(cp_change, total_change);
X	}
X	else
X	{
X	    /* we'll do it next time */
X	    if (smart_terminal)
X	    {
X		z_cpustates();
X	    }
X	    else
X	    {
X		putchar('\n');
X	    }
X	    dostates = Yes;
X
X	    /* remember the current values as "old" values */
X	    bcopy(cp_time, cp_old, sizeof(cp_time));
X	}
X
X	/* display main memory statistics */
X	(*d_memory)(
X		pagetok(total.t_rm), pagetok(total.t_arm),
X		pagetok(total.t_vm), pagetok(total.t_avm),
X		pagetok(total.t_free));
X
X	i = 0;
X	if (topn > 0)
X	{
X	    /* update the header area */
X	    (*d_header)(uname_field);
X    
X	    /* sort by cpu percentage (pctcpu) */
X	    qsort(pref, active_procs, sizeof(struct proc *), proc_compar);
X    
X	    /* adjust for a lack of processes */
X	    if (active_procs > topn)
X	    {
X		active_procs = topn;
X	    }
X
X	    /*
X	     *  Now, show the top "n" processes.  The method is slightly
X	     *	different for dumb terminals, so we will just use two very
X	     *	similar loops; this increases speed but also code size.
X	     */
X	    if (smart_terminal)
X	    {
X		for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
X		{
X		    pp = *prefp;
X		    (*d_process)(i, pp, get_userid);
X		}
X	    }
X	    else for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
X	    {
X		pp = *prefp;
X		/* (only one buffer lien with dumb terminals) */
X		(*d_process)(0, pp, get_userid);
X	    }
X	}
X
X	/* do end-screen processing */
X	u_endscreen(i);
X
X	/* now, flush the output buffer */
X	fflush(stdout);
X
X	/* only do the rest if we have more displays to show */
X	if (displays)
X	{
X	    /* switch out for new display on smart terminals */
X	    if (smart_terminal)
X	    {
X		d_loadave = u_loadave;
X		d_procstates = u_procstates;
X		d_cpustates = u_cpustates;
X		d_memory = u_memory;
X		d_header = u_header;
X		d_process = u_process;
X	    }
X    
X#ifndef FOUR_ONE
X	    if (!interactive)
X#endif
X	    {
X		/* set up alarm */
X		signal(SIGALRM, onalrm);
X		alarm(delay);
X    
X		/* wait for the rest of it .... */
X		pause();
X	    }
X#ifndef FOUR_ONE
X	    else
X	    {
X		/* wait for either input or the end of the delay period */
X		readfds = 1;			/* for standard input */
X		timeout.tv_sec  = delay;
X		timeout.tv_usec = 0;
X		if (select(32, &readfds, 0, 0, &timeout) > 0)
X		{
X		    int newval;
X		    char *errmsg;
X    
X		    /* something to read -- clear the message area first */
X		    if (msg_showing)
X		    {
X			if (smart_terminal)
X			{
X			    putcap(clear_line);
X			}
X			msg_showing = No;
X		    }
X
X		    /* now read it and act on it */
X		    read(0, &ch, 1);
X		    switch(ch)
X		    {
X			case '\f':		/* redraw screen */
X			    reset_display();
X			    clear();
X			    break;
X
X			case ' ':		/* merely update display */
X			    break;
X	
X			case 'q':		/* quit */
X			    quit(0);
X			    break;
X	
X			case 'h':		/* help */
X			case '?':
X			    reset_display();
X			    clear();
X			    show_help();
X			    standout("Hit any key to continue: ");
X			    fflush(stdout);
X    			    read(0, &ch, 1);
X			    clear();
X			    break;
X    
X			case 'e':		/* show errors */
X			    if (error_count() == 0)
X			    {
X				standout(" Currently no errors to report.");
X				msg_showing = Yes;
X			    }
X			    else
X			    {
X				reset_display();
X				clear();
X				show_errors();
X				standout("Hit any key to continue: ");
X				fflush(stdout);
X				read(0, &ch, 1);
X				clear();
X			    }
X			    break;
X    
X			case 'n':		/* new number */
X			case '#':
X			    standout("Number of processes to show: ");
X			    newval = readline(tempbuf1, 8, Yes);
X			    putchar('\r');
X			    if (newval > -1)
X			    {
X				if (newval > (i = screen_length - Header_lines))
X				{
X				    standout(
X				      " This terminal can only display %d processes.",
X				      i);
X				    newval = i;
X				    msg_showing = Yes;
X				    break;
X				}
X	
X				if (newval > topn)
X				{
X				    /* zero fill appropriate part of screenbuf */
X				    bzero(screenbuf[topn],
X					(newval - topn) * Display_width);
X	
X				    /* redraw header if need be */
X				    if (topn == 0)
X				    {
X					d_header = i_header;
X				    }
X				}
X				topn = newval;
X			    }
X			    putcap(clear_line);
X			    break;
X	
X			case 's':		/* new seconds delay */
X			    standout("Seconds to delay: ");
X			    if ((i = readline(tempbuf1, 8, Yes)) > -1)
X			    {
X				delay = i;
X			    }
X			    putchar('\r');
X			    putcap(clear_line);
X			    break;
X    
X			case 'd':		/* change display count */
X			    standout("Displays to show (currently %s): ",
X				    displays == -1 ? "infinite" :
X						     itoa(displays));
X			    if ((i = readline(tempbuf1, 10, Yes)) > 0)
X			    {
X				displays = i;
X			    }
X			    else if (i == 0)
X			    {
X				quit(0);
X			    }
X			    putchar('\r');
X			    putcap(clear_line);
X			    break;
X
X			case 'k':		/* kill program */
X			    fputs("kill ", stdout);
X			    if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
X			    {
X				if ((errmsg = kill_procs(tempbuf2)) != NULL)
X				{
X				    putchar('\r');
X				    standout(errmsg);
X				}
X				msg_showing = Yes;
X			    }
X			    else
X			    {
X				putchar('\r');
X			    }
X			    putcap(clear_line);
X			    break;
X	
X			case 'r':		/* renice program */
X			    fputs("renice ", stdout);
X			    if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
X			    {
X				if ((errmsg = renice_procs(tempbuf2)) != NULL)
X				{
X				    putchar('\r');
X				    standout(errmsg);
X				    msg_showing = Yes;
X				}
X			    }
X			    else
X			    {
X				putchar('\r');
X			    }
X			    putcap(clear_line);
X			    break;
X	
X			default:
X			    standout(" Command not understood");
X			    msg_showing = Yes;
X		    }
X		}
X		else if (msg_showing)
X		{
X		    if (smart_terminal)
X		    {
X			putcap(clear_line);
X		    }
X		    msg_showing = No;
X		}
X	    }
X#endif
X	}
X    }
X
X    quit(0);
X}
X
X/*
X *  reset_display() - reset all the display routine pointers so that entire
X *	screen will get redrawn.
X */
X
Xreset_display()
X
X{
X    d_loadave    = i_loadave;
X    d_procstates = i_procstates;
X    d_cpustates  = i_cpustates;
X    d_memory     = i_memory;
X    d_header	 = i_header;
X    d_process	 = i_process;
X}
X
Xreadline(buffer, size, numeric)
X
Xchar *buffer;
Xint  size;
Xint  numeric;
X
X{
X    register char *ptr = buffer;
X    register char ch;
X    register char cnt = 0;
X
X    size -= 1;
X    while ((fflush(stdout), read(0, ptr, 1) > 0))
X    {
X	if ((ch = *ptr) == '\n')
X	{
X	    break;
X	}
X
X	if (ch == ch_kill)
X	{
X	    *buffer = '\0';
X	    return(-1);
X	}
X	else if (ch == ch_erase)
X	{
X	    if (cnt <= 0)
X	    {
X		putchar('\7');
X	    }
X	    else
X	    {
X		fputs("\b \b", stdout);
X		ptr--;
X		cnt--;
X	    }
X	}
X	else if (cnt == size || (numeric && (ch < '0' || ch > '9')))
X	{
X	    putchar('\7');
X	}
X	else
X	{
X	    putchar(ch);
X	    ptr++;
X	    cnt++;
X	}
X    }
X    *ptr = '\0';
X    return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
X}
X
X/*
X *  signal handlers
X */
X
Xleave()			/* exit under normal conditions -- INT handler */
X
X{
X    end_screen();
X    exit(0);
X}
X
Xtstop()
X
X{
X    /* move to the lower left */
X    end_screen();
X    fflush(stdout);
X
X#ifdef FOUR_ONE		/* a 4.1 system */
X
X    /* send a STOP (uncatchable) to everyone in the process group */
X    kill(0, SIGSTOP);
X
X    /* reset the signal handler */
X    signal(SIGTSTP, tstop);
X
X#else			/* assume it is a 4.2 system */
X
X    /* default the signal handler action */
X    signal(SIGTSTP, SIG_DFL);
X
X    /* unblock the signal and send ourselves one */
X    sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
X    kill(0, SIGTSTP);
X
X    /* reset the signal handler */
X    signal(SIGTSTP, tstop);
X
X#endif
X    /* reinit screen */
X    reinit_screen();
X
X    /* jump to appropriate place */
X    longjmp(jmp_int, 1);
X
X    /*NOTREACHED*/
X}
X
Xquit(status)		/* exit under duress */
X
Xint status;
X
X{
X    end_screen();
X    exit(status);
X}
X
Xonalrm()
X
X{
X    return(0);
X}
X
X/*
X *  proc_compar - comparison function for "qsort"
X *	Compares the resource consumption of two processes using five
X *  	distinct keys.  The keys (in descending order of importance) are:
X *  	percent cpu, cpu ticks, state, resident set size, total virtual
X *  	memory usage.  The process states are ordered as follows (from least
X *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
X *  	array declaration below maps a process state index into a number
X *  	that reflects this ordering.
X */
X
Xunsigned char sorted_state[] =
X{
X    0,	/* not used		*/
X    3,	/* sleep		*/
X    1,	/* ABANDONED (WAIT)	*/
X    6,	/* run			*/
X    5,	/* start		*/
X    2,	/* zombie		*/
X    4	/* stop			*/
X};
X 
Xproc_compar(pp1, pp2)
X
Xstruct proc **pp1;
Xstruct proc **pp2;
X
X{
X    register struct proc *p1;
X    register struct proc *p2;
X    register int result;
X#if !defined(sun)
X    register double dresult;
X#endif
X
X    /* remove one level of indirection */
X    p1 = *pp1;
X    p2 = *pp2;
X
X    /* compare percent cpu (pctcpu) */
X#if defined(sun)
X    if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0)
X#else
X    if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
X#endif
X    {
X	/* use cpticks to break the tie */
X	if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
X	{
X	    /* use process state to break the tie */
X	    if ((result = sorted_state[p2->p_stat] -
X			  sorted_state[p1->p_stat])  == 0)
X	    {
X		/* use priority to break the tie */
X		if ((result = p2->p_pri - p1->p_pri) == 0)
X		{
X		    /* use resident set size (rssize) to break the tie */
X#ifdef scs
X		    if ((result = p2->p_maxrss - p1->p_maxrss) == 0)
X#else scs
X		    if ((result = p2->p_rssize - p1->p_rssize) == 0)
X#endif scs
X
X		    {
X			/* use total memory to break the tie */
X#ifdef pyr
X			result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) -
X				 (p1->p_tsize + p1->p_dsize + p1->p_ussize);
X#else pyr
X#ifdef scs
X			result = (p2->p_tdsize + p2->p_ssize) -
X				 (p1->p_tdsize + p1->p_ssize);
X#else scs
X			result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) -
X				 (p1->p_tsize + p1->p_dsize + p1->p_ssize);
X#endif scs
X#endif pyr
X
X		    }
X		}
X	    }
X	}
X    }
X#if !defined(sun)
X    else
X    {
X	result = dresult < 0.0 ? -1 : 1;
X    }
X#endif
X
X    return(result);
X}
X
X/* routines to translate uids into a string */
X
Xchar *user_name(euid, ruid)
X
Xint euid, ruid;
X
X{
X    return(username(euid));
X}
X
Xchar *user_uid(euid, ruid)
X
Xint euid, ruid;
X
X{
X    return(itoa7(euid));
X}
X
X/*
X *  These routines handle uid to username mapping.
X *  They use a hashing table scheme to reduce reading overhead.
X */
X
Xstruct hash_el {
X    int  uid;
X    char name[8];
X};
X
X#define    H_empty	-1
X
X/* simple minded hashing function */
X#define    hashit(i)	((i) % Table_size)
X
Xstruct hash_el hash_table[Table_size];
X
Xinit_hash()
X
X{
X    register int i;
X    register struct hash_el *h;
X
X    for (h = hash_table, i = 0; i < Table_size; h++, i++)
X    {
X	h->uid = H_empty;
X    }
X}
X
Xchar *username(uid)
X
Xregister int uid;
X
X{
X    register int index;
X    register int found;
X    register char *name;
X
X    /* This is incredibly naive, but it'll probably get changed anyway */
X    index = hashit(uid);
X    while ((found = hash_table[index].uid) != uid)
X    {
X	if (found == H_empty)
X	{
X	    /* not here -- get it out of passwd */
X	    index = get_user(uid);
X	    break;		/* out of while */
X	}
X	index = (index + 1) % Table_size;
X    }
X    return(hash_table[index].name);
X}
X
Xenter_user(uid, name)
X
Xregister int  uid;
Xregister char *name;
X
X{
X    register int length;
X    register int index;
X    register int try;
X    static int uid_count = 0;
X
X    /* avoid table overflow -- insure at least one empty slot */
X    if (++uid_count >= Table_size)
X    {
X	fprintf(stderr, "table overflow: too many users\n");
X	quit(10);
X    }
X
X    index = hashit(uid);
X    while ((try = hash_table[index].uid) != H_empty)
X    {
X	if (try == uid)
X	{
X	    return(index);
X	}
X	index = (index + 1) % Table_size;
X    }
X    hash_table[index].uid = uid;
X    strncpy(hash_table[index].name, name, 8);
X    return(index);
X}
X
Xget_user(uid)
X
Xregister int uid;
X
X{
X    struct passwd *pwd;
X    register int last_index;
X
X    while ((pwd = getpwent()) != NULL)
X    {
X	last_index = enter_user(pwd->pw_uid, pwd->pw_name);
X	if (pwd->pw_uid == uid)
X	{
X	    return(last_index);
X	}
X    }
X    return(enter_user(uid, itoa7(uid)));
X}
X
Xatoiwi(str)
X
Xchar *str;
X
X{
X    register int len;
X
X    len = strlen(str);
X    if (len != 0)
X    {
X	if (strncmp(str, "infinity", len) == 0 ||
X	    strncmp(str, "all",      len) == 0 ||
X	    strncmp(str, "maximum",  len) == 0)
X	{
X	    return(Infinity);
X	}
X	else if (str[0] == '-')
X	{
X	    return(Invalid);
X	}
X	else
X	{
X	    return(atoi(str));
X	}
X    }
X    return(0);
X}
X
X/*
X *  itoa - convert integer (decimal) to ascii string for positive numbers
X *  	   only (we don't bother with negative numbers since we know we
X *	   don't use them).
X */
X
Xstatic char buffer[16];		/* shared by the next two routines */
X
Xchar *itoa(val)
X
Xregister int val;
X
X{
X    register char *ptr;
X
X    ptr = buffer + sizeof(buffer);
X    *--ptr = '\0';
X    if (val == 0)
X    {
X	*--ptr = '0';
X    }
X    else while (val != 0)
X    {
X	*--ptr = (val % 10) + '0';
X	val /= 10;
X    }
X    return(ptr);
X}
X
X/*
X *  itoa7(val) - like itoa, except the number is right justified in a 7
X *	character field.  This code is a duplication of itoa instead of
X *	a front end to a more general routine for efficiency.
X */
X
Xchar *itoa7(val)
X
Xregister int val;
X
X{
X    register char *ptr;
X
X    ptr = buffer + sizeof(buffer);
X    *--ptr = '\0';
X    if (val == 0)
X    {
X	*--ptr = '0';
X    }
X    else while (val != 0)
X    {
X	*--ptr = (val % 10) + '0';
X	val /= 10;
X    }
X    while (ptr > buffer + sizeof(buffer) - 7)
X    {
X	*--ptr = ' ';
X    }
X    return(ptr);
X}
X
END_OF_top.c
if test 26926 -ne `wc -c <top.c`; then
    echo shar: \"top.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0


-- 

Rich $alz			"Anger is an energy"
Cronus Project, BBN Labs	rsalz@bbn.com
Moderator, comp.sources.unix	sources@uunet.uu.net