[net.sources] V7 and MC68000 version of SPS

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

Here is the version of SPS for Bell Labs V7 (PDP-11) Unix and Unisoft 1.3
MC68000 Unix for those who have expressed interest in it. It is almost
functionally equivalent of the 4.xbsd version that was posted a few days
ago to net.sources.

This is a somewhat older version than the 4.[12]bsd version of SPS and
hence lacks a few of the fancier features such as hashing of user-ids
as well as some miscellaneous options. However, it shouldn't be too
difficult to update the code to incorporate these additions.

The following notes apply to anyone who is interested in trying to port SPS
to a new machine or system. The majority of the code of SPS is suprisingly
portable to most systems (at least it has been so far). The difficulty
invariably lies in the part of the code that fetches a process' upage
(getcmd11.c) and the code which attempts to locate the base of the command
argument stack in the user address space. If you have the code for ps(1),
you can always rip the relevant sections out of there. Otherwise it means
having to use guesswork and adb(1) and a lot of time.
The code enclosed in "#ifdef VMUNIX" lines below refers to an older version
of SPS for a 4.1bsd Vax only. It is largely obselete and should be ignored.

The code for this and the 4.xbsd versions should, of course, really be combined
together but then the ifdefing becomes a severe headache and makes the code
even more unreadable.

Please send any comments, suggestions, bug fixes and reports to me.

Robert Ward.
	Hasler AG (Abt. 34)
	Belpstrasse 23
	CH-3000 Bern 14
	Switzerland.
*******************************************************************************
This is a sh(1) archive.
To extract, cut after the following dotted line and run sh on that file.
--------------------------------- C U T --- H E R E ---------------------------
#! /bin/sh
echo Extracting Makefile.11
cat > Makefile.11 << '---END-OF-Makefile.11---'
# Makefile for SPS (IIASA PDP-11/70 Version)

PROG	=	sps
OBJS	=	findtty.o flagdecode.o flagsetup.o \
		getcmd11.o globals1.o globals2.o \
		initialise.o main.o mktree.o needed.o openfiles.o \
		prcmd.o prcpu.o prheader.o printall.o \
		printproc.o prsummary.o readstatus.o readsymbols.o \
		select.o select_tty.o ttyinit.o ttystatus.o waitingfor.o
SRCS	=	findtty.c flagdecode.c flagsetup.c \
		getcmd11.c globals1.c globals2.c \
		initialise.c main.c mktree.c needed.c openfiles.c \
		prcmd.c prcpu.c prheader.c printall.c \
		printproc.c prsummary.c readstatus.c readsymbols.c \
		select.c select_tty.c ttyinit.c ttystatus.c waitingfor.c
INCS	=	sps.h
CC	=	cc11
CFLAGS	=	-I/usr/include/local/pdp11/sys

all:		$(PROG)
.c.o:
		$(CC) $(CFLAGS) -c -O $<

$(OBJS):	$(INCS)

$(PROG):	$(OBJS)
		$(CC) -o $@ $(OBJS)

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

lint:
		lint -x -b $(CFLAGS) $(SRCS)
clean:
		rm -f $(OBJS) $(PROG)
---END-OF-Makefile.11---
echo Extracting Makefile.4.1
cat > Makefile.4.1 << '---END-OF-Makefile.4.1---'
# Makefile for SPS (4.1BSD UNIX Version, with or without Chaos network)

PROG	=	sps
OBJS	=	filecount.o findtty.o flagdecode.o flagsetup.o \
		getcmd.o getupage.o globals1.o globals2.o \
		initialise.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 readsymbols.o \
		select.o select_tty.o ttyinit.o ttystatus.o waitingfor.o
SRCS	=	filecount.c findtty.c flagdecode.c flagsetup.c \
		getcmd.c getupage.c globals1.c globals2.c \
		initialise.c main.c mktree.c needed.c openfiles.c \
		percentmem.c prcmd.c prcpu.c prheader.c printall.c \
		printproc.c prsummary.c readstatus.c readsymbols.c \
		select.c select_tty.c ttyinit.c ttystatus.c waitingfor.c
INCS	=	sps.h
CFLAGS	=	-I/usr/src/sys -DVMUNIX -DCHAOS

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

$(OBJS):	$(INCS)

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

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

lint:
		lint -x -b $(CFLAGS) $(SRCS)
clean:
		rm -f $(OBJS) $(PROG)
---END-OF-Makefile.4.1---
echo Extracting Makefile.68000
cat > Makefile.68000 << '---END-OF-Makefile.68000---'
# Makefile for SPS (Unisoft Version 1.3 UNIX)

PROG	=	sps
OBJS	=	findtty.o flagdecode.o flagsetup.o \
		getcmd11.o globals1.o globals2.o \
		initialise.o main.o mktree.o needed.o openfiles.o \
		prcmd.o prcpu.o prheader.o printall.o \
		printproc.o prsummary.o readstatus.o readsymbols.o \
		select.o select_tty.o ttyinit.o ttystatus.o waitingfor.o
SRCS	=	findtty.c flagdecode.c flagsetup.c \
		getcmd11.c globals1.c globals2.c \
		initialise.c main.c mktree.c needed.c openfiles.c \
		prcmd.c prcpu.c prheader.c printall.c \
		printproc.c prsummary.c readstatus.c readsymbols.c \
		select.c select_tty.c ttyinit.c ttystatus.c waitingfor.c
INCS	=	sps.h
CFLAGS	=	-c -DV7 -DM68000 -I/usr/include/sys
CC	=	cc

all:		$(PROG)
.c.o:
		$(CC) -O $(CFLAGS) $<
openfiles.o:
		$(CC) $(CFLAGS) $<

$(OBJS):	$(INCS)

$(PROG):	$(OBJS)
		$(CC) -o $@ $(OBJS)

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

lint:
		lint -x -b $(CFLAGS) $(SRCS)
clean:
		rm -f $(OBJS) $(PROG)
---END-OF-Makefile.68000---
echo Extracting Makefile.v7
cat > Makefile.v7 << '---END-OF-Makefile.v7---'
# Makefile for SPS (Bell Labs PDP-11 Version 7 Unix)

PROG	=	sps
OBJS	=	findtty.o flagdecode.o flagsetup.o \
		getcmd11.o globals1.o globals2.o \
		initialise.o main.o mktree.o needed.o openfiles.o \
		prcmd.o prcpu.o prheader.o printall.o \
		printproc.o prsummary.o readstatus.o readsymbols.o \
		select.o select_tty.o ttyinit.o ttystatus.o waitingfor.o
SRCS	=	findtty.c flagdecode.c flagsetup.c \
		getcmd11.c globals1.c globals2.c \
		initialise.c main.c mktree.c needed.c openfiles.c \
		prcmd.c prcpu.c prheader.c printall.c \
		printproc.c prsummary.c readstatus.c readsymbols.c \
		select.c select_tty.c ttyinit.c ttystatus.c waitingfor.c
INCS	=	sps.h
CFLAGS	=	-DV7 -Dvoid=int -I/v7/usr/include/sys
CC	=	/v7/bin/cc

all:		$(PROG)
.c.o:
		$(CC) $(CFLAGS) -c -O $<

$(OBJS):	$(INCS)

$(PROG):	$(OBJS)
		$(CC) -o $@ $(OBJS)

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

lint:
		lint -x -b $(CFLAGS) $(SRCS)
clean:
		rm -f $(OBJS) $(PROG)
---END-OF-Makefile.v7---
echo Extracting README
cat > README << '---END-OF-README---'
		SPS - Show Process Status
		=========================

SPS currently runs under four different Unix systems :
	Bells Labs PDP-11 Unix,
	Berkeley 4.1BSD VAX Unix (VAX-11/780 and VAX-11/750),
	IIASA PDP-11/70 Unix,
	Unisoft Version 1.3 Unix (PSI-9068).

Compiler options are as follows -
	-DVMUNIX	for 4.1bsd
	-DCHAOS		if the Chaos network is incorporated into 4.1bsd
	-DV7		for V7 Unix, IIASA PDP-11/70 and Unisoft
	-DM68000	for Unisoft
	-DTRACE		for testing/debugging purposes

********************************************************************************

When porting SPS to a new machine, the things to watch out for are the
way in which a process' upage is accessed (see pread() in getcmd11.c)
and the way in which command arguments are set up on the user stack
(see getcmd()). Command arguments are generally set up differently on each
machine ; the difficulty lies in trying to locate the base of the argument
list.

Some versions of Unix also seem to be incorrectly compiled : the `d_ttys'
field of the entries in the `cdevsw' table that refer to tty devices is
sometimes null. The kernel must then be patched so that these fields refer
to the corresponding `struct tty' inside the kernel.

********************************************************************************

If you want to tell SPS about a new type of device, then add make a new
entry in the `struct info' (see sps.h) and add a new line in the symbol
table (see globals2.c).

********************************************************************************

SPS understands if the size of internal kernel tables are changed under VMUNIX,
but not under any other system. It must be recompiled if major modifications
are made to the kernel.
---END-OF-README---
echo Extracting filecount.c
cat > filecount.c << '---END-OF-filecount.c---'
# ifdef VMUNIX

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

# endif
---END-OF-filecount.c---
echo Extracting findtty.c
cat > findtty.c << '---END-OF-findtty.c---'
# include	"sps.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 ;
# ifdef VMUNIX
	extern union userstate	User ;
# endif

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

/* FLAGDECODE - Looks at the argument list and sets various internal switches */
/* Note that the `d' and `D' switches are only implemented under VMUNIX */

flagdecode ( argc, argv )

register int			argc ;
register char			**argv ;

{
	register char		*chp ;
	union flaglist		*plist ;
	union flaglist		*tlist ;
	union flaglist		*ulist ;
	static char		usage[] =
# ifdef VMUNIX
	"sps - Unknown option %s\nUsage - sps [ -defgiqrvwyABDFNPSTUWZ ][ process|tty|user ] ...\n";
# else
	"sps - Unknown option %s\nUsage - sps [ -efgiqrvwyABFNPSTUWZ ][ process|tty|user ] ...\n";
# endif
	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 ;
# ifdef VMUNIX
				case 'd' :
					/* List disc orientated information */
					Flg.flg_d = 1 ;
					Flg.flg_v = 0 ;
					continue ;
# endif
				case 'e' :
				case 'E' :
					/* List environment strings */
					Flg.flg_e = 1 ;
					continue ;
				case 'f' :
					/* List the father's process id */
					Flg.flg_f = 1 ;
					Flg.flg_g = 0 ;
					continue ;
				case 'g' :
				case 'G' :
					/* List the process group id */
					Flg.flg_f = 0 ;
					Flg.flg_g = 1 ;
					continue ;
				case 'i' :
				case 'I' :
					/* Initialise (super-user only) */
					Flg.flg_i = 1 ;
					continue ;
				case 'l' :
				case 'v' :
				case 'L' :
				case 'V' :
					/* Verbose output */
# ifdef VMUNIX
					Flg.flg_d = 0 ;
# endif
					Flg.flg_v = 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 5 seconds */
					Flg.flg_r = 1 ;
					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 ;
# ifdef VMUNIX
				case 'D' :
					/* List only detached processes */
					Flg.flg_AZ = 1 ;
					Flg.flg_D = 1 ;
					continue ;
# endif
				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' :
				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 flagsetup.c
cat > flagsetup.c << '---END-OF-flagsetup.c---'
# include	"sps.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 < MAXUSERS ; i++ )
				if ( !strncmp( chp, Info.i_unames[i], UNAMELEN))
				{
					fp->f_uid = i, 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++ )
	{
# ifdef VMUNIX
		/* 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' ;
# endif
		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---'
# ifdef VMUNIX

# include	"sps.h"

/*
** 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 ;
	struct pte		pte ;
	char			*argptr ;
	int			nbad, i ;
	static char		argbuf[ MAXARGPG * NBPG ] ;
	static struct pte	ptetable[ PAGETABLESIZE ] ;
	extern struct flags	Flg ;
	extern union userstate	User ;
	extern int		Flmem ;
	extern int		Flswap ;
	char			*strcat(), *strsave() ;
	long			lseek() ;

	p->pr_csaved = 0 ;
	p->pr_upag = 0 ;
	if ( p->pr_p.p_stat == SZOMB )
		return ( "** Defunct **" ) ;
	/* Find the process' upage */
	if ( !getupage( p, ptetable ) )		
		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.
	   To locate each user page, we must access the page table entries
	   to find the physical locations. */
	for ( i = 0 ; i < MAXARGPG ; i++ )
	{
		argptr = &argbuf[( MAXARGPG - 1 - i ) * NBPG] ;
		pte = ptetable[MAXARGPG - 1 - i] ;
		if ( (p->pr_p.p_flag & SLOAD) && !pte.pg_fod && pte.pg_pfnum )
		{	/* If the page is loaded, read the arguments from
			   physical memory. */
			(void)lseek( Flmem, (long)ctob( pte.pg_pfnum ), 0 ) ;
			if ( read( Flmem, argptr, NBPG ) != NBPG )
				return ( "** Mem read error **" ) ;
		}
		else				
		{	/* Otherwise the page is on the swap device */
			(void)lseek( Flswap,
			(long)ctob( User.u_us.u_smap.dm_map[0]+DMMIN-1-i), 0 ) ;
			if ( read( Flswap, argptr, NBPG ) != NBPG )
				return ( "** Swap read error **" ) ;
		}
		/* Arrive here with `argptr' pointing to the top of the
		   upage. Look down until the end of command arguments
		   is found. */
		ip = (int*)&argptr[NBPG] ;	
		if ( i == 0 )
		{
			*--ip = 0 ;
			ip-- ;
		}
		while ( ip > (int*)argptr && *--ip )
			 ;
		if ( ip > (int*)argptr || !*ip )
			break ;
		/* If no command arguments were found on this page, then
		   access the next. */
	}
	p->pr_upag = 1 ;
	p->pr_csaved = 1 ;
	/* If the command arguments could not be accessed from the user's
	   memory space, get the command name from the system's idea
	   of what the name should be. */
	if ( i >= MAXARGPG )		
	{				
		argbuf[0] = '(' ;
		(void)strncpy( &argbuf[1], User.u_us.u_comm,
			sizeof( User.u_us.u_comm ) ) ;
		(void)strcat( argbuf, ")" ) ;
		return ( strsave( argbuf ) ) ;
	}
	/* Process the command arguments, looking for nulls and unprintable
	   characters. */
	cp0 = (char*)(ip + 1) ;
	if ( !*cp0 )			
		cp0++ ;			
	nbad = 0 ;			
	for ( cp = cp0 ; cp < &argbuf[MAXARGPG*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 ) ) ;
}

# endif
---END-OF-getcmd.c---
echo Extracting getcmd11.c
cat > getcmd11.c << '---END-OF-getcmd11.c---'
# ifndef VMUNIX

# include	"sps.h"

/* The effective `page' size for a PDP-11/70 ... */
# ifndef NBPG
# define	NBPG		512
# endif

/* For the benefit of Unisoft Unix ... */
# ifdef M68000
# ifndef UBASE
# define	UBASE		0x1ff000
# endif
# endif

/*
** GETCMD - Returns a character string read from system memory.
** This character string should represent the arguments to the current process.

** This is functionally similar to the VAX version of getcmd(). Notice, however,
** that, in the interests of speed, only one page is ever accessed to
** determine the command arguments. If the combined arguments plus
** environment span across more than one page, an attempt is made to locate
** the command name from system space.
*/
char	*getcmd ( p )

register struct process		*p ;

{
	register char		*cp ;
	register char		*cp0 ;
# ifdef M68000
	register int		*cpi ;
# endif
	int			nbad ;
	static char		argbuf[ NBPG ] ;
	extern struct flags	Flg ;
	extern struct info	Info ;
	char			*strsave() ;

	p->pr_csaved = 0 ;
	p->pr_upag = 0 ;
	if ( p->pr_p.p_stat == SZOMB )
		return ( "** Defunct **" ) ;
	if ( p->pr_p.p_flag & SSYS )
	{
		p->pr_upag = 1 ;
		return ( "UNIX Swapper" ) ;
	}
	if ( !(p->pr_p.p_flag & SLOAD) && p->pr_p.p_addr > Info.i_nswap )
		return ( "** Swap address error **" ) ;
	if ( pread( p, NBPG, argbuf, sizeof( argbuf ), 1 ) == -1 )
		return ( "** Read error **" ) ;
	p->pr_upag = 1 ;
# ifdef M68000
	cpi = (int*)&argbuf[NBPG] ;	
	while ( cpi > (int*)argbuf && !*--cpi )
		 ;
	if ( cpi <= (int*)&argbuf[0] )
		goto notfound ;
	while ( cpi > (int*)argbuf && *--cpi )
		 ;
	if ( cpi <= (int*)&argbuf[0] )
		goto notfound ;
	cp0 = (char*)(cpi+1) ;
# else
	/* Locate the start and end of the command arguments in the buffer.
	   The start is determined by locating a byte containing 0377,
	   searching from the end of the buffer. */
	cp = &argbuf[NBPG] ;
	while ( cp > &argbuf[0] && (int)*--cp != -1 )
		;
	if ( cp <= &argbuf[0] )
		goto notfound ;
	/* If the command arguments were successfully accessed,
	   we arrive here with `cp' pointing just before a byte containing
	   0377, followed (possibly) by a series of non-null bytes,
	   followed by a series of nulls.
	   Now increment `cp' until it points to the first byte
	   past the nulls. This, hopefully, represents the beginning
	   of the command arguments. */
	cp += 2 ;
	while ( cp < &argbuf[NBPG] && *cp++ )
		;
	if ( cp >= &argbuf[NBPG] )
		goto notfound ;
	cp-- ;
	while ( cp < &argbuf[NBPG] && !*cp++ )
		;
	if ( cp >= &argbuf[NBPG] )
		goto notfound ;
	cp0 = cp-1 ;
# endif
	p->pr_csaved = 1 ;
	/* Process the command arguments, looking for nulls and unprintable
	   characters. */
	nbad = 0 ;
	for ( cp = cp0 ; cp < &argbuf[ 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 ) ) ;
notfound :
	/* We arrive here if the page of command arguments is full,
	   in other words, if we have not found the beginning of the
	   command. Now try to determine the name of the command
	   by accessing the system's idea of what the name is. */
	if ( pread( p, (int)((struct user*)0)->u_comm, &argbuf[1],
		DIRSIZ, 0 ) == -1 )
		return ( "** Too many arguments **" ) ;
	argbuf[0] = '(' ;
	argbuf[DIRSIZ+1] = '\0' ;
	for ( cp = &argbuf[1] ; cp <= &argbuf[DIRSIZ+1] ; cp++ )
	{
		*cp &= 0177 ;
		if ( !*cp )
		{
			*cp++ = ')' ;
			*cp = '\0' ;
			break ;
		}
		if ( *cp < ' ' || *cp == 0177 )
			*cp = '?' ;
	}
	p->pr_csaved = 1 ;
	return ( strsave( &argbuf[0] ) ) ;
}

/* PREAD - Read process information */
pread ( p, addr, buf, nbytes, flag )

register struct process		*p ;
register unsigned		addr ;
register char			*buf ;
int				nbytes ;
int				flag ;

{
	long			seekaddr ;
	long			saddr1, saddr2 ;
	char			*cp ;
	static char		localbuf[ NBPG ] ;
	extern int		Flmem ;
	extern int		Flswap ;
	extern struct info	Info ;
	long			lseek() ;

	/* `Flag' indicates whether the search is to be from the top
	   of the process address space, or the upage area. */
	seekaddr = flag ? (long)ctob( p->pr_p.p_size ) - addr :
			addr + (int)Info.i_u - UBASE ;
	/* If the process is loaded, look in its address space. */
	if ( p->pr_p.p_flag & SLOAD )
	{
		(void)lseek( Flmem, seekaddr + (long)ctob( p->pr_p.p_addr), 0 );
		return ( read( Flmem, buf, nbytes ) ) ;
	}
	/* Otherwise the process is swapped out. Look on the swap device.
	   The code here seems to be because /dev/swap is a block device
	   and therefore must be accessed one block at a time. */
	seekaddr += ((long)(unsigned)p->pr_p.p_addr + Info.i_swplo) << 9 ;
	saddr1 = seekaddr / 512L * 512L ;
	(void)lseek( Flswap, saddr1, 0 ) ;
	saddr2 = seekaddr % 512L ;
	if ( !saddr2 )
		return ( read( Flswap, buf, nbytes ) ) ;
	(void)read( Flswap, localbuf, sizeof( localbuf ) ) ;
	cp = &localbuf[ (int)saddr2 ] ;
	while ( cp < &localbuf[ sizeof( localbuf ) ] && nbytes-- )
		*buf++ = *cp++ ;
	if ( nbytes > 0 )
		return ( read( Flswap, buf, nbytes ) ) ;
	return ( 0 ) ;
}

/* GETUINFO - Read information from the process' upage */
getuinfo ( p )

register struct process		*p ;

{
	/* NOTE THAT THE STRUCTURE SHOWN BELOW SHOULD MIMIC HOW
	   THE TIME INFORMATION IS STORED IN A PROCESS' UPAGE. */
	struct
	{
		time_t		t_utime ;
		time_t		t_stime ;
		time_t		t_cutime ;
		time_t		t_cstime ;
	} t ;

	/* Firstly, read the process cpu times from the upage */
	if ( pread( p, (int)&((struct user*)0)->u_utime, (char*)&t,
		sizeof( t ), 0 ) == -1 )
	{
		p->pr_utime = 0L ;
		p->pr_stime = 0L ;
		p->pr_cutime = 0L ;
		p->pr_cstime = 0L ;
	}
	else
	{
		p->pr_utime = t.t_utime ;
		p->pr_stime = t.t_stime ;
		p->pr_cutime = t.t_cutime ;
		p->pr_cstime = t.t_cstime ;
	}
	/* Now determine to which tty the process is connected */
	if ( pread( p, (int)&((struct user*)0)->u_ttyd, (char*)&p->pr_ttyd,
		sizeof( dev_t ), 0 ) == -1 )
		p->pr_ttyd = (dev_t)0 ;
}
---END-OF-getcmd11.c---
echo Extracting getupage.c
cat > getupage.c << '---END-OF-getupage.c---'
# ifdef VMUNIX

# include	"sps.h"
# include	<stdio.h>

/* GETUPAGE - Reads the upage for the specified process */

# define	usrpt		(Info.i_usrpt)

getupage ( p, ptetable )

register struct process		*p ;
struct pte			*ptetable ;

{
	struct pte		*pteaddr ;
	struct pte		pte ;
	register int		i ;
	int			ncl ;
	extern struct info	Info ;
	extern union userstate	User ;
	extern int		Flmem ;
	extern int		Flkmem ;
	extern int		Flswap ;
	long			lseek() ;

	/* If the process is not loaded, look for the upage on the swap device*/
	if ( !(p->pr_p.p_flag & SLOAD) )
	{				
		(void)lseek( Flswap, (long)ctob( p->pr_p.p_swaddr ), 0 ) ;
		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. Find where its pte's are kept */
	pteaddr = &Info.i_usrptmap[ btokmx(p->pr_p.p_p0br) + p->pr_p.p_szpt-1 ];
	(void)lseek( Flkmem, (long)pteaddr, 0 );
	if ( read( Flkmem, (char*)&pte, sizeof( struct pte ) )
	!= sizeof( struct pte ) )
	{
		fprintf	( stderr,
		      "sps - cant read indir pte to get upage for process %d\n",
		    p->pr_p.p_pid ) ;
		return ( 0 ) ;
	}				
	/* Now get sufficient pte's to be able to read the upage */
	(void)lseek( Flmem, (long)	
	    (ctob(pte.pg_pfnum+1) - PAGETABLESIZE*sizeof( struct pte )), 0 ) ;
	if ( read( Flmem, (char*)ptetable, PAGETABLESIZE*sizeof( struct pte ) )
	!= PAGETABLESIZE * sizeof( struct pte ) )
	{
		fprintf( stderr, "sps - cant read page table of process %d\n",
			p->pr_p.p_pid ) ;
		return ( 0 ) ;
	}
	/* Now we can read the pages belonging to the upage */
	ncl = ( sizeof( struct user ) + NBPG*CLSIZE - 1 ) / ( NBPG*CLSIZE ) ;
	while ( --ncl >= 0 )		
	{				
		i = ncl * CLSIZE ;
		(void)lseek(Flmem,(long)ctob(ptetable[MAXARGPG+i].pg_pfnum),0);
		if ( read( Flmem, User.u_pg[i], CLSIZE*NBPG ) != CLSIZE*NBPG )
		{
			fprintf( stderr,
				"sps - cant read page %d of process %d\n",
				ptetable[MAXARGPG+i].pg_pfnum, p->pr_p.p_pid ) ;
			return( 0 ) ;
		}
	}
	return ( 1 ) ;
}

# endif
---END-OF-getupage.c---
echo Extracting globals1.c
cat > globals1.c << '---END-OF-globals1.c---'
# include	"sps.h"
# ifndef VMUNIX
# include	<stdio.h>
# endif

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

# ifdef VMUNIX
union  userstate		User ;		/* Upage of one process */
# endif

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

short				Lastuid ;	/* Last process uid printed */

# ifndef VMUNIX
char				Sobuf[ BUFSIZ ] ;	/* Stdout buffer */
# endif
---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 inside the kernel ;
**	ii.  whether an extra indirection is needed through the kernel
**	     to obtain the value of that symbol ;
**	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. */
# ifdef VMUNIX
	{ "_nproc",	1,  (caddr_t*)&Info.i_nproc,	(char*)0	},
	{ "_ntext",	1,  (caddr_t*)&Info.i_ntext,	(char*)0	},
	{ "_ninode",	1,  (caddr_t*)&Info.i_ninode,	(char*)0	},
	{ "_nswbuf",	1,  (caddr_t*)&Info.i_nswbuf,	(char*)0	},
	{ "_nbuf",	1,  (caddr_t*)&Info.i_nbuf,	(char*)0	},
	{ "_hz",	1,  (caddr_t*)&Info.i_hz,	(char*)0	},
	{ "_ecmx",	1,  (caddr_t*)&Info.i_ecmx,	(char*)0	},
	{ "_pt_tty",	0,  (caddr_t*)&Info.i_pt_tty,	(char*)0	},
	{ "_Usrptmap",	0,  (caddr_t*)&Info.i_usrptmap,	(char*)0	},
	{ "_usrpt",	0,  (caddr_t*)&Info.i_usrpt,	(char*)0	},
	{ "_swbuf",	1,  (caddr_t*)&Info.i_swbuf0,	(char*)0	},
	{ "_proc",	1,  (caddr_t*)&Info.i_proc0,	(char*)0	},
	{ "_text",	1,  (caddr_t*)&Info.i_text0,	(char*)0	},
	{ "_inode",	1,  (caddr_t*)&Info.i_inode0,	(char*)0	},
	{ "_buf",	1,  (caddr_t*)&Info.i_buf0,	(char*)0	},
	{ "_cdevsw",	0,  (caddr_t*)&Info.i_cdevsw,	(char*)0	},
# ifdef CHAOS
	{ "_Chconntab",	0,  &Info.i_Chconntab,		(char*)0	},
# endif
# else
	{ "_nswap",	1,  (caddr_t*)&Info.i_nswap,	(char*)0	},
	{ "_swplo",	1,  (caddr_t*)&Info.i_swplo,	(char*)0	},
	{ "_proc",	0,  (caddr_t*)&Info.i_proc0,	(char*)0	},
	{ "_text",	0,  (caddr_t*)&Info.i_text0,	(char*)0	},
	{ "_inode",	0,  (caddr_t*)&Info.i_inode0,	(char*)0	},
	{ "_buf",	0,  (caddr_t*)&Info.i_buf0,	(char*)0	},
	{ "_cdevsw",	0,  (caddr_t*)&Info.i_cdevsw,	(char*)0	},
# endif
	/* Kernel addresses associated with process wait states. */
# ifdef VMUNIX
	{ "_fltab",	0,  &Info.i_fltab,		"floppy"	},
	{ "_va_softc",	0,  &Info.i_va_softc,		"varian"	},
	{ "_bfreelist",	0,  &Info.i_bfreeli,		"buffer"	},
	{ "_lp_softc",	0,  &Info.i_lpdt,		"printr"	},
	{ "_rswbuf",	0,  &Info.i_rswbuf,		"rswap"		},
# ifdef CHAOS
	{ "_Chrfclist",	0,  &Info.i_chrfclist,		"chrfc"		},
# endif
# else
	{ "_vn11",	0,  &Info.i_va_softc,		"varian"	},
	{ "_bfreeli",	0,  &Info.i_bfreeli,		"buffer"	},
	{ "_lp_dt",	0,  &Info.i_lpdt,		"printr"	},
	{ "_swbuf1",	0,  &Info.i_swap1,		"swap1"		},
	{ "_swbuf2",	0,  &Info.i_swap2,		"swap2"		},
# endif
	{ "_u",		0,  &Info.i_u,			"pause"		},
	{ "_chtbuf",	0,  &Info.i_chtbuf,		"tapecn"	},
	{ "_rhtbuf",	0,  &Info.i_rhtbuf,		"tapeio"	},
	{ "_lbolt",	0,  &Info.i_lbolt,		"lbolt"		},
	{ "_runin",	0,  &Info.i_runin,		"runin"		},
	{ "_runout",	0,  &Info.i_runout,		"runout"	},
	{ "_ipc",	0,  &Info.i_ipc,		"ptrace"	},
	{ (char*)0,	0,  (caddr_t*)0,		(char*)0	}
} ;
---END-OF-globals2.c---
echo Extracting initialise.c
cat > initialise.c << '---END-OF-initialise.c---'
# include	"sps.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 ;
	static char		file_info[] = FILE_INFO ;
	extern struct info	Info ;
	FILE			*fopen() ;

# ifndef TRACE
	if ( getuid() )
		prexit( "sps - You are not the super-user\n" ) ;
# endif
	/* Read kernel addresses */
	readsymbols() ;			
	/* Read user names */
	readusers() ;			
	(void)umask( ~0644 ) ;		
	if ( !(fd = fopen( file_info, "w" )) )
	{
		fprintf( stderr, "sps - Can't create info file %s", file_info );
		sysperror() ;
	}
	/* Find tty addresses */
	ttyinit() ;			
	if ( fwrite( (char*)&Info, sizeof( struct info ), 1, fd ) != 1 )
	{
		fprintf( stderr, "sps - can't write info file %s", file_info ) ;
		sysperror() ;
		exit( 1 ) ;
	}
	(void)fclose( fd ) ;
	printf( "Sps is initialised\n" ) ;
}

/* READUSERS - Read the passwd file and fill in the user name arrays */
readusers ()
{
	register struct passwd	*pw ;
	struct passwd		*getpwent() ;
	char			*strncpy() ;
	extern struct info	Info ;

	while ( pw = getpwent() )
	{
		if ( pw->pw_uid >= MAXUSERS )
		{
			fprintf( stderr,
			      "sps - User %s has uid %d exceeding maximum %d\n",
				pw->pw_name, pw->pw_uid, MAXUSERS ) ;
			continue ;
		}
		if ( !Info.i_unames[ pw->pw_uid ][0] )
			(void)strncpy( &Info.i_unames[ pw->pw_uid ][0],
				pw->pw_name, UNAMELEN ) ;
	}
	endpwent() ;
}
---END-OF-initialise.c---
echo Extracting main.c
cat > main.c << '---END-OF-main.c---'
# include	"sps.h"
# include	<stdio.h>

/* SPS - Show Process Status		*/

/* J. R. Ward - IIASA - 31 March 1983	*/

main ( argc,argv )

int				argc ;
char				**argv ;

{
	register struct process	*plist ;
# ifdef VMUNIX
	register struct process	*process ;
	register struct text	*text ;
# else
	static struct process	process[ NPROC ] ;
	static struct text	text[ NTEXT ] ;
# endif
	int			flinfo ;
	extern struct flags	Flg ;
	extern struct info	Info ;
	extern int		Flmem ;
	extern int		Flkmem ;
	extern int		Flswap ;
# ifndef VMUNIX
	extern char		Sobuf[] ;
# endif
# ifdef VMUNIX
	char			*getcore() ;
# endif
	struct process		*needed(), *mktree() ;

# ifndef TRACE
	(void)nice( -40 ) ;
	(void)setuid( getuid() ) ;
# endif
# ifndef VMUNIX
	setbuf( stdout, Sobuf ) ;
# endif
	/* Decode the flag arguments */
	flagdecode( argc, argv ) ;	
	/* Open the cpu physical memory, kernel virtual memory and swap device*/
	Flmem = openfile( FILE_MEM ) ;	
	Flkmem = openfile( FILE_KMEM ) ;
	Flswap = openfile( FILE_SWAP ) ;
	if ( Flg.flg_i )
	{	/* -i flag for info file initialisation */
		initialise() ;		
		exit( 0 ) ;
	}
	/* Read the information file */
	flinfo = openfile( FILE_INFO ) ;
	if ( read( flinfo, (char*)&Info, sizeof( struct info ) )
	!= sizeof( struct info ) )
	{
		fprintf( stderr, "sps - Can't read info file %s", FILE_INFO ) ;
		sysperror() ;
	}
	(void)close( flinfo ) ;
	/* Find current tty status */
	ttystatus() ;			
	/* Now that we know the available ttys, decode the flags */
	flagsetup() ;			
# ifdef VMUNIX
	process = (struct process*)getcore(Info.i_nproc*sizeof(struct process));
	text = (struct text*)getcore( Info.i_ntext * sizeof( struct text ) ) ;
# endif
	do
	{	/* Read current process and text status */
		readstatus( process, text ) ;
		/* Select those processes to be listed */
		plist = needed( getuid(), 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() ;
			sleep( 5 ) ;
		}
	} 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	<stdio.h>

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

int				thisuid ;
register struct process		*process ;
struct text			*text ;

{
	register struct process	*p ;
# ifdef V7
	register struct process	*pp ;
# endif
	register struct process	*plist ;
	struct process		*lastp ;
	extern struct flags	Flg ;
# ifdef VMUNIX
	extern union userstate	User ;
# endif
	extern struct info	Info ;
	extern struct ttyline	Notty ;
	struct ttyline		*findtty() ;
	char			*getcmd() ;

	plist = (struct process*)0 ;
# ifdef VMUNIX
	lastp = &process[Info.i_nproc] ;
# else
	lastp = &process[NPROC] ;
# endif
	/* 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 ;
# ifdef V7
		/* Compensate for the lack of a parent pointer in the
		   standard V7 proc structure ; here we go through the
		   proc array searching for a process that has the same
		   id as that of the current parent. */
		p->pr_pptr = (struct process*)0 ;
		for ( pp = process ; pp < lastp ; pp++ )
			if ( pp->pr_p.p_pid == p->pr_p.p_ppid )
			{
				p->pr_pptr = pp ;
				break ;
			}
# else
		/* Normalise internal process parent pointers */
		p->pr_pptr = p->pr_p.p_pptr ?
			&process[ p->pr_p.p_pptr - Info.i_proc0 ] :
			(struct process*)0 ;
# endif
		/* 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. */
	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 && !select( p, process, thisuid ))
			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 VMUNIX
			p->pr_vself = User.u_us.u_vm ;
			p->pr_vchild = User.u_us.u_cvm ;
			p->pr_tty = findtty( p ) ;
			p->pr_files = filecount() ;
# else
			getuinfo( p ) ;
			p->pr_tty = findtty( p ) ;
# ifndef V7
			/* Here we simulate the missing SDETACH bit from the
			   proc structure. A process is detached if its parent
			   is detached, or if its parent is process 1 and it
			   is running in the background. */
			p->pr_detach = p->pr_pptr->pr_detach 
				|| (p->pr_pptr->pr_p.p_pid == 1
				   && (p->pr_p.p_flag & SBGRND)) ;
# endif
# endif
		}
		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 && !select_tty( 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 ;
		/* Check for valid process user-id */
		if ( p->pr_p.p_uid >= MAXUSERS )
		{
			fprintf( stderr,
			"sps - Process %d has uid %d exceeding maximum %d\n",
				p->pr_p.p_pid, p->pr_p.p_uid, MAXUSERS ) ;
			p->pr_p.p_uid %= MAXUSERS ;
		}
	}
	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 */
# ifdef VMUNIX
	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 ;
# else
	Summary.sm_ktotal += p->pr_p.p_size ;
	if ( p->pr_p.p_flag & SLOAD )
		Summary.sm_kloaded += p->pr_p.p_size ;
# endif
	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++ ;
# ifdef VMUNIX
		Summary.sm_kbusy += p->pr_p.p_dsize + p->pr_p.p_ssize ;
# else
		Summary.sm_kbusy += p->pr_p.p_size ;
# endif
	}
	/* Now account for their texts */
	if ( !(tp = p->pr_p.p_textp) || !tp->x_count )
		return ;		
	Summary.sm_ktotal += tp->x_size ;
# ifdef VMUNIX
	Summary.sm_kloaded += tp->x_rssize ;
	Summary.sm_kswapped += tp->x_swrss ;
# else
	if ( p->pr_p.p_flag & SLOAD )
		Summary.sm_kloaded += tp->x_size ;
# endif
	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"

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

# 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] ) ;
	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---'
# ifdef VMUNIX

# include	"sps.h"

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

register struct process		*p ;

{
	register struct text	*tp ;
	double			fracmem ;
	int			szptudot ;
	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 ) ;
}

# endif
---END-OF-percentmem.c---
echo Extracting prcmd.c
cat > prcmd.c << '---END-OF-prcmd.c---'
# include	"sps.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 ;

# ifdef VMUNIX
	printf( "%*d ", lpad+5, Flg.flg_f ? p->pr_p.p_ppid :
# else
	printf( "%*d ", lpad+5, Flg.flg_f ? p->pr_pptr->pr_p.p_pid :
# endif
			Flg.flg_g ? p->pr_p.p_pgrp : p->pr_p.p_pid ) ;
	if ( Flg.flg_w )
		printf( "%s\n", p->pr_cmd ) ;
	else
		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 VMUNIX
# define	HERTZ	(Info.i_hz)
# else
# define	HERTZ	HZ
# endif

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

register time_t			time ;

{
	extern struct info	Info ;

	/* Assume more than 1000 hours is in error */
	if ( time < 0 || time > HERTZ*1000L*60L*60L )
	{				
		printf( "     " ) ;	
		return ;
	}
	if ( time < HERTZ*60L*10L )
	{	/* Less than 10 minutes */
		printf( "%3d.%1d", (int)(time/HERTZ),
			(int)(time % HERTZ / (HERTZ/10L)) ) ;
		return ;
	}
	time /= HERTZ ;
	/* 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 ) ;
}
---END-OF-prcpu.c---
echo Extracting prheader.c
cat > prheader.c << '---END-OF-prheader.c---'
# include	"sps.h"

/* PRHEADER - Print a header according to the switches */
prheader ()
{
# ifdef VMUNIX
 	static char		vhdr[] =
	  "Status Fl Nice Virtual Resident %M  Time Child %C Proc# Command\n" ;
 	static char		dhdr[] =
	  " Files    PageFaults Swap BlockI/O Kbytsecs Proc# Command\n" ;
# else
 	static char		vhdr[] =
	  "Status Fl Nice Memory     Time Child Proc# Command\n" ;
# endif
	static char		hdr[] =
	  "Proc# Command\n" ;
	extern struct flags	Flg ;

	printf( "Ty User     " ) ;
# ifdef VMUNIX
	printf( "%s", Flg.flg_v ? vhdr : Flg.flg_d ? dhdr : hdr ) ;
# else
	printf( "%s", Flg.flg_v ? vhdr : hdr ) ;
# endif
}
---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"

/* 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 ;
	char			chbuf[10] ;
	time_t			time ;
	time_t			chtime ;
	extern short		Lastuid ;
	extern struct flags	Flg ;
	extern struct info	Info ;
	char			*waitingfor() ;
# ifdef VMUNIX
	double			percentmem() ;
# endif

	/* List tty name and foreground/background/detached information */
	printf( "%2.2s%c", p->pr_tty->l_name,
		!p->pr_p.p_pgrp ? ' ' :
# ifdef VMUNIX
		p->pr_p.p_flag & SDETACH ? '_' :
# else
# ifndef V7
		p->pr_detach ? '_' :
# endif
# endif
		p->pr_p.p_pgrp == p->pr_tty->l_pgrp ? '.' : ' ' ) ;
	if ( !md  )				
	{	/* If a top-level process, list the user name */
		if ( Info.i_unames[ p->pr_p.p_uid ][0] )
			printf( "%-8.8s ", Info.i_unames[ p->pr_p.p_uid ] ) ;
		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*", md, "" ) ;	
		/* But beware of setuid processes */
		chp = p->pr_p.p_uid == Lastuid ? "" :
			Info.i_unames[ p->pr_p.p_uid ] ;
		md = 8 - md ;
		printf( "%-*.*s", md, md, chp ) ;
	}
	Lastuid = p->pr_p.p_uid ;
# ifdef VMUNIX
	if ( Flg.flg_d )			
	{	/* List disc I/O and paging information */
		if ( !p->pr_upag || p->pr_p.p_stat == SZOMB )
		{
			prcmd( p, 44, 17 ) ;
			return ;
		}
		printf( "%2d %8d+%8d %4d %8d %8D ",
			p->pr_files,
			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,
			MSIZE( ( p->pr_vself.vm_idsrss
				+ p->pr_vself.vm_ixrss )/Info.i_hz ) ) ;
		prcmd( p, 0, 17 ) ;
		return ;
	}
# endif
	if ( !Flg.flg_v )			
	{	/* Not verbose so just list command arguments */
		prcmd( p, 0, 61 ) ;
		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 */
# ifdef VMUNIX
	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 & (SANOM|SUANOM) ? 'A' : ' ' ) ;
# else
	printf( "%c%c%c", p->pr_p.p_flag & SSYS ? 'U' :
		p->pr_p.p_flag & STRC ? 'T' : ' ',
		p->pr_p.p_flag & SSWAP ? 'S' : ' ',
		p->pr_p.p_flag & SULOCK ? 'L' : ' ' ) ;
# endif
	/* 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 )
	{
# ifdef VMUNIX
		prcmd( p, 36, 11 ) ;
# else
		prcmd( p, 23, 13 ) ;
# endif
		return ;
	}					
	/* List process and text virtual sizes */
# ifdef VMUNIX
	printf( "%4d", MSIZE( p->pr_p.p_dsize + p->pr_p.p_ssize ) ) ;
	if ( tp = p->pr_p.p_textp )
		printf( "+%3d ", MSIZE( tp->x_size ) ) ;
	else
		printf( "     " ) ;
	/* List process and text real sizes */
	printf( "%4d", MSIZE( p->pr_p.p_rssize ) ) ;
	if ( tp )
		printf( "+%3d %2.0f ", MSIZE( tp->x_rssize ), percentmem( p ) );
	else
		printf( "        " ) ;
# else
	printf( " " ) ;
	prmem( p->pr_p.p_size ) ;
	if ( tp = p->pr_p.p_textp )
	{
		printf( "+" ) ;
		prmem( tp->x_size ) ;
	}
	else
		printf( "     " ) ;
	printf( " " ) ;
# endif
	/* List information obtained from the upage. This includes the process
	   times and command arguments. */
	if ( !p->pr_upag )
	{
# ifdef VMUNIX
		prcmd( p, 15, 11 ) ;
# else
		prcmd( p, 12, 24 ) ;
# endif
		return ;
	}					
	/* List process time information */
# ifdef VMUNIX
	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 ;
# else
	time   = Flg.flg_q ? p->pr_utime : p->pr_utime + p->pr_stime ;
	chtime = Flg.flg_q ? p->pr_cutime : p->pr_cutime + p->pr_cstime ;
# endif
	prcpu( time ) ;
	if ( chtime != 0L )
	{
		printf( "+" ) ;
		prcpu( chtime ) ;
	}
	else
		printf( "      " ) ;
# ifdef VMUNIX
	if ( time )
		printf( " %2.0f ", p->pr_p.p_pctcpu * 100.0 ) ;
	else
		printf( "    " ) ;
# endif
	/* Finally, list the process command arguments. */
# ifdef VMUNIX
	prcmd( p, 0, 11 ) ;			
# else
	prcmd( p, 1, 24 ) ;			
# endif
}

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

# ifndef VMUNIX

/* PRMEM - List the memory size of a given process */
prmem ( size )

register int			size ;

{
# ifdef M68000
	printf( "%4d", MSIZE( size ) ) ;
# else
	printf( "%2d.%1d", (size / 16) & 07777, (size % 16) * 10/64 ) ;
# endif
}

# endif
---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(
# ifdef VMUNIX
"%D (%Dk) processes, %D (%Dk) busy, %D (%Dk) loaded, %D (%Dk) swapped\n",
# else
"%D (%Dk) processes, %D (%Dk) busy, %D (%Dk) loaded, %D swapped\n",
# endif
		Summary.sm_ntotal, MSIZE( Summary.sm_ktotal ),
		Summary.sm_nbusy, MSIZE( Summary.sm_kbusy ),
		Summary.sm_nloaded, MSIZE( Summary.sm_kloaded ),
# ifdef VMUNIX
		Summary.sm_nswapped, MSIZE( Summary.sm_kswapped ) ) ;
# else
		Summary.sm_nswapped ) ;
# endif
	Summary.sm_ntotal = Summary.sm_ktotal = 0L ;
	Summary.sm_nbusy = Summary.sm_kbusy = 0L ;
	Summary.sm_nloaded = Summary.sm_kloaded = 0L ;
# ifdef VMUNIX
	Summary.sm_nswapped = Summary.sm_kswapped = 0L ;
# else
	Summary.sm_nswapped = 0L ;
# endif
}
---END-OF-prsummary.c---
echo Extracting readstatus.c
cat > readstatus.c << '---END-OF-readstatus.c---'
# include	"sps.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 ;
# ifdef VMUNIX
	register struct proc	*p0 ;
# else
	static struct proc	p0[ NPROC ] ;
# endif
	register struct process	*pr ;
	extern struct info	Info ;
	extern int		Flkmem ;
	long			lseek() ;
# ifdef VMUNIX
	char			*getcore() ;
# endif

	/* Read current text information */
	(void)lseek( Flkmem, (long)Info.i_text0, 0 ) ;
# ifdef VMUNIX
	(void)read( Flkmem, (char*)text, Info.i_ntext * sizeof( struct text ) );
# else
	(void)read( Flkmem, (char*)text, NTEXT * sizeof( struct text ) );
# endif
	/* Read current process information */
# ifdef VMUNIX
	p0 = (struct proc*)getcore( sizeof( struct proc )*Info.i_nproc ) ;
# endif
	(void)lseek( Flkmem, (long)Info.i_proc0, 0 ) ;
# ifdef VMUNIX
	(void)read( Flkmem, (char*)p0, Info.i_nproc * sizeof( struct proc ) ) ;
	/* 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 ) ;
# else
	(void)read( Flkmem, (char*)p0, NPROC * sizeof( struct proc ) ) ;
	for ( p = p0, pr = process ; pr < &process[NPROC] ; p++, pr++ )
		pr->pr_p = *p ;
# endif
}
---END-OF-readstatus.c---
echo Extracting readsymbols.c
cat > readsymbols.c << '---END-OF-readsymbols.c---'
# include	"sps.h"
# ifdef VMUNIX
# include	<nlist.h>
# else
# include	<a.out.h>
# endif
# include	<stdio.h>

# ifndef NCPS
# define	NCPS		8
# endif

/* READSYMBOLS - 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.
*/
readsymbols ()
{
	register struct nlist	*np ;
	register struct symbol	*s ;
	register struct nlist	*np0 ;
	static char		file_symbol[] = FILE_SYMBOL ;
	extern struct symbol	Symbollist[] ;
	extern int		Flkmem ;
	char			*getcore() ;
	long			lseek() ;
# ifndef VMUNIX
	char			*strncpy() ;
# endif

	/* 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++ )
	{					
# ifdef VMUNIX
		np->n_name = s->s_kname ;	
		np[1].n_name = (char*)0 ;	
# else
		(void)strncpy( np->n_name, s->s_kname, NCPS ) ;
		np[1].n_name[0] = '\0' ;
# endif
		np->n_value = 0 ;
	}
	/* Get kernel addresses */
	nlist( file_symbol, np0 ) ;		
# ifdef VMUNIX
	if ( np0[0].n_value == -1 )
	{
		fprintf( stderr,
			"sps - Can't read symbol file %s", file_symbol ) ;
		sysperror() ;
	}
# endif
	/* Copy addresses and values into the `Info' structure */
	for ( s = Symbollist, np = np0 ; s->s_kname ; s++, np++ )
	{					
		if ( !np->n_value )		
		{
# ifdef VMUNIX
			fprintf( stderr, "sps - Can't find symbol %s in %s\n",
				np->n_name, file_symbol ) ;
# else
			fprintf( stderr,
				"sps - Can't find symbol %*.*s in %s\n",
				NCPS, NCPS, np->n_name, file_symbol ) ;
# endif
			*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 */
		(void)lseek( Flkmem, (long)np->n_value, 0 ) ;
		/* 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-readsymbols.c---
echo Extracting select.c
cat > select.c << '---END-OF-select.c---'
# include	"sps.h"

/*
** SELECT - Given a process structure, this procedure decides whether
** the process is a candidate for printing.
*/
select ( 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 ;
	}
# ifdef VMUNIX
	if ( Flg.flg_D && p->pr_p.p_pgrp && (p->pr_p.p_flag & SDETACH) )
		return ( 1 ) ;
# endif
	return ( 0 ) ;
}
---END-OF-select.c---
echo Extracting select_tty.c
cat > select_tty.c << '---END-OF-select_tty.c---'
# include	"sps.h"

/* SELECT_TTY - Decides whether this process is interesting for its tty */
select_tty ( 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-select_tty.c---
echo Extracting sps.h
cat > sps.h << '---END-OF-sps.h---'
# ifdef VMUNIX
# include	<h/param.h>
# include	<h/dir.h>
# include	<h/user.h>
# include	<h/proc.h>
# include	<h/pte.h>
# include	<h/vm.h>
# include	<h/inode.h>
# include	<h/file.h>
# include	<h/buf.h>
# include	<h/text.h>
# include	<h/tty.h>
# include	<h/conf.h>
# else
# include	<param.h>
# include	<dir.h>
# include	<user.h>
# include	<proc.h>
# include	<inode.h>
# include	<file.h>
# include	<buf.h>
# include	<text.h>
# include	<tty.h>
# include	<conf.h>
# endif

/* Max # users to be considered ... */
# define	MAXUSERS	300
/* Max # ttys to be considered ... */
# define	MAXTTYS		50
/* Max user name length ... */
# define	UNAMELEN	8
/* Max process-id not to be considered busy ... */
# define	MSPID		2

# ifdef VMUNIX
/* # process' virtual pages to be read to determine argument list ... */
# define	MAXARGPG	5
/* Length of each process' page table to be obtained ... */
# define	PAGETABLESIZE	(UPAGES + MAXARGPG)
# endif

/* Convert clicks to kbytes ... */
# ifdef VMUNIX
# define	KSHIFT		10
# define	MSIZE( s )	( (s) >> (KSHIFT - PGSHIFT) )
# else
# ifdef M68000
# define	MSIZE( s )	( (s) << 1 )
# else
# define	MSIZE( s )	( (s) >> 4 )
# endif
# endif

/* 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/swap"	/* Swap/paging device */
# ifdef VMUNIX
# define	FILE_SYMBOL	"/vmunix"	/* Symbol file for nlist() */
# else
# define	FILE_SYMBOL	"/unix"
# endif
# define	FILE_DEV	"/dev"		/* Directory of tty entries */
# ifdef TRACE
# define	FILE_INFO	"./spsinfo"	/* Information file */
# else
# define	FILE_INFO	"/etc/spsinfo"
# endif

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

/* Format of the standard information file maintained by sps ... */
/*
** This structure is filled in at initialisation time. Otherwise it
** is read in whenever sps is invoked. Keeping known kernel addresses
** in a file is much faster than accessing them every time the program
** 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 ... */
# ifdef VMUNIX
	int			i_nproc ;	/* length of process table */
	int			i_ntext ;	/* length of text table */
	int			i_ninode ;	/* length of inode table */
	int			i_nswbuf ;	/* # swap buffers */
	int			i_nbuf ;	/* # i/o buffers */
	int			i_hz ;		/* clock rate on this machine */
	int			i_ecmx ;	/* max physical memory address*/
	struct tty		*i_pt_tty ;	/* pseudo-tty device base */
	struct pte		*i_usrptmap ;	/* page table map */
	struct pte		*i_usrpt ;	/* page table map */
	struct buf		*i_swbuf0 ;	/* address of swap buffers */
# ifdef	CHAOS
	caddr_t			i_Chconntab ;	/* Chaos connection table */
# endif
# else
	int			i_nswap ;
	int			i_swplo ;
# endif
	struct proc		*i_proc0 ;	/* address of process table */
	struct text		*i_text0 ;	/* address of text table */
	struct inode		*i_inode0 ;	/* address of inode table */
	struct buf		*i_buf0 ;	/* address of i/o buffers */
	struct cdevsw		*i_cdevsw ;	/* device switch to find ttys */
	/* These remaining kernel addresses are associated with
	   process wait states ... */
# ifdef VMUNIX
	caddr_t			i_fltab ;
	caddr_t			i_rswbuf ;
# ifdef	CHAOS
	caddr_t			i_chrfclist ;
# endif	
# else
	caddr_t			i_swap1 ;
	caddr_t			i_swap2 ;
# endif
	caddr_t			i_va_softc ;
	caddr_t			i_bfreeli ;
	caddr_t			i_lpdt ;
	caddr_t			i_lbolt ;
	caddr_t			i_runin ;
	caddr_t			i_runout ;
	caddr_t			i_ipc ;
	caddr_t			i_u ;
	caddr_t			i_chtbuf ;
	caddr_t			i_rhtbuf ;
	/* User names, indexed by uid ... */
	char			i_unames[ MAXUSERS ][ UNAMELEN ] ;
	/* Tty device info ... */
	struct ttyline		i_ttyline[ MAXTTYS ] ;
} ;

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

/* 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 ;
# ifdef VMUNIX
	struct vtimes		pr_vself ;	/* Read from upage for self */
	struct vtimes		pr_vchild ;	/* ... and the children */
	int			pr_files ;	/* # open files */
# else
	time_t			pr_utime ;
	time_t			pr_stime ;
	time_t			pr_cutime ;
	time_t			pr_cstime ;
	dev_t			pr_ttyd ;
# endif
	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() */
# ifndef VMUNIX
# ifndef V7
	int			pr_detach:1 ;	/* Simulate the SDETACH bit */
# endif
# endif
} ;

/* To hold 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 */
} ;

/* To hold global information specifed by options in the arg list ... */
struct flags
{
# ifdef VMUNIX
	int			flg_d:1 ;	/* disc orientated output */
# endif
	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 */
	int			flg_q:1 ;	/* show user time only */
	int			flg_r:1 ;	/* repeat output */
	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 */
# ifdef VMUNIX
	int			flg_D:1 ;	/* print detached processes */
# endif
	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 */
} ;

/* 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 */
# ifdef VMUNIX
	long			sm_kswapped ;	/* Size totally swapped out */
# endif
} ;

/*
** 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 */
} ;
---END-OF-sps.h---
echo Extracting ttyinit.c
cat > ttyinit.c << '---END-OF-ttyinit.c---'
# include	"sps.h"
# include	<stdio.h>
# include	<sys/stat.h>

/* TTYINIT - Initialise the tty part of the info structure */
ttyinit ()
{
	register struct ttyline	*lp ;
	FILE			*fd ;
	struct direct		dirbuf ;
	struct stat		statbuf ;
	static char		file_dev[] = FILE_DEV ;
	extern struct info	Info ;
	extern int		Flkmem ;
	FILE			*fdopen() ;

	lp = Info.i_ttyline ;
	fd = fdopen( openfile( file_dev ), "r" ) ;
	if ( chdir( file_dev ) < 0 )
		prexit( "sps - Can't chdir to %s\n", file_dev ) ;
	/* Read all entries in the device directory, looking for ttys */
	while ( fread( (char*)&dirbuf, sizeof( struct direct ), 1, fd ) == 1 )
	{
		if ( !dirbuf.d_ino )
			continue ;
		/* Skip entries that do not match "tty" or "console" */
		if ( strncmp( "tty", dirbuf.d_name, 3 )
		&&   strcmp( "console", dirbuf.d_name ) )
			continue ;
		/* Skip "tty" itself */
		if ( dirbuf.d_name[3] == '\0' )
			continue ;
# ifdef CHAOS
		/* Skip chaos ttys ; they are accessed during ttystatus() */
		if ( dirbuf.d_name[ sizeof( "tty" ) - 1 ] == 'C' )
			continue ;
# endif
		if ( lp >= &Info.i_ttyline[ MAXTTYS ] )
			prexit( "sps - Too many ttys in %s\n", file_dev ) ;
		/* Copy the tty name into the information entry */
		if ( !strcmp( dirbuf.d_name, "console" ) )
			lp->l_name[0] = 'c', lp->l_name[1] = 'o' ;
		else
		{
			lp->l_name[0] = dirbuf.d_name[3] ;
			lp->l_name[1] = dirbuf.d_name[4] ;
		}
		/* Ensure that this tty is actually a valid character device */
		if ( stat( dirbuf.d_name, &statbuf ) < 0 )
			continue ;
		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 ;
		(void)lseek( Flkmem,
			(long)&Info.i_cdevsw[ major( statbuf.st_rdev ) ].d_ttys,
			0 ) ;
		(void)read( Flkmem, (char*)&lp->l_addr, sizeof( lp->l_addr ) ) ;
		lp->l_addr += (int)minor( statbuf.st_rdev ) ;
		lp++ ;
	}
	(void)fclose( fd ) ;
}
---END-OF-ttyinit.c---
echo Extracting ttystatus.c
cat > ttystatus.c << '---END-OF-ttystatus.c---'
# include	"sps.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 ;
	long			lseek() ;

	if ( Flg.flg_y )
# if VMUNIX | M68000
		printf( "Ty   Dev       Addr Rawq Canq Outq  Pgrp\n" ) ;
# else
		printf( "Ty   Dev     Addr Rawq Canq Outq  Pgrp\n" ) ;
# endif
	lp = Info.i_ttyline ;
# ifdef CHAOS
	while ( lp->l_name[0] && lp->l_name[0] != 'C' )
# else
	while ( lp->l_name[0] )
# endif
	{
		(void)lseek( Flkmem, (long)lp->l_addr, 0 ) ;
		(void)read( Flkmem, (char*)&tty, sizeof( struct tty ) ) ;
		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 ;
# if VMUNIX | M68000
	printf( "%-2.2s %2d,%2d 0x%08x %4d %4d %4d %5d\n",
# else
	printf( "%-2.2s %2d,%2d 0o%06o %4d %4d %4d %5d\n",
# endif
		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 ;

	(void)lseek( Flkmem, (long)Info.i_Chconntab, 0 ) ;
	(void)read( Flkmem, (char*)conntab, sizeof( conntab ) ) ;
	for ( i = 0, cnp = conntab ; cnp < &conntab[CHNCONNS] ; i++, cnp++ )
	{
		if ( !*cnp )
			continue ;
		(void)lseek( Flkmem, (long)*cnp, 0 ) ;
		(void)read( Flkmem, (char*)&conn, sizeof( struct connection ) );
		if ( !(conn.cn_flags & CHTTY) )
			continue ;
		(void)lseek( Flkmem, (long)conn.cn_ttyp, 0 ) ;
		(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"

/* WAITINGFOR - Determine what a process is waiting for and describe it. */

# define	INRANGE( w, a1, a2 ) \
			( (caddr_t)(a1) <= (w) && (w) < (caddr_t)(a2) )

# ifdef VMUNIX
/* Max # active pty devices (this # is unobtainable from the system) ... */
# define	NPTY		16
# endif

char	*waitingfor ( p )

struct process			*p ;

{
	register caddr_t	w ;
	register struct ttyline	*lp ;
	register struct symbol	*s ;
	register char		*cp ;
	static char		wbuf[ 7 ] ;
	extern struct info	Info ;
	extern struct symbol	Symbollist[] ;
	char			*sprintf() ;

	w = p->pr_p.p_wchan ;
	if ( !w )
		return ( "null" ) ;
# ifdef VMUNIX
	if ( INRANGE( w, Info.i_proc0, &Info.i_proc0[ Info.i_nproc ] ) )
		return ( p->pr_p.p_flag & SNOVM ? "vfork" : "child" ) ;
	if ( INRANGE( w, Info.i_swbuf0, &Info.i_swbuf0[ Info.i_nswbuf ] ) )
		return ( "swap" ) ;
	if ( INRANGE( w, Info.i_buf0, &Info.i_buf0[ Info.i_nbuf ] ) )
		return ( "discio" ) ;
	if ( INRANGE( w, Info.i_text0, &Info.i_text0[ Info.i_ntext ] ) )
		return ( "swtext" ) ;
# else
	if ( INRANGE( w, Info.i_proc0, &Info.i_proc0[ NPROC ] ) )
		return ( "child" ) ;
	if ( INRANGE( w, Info.i_buf0, &Info.i_buf0[ NBUF ] ) )
		return ( "discio" ) ;
# endif
	/* Is the process waiting for a tty ? */
	for ( lp = Info.i_ttyline ; lp->l_name[0] ; lp++ )
		if ( INRANGE( w, &lp->l_addr[0], &lp->l_addr[1] ) )
		{
			w -= (int)lp->l_addr ;
			cp = w == (caddr_t)&((struct tty*)0)->t_rawq ? "rtty??"
			   : w == (caddr_t)&((struct tty*)0)->t_outq ? "wtty??"
			   : w == (caddr_t)&((struct tty*)0)->t_state ? "otty??"
				: "?tty??" ;
			cp[4] = lp->l_name[0] ;
			cp[5] = lp->l_name[1] ;
			return ( cp ) ;
		}
	/* Is the process waiting for an inode ? */
# ifdef VMUNIX
	if ( INRANGE( w, Info.i_inode0, &Info.i_inode0[Info.i_ninode] ) )
# else
	if ( INRANGE( w, Info.i_inode0, &Info.i_inode0[NINODE] ) )
# endif
		switch ( ((int)w - (int)Info.i_inode0) % sizeof( struct inode ))
		{
			case 1 :
				return ( "wpipe" ) ;
			case 2 :
				return ( "rpipe" ) ;
# ifdef VMUNIX
			case (int)&((struct inode*)0)->i_un.i_group.g_datq :
				return ( "rmux" ) ;
# endif
			default :
				return ( "inode" ) ;
		}
# ifdef VMUNIX
# if NPTY > 0
	/* Is the process waiting for a pseudo tty ? */
	if ( INRANGE( w, Info.i_pt_tty, &Info.i_pt_tty[NPTY] ) )
		switch ( ((int)w - (int)Info.i_pt_tty) % sizeof( struct tty ) )
		{
			case (int)&((struct tty*)0)->t_rawq :
				return ( "rspty" ) ;
			case (int)&((struct tty*)0)->t_rawq.c_cf :
				return ( "wcpty" ) ;
			case (int)&((struct tty*)0)->t_outq :
				return ( "wspty" ) ;
			case (int)&((struct tty*)0)->t_outq.c_cf :
				return ( "rcpty" ) ;
			default :
				return ( "??pty" ) ;
		}
# endif
# 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. */
# ifdef VMUNIX
	(void)sprintf( wbuf, "x%05x", w - 0x80000000 ) ;
# else
	(void)sprintf( wbuf, "x%05x", w ) ;
# endif
	return ( wbuf ) ;
}
---END-OF-waitingfor.c---