static char rcsid[] = "$Header:w.c 12.0$";
/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)w.c	5.3 (Berkeley) 2/23/86";
#endif not lint

/*
 * w - print system status (who and what)
 *
 * This program is similar to the systat command on Tenex/Tops 10/20
 * It needs read permission on /dev/mem, /dev/kmem, and /dev/drum.
 */
#include <sys/param.h>
#include <nlist.h>
#include <stdio.h>
#include <ctype.h>
#include <utmp.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <machine/pte.h>
#include <sys/vm.h>

#define NMAX sizeof(utmp.ut_name)
#define LMAX sizeof(utmp.ut_line)

#define ARGWIDTH	33	/* # chars left on 80 col crt for args */

struct pr {
	short	w_pid;			/* proc.p_pid */
	char	w_flag;			/* proc.p_flag */
	short	w_size;			/* proc.p_size */
	long	w_seekaddr;		/* where to find args */
	long	w_lastpg;		/* disk address of stack */
	int	w_igintr;		/* INTR+3*QUIT, 0=die, 1=ign, 2=catch */
	time_t	w_time;			/* CPU time used by this process */
	time_t	w_ctime;		/* CPU time used by children */
	dev_t	w_tty;			/* tty device of process */
	int	w_uid;			/* uid of process */
	char	w_comm[15];		/* user.u_comm, null terminated */
	char	w_args[ARGWIDTH+1];	/* args if interesting process */
} *pr;
int	nproc;

struct	nlist nl[] = {
	{ "_proc" },
#define	X_PROC		0
	{ "_swapdev" },
#define	X_SWAPDEV	1
	{ "_Usrptmap" },
#define	X_USRPTMA	2
	{ "_usrpt" },
#define	X_USRPT		3
	{ "_nswap" },
#define	X_NSWAP		4
	{ "_avenrun" },
#define	X_AVENRUN	5
	{ "_boottime" },
#define	X_BOOTTIME	6
	{ "_nproc" },
#define	X_NPROC		7
	{ "_dmmin" },
#define	X_DMMIN		8
	{ "_dmmax" },
#define	X_DMMAX		9
#ifdef ibm032
	{ "_userSIZE" },
#define X_USERSIZE	10
	{ "_procSIZE" },
#define X_PROCSIZE	11
	{ "" },
#endif
};

FILE	*ps;
FILE	*ut;
FILE	*bootfd;
int	kmem;
int	mem;
int	swap;			/* /dev/kmem, mem, and swap */
char	*memf, *kmemf, *swapf;
int	nswap;
int	dmmin, dmmax;
dev_t	tty;
int	uid;
char	doing[520];		/* process attached to terminal */
time_t	proctime;		/* cpu time of process in doing */
double	avenrun[3];
struct	proc *aproc;

#define	DIV60(t)	((t+30)/60)    /* x/60 rounded */ 
#define	TTYEQ		(tty == pr[i].w_tty && uid == pr[i].w_uid)
#define IGINT		(1+3*1)		/* ignoring both SIGINT & SIGQUIT */

char	*getargs();
char	*fread();
char	*ctime();
char	*rindex();
FILE	*popen();
struct	tm *localtime();
time_t	findidle();

#define debug dflg		/* for use with getu */
int	debug;			/* true if -d flag: debugging output */
int	header = 1;		/* true if -h flag: don't print heading */
int	lflag = 1;		/* true if -l flag: long style output */
int	login;			/* true if invoked as login shell */
time_t	idle;			/* number of minutes user is idle */
int	nusers;			/* number of users logged in now */
char *	sel_user;		/* login of particular user selected */
char firstchar;			/* first char of name of prog invoked as */
time_t	jobtime;		/* total cpu time visible */
time_t	now;			/* the current time of day */
struct	tm *nowt;		/* current time as time struct */
struct	timeval boottime;
time_t	uptime;			/* time of last reboot & elapsed time since */
int	np;			/* number of processes currently active */
struct	utmp utmp;
struct	proc mproc;
union {
	struct user U_up;
	char	pad[NBPG][UPAGES];
} Up;
#define	up	Up.U_up
#define u	up

char *getcmd();
char *progname;

int procsize = sizeof (struct proc);
int usersize = sizeof (struct user);

main(argc, argv)
	char **argv;
{
	int days, hrs, mins;
	register int i, j;
	char *cp;
	register int curpid, empty;

	login = (argv[0][0] == '-');
	cp = rindex(argv[0], '/');
	firstchar = login ? argv[0][1] : (cp==0) ? argv[0][0] : cp[1];
	progname = cp = argv[0];	/* for Usage */

	while (argc > 1) {
		if (argv[1][0] == '-') {
			for (i=1; argv[1][i]; i++) {
				switch(argv[1][i]) {

				case 'd':
					debug++;
					break;

				case 'h':
					header = 0;
					break;

				case 'l':
					lflag++;
					break;

				case 's':
					lflag = 0;
					break;

				case 'u':
				case 'w':
					firstchar = argv[1][i];
					break;

				default:
					printf("%s: Bad flag %s\n", progname, argv[1]);
					exit(1);
				}
			}
		} else {
			if (!isalnum(argv[1][0]) || argc > 2) {
				printf("Usage: %s [ -hlsuw ] [ user ]\n", cp);
				exit(1);
			} else
				sel_user = argv[1];
		}
		argc--; argv++;
	}

	if ((kmem = open("/dev/kmem", 0)) < 0) {
		fprintf(stderr, "%s: No kmem\n",progname);
		exit(1);
	}
	nlist("/vmunix", nl);
	if (nl[0].n_type==0) {
		fprintf(stderr, "%s: No namelist\n",progname);
		exit(1);
	}

	if (firstchar != 'u')
		readpr();

	if((ut = fopen("/etc/utmp","r")) == NULL) {
		fprintf(stderr, "%s: Error opening /etc/utmp\n",progname);
		exit(1);
	}
	time(&now);
	if (header) {
		/* Print time of day */
		prtat(&now);

		/*
		 * Print how long system has been up.
		 * (Found by looking for "boottime" in kernel)
		 */
		klseek(kmem, (long)nl[X_BOOTTIME].n_value, 0);
		klread(kmem, &boottime, sizeof (boottime));

		uptime = now - boottime.tv_sec;
		uptime += 30;
		days = uptime / (60*60*24);
		uptime %= (60*60*24);
		hrs = uptime / (60*60);
		uptime %= (60*60);
		mins = uptime / 60;

		printf("  up");
		if (days > 0)
			printf(" %d day%s,", days, days>1?"s":"");
		if (hrs > 0 && mins > 0) {
			printf(" %2d:%02d,", hrs, mins);
		} else {
			if (hrs > 0)
				printf(" %d hr%s,", hrs, hrs>1?"s":"");
			if (mins > 0)
				printf(" %d min%s,", mins, mins>1?"s":"");
		}

		/* Print number of users logged in to system */
		while (fread(&utmp, sizeof(utmp), 1, ut)) {
			if (utmp.ut_name[0] != '\0')
				nusers++;
		}
		rewind(ut);
		printf("  %d user%s", nusers, nusers>1?"s":"");

		/*
		 * Print 1, 5, and 15 minute load averages.
		 * (Found by looking in kernel for avenrun).
		 */
		printf(",  load average:");
		klseek(kmem, (long)nl[X_AVENRUN].n_value, 0);
		klread(kmem, avenrun, sizeof(avenrun));
		for (i = 0; i < (sizeof(avenrun)/sizeof(avenrun[0])); i++) {
			if (i > 0)
				printf(",");
			printf(" %.2f", avenrun[i]);
		}
		printf("\n");
		if (firstchar == 'u')
			exit(0);

		/* Headers for rest of output */
		if (lflag)
			printf("User     tty       login@  idle   JCPU   PCPU  what\n");
		else
			printf("User    tty  idle  what\n");
		fflush(stdout);
	}


	for (;;) {	/* for each entry in utmp */
		if (fread(&utmp, sizeof(utmp), 1, ut) == NULL) {
			fclose(ut);
			exit(0);
		}
		if (utmp.ut_name[0] == '\0')
			continue;	/* that tty is free */
		if (sel_user && strcmpn(utmp.ut_name, sel_user, NMAX) != 0)
			continue;	/* we wanted only somebody else */

		gettty();
		jobtime = 0;
		proctime = 0;
		strcpy(doing, "-");	/* default act: normally never prints */
		empty = 1;
		curpid = -1;
		idle = findidle();
		for (i=0; i<np; i++) {	/* for each process on this tty */
			if (!(TTYEQ))
				continue;
			jobtime += pr[i].w_time + pr[i].w_ctime;
			proctime += pr[i].w_time;
			/* 
			 * Meaning of debug fields following proc name is:
			 * & by itself: ignoring both SIGINT and QUIT.
			 *		(==> this proc is not a candidate.)
			 * & <i> <q>:   i is SIGINT status, q is quit.
			 *		0 == DFL, 1 == IGN, 2 == caught.
			 * *:		proc pgrp == tty pgrp.
			 */
			if (debug) {
				printf("\t\t%d\t%s", pr[i].w_pid, pr[i].w_args);
				if ((j=pr[i].w_igintr) > 0)
					if (j==IGINT)
						printf(" &");
					else
						printf(" & %d %d", j%3, j/3);
				printf("\n");
			}
			if (empty && pr[i].w_igintr!=IGINT) {
				empty = 0;
				curpid = -1;
			}
			if(pr[i].w_pid>curpid && (pr[i].w_igintr!=IGINT || empty)){
				curpid = pr[i].w_pid;
				strcpy(doing, lflag ? pr[i].w_args : pr[i].w_comm);
#ifdef notdef
				if (doing[0]==0 || doing[0]=='-' && doing[1]<=' ' || doing[0] == '?') {
					strcat(doing, " (");
					strcat(doing, pr[i].w_comm);
					strcat(doing, ")");
				}
#endif
			}
		}
		putline();
	}
}

/* figure out the major/minor device # pair for this tty */
gettty()
{
	char ttybuf[20];
	struct stat statbuf;

	ttybuf[0] = 0;
	strcpy(ttybuf, "/dev/");
	strcat(ttybuf, utmp.ut_line);
	stat(ttybuf, &statbuf);
	tty = statbuf.st_rdev;
	uid = statbuf.st_uid;
}

/*
 * putline: print out the accumulated line of info about one user.
 */
putline()
{
	register int tm;

	/* print login name of the user */
	printf("%-*.*s ", NMAX, NMAX, utmp.ut_name);

	/* print tty user is on */
	if (lflag)
		/* long form: all (up to) LMAX chars */
		printf("%-*.*s", LMAX, LMAX, utmp.ut_line);
	else {
		/* short form: 2 chars, skipping 'tty' if there */
		if (utmp.ut_line[0]=='t' && utmp.ut_line[1]=='t' && utmp.ut_line[2]=='y')
			printf("%-2.2s", &utmp.ut_line[3]);
		else
			printf("%-2.2s", utmp.ut_line);
	}

	if (lflag)
		/* print when the user logged in */
		prtat(&utmp.ut_time);

	/* print idle time */
	if (idle >= 36 * 60)
		printf("%2ddays ", (idle + 12 * 60) / (24 * 60));
	else
		prttime(idle," ");

	if (lflag) {
		/* print CPU time for all processes & children */
		prttime(jobtime," ");
		/* print cpu time for interesting process */
		prttime(proctime," ");
	}

	/* what user is doing, either command tail or args */
	printf(" %-.32s\n",doing);
	fflush(stdout);
}

/* find & return number of minutes current tty has been idle */
time_t findidle()
{
	struct stat stbuf;
	long lastaction, diff;
	char ttyname[20];

	strcpy(ttyname, "/dev/");
	strcatn(ttyname, utmp.ut_line, LMAX);
	stat(ttyname, &stbuf);
	time(&now);
	lastaction = stbuf.st_atime;
	diff = now - lastaction;
	diff = DIV60(diff);
	if (diff < 0) diff = 0;
	return(diff);
}

#define	HR	(60 * 60)
#define	DAY	(24 * HR)
#define	MON	(30 * DAY)

/*
 * prttime prints a time in hours and minutes or minutes and seconds.
 * The character string tail is printed at the end, obvious
 * strings to pass are "", " ", or "am".
 */
prttime(tim, tail)
	time_t tim;
	char *tail;
{

	if (tim >= 60) {
		printf("%3d:", tim/60);
		tim %= 60;
		printf("%02d", tim);
	} else if (tim > 0)
		printf("    %2d", tim);
	else
		printf("      ");
	printf("%s", tail);
}

char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

/* prtat prints a 12 hour time given a pointer to a time of day */
prtat(time)
	long *time;
{
	struct tm *p;
	register int hr, pm;

	p = localtime(time);
	hr = p->tm_hour;
	pm = (hr > 11);
	if (hr > 11)
		hr -= 12;
	if (hr == 0)
		hr = 12;
	if (now - *time <= 18 * HR)
		prttime(hr * 60 + p->tm_min, pm ? "pm" : "am");
	else if (now - *time <= 7 * DAY)
		printf(" %s%2d%s", weekday[p->tm_wday], hr, pm ? "pm" : "am");
	else
		printf(" %2d%s%2d", p->tm_mday, month[p->tm_mon], p->tm_year);
}

struct pte *Usrptma, *usrpt, *pte, apte;
/*
 * readpr finds and reads in the array pr, containing the interesting
 * parts of the proc and user tables for each live process.
 */
readpr()
{
	int pn, mf, addr, c;
	int szpt, pfnum, i;
	struct dblock db;

	Usrptma = (struct pte *) nl[X_USRPTMA].n_value;
	usrpt = (struct pte *) nl[X_USRPT].n_value;
	if((mem = open(memf = "/dev/mem", 0)) < 0) {
		fprintf(stderr, "%s: No mem\n",progname);
		exit(1);
	}
	if ((swap = open(swapf = "/dev/drum", 0)) < 0) {
		fprintf(stderr, "%s: No drum\n",progname);
		exit(1);
	}
	/*
	 * read mem to find swap dev.
	 */
	klseek(kmem, (long)nl[X_SWAPDEV].n_value, 0);
	klread(kmem, &nl[X_SWAPDEV].n_value, sizeof(nl[X_SWAPDEV].n_value));
	/*
	 * Find base of and parameters of swap
	 */
	nswap = getw(nl[X_NSWAP].n_value);
	dmmin = getw(nl[X_DMMIN].n_value);
	dmmax = getw(nl[X_DMMAX].n_value);
	/*
	 * validate proc and user structure sizes
	 */
#ifdef ibm032
	if (nl[X_USERSIZE].n_value) {
		usersize = getw(nl[X_USERSIZE].n_value);
		procsize = getw(nl[X_PROCSIZE].n_value);
		if (procsize != sizeof (struct proc) ||
				usersize != sizeof (struct user)) {
/*			fprintf(stderr,"%s: %s and kernel (%s) are inconsistent (proc%+d/user%+d)\n",progname,progname,"/vmunix",procsize-sizeof (struct proc),usersize-sizeof (struct user)); /* debug */
		}
	}
#endif ibm032
	/*
	 * Locate proc table
	 */
	klseek(kmem, (long)nl[X_NPROC].n_value, 0);
	klread(kmem, &nproc, sizeof(nproc));
	pr = (struct pr *)calloc(nproc, sizeof (struct pr));
	np = 0;
	klseek(kmem, (long)nl[X_PROC].n_value, 0);
	klread(kmem, &aproc, sizeof(aproc));
	for (pn=0; pn<nproc; pn++) {
		if (procsize == sizeof (struct proc))
			klseek(kmem, (int)(aproc + pn), 0);
		else
			klseek(kmem, ((int)aproc) + procsize * pn, 0);
		klread(kmem, &mproc, sizeof mproc);
		/* decide if it's an interesting process */
		if (mproc.p_stat==0 || mproc.p_pgrp==0)
			continue;
		/* find & read in the user structure */
		if (!getu(&mproc,&up,0,0))
			continue;
#if !defined(ibm032) || !defined(BSD4_3)
		vstodb(0, CLSIZE, &up.u_smap, &db, 1);
#else
		vstodb(CLSIZE, CLSIZE, &up.u_smap, &db, 1);
#endif
		pr[np].w_lastpg = dtob(db.db_base);
		if (up.u_ttyp == NULL)
			continue;

		/* save the interesting parts */
		pr[np].w_pid = mproc.p_pid;
		pr[np].w_flag = mproc.p_flag;
		pr[np].w_size = mproc.p_dsize + mproc.p_ssize;
		pr[np].w_igintr = (((int)up.u_signal[2]==1) +
		    2*((int)up.u_signal[2]>1) + 3*((int)up.u_signal[3]==1)) +
		    6*((int)up.u_signal[3]>1);
		pr[np].w_time =
		    up.u_ru.ru_utime.tv_sec + up.u_ru.ru_stime.tv_sec;
		pr[np].w_ctime =
		    up.u_cru.ru_utime.tv_sec + up.u_cru.ru_stime.tv_sec;
		pr[np].w_tty = up.u_ttyd;
		pr[np].w_uid = mproc.p_uid;
		up.u_comm[14] = 0;	/* Bug: This bombs next field. */
		strcpy(pr[np].w_comm, up.u_comm);
		/*
		 * Get args if there's a chance we'll print it.
		 * Cant just save pointer: getargs returns static place.
		 * Cant use strcpyn: that crock blank pads.
		 */
		pr[np].w_args[0] = 0;
		strcatn(pr[np].w_args,getargs(&mproc,ARGWIDTH),ARGWIDTH);
		if (pr[np].w_args[0]==0 || pr[np].w_args[0]=='-' && pr[np].w_args[1]<=' ' || pr[np].w_args[0] == '?') {
			strcat(pr[np].w_args, " (");
			strcat(pr[np].w_args, pr[np].w_comm);
			strcat(pr[np].w_args, ")");
		}
		np++;
	}
}

/*
 * get word at "addr" in kmem
 */
getw(addr)
{
	int value;
	klseek(kmem, (long) addr, 0);
	klread(kmem, &value, sizeof(value));
	return(value);
}

panic(cp)
	char *cp;
{

	/* printf("%s\n", cp); */
}

min(a, b)
{

	return (a < b ? a : b);
}


char *
getargs(mproc,width)
	register struct proc *mproc;
	register int width;
{
	register char *p;
	p = getcmd(mproc, 0, 0);
	if (strlen(p) > width)
		p[width] = 0;
	return(p);
}

char *savestr(str) 
	register char *str;
{
	static char temp[ARGWIDTH+1];
	register char *p;

	for (p=temp; (*p = *str++) && p<temp+ARGWIDTH; ++p)
		;
	
	*p = 0;
	temp[ARGWIDTH] = 0;
	return(temp);
}

/*-------------------------------CUT HERE-------------------------------------*/

/*
 * this code isolates the system dependancies (or most of them) 
 * in getting the 'u' area from the kernel memory or swap area
 * and also obtains the argument list (and possibly the environent)
 * from the user process.
 *
 * this code is (currently) included in ps and w.
 * YET TO BE DONE:
 * 1. isolate the symbols needed by this code into this module 
 * 	and do a merge before calling 'nlist' to obtain them.
 * 2. put into a separate file (getu.c?)
 * 3. put the object code into a library (-lps?) for general availability.
 *
 * the #ifndef FILE is so that the #includes aren't done twice (for now)
 * but are present so that later this can be separated into getu.c
 */

#ifndef FILE
#include <stdio.h>

#include <sys/param.h>
#include <sys/tty.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <machine/pte.h>
#include <sys/vm.h>
#include <sys/text.h>
#include <sys/stat.h>
#include <sys/mbuf.h>
#ifdef ibm032
#include <machine/fp.h>
#endif
#endif NFILE

#ifdef vax
#define clear(x) 	((int)x & 0x7fffffff)
#endif
#ifdef ibm032
#define clear(x) 	((int)x & 0x0fffffff)
#endif

/*
 * external input variables (should be parameters but this was a lot
 * easier to do.
 */
extern int swap;
extern int kmem;
extern int mem;
char	*kmemf, *memf, *swapf, *nlistf;
int	argaddr;
int	pcbpf;
extern struct	pte *Usrptma, *usrpt;
int	dflg;			/* debug flag */
int	kflg;			/* if reading from kernel */
char	*savestr();
extern int	nproc, ntext;
extern int	dmmin, dmmax;
extern int	nswap;

#ifdef ibm032
#ifdef BSD4_3
#define REDSIZE CLSIZE*2		/* red zone size plus reserved page */
#else
#define REDSIZE CLSIZE			/* red zone size */
#endif BSD4_3
struct userx
{
	char userfill[UPAGES*NBPG-sizeof (struct user)];
	struct user user;
};

union {
	struct	userx userx;
	char	upages[UPAGES][NBPG];
} user;
#define U	user.userx.user
char *kernel_stack_top = &user.upages[0][0];
char *kernel_stack_base = (char *) &u;

#else
#define REDSIZE 0		/* red zone size */

union {
	struct	user user;
	char	upages[UPAGES][NBPG];
} user;
char *kernel_stack_base = &user.upages[UPAGES][0];
char *kernel_stack_top = (char *) u.u_stack;
#define U	user.user
#endif	ibm032


/*
 * read in the u area in a (pretty much) machine independent fasion.
 * mproc is pointer to (incore) proc structure 
 * uptr is where to copy the result to
 * sflg != 0	read the kernel stack as well as the u structure
 * kflg != 0	post mortem mode
 */
getu(mproc, uptr, sflg, kflg)
	register struct proc *mproc;
	register struct user *uptr;
{
	struct pte *pteaddr, apte;
/* on ibm032 the arrangement is:
 * 1. top of user stack
 * 2. red zone
 * 3. kernel stack 
 * 4. kernel stack and u area
 * 
 * the u area is at the very end of P1.
 */
	struct pte arguutl[UPAGES+CLSIZE +REDSIZE];
	register int i;
	int ncl, size;

#ifdef ibm032
	size = ctob(UPAGES);
#else
	size = sflg ? ctob(UPAGES) : sizeof (struct user);
#endif
	if (dflg > 2)
		printf("getu(%x,%x,%d,%d)\n",mproc,uptr,sflg,kflg);
	if ((mproc->p_flag & SLOAD) == 0) {
		if (swap < 0) {
			bzero((char *) uptr, sizeof (struct user));
			return (1);	/* pretend we got it (for -k case) */
		}
		(void) klseek(swap, (long)dtob(mproc->p_swaddr), 0);
		if (klread(swap, (char *)user.upages, size) != size) {
			fprintf(stderr, "getu: cant read u for pid %d from %s\n",
			    mproc->p_pid, swapf);
			return (0);
		}
		pcbpf = 0;
		argaddr = 0;
#ifdef ibm032
		if ((i = usersize - sizeof (struct user)) > 0)
			bcopy(((char *) &U) - i, (char *) uptr, sizeof (struct user));	/* fake the location of the u structure */
		else
#endif ibm032
		*uptr = U;   /* added 8-9-85 for consistency */
		return (1);
	}
	if (kflg)
		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 (klread(kmem, (char *)&apte, sizeof(apte)) != sizeof(apte)) {
		printf("getu: cant read indir pte to get u for pid %d from %s\n",
		    mproc->p_pid, swapf);
		return (0);
	}
/* locate the last entry in the page table array which will include 
 * the entry for the u area 
 */
	klseek(mem,
	    (long)ctob(apte.pg_pfnum+1) - (UPAGES+CLSIZE+REDSIZE) * sizeof (struct pte),
		0);
	if (klread(mem, (char *)arguutl, sizeof(arguutl)) != sizeof(arguutl)) {
		printf("getu: 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[CLSIZE+REDSIZE].pg_pfnum;
	ncl = (size + NBPG*CLSIZE - 1) / (NBPG*CLSIZE);
	while (--ncl >= 0) {
		long addr;
		i = ncl * CLSIZE;
		addr = (long)ctob(arguutl[CLSIZE+REDSIZE+i].pg_pfnum);
		if (addr == 0) {
			bzero((char *) uptr, sizeof (struct user));
			return(1);	/* faked for swapper */
		}
		klseek(mem, addr, 0);
		if (klread(mem, user.upages[i], CLSIZE*NBPG) != CLSIZE*NBPG) {
			printf("getu: cant read page %d of u of pid %d from %s\n",
			    arguutl[CLSIZE+REDSIZE+i].pg_pfnum, mproc->p_pid, memf);
			return(0);
		}
	}
#ifdef ibm032
	if ((i = usersize - sizeof (struct user)) > 0)
		bcopy(((char *) &U) - i, (char *) uptr, sizeof (struct user));	/* fake the location of the u structure */
	else
#endif ibm032
	*uptr = U;	/* return the structure */
	if (dflg > 3)
		prhex("user struct",
			(char *) uptr,dflg > 4 ? sizeof (struct user) : 100 );
	return (1);
}

prhex(msg,str,length)
register char *msg, *str; register int length;
{
	printf("%s @%x (%d bytes)\n",msg,str,length);
	while (--length >= 0)
		printf(" %02x",*str++);
	printf("\n");
}

char *
getcmd(mproc,cflg,eflg)
	register struct proc *mproc;
	int cflg;
	int eflg;
{
	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;
	char *file;
	char *limit;

	if (mproc->p_stat == SZOMB || mproc->p_flag&(SSYS|SWEXIT))
		return ("");
	if (cflg) {
		(void) strncpy(cmdbuf, u.u_comm, sizeof (u.u_comm));
		if (dflg)
			printf("cmdbuf1=%s\n",cmdbuf);
		return (savestr(cmdbuf));
	}
	if ((mproc->p_flag & SLOAD) == 0 || argaddr == 0) {
		if (swap < 0)
			goto retucomm;
#if !defined(ibm032) || !defined(BSD4_3)
		vstodb(0, CLSIZE, &u.u_smap, &db, 1);
#else
		vstodb(CLSIZE, CLSIZE, &u.u_smap, &db, 1);
#endif
		(void) klseek(swap, (long)dtob(db.db_base), 0);
		if (klread(swap, (char *)&argspac, sizeof(argspac))
		    != sizeof(argspac))
			goto bad;
		file = swapf;
	} else {
		klseek(mem, (long)argaddr, 0);
		if (klread(mem, (char *)&argspac, sizeof (argspac))
		    != sizeof (argspac))
			goto bad;
		file = memf;
	}
	if (dflg > 3)
		prhex("argspac",(char *) &argspac,
			dflg > 8 ? sizeof argspac :  (2 << dflg));
	ip = &argspac.argi[CLSIZE*NBPG/sizeof (int)];
#if defined(ibm032) && defined(NFL) && !defined(BSD4_3)
	if (dflg>3)
		printf("sizeof fp_mach = %d\n",sizeof (struct fp_mach));
	ip -= sizeof (struct fp_mach) / sizeof (int);
#endif ibm032
	limit = (char *) ip;
	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 < limit; cp++) {
		c = *cp & 0177;
		if (c == 0)
			*cp = ' ';
		else if (c < ' ' || c > 0176) {
			if (++nbad >= 5*(eflg+1)) {
				*cp++ = ' ';
				break;
			}
			*cp = '?';
		} else if (eflg == 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 (xflg == 0 && gflg == 0 && tptr == 0 && cp[0] == '-')
		return (0);
*/
	if (dflg)
		printf("cmdbuf2=%s\n",cmdbuf);
	return (savestr(cmdbuf));

bad:
	fprintf(stderr, "getcmd: 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, ")");
	if (dflg)
		printf("cmdbuf3=%s\n",cmdbuf);
	return (savestr(cmdbuf));
}

/*
 * 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;
	register swblk_t *ip = dmp->dm_map;

	vsbase = ctod(vsbase);
	vssize = ctod(vssize);
	if (vsbase < 0 || vsbase + vssize > dmp->dm_size)
		panic("vstodb");
	while (vsbase >= blk) {
		vsbase -= blk;
		if (blk < dmmax)
			blk *= 2;
		ip++;
	}
	if (*ip <= 0 || *ip + blk > nswap)
		panic("vstodb *ip");
	dbp->db_size = min(vssize, blk - vsbase);
	dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
}


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

	if (kflg)
		loc = clear(loc);
	if (dflg > 1)
		printf("klseek(%s,%x,%x)\n",fd == kmem ? "kmem" : (fd == mem ? "mem" : "swap"),loc,off);
	(void) lseek(fd, (long)loc, off);
}


klread(fd, buff, length) char *buff;
{
	register int n = read(fd,buff,length);
	register int i, j = (dflg-2) << 4;

	if (dflg > 1)
		printf("klread(%s,%x,%x) (%x) ",fd == kmem ? "kmem" : (fd == mem ? "mem" : "swap"),buff,length,n);
	for (i=0; i<n && i < j ; ++i)
		printf(" %02x",buff[i]);
	if (dflg > 1)
		printf("\n");
	return(n);
}

