2.9BSD/usr/src/cmd/ps/ps.c

Compare this file to the similar file:
Show the results in this format:

char	*sccsid	= "%W%";

/*
 *	ps - process status
 *	This is the augmented UCB ps for 2.9BSD PDP-11 Unix (11/82).
 *	It is not very portable, using the phys sys call and
 *	knowing the format of an a.out symbol table.
 *	Examine and print certain things about processes
 *	Usage:  ps [ acgklnrtuwxU# ] [ corefile [ swapfile [ system ] ] ]
 */

#include	"whoami.h"
#include	<sys/param.h>
#include	<stdio.h>
#include	<pwd.h>
#include	<a.out.h>
#include	<core.h>
#include	<ctype.h>
#include	<sys/proc.h>
#include	<sys/stat.h>
#include	<sys/tty.h>
#include	<sys/dir.h>
#include	<sys/user.h>
#include	<psout.h>

#define	equal		!strcmp
#define	exists(x)	(stat ((x), &stbuf) == 0)
#define	within(x,y,z)	(((unsigned)(x) >= (y)) && ((unsigned)(x) < (z)))
#define	round(x,y)	((long) ((((long) (x) + (long) (y) - 1L) / (long) (y)) * (long) (y)))

struct	nlist nl[] =	{
	{ "_proc" , 0, 0},
#define	X_PROC		0
	{ "_swplo" , 0, 0},
#define	X_SWPLO		1
	{ "_nproc", 0, 0},
#define X_NPROC		2
	{ "_hz", 0, 0},
#define	X_HZ		3
	0
};
#define	NNAMESIZ	(sizeof nl[0].n_name)

struct	proc *mproc, proc [8];
struct	user u;
struct	stat	stbuf;

int	hz;
int	chkpid	= 0;
int	aflg;	/* -a: all processes, not just mine */
int	cflg;	/* -c: not complete listing of args, just comm. */
int	gflg;	/* -g: complete listing including group headers, etc */
int	kflg;	/* -k: read from core file instead of real memory */
int	lflg;	/* -l: long listing form */
int	nflg;	/* -n: numeric wchans */
int	rflg;	/* -r: raw output in style <psout.h> */
int	uflg;	/* -u: user name */
int	wflg;	/* -w[w]: wide terminal */
int	xflg;	/* -x: ALL processes, even those without ttys */
int	Uflg;	/* -U: update the private list */
char	*tptr, *mytty;
char	*nlistf, *uname;
int	file;
off_t	swplo;
int	nproc;
off_t	tell;
int	nchans;
int	ndev;
int	nsyms;
int	ismem;
#ifndef	PSFILE
char	*psdb	= "/etc/psdatabase";
#else
char	*psdb	= PSFILE;
#endif

/*
 *	Structure for the unix wchan table
 */
struct	wchan	{
	char	cname [NNAMESIZ];
	unsigned	caddr;
};
struct	wchan	*wchanhd;

char	*calloc (), *malloc (), *realloc ();
char	*gettty (), *getptr (), *getchan ();
char	*ttyname ();
int	pscomp ();
int	wchancomp ();
off_t	lseek ();

#ifndef	NUMDEV
#define	NUMDEV		256
#endif
struct	devl	{
	char	dname[DIRSIZ];
	dev_t	dev;
} devl[NUMDEV];

struct	map	{
	off_t	b1, e1; off_t	f1;
	off_t	b2, e2; off_t	f2;
};
struct	map datmap;

struct	psout *outargs;		/* info for first npr processes */
int	npr;			/* number of processes found so far */
int	twidth;			/* terminal width */
int	cmdstart;		/* starting position for command field */

char	*memf;			/* name of kernel memory file to use */
char	*kmemf;			/* name of physical memory file to use */
char	*swapf;			/* name of swap file to use */
char	*nlistf;		/* name of symbol table file to use */
int	kmem, mem, swap;
extern	char	_sobuf[];
#ifdef	TERMCAP
char	*getenv ();
#endif

main (argc, argv)
char	**argv;
{
	int	uid, euid, puid, nread;
	register	i, j;
	char	*ap;
	register	struct	proc	*procp;
#ifdef	TERMCAP
	char	*termp, capbuf [1024];
#endif

#ifdef	TERMCAP
	if ((termp = getenv ("TERM")) != (char *) NULL)
		if (tgetent (capbuf, termp) == 1)
			twidth	= tgetnum ("co");
		else	;
	else
#endif
		twidth	= 80;

	setbuf (stdout, _sobuf);
	argc--, argv++;
	if (argc > 0)	{
		ap	= argv [0];
		while (*ap) switch (*ap++)	{
		case '-':
			break;

		case 'a':
			aflg++;
			break;

		case 'c':
			cflg++;
			break;

		case 'g':
			gflg++;
			break;

		case 'k':
			kflg++;
			break;

		case 'l':
			lflg	= 1;
			break;

		case 'n':
			nflg++;
			lflg	= 1;
			break;

		case 'r':
			rflg++;
			break;

		case 't':
			if (*ap)
				tptr	= ap;
			aflg++;
			gflg++;
			if (*tptr == '?')
				xflg++;
			while (*ap)
				ap++;
			break;

		case 'u':
			uflg	= 1;
			break;

		case 'U':
			Uflg++;
			break;

		case 'w':
			if (twidth == 80)
				twidth	= 132;
			else	twidth	= BUFSIZ;
			wflg++;
			break;

		case 'x':
			xflg++;
			break;

		default:
			if (!isdigit (ap[-1]))
				break;
			chkpid	= atoi (--ap);
			*ap	= '\0';
			aflg++;
			xflg++;
			break;
		}
	}

	openfiles (argc, argv);
	getkvars (argc, argv);
	if (kflg)
		swplo	= (off_t) 0;
	uid	= getuid ();
	euid	= geteuid ();
	mytty	= ttyname (0);
	printhdr ();
	for (i = 0; i < nproc; i += 8)	{
		j	= nproc - i;
		if (j > 8)
			j	= 8;
		j	*= sizeof (struct proc);
		if ((nread = read (kmem, (char *) proc, j)) != j)	{
			cantread ("proc table", kmemf);
			if (nread == -1)
				break;
			}
		for (j = nread / sizeof (struct proc) - 1; j >= 0; j--)	{
			mproc	= &proc[j];
			procp	= mproc;
			/* skip processes that don't exist */
			if (procp->p_stat == 0)
				continue;
			/* skip those without a tty unless -x */
			if (procp->p_pgrp == 0 && xflg == 0)
				continue;
			/* skip group leaders on a tty unless -g, -x, or -t.. */
#ifdef	MENLO_JCL
			if (!tptr && !gflg && !xflg && procp->p_ppid == 1 && (procp->p_flag & SDETACH) == 0)
#else
			if (!tptr && !gflg && !xflg && procp->p_pid == procp->p_pgrp)
#endif
				continue;
			/* -g also skips those where **argv is "-" - see savcom */
			puid = procp->p_uid;
			/* skip other peoples processes unless -a or a specific pid */
			if ((uid != puid && euid != puid && aflg == 0) ||
			    (chkpid != 0 && chkpid != procp->p_pid))
				continue;
			if (savcom (puid))
				npr++;
		}
	}
	fixup (npr);
	for (i = 0; i < npr; i++)	{
#ifdef	TERMCAP
		register	cmdwidth	= twidth - cmdstart - 2;
#endif
		register	struct	psout	*a	= &outargs[i];

		if (rflg)	{
			if (write (1, (char *) a, sizeof (struct psout)) != sizeof (struct psout))
				perror ("write");
			continue;
			}
		else	if (lflg)
				lpr (a);
			else	if (uflg)
					upr (a);
				else	spr (a);
#ifdef	TERMCAP
		if (cmdwidth < 0)
			cmdwidth	= 80 - cmdstart - 2;
#endif
		if (a->o_stat == SZOMB)
#ifdef	TERMCAP
			printf ("%.*s", cmdwidth, " <defunct>");
#else
			printf (" <defunct>");
#endif
		else	if (a->o_pid == 0)
#ifdef	TERMCAP
				printf ("%.*s", cmdwidth, " swapper");
#else
				printf (" swapper");
#endif
			else	printf (" %.*s", twidth - cmdstart - 2, cflg ?  a->o_comm : a->o_args);
		putchar ('\n');
		}
	exit (!npr);
}

/*
 *	Getdev() changes directories so it should be
 *	called only after other necessary files have
 *	been opened.
 */
getdev ()
{
	register	FILE	*df;
	struct	direct	dbuf;
	register	struct	direct	*dirp	= &dbuf;

	if ((chdir ("/dev") < 0) || ((df = fopen (".", "r")) == NULL))
		perrexit ("/dev");
	while (fread ((char *) &dbuf, sizeof (dbuf), 1, df) == 1)	{
		if (dirp->d_ino == 0)
			continue;
		if (!exists (dirp->d_name))
			continue;
		if ((stbuf.st_mode&S_IFMT) != S_IFCHR)
			continue;
		strncpy (devl[ndev].dname, dirp->d_name, sizeof devl[ndev].dname);
		devl[ndev].dev = stbuf.st_rdev;
		ndev++;
		}
	fclose (df);
}

savcom (puid)
{
	char	*tp;
	off_t	addr;
#ifdef	VIRUS_VFORK
	off_t	daddr, saddr;
#endif
	register	struct	psout	*a;
	register	struct	proc	*procp	= mproc;
	register	struct	user	*up	= &u;
	long	txtsiz, datsiz, stksiz;
	int	septxt;

	if (procp->p_flag & SLOAD)	{
		addr	= ctob ((off_t) procp->p_addr);
#ifdef	VIRUS_VFORK
		daddr	= ctob ((off_t) procp->p_daddr);
		saddr	= ctob ((off_t) procp->p_saddr);
#endif
		file	= mem;
	} else	{
		addr	= (procp->p_addr + swplo) << 9;
#ifdef	VIRUS_VFORK
		daddr	= (procp->p_daddr + swplo) << 9;
		saddr	= (procp->p_saddr + swplo) << 9;
#endif
		file	= swap;
	}
	if (pread (file, (char *) up, sizeof (u), addr) != sizeof (u))
		return (0);

	/* set up address maps for user pcs */
	txtsiz	= ctob (up->u_tsize);
	datsiz	= ctob (up->u_dsize);
	stksiz	= ctob (up->u_ssize);
	septxt	= up->u_sep;
	datmap.b1	= (septxt ?  0 : round (txtsiz, TXTRNDSIZ));
	datmap.e1	= datmap.b1 + datsiz;
#ifdef	VIRUS_VFORK
	datmap.f1	= daddr;
#else
	datmap.f1	= ctob (USIZE) + addr;
#endif
	datmap.b2	= stackbas (stksiz);
	datmap.e2	= stacktop (stksiz);
#ifdef	VIRUS_VFORK
	datmap.f2	= saddr;
#else
	datmap.f2	= ctob (USIZE) + (datmap.e1 - datmap.b1) + addr;
#endif

	tp	= gettty ();
	if ((tptr && strcmp (tptr, tp)) || (strcmp (mytty, tp) && !aflg))
		return (0);

	a	= &outargs[npr];
	/* saving com starts here */
	a->o_uid	= puid;
	a->o_pid	= procp->p_pid;
	a->o_flag	= procp->p_flag;
	a->o_ppid	= procp->p_ppid;
	a->o_cpu 	= procp->p_cpu;
	a->o_pri 	= procp->p_pri;
	a->o_nice	= procp->p_nice;
	a->o_addr0	= procp->p_addr;
#ifdef	VIRUS_VFORK
	a->o_size	= ctod(procp->p_dsize + procp->p_ssize + USIZE);
#else
	a->o_size	= (procp->p_size + 7) >> 3;
#endif
	a->o_wchan	= procp->p_wchan;
	a->o_pgrp	= procp->p_pgrp;
	strncpy (a->o_tty, tp, 8);
	a->o_ttyd	= tp[0]	== '?' ?  -1 : up->u_ttyd;
	a->o_stat	= procp->p_stat;
	a->o_flag	= procp->p_flag;

	if (a->o_stat == SZOMB)
		return (1);
	a->o_utime	= up->u_utime;
	a->o_stime	= up->u_stime;
	a->o_cutime	= up->u_cutime;
	a->o_cstime	= up->u_cstime;
	a->o_sigs = (int)up->u_signal[SIGINT] + (int)up->u_signal[SIGQUIT];
	a->o_uname[0]	= 0;
	strncpy (a->o_comm, up->u_comm, 14);

	if (cflg)
		return (1);
#ifdef	VIRUS_VFORK
	else	return (getcmd (a, saddr));
#else
	else	return (getcmd (a, addr));
#endif
}


char	*
gettty ()
{
	register	i;
	register	char	*p;

	if (u.u_ttyp == 0)
		return ("?");
	for (i = 0; i < ndev; i++)	{
		if (devl[i].dev == u.u_ttyd)	{
			p	= devl[i].dname;
			if (p[0] == 't' && p[1] == 't' && p[2] == 'y')
				p	+= 3;
			return (p);
			}
		}
	return ("?");
}

char	*
ttyname (fd)
{
	register	i;
	register	char	*p;
	struct	stat	statb;

	if (fstat (fd, &statb))
		return ("?");
	for (i = 0; i < ndev; i++)	{
		if (devl[i].dev == statb.st_rdev)	{
			p	= devl[i].dname;
			if (p[0] == 't' && p[1] == 't' && p[2] == 'y')
				p	+= 3;
			return (p);
			}
		}
	return ("?");
}

/*
 * fixup figures out everybodys name and sorts into a nice order.
 */
fixup (np)
register	np;
{
	register	i;
	register	struct	passwd	*pw;
	struct	passwd	*getpwent ();

	if (uflg)	{
		/*
		 * If we want names, traverse the password file. For each
		 * passwd entry, look for it in the processes.
		 * In case of multiple entries in /etc/passwd, we believe
		 * the first one (same thing ls does).
		 */
		while ((pw = getpwent ()) != (struct passwd *) NULL)	{
			for (i = 0; i < np; i++)
				if (outargs[i].o_uid == pw->pw_uid)	{
					if (outargs[i].o_uname[0] == 0)
						strcpy (outargs[i].o_uname, pw->pw_name);
					}
			}
		}

	qsort (outargs, np, sizeof (outargs[0]), pscomp);
}

pscomp (x1, x2)
register	struct	psout	*x1, *x2;
{
	register	c;

	c	= (x1)->o_ttyd - (x2)->o_ttyd;
	if (c == 0)
		c	= (x1)->o_pid - (x2)->o_pid;
	return (c);
}

wchancomp (x1, x2)
register	struct	wchan	*x1, *x2;
{
	if (x1->caddr > x2->caddr)
		return (1);
	else	if (x1->caddr == x2->caddr)
			return (0);
		else	return (-1);
}

char	*
getptr (adr)
char	**adr;
{
	char	*ptr;
	register	char	*p, *pa;
	register	i;

	ptr	= 0;
	pa	= (char *)adr;
	p	= (char *)&ptr;
	for (i = 0; i < sizeof (ptr); i++)
		*p++	= getbyte (pa++);
	return (ptr);
}

getbyte (adr)
register	char	*adr;
{
	register	struct	map	*amap	= &datmap;
	char	b;
	off_t	saddr;

	if (!within (adr, amap->b1, amap->e1))
		if (within (adr, amap->b2, amap->e2))
			saddr	= (unsigned) adr + amap->f2 - amap->b2;
		else	return (0);
	else	saddr	= (unsigned) adr + amap->f1 - amap->b1;
	if (lseek (file, saddr, 0) == (off_t) -1 || read (file, &b, 1) < 1)
		return (0);
	return ((unsigned) b);
}

/*
 * pread is like read, but if it's /dev/mem we use the phys
 * system call for speed.  On systems without phys we have
 * to use regular read.
 */
pread (fd, ptr, nbytes, loc)
char	*ptr;
off_t	loc;
{
	register	rc;

	if (fd == mem && ismem)	{
		rc	= phys (6, nbytes / 64 + 1, (short) (loc / 64));
		if (rc >= 0)	{
			bmove (0140000, ptr, nbytes);
			return (nbytes);
			}
		}
	lseek (fd, loc, 0);
	return (read (fd, ptr, nbytes));
}

addchan (name, caddr)
char	*name;
unsigned	caddr;
{
	register	nc	= nchans;
	register	struct	wchan	*wp	= wchanhd;

	if (nc == 0)
		wp	= (struct wchan *) malloc (sizeof (struct wchan));
	else	wp	= (struct wchan *) realloc (wp, sizeof (struct wchan) * (nc + 1));
	if (wp == (struct wchan *) NULL)
		err ("Too many symbols");
	strncpy (wp[nc].cname, name, NNAMESIZ - 1);
	wp[nc].cname[NNAMESIZ-1]	= '\0';
	wp[nc].caddr	= caddr;
	wchanhd	= wp;
	nchans++;
}

char	*
getchan (chan)
register	unsigned	chan;
{
	register	i;
	register	char	*prevsym;

	prevsym	= "";
	if (chan)
		for (i = 0; i < nchans; i++)	{
			if (wchanhd[i].caddr > chan)
				return (prevsym);
			prevsym	= wchanhd[i].cname;
			}
	return (prevsym);
}

nlist (aout, tgt)
char	*aout;
struct	nlist *tgt;
{
	register	i;
	register	FILE *aaa;
	register	struct	nlist	*nnn;
	int	iii, nllen;
	struct	ovlhdr	ovlbuf;
	static	struct	nlist	nbuf;
	static	struct	exec	hbuf;
	off_t	sa	= (off_t) 0;

	nllen	= sizeof nl / sizeof (struct nlist);
	if ((aaa = fopen (aout, "r")) == 0 )
		perrexit (aout);
	if (fread (&hbuf, sizeof hbuf, 1, aaa) != 1 )
		err ("Invalid symbol table");
	if (N_BADMAG (hbuf))	{
		fprintf (stderr, "%s:  not in object file format\n", aout);
		exit (1);
		}
	if (hbuf.a_magic == A_MAGIC5 || hbuf.a_magic == A_MAGIC6)	{
		fread ((char *) &ovlbuf, 1, sizeof ovlbuf, aaa);
		for (i = 0; i < NOVL; i++)
			sa	+= (off_t) (ovlbuf.ov_siz)[i];
		fseek (aaa, sa, 1);
		}
	sa	= (off_t) hbuf.a_text + hbuf.a_data;
	if ((hbuf.a_flag & 01) == 0)
		sa	*= (off_t) 2;
	fseek (aaa, sa, 1);
	nsyms	= hbuf.a_syms / sizeof nbuf;
	if (nsyms == 0)	{
		fprintf (stderr, "%s:  no symbol table\n", aout);
		exit (1);
		}
	while (--nsyms >= 0 && fread (&nbuf, sizeof (nbuf), 1, aaa) == 1 )	{
		if (nbuf.n_name[0] != '_' )
			continue;
		iii	= nbuf.n_type&(N_TYPE|N_EXT);
		if ((nbuf.n_type&N_TYPE) != N_ABS && iii !=  (N_EXT|N_DATA) && iii !=  (N_EXT|N_BSS) )
			continue;
		if (!nflg)
			addchan (nbuf.n_name+1, (unsigned) (nbuf.n_value));
		if (nllen)
			for (nnn = tgt; nnn->n_name[0] != '\0'; nnn++ )
				if (!strncmp (nnn->n_name, nbuf.n_name, NNAMESIZ))	{
					nnn->n_value	= nbuf.n_value;
					nnn->n_type	= nbuf.n_type;
					nllen--;
					break;
					}
		}
	fclose (aaa);
	if (!nflg)
		qsort (wchanhd, nchans, sizeof (struct wchan), wchancomp);
	return (nllen == 0 ? 0 : -1);
}

err (msg)
char	*msg;
{
	fprintf (stderr, "%s\n", msg);
	exit (1);
}

perrexit (msg)
char	*msg;
{
	perror (msg);
	exit (1);
}

writepsdb (unixname)
char	*unixname;
{
	int	nllen;
	register	FILE	*fp;

	setuid (getuid ());
	if ((fp = fopen (psdb, "w")) == NULL)
		perrexit (psdb);
	else	chmod (psdb, 0644);
	nllen	= sizeof nl / sizeof (struct nlist);
	fwrite (unixname, strlen (unixname) + 1, 1, fp);
	fwrite ((char *) &nllen, sizeof nllen, 1, fp);
	fwrite ((char *) &ndev, sizeof ndev, 1, fp);
	fwrite ((char *) &nchans, sizeof nchans, 1, fp);
	fwrite ((char *) nl, sizeof (struct nlist), nllen, fp);
	fwrite ((char *) devl, sizeof (struct devl), ndev, fp);
	fwrite ((char *) wchanhd, sizeof (struct wchan), nchans, fp);
	fclose (fp);
}

char	*
readpsdb ()
{
	int	nllen;
	register	i;
	register	FILE	*fp;
	static	char	unamebuf [BUFSIZ];
	char	*p	= unamebuf;
	register	struct	wchan	*ccc;

	if ((fp = fopen (psdb, "r")) == NULL)
		perrexit (psdb);

	while ((*p= getc (fp)) != '\0')
		p++;
	fread (&nllen, sizeof nllen, 1, fp);
	fread (&ndev, sizeof ndev, 1, fp);
	fread (&nchans, sizeof nchans, 1, fp);
	fread (nl, sizeof (struct nlist), nllen, fp);
	fread (devl, sizeof (struct devl), ndev, fp);
	if (!nflg)
		if ((wchanhd = calloc (nchans, sizeof (struct wchan))) == (struct wchan *) NULL)
			err ("Too many symbols");
		else	for (i = 0, ccc = wchanhd; i < nchans; i++)	{
				fread ((char *) ccc, sizeof (struct wchan), 1, fp);
				ccc++;
				}
	return (unamebuf);
}

openfiles (argc, argv)
char	**argv;
{
	kmemf	= "/dev/kmem";

	if (kflg)
		kmemf	= argc > 1 ?  argv[1] : "/usr/sys/core";
	kmem = open (kmemf, 0);
	if (kmem < 0)
		perrexit (kmemf);
	if (!kflg)	{
		memf	= "/dev/mem";
		ismem++;
		}
	else	memf	= kmemf;
	mem	= open (memf, 0);
	if (mem < 0)
		perrexit (memf);
	swapf	= argc > 2 ?  argv[2] : "/dev/swap";
	swap = open (swapf, 0);
	if (swap < 0)
		perrexit (swapf);
}

getkvars (argc, argv)
char	**argv;
{
	nlistf	= argc > 3 ?  argv[3] : "/unix";
	if (Uflg)	{
		nlist (nlistf, nl);
		getdev ();
		writepsdb (nlistf);
		exit (0);
		}
	else	if (exists (psdb))	{
			uname	= readpsdb ();
			if (!equal (uname, nlistf))	{
				/*
				 * Let addchan() do the work.
				 */
				nchans	= 0;
				free ((char *) wchanhd);
				nlist (nlistf, nl);
				}
			}
		else	nlist (nlistf, nl), getdev ();

	/*
	 * Find base of swap
	 */
	lseek (kmem, (off_t) nl[X_SWPLO].n_value, 0);
	if (read (kmem, (char *) &swplo, sizeof (swplo)) != sizeof (swplo))
		cantread ("swplo", kmemf);

	/*
	 * Find number of procs
	 */
	if (nl[X_NPROC].n_value) {
		lseek (kmem, (off_t) nl[X_NPROC].n_value, 0);
		if (read(kmem, (char *)&nproc, sizeof(nproc)) != sizeof(nproc)){
			perror(kmemf);
			exit(1);
		}
	} else {
		fprintf(stderr, "nproc not in namelist\n");
		exit(1);
	}

	outargs = (struct psout *)calloc(nproc, sizeof(struct psout));
	if (outargs == (struct psout *)NULL) {
		fprintf(stderr, "ps: not enough memory for saving info\n");
		exit(1);
	}
	/*
	 * Find value of hz
	 */
	lseek (kmem, (off_t) nl[X_HZ].n_value, 0);
	read (kmem, (char *) &hz, sizeof hz);

	/*
	 * Locate proc table
	 */
	lseek (kmem, (off_t) nl[X_PROC].n_value, 0);
	tell	= (off_t) nl[X_PROC].n_value;
}


char	*uhdr	=
"USER       PID NICE  SZ TTY       TIME";
upr (a)
register	struct	psout	*a;
{
	printf ("%-8.8s", a->o_uname);
	printf ("%6u", a->o_pid);
	printf ("%4d ", a->o_nice);
	printf ("%4d", a->o_size);
	ptty (a->o_tty);
	ptime (a);
}

char	*shdr	=
"   PID TTY       TIME";
spr (a)
register	struct	psout	*a;
{
	printf ("%6u", a->o_pid);
	ptty (a->o_tty);
	ptime (a);
}

char	*lhdr	=
"  F S   UID   PID  PPID CPU PRI NICE  ADDR  SZ WCHAN  TTY       TIME";
lpr (a)
register	struct	psout	*a;
{
	printf ("%3o %c", 0377 & a->o_flag, "0SWRIZT"[a->o_stat]);
	printf ("%6u", a->o_uid);
	printf ("%6u", a->o_pid);
	printf ("%6u%4d%4d%4d%7o", a->o_ppid, a->o_cpu&0377, a->o_pri, a->o_nice, a->o_addr0);
	printf ("%4d", a->o_size);
	if (nflg)
		if (a->o_wchan)
			printf ("%7o", a->o_wchan);
		else	printf ("       ");
	else	printf (" %-6.6s", getchan (a->o_wchan));
	ptty (a->o_tty);
	ptime (a);
}

ptty (tp)
char	*tp;
{
	printf (" %-8.8s", tp);
}

ptime (a)
register	struct	psout	*a;
{
	time_t	tm;

	tm	= (a->o_utime + a->o_stime + 30) / hz;
	printf ("%3ld:", tm / 60);
	tm	%= 60;
	printf (tm < 10 ?  "0%ld" : "%ld", tm);
}

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

getcmd (a, addr)
off_t	addr;
register	struct	psout	*a;
{
	/* amount of top of stack to examine for args */
#define ARGLIST	(1024/sizeof(int))
	register	*ip;
	register	char	*cp, *cp1;
	char	c;
	char	**ap;
	int	cc, nbad, abuf [ARGLIST];

	a->o_args[0]	= 0;	/* in case of early return */
#ifdef	VIRUS_VFORK
	addr	+= ctob ((off_t) mproc->p_ssize) - ARGLIST*sizeof(int);
#else
	addr	+= ctob ((off_t) mproc->p_size) - ARGLIST*sizeof(int);
#endif

	/* look for sh special */
	lseek (file, addr + ARGLIST*sizeof(int) - sizeof (char **), 0);
	if (read (file, (char *) &ap, sizeof (char *)) != sizeof (char *))
		return (1);
	if (ap)	{
		char	b[82];
		char	*bp	= b;
		while ((cp = getptr (ap++)) && cp && (bp < b+sizeof (a->o_args)) )	{
			nbad	= 0;
			while ((c = getbyte (cp++)) && (bp < b+sizeof (a->o_args)))	{
				if (c<' ' || c > '~')	{
					if (nbad++ > 3)
						break;
					continue;
					}
				*bp++	= c;
				}
			*bp++	= ' ';
			}
		*bp++	= 0;
		strcpy (a->o_args, b);
		return (1);
		}

	if (pread (file, (char *) abuf, sizeof (abuf), addr) != sizeof (abuf))
		return (1);
	abuf[ARGLIST-1]	= 0;
	for (ip = &abuf[ARGLIST-2]; ip > abuf;)	{
		if (*--ip == -1 || *ip == 0)	{
			cp	= (char *) (ip + 1);
			if (*cp == '\0')
				cp++;
			nbad	= 0;
			for (cp1 = cp; cp1 < (char *) &abuf[ARGLIST]; cp1++)	{
				cc	= *cp1 & 0177;
				if (cc == 0)
					*cp1	= ' ';
				else if (cc < ' ' || cc > 0176)	{
					if (++nbad >= 5)	{
						*cp1++	= ' ';
						break;
						}
					*cp1	= '?';
				} else if (cc == '=')	{
					*cp1	= '\0';
					while (cp1 > cp && *--cp1 != ' ')
						*cp1	= '\0';
					break;
					}
				}
			while (*--cp1 == ' ')
				*cp1	= 0;
			strcpy (a->o_args, cp);
garbage:
			cp	= a->o_args;
			if (cp[0] == '-' && cp[1] <= ' ' || cp[0] == '?' || cp[0] <= ' ')	{
				strcat (cp, " (");
				strcat (cp, u.u_comm);
				strcat (cp, ")");
				}
			cp[63]	= 0;	/* max room in psout is 64 chars */
			if (xflg || gflg || tptr || cp[0] != '-')
				return (1);
			return (0);
			}
		}
	goto garbage;
}

printhdr ()
{
	register	char	*hdr;
#ifdef	TERMCAP
	register	char	*cmdstr	= " COMMAND";
#endif

	if (rflg)
		return;
	if (lflg && uflg)
		err ("ps:  specify only one of l and u");
	hdr	= lflg ?  lhdr : (uflg ?  uhdr : shdr);
	cmdstart	= strlen (hdr);
#ifdef	TERMCAP
	if (cmdstart + strlen (cmdstr) >= twidth)
		cmdstr	= " CMD";
	printf ("%s%s\n", hdr, cmdstr);
#else
	printf ("%s COMMAND\n", hdr);
#endif
	
	fflush (stdout);
}