#ifndef lint
static	char *sccsid = "@(#)ps.c	4.21 (Berkeley) 2/9/83";
#endif

/*
 * ps -- Display process related information upon stdout.
 *
 *	History -- present to past:
 *	---------------------------
 *	850604	jht -- Adapted for Valid's 4.2bsd:
 *			o Requires the 'cc -Dxxx' options to use includes, esp:
 *			  USE_FIXED_POINT -- p_pctcpu within proc-structure;
 *			  QUOTA		  -- u_quota & u_qflags in u.
 *			  s32		  -- Differentiate from vax, et al.
 *
 *			o Allow for u_uid to be full 5-digits (u_short);
 *
 *	831215	Origional is probably 4.1c, adapted by Jamie Markevitz.
 *
 */

#define	min(a,b)	(a < b ? a : b)		/* Inline rather than function*/

/*
 * Need to put these in front so the -UKERNEL in the make file
 * won't prevent the right ones being included.
 */
#include "../../h/types.h"
#include "../../machine/cpu.h"
#include "../../machine/param.h"
#include "../../h/dmap.h"

#include "../../usr.include/stdio.h"
#include "../../usr.include/ctype.h"

#ifdef	s32
#include "../../usr.include/a.out.h"
#else	s32
#include <nlist.h>
#endif	s32

#include "../../usr.include/pwd.h"
#include "../../h/param.h"
#include "../../h/proc.h"
#include "../../h/vm.h"
#include "../../h/text.h"
#include "../../h/stat.h"
#include "../../h/mbuf.h"
#include "../../h/user.h"
#include "../../usr.include/math.h"

/*
 * "What to get" table for nlist-ing
 * stuff from the kernel RAM.
 */
struct nlist nl[] = {
	{ "proc" },		/* Process table */
#define	X_PROC		0

	{ "Usrptmap" },		/* Process-slot to page-table-entry map */
#define	X_USRPTMA	1

	{ "usrpt" },
#define	X_USRPT		2

	{ "text" },		/* Describes executing or sticky runnables */
#define	X_TEXT		3

	{ "nswap" },		/* Capacity of the aggregate swap areas */
#define	X_NSWAP		4

	{ "maxslp" },
#define	X_MAXSLP	5

	{ "ccpu" },
#define	X_CCPU		6

	{ "ecmx" },
#define	X_ECMX		7

	{ "nproc" },		/* Max # of processes the kernel will support */
#define	X_NPROC		8

	{ "ntext" },		/* # buckets for executable files */
#define	X_NTEXT		9

#ifdef	s32
	{ "dmmin" },
#define	X_DMMIN		10

	{ "dmmax" },
#define	X_DMMAX		11

#endif	s32

	{ "" },
};

/*
 * Prefixes of device names to ignore while
 * hunting for tty devices.
 */
struct	blkDevNames {
	char	*namep;
	int	len;
} blkDevNames[] = {

	{"rim",	3},	/* Valid's Rimfire ANSI Winchester interface	*/
	{"is",	2},	/* Valid's Interphase SMD interface	 	*/
	{"mt",	2},	/* Nominal magtape: Rimfire-45 interface	*/

	{"rp",	2},	/* Old UNIBUS Winchester			*/
	{"up",	2},	/* Often the System Industries 9400/9700 Winch.	*/
	{"rk",	2},	/* UNIBUS removable disk cartridge		*/
	{"rm",	2},	/* UNIBUS/MASSBUS high performance Winchester	*/
	{"rf",	2},	/* Floppy; or old UNIBUS head-per-track RF11	*/
	{(char *)NULL, 0}
};

struct	savcom {
	union {
		struct	lsav *lp;
		float	u_pctcpu;
		struct	vsav *vp;
		int	s_ssiz;
	} s_un;
	struct	asav *ap;
} *savcom;

struct	asav {
	char	*a_cmdp;	/* Ptr to the associated command-string	*/
	int	a_flag;
	short	a_stat;
	ushort	a_uid, a_pid;
	short	a_nice,
		a_pri,
		a_slptime,
		a_time;
	size_t	a_size, a_rss, a_tsiz, a_txtrss;
	short	a_xccount;
	char	a_tty[MAXNAMLEN+1];
	dev_t	a_ttyd;
	time_t	a_cpu;		/* u.u_ru.ru_utime + u.u_ru.ru_stime	*/
	size_t	a_maxrss;	/* Maximum resident-set-size		*/
};

char	*lHdr, *wlHdr;
struct	lsav {			/* Saved info for long-form display	*/
	short	l_ppid;		/* Parent process's pid			*/
	char	l_cpu;		/* p_cpu: cpu usage for scheduling	*/
	int	l_addr;		/* u-page pageframe #			*/
	caddr_t	l_wchan;	/* What process is waiting upon, if  blocked */
	/*
	 * The following are only for 'w' or 'ww' display:
	 */
	caddr_t	lw_rpcresults;	/* Ptr to results at RPC server	*/
	u_char	lw_rpcflags;	/* RPC server flags		*/
	u_char	lw_rpcerror;	/* RPC server error-code	*/
};

char	*uHdr, *wuHdr;
char	*sHdr, *wsHdr;

char	*vHdr;
struct	vsav {			/* Saved info for virtual datums */
	u_int	v_majflt;
	size_t	v_swrss, v_txtswrss;
	float	v_pctcpu;
};

struct	proc proc[8];		/* 8 == a few, for less syscalls */
struct	proc *mproc;
struct	text *text;

union {
	struct	user user;
	char	upages[UPAGES][NBPG];
} user;
#define u	user.user	/* Kernel syntax... */

#define clear(x) 	((int)x & 0x7fffffff)

int	chkpid;

int	aFlag,	/* Display info for ALL user processes		*/
	cFlag,	/* Get command-string from u.u_comm		*/
	eFlag,	/* Get command-string from argv-space		*/
	gFlag,	/* DUNNO...					*/
	kFlag,	/* Specifies file from which to obtain mem-file	*/
	lFlag,	/* Display info in LONG, detailed format	*/
	sFlag,	/* Get command info from u.u_stack		*/
	uFlag,	/* Display info about Userness of processes	*/
	vFlag,	/* Display info about virtualness; Implies cFlag	*/
	xFlag;	/* Display processes w/o a process-group tty	*/

char	*tptr;
char	*gettty(), *getcmd(), *getname(), *savestr(), *alloc(), *state();
char	*rindex(), *calloc(), *sbrk(), *strcpy(), *strcat(), *strncat();

long	lseek();
double	pcpu(), pmem();
int	pscomp();
int	nswap, maxslp;
struct	text *atext;
double	ccpu;
int	ecmx;
struct	pte *Usrptma, *usrpt;
int	nproc, ntext;
int	dmmin, dmmax;

struct	ttys {				/* Bucket for tty information */
	char	name[MAXNAMLEN+1];
	dev_t	ttyd;
	struct	ttys *next;
	struct	ttys *cand;
} *allttys, *cand[16];

int	npr;

int	cmdstart;	/* Column # where command-strings begin	*/
int	twidth;		/* Width of the display terminal	*/

/*
 * Pointers to pathname strings used by open().
 * These may be the indicated defaults
 * or command-line specified alternatives.
 */
char	*kmemf,		/* "/dev/kmem"	*/
	*memf,		/* "/dev/mem"	*/
	*swapf,		/* "/dev/swap"	*/
	*nlistf;	/* "/vmunix"	*/

int	kmem, mem, swap = -1;
int	rawcpu, sumcpu;

int	pcbpf;		/* Pageframe # for the arglist	*/
int	argaddr;

extern	char _sobuf[];

#define	pgtok(a)	((a)*CLBYTES/1024)

#ifdef s32
int	psdebug = 0;
#endif s32

char	**ourName;	/* Save our invocation name */

main(argc, argv)
	char	**argv;
{
	register int	i, j;
	register char	*ap;
	unsigned int	uid;
	off_t		procp;

	twidth = 80;
	argc--, ourName = argv++;
	if (argc > 0) {
		ap = argv[0];
		while (*ap) switch (*ap++) {

#ifdef s32
		case 'J':
		{
			extern int nldebug;

			nldebug = 1;
			break;
		}
		case 'j':
			psdebug = 1;
			break;
#endif s32
		case 'C':
			rawcpu++;
			break;
		case 'S':
			sumcpu++;
			break;
		case 'a':
			aFlag++;
			break;
		case 'c':
			cFlag = !cFlag;
			break;
		case 'e':
			eFlag++;
			break;
		case 'g':
			gFlag++;
			break;
		case 'k':
			kFlag++;
			break;
		case 'l':
			lFlag++;
			break;
		case 's':
			sFlag++;
			break;
		case 't':
			if (*ap)
				tptr = ap;
			aFlag++;
			gFlag++;
			if (tptr && *tptr == '?')
				xFlag++;
			while (*ap)
				ap++;
			break;
		case 'u': 
			uFlag++;
			break;
		case 'v':
			cFlag = 1;
			vFlag++;
			break;
		case 'w':
			if (twidth == 80)
				twidth = 132;
			else
				twidth = BUFSIZ;
			break;
		case 'x':
			xFlag++;
			break;
		default:
			if (!isdigit(ap[-1]))
				break;
			chkpid = atoi(--ap);
			*ap = 0;
			aFlag++;
			xFlag++;
			break;
		}
	}
	openfiles(argc, argv);
	getkvars(argc, argv);
	if (chdir("/dev") < 0) {
		perror("/dev");
		exit(1);
	}
	getdev();
	uid = getuid();
	printHdr();
	procp = getw(nl[X_PROC].n_value);
if (psdebug)
printf("@proc = 0x%x\n", procp);
	nproc = getw(nl[X_NPROC].n_value);
if (psdebug)
printf("@nproc = %d\n", nproc);
	savcom = (struct savcom *)calloc(nproc, sizeof (*savcom));
	for (i=0; i<nproc; i += 8) {
		klseek(kmem, (long)procp, 0);
		j = nproc - i;
		if (j > 8)
			j = 8;
		j *= sizeof (struct proc);
		if (read(kmem, (char *)proc, j) != j) {
			cantread("proc table", kmemf);
			exit(1);
		}
		procp += j;
		for (j = j / sizeof (struct proc) - 1; j >= 0; j--) {
			mproc = &proc[j];
if (psdebug)
{
register unsigned long *lp = (unsigned long *)mproc;
register int count = sizeof(struct proc) / sizeof(unsigned long);
printf("p%d:", mproc->p_pid);
while (count--)
printf("\t%x", *lp++);
printf("\n");
}
			if (mproc->p_stat == 0 ||
			    mproc->p_pgrp == 0 && xFlag == 0)
				continue;
			if (tptr == 0 && gFlag == 0 && xFlag == 0 &&
			    mproc->p_ppid == 1)
				continue;
			if (uid != mproc->p_uid && aFlag==0 ||
			    chkpid != 0 && chkpid != mproc->p_pid)
				continue;
			if (vFlag && gFlag == 0 && xFlag == 0) {
				if (mproc->p_stat == SZOMB ||
				    mproc->p_flag&SWEXIT)
					continue;
				if (mproc->p_slptime > MAXSLP &&
				    (mproc->p_stat == SSLEEP ||
				     mproc->p_stat == SSTOP))
				continue;
			}
if (psdebug)
printf("Saving...\n");
			save();
		}
	}
	qsort(savcom, npr, sizeof(savcom[0]), pscomp);
	for (i=0; i<npr; i++) {
		register struct savcom *sp = &savcom[i];
		if (lFlag)
			lpr(sp);
		else if (vFlag)
			vpr(sp);
		else if (uFlag)
			upr(sp);
		else
			spr(sp);
		if (sp->ap->a_flag & SWEXIT)
			printf(" <exiting>");
		else if (sp->ap->a_stat == SZOMB)
			printf(" <defunct>");
		else if (sp->ap->a_pid == 0)
			printf(" swapper");
		else if (sp->ap->a_pid == 2)
			printf(" pagedaemon");
#ifndef s32
		else if (sp->ap->a_pid == 3 && sp->ap->a_flag & SSYS)
			printf(" ip input");
#endif s32
		else
			printf(" %.*s", twidth - cmdstart - 2, sp->ap->a_cmdp);
		printf("\n");
	}
	exit(npr == 0);
}

getw(loc)
	unsigned long loc;
{
	long word;

	klseek(kmem, (long)loc, 0);
	if (read(kmem, (char *)&word, sizeof (word)) != sizeof (word))
		printf("error reading kmem at %x\n", loc);
	return (word);
}

klseek(fd, loc, off)
	int fd;
	long loc;
	int off;
{

	if (kFlag)
		loc &= 0x7fffffff;
	(void) lseek(fd, (long)loc, off);
}

openfiles(argc, argv)
	char **argv;
{

	kmemf = "/dev/kmem";
	if (kFlag)
		kmemf = argc > 2 ? argv[2] : "/vmcore";
	kmem = open(kmemf, 0);
	if (kmem < 0) {
		perror(kmemf);
		exit(1);
	}
	if (kFlag)  {
		mem = kmem;
		memf = kmemf;
	} else {
		memf = "/dev/mem";
		mem = open(memf, 0);
		if (mem < 0) {
			perror(memf);
			exit(1);
		}
	}
	if (kFlag == 0 || argc > 3) {
		swapf = argc>3 ? argv[3]: "/dev/drum";
		swap = open(swapf, 0);
		if (swap < 0) {
			perror(swapf);
			exit(1);
		}
	}
}

getkvars(argc, argv)
	char **argv;
{
	register struct nlist *nlp;

	nlistf = argc > 1 ? argv[1] : "/vmunix";
#ifdef s32
	if (argc > 1 || vmnlist("/dev/mem", nl) == -1 || nl[0].n_type == 0)
		nlist(nlistf, nl);
#else s32
	nlist(nlistf, nl);
#endif s32
	if (nl[0].n_type == 0) {
		fprintf(stderr, "%s: No namelist\n", nlistf);
		exit(1);
	}
	if (kFlag)
		for (nlp = nl; nlp < &nl[sizeof (nl)/sizeof (nl[0])]; nlp++)
			nlp->n_value = clear(nlp->n_value);
#ifndef s32
	usrpt = (struct pte *)nl[X_USRPT].n_value;	/* don't clear!! */
#endif s32
	Usrptma = (struct pte *)nl[X_USRPTMA].n_value;
#ifdef s32
	klseek(kmem, (long)nl[X_USRPT].n_value, 0);
	if (read(kmem, (char *)&usrpt, sizeof (usrpt)) != sizeof (usrpt)) {
		cantread("usrpt", kmemf);
		exit(1);
	}
if (psdebug)
printf("@usrpt = 0x%x\n", usrpt);
#endif s32
	klseek(kmem, (long)nl[X_NSWAP].n_value, 0);
	if (read(kmem, (char *)&nswap, sizeof (nswap)) != sizeof (nswap)) {
		cantread("nswap", kmemf);
		exit(1);
	}
if (psdebug)
printf("@nswap = %d\n", nswap);
	klseek(kmem, (long)nl[X_MAXSLP].n_value, 0);
	if (read(kmem, (char *)&maxslp, sizeof (maxslp)) != sizeof (maxslp)) {
		cantread("maxslp", kmemf);
		exit(1);
	}
if (psdebug)
printf("@maxslp = %d\n", maxslp);
	klseek(kmem, (long)nl[X_CCPU].n_value, 0);
	if (read(kmem, (char *)&ccpu, sizeof (ccpu)) != sizeof (ccpu)) {
		cantread("ccpu", kmemf);
		exit(1);
	}
if (psdebug)
printf("@ccpu = %g\n", ccpu);
	klseek(kmem, (long)nl[X_ECMX].n_value, 0);
	if (read(kmem, (char *)&ecmx, sizeof (ecmx)) != sizeof (ecmx)) {
		cantread("ecmx", kmemf);
		exit(1);
	}
if (psdebug)
printf("@ecmx = 0x%x\n", ecmx);
	if (uFlag || vFlag) {
		ntext = getw(nl[X_NTEXT].n_value);
if (psdebug)
printf("@ntext = %d\n", ntext);
		text = (struct text *)alloc(ntext * sizeof (struct text));
		if (text == 0) {
			fprintf(stderr, "no room for text table\n");
			exit(1);
		}
		atext = (struct text *)getw(nl[X_TEXT].n_value);
if (psdebug)
printf("@text = 0x%x\n", atext);
		klseek(kmem, (long)atext, 0);
		if (read(kmem, (char *)text, ntext * sizeof (struct text))
		    != ntext * sizeof (struct text)) {
			cantread("text table", kmemf);
			exit(1);
		}
	}
	dmmin = getw(nl[X_DMMIN].n_value);
	dmmax = getw(nl[X_DMMAX].n_value);
}

printHdr()
{
	char *Hdr;

	if (sFlag+lFlag+vFlag+uFlag > 1) {
		fprintf(stderr, "ps: specify only one of s,l,v and u\n");
		exit(1);
	}
	Hdr	= lFlag
		/*
		 * "-l" flag's header.
		 */
		? ((twidth>80)
			? wlHdr				/* Xtra long header */
			:  lHdr)
		: (vFlag
			/*
			 * "-v" flag's header.
			 */
			? vHdr
			: (uFlag
				/*
				 * "-u" flag's header.
				 */
				? ((twidth>80)
					? wuHdr		/* Xtra long header */
					:  uHdr)
				/*
				 * Otherwise, use one of the
				 * nominal short-form headers.
				 */
				: ((twidth>80)
					? wsHdr		/* Long "short" header*/
					:  sHdr)));
	if (lFlag+vFlag+uFlag+sFlag == 0)
		Hdr += strlen("SSIZ ");
	cmdstart = strlen(Hdr);
	printf("%s COMMAND\n", Hdr);
	(void) fflush(stdout);
}

cantread(what, fromwhat)
	char *what, *fromwhat;
{

	fprintf(stderr, "ps: error reading %s from %s\n", what, fromwhat);
}

struct	direct	*dbuf;
int	dialbase;

getdev()
{
	register DIR *df;

	dialbase = -1;
	if ((df = opendir(".")) == NULL) {
		fprintf(stderr, "Can't open . in /dev\n");
		exit(1);
	}
	/*
	 * Cycle through the directory,
	 * reading the successive entries.
	 */
	while ((dbuf = readdir(df)) != NULL) 
		maybeTty();
	closedir(df);
}

/*
 * Attempt to avoid stats by guessing minor device
 * numbers from tty names.  Console is known,
 * know that r(hp|up|mt|is|rim) are unlikely,
 * as are different mem's,
 * floppy, null, tty, etc.
 */
maybeTty()
{
	register char		*cp = dbuf->d_name;
	register struct	ttys	*dp;
	int			 x;
	struct	 stat		 stb;

	switch (cp[0]) {

	case 'c':
		if (!strcmp(cp, "console")) {
			x = 0;
			goto donecand;
		}
		/* cu[la]? are possible!?! don't rule them out */
		break;

	case 'd':
		if (!strcmp(cp, "drum"))
			return;
		break;

	case 'f':
		if (!strcmp(cp, "floppy"))
			return;
		break;

	case 'k':
		cp++;
		if (*cp == 'U')
			cp++;
		goto trymem;

	case 'r':	/* Potential character device */

		{	register struct	blkDevNames	*bp;

			/*
			 * Heuristic: look for a (partial) match
			 * in the table of known (non-tty)
			 * block/character device names.
			 */
			for (bp = &blkDevNames[0]; bp->namep; bp++)
				if (!strncmp(cp, bp->namep, bp->len))
					return;

			--cp;	/* Nope, try the block devices */
			for (bp = &blkDevNames[0]; bp->namep; bp++)
				if (!strncmp(cp, bp->namep, bp->len))
					return;
		}
		break;

	case 'm':
trymem:
		if (cp[1] == 'e' && cp[2] == 'm' && cp[3] == 0)
			return;
		/*
		 * We already tested for magtape above at 'r'
		 */
		break;

	case 'n':
		if (!strcmp(cp, "null"))
			return;
		break;

	case 'v':
		if ((cp[1] == 'a' || cp[1] == 'p') && isdigit(cp[2]) &&
		    cp[3] == 0)
			return;
		break;
	}
	cp = dbuf->d_name + dbuf->d_namlen - 1;
	x = 0;
	if (cp[-1] == 'd') {
		if (dialbase == -1) {
			if (stat("ttyd0", &stb) == 0)
				dialbase = stb.st_rdev & 0xF;
			else
				dialbase = -2;
		}
		if (dialbase == -2)
			x = 0;
		else
			x = 11;
	}
	if (cp > dbuf->d_name && isdigit(cp[-1]) && isdigit(*cp))
		x += 10 * (cp[-1] - ' ') + cp[0] - '0';
	else if (*cp >= 'a' && *cp <= 'f')
		x += 10 + *cp - 'a';
	else if (isdigit(*cp))
		x += *cp - '0';
	else
		x = -1;
donecand:
	dp = (struct ttys *)alloc(sizeof (struct ttys));
	(void) strcpy(dp->name, dbuf->d_name);
	dp->next = allttys;
	dp->ttyd = -1;
	allttys  = dp;
	if (x == -1)
		return;
	x	&= 017;
	dp->cand = cand[x];
	cand[x]  = dp;
}

char *
gettty()
{
	register char *p;
	register struct ttys *dp;
	struct stat stb;
	int x;

	if (u.u_ttyp == 0)
		return("?");
	x = u.u_ttyd & 0xF;
	for (dp = cand[x]; dp; dp = dp->cand) {
		if (dp->ttyd == -1) {
			if (stat(dp->name, &stb) == 0 &&
			   (stb.st_mode&S_IFMT)==S_IFCHR)
				dp->ttyd = stb.st_rdev;
			else
				dp->ttyd = -2;
		}
		if (dp->ttyd == u.u_ttyd)
			goto found;
	}
	/* ick */
	for (dp = allttys; dp; dp = dp->next) {
		if (dp->ttyd == -1) {
			if (stat(dp->name, &stb) == 0 &&
			   (stb.st_mode&S_IFMT)==S_IFCHR)
				dp->ttyd = stb.st_rdev;
			else
				dp->ttyd = -2;
		}
		if (dp->ttyd == u.u_ttyd)
			goto found;
	}
	return ("?");
found:
	p = dp->name;
	if (p[0]=='t' && p[1]=='t' && p[2]=='y')
		p += 3;
	return (p);
}

save()
{
	register struct savcom *sp;
	register struct asav *ap;
	register char *cp;
	register struct text *xp;
	char *ttyp, *cmdp;

	if (mproc->p_stat != SZOMB && getu() == 0)
		return;
	ttyp = gettty();
	if (xFlag == 0 && ttyp[0] == '?' || tptr && strncmp(tptr, ttyp, 2))
		return;
	sp = &savcom[npr];
	cmdp = getcmd();
	if (cmdp == 0)
		return;
	sp->ap = ap = (struct asav *)alloc(sizeof (struct asav));
	sp->ap->a_cmdp = cmdp;
#define e(a,b) ap->a = mproc->b
	e(a_flag, p_flag); e(a_stat, p_stat); e(a_nice, p_nice);
	e(a_uid, p_uid); e(a_pid, p_pid); e(a_pri, p_pri);
	e(a_slptime, p_slptime); e(a_time, p_time);
	ap->a_tty[0] = ttyp[0];
	ap->a_tty[1] = ttyp[1] ? ttyp[1] : ' ';
	if (ap->a_stat == SZOMB) {
		ap->a_cpu = 0;
	} else {
		ap->a_size = mproc->p_dsize + mproc->p_ssize;
		e(a_rss, p_rssize); 
		ap->a_ttyd = u.u_ttyd;
		ap->a_cpu = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec;
		if (sumcpu)
			ap->a_cpu += u.u_cru.ru_utime.tv_sec + u.u_cru.ru_stime.tv_sec;
		if (mproc->p_textp && text) {
			xp = &text[mproc->p_textp - atext];
			ap->a_tsiz = xp->x_size;
			ap->a_txtrss = xp->x_rssize;
			ap->a_xccount = xp->x_ccount;
		}
	}
#undef e
	ap->a_maxrss = mproc->p_maxrss;
	if (lFlag) {
		register struct lsav *lp;

		sp->s_un.lp = lp = (struct lsav *)alloc(sizeof (struct lsav));
#define e(a,b) lp->a = mproc->b
		e(l_ppid, p_ppid); e(l_cpu, p_cpu);
		if (ap->a_stat != SZOMB) {
			e(l_wchan, p_wchan);
#ifdef	s32
			e(lw_rpcresults, p_rpcresults);
			e(lw_rpcflags, p_rpcflags);
			e(lw_rpcerror, p_rpcerror);
#endif
		}
#undef e
		lp->l_addr = pcbpf;
	} else if (vFlag) {
		register struct vsav *vp;

		sp->s_un.vp = vp = (struct vsav *)alloc(sizeof (struct vsav));
#define e(a,b) vp->a = mproc->b
		if (ap->a_stat != SZOMB) {
			e(v_swrss, p_swrss);
			vp->v_majflt = u.u_ru.ru_majflt;
			if (mproc->p_textp)
				vp->v_txtswrss = xp->x_swrss;
		}
		vp->v_pctcpu = pcpu();
#undef e
	} else if (uFlag)
		sp->s_un.u_pctcpu = pcpu();
	else if (sFlag) {
		if (ap->a_stat != SZOMB) {
			for (cp = (char *)u.u_stack;
			    cp < &user.upages[UPAGES][0]; )
				if (*cp++)
					break;
			sp->s_un.s_ssiz = (&user.upages[UPAGES][0] - cp);
		}
	}

	npr++;
}

double
pmem(ap)
	register struct asav *ap;
{
	double fracmem;
	int szptudot;

	if ((ap->a_flag&SLOAD) == 0)
		fracmem = 0.0;
	else {
		szptudot = UPAGES + clrnd(ctopt(ap->a_size+ap->a_tsiz));
		fracmem = ((float)ap->a_rss+szptudot)/CLSIZE/ecmx;
		if (ap->a_xccount)
			fracmem += ((float)ap->a_txtrss)/CLSIZE/
			    ap->a_xccount/ecmx;
	}
	return (100.0 * fracmem);
}

/*
 * Return % cpu iff process
 * loaded and has non-zero cpu time.
 */
double
pcpu()
{
	time_t time;
	double	 tmp1, tmp2;

	time = mproc->p_time;
	if (time == 0 || (mproc->p_flag&SLOAD) == 0)
		return (0.0);
#ifdef	s32	/* USE_FIXED_POINT */
	tmp1 = (100.0 * (float)mproc->p_pctcpu / 16384.);
	if (rawcpu)
		return tmp1;
	tmp2 = (1.0 - exp(time * log(ccpu)));
	if (tmp2) return tmp1/tmp2;
#else	s32	/* USE_FIXED_POINT */
	if (rawcpu)
		return (100.0 * mproc->p_pctcpu);
	return (100.0 * mproc->p_pctcpu / (1.0 - exp(time * log(ccpu))));
#endif	s32	/* USE_FIXED_POINT */
}

/*
 * Read the u-page(s) for the process denoted by 'mproc'
 * into the global user.user
 *
 * Return 1 iff successful;
 * Return 0 otherwise.
 */
getu()
{
	struct pte *pteaddr, apte;
	struct pte arguutl[UPAGES+UWASTE+CLSIZE];
	register int i;
	int ncl, size;

#ifdef s32
	size = ctob(UPAGES);
#else s32
	size = sFlag ? ctob(UPAGES) : sizeof (struct user);
#endif s32
	/*
	 * If the process is NOT loaded,
	 * then we fetch the u-page from the swap-area.
	 */
	if ((mproc->p_flag & SLOAD) == 0) {
		if (swap < 0)
			return (0);
		(void) lseek(swap, (long)dtob(mproc->p_swaddr), 0);
		if (read(swap, (char *)&user.user, size) != size) {
			fprintf(stderr, "ps: cant read u for pid %d from %s\n",
			    mproc->p_pid, swapf);
			return (0);
		}
		pcbpf = 0;
		argaddr = 0;
		return (1);
	}
	/*
	 * Otherwise the u-page is in kernel-RAM,
	 * or in an image-file of a (deceased) kernel-RAM.
	 */
	if (kFlag)
		mproc->p_p0br = (struct pte *)clear(mproc->p_p0br);
	pteaddr = &Usrptma[btokmx(mproc->p_p0br) + mproc->p_szpt - 1];
	klseek(kmem, (long)pteaddr, 0);
	if (read(kmem, (char *)&apte, sizeof(apte)) != sizeof(apte)) {
		printf("ps: cant read indir pte to get u for pid %d from %s\n",
		    mproc->p_pid, swapf);
		return (0);
	}
	klseek(mem,
	    (long)ctob(apte.pg_pfnum+1) - (UPAGES+UWASTE+CLSIZE) * sizeof (struct pte),
		0);
	if (read(mem, (char *)arguutl, sizeof(arguutl)) != sizeof(arguutl)) {
		printf("ps: cant read page table for u of pid %d from %s\n",
		    mproc->p_pid, kmemf);
		return (0);
	}
	if (arguutl[0].pg_fod == 0 && arguutl[0].pg_pfnum)
		argaddr = ctob(arguutl[0].pg_pfnum);
	else
		argaddr = 0;
	pcbpf = arguutl[UWASTE+CLSIZE].pg_pfnum;
	ncl = (size + NBPG*CLSIZE - 1) / (NBPG*CLSIZE);
	while (--ncl >= 0) {
		i = ncl * CLSIZE;
		klseek(mem, (long)ctob(arguutl[UWASTE+CLSIZE+i].pg_pfnum), 0);
		if (read(mem, user.upages[i], CLSIZE*NBPG) != CLSIZE*NBPG) {
			printf("ps: cant read page %d of u of pid %d from %s\n",
			    arguutl[UWASTE+CLSIZE+i].pg_pfnum, mproc->p_pid, memf);
			return(0);
		}
	}
	return (1);
}

/*
 * Return pointer to the command string
 * associated with the invocation of a process.
 *
 * While the 'real' process might not be SLOADed, ie, its upage
 * is not in /dev/kmem, getu() has fetched the upage
 * into our own local upage cache.
 *
 * The exception is if we are processing a crash-dump,
 * which of course has no attendant /dev/swap (yet).
 */
char *
getcmd()
{
	char	cmdbuf[CLSIZE*NBPG];
	union {
		char	argc[CLSIZE*NBPG];
		int	argi[CLSIZE*NBPG/sizeof (int)];
	}	argspac;
	register char	*cp;
	register int	*ip;
	char		 c;
	int		 nbad;
	struct	dblock	 db;		/* Set within vstodb() */
	char		*file;

	if (mproc->p_stat == SZOMB
	 || mproc->p_flag&(SSYS|SWEXIT))
#ifdef s32
	{
		if (!(mproc->p_flag & SSYS) || u.u_comm[0] == '\0')
			return("===");
		/*
		 * Return something for SZOMBy or SWEXITing processes.
		 */
		strncpy(cmdbuf, u.u_comm, sizeof(u.u_comm));
		return(savestr(cmdbuf));
	}
	/*
	 * For RPC and other servers
	 * which have no normal resources or argv[].
	 */
	if (((mproc->p_dsize + mproc->p_ssize) == 0)	/* Sans RAM resources */
	   && u.u_comm[0]) {				/* A printable cmmd   */

		strncpy(cmdbuf, u.u_comm, sizeof(u.u_comm));
		return(savestr(cmdbuf));
	}
#else s32
		return ("");
#endif s32
	if (cFlag) {
		(void) strncpy(cmdbuf, u.u_comm, sizeof (u.u_comm));
		return (savestr(cmdbuf));
	}
	if (!(mproc->p_flag & SLOAD) || argaddr == 0) {
		if (swap < 0)
			goto retucomm;
		/*
		 * Compute db.db_base and db.db_size:
		 *
		 * We end up here for the RPC servers,
		 * which have NO data-space or stack-space pages.
		 * Nevertheless, alles arbietet OK...
		 */
		vstodb(	0,
			CLSIZE,
			&u.u_smap,	/* Disk map for stack segment */
			&db,		/* Where vstodb() is to put results */
			1);		/* Reverse the computation */
		/*
		 * Fill the argspac from the area on the swapdevice.
		 * We presume that db.db_size >= sizeof(argspac).
		 */
		(void) lseek(swap, (long)dtob(db.db_base), 0);
		if (read(swap, (char *)&argspac, sizeof(argspac))
		    != sizeof(argspac))
			goto bad;
		file = swapf;
	} else {
		klseek(mem, (long)argaddr, 0);
		if (read(mem, (char *)&argspac, sizeof (argspac))
		    != sizeof (argspac))
			goto bad;
		file = memf;
	}
	ip = &argspac.argi[CLSIZE*NBPG/sizeof (int)];
	ip -= 2;		/* last arg word and .long 0 */
	while (*--ip)
		if (ip == argspac.argi)
			goto retucomm;
	*(char *)ip = ' ';
	ip++;
	nbad = 0;
	for (cp = (char *)ip; cp < &argspac.argc[CLSIZE*NBPG]; cp++) {
		c = *cp & 0177;
		if (c == 0)
			*cp = ' ';
		else if (c < ' ' || c > 0176) {
			if (++nbad >= 5*(eFlag+1)) {
				*cp++ = ' ';
				break;
			}
			*cp = '?';
		} else if (eFlag == 0 && c == '=') {
			while (*--cp != ' ')
				if (cp <= (char *)ip)
					break;
			break;
		}
	}
	*cp = 0;
	while (*--cp == ' ')
		*cp = 0;
	cp = (char *)ip;
	(void) strncpy(cmdbuf, cp, &argspac.argc[CLSIZE*NBPG] - cp);
	if (cp[0] == '-' || cp[0] == '?' || cp[0] <= ' ') {
		(void) strcat(cmdbuf, " (");
		(void) strncat(cmdbuf, u.u_comm, sizeof(u.u_comm));
		(void) strcat(cmdbuf, ")");
	}
/*
	if (xFlag == 0 && gFlag == 0 && tptr == 0 && cp[0] == '-')
		return (0);
*/
	return (savestr(cmdbuf));

bad:
	fprintf(stderr, "ps: error locating command name for pid %d from %s\n",
	    mproc->p_pid, file);
retucomm:
	(void) strcpy(cmdbuf, " (");
	(void) strncat(cmdbuf, u.u_comm, sizeof (u.u_comm));
	(void) strcat(cmdbuf, ")");
	return (savestr(cmdbuf));
}

/*
 * Long-form display:
 */
char	*lHdr =
"      F  UID   PID  PPID CP PRI NI ADDR SZk  RSS  WCHAN STAT TT  TIME";
char	*wlHdr =
"      F  UID   PID  PPID CP PRI NI ADDR SZk  RSS  WCHAN STAT TT DD+HH:MM:SS";
lpr(sp)
	struct savcom *sp;
{
	register struct asav *ap = sp->ap;
	register struct lsav *lp = sp->s_un.lp;

	printf("%7x%5u%6u%6u%3d%4d%3d%5x%4d%5d",
	    ap->a_flag, ap->a_uid,
	    ap->a_pid, lp->l_ppid, lp->l_cpu&0377, ap->a_pri-PZERO,
	    ap->a_nice-NZERO, lp->l_addr, pgtok(ap->a_size), pgtok(ap->a_rss));
	/*
	 * Can sleep upon any 24-bit Mbus address...
	 */
	printf(lp->l_wchan ? " %6x" : "       ", (int)lp->l_wchan&0xffffff);
	printf(" %4.4s ", state(ap));
	ptty(ap->a_tty);
	if (twidth>80)
		ddhhProcTime(ap);
	else	pTime(ap);
}

/*
 * Display the tty
 */
ptty(tp)
	char *tp;
{
	printf("%-2.2s", tp);
}

/*
 * Display the cpu time
 * in mm:ss
 */
pTime(ap)
	struct asav *ap;
{

	printf("%3ld:%02ld", ap->a_cpu / 60, ap->a_cpu % 60);
}


/*
 * Display the cpu time
 * in 'dd+hh:mm:ss' format.
 */
ddhhProcTime(ap)
	struct asav *ap;
{
	register int	dd, hh, mm, ss;

	mm  = ap->a_cpu / 60;
	ss  = ap->a_cpu % 60;

	hh  = mm / 60;
	mm %= 60;

	dd  = hh / 24;
	hh %= 24;

	if (dd)
		printf(" %2ld+%02ld:%02ld:%02ld", dd, hh, mm, ss);
	else if (hh)
		printf("    %2ld:%02ld:%02ld",	     hh, mm, ss);
	else
		printf("       %2ld:%02ld",		 mm, ss);
}

 char	*uHdr =
"USER       PID %CPU %MEM   SZ  RSS TT STAT  TIME";

 char	*wuHdr =
"USER       PID %CPU %MEM   SZ  RSS TT STAT DD+HH:MM:SS";

upr(sp)
	struct savcom *sp;
{
	register struct asav	*ap = sp->ap;
	register char		*cp;
	int	 		 vmsize, rmsize;
	char	 uidAsciiBuf[16];	/* Won't be overrun (in the stack) */

	vmsize = pgtok((ap->a_size + ap->a_tsiz));
	rmsize = pgtok(ap->a_rss);
	if (ap->a_xccount)
		rmsize += pgtok(ap->a_txtrss/ap->a_xccount);

#if 0	/* Debugging */
	if (!(cp = getname(ap->a_uid))) {
		cp = (char *)sprintf(&uidAsciiBuf[0], "[%-6u]  ", ap->a_uid);
		if (!cp) {
			printf("cp=0x%X a_pid=%d a_uid=%d\n",
				cp, ap->a_pid, ap->a_uid);
			return;	/* Preclude segmentation error */
		}
	}
#endif 
	printf("%-8.8s %5d%5.1f%5.1f%5d%5d ",
	    ((cp = getname(ap->a_uid))
	       ? cp
	       : (char *)sprintf(&uidAsciiBuf[0], "[%-6u]  ", ap->a_uid)
	    ),
	    ap->a_pid,
	    sp->s_un.u_pctcpu,
	    pmem(ap),
	    vmsize, rmsize);
	ptty(ap->a_tty);
	printf(" %4.4s", state(ap));
	if (twidth > 80)
		ddhhProcTime(ap);		/* Long format for time */
	else	pTime(ap);
}

/*
 * Display state of the virtual system
 */
 char *vHdr =
" SIZE  PID TT STAT  TIME SL RE  PAGEIN  SIZE  RSS  LIM TSIZ TRS %CPU %MEM"+5;

vpr(sp)
	struct savcom *sp;
{
	register struct vsav *vp = sp->s_un.vp;
	register struct asav *ap = sp->ap;

	printf("%5u ", ap->a_pid);
	ptty(ap->a_tty);
	printf(" %4.4s", state(ap));
	pTime(ap);
	printf("%3d%3d%8d%6d%5d",
	   ap->a_slptime > 99 ? 99 : ap-> a_slptime,
	   ap->a_time > 99 ? 99 : ap->a_time, vp->v_majflt,
	   pgtok(ap->a_size), pgtok(ap->a_rss));
	if (ap->a_maxrss == (RLIM_INFINITY/NBPG))
		printf("   xx");
	else
		printf("%5d", pgtok(ap->a_maxrss));
	printf("%5d%4d%5.1f%5.1f",
	   pgtok(ap->a_tsiz), pgtok(ap->a_txtrss), vp->v_pctcpu, pmem(ap));
}

/*
 * Display information in short-form
 */
 char	*sHdr =
"SSIZ   PID TT STAT  TIME";
 char	*wsHdr =
"SSIZ   PID TT STAT DD+HH:MM:SS";

spr(sp)
	struct savcom *sp;
{
	register struct asav *ap = sp->ap;

	if (sFlag)
		printf("%4d ", sp->s_un.s_ssiz);
	printf("%5u ", ap->a_pid);
	ptty(ap->a_tty);
	printf(" %4.4s", state(ap));
	if (twidth > 80)
		ddhhProcTime(ap);
	else	pTime(ap);
}

/*
 * Return pointer to ascii representation
 * of per-process state-information.
 */
 char *
state(ap)
	register struct asav *ap;
{
	char stat, load, nice, anom;
	static char res[5];

	switch (ap->a_stat) {

	case SSTOP:
		stat = 'T';
		break;

	case SSLEEP:
		if (ap->a_pri >= PZERO)
			if (ap->a_slptime >= MAXSLP)
				stat = 'I';
			else
				stat = 'S';
		else if (ap->a_flag & SPAGE)
			stat = 'P';
		else
			stat = 'D';
		break;

	case SWAIT:
	case SRUN:
	case SIDL:
		stat = 'R';
		break;

	case SZOMB:
		stat = 'Z';
		break;

	default:
		stat = '?';
	}
	load = ap->a_flag & SLOAD ? (ap->a_rss>ap->a_maxrss ? '>' : ' ') : 'W';
	if (ap->a_nice < NZERO)
		nice = '<';
	else if (ap->a_nice > NZERO)
		nice = 'N';
	else
		nice = ' ';
	anom = (ap->a_flag&SUANOM) ? 'A' : ((ap->a_flag&SSEQL) ? 'S' : ' ');
	res[0] = stat; res[1] = load; res[2] = nice; res[3] = anom;
	return (res);
}

/*
 * Given a base/size pair in virtual swap area,
 * return a physical base/size pair which is the
 * (largest) initial, physically contiguous block.
 */
vstodb(vsbase, vssize, dmp, dbp, rev)
	register int	 vsbase;
	int		 vssize;
	struct	 dmap	*dmp;
	register struct	 dblock	*dbp;
{
	register int	  blk = dmmin;	/* Use value that the kernel is using */
	register swblk_t *ip = dmp->dm_map;

	vsbase = ctod(vsbase);
	vssize = ctod(vssize);
	/*
	 * Sanity check
	 */
	if (vsbase >= 0) {
		/*
		 * (A couple of kinds of system processes have
		 * NO stack pages allocated to them.)
		 */
		if (dmp->dm_size	/* Test only if some stack pages */
		 && vsbase + vssize > dmp->dm_size) {
			panic("vstodb/E: p_pid=%d vsbase=0x%X vssize=0x%X dm_size=0x%X\n",
				mproc->p_pid, vsbase, vssize, dmp->dm_size);
		}
	} else {
		/*
		 * Obvious brain damage...
		 */
		panic("vstodb/E: p_pid=%d vsbase=0x%X is NEGATIVE! vssize=0x%X dm_size=0x%X\n",
			mproc->p_pid, vsbase, vssize, dmp->dm_size);
	}
	while (vsbase >= blk) {
		vsbase -= blk;
		if (blk < dmmax)
			blk *= 2;
		ip++;
	}
	/*
	 * Sanity check.
	 *
	 * This is fairly arcane:
	 * RPC servers have no stack or data-space
	 * pages...and this is ok since we only
	 * harmlessly read the swapf for argspace blocks.
	 * For RPC servers, *ip==0.
	 */
	if ((*ip==0 && mproc->p_ssize)	/* Only if ANY stack pages */
	 ||  *ip < 0
	 || (*ip + blk) > nswap) {
		panic("vstodb *ip/E: p_pid=%d ip=0x%X *ip=0x%X blk=0x%X nswap=0x%X\n",
			mproc->p_pid,ip, *ip, blk, nswap);
	}
	/*
	 * We set these for our caller:
	 */
	dbp->db_size = min(vssize, blk - vsbase);
	dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
}

/*ARGSUSED*/
panic(cp, a,b,c,d,e,f,g,h,i,j,k)
	char *cp, a,b,c,d,e,f,g,h,i,j,k;
{
#ifdef DEBUG
	printf("%s: ", *ourName);
	printf(cp, a,b,c,d,e,f,g,h,i,j,k);
#endif
}

/*
 * Comparisons for sorting by %cpu, vsize, or pid
 */
pscomp(s1, s2)
	struct savcom *s1, *s2;
{
	register int i;

	if (uFlag)
		return (s2->s_un.u_pctcpu > s1->s_un.u_pctcpu ? 1 : -1);
	if (vFlag)
		return (vsize(s2) - vsize(s1));
	/*
	 * Sort by tty dev
	 * if ttys are different
	 */
	i = s1->ap->a_ttyd - s2->ap->a_ttyd;
	if (i == 0)
		/*
		 * Ttys are same or 0,
		 * so sort by process id #
		 */
		i = s1->ap->a_pid - s2->ap->a_pid;
	return (i);
}

vsize(sp)
	struct savcom *sp;
{
	register struct asav *ap = sp->ap;
	register struct vsav *vp = sp->s_un.vp;
	
	if (ap->a_flag & SLOAD)
		return (ap->a_rss +
		    ap->a_txtrss / (ap->a_xccount ? ap->a_xccount : 1));
	return (vp->v_swrss + (ap->a_xccount ? 0 : vp->v_txtswrss));
}

#define	NMAX	8
#ifndef	s32
#define	NUID	2048
#else	s32
#define	NUID	32767	/* u_uid, u_gid: 'signed short': 0x7fff == 32767 */
#endif	s32

 char	names[NUID][NMAX+1];

/*
 * Stolen from ls...
 */
 char *
getname(uid)
	unsigned int	uid;
{
	register struct passwd *pw;
	static init;
	struct passwd *getpwent();

	if (uid >= 0 && uid < NUID && names[uid][0])
		return (&names[uid][0]);
	if (init == 2)
		return (0);
	if (init == 0)
		setpwent(), init = 1;
	while (pw = getpwent()) {
		if (pw->pw_uid >= NUID)
			continue;
		if (names[pw->pw_uid][0])
			continue;
		(void) strncpy(names[pw->pw_uid], pw->pw_name, NMAX);
		if (pw->pw_uid == uid)
			return (&names[uid][0]);
	}
	init = 2;
	endpwent();
	return (0);
}

 char	*freebase;
 int	nleft;

 char *
alloc(size)
	int size;
{
	register char *cp;
	register int i;

#ifdef s32
	size = (size+1)&~1;
#endif s32
#ifdef sun
	size = (size+1)&~1;
#endif
	if (size > nleft) {
		freebase = (char *)sbrk((int)(i = size > 2048 ? size : 2048));
		if (freebase == 0) {
			fprintf(stderr, "ps: ran out of memory\n");
			exit(1);
		}
		nleft = i - size;
	} else
		nleft -= size;
	cp = freebase;
	for (i = size; --i >= 0; )
		*cp++ = 0;
	freebase = cp;
	return (cp - size);
}

/*
 * Return a pointer to a saved copy of a string.
 */
 char *
savestr(cp)
	char *cp;
{
	register int len;
	register char *dp;

	len = strlen(cp);
	dp = (char *)alloc(len+1);
	(void) strcpy(dp, cp);
	return (dp);
}
