[mod.sources] v07i030: Top users display for 4.2BSD, Version 2.0, Part01/02

sources-request@mirror.UUCP (09/24/86)

Submitted by: William LeFebvre <phil@rice.edu>
Mod.sources: Volume 7, Issue 30
Archive-name: top2/Part01

Well, here it is at last:  top, version 2.0.

This is a new version of a top users display for Berkeley 4.2 Unix.  It
uses termcap (but not curses) to periodically update the screen with
information about the top so-many processes that are using CPU cycles.

This distribution will work on the following Unix systems:  standard
Berkeley 4.2 (i.e.: for a VAX), Sun Unix (both 2.0 and 3.0), and
Pyramid Unix.  I received copies of top programs that were modified to
run on a Masscomp and a Sequent, but in both cases the number of
changes that were necessary were too high.  For comparison, the number
of #ifdef lines necessary for the two current variants are:  8 for the
Sun and 3 for the Pyramid.  And none of these changes made the display
look any different.  In contrast, the number of changes that would be
required for the Masscomp was somewhere in the area of 40, and several
changes to the display were necessary.  Similar comments hold for the
Sequent.  For these reasons, I am reluctant to add support for these
machines.  It would be just as easy to maintain completely different
source files for these architectures.  After so many ifdef's, code
becomes unreadable.

There is important information in the README.  Please read it even if
you are intimately familiar with top.  Some individuals will also be
interested in reading the Changes file.

Several suggestions that I received through the net were incorporated
in 2.0.  My thanks to all those who made recommendations.  Some day, I
should add acknowledgements, but not today.  Feel free to send me
changes and especially bug fixes.  If you succeed in getting top to run
on an unsupported architecture, and the number of changes is not too
high, then please send me the code (or at least a diff).  I will try to
include it in future versions.

			William LeFebvre
			Department of Computer Science
			Rice University
			<phil@Rice.edu>

-----cut here ----- -----cut here ----- -----cut here ----- -----cut here -----
#!/bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If all goes well, you will see the message "No problems found."

# Exit status; set to 1 on "wc" errors or if would overwrite.
STATUS=0
# Contents:  Changes Makefile Manifest README boolean.h bzero.c
#	commands.c display.c getopt.c kernel.c layout.h screen.c screen.h
#	sigconv.awk top.h
 
echo x - Changes
if test -f Changes ; then
    echo Changes exists, putting output in $$Changes
    OUT=$$Changes
    STATUS=1
else
    OUT=Changes
fi
sed 's/^X//' > $OUT <<'@//E*O*F Changes//'
XThu Sep  4 1986 - wnl (2.0, at last)
X	This is the version that will (hopefully) get released to the
X	world as top 2.0.
X	Added the "r" and "k" commands for renice and kill, respectively.
X	This required adding a way to handle system call errors, and the
X	addition of the "e" command.  Help screen and manual page were
X	changed to reflect this change.  Changed all "#ifdef SUN" directives
X	to "#ifdef sun", and changed all "#ifdef PYRAMID" directives to
X	"#ifdef pyr".  As much as I hate those choices of preprocessor
X	names (they too easily conflict with real variable names), it does
X	make automatic compilation possible---people don't have to change
X	the Makefile anymore for specific machines.  The manual page was
X	changed to automatically incorporate the defaults as set in the
X	Makefile (including an infinite value for TOPN) and the way the
X	manual page is generated by the Makefile was changed to make
X	maintenance of this information automatic.

XMon Jul 28 1986 - wnl (still pre 2.0)
X	Real close now.  I put in a new definition for the macro "pagetok"
X	that does an explicit shift of a constant expression involving
X	PGSHIFT.  Appropriate checks are made if PGSHIFT is to small.
X	"pagetok" is now used exclusively everywhere to convert kernel
X	clicks to kilobytes.  I added a full blown interactive mode with
X	the ability to change some of the runtime parameters (how many to
X	display, time delay, etc.) while top is running.  I also
X	incorporated a few ideas from the net:  control characters in the
X	command name are replaced with '?'; the '-S' option makes the
X	swapper and pager visible; options have been added to control the
X	number of displays produced (this makes it easier to make
X	performance snapshots with top).  I have also added the notion of
X	"infinite" values for number of processes and number of displays.
X	I fixed a long-standing bug in the uid to username mapping code
X	that was only aggravated on the pyramids:  it was an ill-defined
X	expression (akin to i = i++).  I tweaked the proc_compar routine
X	for qsort slightly so that stopped processes were more likely to
X	show up.  Manual page was updated to reflect all changes
X	noticeable to the user.

XTue Jul  1 1986 - wnl (pre 2.0 -- 1.9999?)
X	In the process of major revamping on the way to version 2.0.
X	I have completely done away with curses by adding my own screen
X	management routines in a separate file (screen.c).  The rationale
X	for this is that top knows a whole lot more about what is and is
X	not redundant on the screen and can compare simple integer values
X	where curses would have to compare strings.  This has turned out
X	to be a very big win speed-wise.  The proc_compar routine for
X	sorting has been rewritten to include several more keys.  I
X	decided this was necessary when I noticed that the "top" process
X	itself kept disappearing off the top 10 list on a Sun-3.  All the
X	processes had the same percentage (0%) and the sort wasn't really
X	doing anything worthwhile.  I changed the expression that computes
X	memory usage to use the ctob macro instead of just assuming that
X	pages were 512 bytes.  More work still needs to be done before
X	this version is usable.  I changed options-processing to use
X	getopt and added appropriate incantations to the Makefile.

XWed Feb 20 1985 - wnl (still 1.8)
X	Put in the ifdef FOUR_ONE statements to make top still compilable
X	on a 4.1 system.  Apparently, there are some users out there that
X	need this functionality.  Oh well.  I don't guarantee any of it,
X	since I can't test it.  Made appropriate changes to README and
X	final installation related changes to Makefile.

XSat Feb  2 1985 - wnl (1.8)
X	Removed all the ifdef FOUR_TWO statements and made "top" into a
X	4.2 only program.  If someone really wants to still run it on 4.1,
X	then they can do all the work.  We don't have a 4.1 machine
X	anymore, so I don't even know if the thing still works under 4.1.
X	Cleaned up the Makefile and the README.  Added installation rules
X	to the Makefile, as requested by several sites.  Fixed a very
X	obscure divide-by-zero bug.  Added a second "key" to the qsort
X	comparison function (proc_compar) so that comparisons are based on
X	cpu ticks if the percentages are equal (provided by Jonathon
X	Feiber at Sun).

XTue Dec 11 1984 - wnl (1.7)
X	Added the virtual and real memory status line to the header area
X	(provided by Jonathon Feiber at Sun)

XTue Nov 20 1984 - wnl (1.6)
X	Added an "exit" if sbrk's fail.  Added changes from Jonathon
X	Feiber at Sun:  ifdef SUN to make top work on Suns (they don't use
X	doubles in the proc structure), register declarations, check for
X	getting a user structure that has disappeared since the proc array
X	was read (it used to die, now it just shows the process as swapped).

XTue Nov 13 1984 - wnl (1.5)
X	If the number of displayable processes ("active_procs") was less
X	than the number of requested processes ("topn"), top would
X	segmentation fault.  This bug has been fixed.  Thanks to Prentiss
X	Riddle at ut-sally for pointing out the existence of this bug.

XTue Oct 23 1984 - wnl (1.4)
X	Finally fixed the hash table bug that caused processes owned by
X	root to sometimes appear with either no name or a different name
X	that had UID 0 (such as "operator").  Removed all the ifdef DEBUG
X	blocks to make top ready for distribution to the real world.

XSun Apr  8 1984 - wnl (still 1.3)
X	Made some slight changes to the display format.  It now looks more
X	aesthetically pleasing.  Added some preprocessor constants so that
X	the two defaults (number of processes and seconds of delay) easier
X	to change.

XThu Apr  5 1984 - wnl (1.3)
X	Changed the order in which things are done at initialization time.
X	This way, if an error occurs before starting the main loop, curses
X	will never get started.  Also changed other error handlers so that
X	endwin() is called before any flavor of exit.  Specifying a number
X	of processes that is more than the screen can handle is no longer
X	fatal.  It displays a warning message and pretends the user
X	specified the maximum for the screen.  Finally cured all the TSTP
X	blues (well, almost all).  I removed my TSTP handler and convinced
X	the system to always use the one that curses sets up.  Turns out
X	that "sleep" was stepping all over it during a pause.  So, I don't
X	use sleep anymore.  The only problem that remains with it now is
X	redrawing the old display before updating it after a pause.

XTue Apr  3 1984 - wnl (from 1.0 to 1.2)
X	I changed the format of the TIME column from just "seconds" to
X	"minutes:seconds".  I also made pausing work correctly.  Screen
X	redraws with an up to date display.  For compatibility with 4.2, I
X	changed the name of the "zero" function to "bzero".  The makefile
X	has been altered to handle versions for 4.1 and 4.2, and README
X	has been updated to reflect these recent changes.
@//E*O*F Changes//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - Makefile
if test -f Makefile ; then
    echo Makefile exists, putting output in $$Makefile
    OUT=$$Makefile
    STATUS=1
else
    OUT=Makefile
fi
sed 's/^X//' > $OUT <<'@//E*O*F Makefile//'
X# Makefile for "top", a top 10 process display for Unix
X#
X# This makefile is for top, version 2.0
X#
X# Written by William LeFebvre, Rice University graduate student

X# installation information:
X#	OWNER	- name (or uid) for the installed executable's owner
X#	GROUP	- group name (or gid) for the installed executable's group
X#	MODE	- mode for the installed executable (should start with a 0)
X#	BINDIR	- directory where the executable should live
X#	MANDIR	- directory where the manual page should live
X#	MAN	- troff macros for manual pages
X#	TROFF	- most appropriate troff command

XOWNER  = phil
XGROUP  = staff
XMODE   = 755
XBINDIR = /usr/local
XMANDIR = /usr/man/manl
XMAN    = man
XTROFF  = troff

X# Values for the two defaults in "top":
X#	TOPN	- default number of processes to display
X#	DELAY	- default delay between updates
X#
X# set TOPN to -1 to indicate infinity (so that top will display as many
X# as the screen will hold).

XTOPN = 10
XDELAY = 5

XTABLE =
X# Top maintains an internal hash table for translating uid to username.
X# This hash table must be big enough to hold every name in /etc/passwd.
X# It is possible, but not necessary, to specify the hash table size in
X# this Makefile.  Just uncomment the following line and provide a number.
X#TABLE = -DTable_size=

XTARFILES = README Changes Makefile top.c commands.c display.c kernel.c \
X	   screen.c getopt.c \
X	   boolean.h layout.h screen.h top.h top.local.h bzero.c \
X	   sigconv.awk top.man
XOBJS = top.o commands.o display.o kernel.o screen.o getopt.o

X# Top uses the preprocessor variables "sun" and "pyr" for specific changes
X# required by Suns and Pyramids.  No changes to "CFLAGS" are required for
X# these architectres.
XCFLAGS = -O
X# To make a version for 4.1, comment out the previous line and
X# uncomment the following two lines:
X#CFLAGS = -DFOUR_ONE -O
X#OBJS = top.o commands.o display.o kernel.o screen.o getopt.o bzero.o

Xall: top top.1

Xtop: $(OBJS)
X	cc $(CFLAGS) -o top $(OBJS) -ltermcap -lm

Xtop.o: top.c Makefile
X	cc -c $(CFLAGS) $(TABLE) -DDefault_TOPN=$(TOPN) -DDefault_DELAY=$(DELAY) top.c

X# include file dependencies
Xtop.o: boolean.h layout.h screen.h top.h top.local.h
Xcommands.o: sigdesc.h
Xdisplay.o: boolean.h layout.h screen.h top.h
Xkernel.o: top.local.h
Xscreen.o: boolean.h screen.h

X# automatically built include file
Xsigdesc.h: sigconv.awk /usr/include/signal.h
X	awk -f sigconv.awk /usr/include/signal.h >sigdesc.h

X# top.1 is built by combining the actual text with the default information
Xtop.1: top.man Makefile
X	echo '.nr N' $(TOPN) > top.1
X	echo '.nr D' $(DELAY) >>top.1
X	cat top.man >>top.1

Xtop.cat: top.1
X	nroff -$(MAN) top.1 | cat -s >top.cat

Xtroff: top.1
X	$(TROFF) -man top.1

Xtar:
X	rm -f top.tar
X	tar cvf top.tar $(TARFILES)

Xclean:
X	rm -f *.o top top.cat top.tar top.1 core

Xinstall: top top.1
X	install -s -o $(OWNER) -m $(MODE) -g $(GROUP) top $(BINDIR)
X	install -c top.1 $(MANDIR)
@//E*O*F Makefile//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - Manifest
if test -f Manifest ; then
    echo Manifest exists, putting output in $$Manifest
    OUT=$$Manifest
    STATUS=1
else
    OUT=Manifest
fi
sed 's/^X//' > $OUT <<'@//E*O*F Manifest//'
XChanges
XMakefile
XManifest
XREADME
Xboolean.h
Xbzero.c
Xcommands.c
Xdisplay.c
Xgetopt.c
Xkernel.c
Xlayout.h
Xscreen.c
Xscreen.h
Xsigconv.awk
Xtop.c
Xtop.h
Xtop.local.h
Xtop.man
@//E*O*F Manifest//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - README
if test -f README ; then
    echo README exists, putting output in $$README
    OUT=$$README
    STATUS=1
else
    OUT=README
fi
sed 's/^X//' > $OUT <<'@//E*O*F README//'
XThis file contains a few comments about "top", version 2.0

X"top" is a program that will give continual reports about the state of the
Xsystem, including a list of the top cpu using processes.  It requires read
Xaccess to the memory files "/dev/kmem" and "/dev/mem" as well as the system
Ximage "/vmunix".  Some installations have these files protected from general
Xaccess.  These sites would have to install this program in the same way that
Xprograms such as "ps" are installed.

XCAVEAT:  version 2.0 of top has internal commands that kill and renice
Xprocesses.  DO NOT INSTALL TOP AS A SETUID PROGRAM, or you will open up a
Xbig security hole since top makes no process ownership checks on its own.
XNote that it is still safe to install top as a set group-id program, since
Xgroup-id has no bearing on who can renice or send signals to what processes.

XThere are a few things that need to be checked before compiling the program:

XThe most important item is the internal hash table size.  This size is
Xdefined in the program with the preprocessor variable "Table_size".  This
Xconstant MUST be larger than the number of lines in the file /etc/passwd.
XIt is advisable that this number be about twice the number of lines, and
Xthat it be a prime number (since it dictates the size of the hash table).
XMake sure this is checked before compilation.  Its definition exists in
Xthe file "top.local.h", but it is also settable in the Makefile.

XSeveral other things are set in "top.local.h", including the file names
Xused for certain system files ("/vmunix", "/dev/kmem", etc.).  Although I
Xdon't expect those to vary much, they are put there for convenience.
XAnother parameter in this file is "Nominal_TOPN".  This will be discussed
Xin the next paragraph.

XThere are two preprocessor variables that are defined at compile time by
Xthe makefile.  These are "Default_TOPN" and "Default_DELAY".  Their values
Xare the defaults used for the top number of processes to be displayed and
Xthe number of seconds to delay between displays, respectively.  They are
Xset by the Makefile variables "TOPN" and "DELAY", respectively.  These
Xconstants are preset as follows:  TOPN=10, DELAY=5.  These can be
Xoverridden by either changing the Makefile or by specifying the change on
Xthe make command line (with something like "make TOPN=15").  Version 2 of
Xtop understands an "infinite" value for the number of processes to
Xdisplay.  Such a value indicates that top should display as much as will
Xfill the screen.  To specify a Default_TOPN of infinity, set TOPN equal
Xto -1.  Version 2 also understands the difference between an intelligent
Xterminal and a dumb terminal (such as a hardcopy device or a file).
XTypically, a default of infinity would not be desirable on a dumb
Xterminal, so the value of "Nominal_TOPN" is used when (1) Default_TOPN is
Xinfinity and (2) the output device is a dumb terminal or a file.  The
Xvalue for this preprocessor variable is set in "top.local.h" and can also
Xbe set from the "make" command line.  In the distribution, it is set to 18.

XBy default, the makefile will make a "top" for one of the following
Xsystems:  Berkeley 4.2, Sun Unix (version 1.1 and higher), and Pyramid
XUnix.  Previous versions of top fully supported Berkeley 4.1 Unix.  This
Xsupport has waned in version 2, and is not guaranteed to even work.  If
Xyou really must give it a try, you can change the makefile variable
X"CFLAGS" to make a 4.1 "top".  Instructions for doing this can be found in
X"Makefile".

XThe file "bzero.c" contains a function that will zero a block of memory on
Xa VAX.  This is only needed for Berkeley 4.1, since 4.2 has a bzero
Xdefined in the C run time library.  If you are strange enough to be
Xrunning 4.1 on something besides a VAX, you will have to replace this
Xroutine with one that will work on your machine.  If you don't know a
Xquick way to do it, then writing a simple loop will suffice.  "Bzero"
Xtakes two arguments:  a pointer to the buffer to zero, and the number of
Xbytes to zero.

XThere are also several parameters in the makefile that control
Xinstallation.  These should be altered to suit the desires and needs of
Xindividual sites.

XVersion 2.0 still only supports standard 4.2 and Sun and Pyramid
Xarchitectures.  I attempted to add sufficient changes to make top work on
Xa Masscomp, but found the number of required changes to be overwhelming.
XFeel free to alter top to make it run on whatever funny architecture you
Xhave.  I also encourage you to send those changes back to me at the
Xaddress below.  But, if the number of changes is high, I will be reluctant
Xto include the changes in the next version of top.  As an example, there
Xwere only 10 modifications required for the Sun version (and all changes
Xwere trivial), and just 4 changes were needed for the Pyramid.

XIf you make any kind of change to "top" that you feel would be beneficial
Xto others who use this program, or if you find and fix a bug, please send
Xme the change.

XEnjoy!

X                                William LeFebvre
X				Department of Computer Science
X				Rice University
X                                ARPANet address: <phil@Rice.edu>

X				U.S. Mail address:
X				    William LeFebvre
X				    P.O. Box 1892
X				    Department of Computer Science
X				    Houston, TX  77251
@//E*O*F README//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - boolean.h
if test -f boolean.h ; then
    echo boolean.h exists, putting output in $$boolean.h
    OUT=$$boolean.h
    STATUS=1
else
    OUT=boolean.h
fi
sed 's/^X//' > $OUT <<'@//E*O*F boolean.h//'
X/* My favorite names for boolean values */
X#define  No	0
X#define  Yes	1
X#define  Maybe	2		/* tri-state boolean, actually */

@//E*O*F boolean.h//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - bzero.c
if test -f bzero.c ; then
    echo bzero.c exists, putting output in $$bzero.c
    OUT=$$bzero.c
    STATUS=1
else
    OUT=bzero.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F bzero.c//'
X/*
X *  Fast, sleazy, and ugly zero function.
X *
X *  Note that this will only work on a VAX, but it is real easy to write a
X *  similar function for whatever machine you may need.  If nothing else,
X *  just a simple loop in C will suffice.
X *
X *  Dave Johnson, Rice University.
X *
X *  Enhanced by William LeFebvre of Rice University to handle zeroing more
X *  than 64K.
X */

X# define   K	1024

X/*
X *  bzero(memory, amount) - set "amount" bytes starting at "memory" to the
X *			    value 0.
X */

Xbzero(memory, amount)

Xchar *memory;
Xint  amount;

X{
X    while (amount >= 64*K)
X    {
X	_bzero64(memory, 64*K-1);
X	memory += 64*K-1;
X	amount -= 64*K-1;
X    }
X    _bzero64(memory, amount);
X}

X_bzero64(memory, amount)

Xchar *memory;
Xint  amount;

X{
X    asm("	movc5	$0, (sp), $0, 8(ap), *4(ap)");
X}
@//E*O*F bzero.c//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - commands.c
if test -f commands.c ; then
    echo commands.c exists, putting output in $$commands.c
    OUT=$$commands.c
    STATUS=1
else
    OUT=commands.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F commands.c//'
X/*
X *  Top users display for Berkeley Unix
X *
X *  This file contains the routines that implement some of the interactive
X *  mode commands.  Note that some of the commands are implemented in-line
X *  in "main".  This is necessary because they change the global state of
X *  "top" (i.e.:  changing the number of processes to display).
X */

X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X#include "sigdesc.h"		/* generated automatically */
X#include "boolean.h"

Xextern int  errno;
Xextern int  sys_nerr;
Xextern char *sys_errlist[];

Xextern char *copyright;

Xint err_compar();
Xchar *err_string();
Xchar *index();

X/*
X *  show_help() - display the help screen; invoked in response to
X *		either 'h' or '?'.
X */

Xshow_help()

X{
X    fputs(copyright, stdout);
X    fputs("\n\n\
XA top users display for Unix\n\
X\n\
XThese single-character commands are available:\n\
X\n\
X^L      - redraw screen\n\
Xq       - quit\n\
Xh or ?  - help; show this text\n\
Xd       - change number of displays to show\n\
Xe       - list errors generated by last \"kill\" or \"renice\" command\n\
Xk       - kill processes; send a signal to a list of processes\n\
Xn or #  - change number of processes to display\n\
Xr       - renice a process\n\
Xs       - change number of seconds to delay between updates\n\
X\n\
X\n", stdout);
X}

X/*
X *  Utility routines that help with some of the commands.
X */

Xchar *next_field(str)

Xregister char *str;

X{
X    register char *temp;

X    if ((str = index(str, ' ')) == NULL)
X    {
X	return(NULL);
X    }
X    *str = '\0';
X    while (*++str == ' ') /* loop */;
X    return(str);
X}

Xscanint(str, intp)

Xchar *str;
Xint  *intp;

X{
X    register int val = 0;
X    register char ch;

X    while ((ch = *str++) != '\0')
X    {
X	if (isdigit(ch))
X	{
X	    val = val * 10 + (ch - '0');
X	}
X	else if (isspace(ch))
X	{
X	    break;
X	}
X	else
X	{
X	    return(-1);
X	}
X    }
X    *intp = val;
X    return(0);
X}

X/*
X *  Some of the commands make system calls that could generate errors.
X *  These errors are collected up in an array of structures for later
X *  contemplation and display.  Such routines return a string containing an
X *  error message, or NULL if no errors occurred.  The next few routines are
X *  for manipulating and displaying these errors.  We need an upper limit on
X *  the number of errors, so we arbitrarily choose 20.
X */

X#define ERRMAX 20

Xstruct errs		/* structure for a system-call error */
X{
X    int  errno;		/* value of errno (that is, the actual error) */
X    char *arg;		/* argument that caused the error */
X};

Xstatic struct errs errs[ERRMAX];
Xstatic int errcnt;
Xstatic char *err_toomany = " too many errors occurred";
Xstatic char *err_listem = 
X	" Many errors occurred.  Press `e' to display the list of errors.";

X/* These macros get used to reset and log the errors */
X#define ERR_RESET   errcnt = 0
X#define ERROR(p, e) if (errcnt >= ERRMAX) \
X		    { \
X			return(err_toomany); \
X		    } \
X		    else \
X		    { \
X			errs[errcnt].arg = (p); \
X			errs[errcnt++].errno = (e); \
X		    }

X/*
X *  err_string() - return an appropriate error string.  This is what the
X *	command will return for displaying.  If no errors were logged, then
X *	return NULL.  The maximum length of the error string is defined by
X *	"STRMAX".
X */

X#define STRMAX 80

Xchar *err_string()

X{
X    register char *ptr;
X    register struct errs *errp;
X    register int  cnt = 0;
X    register int  first = Yes;
X    register int  currerr = -1;
X    int stringlen;		/* characters still available in "string" */
X    char string[STRMAX];

X    /* if there are no errors, return NULL */
X    if (errcnt == 0)
X    {
X	return(NULL);
X    }

X    /* sort the errors */
X    qsort(errs, errcnt, sizeof(struct errs), err_compar);

X    /* need a space at the from of the error string */
X    string[0] = ' ';
X    string[1] = '\0';
X    stringlen = STRMAX - 2;

X    /* loop thru the sorted list, building an error string */
X    ptr = string;
X    while (cnt < errcnt)
X    {
X	errp = &(errs[cnt++]);
X	if (errp->errno != currerr)
X	{
X	    if (currerr != -1)
X	    {
X		if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
X		{
X		    return(err_listem);
X		}
X		strcat(string, "; ");		/* we know there's more */
X	    }
X	    currerr = errp->errno;
X	    first = Yes;
X	}
X	if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
X	{
X	    return(err_listem);
X	}
X	first = No;
X    }

X    /* add final message */
X    stringlen = str_adderr(string, stringlen, currerr);

X    /* return the error string */
X    return(stringlen == 0 ? err_listem : string);
X}

X/*
X *  str_adderr(str, len, err) - add an explanation of error "err" to
X *	the string "str".
X */

Xstr_adderr(str, len, err)

Xchar *str;
Xint len;
Xint err;

X{
X    register char *msg;
X    register int  msglen;

X    msg = err == 0 ? "Not a number" : sys_errlist[err];
X    msglen = strlen(msg) + 2;
X    if (len <= msglen)
X    {
X	return(0);
X    }
X    strcat(str, ": ");
X    strcat(str, msg);
X    return(len - msglen);
X}

X/*
X *  str_addarg(str, len, arg, first) - add the string argument "arg" to
X *	the string "str".  This is the first in the group when "first"
X *	is set (indicating that a comma should NOT be added to the front).
X */

Xstr_addarg(str, len, arg, first)

Xchar *str;
Xint  len;
Xchar *arg;
Xint  first;

X{
X    register int arglen;

X    arglen = strlen(arg);
X    if (!first)
X    {
X	arglen += 2;
X    }
X    if (len <= arglen)
X    {
X	return(0);
X    }
X    if (!first)
X    {
X	strcat(str, ", ");
X    }
X    strcat(str, arg);
X    return(len - arglen);
X}

X/*
X *  err_compar(p1, p2) - comparison routine used by "qsort"
X *	for sorting errors.
X */

Xerr_compar(p1, p2)

Xregister struct errs *p1, *p2;

X{
X    register int result;

X    if ((result = p1->errno - p2->errno) == 0)
X    {
X	return(strcmp(p1->arg, p2->arg));
X    }
X    return(result);
X}

X/*
X *  error_count() - return the number of errors currently logged.
X */

Xerror_count()

X{
X    return(errcnt);
X}

X/*
X *  show_errors() - display on stdout the current log of errors.
X */

Xshow_errors()

X{
X    register int cnt = 0;
X    register struct errs *errp = errs;

X    printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
X    while (cnt++ < errcnt)
X    {
X	printf("%5s: %s\n", errp->arg,
X	    errp->errno == 0 ? "Not a number" : sys_errlist[errp->errno]);
X	errp++;
X    }
X}

X/*
X *  kill_procs(str) - send signals to processes, much like the "kill"
X *		command does; invoked in response to 'k'.
X */

Xchar *kill_procs(str)

Xchar *str;

X{
X    register char *nptr;
X    register char *optr;
X    int signum = SIGTERM;	/* default */
X    int procnum;
X    char badnum = 0;
X    struct sigdesc *sigp;

X    ERR_RESET;
X    if (str[0] == '-')
X    {
X	/* explicit signal specified */
X	if ((optr = nptr = next_field(str)) == NULL)
X	{
X	    return(" kill: no processes specified");
X	}

X	if (isdigit(str[1]))
X	{
X	    scanint(str + 1, &signum);
X	    if (signum <= 0 || signum >= NSIG)
X	    {
X		return(" invalid signal number");
X	    }
X	}
X	else 
X	{
X	    /* terminate the end of the signal name */
X	    while (*--optr == ' ');
X	    *++optr = '\0';

X	    /* translate the name into a number */
X	    for (sigp = sigdesc; sigp->name != NULL; sigp++)
X	    {
X		if (strcmp(sigp->name, str + 1) == 0)
X		{
X		    signum = sigp->number;
X		    break;
X		}
X	    }

X	    /* was it ever found */
X	    if (sigp->name == NULL)
X	    {
X		return(" bad signal name");
X	    }
X	}
X	/* put the new pointer in place */
X	str = nptr;
X    }

X    /* loop thru the string, killing processes */
X    do
X    {
X	if (scanint(str, &procnum) == -1)
X	{
X	    ERROR(str, 0);
X	}
X	else if (kill(procnum, signum) == -1)
X	{
X	    /* chalk up an error */
X	    ERROR(str, errno);
X	}
X    } while ((str = next_field(str)) != NULL);

X    /* return appropriate error string */
X    return(err_string());
X}

X/*
X *  renice_procs(str) - change the "nice" of processes, much like the
X *		"renice" command does; invoked in response to 'r'.
X */

Xchar *renice_procs(str)

Xchar *str;

X{
X    register char negate;
X    int prio;
X    int procnum;

X    ERR_RESET;

X    /* allow for negative priority values */
X    if ((negate = *str == '-'))
X    {
X	/* move past the minus sign */
X	str++;
X    }

X    /* use procnum as a temporary holding place and get the number */
X    procnum = scanint(str, &prio);

X    /* negate if necessary */
X    if (negate)
X    {
X	prio = -prio;
X    }

X    /* check for validity */
X    if (procnum == -1 || prio <= PRIO_MIN || prio >= PRIO_MAX)
X    {
X	return(" bad priority value");
X    }

X    /* move to the first process number */
X    if ((str = next_field(str)) == NULL)
X    {
X	return(" no processes specified");
X    }

X    /* loop thru the process numbers, renicing each one */
X    do
X    {
X	if (scanint(str, &procnum) == -1)
X	{
X	    ERROR(str, 0);
X	}
X	else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
X	{
X	    ERROR(str, errno);
X	}
X    } while ((str = next_field(str)) != NULL);

X    /* return appropriate error string */
X    return(err_string());
X}

@//E*O*F commands.c//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - display.c
if test -f display.c ; then
    echo display.c exists, putting output in $$display.c
    OUT=$$display.c
    STATUS=1
else
    OUT=display.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F 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#include <stdio.h>
X#include <ctype.h>
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/user.h>
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"

Xstatic int lmpid = 0;
Xstatic struct user u;

Xchar *printable();

X/* Verbose process state names */

Xchar *state_name[] =
X{
X    "", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped"
X};

X/* process state names for the "STATE" column of the display */

Xchar *state_abbrev[] =
X{
X    "", "sleep", "WAIT", "run", "start", "zomb", "stop"
X};

X/* cpu state names for percentages */

Xchar *cpu_state[] =
X{
X    "user", "nice", "system", "idle"
X};

X/* screen positions for cpustate figures */
Xchar x_cpustates[] = { 12, 24, 36, 50 };

Xi_loadave(mpid, avenrun)

Xint mpid;
X#ifdef sun
Xlong *avenrun;
X#else
Xdouble *avenrun;
X#endif sun

X{
X    register int i;

X    printf("last pid: %5d;  load averages", mpid);

X    for (i = 0; i < 3; i++)
X    {
X	printf("%c %4.2f",
X	    i == 0 ? ':' : ',',
X#ifdef sun
X	    (double)avenrun[i] / FSCALE);
X#else
X	    avenrun[i]);
X#endif
X    }
X    lmpid = mpid;
X}

Xu_loadave(mpid, avenrun)

Xint mpid;
X#ifdef sun
Xlong *avenrun;
X#else
Xdouble *avenrun;
X#endif sun

X{
X    register int i;

X    if (mpid != lmpid);
X    {
X	Move_to(x_lastpid, y_lastpid);
X	printf("%5d", mpid);
X	lmpid = mpid;
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#ifdef sun
X	    (double)avenrun[i] / FSCALE);
X#else
X	    avenrun[i]);
X#endif
X    }
X}

Xstatic int ltotal = 0;
Xstatic int lbrkdn[7];

Xi_procstates(total, brkdn)

Xint total;
Xint *brkdn;

X{
X    register int i;

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}

Xu_procstates(total, brkdn)

Xint total;
Xint *brkdn;

X{
X    register int i;

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

Xi_cpustates(changes, total)

Xint *changes;
Xint total;

X{
X    register int i;

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}

Xu_cpustates(changes, total)

Xint *changes;
Xint total;

X{
X    register int i;

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}

Xz_cpustates()

X{
X    register int i;

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}

Xi_memory(i1, i2, i3, i4, i5)

Xint i1, i2, i3, i4, i5;

X{
X    printf("Memory: %4dK (%4dK) real, %4dK (%4dK) virtual, %4dK free",
X	i1, i2, i3, i4, i5);
X}

Xu_memory(i1, i2, i3, i4, i5)

Xint i1, i2, i3, i4, i5;

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}

Xi_header(f2)

Xchar *f2;

X{
X    printf(
X      "\n\n  PID %s PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND", 
X      f2);
X}

Xu_header()

X{
X    Move_to(0, y_header);
X}

X#ifdef sun
X#define percent_cpu(pp) ((double)(pp)->p_pctcpu / FSCALE)
X#else
X#define percent_cpu(pp) ((pp)->p_pctcpu)
X#endif

X#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
X			 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))

X#define Proc_format "%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s"

X#ifdef DEBUG
XFILE *debug;
X#endif

Xi_process(line, pp, get_userid)

Xint line;
Xstruct proc *pp;
Xchar *(*get_userid)();

X{
X    register long cputime;
X    register double pctcpu;
X    register char *thisline;
X    int len;

X#ifdef DEBUG
X    debug = fopen("debug", "w");
X#endif
X    /* calculate a pointer to the buffer for this line */
X    thisline = screenbuf[line];

X    /* get the cpu usage and calculate the cpu percentages */
X    cputime = get_ucpu(pp);
X    pctcpu = percent_cpu(pp);

X    /* format the line */
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#ifdef pyr
X	pagetok(pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize),
X#else
X	pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize),
X#endif
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    /* write the line out */
X    putchar('\n');
X    fputs(thisline, stdout);

X    /* zero fill the rest of it */
X    len = strlen(thisline);
X    bzero(thisline + len, Display_width - len);
X}

Xstatic int lastline = 0;

Xu_process(line, pp, get_userid)

Xint line;
Xstruct proc *pp;
Xchar *(*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    register long cputime;
X    register double pctcpu;
X    char cursor_on_line = No;
X    char *thisline;
X    int screen_line = line + Header_lines;
X    static char newline[Display_width];

X    /* get a pointer to the old text for this line */
X    optr = thisline = screenbuf[line];

X    /* get the cpu usage and calculate the cpu percentages */
X    cputime = get_ucpu(pp);
X    pctcpu = percent_cpu(pp);

X    /* format the line */
X    sprintf(newline, Proc_format,
X	pp->p_pid,
X	(*get_userid)(pp->p_uid),
X	pp->p_pri - PZERO,
X	pp->p_nice - NZERO,
X#ifdef pyr
X	pagetok(pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize),
X#else
X	pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize),
X#endif
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    /* 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    /* 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     *  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	    /* 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	    /* 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	/* update working column and screen buffer pointer */
X	newcol++;
X	optr++;

X    } while (ch != '\0');

X    /* zero out the rest of the line buffer -- MUST BE DONE! */
X    bzero(optr, Display_width - newcol);

X    /* remember where the current line is */
X    if (cursor_on_line)
X    {
X	lastline = screen_line;
X    }
X}

Xstatic int last_hi = 0;

Xu_endscreen(hi)

Xregister int hi;

X{
X    register int screen_line = hi + Header_lines;

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

Xget_ucpu(pp)

Xstruct proc *pp;

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#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}

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

Xchar *printable(str)

Xchar *str;

X{
X    register char *ptr;
X    register char ch;

X    ptr = str;
X    while ((ch = *ptr) != '\0')
X    {
X	if (!isprint(ch))
X	{
X	    *ptr = '?';
X	}
X	ptr++;
X    }
X    return(str);
X}
@//E*O*F display.c//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - getopt.c
if test -f getopt.c ; then
    echo getopt.c exists, putting output in $$getopt.c
    OUT=$$getopt.c
    STATUS=1
else
    OUT=getopt.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F getopt.c//'
X/*LINTLIBRARY*/
X#define NULL	0
X#define EOF	(-1)
X#define ERR(s, c)	if(opterr){\
X	extern int strlen(), write();\
X	char errbuf[2];\
X	errbuf[0] = c; errbuf[1] = '\n';\
X	(void) write(2, argv[0], (unsigned)strlen(argv[0]));\
X	(void) write(2, s, (unsigned)strlen(s));\
X	(void) write(2, errbuf, 2);}

X#define strchr index

Xextern int strcmp();
Xextern char *strchr();

Xint	opterr = 1;
Xint	optind = 1;
Xint	optopt;
Xchar	*optarg;

Xint
Xgetopt(argc, argv, opts)
Xint	argc;
Xchar	**argv, *opts;
X{
X	static int sp = 1;
X	register int c;
X	register char *cp;

X	if(sp == 1)
X		if(optind >= argc ||
X		   argv[optind][0] != '-' || argv[optind][1] == '\0')
X			return(EOF);
X		else if(strcmp(argv[optind], "--") == NULL) {
X			optind++;
X			return(EOF);
X		}
X	optopt = c = argv[optind][sp];
X	if(c == ':' || (cp=strchr(opts, c)) == NULL) {
X		ERR(": unknown option, -", c);
X		if(argv[optind][++sp] == '\0') {
X			optind++;
X			sp = 1;
X		}
X		return('?');
X	}
X	if(*++cp == ':') {
X		if(argv[optind][sp+1] != '\0')
X			optarg = &argv[optind++][sp+1];
X		else if(++optind >= argc) {
X			ERR(": argument missing for -", c);
X			sp = 1;
X			return('?');
X		} else
X			optarg = argv[optind++];
X		sp = 1;
X	} else {
X		if(argv[optind][++sp] == '\0') {
X			sp = 1;
X			optind++;
X		}
X		optarg = NULL;
X	}
X	return(c);
X}
@//E*O*F getopt.c//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - kernel.c
if test -f kernel.c ; then
    echo kernel.c exists, putting output in $$kernel.c
    OUT=$$kernel.c
    STATUS=1
else
    OUT=kernel.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F kernel.c//'
X/*
X *  Top - a top users display for Berkeley Unix
X *  
X *  This file contains all the routines that retrieve values from
X *  kernel and user memory.
X */

X#include <stdio.h>
X#if defined(FOUR_ONE) || defined(pyr)
X#include <sys/pte.h>
X#else
X#include <machine/pte.h>
X#endif
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#include <sys/proc.h>

X#include "top.local.h"

X/* useful externals */
Xextern int errno;
Xextern char *sys_errlist[];

Xstatic int kmem = -1;
Xstatic int mem = -1;

Xinit_kernel()
X{
X    /* open kmem and mem */
X    if ((kmem = open(KMEM, 0)) < 0)
X    {
X	perror(KMEM);
X	exit(20);
X    }
X    if ((mem = open(MEM, 0)) < 0)
X    {
X	perror(MEM);
X	exit(21);
X    }

X}

X/*
X *  getu(p, u) - get the user structure for the process whose proc structure
X *	is pointed to by p.  The user structure is put in the buffer pointed
X *	to by u.  Return 0 if successful, -1 on failure (such as the process
X *	being swapped out).
X */

Xgetu(p, u)

Xregister struct proc *p;
Xstruct user *u;

X{
X    struct pte uptes[UPAGES];
X    register caddr_t upage;
X    register struct pte *pte;
X    register nbytes, n;

X    /*
X     *  Check if the process is currently loaded or swapped out.  The way we
X     *  get the u area is totally different for the two cases.  For this
X     *  application, we just don't bother if the process is swapped out.
X     */
X    if ((p->p_flag & SLOAD) == 0)
X    {
X	return(-1);
X    }

X    /*
X     *  Process is currently in memory, we hope!
X     */
X    if (!getkval(p->p_addr, uptes, sizeof(uptes), "!p->p_addr"))
X    {
X	/* we can't seem to get to it, so pretend it's swapped out */
X	return(-1);
X    } 
X    upage = (caddr_t)u;
X    pte = uptes;
X    for (nbytes = sizeof(struct user); nbytes > 0; nbytes -= NBPG)
X    {
X    	lseek(mem, pte++->pg_pfnum * NBPG, 0);
X	n = MIN(nbytes, NBPG);
X	if (read(mem, upage, n) != n)
X	{
X	    /* we can't seem to get to it, so pretend it's swapped out */
X	    return(-1);
X	}
X	upage += n;
X    }
X    return(0);
X}

X/*
X *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
X *	"offset" is the byte offset into the kernel for the desired value,
X *  	"ptr" points to a buffer into which the value is retrieved,
X *  	"size" is the size of the buffer (and the object to retrieve),
X *  	"refstr" is a reference string used when printing error meessages,
X *	    if "refstr" starts with a '!', then a failure on read will not
X *  	    be fatal (this may seem like a silly way to do things, but I
X *  	    really didn't want the overhead of another argument).
X *  	
X */

Xgetkval(offset, ptr, size, refstr)

Xlong offset;
Xint *ptr;
Xint size;
Xchar *refstr;

X{
X    if (lseek(kmem, offset, 0) == -1)
X    {
X	if (*refstr == '!')
X	{
X	    refstr++;
X	}
X	fprintf(stderr, "%s: lseek to %s: %s\n",
X	    KMEM, refstr, sys_errlist[errno]);
X	quit(22);
X    }
X    if (read(kmem, ptr, size) == -1)
X    {
X	if (*refstr == '!')
X	{
X	    /* we lost the race with the kernel, process isn't in memory */
X	    return(0);
X	} 
X	else 
X	{
X	    fprintf(stderr, "%s: reading %s: %s\n",
X		KMEM, refstr, sys_errlist[errno]);
X	    quit(23);
X	}
X    }
X    return(1);
X}
@//E*O*F kernel.c//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - layout.h
if test -f layout.h ; then
    echo layout.h exists, putting output in $$layout.h
    OUT=$$layout.h
    STATUS=1
else
    OUT=layout.h
fi
sed 's/^X//' > $OUT <<'@//E*O*F layout.h//'
X/*
X *  Top - a top users display for Berkeley Unix
X *
X *  This file defines the locations on tne screen for various parts of the
X *  display.  These definitions are used by the routines in "display.c" for
X *  cursor addressing.
X */

X#define  x_lastpid	10
X#define  y_lastpid	0
X#define  x_loadave	33
X#define  y_loadave	0
X#define  x_procstate	0
X#define  y_procstate	1
X#define  x_brkdn	14
X#define  y_brkdn	1
X#define  x_realmem	8
X#define  x_virtmem	28
X#define  x_free		51
X#define  y_mem		3
X#define  x_header	0
X#define  y_header	5
X#define  x_idlecursor	0
X#define  y_idlecursor	4
X#define  y_procs	6
X#define  x_p_pid	0
X#define  x_p_user	6
X#define  x_p_pri	15
X#define  x_p_nice	20
X#define  x_p_size	25
X#define  x_p_res	31
X#define  x_p_state	37
X#define  x_p_time	43
X#define  x_p_wcpu	50
X#define  x_p_cpu	57
X#define  x_p_command	64

X#define  y_cpustates	2
@//E*O*F layout.h//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - screen.c
if test -f screen.c ; then
    echo screen.c exists, putting output in $$screen.c
    OUT=$$screen.c
    STATUS=1
else
    OUT=screen.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F screen.c//'
X/*
X *  Top - a top users display for Berkeley Unix
X *
X *  This file contains the routines that interface to termcap and stty/gtty.
X */

X#include <stdio.h>
X#include <sgtty.h>
X#include "screen.h"
X#include "boolean.h"

Xextern char *myname;

Xint putstdout();

Xint  scrolls;
Xint  hardcopy;
Xint  screen_length;
Xint  screen_width;
Xchar ch_erase;
Xchar ch_kill;
Xchar smart_terminal;
Xchar PC;
Xchar *tgetstr();
Xchar *tgoto();
Xchar termcap_buf[1024];
Xchar init_buf[1024];
Xchar string_buffer[1024];
Xchar home[15];
Xchar lower_left[15];
Xchar *clear_line;
Xchar *clear_screen;
Xchar *cursor_motion;
Xchar *start_standout;
Xchar *end_standout;
Xchar *terminal_init;
Xchar *terminal_end;
Xshort ospeed;

Xstatic struct sgttyb old_settings;
Xstatic struct sgttyb new_settings;
Xstatic char is_a_terminal = No;

Xinit_termcap()

X{
X    char *bufptr;
X    char *PCptr;
X    char *term_name;
X    char *temp_ptr;
X    char *getenv();
X    int status;

X    /* assume we have a smart terminal until proven otherwise */
X    smart_terminal = Yes;

X    /* now get terminal name and termcap entry */
X    term_name = getenv("TERM");
X    if ((status = tgetent(termcap_buf, term_name)) != 1)
X    {
X	if (status == -1)
X	{
X	    fprintf(stderr, "%s: can't open termcap file\n", myname);
X	}
X	else
X	{
X	    fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
X		    myname, getenv("TERM"));
X	}

X	/* pretend it's dumb and proceed */
X	smart_terminal = No;
X	return;
X    }

X    /* these immediately indicate a very stupid terminal */
X    if (tgetflag("hc") || tgetflag("os"))
X    {
X	smart_terminal = No;
X	return;
X    }

X    /* set up common terminal capabilities */
X    if ((screen_length = tgetnum("li")) <= 0)
X    {
X	screen_length = smart_terminal = 0;
X	return;
X    }

X    /* screen_width is a little different */
X    if ((screen_width = tgetnum("co")) == -1)
X    {
X	screen_width = 79;
X    }
X    else
X    {
X	screen_width -= 1;
X    }

X    /* initialize the pointer into the termcap string buffer */
X    bufptr = string_buffer;

X    /* get necessary capabilities */
X    if ((clear_line    = tgetstr("ce", &bufptr)) == NULL ||
X	(clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
X	(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
X    {
X	smart_terminal = No;
X	return;
X    }

X    /* get some more sophisticated stuff -- these are optional */
X    terminal_init  = tgetstr("ti", &bufptr);
X    terminal_end   = tgetstr("te", &bufptr);
X    start_standout = tgetstr("so", &bufptr);
X    end_standout   = tgetstr("se", &bufptr);

X    /* pad character */
X    PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;

X    /* set convenience strings */
X    strcpy(home, tgoto(cursor_motion, 0, 0));
X    strcpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1));

X    /* if stdout is not a terminal, pretend we are a dumb terminal */
X    if (gtty(1, &old_settings) == -1)
X    {
X	smart_terminal = No;
X    }
X}

Xinit_screen()

X{
X    /* get the old settings for safe keeping */
X    if (gtty(1, &old_settings) == 0)
X    {
X	/* copy the settings so we can modify them */
X	new_settings = old_settings;

X	/* turn on CBREAK and turn off character echo and tab expansion */
X	new_settings.sg_flags |= CBREAK;
X	new_settings.sg_flags &= ~(ECHO|XTABS);
X	stty(1, &new_settings);

X	/* remember the erase and kill characters */
X	ch_erase = old_settings.sg_erase;
X	ch_kill  = old_settings.sg_kill;

X	/* remember that it really is a terminal */
X	is_a_terminal = Yes;

X	/* send the termcap initialization string */
X	putcap(terminal_init);
X    }
X    else
X    {
X	/* not a terminal at all---consider it dumb */
X	smart_terminal = No;
X    }
X}

Xend_screen()

X{
X    /* move to the lower left, clear the line and send "te" */
X    if (smart_terminal)
X    {
X	putcap(lower_left);
X	putcap(clear_line);
X	putcap(terminal_end);
X    }

X    /* if we have settings to reset, then do so */
X    if (is_a_terminal)
X    {
X	stty(1, &old_settings);
X    }
X}

Xreinit_screen()

X{
X    /* install our settings if it is a terminal */
X    if (is_a_terminal)
X    {
X	stty(1, &new_settings);
X    }

X    /* send init string */
X    if (smart_terminal)
X    {
X	putcap(terminal_init);
X    }
X}

Xstandout(fmt, a1, a2, a3)

Xchar *fmt;
Xint a1, a2, a3;

X{
X    if (smart_terminal)
X    {
X	putcap(start_standout);
X	printf(fmt, a1, a2, a3);
X	putcap(end_standout);
X    }
X    else
X    {
X	printf(fmt, a1, a2, a3);
X    }
X}

Xclear()

X{
X    if (smart_terminal)
X    {
X	putcap(clear_screen);
X    }
X}

X/* This has to be defined as a subroutine for tputs (instead of a macro) */

Xputstdout(ch)

Xchar ch;

X{
X    putchar(ch);
X}

@//E*O*F screen.c//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - screen.h
if test -f screen.h ; then
    echo screen.h exists, putting output in $$screen.h
    OUT=$$screen.h
    STATUS=1
else
    OUT=screen.h
fi
sed 's/^X//' > $OUT <<'@//E*O*F screen.h//'
X/*
X *  top - a top users display for Unix 4.2
X *
X *  This file contains all the definitions necessary to use the hand-written
X *  screen package in "screen.c"
X */

X#define TCputs(str)	tputs(str, 1, putstdout)
X#define putcap(str)	((str) != NULL ? TCputs(str) : 0)
X#define Move_to(x, y)	TCputs(tgoto(cursor_motion, x, y))

Xextern char ch_erase;		/* set to the user's erase character */
Xextern char ch_kill;		/* set to the user's kill  character */
Xextern char smart_terminal;     /* set if the terminal has sufficient termcap
X				   capabilities for normal operation */

X/* These aresome termcap strings for use outside of "screen.c" */
Xextern char *cursor_motion;
Xextern char *clear_line;

X/* rows and columns on the screen according to termcap */
Xextern int  screen_length;
Xextern int  screen_width;

X/* a function that puts a single character on stdout */
Xint putstdout();
@//E*O*F screen.h//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - sigconv.awk
if test -f sigconv.awk ; then
    echo sigconv.awk exists, putting output in $$sigconv.awk
    OUT=$$sigconv.awk
    STATUS=1
else
    OUT=sigconv.awk
fi
sed 's/^X//' > $OUT <<'@//E*O*F sigconv.awk//'
XBEGIN		{
X		    print "/* This file was automatically generated */"
X		    print "/* by the awk script \"sigconv.awk\".      */\n"
X		    print "struct sigdesc {"
X		    print "    char *name;"
X		    print "    int  number;"
X		    print "};\n"
X		    print "struct sigdesc sigdesc[] = {"
X		}

X/^#define[ \t][ \t]*SIG[A-Z]/	{
X				    printf "    \"%s\",\t%2d,\n", \
X					substr($2, 4), $3
X				}

XEND				{
X				    print "    NULL,\t 0\n};"
X				}
@//E*O*F sigconv.awk//
chmod u=rw,g=rw,o=rw $OUT
 
echo x - top.h
if test -f top.h ; then
    echo top.h exists, putting output in $$top.h
    OUT=$$top.h
    STATUS=1
else
    OUT=top.h
fi
sed 's/^X//' > $OUT <<'@//E*O*F top.h//'
X/*
X *  Top - a top users display for Berkeley Unix
X *
X *  General (global) definitions
X */

X/* Number of lines of header information on the standard screen */
X#define Header_lines	6

X/* Number of columns needed for display */
X#define Display_width	80

X/* Log base 2 of 1024 is 10 (2^10 == 1024) */
X#define LOG1024		10

X/* Convert clicks (kernel pages) to kbytes ... */
X/* If there is no PGSHIFT defined, assume it is 11 */
X/* Is this needed for compatability with some old flavor of 4.2 or 4.1? */
X#ifndef PGSHIFT
X#define pagetok(size)	((size) << 1)
X#else
X#if PGSHIFT>10
X#define pagetok(size)	((size) << (PGSHIFT - LOG1024))
X#else
X#define pagetok(size)	((size) >> (LOG1024 - PGSHIFT))
X#endif
X#endif

Xextern double logcpu;

Xdouble log();
Xdouble exp();

Xextern char (* screenbuf)[Display_width];
@//E*O*F top.h//
chmod u=rw,g=rw,o=rw $OUT
 
echo Inspecting for damage in transit...
temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     125    1165    6805 Changes
      96     461    2875 Makefile
      18      18     161 Manifest
      97     888    5259 README
       5      22     125 boolean.h
      43     131     789 bzero.c
     449    1382    8905 commands.c
     590    1631   11346 display.c
      66     182    1259 getopt.c
     141     497    3092 kernel.c
      38     129     845 layout.h
     233     604    4485 screen.c
      26     135     874 screen.h
      18      58     439 sigconv.awk
      34     132     794 top.h
    1979    7435   48053 total
!!!
wc  Changes Makefile Manifest README boolean.h bzero.c commands.c display.c getopt.c kernel.c layout.h screen.c screen.h sigconv.awk top.h | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if test -s $dtemp ; then
    echo "Ouch [diff of wc output]:"
    cat $dtemp
    STATUS=1
elif test $STATUS = 0 ; then
    echo "No problems found."
else
    echo "WARNING -- PROBLEMS WERE FOUND..."
fi
exit $STATUS