[net.sources] SPS - a useful replacement for ps

robert@hslrswi.UUCP (Robert Ward) (06/21/85)

The following program is intended as a replacement for the standard
ps(1) program for most applications. This version of SPS is implemented
for Vaxen running 4.[12]bsd.
This is a totally rewritten version of the code that was distributed
in /usr/src/undoc/sps for 4.2bsd.

Advantages of SPS over ps(1):
=============================
1. The output is printed in a more comprehensible style:
   User-names are listed rather than user-ids;
   Processes are listed in an order depicting their relationship
      i.e. each child process is listed underneath its corresponding parent
      process and with an indented asterisk to show that it is a sub-process.
      The level of indentation depicts how remote its ancestory is.
      Corresponding marks are used to show which processes belong to
      the same process group; Setuid processes are also shown;
   Wait channels or process states are listed symbolically, rather than using
      hexadecimal numbers;
   Times are listed in minutes and then hours when they become too big
      to be represented as second quantities;
   Lists the resident set size as well as the virtual memory sizes;
   Sps depicts which process are running in the foreground.
2. It is faster:
      Sps keeps the addresses of kernel data structures, the names of
      ttys and ptys as well as the names of users in a known file.
      This file must be reinitialised if users, ttys or ptys are
      added or removed from the system or if the kernel is changed.
      However, it does mean that sps can avoid doing an expensive
      nlist() each time it is run to locate kernel structures,
      as well as having to search through /dev to find [pt]tys.
3. Sps has a multitude of options to restrict output to a subset of processes.
      For instance, it can be instructed to list "busy" processes or
      just the processes of particular users or particular ttys.
      It can also be instructed to list the environment strings of
      particular processes and to list all the command arguments.
      Sps can be made to repeat its output with an arbitrary delay.
In short, my opinion is that SPS is a highly useful program that gives
me the information I want to know - but, there again, I may be biased.
********************************************************************************
Author:
=======
J. Robert Ward
(Hasler AG Bern, Abt. 34, Belpstrasse 23, CH-3000 Bern 14, Switzerland)
..... !mcvax!cernvax!hslrswi!robert
Comments, bug reports, bug fixes, improvements are welcome.
(If anyone is interested, I also have older versions of SPS for
standard V7 PDP-11 Unix and Unisoft/MC68k Version 1.3 Unix).
********************************************************************************
Compilation:
============
The following is a sh(1) archive.

To compile and test on a VAX-11 running 4.2bsd, cut after the
following dotted line and run sh on that file.
(For systems running 4.1bsd, change the line below from
"ln Makefile.4.2 Makefile" to "ln Makefile.4.1 Makefile").

For large systems, check in sps.h to ensure that it is configured
for sufficient ttys and users (the current limits are MAXUSERID=100
and MAXTTYS=50). Increase these values if necessary before compilation.

--------------------------------- C U T --- H E R E ---------------------------
#! /bin/sh
echo Extracting README
cat > README << '---END-OF-README---'
		SPS - Show Process Status
		=========================
To compile, install and test, type the following commands (as super-user):
	% make			# Compile SPS
	% make install		# Install SPS into /bin/sps
	% sps i			# Initialise SPS
	% sps va		# Run SPS
********************************************************************************
The files in this directory are for the 4.[12]BSD / VAX-11 version of SPS.
Compiler options are as follows -
	-DCHAOS		if the Chaos network is incorporated into 4.xbsd
	-DTRACE		for testing/debugging purposes
	-DBSD42		compile SPS for 4.2bsd.
			Otherwise, SPS is compiled for 4.1bsd.
********************************************************************************
If you want to tell SPS about a new type of device, then add a new line to
the symbol table (see globals2.c),after ensuring that there is sufficient
room in the `info' structure. (NWAITSTATE may need to be increased in sps.h).
********************************************************************************
SPS understands if the size of internal kernel tables are changed under VMUNIX,
but must be recompiled if major modifications are made to the kernel.
---END-OF-README---
echo Extracting Makefile.4.1
cat > Makefile.4.1 << '---END-OF-Makefile.4.1---'
# Makefile for SPS (4.1BSD UNIX Version)

PROG	=	sps
OBJS	=	filecount.o findtty.o flagdecode.o flagsetup.o \
		getcmd.o getupage.o globals1.o globals2.o hashuid.o \
		initialise.o initsymbols.o inittty.o main.o mktree.o \
		needed.o openfiles.o percentmem.o prcmd.o prcpu.o \
		prheader.o printall.o printproc.o prsummary.o readstatus.o \
		selectproc.o selecttty.o termwidth.o ttystatus.o waitingfor.o

INCS	=	sps.h
LIBS	=	-ltermlib
CFLAGS	=	-I/usr/src/sys

all:		$(PROG)
.c.o:
		cc $(CFLAGS) -c -O -R $<
globals1.o waitingfor.o:
		cc $(CFLAGS) -c -O $<

$(OBJS):	$(INCS)

$(PROG):	$(OBJS)
		cc -o $@ $(OBJS) $(LIBS)

install:	$(PROG)
		strip $(PROG)
		mv $(PROG) /bin/$(PROG)
		/etc/chown root /bin/$(PROG)
		chmod 4711 /bin/$(PROG)

lint:
		lint -x -b $(CFLAGS) *.c
clean:
		rm -f $(OBJS) $(PROG)
---END-OF-Makefile.4.1---
echo Extracting Makefile.4.2
cat > Makefile.4.2 << '---END-OF-Makefile.4.2---'
# Makefile for SPS (4.2BSD UNIX Version)

PROG	=	sps
OBJS	=	filecount.o findtty.o flagdecode.o flagsetup.o \
		getcmd.o getupage.o globals1.o globals2.o hashuid.o \
		initialise.o initsymbols.o inittty.o main.o mktree.o \
		needed.o openfiles.o percentmem.o prcmd.o prcpu.o \
		prheader.o printall.o printproc.o prsummary.o readstatus.o \
		selectproc.o selecttty.o termwidth.o ttystatus.o waitingfor.o

INCS	=	sps.h
LIBS	=	-ltermlib
CFLAGS	=	-DBSD42 -I/sys

all:		$(PROG)
.c.o:
		cc $(CFLAGS) -c -O -R $<
globals1.o waitingfor.o:
		cc $(CFLAGS) -c -O $<

$(OBJS):	$(INCS)

$(PROG):	$(OBJS)
		cc -o $@ $(OBJS) $(LIBS)

install:	$(PROG)
		strip $(PROG)
		mv $(PROG) /bin/$(PROG)
		/etc/chown root /bin/$(PROG)
		chmod 4711 /bin/$(PROG)

lint:
		lint -x -b $(CFLAGS) *.c
clean:
		rm -f $(OBJS) $(PROG)
---END-OF-Makefile.4.2---
echo Extracting filecount.c
cat > filecount.c << '---END-OF-filecount.c---'
# include	"sps.h"

/* FILECOUNT - Counts the # open files for the current process */
filecount ()
{
	register int		i ;
	register struct file	**f ;
	register int		count ;
	extern union userstate	User ;

	count = 0 ;
	for ( i = 0, f = User.u_us.u_ofile ; i < NOFILE ; i++ )
		if ( *f++ )
			count++ ;
	return ( count ) ;
}
---END-OF-filecount.c---
echo Extracting findtty.c
cat > findtty.c << '---END-OF-findtty.c---'
# include	"sps.h"
# include	<h/tty.h>

/* FINDTTY - Attempts to determine to which tty a process is connected */
struct ttyline	*findtty ( p )

register struct process		*p ;

{
	register struct ttyline	*lp ;
	extern struct info	Info ;
	extern struct ttyline	Notty ;
	extern union userstate	User ;

	if ( !p->pr_p.p_pgrp )
		return ( &Notty ) ;
	for ( lp = Info.i_ttyline ; lp->l_name[0] ; lp++ )
		if ( lp->l_dev == User.u_us.u_ttyd )
			return ( lp ) ;
	return ( &Notty ) ;
}
---END-OF-findtty.c---
echo Extracting flagdecode.c
cat > flagdecode.c << '---END-OF-flagdecode.c---'
# include	"sps.h"
# include	"flags.h"

/* FLAGDECODE - Looks at the argument list and sets various internal switches */
flagdecode ( argc, argv )

register int			argc ;
register char			**argv ;

{
	register char		*chp ;
	union flaglist		*plist ;
	union flaglist		*tlist ;
	union flaglist		*ulist ;
	static char		usage[] =
	"sps - Unknown option %s\nUsage - sps [ -defgijkoqrsvwyABFNPSTUWZ ][ process|tty|user ] ...\n";
	union flaglist		*getflgsp() ;
	extern struct flags	Flg ;

	plist = tlist = ulist = (union flaglist*)0 ;
	for ( argv++ ; --argc ; argv++ )
	{
		chp = *argv ;
		while ( *chp )
			switch ( *chp++ )
			{
				case '-' :
					/* Separation character */
					continue ;
				case 'd' :
				case 'D' :
					/* List disc orientated information */
					Flg.flg_d = 1 ;
					Flg.flg_v = 0 ;
					continue ;
				case 'e' :
				case 'E' :
					/* List environment strings */
					Flg.flg_e = 1 ;
					continue ;
				case 'f' :
					/* List the father's process id */
					Flg.flg_f = 1 ;
					continue ;
				case 'g' :
				case 'G' :
					/* List the process group id */
					Flg.flg_g = 1 ;
					continue ;
				case 'i' :
				case 'I' :
					/* Initialise (super-user only) */
					Flg.flg_i = 1 ;
					continue ;
				case 'j' :
				case 'J' :
					/* The next argument specifies the
					   name of the information file */
					if ( argc <= 1 )
						prexit(
	      "sps - Name of an information file expected after `-j' flag\n" ) ;
					argc-- ;
					Flg.flg_j = *++argv ;
					continue ;
				case 'k' :
				case 'K' :
					/* Use a disc file such as /vmcore
					   rather than /dev/{k}mem for
					   accessing kernel data. The next
					   argument specifies the file name. */
					if ( argc <= 1 )
						prexit(
	       "sps - Name of a memory dump file expected after `-k' flag\n" ) ;
					argc-- ;
					Flg.flg_k = *++argv ;
					Flg.flg_o = 1 ;
					continue ;
				case 'l' :
				case 'v' :
				case 'L' :
				case 'V' :
					/* Verbose output */
					Flg.flg_d = 0 ;
					Flg.flg_v = 1 ;
					continue ;
				case 'o' :
				case 'O' :
					/* Avoid looking at the swap device */
					Flg.flg_o = 1 ;
					continue ;
				case 'q' :
				case 'Q' :
					/* Show only the user time, not the
					   user + system times together. */
					Flg.flg_q = 1 ;
					continue ;
				case 'r' :
				case 'R' :
					/* Repeat output every n seconds.
					   The next argument specifies n which
					   defaults to 5 if omitted. */
					Flg.flg_r = 1 ;
					if ( argc > 1 )
					{
						if ( **++argv >= '0'
						&& **argv <= '9' )
						{
							argc-- ;
							Flg.flg_rdelay
							       = atoi( *argv ) ;
							continue ;
						}
						argv-- ;
					}
					Flg.flg_rdelay = 0 ;
					continue ;
				case 's' :
					/* Next argument specifies a symbol
					   file rather than the default
					   /vmunix. */
					if ( argc <= 1 )
						prexit(
		    "sps - Name of a symbol file expected after `-s' flag\n" ) ;
					argc-- ;
					Flg.flg_s = *++argv ;
					continue ;
				case 'w' :
					/* Wide output, exceeding 79 columns */
					Flg.flg_w = 1 ;
					continue ;
				case 'y' :
				case 'Y' :
					/* List current tty information */
					Flg.flg_y = 1 ;
					continue ;
				case 'a' :
				case 'A' :
					/* List all processes */
					Flg.flg_AZ = 1 ;
					Flg.flg_A = 1 ;
					continue ;
				case 'b' :
				case 'B' :
					/* List only busy processes */
					Flg.flg_AZ = 1 ;
					Flg.flg_B = 1 ;
					continue ;
				case 'F' :
					/* List only foreground processes */
					Flg.flg_AZ = 1 ;
					Flg.flg_F = 1 ;
					continue ;
				case 'n' :
				case 'N' :
					/* No processes, just the summary line*/
					Flg.flg_AZ = 1 ;
					Flg.flg_N = 1 ;
					continue ;
				case 'p' :
				case 'P' :
					/* List only the given process ids */
					Flg.flg_AZ = 1 ;
					Flg.flg_P = 1 ;
					if ( !plist )
					   plist=Flg.flg_Plist=getflgsp( argc );
					while ( argc > 1 )
					{
						if ( **++argv == '-' )
						{
							--argv ;
							break ;
						}
						--argc ;
						plist->f_chp = *argv ;
						(++plist)->f_chp = (char*)0 ;
					}
					continue ;
				case 'S' :
					/* List only stopped processes */
					Flg.flg_AZ = 1 ;
					Flg.flg_S = 1 ;
					continue ;
				case 't' :
				case 'T' :
					/* List only processes attached to the
					   specified terminals */
					Flg.flg_AZ = 1 ;
					Flg.flg_T = 1 ;
					if ( !tlist )
					   tlist=Flg.flg_Tlist=getflgsp( argc );
					while ( argc > 1 )
					{
						if ( **++argv == '-' )
						{
							--argv ;
							break ;
						}
						--argc ;
						tlist->f_chp = *argv ;
						(++tlist)->f_chp = (char*)0 ;
					}
					continue ;
				case 'u' :
				case 'U' :
					/* List only processes belonging to the
					   specified users */
					Flg.flg_AZ = 1 ;
					Flg.flg_U = 1 ;
					if ( !ulist )
					   ulist=Flg.flg_Ulist=getflgsp( argc );
					while ( argc > 1 )
					{
						if ( **++argv == '-' )
						{
							--argv ;
							break ;
						}
						--argc ;
						ulist->f_chp = *argv ;
						(++ulist)->f_chp = (char*)0 ;
					}
					continue ;
				case 'W' :
					/* List only waiting processes */
					Flg.flg_AZ = 1 ;
					Flg.flg_W = 1 ;
					continue ;
				case 'z' :
				case 'Z' :
					/* List only zombie processes */
					Flg.flg_AZ = 1 ;
					Flg.flg_Z = 1 ;
					continue ;
				default :
					prexit( usage, *argv ) ;
					/* NOTREACHED */
			}
	}
}
---END-OF-flagdecode.c---
echo Extracting flags.h
cat > flags.h << '---END-OF-flags.h---'
/* Structure holding information specified in the option list ... */
union flaglist
{
	char			*f_chp ;	/* Option specified as string */
	int			f_uid ;		/* Numerical user id */
	int			f_pid ;		/* Numerical process id */
	struct ttyline		*f_ttyline ;	/* Specified tty */
} ;

/* Structure holding global information specifed by arg list options ... */
struct flags
{
	int			flg_d:1 ;	/* disc orientated output */
	int			flg_e:1 ;	/* print environment string */
	int			flg_f:1 ;	/* print process father # */
	int			flg_g:1 ;	/* print process group # */
	int			flg_i:1 ;	/* initialise sps */
	char			*flg_j ;	/* Use this as the info file */
	char			*flg_k ;	/* Use this as the {k}mem file*/
	int			flg_o:1 ;	/* avoid the swap device */
	int			flg_q:1 ;	/* show user time only */
	int			flg_r:1 ;	/* repeat output */
	unsigned		flg_rdelay ;	/* ... with this much delay */
	char			*flg_s ;	/* Use this as the symbol file*/
	int			flg_v:1 ;	/* print verbose listing */
	int			flg_w:1 ;	/* print wide output */
	int			flg_y:1 ;	/* print tty information */
	int			flg_A:1 ;	/* print all processes */
	int			flg_B:1 ;	/* print busy processes */
	int			flg_F:1 ;	/* print foreground processes */
	int			flg_N:1 ;	/* print no processes */
	int			flg_P:1 ;	/* print specified process #'s*/
	int			flg_S:1 ;	/* print stopped processes */
	int			flg_T:1 ;	/* print procs for given ttys */
	int			flg_U:1 ;	/* print procs for given users*/
	int			flg_W:1 ;	/* print waiting processes */
	int			flg_Z:1 ;	/* print zombie processes */
	int			flg_AZ:1 ;	/* One of A to Z was specified*/
	union flaglist		*flg_Plist ;	/* List of specified processes*/
	union flaglist		*flg_Tlist ;	/* List of specified ttys */
	union flaglist		*flg_Ulist ;	/* List of specified users */
} ;
---END-OF-flags.h---
echo Extracting flagsetup.c
cat > flagsetup.c << '---END-OF-flagsetup.c---'
# include	"sps.h"
# include	"flags.h"
# include	<h/tty.h>

/*
** FLAGSETUP - Replaces any users or processes specified by flagdecode()
** with numerical equivalents. The lists are terminated by negative values.
** or null pointers. Ttystatus() must have been previously called to
** initialise the Info structure with chaos tty values.
*/
flagsetup ()
{
	register union flaglist	*fp ;
	register char		*chp ;
	register int		i ;
	register struct ttyline	*lp ;
	int			found ;
	extern struct flags	Flg ;
	extern struct info	Info ;

	/* Look for specified users */
	if ( Flg.flg_U )		
	{
		if ( !Flg.flg_Ulist->f_chp )
			prexit( "sps - User name was expected after -u flag\n");
		for ( fp = Flg.flg_Ulist ; chp = fp->f_chp ; fp++ )
		{
			found = 0 ;
			for ( i = 0 ; i < MAXUSERID ; i++ )
				if ( !strncmp( chp, Info.i_hnames[i].h_uname,
					UNAMELEN ) )
				{
					fp->f_uid = Info.i_hnames[i].h_uid ;
					found = 1 ;
					break ;
				}
			if ( !found )
				prexit( "sps - Unknown user: %s\n", chp ) ;
		}
		fp->f_uid = -1 ;
	}
	/* Look for specified process ids */
	if ( Flg.flg_P )		
	{
		if ( !Flg.flg_Plist->f_chp )
			prexit(
			     "sps - Process id was expected after -p flag\n" ) ;
		for ( fp = Flg.flg_Plist ; chp = fp->f_chp ; fp++ )
		{
			if ( chp[0] < '0' || chp[0] > '9' )
				prexit( "sps - Bad process id: %s\n", chp ) ;
			fp->f_pid = atoi( chp ) ;
		}
		fp->f_pid = -1 ;
	}
	/* Look for specified ttys */
	if ( !Flg.flg_T )		
		return ;
	if ( !Flg.flg_Tlist->f_chp )
		prexit( "sps - Tty name was expected after -t flag\n" ) ;
	for ( fp = Flg.flg_Tlist ; chp = fp->f_chp ; fp++ )
	{	/* Under VMUNIX, all ttys have two character names.
		   Thus, a flag of the form `t 8' should be expanded to
		   become `t 08'. */
		if ( !chp[1] )
			chp[1] = chp[0], chp[0] = '0' ;
		found = 0 ;
		for ( lp = Info.i_ttyline ; lp->l_name[0] ; lp++ )
			if ( !strncmp( chp, lp->l_name, 2 ) )
			{
				fp->f_ttyline = lp ;
				found = 1 ;
				break ;
			}
		if ( !found )
			prexit( "sps - Unknown tty name: %.2s\n", chp ) ;
	}
	fp->f_ttyline = (struct ttyline*)0 ;
}
---END-OF-flagsetup.c---
echo Extracting getcmd.c
cat > getcmd.c << '---END-OF-getcmd.c---'
# include	"sps.h"
# include	"flags.h"
# include	<h/vm.h>
# ifdef BSD42
# include	<vax/pte.h>
# else
# include	<h/pte.h>
# endif

/*
** GETCMD - Returns a character string read from a process' upage.
** This character string should represent the arguments to the current process.
*/
char	*getcmd ( p )

register struct process		*p ;

{
	register int		*ip ;
	register char		*cp ;
	register char		*cp0 ;
	unsigned		nbad ;
	struct dblock		db ;
	struct pte		ptetbl[ UPAGES + CLSIZE ] ;
	union
	{
		char		a_argc[ CLSIZE * NBPG ] ;
		int		a_argi[ CLSIZE * NBPG / sizeof( int ) ] ;
	} argbuf ;
	extern struct flags	Flg ;
	extern union userstate	User ;
	extern int		Flmem, Flswap ;
	char			*strcat(), *strncpy(), *strsave() ;

	p->pr_csaved = 0 ;
	p->pr_upag = 0 ;
	if ( p->pr_p.p_stat == SZOMB )
		return ( "** Exit **" ) ;
	if ( !(p->pr_p.p_flag & SLOAD) && Flg.flg_o )
		return ( "** Swapped out **" ) ;
	/* Find the process' upage */
	if ( !getupage( p, ptetbl ) )		
		return ( "** No upage **" ) ;
	/* Is this a system process ? */
	if ( p->pr_p.p_flag & SSYS )		
		switch ( p->pr_p.p_pid )
		{
			case 0 :
				p->pr_upag = 1 ;
				return ( "Unix Swapper" ) ;
			case 2 :
				p->pr_upag = 1 ;
				return ( "Unix Pager" ) ;
			default :
				break ;
		}
	/* Look at the top of the upage to locate the command arguments.
	   The page is loaded if the process itself is loaded and the pte
	   contains is marked as valid. */
	if ( (p->pr_p.p_flag & SLOAD)
	&& !ptetbl[0].pg_fod && ptetbl[0].pg_pfnum )
	{	/* If the page is loaded, read the arguments from
		   physical memory. */
		memseek( Flmem, (long)ctob( ptetbl[0].pg_pfnum ) ) ;
		if ( read( Flmem, argbuf.a_argc, CLSIZE*NBPG ) != CLSIZE*NBPG )
			return ( "** Memory read error **" ) ;
	}
	else				
	{	/* Otherwise the page is on the swap device */
		vstodb( 0, CLSIZE, &User.u_us.u_smap, &db, 1 ) ;
# ifdef BSD42
		swseek( (long)dtob( db.db_base ) ) ;
# else
		swseek( (long)ctob( db.db_base ) ) ;
# endif
		if ( Flg.flg_o )
			return ( "** Swapped page **" ) ;
		if ( read( Flswap, argbuf.a_argc, CLSIZE*NBPG ) != CLSIZE*NBPG )
			return ( "** Swap device read error **" ) ;
	}
	/* Look down until the end of command arguments is found. */
	p->pr_upag = 1 ;
	p->pr_csaved = 1 ;
	ip = &argbuf.a_argi[ CLSIZE*NBPG / sizeof( int ) ] ;
	ip -= 2 ;
	while ( *--ip )
		if ( ip == &argbuf.a_argi[0] )
		{	/* If the command arguments cannot be accessed from
			   the user's memory space, get the command name from
			   the system's idea of what the name should be. */
			argbuf.a_argc[0] = '(' ;
			(void)strncpy( &argbuf.a_argc[1], User.u_us.u_comm,
				sizeof( User.u_us.u_comm ) ) ;
			(void)strcat( &argbuf.a_argc[0], ")" ) ;
			return ( strsave( argbuf.a_argc ) ) ;
		}
	/* Process the command arguments, looking for nulls and unprintable
	   characters. */
	cp0 = (char*)(ip + 1) ;
	if ( !*cp0 )			
		cp0++ ;			
	nbad = 0 ;			
	for ( cp = cp0 ; cp < &argbuf.a_argc[ CLSIZE*NBPG ] ; cp++ )
	{
		*cp &= 0177 ;
		if ( !*cp )		
		{	/* Replace nulls with spaces */
			*cp = ' ' ;
			continue ;
		}
		if ( *cp < ' ' || *cp == 0177 )
		{	/* Replace control characters with ?'s */
			if ( ++nbad > 5 )
			{
				*cp++ = ' ' ;
				break ;
			}
			*cp = '?' ;
			continue ;
		}
		if ( !Flg.flg_e && *cp == '=' )
		{	/* Break on an `=' if we are not interested in the
			   environment strings. */
			*cp = '\0' ;
			while ( cp > cp0 && *--cp != ' ' )
				*cp = '\0' ;
			break ;
		}
	}
	while ( *--cp == ' ' )
		*cp = '\0' ;
	return ( strsave( cp0 ) ) ;
}

/*
** VSTODB - Given a base/size pair in virtual swap area,
** return a physical base/size pair which is the
** (largest) initial, physically contiguous block.
/* This code is stolen from the kernel file /sys/sys/vm_drum.c.
*/
vstodb ( vsbase, vssize, dmp, dbp, rev )

register int			vsbase ;
register int			vssize;
struct dmap			*dmp ;
register struct dblock		*dbp ;
int				rev ;

{
	register int		blk ;
	register swblk_t	*ip ;
# ifdef BSD42
	extern struct info	Info ;
# endif

# ifdef BSD42
	blk = Info.i_dmmin ;
# else
	blk = DMMIN ;
# endif
	ip = dmp->dm_map ;
	while ( vsbase >= blk )
	{
		vsbase -= blk ;
# ifdef BSD42
		if ( blk < Info.i_dmmax )
# else
		if ( blk < DMMAX )
# endif
			blk *= 2 ;
		ip++ ;
	}
	dbp->db_size = vssize < blk - vsbase ? vssize : blk - vsbase ;
	dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
}
---END-OF-getcmd.c---
echo Extracting getupage.c
cat > getupage.c << '---END-OF-getupage.c---'
# include	"sps.h"
# include	<h/vm.h>
# ifdef BSD42
# include	<vax/pte.h>
# else
# include	<h/pte.h>
# endif
# include	<stdio.h>

/*
** GETUPAGE - Reads the upage for the specified process as well as sufficient
** page tables entries for reading the command arguments. The pte's are read
** into the argument `ptetbl'. The upage is read into the external variable
** `User'. This procedure returns 1 if the upage was successfully read.
*/

# define	usrpt		(Info.i_usrpt)

getupage ( p, ptetbl )

register struct process		*p ;
register struct pte		*ptetbl ;

{
	register int		i ;
	register int		ncl ;
	struct pte		pte ;
	extern struct info	Info ;
	extern union userstate	User ;
	extern int		Flmem, Flkmem, Flswap ;

	/* If the process is not loaded, look for the upage on the swap device*/
	if ( !(p->pr_p.p_flag & SLOAD) )
	{				
# ifdef BSD42
		swseek( (long)dtob( p->pr_p.p_swaddr ) ) ;
# else
		swseek( (long)ctob( p->pr_p.p_swaddr ) ) ;
# endif
		if ( read( Flswap, (char*)&User.u_us, sizeof( struct user ) )
		!= sizeof( struct user ) )
		{
			fprintf( stderr,
				"sps - Can't read upage of process %d\n",
			    p->pr_p.p_pid ) ;
			return ( 0 ) ;
		}
		return ( 1 ) ;		
	}				
	/* The process is loaded. Locate the process pte's by reading
	   the pte of their base address from system virtual address space. */
	memseek( Flkmem, (long)&Info.i_usrptmap[ btokmx(p->pr_p.p_p0br)
		+ p->pr_p.p_szpt-1 ] ) ;
	if ( read( Flkmem, (char*)&pte, sizeof( struct pte ) )
	!= sizeof( struct pte ) )
	{
		fprintf( stderr,
		      "sps - Can't read indir pte for upage of process %d\n",
		    p->pr_p.p_pid ) ;
		return ( 0 ) ;
	}				
	/* Now read the process' pte's from physical memory. We need to access
	   sufficient pte's for the upage and for the command arguments. */
	memseek( Flmem, (long)ctob( pte.pg_pfnum+1 )
		- (UPAGES+CLSIZE)*sizeof( struct pte ) ) ;
	if ( read( Flmem, (char*)ptetbl, (UPAGES+CLSIZE)*sizeof( struct pte ) )
	!= (UPAGES+CLSIZE)*sizeof( struct pte ) )
	{
		fprintf( stderr, "sps - Can't read page table of process %d\n",
			p->pr_p.p_pid ) ;
		return ( 0 ) ;
	}
	/* Now we can read the pages belonging to the upage.
	   Here we read in an entire click at one go. */
	ncl = (sizeof( struct user ) + NBPG*CLSIZE - 1) / (NBPG*CLSIZE) ;
	while ( --ncl >= 0 )		
	{				
		i = ncl * CLSIZE ;
		memseek( Flmem, (long)ctob( ptetbl[ CLSIZE+i ].pg_pfnum ) ) ;
		if ( read( Flmem, User.u_pg[i], CLSIZE*NBPG ) != CLSIZE*NBPG )
		{
			fprintf( stderr,
				"sps - Can't read page 0x%x of process %d\n",
				ptetbl[ CLSIZE+i ].pg_pfnum, p->pr_p.p_pid ) ;
			return ( 0 ) ;
		}
	}
	return ( 1 ) ;
}
---END-OF-getupage.c---
echo Extracting globals1.c
cat > globals1.c << '---END-OF-globals1.c---'
# include	"sps.h"
# include	"flags.h"

/* Read/Write Variables global to the code of sps */

struct info			Info ;		/* Information structure */

struct flags			Flg ;		/* Flag options */

struct summary			Summary ;	/* Summary of processes */

union  userstate		User ;		/* Upage of one process */

int				Flmem, Flkmem, Flswap ;	/* File descriptors */

unsigned			Termwidth ;	/* Width of output device */

short				Lastpgrp ;	/* Last process pgrp printed */

short				Lastuid ;	/* Last process uid printed */
---END-OF-globals1.c---
echo Extracting globals2.c
cat > globals2.c << '---END-OF-globals2.c---'
# include	"sps.h"

/* Read Only variables, global to the code of sps ... */

/* Null ttyline device ... */
struct ttyline			Notty = { 0, 0, "  ", 0	} ;

/*
** The symbol table. For each address read from the kernel during
** initialisation, this table shows the following:
**	i.   the name of that symbol within the kernel ;
**	ii.  whether an extra indirection is needed through the kernel,
**	     i.e. whether the value of that symbol should be obtained
**	     rather than its address.
**	iii. where the obtained value/address is placed in the Info structure ;
**	iv.  whether the obtained value is associated with a reason for
**	     a process wait state.
*/
/* The order of entries in this table is unimportant. */

extern struct info		Info ;

struct symbol	Symbollist[] =
{	
	/* Kernel addresses required in order to access process,
	   tty and upage information. All these addresses should be
	   located in the symbol file during initialisation. */
	{ "_proc",	1,  (caddr_t*)&Info.i_proc0,	(char*)0	},
	{ "_nproc",	1,  (caddr_t*)&Info.i_nproc,	(char*)0	},
	{ "_text",	1,  (caddr_t*)&Info.i_text0,	(char*)0	},
	{ "_ntext",	1,  (caddr_t*)&Info.i_ntext,	(char*)0	},
	{ "_inode",	1,  (caddr_t*)&Info.i_inode0,	(char*)0	},
	{ "_ninode",	1,  (caddr_t*)&Info.i_ninode,	(char*)0	},
	{ "_swbuf",	1,  (caddr_t*)&Info.i_swbuf0,	(char*)0	},
	{ "_nswbuf",	1,  (caddr_t*)&Info.i_nswbuf,	(char*)0	},
	{ "_buf",	1,  (caddr_t*)&Info.i_buf0,	(char*)0	},
	{ "_nbuf",	1,  (caddr_t*)&Info.i_nbuf,	(char*)0	},
	{ "_ecmx",	1,  (caddr_t*)&Info.i_ecmx,	(char*)0	},
	{ "_Usrptmap",	0,  (caddr_t*)&Info.i_usrptmap,	(char*)0	},
	{ "_usrpt",	0,  (caddr_t*)&Info.i_usrpt,	(char*)0	},
	{ "_cdevsw",	0,  (caddr_t*)&Info.i_cdevsw,	(char*)0	},
# ifdef BSD42
	{ "_quota",	1,  (caddr_t*)&Info.i_quota0,	(char*)0	},
	{ "_nquota",	1,  (caddr_t*)&Info.i_nquota,	(char*)0	},
	{ "_dmmin",	1,  (caddr_t*)&Info.i_dmmin,	(char*)0	},
	{ "_dmmax",	1,  (caddr_t*)&Info.i_dmmax,	(char*)0	},
	{ "_mbutl",	0,  (caddr_t*)&Info.i_mbutl,	(char*)0	},
# else
	{ "_hz",	1,  (caddr_t*)&Info.i_hz,	(char*)0	},
# endif
# ifdef CHAOS
	{ "_Chconntab",	0,  &Info.i_Chconntab,		(char*)0	},
# endif
	/* Kernel addresses associated with process wait states.
	   It is not important if some of these addresses are unresolved
	   at initialisation. */
	{ "_fltab",	0,  &Info.i_waitstate[0],	"floppy"	},
	{ "_tu",	0,  &Info.i_waitstate[1],	"tu58"		},
	{ "_bfreelist",	0,  &Info.i_waitstate[2],	"buffer"	},
	{ "_lp_softc",	0,  &Info.i_waitstate[3],	"printr"	},
	{ "_lbolt",	0,  &Info.i_waitstate[4],	"lbolt"		},
	{ "_runin",	0,  &Info.i_waitstate[5],	"runin"		},
	{ "_runout",	0,  &Info.i_waitstate[6],	"runout"	},
	{ "_ipc",	0,  &Info.i_waitstate[7],	"ptrace"	},
	{ "_u",		0,  &Info.i_waitstate[8],	"pause"		},
	{ "_freemem",	0,  &Info.i_waitstate[9],	"freemm"	},
	{ "_kernelmap",	0,  &Info.i_waitstate[10],	"kermap"	},
	{ "_cwaiting",	0,  &Info.i_waitstate[11],	"cwait"		},
	{ "_rhpbuf",	0,  &Info.i_waitstate[12],	"rhpbuf"	},
	{ "_rhtbuf",	0,  &Info.i_waitstate[13],	"rhtbuf"	},
	{ "_ridcbuf",	0,  &Info.i_waitstate[14],	"ridcbf"	},
	{ "_rikbuf",	0,  &Info.i_waitstate[15],	"rikbuf"	},
	{ "_rmtbuf",	0,  &Info.i_waitstate[16],	"rmtbuf"	},
	{ "_rrkbuf",	0,  &Info.i_waitstate[17],	"rrkbuf"	},
	{ "_rrlbuf",	0,  &Info.i_waitstate[18],	"rrlbuf"	},
	{ "_rrxbuf",	0,  &Info.i_waitstate[19],	"rrxbuf"	},
	{ "_rswbuf",	0,  &Info.i_waitstate[20],	"rswbuf"	},
	{ "_rtmbuf",	0,  &Info.i_waitstate[21],	"rtmbuf"	},
	{ "_rtsbuf",	0,  &Info.i_waitstate[22],	"rtsbuf"	},
	{ "_rudbuf",	0,  &Info.i_waitstate[23],	"rudbuf"	},
	{ "_rupbuf",	0,  &Info.i_waitstate[24],	"rupbuf"	},
	{ "_rutbuf",	0,  &Info.i_waitstate[25],	"rutbuf"	},
	{ "_rvabuf",	0,  &Info.i_waitstate[26],	"rvabuf"	},
	{ "_rvpbuf",	0,  &Info.i_waitstate[27],	"rvpbuf"	},
	{ "_chtbuf",	0,  &Info.i_waitstate[28],	"chtbuf"	},
	{ "_cmtbuf",	0,  &Info.i_waitstate[29],	"cmtbuf"	},
	{ "_ctmbuf",	0,  &Info.i_waitstate[30],	"ctmbuf"	},
	{ "_ctsbuf",	0,  &Info.i_waitstate[31],	"ctsbuf"	},
	{ "_cutbuf",	0,  &Info.i_waitstate[32],	"cutbuf"	},
# ifdef BSD42
	{ "_selwait",	0,  &Info.i_waitstate[33],	"select"	},
# endif
# ifdef CHAOS
	{ "_Chrfclist",	0,  &Info.i_waitstate[34],	"chrfc"		},
# endif
	{ (char*)0,	0,  (caddr_t*)0,		(char*)0	}
} ;
---END-OF-globals2.c---
echo Extracting hashuid.c
cat > hashuid.c << '---END-OF-hashuid.c---'
# include	"sps.h"

/* The hashing functions themselves ... */
# define	HASHFN1( a )		(((a)*91 + 17) % MAXUSERID)
# define	HASHFN2( a )		(((a) + 47) % MAXUSERID)

/*
** HASHUID - Returns a pointer to a slot in the hash table that corresponds
** to the hash table entry for `uid'. It returns a null pointer if there is
** no such slot.
*/
struct hashtab	*hashuid ( uid )

int				uid ;

{
	register struct hashtab	*hp ;
	register int		i ;
	register int		j ;
	extern struct info	Info ;

	j = HASHFN1( uid ) ;
	for ( i = 0 ; i < MAXUSERID ; i++ )
	{
		hp = &Info.i_hnames[ j ] ;
		if ( !hp->h_uname[0] )
			return ( (struct hashtab*)0 ) ;
		if ( hp->h_uid == uid )
			return ( hp ) ;
		j = HASHFN2( j ) ;
	}
	return ( (struct hashtab*)0 ) ;
}

/*
** HASHNEXT - Returns a pointer to the next slot in the hash table that
** may be use for storing information for `uid'. It returns a null pointer
** if there are no more free slots available.
*/
struct hashtab	*hashnext ( uid )

int				uid ;

{
	register struct hashtab	*hp ;
	register int		i ;
	register int		j ;
	extern struct info	Info ;

	j = HASHFN1( uid ) ;
	for ( i = 0 ; i < MAXUSERID ; i++ )
	{
		hp = &Info.i_hnames[ j ] ;
		if ( !hp->h_uname[0] )
			return ( hp ) ;
		j = HASHFN2( j ) ;
	}
	return ( (struct hashtab*)0 ) ;
}
---END-OF-hashuid.c---
echo Extracting initialise.c
cat > initialise.c << '---END-OF-initialise.c---'
# include	"sps.h"
# include	"flags.h"
# include	<pwd.h>
# include	<stdio.h>

/*
** INITIALISE - Called to reset the `Info' structure with new kernel
** addresses and user and tty information.
*/
initialise ()
{
	register FILE		*fd ;
	char			*fileinfo ;
	extern struct flags	Flg ;
	extern struct info	Info ;
	FILE			*fopen() ;

	fileinfo = Flg.flg_j ? Flg.flg_j : FILE_INFO ;
	/* Read kernel addresses */
	initsymbols() ;			
	/* Read user names */
	initusers() ;			
	(void)umask( ~0644 ) ;		
	if ( !(fd = fopen( fileinfo, "w" )) )
	{
		fprintf( stderr, "sps - Can't create info file %s", fileinfo ) ;
		sysperror() ;
	}
	/* Find tty addresses */
	inittty() ;			
	if ( fwrite( (char*)&Info, sizeof( struct info ), 1, fd ) != 1 )
	{
		fprintf( stderr, "sps - Can't write info file %s", fileinfo ) ;
		sysperror() ;
		exit( 1 ) ;
	}
	(void)fclose( fd ) ;
	printf( "sps is initialised\n" ) ;
}

/* INITUSERS - Read the passwd file and fill in the user name arrays */
initusers ()
{
	register struct passwd	*pw ;
	register struct hashtab	*hp ;
	struct passwd		*getpwent() ;
	char			*strncpy() ;
	struct hashtab		*hashuid(), *hashnext() ;

	while ( pw = getpwent() )
	{	/* For each user in the passwd file, first see if that uid
		   has been already allocated in the hash table. */
		if ( hp = hashuid( pw->pw_uid ) )
		{
			fprintf( stderr,
		   "sps - Names %s and %s conflict in passwd file for uid %d\n",
				hp->h_uname, pw->pw_name, pw->pw_uid ) ;
			continue ;
		}
		/* Try to find a free slot in the hash table and fill it. */
		if ( !(hp = hashnext( pw->pw_uid )) )
			prexit( "sps - Too many users in passwd file\n" ) ;
		hp->h_uid = pw->pw_uid ;
		(void)strncpy( hp->h_uname, pw->pw_name, UNAMELEN ) ;
	}
	(void)endpwent() ;
}
---END-OF-initialise.c---
echo Extracting initsymbols.c
cat > initsymbols.c << '---END-OF-initsymbols.c---'
# include	"sps.h"
# include	"flags.h"
# ifdef BSD42
# include	<sys/file.h>
# endif
# include	<nlist.h>
# include	<stdio.h>

/* INITSYMBOLS - Reads kmem values into the Info structure */
/*
** THIS CODE COPIES KMEM VALUES INTO THE INFO STRUCTURE ASSUMING THAT
** VALUES READ FROM THE KERNEL HAVE TYPE CADDR_T. THEREFORE, WE ARE
** MAKING THE DUBIOUS ASSUMPTION THAT INTS, POINTERS AND CADDR_T's
** HAVE IDENTICAL SIZES.
*/
initsymbols ()
{
	register struct nlist	*np ;
	register struct symbol	*s ;
	register struct nlist	*np0 ;
	char			*filesymbol ;
	extern int		Flkmem ;
	extern struct flags	Flg ;
	extern struct symbol	Symbollist[] ;
	extern struct info	Info ;
	char			*getcore() ;
	char			*strncpy() ;

	filesymbol = Flg.flg_s ? Flg.flg_s : FILE_SYMBOL ;
	/* Find the length of the symbol table */
	for ( s = Symbollist ; s->s_kname ; s++ )
		;
	/* Construct an nlist structure by copying names from the symbol table*/
	np0 = (struct nlist*)getcore( (s-Symbollist+1)*sizeof( struct nlist ) );
	for ( s = Symbollist, np = np0 ; s->s_kname ; s++, np++ )
	{					
		np->n_name = s->s_kname ;	
		np[1].n_name = (char*)0 ;	
		np->n_value = 0 ;
	}
# ifdef BSD42
	if ( access( filesymbol, R_OK ) < 0 )
# else
	if ( access( filesymbol, 4 ) < 0 )
# endif
	{
		fprintf( stderr, "sps - Can't open symbol file %s", filesymbol);
		sysperror() ;
	}
	/* Get kernel addresses */
	nlist( filesymbol, np0 ) ;		
	if ( np0[0].n_value == -1 )
	{
		fprintf( stderr, "sps - Can't read symbol file %s", filesymbol);
		sysperror() ;
	}
	for ( s = Symbollist, np = np0 ; s->s_kname ; s++, np++ )
	{					
		if ( !np->n_value )		
		{
			fprintf( stderr, "sps - Can't find symbol %s in %s",
				np->n_name, filesymbol ) ;
			/* Assume this error to be unimportant if the address
			   is only associated with a process wait state.
			   This may happen if the system has been configured
			   without a particular device. */
			fprintf( stderr, &Info.i_waitstate[ 0 ] <= s->s_info
				&& s->s_info < &Info.i_waitstate[ NWAITSTATE ]
				? " (error is not serious)\n"
				: " (ERROR MAY BE SERIOUS)\n" ) ;
			*s->s_info = (caddr_t)0 ;
			continue ;
		}
		/* If no indirection is required, just copy the obtained value
		   into the `Info' structure. */
		if ( !s->s_indirect )		
		{				
		/* DUBIOUS ASSUMPTION THAT KMEM VALUE HAS SIZE OF A CADDR_T */
			*s->s_info = (caddr_t)np->n_value ;
			continue ;		
		}				
		/* Otherwise one level of indirection is required. Using the
		   obtained address, look again in the kernel for the value */
		memseek( Flkmem, (long)np->n_value ) ;
		/* DUBIOUS ASSUMPTION THAT KMEM VALUE HAS SIZE OF A CADDR_T */
		(void)read( Flkmem, (char*)s->s_info, sizeof(caddr_t) ) ;
	}
	free( (char*)np0 ) ;
}
---END-OF-initsymbols.c---
echo Extracting inittty.c
cat > inittty.c << '---END-OF-inittty.c---'
# include	"sps.h"
# include	<h/conf.h>
# include	<h/tty.h>
# include	<sys/stat.h>
# include	<stdio.h>

/* INITTTY - Initialise the tty part of the info structure */
inittty ()
{
	register struct ttyline	*lp ;
# ifdef BSD42
	register struct direct	*dp ;
	DIR			*dfd ;
# else
	struct direct		dir ;
	FILE			*dfd ;
# endif
	struct stat		statbuf ;
	static char		filedev[] = FILE_DEV ;
	extern struct info	Info ;
	extern int		Flkmem ;
# ifdef BSD42
	DIR			*opendir() ;
	struct direct		*readdir() ;
# else
	FILE			*fopen() ;
# endif

	lp = Info.i_ttyline ;
# ifdef BSD42
	if ( !(dfd = opendir( filedev )) )
# else
	if ( !(dfd = fopen( filedev, "r" )) )
# endif
		prexit( "Can't open %s\n", filedev ) ;
	if ( chdir( filedev ) < 0 )
		prexit( "sps - Can't chdir to %s\n", filedev ) ;
# ifdef BSD42
	/* Read all entries in the device directory, looking for ttys */
	while ( dp = readdir( dfd ) )
	{	/* Skip entries that do not match "tty" or "console" */
		if ( strncmp( "tty", dp->d_name, 3 )
		&&   strcmp( "console", dp->d_name ) )
			continue ;
		/* Skip "tty" itself */
		if ( dp->d_namlen == 3 )
			continue ;
# ifdef CHAOS
		/* Skip chaos ttys ; they are accessed during ttystatus() */
		if ( dp->d_namelen > 3 &&
		dp->d_name[ sizeof( "tty" ) - 1 ] == 'C' )
			continue ;
# endif
		if ( lp >= &Info.i_ttyline[ MAXTTYS ] )
			prexit( "sps - Too many ttys in %s\n", filedev ) ;
		/* Copy the tty name into the information entry */
		if ( !strcmp( dp->d_name, "console" ) )
		{
			lp->l_name[0] = 'c' ;
			lp->l_name[1] = 'o' ;
		}
		else
		{
			lp->l_name[0] = dp->d_name[3] ;
			lp->l_name[1] = dp->d_name[4] ;
		}
		/* Ensure that this tty is actually a valid character device */
		if ( stat( dp->d_name, &statbuf ) < 0 )
			continue ;
# else
	/* Read all entries in the device directory, looking for ttys */
	while ( fread( (char*)&dir, sizeof( struct direct ), 1, dfd ) == 1 )
	{	/* Skip entries that do not match "tty" or "console" */
		if ( strncmp( "tty", dir.d_name, 3 )
		&&   strcmp( "console", dir.d_name ) )
			continue ;
		/* Skip "tty" itself */
		if ( dir.d_name[3] == '\0' )
			continue ;
# ifdef CHAOS
		/* Skip chaos ttys ; they are accessed during ttystatus() */
		if ( dir.d_name[ sizeof( "tty" ) - 1 ] == 'C' )
			continue ;
# endif
		if ( lp >= &Info.i_ttyline[ MAXTTYS ] )
			prexit( "sps - Too many ttys in %s\n", filedev ) ;
		/* Copy the tty name into the information entry */
		if ( !strcmp( dir.d_name, "console" ) )
		{
			lp->l_name[0] = 'c' ;
			lp->l_name[1] = 'o' ;
		}
		else
		{
			lp->l_name[0] = dir.d_name[3] ;
			lp->l_name[1] = dir.d_name[4] ;
		}
		/* Ensure that this tty is actually a valid character device */
		if ( stat( dir.d_name, &statbuf ) < 0 )
			continue ;
# endif
		if ( (statbuf.st_mode & S_IFMT) != S_IFCHR )
			continue ;
		/* Find the device # of the tty and the address of its
		   associated struct tty in /dev/kmem. */
		lp->l_dev = statbuf.st_rdev ;
		memseek( Flkmem,
		     (long)&Info.i_cdevsw[ major( statbuf.st_rdev ) ].d_ttys ) ;
		if ( read( Flkmem, (char*)&lp->l_addr, sizeof( lp->l_addr ) )
		!= sizeof( lp->l_addr ) )
		{
			fprintf( stderr, "sps - Can't read struct tty for %s\n",
# ifdef BSD42
				dp->d_name ) ;
# else
				dir.d_name ) ;
# endif
			continue ;
		}
		lp->l_addr += (int)minor( statbuf.st_rdev ) ;
		lp++ ;
	}
# ifdef BSD42
	(void)closedir( dfd ) ;
# else
	(void)fclose( dfd ) ;
# endif
}
---END-OF-inittty.c---
echo Extracting main.c
cat > main.c << '---END-OF-main.c---'
# include	"sps.h"
# include	"flags.h"
# include	<h/text.h>
# include	<stdio.h>

/* SPS - Show Process Status */
/* J. R. Ward - Hasler AG Bern - 24 May 1985 */
main ( argc,argv )

int				argc ;
char				**argv ;

{
	register struct process	*plist ;
	register struct process	*process ;
	register struct text	*text ;
	int			flinfo ;
	extern struct flags	Flg ;
	extern struct info	Info ;
	extern int		Flmem ;
	extern int		Flkmem ;
	extern int		Flswap ;
	char			*getcore() ;
	struct process		*needed(), *mktree() ;

# ifndef TRACE
	(void)nice( -40 ) ;
	(void)setuid( getuid() ) ;
# endif
	/* Decode the flag arguments */
	flagdecode( argc, argv ) ;	
	/* Determine the terminal width */
	if ( !Flg.flg_w && !Flg.flg_N && !Flg.flg_i )
		termwidth() ;
	/* Open the cpu physical memory, kernel virtual memory and swap device*/
	if ( Flg.flg_k )
	{
		Flmem = openfile( Flg.flg_k ) ;
		Flkmem = Flmem ;
	}
	else
	{
		Flmem = openfile( FILE_MEM ) ;
		Flkmem = openfile( FILE_KMEM ) ;
		if ( !Flg.flg_o )
			Flswap = openfile( FILE_SWAP ) ;
	}
	if ( Flg.flg_i )
	{	/* -i flag for info file initialisation */
		initialise() ;		
		exit( 0 ) ;
	}
	/* Read the information file */
	flinfo = openfile( Flg.flg_j ? Flg.flg_j : FILE_INFO ) ;
	if ( read( flinfo, (char*)&Info, sizeof( struct info ) )
	!= sizeof( struct info ) )
	{
		fprintf( stderr, "sps - Can't read info file %s",
			Flg.flg_j ? Flg.flg_j : FILE_INFO ) ;
		sysperror() ;
	}
	(void)close( flinfo ) ;
	/* Find current tty status */
	ttystatus() ;			
	/* Now that we know the available ttys, decode the flags */
	flagsetup() ;			
	process = (struct process*)getcore(Info.i_nproc*sizeof(struct process));
	text = (struct text*)getcore( Info.i_ntext * sizeof( struct text ) ) ;
	do
	{	/* Read current process and text status */
		readstatus( process, text ) ;
		/* Select those processes to be listed */
		plist = needed( process, text ) ;
		/* Form a tree of listed processes */
		plist = mktree( process, plist ) ;
		if ( !Flg.flg_N )
		{	/* Print the processes */
			prheader() ;
			printall( plist, 0 ) ;
		}
		prsummary() ;
		(void)fflush( stdout ) ;
		if ( Flg.flg_r )	
		{	/* If repeating, again get tty status */
			ttystatus() ;
			if ( Flg.flg_rdelay )
# ifdef BSD42
				sleep( Flg.flg_rdelay ) ;
# else
				sleep( (int)Flg.flg_rdelay ) ;
# endif
		}
	} while ( Flg.flg_r ) ;
	exit( 0 ) ;
}
---END-OF-main.c---
echo Extracting mktree.c
cat > mktree.c << '---END-OF-mktree.c---'
# include	"sps.h"

/*
** MKTREE - Sort the needed processes by subtree and at the top by user.
** This procedure takes a list of processes (as returned by needed())
** and returnes a pointer to a sorted list.
*/
struct process	*mktree ( process, plist )

struct process			*process ;
struct process			*plist ;

{
	register struct process	*p ;
	register struct process	*pp ;
	register struct process	*lp ;
	struct process		*op ;
	struct process		proot ;

	proot.pr_sibling = (struct process*)0 ;
	for ( p = plist ; p ; p = p->pr_plink )
	{
		if ( p->pr_pptr > &process[1] )
		{
			for ( pp = plist ; pp ; pp = pp->pr_plink )
			{
				if ( pp != p->pr_pptr )
					continue ;
				if ( lp = pp->pr_child )
				{	/* Does process have children ? */
					op = (struct process*)0 ;
					while (lp &&
					lp->pr_p.p_pid < p->pr_p.p_pid )
					{
						op = lp ;
						lp=lp->pr_sibling ;
					}
					if ( op )
					{
						p->pr_sibling = lp ;
						op->pr_sibling = p ;
						break ;
					}
				}	
				p->pr_sibling = lp ;
				pp->pr_child = p ;
				break ;
			}
			if ( pp )
				continue ;
		}
		/* We have a top level process, sort into top level list.
		   The top level is sorted firstly by user-id and then
		   by process-id. */
		lp = &proot ;
		pp = lp->pr_sibling ;
		while ( pp )
		{
			if ( p->pr_p.p_uid < pp->pr_p.p_uid )
				break ;
			if ( p->pr_p.p_uid == pp->pr_p.p_uid
			&& p->pr_p.p_pid < pp->pr_p.p_pid )
				break ;
			lp = pp, pp = pp->pr_sibling ;
		}
		p->pr_sibling = lp->pr_sibling ;
		lp->pr_sibling = p ;
	}
	return ( proot.pr_sibling ) ;
}
---END-OF-mktree.c---
echo Extracting needed.c
cat > needed.c << '---END-OF-needed.c---'
# include	"sps.h"
# include	"flags.h"
# include	<h/text.h>
# include	<stdio.h>

/*
** NEEDED - Determine which processes are needed for the printout
** and add these to a list of needed processes.
*/
struct process	*needed ( process, text )

register struct process		*process ;
struct text			*text ;

{
	register struct process	*p ;
	register struct process	*plist ;
	struct process		*lastp ;
	int			uid ;
	extern struct flags	Flg ;
	extern union userstate	User ;
	extern struct info	Info ;
	extern struct ttyline	Notty ;
	struct ttyline		*findtty() ;
	char			*getcmd() ;

	plist = (struct process*)0 ;
	lastp = &process[ Info.i_nproc ] ;
	/* Normalise internal pointers from kernel addresses. For each kmem
	   address in the `proc' and `text' structures, we convert that
	   address for our own internal use. */
	for ( p = process ; p < lastp ; p++ )
	{				
		if ( !p->pr_p.p_stat )	
			continue ;
		/* Normalise internal text pointers */
		if ( p->pr_p.p_textp )
			p->pr_p.p_textp = &text[p->pr_p.p_textp - Info.i_text0];
		/* Normalise internal linked list of processes */
		p->pr_plink = p->pr_p.p_link ?
			&process[ p->pr_p.p_link  - Info.i_proc0 ] :
			(struct process*)0 ;
		/* Normalise internal parent pointers */
		p->pr_pptr = p->pr_p.p_pptr ?
			&process[ p->pr_p.p_pptr - Info.i_proc0 ] :
			(struct process*)0 ;
		/* Check for valid parent pointers */
		if ( !p->pr_pptr )
		{
			p->pr_pptr = process ;
			continue ;
		}
		if ( p->pr_pptr < process || p->pr_pptr >= lastp )
		{
			fprintf( stderr, "sps - process %d has bad pptr\n",
				p->pr_p.p_pid ) ;
			p->pr_pptr = process ;
		}
	}
	/* For each process, see if it is a candidate for selection.
	   If so, retrieve its command arguments and upage information. */
	uid = getuid() ;
	for ( p = process ; p < lastp ; p++ )
	{				
		if ( !p->pr_p.p_stat )
			continue ;
		/* Count processes and sizes */
		summarise( p ) ;
		/* Select the given processes. Bear in mind that selection
		   of processes based on the `F' and `T' flags must be
		   postponed until the upage is accessed. */
		if ( !Flg.flg_F && !Flg.flg_T && !selectproc( p, process, uid ))
			continue ;
		/* Try to find the process' command arguments. Accessing the
		   arguments also involves retrieving the upage. */
		p->pr_cmd = getcmd( p ) ;
		/* If the upage was found successfully, use this information */
		if ( p->pr_upag )	
		{
# ifdef BSD42
			p->pr_rself = User.u_us.u_ru ;
			p->pr_rchild = User.u_us.u_cru ;
# else
			p->pr_vself = User.u_us.u_vm ;
			p->pr_vchild = User.u_us.u_cvm ;
# endif
			p->pr_tty = findtty( p ) ;
			p->pr_files = filecount() ;
		}
		else
			p->pr_tty = &Notty ;
		/* Select on the basis of the `F' and `T' flags */
		if ( Flg.flg_F		
		&& !(p->pr_p.p_pgrp && p->pr_p.p_pgrp == p->pr_tty->l_pgrp) )
			continue ;
		if ( Flg.flg_T && !selecttty( p ) )
			continue ;
		/* Arrive here with a selected process. Add this to the
		   linked list of needed processes. */
		p->pr_plink = plist ;	
		plist = p ;
		p->pr_child = (struct process*)0 ;
		p->pr_sibling = (struct process*)0 ;
	}
	return ( plist ) ;
}

/* SUMMARISE - Summarises the given process into the `Summary' structure */
/*
** SHOULD ACCOUNT HERE FOR THE SIZE OF LOADED PAGE TABLES, BUT WE DON'T REALLY
** KNOW THEIR RESIDENT SIZES.
*/
summarise ( p )

register struct process		*p ;

{
	register struct text	*tp ;
	int			busy ;
	extern struct summary	Summary ;

	Summary.sm_ntotal++ ;
	if ( p->pr_p.p_stat == SZOMB )
		return ;
	/* Firstly, account for processes */
	Summary.sm_ktotal += p->pr_p.p_dsize + p->pr_p.p_ssize ;
	Summary.sm_kloaded += p->pr_p.p_rssize ;
	Summary.sm_kswapped += p->pr_p.p_swrss ;
	if ( p->pr_p.p_flag & SLOAD )
		Summary.sm_nloaded++ ;
	else
		Summary.sm_nswapped++ ;
	busy = (p->pr_p.p_stat == SRUN) || (p->pr_p.p_stat==SSLEEP
	     && (p->pr_p.p_pri<PZERO && p->pr_p.p_pid > MSPID) ) ;
	if ( busy )
	{
		Summary.sm_nbusy++ ;
		Summary.sm_kbusy += p->pr_p.p_dsize + p->pr_p.p_ssize ;
	}
	/* Now account for their texts */
	if ( !(tp = p->pr_p.p_textp) || !tp->x_count )
		return ;		
	Summary.sm_ktotal += tp->x_size ;
	Summary.sm_kloaded += tp->x_rssize ;
	Summary.sm_kswapped += tp->x_swrss ;
	if ( busy )
		Summary.sm_kbusy += tp->x_size ;
	tp->x_count = 0 ;
}
---END-OF-needed.c---
echo Extracting openfiles.c
cat > openfiles.c << '---END-OF-openfiles.c---'
# include	<stdio.h>
# include	"sps.h"
# include	"flags.h"

/* Miscellaneous procedures */

/* OPENFILE - Opens the named file */
openfile ( name )

char				*name ;

{
	register int		fd ;

	if ( (fd = open( name, 0 )) >= 0 )
		return ( fd ) ;
	fprintf( stderr, "sps - Can't open %s", name ) ;
	sysperror() ;
	/* NOTREACHED */
}

/* MEMSEEK - Seek on a special file */
memseek ( fd, pos )

int				fd ;
long				pos ;

{
	extern int		errno ;
	extern struct flags	Flg ;
	long			lseek() ;

	errno = 0 ;
	if ( Flg.flg_k )
		pos &= 0x7fffffff ;
	(void)lseek( fd, pos, 0 ) ;
	if ( errno )
	{
		fprintf( stderr, "sps - Seek failed" ) ;
		sysperror() ;
	}
}

/* SWSEEK - Seek on the swap device */
swseek ( pos )

long				pos ;

{
	extern int		Flswap ;
	extern int		errno ;
	long			lseek() ;

	errno = 0 ;
	(void)lseek( Flswap, pos, 0 ) ;
	if ( errno )
	{
		fprintf( stderr, "sps - Seek failed" ) ;
		sysperror() ;
	}
}

# ifdef	lint
int				errno ;
int				sys_nerr ;
char				*sys_errlist[] ;
# endif

/* SYSPERROR - Reports a system defined error msg and then exits gracefully */
sysperror ()
{
	extern int		errno ;
	extern int		sys_nerr ;
	extern char		*sys_errlist[] ;

	if ( 0 < errno && errno < sys_nerr )
		fprintf( stderr, " : %s", sys_errlist[errno] ) ;
	(void)fputc( '\n', stderr ) ;
	exit( 1 ) ;
}

/* STRSAVE - Store a string in core for later use. */
char	*strsave ( cp )

register char			*cp ;

{
	register char		*chp ;
	char			*getcore(), *strcpy() ;

	chp = getcore( strlen( cp ) + 1 ) ;
	(void)strcpy( chp, cp ) ;
	return ( chp ) ;
}

/* GETCORE - Allocate and return a pointer to the asked for amount of core */
char	*getcore ( size )

register int		 	size ;

{
	register char		*chp ;
	char			*malloc() ;

	if ( chp = malloc( (unsigned)size ) )
		return ( chp ) ;
	fprintf( stderr, "sps - Out of core" ) ;
	sysperror() ;
	/* NOTREACHED */
}

union flaglist	*getflgsp ( argc )

register int		 	argc ;

{
	char			*getcore() ;

	return ( (union flaglist*)getcore( sizeof( union flaglist )*argc ) ) ;
}

/* PREXIT - Print an error message and exit */
/* VARARGS1 */
/* ARGSUSED */
prexit ( fmt, args )

char				*fmt ;

{
	_doprnt( fmt, &args, stderr ) ;
	exit( 1 ) ;
}
---END-OF-openfiles.c---
echo Extracting percentmem.c
cat > percentmem.c << '---END-OF-percentmem.c---'
# include	"sps.h"
# include	<h/text.h>
# ifdef BSD42
# include	<vax/pte.h>
# else
# include	<h/pte.h>
# include	<h/vmparam.h>
# endif
# include	<h/vmmac.h>

/* PERCENTMEM - Returns the percentage of real memory used by this process */
double	percentmem ( p )

register struct process		*p ;

{
	register struct text	*tp ;
	int			szptudot ;
	double			fracmem ;
	extern struct info	Info ;

	tp = p->pr_p.p_textp ;
	if ( !(p->pr_p.p_flag & SLOAD) || !tp )
		return ( 0.0 ) ;
	szptudot = UPAGES + clrnd( ctopt( p->pr_p.p_dsize + p->pr_p.p_ssize ) );
	fracmem = ( (double)p->pr_p.p_rssize + szptudot ) / CLSIZE ;
	if ( tp->x_ccount )
		fracmem += ((double)tp->x_rssize)/CLSIZE/tp->x_ccount ;
	return ( 100.0 * fracmem / (double)Info.i_ecmx ) ;
}
---END-OF-percentmem.c---
echo Extracting prcmd.c
cat > prcmd.c << '---END-OF-prcmd.c---'
# include	"sps.h"
# include	"flags.h"

/* PRCMD - Prints the command arguments according to the switches */
prcmd ( p, lpad, width )

register struct process		*p ;
int				lpad ;
int				width ;

{
	extern struct flags	Flg ;
	extern unsigned		Termwidth ;

	printf( "%*d ", lpad, p->pr_p.p_pid ) ;
	if ( Flg.flg_f )
	{
		printf( "%5d ", p->pr_p.p_ppid ) ;
		width -= 6 ;
	}
	if ( Flg.flg_g )
	{
		printf( "%5d ", p->pr_p.p_pgrp ) ;
		width -= 6 ;
	}
	width += Termwidth ;
	if ( Flg.flg_w )
		printf( "%s\n", p->pr_cmd ) ;
	else if ( width > 0 )
		printf( "%-.*s\n", width, p->pr_cmd ) ;
	if ( p->pr_csaved )
		free( p->pr_cmd ) ;
}
---END-OF-prcmd.c---
echo Extracting prcpu.c
cat > prcpu.c << '---END-OF-prcpu.c---'
# include	"sps.h"

# ifdef BSD42

/* PRCPU - Print cpu time */
prcpu ( time, utime )

register time_t			time ;
time_t				utime ;

{
	time += utime / 1000000 ;
	utime %= 1000000 ;
	if ( time < 0L || time > 1000L*60L*60L )
	{	/* Assume more than 1000 hours is in error */
		printf( "     " ) ;	
		return ;
	}
	if ( time < 60L*10L )
	{	/* Less than 10 minutes */
		printf( "%3d.%1d", (int)time, (int)utime/100000 ) ;
		return ;
	}
	/* If less than 10 hours, print as minutes */
	if ( time < 60L*60L*10L )		
		printf( "%3D M", time/60L ) ;
	else
		printf( "%3D H", time/60L/60L ) ;
}

# else

/* PRCPU - Print cpu time */
prcpu ( time )

register time_t			time ;

{
	extern struct info	Info ;

	if ( time < 0L || time > Info.i_hz*1000L*60L*60L )
	{	/* Assume more than 1000 hours is in error */
		printf( "     " ) ;	
		return ;
	}
	if ( time < Info.i_hz*60L*10L )
	{	/* Less than 10 minutes */
		printf( "%3D.%1D", time/Info.i_hz,
			(time % Info.i_hz / (Info.i_hz/10L)) ) ;
		return ;
	}
	/* If less than 10 hours, print as minutes */
	time /= Info.i_hz ;
	if ( time < 60L*60L*10L )		
		printf( "%3D M", time/60L ) ;
	else
		printf( "%3D H", time/60L/60L ) ;
}

# endif
---END-OF-prcpu.c---
echo Extracting prheader.c
cat > prheader.c << '---END-OF-prheader.c---'
# include	"sps.h"
# include	"flags.h"

/* PRHEADER - Print a header according to the switches */
prheader ()
{
	extern struct flags	Flg ;

	printf( "Ty User    %s Proc#", Flg.flg_v ?
		" Status Fl Nice Virtual Resident %M  Time Child %C" :
		Flg.flg_d ?
		"  Files    PageFaults Swap BlockI/O Kbytsecs" : "" ) ;
	if ( Flg.flg_f )
		printf( " Ppid#" ) ;
	if ( Flg.flg_g )
		printf( " Pgrp#" ) ;
	printf( " Command\n" ) ;
}
---END-OF-prheader.c---
echo Extracting printall.c
cat > printall.c << '---END-OF-printall.c---'
# include	<stdio.h>
# include	"sps.h"

/* PRINTALL - Recursively print the process tree. */
printall ( p, md )

register struct process		*p ;
register int			md ;

{
	while ( p )
	{	/* Print this process */
		printproc( p, md ) ;	
		(void)fflush( stdout ) ;
		/* Print child processes */
		printall( p->pr_child, md+1 ) ;
		/* Print brother processes */
		p = p->pr_sibling ;	
	}
}
---END-OF-printall.c---
echo Extracting printproc.c
cat > printproc.c << '---END-OF-printproc.c---'
# include	"sps.h"
# include	"flags.h"
# include	<h/text.h>

/* PRINTPROC - Pretty print a process according to the switches. */
printproc ( p, md )

register struct process		*p ;		
int				md ;		

{
	register char		*chp ;
	register struct text	*tp ;
	register struct hashtab	*hp ;
	char			chbuf[10] ;
	time_t			time ;
	time_t			chtime ;
# ifdef BSD42
	time_t			utime ;
	time_t			uchtime ;
# endif
	extern short		Lastuid, Lastpgrp ;
	extern struct flags	Flg ;
	char			*waitingfor() ;
	struct hashtab		*hashuid() ;
	double			percentmem() ;

	/* List tty name and foreground/background/detached information */
	printf( "%2.2s%c", p->pr_tty->l_name,
		!p->pr_p.p_pgrp ? ' ' :
# ifdef SDETACH
		p->pr_p.p_flag & SDETACH ? '_' :
# endif
		p->pr_p.p_pgrp == p->pr_tty->l_pgrp ? '.' : ' ' ) ;
	hp = hashuid( p->pr_p.p_uid ) ;
	if ( !md  )				
	{	/* If a top-level process, list the user name */
		if ( hp )
			printf( "%-8.8s ", hp->h_uname ) ;
		else
			printf( "user%-4.4d ", p->pr_p.p_uid ) ;
	}
	else					
	{	/* Usually list an asterisk for a child process */
		md = md > 8 ? 8 : md ;
		printf( "%*s%c", md, "",
			p->pr_p.p_pgrp == Lastpgrp ? '|' : '*' ) ;	
		/* But beware of setuid processes */
		md = 8 - md ;
		if ( p->pr_p.p_uid == Lastuid )
			printf( "%-*.*s", md, md, "" ) ;
		else if ( hp )
			printf( "%-*.*s", md, md, hp->h_uname ) ;
		else
		{
			md -= 4 ;
			printf( "user%-*.*d", md, md, p->pr_p.p_uid ) ;
		}
	}
	Lastuid = p->pr_p.p_uid ;
	Lastpgrp = p->pr_p.p_pgrp ;
	if ( Flg.flg_d )			
	{	/* List disc I/O and paging information */
		if ( !p->pr_upag || p->pr_p.p_stat == SZOMB )
		{
			prcmd( p, 49, -63 ) ;
			return ;
		}
		printf( "%2d %8d+%8d %4d %8d %8D ",
			p->pr_files,
# ifdef BSD42
			p->pr_rself.ru_majflt,
			p->pr_rself.ru_minflt,
			p->pr_rself.ru_nswap,
			p->pr_rself.ru_inblock + p->pr_rself.ru_oublock,
			KBYTES( p->pr_rself.ru_idrss + p->pr_rself.ru_isrss
				+ p->pr_rself.ru_ixrss ) ) ;
# else
			p->pr_vself.vm_majflt,
			p->pr_vself.vm_minflt,
			p->pr_vself.vm_nswap,
			p->pr_vself.vm_inblk + p->pr_vself.vm_oublk,
			KBYTES( (p->pr_vself.vm_idsrss
				+ p->pr_vself.vm_ixrss) / Info.i_hz ) ) ;
# endif
		prcmd( p, 5, -63 ) ;
		return ;
	}
	if ( !Flg.flg_v )			
	{	/* Not verbose so just list command arguments */
		prcmd( p, 5, -19 ) ;
		return ;
	}
	/* Arrive here if being verbose ; list cpu information */
	switch ( p->pr_p.p_stat )		
	{					
		case SSLEEP :
		case SWAIT :
		case SIDL :
			/* Determine why a process should be in a wait state */
			chp = waitingfor( p ) ;
			break ;
		case SRUN :
			chp = "run" ;
			break ;
		case SZOMB :
			chp = "exit" ;
			break ;
		case SSTOP :
			chp = "stop" ;
			break ;
	}
	/* If the process is loaded, list the status information in capitals */
	printf( "%-6.6s ", p->pr_p.p_flag & SLOAD ?
		(capitals( chp, chbuf ), chbuf) : chp ) ;
	/* List process flags */
	printf( "%c%c%c", p->pr_p.p_flag & SSYS ? 'U' :
		p->pr_p.p_flag & STRC ? 'T' : ' ',
		p->pr_p.p_flag & SVFORK ? 'V' :
		p->pr_p.p_flag & SPHYSIO ? 'I' : ' ',
		p->pr_p.p_flag & SUANOM ? 'A' : ' ' ) ;
	/* List process niceness */
	if ( p->pr_p.p_nice != NZERO )		
		printf( "%3d ", p->pr_p.p_nice - NZERO ) ;
	else
		printf( "    " ) ;
	if ( p->pr_p.p_stat == SZOMB )
	{
		prcmd( p, 41, -69 ) ;
		return ;
	}					
	/* List process and text virtual sizes */
	printf( "%4d", KBYTES( p->pr_p.p_dsize + p->pr_p.p_ssize ) ) ;
	if ( tp = p->pr_p.p_textp )
		printf( "+%3d ", KBYTES( tp->x_size ) ) ;
	else
		printf( "     " ) ;
	/* List process and text real sizes */
	printf( "%4d", KBYTES( p->pr_p.p_rssize ) ) ;
	if ( tp )
		printf( "+%3d %2.0f ", KBYTES( tp->x_rssize ),
			percentmem( p ) );
	else
		printf( "        " ) ;
	/* List information obtained from the upage. This includes the process
	   times and command arguments. */
	if ( !p->pr_upag )
	{
		prcmd( p, 20, -69 ) ;
		return ;
	}					
	/* List process time information */
# ifdef BSD42
	time   = Flg.flg_q ? p->pr_rself.ru_utime.tv_sec :
		 p->pr_rself.ru_utime.tv_sec + p->pr_rself.ru_stime.tv_sec ;
	utime  = Flg.flg_q ? p->pr_rself.ru_utime.tv_usec :
		 p->pr_rself.ru_utime.tv_usec + p->pr_rself.ru_stime.tv_usec ;
	chtime = Flg.flg_q ? p->pr_rchild.ru_utime.tv_sec :
		 p->pr_rchild.ru_utime.tv_sec + p->pr_rchild.ru_stime.tv_sec ;
	uchtime = Flg.flg_q ? p->pr_rchild.ru_utime.tv_usec :
		 p->pr_rchild.ru_utime.tv_usec + p->pr_rchild.ru_stime.tv_usec ;
	prcpu( time, utime ) ;
	if ( chtime != 0L )
	{
		printf( "+" ) ;
		prcpu( chtime, uchtime ) ;
	}
# else
	time   = Flg.flg_q ? p->pr_vself.vm_utime :
		 p->pr_vself.vm_utime + p->pr_vself.vm_stime ;
	chtime = Flg.flg_q ? p->pr_vchild.vm_utime :
		 p->pr_vchild.vm_utime + p->pr_vchild.vm_stime ;
	prcpu( time ) ;
	if ( chtime != 0L )
	{
		printf( "+" ) ;
		prcpu( chtime ) ;
	}
# endif
	else
		printf( "      " ) ;
# ifdef BSD42
	if ( time || utime )
# else
	if ( time )
# endif
		printf( " %2.0f ", p->pr_p.p_pctcpu * 100.0 ) ;
	else
		printf( "    " ) ;
	/* Finally, list the process command arguments. */
	prcmd( p, 5, -69 ) ;			
}

/* CAPITALS - Converts letters in `chp' to upper-case in buffer `buf'. */
capitals ( chp, buf )

register char			*chp ;
register char			*buf ;

{
	while ( *buf = *chp++ )
	{
		if ( 'a' <= *buf && *buf <= 'z' )
			*buf -= 'a' - 'A' ;
		buf++ ;
	}
}
---END-OF-printproc.c---
echo Extracting prsummary.c
cat > prsummary.c << '---END-OF-prsummary.c---'
# include	"sps.h"

/* PRSUMMARY - Print the summarising information */
prsummary ()
{
	extern struct summary	Summary ;

	printf(
"%D (%Dk) processes, %D (%Dk) busy, %D (%Dk) loaded, %D (%Dk) swapped\n",
		Summary.sm_ntotal, KBYTES( Summary.sm_ktotal ),
		Summary.sm_nbusy, KBYTES( Summary.sm_kbusy ),
		Summary.sm_nloaded, KBYTES( Summary.sm_kloaded ),
		Summary.sm_nswapped, KBYTES( Summary.sm_kswapped ) ) ;
	Summary.sm_ntotal = 0L ;
	Summary.sm_ktotal = 0L ;
	Summary.sm_nbusy = 0L ;
	Summary.sm_kbusy = 0L ;
	Summary.sm_nloaded = 0L ;
	Summary.sm_kloaded = 0L ;
	Summary.sm_nswapped = 0L ;
	Summary.sm_kswapped = 0L ;
}
---END-OF-prsummary.c---
echo Extracting readstatus.c
cat > readstatus.c << '---END-OF-readstatus.c---'
# include	"sps.h"
# include	<h/text.h>

/* READSTATUS - Reads the kernel memory for current processes and texts */
readstatus ( process, text )

register struct process		*process ;
struct text			*text ;

{
	register struct proc	*p ;
	register struct proc	*p0 ;
	register struct process	*pr ;
	extern struct info	Info ;
	extern int		Flkmem ;
	char			*getcore() ;

	/* Read current text information */
	memseek( Flkmem, (long)Info.i_text0 ) ;
	if ( read( Flkmem, (char*)text, Info.i_ntext * sizeof( struct text ) )
	!= Info.i_ntext * sizeof( struct text ) )
		prexit( "sps - Can't read system text table\n" ) ;
	/* Read current process information */
	p0 = (struct proc*)getcore( sizeof( struct proc )*Info.i_nproc ) ;
	memseek( Flkmem, (long)Info.i_proc0 ) ;
	if ( read( Flkmem, (char*)p0, Info.i_nproc * sizeof( struct proc ) )
	!= Info.i_nproc * sizeof( struct proc ) )
		prexit( "sps - Can't read system process table\n" ) ;
	/* Copy process information into our own array */
	for ( p = p0, pr = process ; pr < &process[ Info.i_nproc ] ; p++, pr++ )
		pr->pr_p = *p ;
	free( (char*)p0 ) ;
}
---END-OF-readstatus.c---
echo Extracting selectproc.c
cat > selectproc.c << '---END-OF-selectproc.c---'
# include	"sps.h"
# include	"flags.h"

/*
** SELECTPROC - Given a process structure, this procedure decides whether
** the process is a candidate for printing.
*/
selectproc ( p, process, thisuid )

register struct process		*p ;		
register struct process		*process ;	
int				thisuid ;	

{
	register union flaglist	*fp ;
	register struct process	*pp ;
	extern struct flags	Flg ;

	/* Flg.flg_AZ is an internal flag set if one of flags `A' to `Z'
	   was specified. If this is not set, a process is listed only
	   if it or one of its ancestors belongs to the invoking user. */
	if ( !Flg.flg_AZ )
		for ( pp = p ; pp > &process[1] ; pp = pp->pr_pptr )
			if ( thisuid == pp->pr_p.p_uid )
				return ( 1 ) ;
	if ( Flg.flg_A )
		return ( 1 ) ;
	if ( Flg.flg_P )
		for ( fp = Flg.flg_Plist ; fp->f_pid >= 0 ; fp++ )
			if ( fp->f_pid == p->pr_p.p_pid )
				return ( 1 ) ;
	if ( Flg.flg_U )
		for ( pp = p ; pp > &process[1] ; pp = pp->pr_pptr )
			for ( fp = Flg.flg_Ulist ; fp->f_uid >= 0 ; fp++ )
				if ( fp->f_uid == pp->pr_p.p_uid )
					return ( 1 ) ;
	switch ( p->pr_p.p_stat )
	{
		case SRUN :
			if ( Flg.flg_B )
				return ( 1 ) ;
			break ;
		case SSLEEP :
			if ( Flg.flg_B
			&&   p->pr_p.p_pri < PZERO && p->pr_p.p_pid > MSPID )
				return ( 1 ) ;
		case SWAIT :
		case SIDL :
			if ( Flg.flg_W )
				return ( 1 ) ;
			break ;
		case SSTOP :
			if ( Flg.flg_S )
				return ( 1 ) ;
			break ;
		case SZOMB :
			if ( Flg.flg_Z )
				return ( 1 ) ;
			break ;
		default :
			break ;
	}
	return ( 0 ) ;
}
---END-OF-selectproc.c---
echo Extracting selecttty.c
cat > selecttty.c << '---END-OF-selecttty.c---'
# include	"sps.h"
# include	"flags.h"

/* SELECTTTY - Decides whether this process is interesting for its tty */
selecttty ( p )

register struct process		*p ;

{
	register union flaglist	*fp ;
	extern struct flags	Flg ;

	for ( fp = Flg.flg_Tlist ; fp->f_ttyline ; fp++ )
		if ( fp->f_ttyline == p->pr_tty )
			return ( 1 ) ;
	return ( 0 ) ;
}
---END-OF-selecttty.c---
echo Extracting sps.h
cat > sps.h << '---END-OF-sps.h---'
# include	<h/param.h>
# include	<h/dir.h>
# include	<h/user.h>
# include	<h/proc.h>

/*
** Maximum # of users to be considered. (Should probably be
** approximately double the # of users in /etc/passwd.)
*/
# define	MAXUSERID	100
/* Maximum # ttys to be considered ... */
# define	MAXTTYS		50
/* Maximum user name length ... */
# define	UNAMELEN	8
/* Maximum process-id not to be considered busy ... */
# define	MSPID		2
/* # of wait states defined in the `struct info' ... */
# define	NWAITSTATE	35

/* Convert clicks to kbytes ... */
# define	KBYTES( size )	((size) >> (10 - PGSHIFT))

/* Standard files to be examined ... */
# define	FILE_MEM	"/dev/mem"	/* System physical memory */
# define	FILE_KMEM	"/dev/kmem"	/* Kernel virtual memory */
# define	FILE_SWAP	"/dev/drum"	/* Swap/paging device */
# define	FILE_DEV	"/dev"		/* Directory of tty entries */
# define	FILE_SYMBOL	"/vmunix"	/* Symbol file for nlist() */
# define	FILE_INFO	"/etc/spsinfo"	/* Sps information file */

/* Structure to hold necessary information concerning a tty ... */
struct ttyline
{
	struct tty		*l_addr ;	/* Ptr to tty struct in kmem */
	unsigned short		l_pgrp ;	/* Tty process group */
	char			l_name[2] ;	/* Tty character name */
	dev_t			l_dev ;		/* Tty device # */
} ;

/* Structure holding a single hash table entry ... */
struct hashtab
{
	unsigned short		h_uid ;		/* Uid of user entry */
	char			h_uname[ UNAMELEN ] ; /* Corresponding name */
} ;

/*
** Format of the standard information file maintained by sps.
** This structure is filled in at initialisation time and then is read back
** in whenever sps is invoked.
** Note that the pointer variables in this structure refer to
** kernel virtual addresses, not addresses within sps.
** These variable are typed as such so that pointer arithmetic
** on the kernel addresses will work correctly.
*/
struct info
{	/* Kernel values determining process, tty and upage info ... */
	struct proc		*i_proc0 ;	/* address of process table */
	int			i_nproc ;	/* length of process table */
	struct text		*i_text0 ;	/* address of text table */
	int			i_ntext ;	/* length of text table */
	struct inode		*i_inode0 ;	/* address of inode table */
	int			i_ninode ;	/* length of inode table */
	struct buf		*i_swbuf0 ;	/* address of swap buffers */
	int			i_nswbuf ;	/* # swap buffers */
	struct buf		*i_buf0 ;	/* address of i/o buffers */
	int			i_nbuf ;	/* # i/o buffers */
	int			i_ecmx ;	/* max physical memory address*/
	struct pte		*i_usrptmap ;	/* page table map */
	struct pte		*i_usrpt ;	/* page table map */
	struct cdevsw		*i_cdevsw ;	/* device switch to find ttys */
# ifdef BSD42
	struct quota		*i_quota0 ;	/* disc quota structures */
	int			i_nquota ;	/* # quota structures */
	int			i_dmmin ;	/* The start of the disc map */
	int			i_dmmax ;	/* The end of the disc map */
	struct mbuf		*i_mbutl ;	/* Start of mbuf area */
# else
	int			i_hz ;		/* Clock rate */
# endif
# ifdef	CHAOS
	caddr_t			i_Chconntab ;	/* Chaos connection table */
# endif
	/* Kernel addresses are associated with process wait states ... */
	caddr_t			i_waitstate[ NWAITSTATE ] ;
	/* User names, stored in a hash table ... */
	struct hashtab		i_hnames[ MAXUSERID ] ;
	/* Tty device info ... */
	struct ttyline		i_ttyline[ MAXTTYS ] ;
} ;

/*
** The symbol structure cross-references values read from the kernel with
** their place in the info structure, and if such a value is associated with
** a process wait state or not.
*/
struct symbol
{
	char			*s_kname ;	/* Kernel symbol name */
	char			s_indirect ;	/* Value requires indirection */
	caddr_t			*s_info ;	/* Corresponding info address */
	char			*s_wait ;	/* Reason for wait, if any */
} ;

/* The `user' structure obtained from /dev/mem or /dev/swap ... */
union userstate
{
	struct user		u_us ;
	char			u_pg[ UPAGES ][ NBPG ] ;
} ;

/* Information concerning each process filled from /dev/kmem ... */
struct process
{
	struct proc		pr_p ;		/* struct proc from /dev/kmem */
	struct process		*pr_plink ;	/* Normalised ptrs from above */
	struct process		*pr_sibling ;	/* Ptr to sibling process */
	struct process		*pr_child ;	/* Ptr to child process */
	struct process		*pr_pptr ;	/* Ptr to parent process */
# ifdef BSD42
	struct rusage		pr_rself ;	/* Read from upage for self */
	struct rusage		pr_rchild ;	/* ... and the children */
# else
	struct vtimes		pr_vself ;	/* Read from upage for self */
	struct vtimes		pr_vchild ;	/* ... and the children */
# endif
	int			pr_files ;	/* # open files */
	struct ttyline		*pr_tty ;	/* Associated tty information */
	char			*pr_cmd ;	/* Command args, from upage */
	int			pr_upag:1 ;	/* Upage was obtained */
	int			pr_csaved:1 ;	/* Cmd args saved by malloc() */
} ;

/* Structure to hold summarising information ... */
struct summary
{
	long			sm_ntotal ;	/* Total # processes */
	long			sm_ktotal ;	/* Total virtual memory */
	long			sm_nbusy ;	/* # busy processes */
	long			sm_kbusy ;	/* Busy virtual memory */
	long			sm_nloaded ;	/* # loaded processes */
	long			sm_kloaded ;	/* Active resident memory */
	long			sm_nswapped ;	/* # swapped processes */
	long			sm_kswapped ;	/* Size totally swapped out */
} ;
---END-OF-sps.h---
echo Extracting termwidth.c
cat > termwidth.c << '---END-OF-termwidth.c---'
/*
** TERMWIDTH - Sets the external variable `Termwidth' to the # of columns
** on the terminal.
*/
termwidth ()
{
	register char		*termtype ;
	register int		twidth ;
	char			buf[ 1025 ] ;
	extern unsigned		Termwidth ;
	char			*getenv() ;

	Termwidth = 80 ;
	if ( !(termtype = getenv( "TERM" )) )
		return ;
	if ( tgetent( buf, termtype ) != 1 )
		return ;
	twidth = tgetnum( "co" ) ;
	if ( twidth > 40 )
		Termwidth = twidth ;
}
---END-OF-termwidth.c---
echo Extracting ttystatus.c
cat > ttystatus.c << '---END-OF-ttystatus.c---'
# include	"sps.h"
# include	"flags.h"
# include	<stdio.h>
# include	<h/tty.h>
# ifdef CHAOS
# include	<chunix/chsys.h>
# include	<chaos/chaos.h>
# endif

/*
** TTYSTATUS - Reads the kernel memory for tty structures of active processes.
** The addresses of the associated struct ttys of /dev/kmem are kept in the
** info structure. Here we use those addresses to access the structures.
** Actually, we are mostly interested just in the process group of each tty.
*/
ttystatus ()
{
	register struct ttyline	*lp ;
	struct tty		tty ;
	extern struct flags	Flg ;
	extern struct info	Info ;
	extern int		Flkmem ;

	if ( Flg.flg_y )
		printf( "Ty   Dev       Addr Rawq Canq Outq  Pgrp\n" ) ;
	lp = Info.i_ttyline ;
# ifdef CHAOS
	while ( lp->l_name[0] && lp->l_name[0] != 'C' )
# else
	while ( lp->l_name[0] )
# endif
	{
		memseek( Flkmem, (long)lp->l_addr ) ;
		if ( read( Flkmem, (char*)&tty, sizeof( struct tty ) )
		!= sizeof( struct tty ) )
		{
			fprintf( stderr,
				"sps - Can't read struct tty for tty%.2s\n",
				lp->l_name ) ;
			lp->l_pgrp = 0 ;
			lp++ ;
			continue ;
		}
		lp->l_pgrp = tty.t_pgrp ;
		prtty( lp, &tty ) ;
		lp++ ;
	}
# ifdef CHAOS
	chaosttys( lp ) ;		
# endif
}

/* PRTTY - Print out the tty structure */
prtty ( lp, tty )

register struct ttyline		*lp ;
register struct tty		*tty ;

{
	extern struct flags	Flg ;

	if ( !Flg.flg_y )
		return ;
	printf( "%-2.2s %2d,%2d 0x%08x %4d %4d %4d %5d\n",
		lp->l_name,
		major( lp->l_dev ),
		minor( lp->l_dev ),
		lp->l_addr,
		tty->t_rawq.c_cc,
		tty->t_canq.c_cc,
		tty->t_outq.c_cc,
		tty->t_pgrp ) ;
}

# ifdef CHAOS

/* CHAOSTTYS - Finds ttys attached to the Chaos net */
chaosttys ( lp )

register struct ttyline		*lp ;

{
	register struct connection	**cnp ;
	register int			i ;
	struct tty			tty ;
	struct connection		*conntab[CHNCONNS] ;
	struct connection		conn ;
	extern struct info		Info ;
	extern int			Flkmem ;

	memseek( Flkmem, (long)Info.i_Chconntab ) ;
	(void)read( Flkmem, (char*)conntab, sizeof( conntab ) ) ;
	for ( i = 0, cnp = conntab ; cnp < &conntab[CHNCONNS] ; i++, cnp++ )
	{
		if ( !*cnp )
			continue ;
		memseek( Flkmem, (long)*cnp ) ;
		(void)read( Flkmem, (char*)&conn, sizeof( struct connection ) );
		if ( !(conn.cn_flags & CHTTY) )
			continue ;
		memseek( Flkmem, (long)conn.cn_ttyp ) ;
		(void)read( Flkmem, (char*)&tty, sizeof( struct tty ) ) ;
		if ( lp >= &Info.i_ttyline[MAXTTYS] )
			prexit( "sps - Too many chaos ttys\n" ) ;
		lp->l_addr = conn.cn_ttyp ;
		lp->l_pgrp = tty.t_pgrp ;
		lp->l_dev = tty.t_dev ;
		lp->l_name[0] = 'C' ;
		lp->l_name[1] = i < 10 ? '0'+i : i-10 <= 'z'-'a' ? i-10+'a' :
				i-10-('z'-'a')+'A' ;
		prtty( lp, &tty ) ;
		lp++ ;
	}
}

# endif
---END-OF-ttystatus.c---
echo Extracting waitingfor.c
cat > waitingfor.c << '---END-OF-waitingfor.c---'
# include	"sps.h"
# include	<h/tty.h>
# include	<h/text.h>
# include	<h/inode.h>
# include	<h/buf.h>
# ifdef BSD42
# include	<h/quota.h>
# include	<h/mbuf.h>
# include	<h/socket.h>
# include	<h/socketvar.h>
# endif

/* 1 if `w' is in the address range defined by `a1' and `a2' ... */
# define	INRANGE( w, a1, a2 ) \
			( (caddr_t)(a1) <= (w) && (w) < (caddr_t)(a2) )

/* WAITINGFOR - Determine what a process is waiting for and describe it. */
char	*waitingfor ( p )

struct process			*p ;

{
	register caddr_t	w ;
	register struct ttyline	*lp ;
	register struct symbol	*s ;
	register char		*cp ;
# ifdef BSD42
	struct socket		sc ;
# endif
	static char		wbuf[ 8 ] ;
	extern struct info	Info ;
	extern struct symbol	Symbollist[] ;
	char			*sprintf() ;

	w = p->pr_p.p_wchan ;
	if ( !w )
		return ( "null" ) ;
	/* Waiting for a child process, alternatively in a vfork() ? */
	if ( INRANGE( w, Info.i_proc0, &Info.i_proc0[ Info.i_nproc ] ) )
		return ( p->pr_p.p_flag & SNOVM ? "vfork" : "child" ) ;
	/* Waiting for a page to be brought in ? */
	if ( INRANGE( w, Info.i_swbuf0, &Info.i_swbuf0[ Info.i_nswbuf ] ) )
		return ( "swap" ) ;
	/* Waiting for discio through a block device to complete ? */
	if ( INRANGE( w, Info.i_buf0, &Info.i_buf0[ Info.i_nbuf ] ) )
		/* SHOULD ACTUALLY READ AS "blkio" BUT "discio" IS WHAT
		   IS GENERALLY MEANT HERE. */
		return ( "discio" ) ;
	/* Waiting for a text page to be brought in ? */
	if ( INRANGE( w, Info.i_text0, &Info.i_text0[ Info.i_ntext ] ) )
		return ( "swtext" ) ;
# ifdef BSD42
	/* Waiting for an event associated with the quota system ? */
	if ( INRANGE( w, Info.i_quota0, &Info.i_quota0[ Info.i_nquota ] ) )
		return ( "quota" ) ;
# endif
	/* Waiting for tty I/O ? If so, find which tty it is */
	for ( lp = Info.i_ttyline ; lp->l_name[0] ; lp++ )
		if ( INRANGE( w, &lp->l_addr[0], &lp->l_addr[1] ) )
		{
			switch ( w - (int)lp->l_addr )
			{
				case (int)&((struct tty*)0)->t_rawq :
					/* Read from a tty or slave pty */
					cp = "rtty??" ;
					break ;
				case (int)&((struct tty*)0)->t_outq :
					/* Write to a tty or slave pty */
					cp = "wtty??" ;
					break ;
				case (int)&((struct tty*)0)->t_state :
					/* Tty not open */
					cp = "otty??" ;
					break ;
				case (int)&((struct tty*)0)->t_outq.c_cf :
					/* Read from a controller pty */
					cp = "rpty??" ;
					break ;
				case (int)&((struct tty*)0)->t_rawq.c_cf :
					/* Write to a controller pty */
					cp = "wpty??" ;
					break ;
				default :
					cp = "?tty??" ;
					break ;
			}
			cp[4] = lp->l_name[0] ;
			cp[5] = lp->l_name[1] ;
			return ( cp ) ;
		}
	/* Waiting for an inode ? */
	if ( INRANGE( w, Info.i_inode0, &Info.i_inode0[ Info.i_ninode ] ) )
		switch ( ((int)w - (int)Info.i_inode0) % sizeof( struct inode ))
		{
# ifdef BSD42
			case (int)&((struct inode*)0)->i_exlockc :
				/* Exclusive lock on this inode */
				return ( "exlock" ) ;
			case (int)&((struct inode*)0)->i_shlockc :
				/* Shared lock on this inode */
				return ( "shlock" ) ;
# else
			case 1 :
				return ( "wpipe" ) ;
			case 2 :
				return ( "rpipe" ) ;
			case (int)&((struct inode*)0)->i_un.i_group.g_datq :
				return ( "rmux" ) ;
# endif
			default :
				/* Inode probably locked */
				return ( "inode" ) ;
		}
# ifdef BSD42
	/* Waiting for a structure inside an mbuf ? If so, try to find why */
	if ( INRANGE( w, Info.i_mbutl,
	&Info.i_mbutl[ NMBCLUSTERS * CLBYTES / sizeof( struct mbuf ) ] ) )
		switch ( ((int)w - (int)Info.i_mbutl) % sizeof( struct mbuf )
			- (int)&((struct mbuf*)0)->m_dat[0] )
		{
			case (int)&((struct socket*)0)->so_timeo :
				/* Socket timeout event */
				return ( "socket" ) ;
			case (int)&((struct socket*)0)->so_rcv.sb_cc :
				/* Read from an empty socket. Here we actually
				   attempt to determine whether the socket
				   structure in question really does refer to
				   a socket, or whether it is in fact a pipe
				   in disguise. */
				return ( getsocket( (struct socket*)(w
				    - (int)&((struct socket*)0)->so_rcv.sb_cc),
						&sc )
					&& sc.so_type == SOCK_STREAM
					&& !sc.so_rcv.sb_hiwat
					&& !sc.so_rcv.sb_mbmax
					&& (sc.so_state
					    & (SS_ISCONNECTED|SS_CANTRCVMORE))
					? "rpipe" : "rsockt" ) ;
			case (int)&((struct socket*)0)->so_snd.sb_cc :
				/* Write to a full socket. Again, we try
				   to determine whether or not this is a
				   real socket or a pipe. */
				return ( getsocket( (struct socket*)(w
				    - (int)&((struct socket*)0)->so_snd.sb_cc),
						&sc )
					&& sc.so_type == SOCK_STREAM
					&& sc.so_rcv.sb_hiwat == 2048
					&& sc.so_rcv.sb_mbmax == 4096
					&& (sc.so_state
					    & (SS_ISCONNECTED|SS_CANTSENDMORE))
					? "wpipe" : "wsockt" ) ;
			default :
				/* Other mbuf event */
				return ( "mbuf" ) ;
		}
# endif
	/* Look in the symbol table for known wait addresses. */
	for ( s = Symbollist ; s->s_kname ; s++ )
		if ( s->s_wait && w == *s->s_info )
			return ( s->s_wait ) ;	
	/* No reason for the wait state has been found.
	   Return the wait channel as a hexadecimal address. */
	(void)sprintf( wbuf, "x%05x", w - 0x80000000 ) ;
	return ( wbuf ) ;
}

# ifdef BSD42
/*
** GETSOCKET - Reads a `struct socket' from the kernel virtual memory address
** identified by `ks' into the buffer `s'.
*/
getsocket ( ks, s )

struct socket			*ks ;
struct socket			*s ;

{
	extern int		Flkmem ;

	memseek( Flkmem, (long)ks ) ;
	return ( read( Flkmem, (char*)s, sizeof( struct socket ) )
		== sizeof( struct socket ) ) ;
}
# endif
---END-OF-waitingfor.c---
echo Extracting sps.ms
cat > sps.ms << '---END-OF-sps.ms---'
.if n .pl 66
.TH SPS LOCAL
.SH NAME
sps \ \ \-\ \ \ show process status
.SH SYNOPSIS
\fBsps\ \fP \ [ \fB-defgijkoqrslvwyABDFNPSTUWZ\fP ]\ \ [
\fIprocess\. |\. tty\. |\. user\. .\ .\ .\ .\ .\fP ]
.SH DESCRIPTION
\fISps\fP reports information concerning system processes.
It shows the current state of any process by
listing information such as ownership, CPU time usage, memory usage
and disc activity.
.PP
\fISps\fP should be used in preference to \fIps\fP(1)
as it is faster and the output is easier to understand.
.SH OPTIONS
By default, \fIsps\fP prints basic information about one's own processes.
The various options described below select other processes or make
\fIsps\fP more verbose.
.PP
Upper case options select processes to be described.
Lower case options specify the format of the output.
For instance, the options \fBBv\fP specify that \fIsps\fP
should list "busy" processes in a verbose format.
Unless there is any conflict, lower case options may be used
instead of upper case options, and vice versa.
.PP
The following options specify the format of the listed output -
.TP 8
\fB-d\fP
List output reflecting how each process affects the
disc and paging activity of the system.
.TP
\fB-e\fP
List the environment passed to each process.
.TP
\fB-f\fP
Include the process-id of the parent of each process.
.TP
\fB-g\fP
Include the process group of each process.
.TP
\fB-o\fP
Avoid looking at the swap device (/dev/drum). This tends to make \fIsps\fP
run faster, although no information concerning swapped processes
can be obtained.
.TP
\fB-q\fP
By default, \fIsps\fP
lists the sum of the user plus system times under the
\fITime\fP and \fIChild\fP fields.  This option forces \fIsps\fP
to list only the user times.
.TP
\fB-r\fP
Repeat the output indefinitely.
If the next argument is numeric, \fIsps\fP repeats the output with that
many seconds delay between each repetition.
Otherwise the output is repeated with no delay.
.TP
\fB-l\fP
.br
.ns
.TP
\fB-v\fP
List additional information in a verbose format. See below.
.TP
\fB-w\fP
List output in a wide format.  This option forces \fIsps\fP
to print all the command arguments, even if doing so extends the output
beyond one line.
.TP
\fB-y\fP
Show the status of each terminal line.
.PP
The following options specify which processes are to be described -
.TP 8
\fB-A\fP
List all processes.
.TP
\fB-B\fP
List busy processes.  A process is considered to be busy
if it is immediately runnable or awaiting a fast event such as disc I/O.
.TP
\fB-D\fP
List detached processes.
.TP
\fB-F\fP
List foreground processes.
.TP
\fB-N\fP
Show no processes at all. Only the summary line is printed.
.TP
\fB-P\fP
List only processes whose identifiers are specified in the following arguments.
.TP
\fB-S\fP
List stopped processes.
.TP
\fB-T\fP
List only processes attached to the following specified terminals.
.TP
\fB-U\fP
List only processes belonging to the following specified users.
.TP
\fB-W\fP
List waiting processes.
.TP
\fB-Z\fP
List zomby (exiting) processes.
.PP
The following are miscellaneous options -
.TP 8
\fB-i\fP
Initialise \fIsps\fP.
This is necessary if new users are added to the password file,
or if a new version of UNIX is installed.
Sps builds a new information file summarising pertinent information
read from the password file (/etc/passwd), the executable kernel image
(/vmunix) and the directory of tty devices (/dev).
See also the \fB-j\fP and \fB-s\fP options.
.TP
\fB-j\fP
Specify an information file other than the default (/etc/spsinfo).
The next argument is taken to be the name of a suitable information file.
If the \fB-i\fP flag is also specified, \fIsps\fP builds a
new information file with the given name.
Otherwise, \fBsps\fP reads previously created summarising information
from that file.
.TP
\fB-k\fP
Use a specific disc file rather than the default physical memory (/dev/mem)
and kernel virtual memory (/dev/kmem) files. The next argument is taken
to be the name of a suitable memory dump file.
This flag automatically sets the \fB-o\fP flag.
.TP
\fB-s\fP
This option is used in conjunction with the \fB-i\fP option.
The next argument is taken to be the name of a suitable kernel executable
file, rather than the default (/vmunix).
\fISps\fP looks at the symbol table of this file to determine
the virtual addresses of various kernel structures.
.SH OUTPUT
\fISps\fP produces output in the following fields -
.TP 8
\fITy\fP
The terminal identifier to which the process is attached.
.IP
If this is followed by an underscore, the process is detached.
If it is followed by a period, the process is running in the foreground.
Otherwise the process is running in the background but is still
attached to a terminal.
.TP
\fIUser\fP
The symbolic name of the process' effective user-id (see \fIexec\fP(2)
and \fIsetuid\fP(2)).
This name is defined by the system password file (/etc/passwd)
when \fIsps\fP was last initialised.
Otherwise, an asterisk (*) or vertical bar (|) appearing in this
column denotes that the process is an immediate relative of the
preceding process.
A bar is listed, rather than an asterisk, if both processes belong
to the same process group.
In this case, a user name is listed only if the effective user-id
differs from that of the preceding process.
.TP
\fIProc#\fP
The unique process identifier.
.TP
\fIPpid#\fP
The process-id of the process' parent.
.TP
\fIPgrp#\fP
The process group to which the process belongs.
.TP
\fICommand\fP
The command arguments obtained from the process' own address space.
(If the command name appears in parentheses, \fIsps\fP
was unable to locate the arguments in user space and so reports
the system's idea of the command name.)
.PP
The following additional fields are listed when \fIsps\fP
is invoked with one of the \fB-l\fP or \fB-v\fP options -
.TP 8
\fIStatus\fP
The process' current state.
If this field is listed in upper-case letters, the process is currently
loaded in real memory space ; otherwise it has been swapped out.
The status field may contain one of the following descriptions -
.RS 8
.TP 16
\fIrun\fP
The process can be run immediately.
.TP
\fIstop\fP
The process is stopped (see \fIsigsys\fP(2) or \fIsigvec\fP(2)).
.TP
\fIexit\fP
The process is a zomby.
.RE
.IP
Any other entry in the status field indicates the process is
waiting for some external event to occur.
This is usually for one of the reasons listed below.
(If \fIsps\fP does not know why a process is waiting, it lists
the hexadecimal address of the process' wait channel,
with the initial 80000000 trimmed off.)
A process may be waiting for one of the following reasons -
.RS 8
.TP 16
\fIchild\fP
The process is waiting for a child to terminate (see \fIwait\fP(2)).
.TP
\fIpause\fP
Waiting for a signal to be received (see \fIpause\fP(2)).
.TP
\fIswap\fP
Waiting for a page to be swapped in.
.TP
\fIrswbuf\fP
Waiting for a read from the swap device
\fB/dev/drum.\fP
.TP
\fIdiscio\fP
Waiting for a disc read or write operation.
.TP
\fIrpipe\fP
.br
.ns
.TP
\fIwpipe\fP
Waiting for a read from an empty pipe.  Alternatively, the process
is waiting to write to a full pipe (see pipe\fI(2)\fP).
.TP
\fIrsockt\fP
.br
.ns
.TP
\fIwsockt\fP
Waiting for a read from an empty socket.
Alternatively, the process is waiting to write to a full socket (4.2bsd only).
.TP
\fIsocket\fP
Waiting for a time-out event on a socket (4.2bsd only).
.TP
\fIselect\fP
Blocked by a \fIselect\fP(2) system call (4.2bsd only).
.TP
\fIrmux\fP
Waiting for a read from a multiplexor file (4.1bsd only).
.TP
\fIinode\fP
Waiting for an inode to be allocated or unlocked.
.TP
\fIexlock\fP
.br
.ns
.TP
\fIshlock\fP
Waiting for a file to become unlocked (see flock\fI(2)\fP).
.TP
\fIrtty??\fP
.br
.ns
.TP
\fIwtty??\fP
.br
.ns
.TP
\fIotty??\fP
Waiting for a read or write to the specified terminal, or for the terminal
to be switched on (see \fItty\fP(4)).
Alternatively, waiting for a read or write to the
specified slave pty device (see \fIpty\fP(4)).
.TP
\fIrpty??\fP
.br
.ns
.TP
\fIwpty??\fP
Waiting for a read or write to the specified master pty device
(see \fIpty\fP(4)).
.TP
\fIptrace\fP
This is a parent process tracing its child.
.TP
\fIvfork\fP
This is a vforking parent process waiting for its child to relinquish
memory resources (see \fIvfork\fP(2)).
.TP
\fIfloppy\fP
.br
.ns
.TP
\fIprintr\fP
.br
.ns
.TP
\fIvarian\fP
.br
.ns
.TP
\fIr??buf\fP
Waiting for the specified device to complete an I/O operation.
.RE
.TP 8
\fIFl\fP
Flags associated with the current state of the process.
These flags may be any of the following -
.RS 8
.TP 16
\fIU\fP
The process is a UNIX system process.
.TP
\fIT\fP
The process is being traced or debugged.
.TP
\fIV\fP
The process is a child currently being vforked (see \fIvfork\fP(2)).
.TP
\fII\fP
The process is undergoing physical I/O.
.TP
\fIA\fP
The system has detected, or the user has warned of
anomalous paging behaviour (see \fIvadvise\fP(2)).
.RE
.TP 8
\fINice\fP
The "niceness" of the process (see \fInice\fP(2)).
.TP
\fIVirtual\fP
The virtual memory size of the process in kilobytes.
The first figure indicates the sum of the data and stack segments,
the second figure that of the text segment.
.TP
\fIResident\fP
The resident memory size of the process in kilobytes, representing
the real memory devoted to the process.
.TP
\fI%M\fP
The percentage of available real memory allocated to this process.
.TP
\fITime\fP
The total CPU time accumulated by this process.
(This is the sum of the system plus user times, unless the \fB-q\fP
flag is specified in which case only the user time is listed.)
.TP
\fIChild\fP
The total CPU time accumulated by the process' children.
(This is the sum of the system plus user times, unless the \fB-q\fP
flag is specified.)
.TP
\fI%C\fP
The percentage of available CPU time devoted to the process.
This figure is a decaying average, computed over the past second.
.PP
The following fields are listed when \fIsps\fP is invoked with the
\fB-d\fP option -
.TP 8
\fIFiles\fP
The number of open files for this process.
.TP
\fIPageFaults\fP
The number of major and minor page faults incurred by the process.
.TP
\fISwap\fP
The number of swaps incurred by the process.
.TP
\fIBlockI/O\fP
The number of block read or write operations performed
on behalf of the process.
.TP
\fIKbytesecs\fP
The integral of real memory usage over time.
Thus, if a process uses 60 kilobytes of real memory for 3 seconds,
this figure is incremented by 180.
.PP
The following fields are listed when \fIsps\fP is invoked with the
\fB-y\fP option -
.TP 8
\fIDev\fP
The major and minor device numbers of the terminal.
.TP
\fIAddr\fP
The virtual address of the associated \fBstruct tty\fP in /dev/kmem.
.TP
\fIRawq\fP
.br
.ns
.TP
\fICanq\fP
.br
.ns
.TP
\fIOutq\fP
The number of characters in the terminal I/O queues.
These refer to the raw input queue, the canonical input queue
and the output queue.
.TP
\fIPgrp\fP
The process group associated with the terminal.
.PP
After listing the requested output, \fIsps\fP prints a summary line.
This indicates the number and total virtual memory size of all processes,
the number and total virtual size of busy processes,
the number and real memory size of loaded processes
and the number and real size of swapped processes.
.SH DIAGNOSTICS
\fISps\fP reports a self-explanatory message if it is given an
invalid argument list.
The program also complains if it cannot find necessary system information.
.PP
At initialisation, \fIsps\fP complains if it cannot find the addresses of
requisite system structures in the kernel symbol file.
This is usually the case because the system is rarely configured to support
all known devices.
\fISps\fP also complains if more than one user shares the same user-id
in the password file (/etc/passwd).
.SH EXAMPLES
\fBsps vb\fP
.PP
\fISps\fP describes all busy processes in a verbose manner.
.PP
\fBsps dtg 9 h1 co\fP
.PP
\fISps\fP lists processes associated with terminals 9, h1 and the console.
The output reflects the disc activity caused by these processes.
The process group of each process is also included in the output.
.PP
\fBsps weu robert graham -r 2\fP
.PP
\fISps\fP reports processes belonging to the specified users.
It lists the environment as well as all the command arguments in a wide format.
The output is produced indefinitely, with a delay of two seconds between
each listing.
.PP
\fBsps is /vmunix.new\fP
.PP
\fISps\fP is initialised. It reads its symbol information from the
specified file.
.SH FILES
.ta 2.5i
.nf
/dev/console	Console
/dev/tty??	Terminal and pty devices
/dev/kmem	Kernel virtual memory
/dev/mem	Physical memory
/dev/drum	Paging and swap device
/etc/passwd	Password file
/etc/spsinfo	Information file
/vmunix	Symbol file of /dev/kmem addresses
/etc/termcap	To determine the output terminal width
.fi
.ta
.SH SEE ALSO
\fIiostat\fP(1), \fIkill\fP(1), \fIps\fP(1),
\fIvmstat\fP(1), \fIexec\fP(2),
\fIflock\fP(2), \fInice\fP(2), \fIpause\fP(2), \fIselect\fP(2), \fIsetuid\fP(2),
\fIsigsys\fP(2), \fIsigvec\fP(2), \fIvadvise\fP(2), \fIvfork\fP(2),
\fIvlimit\fP(2), \fIvtimes\fP(2), \fIwait\fP(2),
\fIpty\fP(4),\fItty\fP(4), \fIpstat\fP(8).
.SH AUTHORS
Several. In particular, J. E. Kulp and J. Robert Ward.
.SH BUGS
Because the system is continually changing, the information reported by
\fIsps\fP is only an approximation to reality.
\fISps\fP renices itself to -20 in an attempt to run as
fast as possible.
.PP
\fISps\fP recognises the sizes and addresses of internal kernel
tables whenever it is invoked. However, it must be recompiled
if major modifications are made to the kernel.
.PP
\fISps\fP does not list all the detailed information shown by \fIps\fP(1).
Nor are all the options supported by \fIps\fP(1) available from \fIsps\fP.
.PP
\fISps\fP does not understand all the possible
reasons why a process may be sleeping.
.PP
The number of options to \fIsps\fP is ridiculous.
---END-OF-sps.ms---
echo Linking Makefile.4.2 to Makefile
ln Makefile.4.2 Makefile
echo Making
make
echo Initialising SPS \(ignore error messages here\)
./sps -i -j spsinfo
echo Testing SPS \(should report verbose information for all processes\)
./sps va -j spsinfo
echo End of test. If that worked, now run \"make install\"

mike@peregrine.UUCP (Mike Wexler) (06/27/85)

Does any one have a sun version?  I have a sun 2/120 running 4.2BSD and
when I run sps I get several errors about upages that aren't found.

-- 
--------------------------------------------------------------------------------
Mike Wexler(trwrb!pertec!peregrine!mike) | Send all flames to:
15530 Rockfield, Building C              |	trwrb!pertec!peregrine!nobody
Irvine, Ca 92718                         | They will then be given the 
(714)855-3923                            | consideration they are due.