Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Apr 2004 04:05:25 +0200
From:      Cyrille Lefevre <cyrille.lefevre@laposte.net>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   bin/65803: ps enhancements (posix syntax, and more)
Message-ID:  <20040420020525.GA66658@gits.dyndns.org>
Resent-Message-ID: <200404200210.i3K2AFfu011856@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         65803
>Category:       bin
>Synopsis:       ps enhancements (posix syntax, and more)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr 19 19:10:15 PDT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Cyrille Lefevre
>Release:        FreeBSD 5.2-CURRENT i386
>Organization:
ACME
>Environment:
System: FreeBSD gits 5.2-CURRENT FreeBSD 5.2-CURRENT #14: Mon Apr 19 05:29:50 CEST 2004 root@gits:/disk3/freebsd/current/obj/disk3/freebsd/current/src/sys/CUSTOM i386
>Description:

here is a summarized description of changes. unfortunatelly, the manual page
has not been rewritten yet to reflect all these changes, sorry. it's on my
way, but only when I'll know which changes will be accepted or rejected.

enhancements to the bsd syntax are :

-A		same as posix -A.
-a		show all processes with a controlling terminal.
		in sunos compat mode (-Q), don't show session leader processes.
-G grouplist	same as posix -G (as recently implemented).
-g		show all processes with a controlling terminal and session
		leader processes.
(-k keylist)	sort by the specified keyword(s) (option taken from netbsd,
		*not implemented yet*).
(-K)		don't show kernel thread (option taken from openbsd -k,
		*not implemented yet*).
-n		numerical output of the user and wchan fields (option taken
		from sunos).
-p pidlist	accept a list of pids (as recently implemented).
-s		show signal output, same as -O SFMT (option taken from tru64,
		conflicts w/ netbsd -s, aka show threads w/ different fields,
		IMHO, this may be implemented as part of freebsd -H).
-t ttyslist	accept a list of ttys (as recently implemented).
		also accept ttyXX in addition to /dev/ttyXX and XX (as
		required by susv3).
-U userlist	accept a list of effective user names (as recently implemented).
		also accept euids in addition of effective user names.
-X		don't show processes without a controlling terminal (opposite
		of -x) (as recently implemented).
-Z		the label field is always prepended.

output is formated more intelligently when combining options such as -j, -l, -s,
-u and -v.  format strings are combined instead of being concatenated.
so, field are always in the same place.

old way :
$ ps -lu
UID   PID  PPID CPU PRI NI   VSZ  RSS MWCHAN STAT  TT       TIME COMMAND          USER %CPU %MEM STARTED
$ps -ul
USER   PID %CPU %MEM   VSZ  RSS  TT  STAT STARTED      TIME COMMAND            UID  PPID CPU PRI NI MWCHAN

new way :
$ ps -lu # or ps -ul
USER   UID   PID  PPID CPU PRI  NI %CPU %MEM   VSZ   RSS MWCHAN   TT STAT STARTED      TIME COMMAND

standard posix syntax is :

-A		show all processes, whether or not they have a controlling
		terminal or they are a session leader process.
-a		show all processes with a controlling terminal except session
		leader processes.
-d		show all processes with or without a controlling terminal
		except session leader processes.
-e		show all processes (same as -A).
-f		user oriented output.
-G grouplist	select processes by effective group name(s) or effective group
		id(s) (well, the standard says that is should be only id(s) and
		not name(s), which isn't coherent w/ -U and -u !).
-g pgidlist	select processes by process group id(s).
-l		long format output.
-n namelist	see below.
-o fmtlist	similar to bsd -o fmtlist.
-p pidlist	select processes by process id(s).
-t ttylist	select processes by terminal name(s) which may have the form
		co (for the system console), XX, ttyXX ot /dev/ttyXX.
-U userlist	select processes by real user name(s) or real user id(s).
-u userlist	select processes by effective user name(s) or effective user
		id(s).

enhancements to the posix syntax are :

-C cmdlist	select processes by process name (option taken from hpux).
-c		omits the nice field and replaces the c field with the
		class field (option taken from solaris).
-j		job control oriented output (option taken from almost all sysv
		implementations)
-L		show threads (option taken from solaris).
-M		label field prepended (option taken from irix).
-m		show threads (option taken from tru64).
-n system [-n core]
		specifies an alternative system namelist file
		and optionally, a core file if repeated (this is non standard).
-O		same as -o pid,fmtlist,tty,time,comm
-o help		behaves as bsd -L.
-P		includes the psr field (option taken from solaris).
-s sidlist	select processes by session id (option taken from almost all
		sysv implementations).
-T		show threads (option taken from irix).
-x		wide output format (option taken from hpux, similar to bsd -ww).
-y		omits the f field and replaces the addr field with the rss
		field (option taken from solaris).

both syntax :

-q		enable or disable the posix compatibility.
-Q		enable or disable compatibility with sunos and tru64.
		in bsd mode, leader processes aren't selected by default,
		uses -g to show them.
		in bsd and posix modes, size are displayed in KMGPT format
		(ex.: 1.54M instead of 1580).

The lists used as argument are either an identifier or a list of identifiers
enclosed in " " (double quotes) and separated from one another by a comma or
one or more spaces, or both.  Because of the way the shell treats spaces and
tabs, you need to quote space-separated lists.

Options with a list used as argument may be specified more than once, and one
or more of them may be empty since at least one of the options contains a
specifier, if all the options does not contain a specifier, a warning message
is displayed and ps exits.

ex.:
	ps -p , -p ,$$, is right and refer to the current shell only while
	ps -p , -p '' is wrong.

Format string which contains both an equal sign and a comma are considered
to be comma only separated list. so, both
-o "pid=process id,ppid=parent pid,command=command line" and
-o "pid=child ppid=parent command=command" are well parsed,
unfortunately, mixing "user=user name,pid=pid command" isn't...

<quote "right">
When printing using the command keyword, a process that has exited and
has a parent that has not yet waited for the process (in other words, a
zombie) is listed as ``<defunct>''.
</quote>

<quote "wrong">
and a process which is blocked while trying to exit is listed as ``<exiting>''
</quote>

<cite "netbsd">
If the command vector cannot be located (usually because it has not been
set, as is the case of system processes and/or kernel threads) the com-
mand name is printed within square brackets.

If the command vector cannot be located (usually because it has not been
set, as is the case of system processes and/or kernel threads) the com-
mand name is printed within square brackets.
</cite>

modified keywords :

     state     The state is given by a sequence of characters, for example,
	       ``RWNA''.  The first character indicates the run state of the
	       process:

	       O       Marks a running process (on processor).
	       		(state taken from solaris and irix)

	       Additional characters after these, if any, indicate additional
	       state information:

	       >       The process has specified a soft limit on memory
		       requirements and is currently exceeding that limit;
		       such a process is (necessarily) not swapped.
			(historical state taken from 4.3bsd)
	       a       The process is using scheduler activations.
			(state taken from netbsd)
               K       The process is a kernel thread (mutially exclusive w/ L).
			(state taken from openbsd)

	comm		command (the argv[0] value)
			(description change)
	etime		elapsed time since the process was started, in the form
			[[dd-]hh:]mm:ss (description change taken from netbsd)
if WITH_MAXRSS is defined -- implemented
	lim		memory usage limit (description change taken from
			openbsd)
	logname		login name of user who started the process (alias login)
			(description change taken from netbsd/openbsd)
	paddr		kernel virtual address of the struct proc belonging to
			the process (description change taken from netbsd)

added keywords (bsd) :

	keyword	header	description
	ctime	CTIME	accumulated CPU time of all children that have exited
			[netbsd]
	dsiz	DSIZ	data size (in Kbytes) [openbsd]
	egid	EGID	effective group id [netbsd/openbsd]
	egroup	EGROUP	group name (from egid) [netbsd/openbsd]
	euid	EUID	effective user id [netbsd/openbsd]
	euser	EUSER	user name (from euid) [netbsd/openbsd]
	gid	GID	effective group id [netbsd/openbsd]
	group	GROUP	group name (from gid) [openbsd/hpux/irix/solaris/tru64]
	groups	GROUPS	group access list [netbsd/openbsd]
	groupnames GROUPNAMES group names (from group access list)
			[netbsd/openbsd]
	holdcnt	HOLDCNT	number of holds on the process (if non-zero, process
			can't be swapped) [netbsd/openbsd]
	rssize		alias to rsz [43reno/44bsd/netbsd/openbsd/tru64]
	rsz	RSZ	alias to rss [43reno/44bsd/netbsd/openbsd/tru64]
	ssiz	SSIZ	stack size (in Kbytes) [openbsd]
	svgroup	SVGROUP	group name (from svgid) [netbsd/openbsd]
	svuser	SVUSER	user name (from svuid) [netbsd/openbsd]
if WITH_UMASK is defined
	umask	UMAKS	process umask [43reno/tru64]

required keywords (posix) :

	keyword	header	description
	addr	ADDR	alias to paddr [hpux/solaris/tru64]
	c	C	alias to cpu [solaris/tru64]
	class	CLS	process class scheduling [irix/solaris]
	psr	PSR	processor number to which the process is bound
			[solaris/tru64]
	s	S	alias to state [solaris]
	stime	STIME	started time of the process, in the form hh:mm:ss or
			mmm:dd if the process is older than one day.
			[hpux/irix/solaris/tru64]
	sz	SZ	alias to vsz [hpux/tru64]


if COMPAT_TRU64 is defined
	DFMT	similar to "pid tname state cputime command"
	F5FMT	similar to "uname pid ppid pcpu start tt time command"
	FL5FMT	similar to "f state uid pid ppid pcpu pri nice rss wchan start
			    time command"
	JFMT	similar to "user pid ppid pgid sess jobc state tname cputime
			    command"
	L5FMT	similar to "f state uid pid ppid pcpu pri nice rss wchan tt time
			    ucomm"
	LFMT	similar to "uid pid ppid cp pri nice vsz rss wchan state tname
			    cputime command"
	RUSAGE	similar to "minflt majflt nswap inblock oublock msgsnd msgrcv
			    nsigs nvcsw nivcsw"
	SCHED	similar to "user pcpu pri usrpri nice psxpri psr policy pset"
	SFMT	similar to "uid pid cursig sig sigmask sigignore sigcatch
			    stat tname command"
	THREAD	similar to "user pcpu pri scnt wchan usertime systime"
	UFMT	similar to "uname pid pcpu pmem vsz rss tt state start time
			    command"
	VFMT	similar to "pid tt state time sl pagein vsz rss pcpu pmem
			    command"

	keyword	header	description
	cp	CP	alias to cpu [43reno/tru64]
if WITH_CURSIG is defined
	cursig	CURSIG	current signal [tru64]
	policy	POL	alias to class [tru64]
	pset	PSET	current processor set (not implemented yet)
			(hpux/solaris/tru64)
	psxpri		alias to pri [tru64]
	scnt	SCNT	suspend count (not implmented yet) [tru64]
	sess	SESS	alias to sid [43reno/netbsd/openbsd/tru64]
if WITH_CHILDTIMES is defined
	systime	SYSTIME	accumulated system CPU time [tru64]
	tsess	TSESS	alias to tsid [43reno/netbsd/openbsd/tru64]
	uname		alias to user [43reno/tru64]
if WITH_CHILDTIMES is defined
	usertime USRTIME accumulated user CPU time [tru64]

if WITH_EXTRA_ALIASES is defined

	keyword	header	description
	cls		alias to class [hpux]
	cmd	CMD	alias to command [tru64]
	dev		alias to tdev [43reno/tru64]
	flag		alias to f (43reno/irix/tru64)
	fname	FNAME	alias to ucomm [solaris]
	intpri		alias to pri [hpux]
	ivcsw	IVCSW	alias to nicvsw [43reno/tru64]
	ktracep	KTRACEP	tracing vnode [43reno/netbsd/openbsd]
	longtname	alias to tty [43reno/tru64]
	longtty		alias to tty [43reno/tru64]
	maxrss		alias to lim (43reno)
	opri		alias to pri [solaris]
	osz		alias to sz [solaris]
	p	P	alias to psr [irix]
	p_rss		alias to rss [43reno/tru64]
	procaddr	alias to paddr (43reno)
	resident	alias to re (43reno)
	runame		alias to ruser [43reno/tru64]
	scount		alias to scnt [tru64]
	session		alias to sess [tru64]
	size		alias to sz [just for convenience]
	slp		alias to sl [43reno/tru64]
	slptime		alias to sl [43reno/tru64]
	tname		alias to tt [43reno/tru64]
	traceflag	alias to ktrace (43reno)
	tsession	alias to tsess [43reno/tru64]
if WITH_UMASK is defined
	u_cmask		alias to umask [43reno/tru64]
	u_procp		alias to uprocp [43reno/tru64]
	util		alias to c [irix]
	vcsw	VCSW	alias to nvcsw [43reno/tru64]
	wname		alias to mwchan [irix]

if WITH_NOP_ALIASES is defined

	keyword	header	description
	emul	EMUL	name of system call emulation environment (keyword
			taken from openbsd)
	lid	LID	ID of the LWP [netbsd]
	lstate		symbolic LWP state [netbsd]
	lwp	LWP	ID of the LWP [solaris]
	nlwp	NLWP	number of LWPs in the process [netbsd/solaris]
	p_ru	P_RU	resource usage pointer (valid only for zombie)
			[43reno/44bsd/netbsd/openbsd]
	poip	POIP	pageouts in progress (43reno)
	prmgrp	PRMGRP	[hpux]
	prmid	PRMID	[hpux]
	projid	PROJID	project ID of the process [solaris]
	project	PROJECT	project name of the process [solaris]
	rlink	RLINK	reverse link on run queue [43reno/44bsd/netbsd/openbsd]
	rlwp	RLWP	number of LWPs on a processor or run queue [netbsd]
	status	STATUS	process status [tru64]
	taskid	TASKID	task ID of the process [solaris]
	trs	TRS	text resident size [43reno]

here is a detailled description of source level changes :

*** fmt.c

const char * prototypes added

fmt_argv() say [xxx] instead of " (xxx)" if argv is empty as on netbsd.
mostly for system processes and zombies.

*** extern.h

cflag -> commandonly
now deleted -- replaced by gettime()
vhead -> varlist
fmtsep, posix and extended added
class() added
fmt_argv() w/ const char * prototype
gettime() added
gname()/s_gname() added
groups() added
groupnames() added
lockname() sorted
maxrss() #if defined(WITH_MAXRSS)
nop() added
parsefmt() w/ const char * prototype
rgroupname() -> rgname()
s_rgroupname() -> s_rgname()
s_time() added
stime() added
svgname()/s_svgname() added
svuname()/s_svuname() added
upr() added

if COMPAT_TRU64 is defined
systime()/usertime() added

*** keyword.c

var array modified and reformated so values are almost always aligned
the same way.

new keywords are + prefixed, modified keywords are ! prefixed
!	acflag		3 -> 4 (as xstat)
+	addr=ADDR	-> paddr
!	args		16 -> MAXCOMLEN (see sys/proc.h)
+	c=C		-> cpu (alias w/ different header)
+	class=CLS	-> class()
!	comm=COMMAND	-> ucomm (alias w/ different header)
!	command		16 -> MAXCOMLEN (see sys/proc.h)
+	ctime=CTIME	-> cldtime()/s_time()
+	dsiz		-> kvar(ki_dsize)
+	egid=EGID	-> gid
+	egroup=EGROUP	-> group
+	euid=EUID	-> uid
+	euser=EUSER	-> user
+	gid=GID		-> kvar(ki_groups[0])
+	group=GROUP	-> gname()/s_gname()
+	groupnames=GROUPNAMES	-> groupnames() (no dynamic sizing for instance)
+	groups=GROUPS	-> groups() (no dynamic sizing for instance)
+	holdcnt=HOLDCNT	-> kval(ki_lock)
!	inblk		4 -> 5 (medium counter)
if WITH_MAXRSS is defined
!	lim		nop (still "-") -> maxrss (requires kernel patch)
!	login		MAXLOGNAME-1 -> USERLEN (macro to MAXLOGNAME-1)
!	majflt		4 -> 7 (great counter)
!	minflt		4 -> 7 (great counter)
!	msgrcv		4 -> 7 (great counter)
!	msgsnd		4 -> 7 (great counter)
!	nice		2 -> 3 (-20 to 20)
!	nsigs		4 -> 5 (medium counter)
!	nswap		4 -> 5 (medium counter)
!	oublk		4 -> 5 (medium counter)
!	pagein		6 -> 7 (great counter)
+	psr=PSR		-> kvar(ki_oncpu)
!	rss		4 -> 5 (medium counter, as vsz)
!	rtprio		priorityr() is buggy (xxx:xxx instead of normal, etc)
+	rssize		-> rsz
+	rsz=RSZ		-> rss
+	s=S		-> state (alias w/ different header)
+	ssiz		-> kvar(ki_ssize)
+	stime=STIME	-> stime()
+	svgroup=SVGROUP	-> svgname()/s_svgname()
+	svuser=SVUSER	-> svuname()/s_svuname()
+	sz=SZ		-> vsz
!	tdev		4 -> 6 (xxx,xxx)
!	time		9 -> s_time()
!	tpgid		4 -> PIDLEN
!	tsiz		4 -> 5 (medium counter)
!	tt		3 -> 2 (as the manual says and other systems does)
if WITH_UMASK is defined
+	umask=UMASK	-> kvar(ki_cmask) (requires a kernel patch)
!	upr		kvar(ki_pri.pri_user) -> upr() (scaled to PZERO)
!	uprocp		-> paddr (alias w/ different header)

if COMPAT_TRU64 is defined
+	DFMT, F5FMT, FL5FMT, JFMT, L5FMT, LFMT, RUSAGE, SCHED, SFMT, THREAD,
	UFMT, VFMT	multiple aliases
+	cp=CP		-> cpu (alias w/ different header)
if WITH_CURSIG is defined
+	cursig=CURSIG	-> nop (requires kernel patch)
+	policy=POL	-> psr (alias w/ different header)
+	pset=PSET	-> nop (not implementable right now)
+	psxpri=PRI	-> pri (alias w/ different header)
+	scnt=SCNT	-> nop ()
+	sess=SESS	-> sid (alias w/ different header)
if WITH_CHILDTIMES is defined
+	systime=SYSTIME	-> systime()/s_time() (requires kernel patch)
+	tsess=TSESS	-> tsid (alias w/ different header)
+	uname		-> user (alias)
if WITH_CHILDTIMES is defined
+	usertime=USRTIME -> usertime()/s_time() (requires kernel patch)

parsefmt() converted to sys/queue.h and rewritten to handle
the new "internal format string" format which looks like :
	keyword[:flags in reverse polish notation][=header]
reverse polish notation:
	x by itself, * means any, x! means not x,
	xy| means x | y, xy& means x & y, xy^ means x ^ y.
	[xyz] is a more compact and readable form of xy|z|
	[a-z] isn't supported and is understood as a-|z|
	() may be used for readability but are no-op.
bsd flags: d12jlsuv, bsd modifiers: nZ
posix flags: d12fjl, posix modifiers: cMPy
both modifiers: X if extended.

this avoid duplicating similar format strings and on/off options.
also, blank separator is ignored if the format string contains "=".

findvar() rewritten to handle header specification on aliases.
now, you may say "ps -O acflg=XXX" as well as "ps -O acfalg=XXX"

initially, these functions have been rewritten to avoid duplicated
keywords. this has been fixed, since. also, prototypes are now
const char *.

*** print.c

static function prototypes added.
printheader() converted to sys/queue.h
arguments() converted to sys/queue.h, zombies are marked <defunct>.
	if posix, left is forced to 80.
command() same as arguments(). cflag -> commandonly.
ucomm() zombies are marked <defunct>. also, don't pad if last field.
state() 'O' (onproc) state added depending on ki_oncpu != NOCPU, I'm right ?
	'>', 'K' and 'a' sub-states added
pri() scalepri() macro added
upr() added, same as pri().
rgroupname()/s_rgroupname() -> rgname()/s_rgname() to match netbsd/openbsd
gname()/s_gname() added to handle the effective group-id.
groups() added to handle additional group-ids.
groupnames() added to handle additional group-names.
svgname()/s_svgname() added to handle the saved group-id.
svuname()/s_svuname() added to handle the saved user-id.
gettime() added in place of the now global variable.
started()/lstarted() gcc -Wall do not like %y, %b and %c !
stime() added
vsize() converted to printsize() (see below)
printtime()/s_time() added to handle posix vs. no posix times
cputime() converted to printtime()
cldtime() added
elapsed() converted to printtime()
nop() added
maxrss() handled using kernel mods
priorityr() debuggyfied :) PRI_ITHD added
class() added, based on priorityr().
printsize() added to handle tru64 size output format (see -Q above)
$ ps -u
root 96687  0.0  0.8  1528   980  p1  OL+   7:13PM   0:00.05 ps -u
$ ps -Qu
root 91419  0.0  0.8 1.49M  980K  p1  OL+   7:13PM   0:00.05 ps -XXu
$ ps -qlcy
OL+      0  1328 16850 208     96   980   284 -      ttyp1     00:00:00 ps
$ ps -Qqcly
OL+      0 76337 16850 208     96  980K  284K -      ttyp1     00:00:00 ps
printval() PGTOK converted to printsize()
label() '-' instead of nothing

#if defined(COMPAT_TRU64)
systime() added, printtime() of ki_rusage.ru_stime
usertime() added, printtime() of ki_rusage.ru_utime
summing children systime and usertime requires kernel mods...

many some functions buf[x] -> buf[BUFLEN]

*** ps.h

stddef.h and sys/queue.h added
LTIMELEN/STIMELEN/UIDLEN/USERLEN/KOFF added
VARENT converted to sys/queue.h

*** ps.c

many variables made static and many other variables made local
varlist converted to sys/queue.h
struct list added.
xkeep/xkeep_implied (was xflg) -> noctty
leader added
optfatal -> fatal
function prototypes modified:
	fmt() -> format() kvm_t * parameter added
	saveuser() kvm_t * parameter added
*fmt deleted
getopt loop moved from main() to parseopts()
	sets posix if getenv("POSIX") is != 0 (this is #defined) or
	if the command name contains a 'x'.
	sets extended is the command name begins with an 's'.
	nokudge made differently (maybe the old way, don't remember)
	nselectors / noctty / leader / all / nkept / are handled differently.
	the "new" process selection code has been integrated (or instead of
	and) and moved to findproc() to avoid goto's.
findproc() added, processes w/ a controlling terminal are selected using
	KI_CTTY (this is #defined).
parseopts() added.
getuids -> parselst() to handle list parameters.
addelem_*/*_list deleted (sorry, the new code conflicts with mine).
find_varentry()/scanvars()/dynsizevars()/sizevars() converted to sys/queue.h
fmt() -> format() w/ kvm_t * as parameter
saveuser() w/ kvm_t * as parameter, system processes are marked []
	needcomm/needenv && UREADOK() fallback added
pscomp() shortened...
kludge_oldps_options() posix disabled if no -
usage() updated.

>How-To-Repeat:
	n/a
>Fix:
Index: Makefile
===================================================================
RCS file: /home/ncvs/src/bin/ps/Makefile,v
retrieving revision 1.25
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.25 Makefile
--- Makefile	23 Feb 2004 20:05:14 -0000	1.25
+++ Makefile	20 Apr 2004 01:57:26 -0000
@@ -3,6 +3,8 @@
 
 PROG=	ps
 SRCS=	fmt.c keyword.c nlist.c print.c ps.c
+LINKS=	${BINDIR}/ps ${BINDIR}/xps \
+	${BINDIR}/ps ${BINDIR}/sps
 WARNS?=	5
 #
 # To support "lazy" ps for non root/wheel users
@@ -11,6 +13,57 @@
 # on large systems.
 #
 CFLAGS+=-DLAZY_PS
+#
+# To display some runtime debugging informations.
+#
+.if defined(DEBUG_FLAGS)
+CFLAGS+=-DDEBUG
+.endif
+#
+# enable posix mode through POSIX environment variable.
+#
+#CFLAGS+= -DWITH_POSIX_ENV
+#
+# select processes with a controlling terminals using
+# KI_CTTY instead of P_CONTROLT.
+#
+CFLAGS+= -DWITH_CTTY
+#
+# kernel supports ki_childstime and ki_childstime instead of ki_childtime.
+# (tru64 systime and usertime keywords).
+#
+CFLAGS+= -DWITH_CHILDTIMES
+#
+# kernel supports ki_cursig (cursig keyword).
+#
+CFLAGS+= -DWITH_CURSIG
+#
+# kernel supports ki_maxrss (lim keyword).
+#
+CFLAGS+= -DWITH_MAXRSS
+#
+# kernel supports ki_cmask (umask keyword).
+#
+CFLAGS+= -DWITH_UMASK
+#
+# enable tru64 keywords (*FMT, cp, cursig, policy, pset, psxpri,
+# scnt, sess, systime, tname, tsess, uname, usertime).
+#
+CFLAGS+= -DCOMPAT_TRU64
+#
+# enable yet more keywords (cls, cmd, dev, flag, fname, intpri,
+# ivcsw, ktracep, longtname, longtty, maxrss, opri, osz, p, p_rss,
+# procaddr, resident, runame, scount, session, size, slp, slptime,
+# traceflag, tsession, u_cmask, u_procp, util, vcsw, wname).
+#
+#CFLAGS+= -DWITH_EXTRA_ALIASES
+#
+# enable no-op keywords, aka not implemented yet.
+# (emul, lid, lstate, lwp, nlwp, p_ru, poip, prmgrp, prmid,
+# projid, project, rlink, rlwp, status, taskid, trs).
+#
+#CFLAGS+= -DWITH_NOP_ALIASES
+#
 DPADD=	${LIBM} ${LIBKVM}
 LDADD=	-lm -lkvm
 #BINGRP=	kmem
Index: extern.h
===================================================================
RCS file: /home/ncvs/src/bin/ps/extern.h,v
retrieving revision 1.32
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.32 extern.h
--- extern.h	6 Apr 2004 20:06:49 -0000	1.32
+++ extern.h	19 Apr 2004 22:15:39 -0000
@@ -27,60 +27,81 @@
  * SUCH DAMAGE.
  *
  *	@(#)extern.h	8.3 (Berkeley) 4/2/94
- * $FreeBSD$
+ * $FreeBSD: src/bin/ps/extern.h,v 1.9 1999/08/27 23:14:50 peter Exp $
  */
 
-struct kinfo;
-struct nlist;
-struct var;
-struct varent;
-
 extern fixpt_t ccpu;
-extern int cflag, eval, fscale, nlistread, rawcpu;
+extern int commandonly, eval, fscale, nlistread, rawcpu;
 extern unsigned long mempages;
-extern time_t now;
 extern int sumrusage, termwidth, totwidth;
-extern VARENT *vhead;
+extern VARLIST varlist;
+extern const char fmtsep[];
+
+extern int posix;
+extern int extended;
 
 __BEGIN_DECLS
 void	 arguments(KINFO *, VARENT *);
+void	 class(KINFO *, VARENT *);
 void	 command(KINFO *, VARENT *);
 void	 cputime(KINFO *, VARENT *);
+void	 cldtime(KINFO *, VARENT *);
 int	 donlist(void);
 void	 elapsed(KINFO *, VARENT *);
 VARENT	*find_varentry(VAR *);
-const	 char *fmt_argv(char **, char *, size_t);
+const	 char *fmt_argv(const char **, const char *, size_t);
 double	 getpcpu(const KINFO *);
+void	 gettime(time_t *, struct timeval *);
+void	 gname(KINFO *, VARENT *);
+void	 groups(KINFO *, VARENT *);
+void	 groupnames(KINFO *, VARENT *);
 void	 kvar(KINFO *, VARENT *);
 void	 label(KINFO *, VARENT *);
+void	 lockname(KINFO *, VARENT *);
 void	 logname(KINFO *, VARENT *);
 void	 longtname(KINFO *, VARENT *);
 void	 lstarted(KINFO *, VARENT *);
+#if defined(WITH_MAXRSS)
 void	 maxrss(KINFO *, VARENT *);
-void	 lockname(KINFO *, VARENT *);
+#endif
 void	 mwchan(KINFO *, VARENT *);
+void	 nop(KINFO *, VARENT *);
 void	 nwchan(KINFO *, VARENT *);
 void	 pagein(KINFO *, VARENT *);
-void	 parsefmt(const char *, int);
+void	 parsefmt(const char *, const char *);
 void	 pcpu(KINFO *, VARENT *);
 void	 pmem(KINFO *, VARENT *);
 void	 pri(KINFO *, VARENT *);
 void	 printheader(void);
 void	 priorityr(KINFO *, VARENT *);
-void	 rgroupname(KINFO *, VARENT *);
+void	 rgname(KINFO *, VARENT *);
 void	 runame(KINFO *, VARENT *);
 void	 rvar(KINFO *, VARENT *);
+int	 s_gname(KINFO *);
 int	 s_label(KINFO *);
-int	 s_rgroupname(KINFO *);
+int	 s_rgname(KINFO *);
 int	 s_runame(KINFO *);
+int	 s_svgname(KINFO *);
+int	 s_svuname(KINFO *);
+int	 s_time(KINFO *);
 int	 s_uname(KINFO *);
 void	 showkey(void);
 void	 started(KINFO *, VARENT *);
 void	 state(KINFO *, VARENT *);
+void	 stime(KINFO *, VARENT *);
+void	 svuname(KINFO *, VARENT *);
+void	 svgname(KINFO *, VARENT *);
+#if defined(COMPAT_TRU64)
+void	 systime(KINFO *, VARENT *);
+#endif
 void	 tdev(KINFO *, VARENT *);
 void	 tname(KINFO *, VARENT *);
 void	 ucomm(KINFO *, VARENT *);
 void	 uname(KINFO *, VARENT *);
+void	 upr(KINFO *, VARENT *);
+#if defined(COMPAT_TRU64)
+void	 usertime(KINFO *, VARENT *);
+#endif
 void	 vsize(KINFO *, VARENT *);
 void	 wchan(KINFO *, VARENT *);
 __END_DECLS
Index: fmt.c
===================================================================
RCS file: /home/ncvs/src/bin/ps/fmt.c,v
retrieving revision 1.30
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.30 fmt.c
--- fmt.c	6 Apr 2004 20:06:49 -0000	1.30
+++ fmt.c	17 Apr 2004 20:26:57 -0000
@@ -51,16 +51,17 @@
 
 #include "ps.h"
 
-static char *cmdpart(char *);
-static char *shquote(char **);
+static const char *cmdpart(const char *);
+static const char *shquote(const char **);
 
-static char *
-shquote(char **argv)
+static const char *
+shquote(const char **argv)
 {
 	long arg_max;
 	static size_t buf_size;
 	size_t len;
-	char **p, *dst, *src;
+	char *dst;
+	const char **p, *src;
 	static char *buf = NULL;
 
 	if (buf == NULL) {
@@ -72,7 +73,6 @@
 		if ((buf = malloc(buf_size)) == NULL)
 			errx(1, "malloc failed");
 	}
-
 	if (*argv == NULL) {
 		buf[0] = '\0';
 		return (buf);
@@ -96,19 +96,20 @@
 	return (buf);
 }
 
-static char *
-cmdpart(char *arg0)
+static const char *
+cmdpart(const char *arg0)
 {
-	char *cp;
+	const char *cp;
 
 	return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0);
 }
 
 const char *
-fmt_argv(char **argv, char *cmd, size_t maxlen)
+fmt_argv(const char **argv, const char *cmd, size_t maxlen)
 {
 	size_t len;
-	char *ap, *cp;
+	const char *ap;
+	char *cp;
 
 	if (argv == NULL || argv[0] == NULL) {
 		if (cmd == NULL)
@@ -123,9 +124,9 @@
 	if (cp == NULL)
 		errx(1, "malloc failed");
 	if (ap == NULL)
-		sprintf(cp, " (%.*s)", (int)maxlen, cmd);
+		(void)sprintf(cp, "[%.*s]", (int)maxlen, cmd);
 	else if (strncmp(cmdpart(argv[0]), cmd, maxlen) != 0)
-		sprintf(cp, "%s (%.*s)", ap, (int)maxlen, cmd);
+		(void)sprintf(cp, "%s (%.*s)", ap, (int)maxlen, cmd);
 	else
 		(void) strcpy(cp, ap);
 	return (cp);
Index: keyword.c
===================================================================
RCS file: /home/ncvs/src/bin/ps/keyword.c,v
retrieving revision 1.66
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.66 keyword.c
--- keyword.c	6 Apr 2004 20:06:49 -0000	1.66
+++ keyword.c	20 Apr 2004 00:34:28 -0000
@@ -43,162 +43,391 @@
 #include <sys/user.h>
 
 #include <err.h>
-#include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "ps.h"
 
-static VAR *findvar(char *, int, char **header);
+static int parseexpr(char *, const char *);
+static void parsefmt1(char *, const char *, const char *);
+static VAR *findvar(const char *, const char *);
 static int  vcmp(const void *, const void *);
 
+const char  fmtsep[] = " ,\t\n"; /* separators is this order precisely */
+
 /* Compute offset in common structures. */
-#define	KOFF(x)	offsetof(struct kinfo_proc, x)
+/* KOFF() has been moved to ps.h */
 #define	ROFF(x)	offsetof(struct rusage, x)
 
-#define	UIDFMT	"u"
-#define	UIDLEN	5
-#define	PIDFMT	"d"
-#define	PIDLEN	5
-#define USERLEN (MAXLOGNAME - 1)
+#define CLSLEN	6		/* class */
+#define LCNTFMT	"ld"		/* rvar */
+#define LCNTLEN	7		/* majflt, minflt, msgrcv, msgsnd */
+#define SCNTFMT	"ld"		/* rvar */
+#define SCNTLEN	5		/* inblk, nivcsw, nsigs, nswap, nvcsw, oublk,
+				   pagein, scnt */
+#undef COMMLEN
+#define COMMLEN	MAXCOMLEN	/* args, command, ucomm */
+#define DATELEN	28		/* lstart */
+#define DEVLEN	6		/* tdev */
+#define EMULLEN	7		/* emul */
+#define FLAGFMT	"x"		/* kvar */
+#define FLAGLEN	7		/* f */
+#define GRPLEN	USERLEN		/* group, rgroup, svgroup */
+#define KTRCFMT	"x"		/* kvar */
+#define KTRCLEN	8		/* ktrace */
+#define KPTRFMT	"lx"		/* kvar */
+#define KPTRLEN	8		/* paddr, nwchan */
+#define LABLLEN	SHRT_MAX	/* label */
+#define LOCKLEN	6		/* lockname */
+#define PRIOFMT	"d"		/* kvar */
+#define PRIOLEN	3		/* cpu, intpri, jobc, nice, opri, pri, upr,
+				   pset, psr */
+#define RTPRLEN	7		/* rtprio */
+#define PERCLEN	4		/* %cpu, %mem */
+#define PIDFMT	"d"		/* kvar */
+#define PIDLEN	5		/* pgid, pid, ppid, sid, tpgid, tsid */
+#define K127FMT	"d"		/* kvar */
+#define K127LEN	3		/* re, sl */
+#define SHEXFMT	"x"		/* kvar */
+#define SHEXLEN	4		/* acflag, xstat */
+#define SIGFMT	"x"		/* kvar */
+#define SIGLEN	8		/* cursig, sig, sigcatch, sigignore, sigmask */
+#define LSZFMT	"ld"		/* kvar, rvar */
+#define LSZLEN	5		/* lim, rss, vsz */
+#define SSZFMT	"ld"		/* kvar, rvar */
+#define SSZLEN	4		/* dsiz, ssiz, sz, tsiz */
+#define STATLEN	4		/* state */
+#define STRTLEN	7		/* start */
+/* LTIMELEN has been moved to ps.h : etime */
+/* STIMELEN has been moved to ps.h : stime */
+#define TIMELEN	LTIMELEN	/* ctime, systime, time, usertime */
+#define LTTYLEN	10		/* tty */
+#define STTYLEN	3		/* tt */
+#define UIDFMT	"u"		/* kvar */
+/* UIDLEN has been moved to ps.h : gid, rgid, ruid, svgid, svuid, uid */
+#define UMSKFMT "#o"
+#define UMSKLEN 4
+/* USERLEN has been moved to ps.h : login, ruser, user, svuser */
+#define WCHNLEN 6		/* mwchan */
+
+#define ALIAS(n, h, a)	\
+	{ n, h, a, 0, NULL, NULL, 0, 0, CHAR, NULL, 0 }
+#define K127(n, h, s, t, lf)	\
+	{ n, h, NULL, INF127, kvar, NULL, lf##LEN, KOFF(s), t, lf##FMT, 0 }
+#define KVAR(n, h, s, t, lf)	\
+	{ n, h, NULL, 0, kvar, NULL, lf##LEN, KOFF(s), t, lf##FMT, 0 }
+#define RVAR(n, h, s, t, lf)	\
+	{ n, h, NULL, 0, rvar, NULL, lf##LEN, ROFF(s), t, lf##FMT, 0 }
+#define SIZE(n, h, fl, fn, sz, ln)	\
+	{ n, h, NULL, fl|DSIZ, fn, sz, ln##LEN, 0, CHAR, NULL, 0 }
+#define CALL(n, h, fl, fn, ln)	\
+	{ n, h, NULL, fl, fn, NULL, ln##LEN, 0, CHAR, NULL, 0 }
+
+#define PID(n, h, s)	KVAR(n, h, s, UINT, PID)
+#define SIG(n, h, s)	KVAR(n, h, s, UINT, SIG)
+#define UID(n, h, s)	KVAR(n, h, s, UINT, UID)
+#define GID(n, h, s)	UID(n, h, s)
 
 /* PLEASE KEEP THE TABLE BELOW SORTED ALPHABETICALLY!!! */
-static VAR var[] = {
-	{"%cpu", "%CPU", NULL, 0, pcpu, NULL, 4, 0, CHAR, NULL, 0},
-	{"%mem", "%MEM", NULL, 0, pmem, NULL, 4, 0, CHAR, NULL, 0},
-	{"acflag", "ACFLG", NULL, 0, kvar, NULL, 3, KOFF(ki_acflag), USHORT,
-		"x", 0},
-	{"acflg", "", "acflag", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"args", "COMMAND", NULL, COMM|LJUST|USER, arguments, NULL, 16, 0,
-		CHAR, NULL, 0},
-	{"blocked", "", "sigmask", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"caught", "", "sigcatch", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"comm", "COMMAND", NULL, LJUST, ucomm, NULL, MAXCOMLEN, 0, CHAR,
-		NULL, 0},
-	{"command", "COMMAND", NULL, COMM|LJUST|USER, command, NULL, 16, 0,
-		CHAR, NULL, 0},
-	{"cpu", "CPU", NULL, 0, kvar, NULL, 3, KOFF(ki_estcpu), UINT, "d",
-		0},
-	{"cputime", "", "time", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"etime", "ELAPSED", NULL, USER, elapsed, NULL, 12, 0, CHAR, NULL, 0},
-	{"f", "F", NULL, 0, kvar, NULL, 7, KOFF(ki_flag), INT, "x", 0},
-	{"flags", "", "f", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"ignored", "", "sigignore", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"inblk", "INBLK", NULL, USER, rvar, NULL, 4, ROFF(ru_inblock), LONG,
-		"ld", 0},
-	{"inblock", "", "inblk", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"jobc", "JOBC", NULL, 0, kvar, NULL, 4, KOFF(ki_jobc), SHORT, "d",
-		0},
-	{"ktrace", "KTRACE", NULL, 0, kvar, NULL, 8, KOFF(ki_traceflag), INT,
-		"x", 0},
-	{"label", "LABEL", NULL, LJUST|DSIZ, label, s_label, SHRT_MAX, 0, CHAR,
-	    NULL, 0},
-	{"lim", "LIM", NULL, 0, maxrss, NULL, 5, 0, CHAR, NULL, 0},
-	{"lockname", "LOCK", NULL, LJUST, lockname, NULL, 6, 0, CHAR, NULL,
-		0},
-	{"login", "LOGIN", NULL, LJUST, logname, NULL, MAXLOGNAME-1, 0, CHAR,
-		NULL, 0},
-	{"logname", "", "login", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"lstart", "STARTED", NULL, LJUST|USER, lstarted, NULL, 28, 0, CHAR,
-		NULL, 0},
-	{"majflt", "MAJFLT", NULL, USER, rvar, NULL, 4, ROFF(ru_majflt),
-		LONG, "ld", 0},
-	{"minflt", "MINFLT", NULL, USER, rvar, NULL, 4, ROFF(ru_minflt),
-		LONG, "ld", 0},
-	{"msgrcv", "MSGRCV", NULL, USER, rvar, NULL, 4, ROFF(ru_msgrcv),
-		LONG, "ld", 0},
-	{"msgsnd", "MSGSND", NULL, USER, rvar, NULL, 4, ROFF(ru_msgsnd),
-		LONG, "ld", 0},
-	{"mwchan", "MWCHAN", NULL, LJUST, mwchan, NULL, 6, 0, CHAR, NULL, 0},
-	{"ni", "", "nice", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"nice", "NI", NULL, 0, kvar, NULL, 2, KOFF(ki_nice), CHAR, "d",
-		0},
-	{"nivcsw", "NIVCSW", NULL, USER, rvar, NULL, 5, ROFF(ru_nivcsw),
-		LONG, "ld", 0},
-	{"nsignals", "", "nsigs", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"nsigs", "NSIGS", NULL, USER, rvar, NULL, 4, ROFF(ru_nsignals),
-		LONG, "ld", 0},
-	{"nswap", "NSWAP", NULL, USER, rvar, NULL, 4, ROFF(ru_nswap),
-		LONG, "ld", 0},
-	{"nvcsw", "NVCSW", NULL, USER, rvar, NULL, 5, ROFF(ru_nvcsw),
-		LONG, "ld", 0},
-	{"nwchan", "NWCHAN", NULL, LJUST, nwchan, NULL, 8, 0, CHAR, NULL, 0},
-	{"oublk", "OUBLK", NULL, USER, rvar, NULL, 4, ROFF(ru_oublock),
-		LONG, "ld", 0},
-	{"oublock", "", "oublk", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"paddr", "PADDR", NULL, 0, kvar, NULL, 8, KOFF(ki_paddr), KPTR,
-		"lx", 0},
-	{"pagein", "PAGEIN", NULL, USER, pagein, NULL, 6, 0, CHAR, NULL, 0},
-	{"pcpu", "", "%cpu", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"pending", "", "sig", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"pgid", "PGID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_pgid), UINT,
-		PIDFMT, 0},
-	{"pid", "PID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_pid), UINT,
-		PIDFMT, 0},
-	{"pmem", "", "%mem", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"ppid", "PPID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_ppid), UINT,
-		PIDFMT, 0},
-	{"pri", "PRI", NULL, 0, pri, NULL, 3, 0, CHAR, NULL, 0},
-	{"re", "RE", NULL, INF127, kvar, NULL, 3, KOFF(ki_swtime), UINT, "d",
-		0},
-	{"rgid", "RGID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_rgid),
-		UINT, UIDFMT, 0},
-	{"rgroup", "RGROUP", NULL, LJUST|DSIZ, rgroupname, s_rgroupname,
-		USERLEN, 0, CHAR, NULL, 0},
-	{"rss", "RSS", NULL, 0, kvar, NULL, 4, KOFF(ki_rssize), PGTOK, "ld", 0},
-	{"rtprio", "RTPRIO", NULL, 0, priorityr, NULL, 7, KOFF(ki_pri), CHAR,
-		NULL, 0},
-	{"ruid", "RUID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_ruid),
-		UINT, UIDFMT, 0},
-	{"ruser", "RUSER", NULL, LJUST|DSIZ, runame, s_runame, USERLEN,
-		0, CHAR, NULL, 0},
-	{"sid", "SID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_sid), UINT,
-		PIDFMT, 0},
-	{"sig", "PENDING", NULL, 0, kvar, NULL, 8, KOFF(ki_siglist), INT,
-		"x", 0},
-	{"sigcatch", "CAUGHT", NULL, 0, kvar, NULL, 8, KOFF(ki_sigcatch),
-		UINT, "x", 0},
-	{"sigignore", "IGNORED", NULL, 0, kvar, NULL, 8, KOFF(ki_sigignore),
-		UINT, "x", 0},
-	{"sigmask", "BLOCKED", NULL, 0, kvar, NULL, 8, KOFF(ki_sigmask),
-		UINT, "x", 0},
-	{"sl", "SL", NULL, INF127, kvar, NULL, 3, KOFF(ki_slptime), UINT, "d",
-		0},
-	{"start", "STARTED", NULL, LJUST|USER, started, NULL, 7, 0, CHAR, NULL,
-		0},
-	{"stat", "", "state", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"state", "STAT", NULL, 0, state, NULL, 4, 0, CHAR, NULL, 0},
-	{"svgid", "SVGID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_svgid),
-		UINT, UIDFMT, 0},
-	{"svuid", "SVUID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_svuid),
-		UINT, UIDFMT, 0},
-	{"tdev", "TDEV", NULL, 0, tdev, NULL, 4, 0, CHAR, NULL, 0},
-	{"time", "TIME", NULL, USER, cputime, NULL, 9, 0, CHAR, NULL, 0},
-	{"tpgid", "TPGID", NULL, 0, kvar, NULL, 4, KOFF(ki_tpgid), UINT,
-		PIDFMT, 0},
-	{"tsid", "TSID", NULL, 0, kvar, NULL, PIDLEN, KOFF(ki_tsid), UINT,
-		PIDFMT, 0},
-	{"tsiz", "TSIZ", NULL, 0, kvar, NULL, 4, KOFF(ki_tsize), PGTOK, "ld", 0},
-	{"tt", "TT ", NULL, 0, tname, NULL, 4, 0, CHAR, NULL, 0},
-	{"tty", "TTY", NULL, LJUST, longtname, NULL, 8, 0, CHAR, NULL, 0},
-	{"ucomm", "UCOMM", NULL, LJUST, ucomm, NULL, MAXCOMLEN, 0, CHAR, NULL,
-		0},
-	{"uid", "UID", NULL, 0, kvar, NULL, UIDLEN, KOFF(ki_uid), UINT,
-		UIDFMT, 0},
-	{"upr", "UPR", NULL, 0, kvar, NULL, 3, KOFF(ki_pri.pri_user), UCHAR,
-		"d", 0},
-	{"uprocp", "UPROCP", NULL, 0, kvar, NULL, 8, KOFF(ki_paddr), KPTR,
-		"lx", 0},
-	{"user", "USER", NULL, LJUST|DSIZ, uname, s_uname, USERLEN, 0, CHAR,
-		NULL, 0},
-	{"usrpri", "", "upr", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"vsize", "", "vsz", 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
-	{"vsz", "VSZ", NULL, 0, vsize, NULL, 5, 0, CHAR, NULL, 0},
-	{"wchan", "WCHAN", NULL, LJUST, wchan, NULL, 6, 0, CHAR, NULL, 0},
-	{"xstat", "XSTAT", NULL, 0, kvar, NULL, 4, KOFF(ki_xstat), USHORT,
-		"x", 0},
-	{"", NULL, NULL, 0, NULL, NULL, 0, 0, CHAR, NULL, 0},
+/* _:required #:optional +:add-on */
+/* 3:bsd43reno 4:bsd44lite2 f:freebsd n:netbsd o:openbsd */
+/* x:susv3 h:hpux i:irix s:solaris t:tru64 */
+VAR var[] = {
+/* 34fnoxhist*/
+/*_34fno    t*/	CALL("%cpu", "%CPU", 0, pcpu, PERC),
+/*_34fno    t*/	CALL("%mem", "%MEM", 0, pmem, PERC),
+#if defined(COMPAT_TRU64)
+/*-3        t*/	ALIAS("DFMT", NULL, "pid tname state cputime command"),
+/*-         t*/	ALIAS("F5FMT", NULL,
+		    "uname pid ppid pcpu start tt time command"),
+/*-         t*/	ALIAS("FL5FMT", NULL,
+	     "f state uid pid ppid pcpu pri nice rss wchan start time command"),
+/*-3        t*/	ALIAS("JFMT", NULL,
+		    "user pid ppid pgid sess jobc state tname cputime command"),
+/*-         t*/	ALIAS("L5FMT", NULL,
+		  "f state uid pid ppid pcpu pri nice rss wchan tt time ucomm"),
+/*-3        t*/	ALIAS("LFMT", NULL,
+	  "uid pid ppid cp pri nice vsz rss wchan state tname cputime command"),
+/*-         t	NUMA="user psr pset RAD NSG s tty time command"*/
+/*-3        t*/	ALIAS("RUSAGE", NULL,
+	"minflt majflt nswap inblock oublock msgsnd msgrcv nsigs nvcsw nivcsw"),
+/*-         t*/	ALIAS("SCHED", NULL,
+		    "user pcpu pri usrpri nice psxpri psr policy pset"),
+/*-3        t*/	ALIAS("SFMT", NULL,
+	    "uid pid cursig sig sigmask sigignore sigcatch stat tname command"),
+/*-         t*/	ALIAS("THREAD", NULL,
+		    "user pcpu pri scnt wchan usertime systime"),
+/*-3        t*/	ALIAS("UFMT", NULL,
+		    "uname pid pcpu pmem vsz rss tt state start time command"),
+/*-3        t*/	ALIAS("VFMT", NULL,
+		    "pid tt state time sl pagein vsz rss pcpu pmem command"),
+#endif
+/* 34fno    t*/	KVAR("acflag", "ACFLG", ki_acflag, USHORT, SHEX),
+/* 34fno    t*/	ALIAS("acflg", NULL, "acflag"),
+/*_     xh st*/	ALIAS("addr", "ADDR", "paddr"),
+/*_  f oxhist*/	CALL("args", "COMMAND", COMM|LJUST|USER, arguments, COMM),
+/* 34fno    t*/	ALIAS("blocked", NULL, "sigmask"),
+/*_     x  st*/	ALIAS("c", "C", "cpu"),
+/* 34fno    t*/	ALIAS("caught", NULL, "sigcatch"),
+/*_       is */	CALL("class", "CLS", LJUST, class, CLS),
+#if defined(WITH_EXTRA_ALIASES)
+/*#      h   */	ALIAS("cls", NULL, "class"),
+/*#         t*/	ALIAS("cmd", "CMD", "command"),
+#endif
+/*_  f oxhist*/	 ALIAS("comm", "COMMAND", "ucomm"),
+/*_34fno    t*/	CALL("command", "COMMAND", COMM|LJUST|USER, command, COMM),
+#if defined(COMPAT_TRU64)
+/*-3        t*/	ALIAS("cp", "CP", "cpu"),
+#endif
+/*_34fno hi t*/	KVAR("cpu", "CPU", ki_estcpu, UINT, PRIO),
+/*34 fno    t*/	ALIAS("cputime", NULL, "time"),
+/*+   n      */	SIZE("ctime", "CTIME", USER, cldtime, s_time, TIME),
+#if defined(COMPAT_TRU64)
+#if defined(WITH_CURSIG)
+/*-         t*/	SIG("cursig", "CURSIG", ki_cursig),
+#else
+/*-         t*/	CALL("cursig", "CURSIG", 0, nop, SIG),	/* XXX nop */
+#endif
+#endif
+#if defined(WITH_EXTRA_ALIASES)
+/*#3        t*/	ALIAS("dev", NULL, "tdev"),
+#endif
+/*+    o     */	KVAR("dsiz", "DSIZ", ki_dsize, PGTOK, SSZ),
+#if defined(WITH_NOP_ALIASES)
+/*#    o     */	CALL("emul", "EMUL", 0, nop, EMUL),	/* XXX nop */
+#endif
+/*+   no     */	ALIAS("egid", "EGID", "gid"),
+/*+   no     */	ALIAS("egroup", "EGROUP", "group"),
+/*   f oxhist*/	CALL("etime", "ELAPSED", USER, elapsed, LTIME),
+/*+   no     */	ALIAS("euid", "EUID", "uid"),
+/*+   no     */	ALIAS("euser", "EUSER", "user"),
+/*_34fnox  st*/	KVAR("f", "F", ki_flag, INT, FLAG),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3      i t*/	ALIAS("flag", NULL, "f"),
+#endif
+/*  4fno h   */	ALIAS("flags", NULL, "f"),
+#if defined(WITH_EXTRA_ALIASES)
+/*#        s */	ALIAS("fname", NULL, "ucomm"),
+#endif
+/*+   no h s */	GID("gid", "GID", ki_groups),
+/*+    oxhist*/	SIZE("group", "GROUP", LJUST, gname, s_gname, GRP),
+/*+   no     */	CALL("groupnames", "GROUPNAMES", LJUST, groupnames, GRP),
+/*+   no     */	CALL("groups", "GROUPS", LJUST, groups, GRP),
+/*+   no     */	KVAR("holdcnt", "HOLDCNT", ki_lock, CHAR, SCNT),
+/* 34fno    t*/	ALIAS("ignored", NULL, "sigignore"),
+/* 34fno    t*/	RVAR("inblk", "INBLK", ru_inblock, LONG, SCNT),
+/* 34fno    t*/	ALIAS("inblock", NULL, "inblk"),
+#if defined(WITH_EXTRA_ALIASES)
+/*#      h   */	ALIAS("intpri","PRI","pri"),
+/*#3        t*/	ALIAS("ivcsw","IVCSW","nivcsw"),
+#endif
+/*_34fno    t*/	KVAR("jobc", "JOBC", ki_jobc, SHORT, PRIO),
+/* 34fno     */	KVAR("ktrace", "KTRACE", ki_traceflag, INT, KTRC),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3  no     */	KVAR("ktracep", "KTRACEP", ki_tracep, KPTR, KPTR),
+#endif
+/*_  f    i  */	SIZE("label", "LABEL", LJUST, label, s_label, LABL),
+#if defined(WITH_NOP_ALIASES)
+/*#   n      */	CALL("lid", "LID", 0, nop, PID),	/* XXX nop */
+#endif
+#if defined(WITH_MAXRSS)
+/*_34fno     */	CALL("lim", "LIM", 0, maxrss, LSZ),
+#else
+/*_34fno     */	CALL("lim", "LIM", 0, nop, LSZ),	/* XXX nop */
+#endif
+/*   f       */	CALL("lockname", "LOCK", LJUST, lockname, LOCK),
+/* 34fno     */	CALL("login", "LOGIN", LJUST, logname, USER),
+/* 34fno    t*/	ALIAS("logname", NULL, "login"),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3        t*/	ALIAS("longtname", NULL, "tty"),
+/*#3        t*/	ALIAS("longtty", NULL, "tty"),
+#endif
+/* 34fno    t*/	CALL("lstart", "STARTED", LJUST|USER, lstarted, DATE),
+#if defined(WITH_NOP_ALIASES)
+/*#   n      */	CALL("lstate", "LSTATE", 0, nop, STAT),	/* XXX nop */
+/*#        s */	CALL("lwp", "LWP", 0, nop, PID),	/* XXX nop */
+#endif
+/* 34fno    t*/	RVAR("majflt", "MAJFLT", ru_majflt, LONG, LCNT),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3         */	ALIAS("maxrss", NULL, "lim"),
+#endif
+/* 34fno    t*/	RVAR("minflt", "MINFLT", ru_minflt, LONG, LCNT),
+/* 34fno    t*/	RVAR("msgrcv", "MSGRCV", ru_msgrcv, LONG, LCNT),
+/* 34fno    t*/	RVAR("msgsnd", "MSGSND", ru_msgsnd, LONG, LCNT),
+/*_  f       */	CALL("mwchan", "MWCHAN", LJUST, mwchan, WCHN),
+/* 34fno    t*/	ALIAS("ni", NULL, "nice"),
+/*_34fnoxhist*/	KVAR("nice", "NI", ki_nice, CHAR, PRIO),
+#if defined(WITH_NOP_ALIASES)
+/*#   n    s */	CALL("nlwp", "NLWP", 0, nop, PID),	/* XXX nop */
+#endif
+/* 34fno    t*/	RVAR("nivcsw", "NIVCSW", ru_nivcsw, LONG, SCNT),
+/* 34fno    t*/	ALIAS("nsignals", NULL, "nsigs"),
+/* 34fno    t*/	RVAR("nsigs", "NSIGS", ru_nsignals, LONG, SCNT),
+/* 34fno    t*/	RVAR("nswap", "NSWAP", ru_nswap, LONG, SCNT),
+/* 34fno    t*/	RVAR("nvcsw", "NVCSW", ru_nvcsw, LONG, SCNT),
+/*_34fno    t*/	CALL("nwchan", "NWCHAN", LJUST, nwchan,  KPTR),
+#if defined(WITH_NOP_ALIASES)
+/*#        s */ ALIAS("opri", NULL, "pri"),
+/*#        s */	ALIAS("osz", NULL, "sz"),
+#endif
+/* 34fno    t*/	RVAR("oublk", "OUBLK", ru_oublock, LONG, SCNT),
+/* 34fno    t*/	ALIAS("oublock", NULL, "oublk"),
+#if defined(WITH_EXTRA_ALIASES)
+/*#       i  */	ALIAS("p", "P", "psr"),
+/*#3        t*/	ALIAS("p_rss", NULL, "rss"),
+#endif
+#if defined(WITH_NOP_ALIASES)
+/*#34 no     */	CALL("p_ru", "P_RU", 0, nop, KPTR),	/* XXX nop */
+#endif
+/* 34fno     */	KVAR("paddr", "PADDR", ki_paddr, KPTR, KPTR),
+/*_34fno    t*/	CALL("pagein", "PAGEIN", USER, pagein, SCNT),
+/* 34fnoxhist*/	ALIAS("pcpu", NULL, "%cpu"),
+/* 34fno    t*/	ALIAS("pending", NULL, "sig"),
+/*_34fnoxhist*/	PID("pgid", "PGID", ki_pgid),
+/*_34fnoxhist*/	PID("pid", "PID", ki_pid),
+/* 34fno   st*/	ALIAS("pmem", NULL, "%mem"),
+#if defined(WITH_NOP_ALIASES)
+/*#3         */	CALL("poip", "POIP", 0, nop, SCNT),	/* XXX nop */
+#endif
+#if defined(COMPAT_TRU64)
+/*-         t*/	ALIAS("policy", "POL", "class"),
+#endif
+/*_34fnoxhist*/	PID("ppid", "PPID", ki_ppid),
+/*_34fno h st*/	CALL("pri", "PRI", 0, pri, PRIO),
+#if defined(WITH_NOP_ALIASES)
+/*#      h   */	CALL("prmgrp", "PRMGRP", 0, nop, GRP),	/* XXX nop */
+/*#      h   */	CALL("prmid", "PRMID", 0, nop, PID),	/* XXX nop */
+/*#        s */	CALL("project", "PROJECT", 0, nop, USER),/* XXX nop */
+/*#        s */	CALL("projid", "PROJID", 0, nop, PID),	/* XXX nop */
+#endif
+#if defined(WITH_EXTRA_ALIASES)
+/*#3         */	ALIAS("procaddr", NULL, "paddr"),
+#endif
+#if defined(COMPAT_TRU64)
+/*-      h st*/	CALL("pset", "PSET", 0, nop, PRIO),	/* XXX nop */
+#endif
+/*_        st*/	KVAR("psr", "PSR", ki_oncpu, UCHAR, PRIO),
+#if defined(COMPAT_TRU64)
+/*-         t*/	ALIAS("psxpri", NULL, "pri"),
+#endif
+/*_34fno     */	K127("re", "RE", ki_swtime, UINT, K127),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3         */	ALIAS("resident", NULL, "re"),
+#endif
+/* 34fno h st*/	GID("rgid", "RGID", ki_rgid),
+/*   fnoxhist*/	SIZE("rgroup", "RGROUP", LJUST, rgname, s_rgname, GRP),
+#if defined(WITH_NOP_ALIASES)
+/*#34 no     */	CALL("rlink", "RLINK", 0, nop, KPTR),	/* XXX nop */
+/*#   n      */	CALL("rlwp", "RLWP", 0, nop, PID),	/* XXX nop */
+#endif
+/*_34fno   st*/	KVAR("rss", "RSS", ki_rssize, PGTOK, LSZ),
+/*+34 no    t*/	ALIAS("rssize", NULL, "rsz"),
+/*+34 no    t*/	ALIAS("rsz", "RSZ", "rss"),
+/*   f       */	CALL("rtprio", "RTPRIO", 0, priorityr, RTPR),
+/* 34fno h st*/	UID("ruid", "RUID", ki_ruid),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3        t*/	ALIAS("runame", NULL, "ruser"),
+#endif
+/* 34fnoxhist*/	SIZE("ruser", "RUSER", LJUST, runame, s_runame, USER),
+/*_     x  s */	ALIAS("s", "S", "state"),
+#if defined(COMPAT_TRU64)
+/*-         t*/	CALL("scnt", "SCNT", 0, nop, SCNT), /* XXX nop p_suspcount? */
+#if defined(WITH_EXTRA_ALIASES)
+/*#         t*/	ALIAS("scount", NULL, "scnt"),
+#endif
+/*-3  no    t*/	ALIAS("sess", "SESS", "sid"),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3        t*/	ALIAS("session", NULL, "sess"),
+#endif
+#endif
+/*_  fn  h s */	PID("sid", "SID", ki_sid),
+/*_34fno    t*/	SIG("sig", "PENDING", ki_siglist),
+/*_34fno    t*/	SIG("sigcatch", "CAUGHT", ki_sigcatch),
+/*_34fno    t*/	SIG("sigignore", "IGNORED", ki_sigignore),
+/*_34fno    t*/	SIG("sigmask", "BLOCKED", ki_sigmask),
+#if defined(WITH_EXTRA_ALIASES)
+/*#          */ ALIAS("size", NULL, "sz"),
+#endif
+/*_34fno    t*/	K127("sl", "SL", ki_slptime, UINT, K127),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3        t*/	ALIAS("slp", NULL, "sl"),
+/*#3        t*/	ALIAS("slptime", NULL, "sl"),
+#endif
+/*+    o     */	KVAR("ssiz", "SSIZ", ki_ssize, PGTOK, SSZ),
+/*_34fno    t*/	CALL("start", "STARTED", LJUST|USER, started, STRT),
+/* 34fno    t*/	ALIAS("stat", NULL, "state"),
+/*_34fno hi t*/	CALL("state", "STAT", 0, state, STAT),
+#if defined(WITH_NOP_ALIASES)
+/*#         t*/	CALL("status", "STATUS", 0, nop, STAT),	/* XXX nop ki_valid? */
+#endif
+/*_     xhist*/	CALL("stime", "STIME", USER, stime, STIME),
+/* 34fno    t*/	GID("svgid", "SVGID", ki_svgid),
+/*+   no     */	SIZE("svgroup", "SVGROUP", LJUST, svgname, s_svgname, GRP),
+/* 34fno    t*/	UID("svuid", "SVUID", ki_svuid),
+/*+   no     */	SIZE("svuser", "SVUSER", LJUST, svuname, s_svuname, USER),
+#if defined(COMPAT_TRU64)
+/*-         t*/	SIZE("systime", "SYSTIME", USER, systime, s_time, TIME),
+#endif
+/*_     xh  t*/	ALIAS("sz", "SZ", "vsz"),
+#if defined(WITH_NOP_ALIASES)
+/*#        s */	CALL("taskid", "TASKID", 0, nop, PID),
+#endif
+/* 34fno    t*/	CALL("tdev", "TDEV", 0, tdev, DEV),
+/*_34fnoxhist*/	SIZE("time", "TIME", USER, cputime, s_time, TIME),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3        t*/	ALIAS("tname", NULL, "tt"),
+#endif
+/*_34fno    t*/	PID("tpgid", "TPGID", ki_tpgid),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3         */	ALIAS("traceflag", NULL, "ktrace"),
+#endif
+#if defined(WITH_NOP_ALIASES)
+/*#3         */	CALL("trs", "TRS", 0, nop, LSZ),	/* XXX nop */
+#endif
+#if defined(COMPAT_TRU64)
+/*-3  no    t*/	ALIAS("tsess", "TSESS", "tsid"),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3        t*/	ALIAS("tsession", NULL, "tsess"),
+#endif
+#endif
+/*   f       */	PID("tsid", "TSID", ki_tsid),
+/*_34fno     */	KVAR("tsiz", "TSIZ", ki_tsize, PGTOK, SSZ),
+/* 34fno    t*/	CALL("tt", "TT ", 0, tname, STTY),
+/*_34fnoxhist*/	CALL("tty", "TTY", LJUST, longtname, LTTY),
+#if defined(WITH_EXTRA_ALIASES)
+/*#3        t*/	ALIAS("u_cmask", NULL, "umask"),
+/*#3        t*/	ALIAS("u_procp", NULL, "uprocp"),
+#endif
+/* 34fno    t*/	CALL("ucomm", "UCOMM", LJUST, ucomm, COMM),
+/*_34fnoxhist*/	UID("uid", "UID", ki_uid),
+#if defined(WITH_UMASK)
+/*+3        t*/	KVAR("umask", "UMASK", ki_cmask, USHORT, UMSK),
+#else
+/*+3        t*/	CALL("umask", "UMASK", 0, nop, UMSK),	/* XXX nop */
+#endif
+#if defined(COMPAT_TRU64)
+/*-3        t*/	ALIAS("uname", NULL, "user"),
+#endif
+/* 34fno     */	CALL("upr", "UPR", 0, upr, PRIO),
+/* 3 f      t*/	ALIAS("uprocp", "UPROCP", "paddr"),
+/*_34fnoxhist*/	SIZE("user", "USER", LJUST, uname, s_uname, USER),
+#if defined(COMPAT_TRU64)
+/*-         t*/	SIZE("usertime", "USRTIME", USER, usertime, s_time, TIME),
+#endif
+/* 34fno    t*/	ALIAS("usrpri", NULL, "upr"),
+#if defined(WITH_EXTRA_ALIASES)
+/*#       i  */	ALIAS("util", NULL, "c"),
+/*#3        t*/	ALIAS("vcsw", "VCSW", "nvcsw"),
+#endif
+/* 34fno    t*/	ALIAS("vsize", NULL, "vsz"),
+/*_34fnoxhist*/	CALL("vsz", "VSZ", 0, vsize, LSZ),
+/*_34fnoxhist*/	CALL("wchan", "WCHAN", LJUST, wchan, WCHN),
+#if defined(WITH_EXTRA_ALIASES)
+/*#       i  */	ALIAS("wname", NULL, "mwchan"),
+#endif
+/* 34fno     */	KVAR("xstat", "XSTAT", ki_xstat, USHORT, SHEX),
+	ALIAS("", NULL, NULL)
 };
 
 void
@@ -223,103 +452,176 @@
 }
 
 void
-parsefmt(const char *p, int user)
+parsefmt(const char *p, const char *f)
 {
-	static struct varent *vtail;
-	char *tempstr, *tempstr1;
+	char *cp;
+
+	cp = strdup(p);
+	if (!cp)
+		errx(1, "malloc failed");
+	parsefmt1(cp, f, NULL);
+}
 
-#define		FMTSEP	" \t,\n"
-	tempstr1 = tempstr = strdup(p);
-	while (tempstr && *tempstr) {
-		char *cp, *hp;
-		VAR *v;
-		struct varent *vent;
-
-		/*
-		 * If an item contains an equals sign, it specifies a column
-		 * header, may contain embedded separator characters and
-		 * is always the last item.	
-		 */
-		if (tempstr[strcspn(tempstr, "="FMTSEP)] != '=')
-			while ((cp = strsep(&tempstr, FMTSEP)) != NULL &&
-			    *cp == '\0')
-				/* void */;
-		else {
-			cp = tempstr;
-			tempstr = NULL;
+static int
+parseexpr(char *e, const char *f)
+{
+#define STACKSIZE	10
+#define STACKUNDERFLOW	-1
+#define STACKOVERFLOW	-2
+#define SYNTAXERROR	-3
+	int stack[STACKSIZE];
+	int top;
+	char *p;
+
+	for (top = -1; *e; e++) {
+		switch (*e) {
+		case '(':
+		case ')':
+			/* nop */
+			break;
+		case '&':
+			if (--top < 0)
+				return(STACKUNDERFLOW);
+			stack[top] = stack[top] & stack[top+1];
+			break;
+		case '|':
+			if (--top < 0)
+				return(STACKUNDERFLOW);
+			stack[top] = stack[top] | stack[top+1];
+			break;
+		case '^':
+			if (--top < 0)
+				return(STACKUNDERFLOW);
+			stack[top] = stack[top] ^ stack[top+1];
+			break;
+		case '!':
+			if (top < 0)
+				return(STACKUNDERFLOW);
+			stack[top] = !stack[top];
+			break;
+		case '[':
+			if (++top >= STACKSIZE)
+				return(STACKOVERFLOW);
+			for (p = ++e; *e && *e != ']'; e++)
+				if (strchr("[()&|^!", *e))
+					return(SYNTAXERROR);
+			if (*e) {
+				*e = '\0';
+				stack[top] = strpbrk(f, p) != NULL;
+				break;
+			}
+			/*FALLTHROUGH*/
+		case ']':
+			return(SYNTAXERROR);
+		default:
+			if (++top >= STACKSIZE)
+				return(STACKOVERFLOW);
+			stack[top] = *e == '*' || strchr(f, *e) != NULL;
 		}
-		if (cp == NULL || !(v = findvar(cp, user, &hp)))
+	}
+	if (top == 0)
+		return(stack[top]);
+	return(SYNTAXERROR);
+}
+
+static void
+parsefmt1(char *p, const char *f, const char *h)
+{
+	char *cp, *hp, *ep;
+	VAR *v;
+	VARENT *vent;
+	const char *sep;
+	int rc;
+
+	sep = fmtsep + (strchr(p, '=') != NULL);
+loop:
+	while (p && *p) {
+		while ((cp = strsep(&p, sep)) != NULL && *cp == '\0')
+			/* void */;
+		if (cp == NULL)
 			continue;
-		if (!user) {
-			/*
-			 * If the user is NOT adding this field manually,
-			 * get on with our lives if this VAR is already
-			 * represented in the list.
-			 */
-			vent = find_varentry(v);
-			if (vent != NULL)
-				continue;
+		if ((hp = strpbrk(cp, ":=")) != NULL) {
+			switch (*hp) {
+			case ':':
+				*hp++ = '\0';
+				if ((hp = strchr(ep = hp, '=')) != NULL)
+					*hp++ = '\0';
+				if (f && *f && (rc = parseexpr(ep, f)) <= 0) {
+					const char *msg;
+
+					switch (rc) {
+					case STACKUNDERFLOW:
+						msg = "stack underflow";
+						break;
+					case STACKOVERFLOW:
+						msg = "stack overflow";
+						break;
+					case SYNTAXERROR:
+						msg = "syntax error";
+						break;
+					default:
+						msg = "ignored keyword";
+						break;
+					}
+					if (rc < 0)
+						warnx("%s in %s:%s", msg,
+						    cp, hp);
+					continue;
+				}
+				break;
+			case '=':
+				*hp++ = '\0';
+			}
 		}
-		if ((vent = malloc(sizeof(struct varent))) == NULL)
-			errx(1, "malloc failed");
-		vent->header = v->header;
-		if (hp) {
-			hp = strdup(hp);
-			if (hp)
-				vent->header = hp;
-		}
-		vent->var = malloc(sizeof(*vent->var));
-		if (vent->var == NULL)
+		if (!(v = findvar(cp, hp ? hp : h)))
+			continue;
+		STAILQ_FOREACH(vent, &varlist, list)
+			if (!strcmp(v->name, vent->var->name)) {
+				warnx("%s: duplicate keyword", v->name);
+				goto loop;
+			}
+		if ((vent = malloc(sizeof(VARENT))) == NULL)
 			errx(1, "malloc failed");
-		memcpy(vent->var, v, sizeof(*vent->var));
-		vent->next = NULL;
-		if (vhead == NULL)
-			vhead = vtail = vent;
-		else {
-			vtail->next = vent;
-			vtail = vent;
-		}
+		vent->var = v;
+		STAILQ_INSERT_TAIL(&varlist, vent, list);
 	}
-	free(tempstr1);
-	if (!vhead) {
-		warnx("no valid keywords; valid keywords:");
-		showkey();
-		exit(1);
+	if (STAILQ_EMPTY(&varlist)) {
+		if (posix) {
+			warnx("no valid keywords; valid keywords:");
+			showkey();
+			exit(1);
+		} else
+			errx(1, "no valid keywords");
 	}
 }
 
 static VAR *
-findvar(char *p, int user, char **header)
+findvar(const char *p, const char *h)
 {
 	VAR *v, key;
-	char *hp;
-
-	hp = strchr(p, '=');
-	if (hp)
-		*hp++ = '\0';
 
 	key.name = p;
 	v = bsearch(&key, var, sizeof(var)/sizeof(VAR) - 1, sizeof(VAR), vcmp);
-
-	if (v && v->alias) {
-		if (hp) {
-			warnx("%s: illegal keyword specification", p);
-			eval = 1;
-		}
-		parsefmt(v->alias, user);
-		return ((VAR *)NULL);
-	}
-	if (!v) {
+	if (v) {
+		if (v->alias) {
+			char *alias;
+
+			if ((alias = strdup(v->alias)) == NULL)
+				errx(1, "malloc failed");
+			parsefmt1(alias, NULL, h ? h : v->header);
+			free(alias);
+			return ((VAR *)NULL);
+		} else if (h && (v->header = strdup(h)) == NULL)
+			errx(1, "malloc failed");
+	} else {
 		warnx("%s: keyword not found", p);
 		eval = 1;
 	}
-	if (header)
-		*header = hp;
 	return (v);
 }
 
 static int
 vcmp(const void *a, const void *b)
 {
-        return (strcmp(((const VAR *)a)->name, ((const VAR *)b)->name));
+	return (strcmp(((const VAR *)a)->name, ((const VAR *)b)->name));
 }
Index: print.c
===================================================================
RCS file: /home/ncvs/src/bin/ps/print.c,v
retrieving revision 1.85
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.85 print.c
--- print.c	6 Apr 2004 20:06:49 -0000	1.85
+++ print.c	20 Apr 2004 00:53:49 -0000
@@ -41,7 +41,6 @@
 #include <sys/resource.h>
 #include <sys/proc.h>
 #include <sys/stat.h>
-
 #include <sys/mac.h>
 #include <sys/user.h>
 #include <sys/sysctl.h>
@@ -64,31 +63,37 @@
 
 #define	ps_pgtok(a)	(((a) * getpagesize()) / 1024)
 
+#define FMTLEN	64
+#define BUFLEN	128
+
+static const char	defunct[] = "<defunct>";
+
+static void	printtime(time_t, time_t, short, int);
+static void	printsize(const char *, short, u_long);
+static double	getpmem(KINFO *);
+static void	printval(char *, VAR *v);
+
 void
 printheader(void)
 {
 	VAR *v;
-	struct varent *vent;
-	int allempty;
+	VARENT *vent;
 
-	allempty = 1;
-	for (vent = vhead; vent; vent = vent->next)
-		if (*vent->header != '\0') {
-			allempty = 0;
+	STAILQ_FOREACH(vent, &varlist, list)
+		if (*vent->var->header)
 			break;
-		}
-	if (allempty)
+	if (!vent)
 		return;
-	for (vent = vhead; vent; vent = vent->next) {
+	STAILQ_FOREACH(vent, &varlist, list) {
 		v = vent->var;
 		if (v->flag & LJUST) {
-			if (vent->next == NULL)	/* last one */
-				(void)printf("%s", vent->header);
+			if (STAILQ_NEXT(vent, list) == NULL)	/* last one */
+				(void)printf("%s", v->header);
 			else
-				(void)printf("%-*s", v->width, vent->header);
+				(void)printf("%-*s", v->width, v->header);
 		} else
-			(void)printf("%*s", v->width, vent->header);
-		if (vent->next != NULL)
+			(void)printf("%*s", v->width, v->header);
+		if (STAILQ_NEXT(vent, list) != NULL)
 			(void)putchar(' ');
 	}
 	(void)putchar('\n');
@@ -102,11 +107,17 @@
 	char *cp, *vis_args;
 
 	v = ve->var;
+	if (k->ki_p->ki_stat == SZOMB) {
+		if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */
+			(void)printf("%s", defunct);
+		else
+			(void)printf("%-*s", v->width, defunct);
+		return;
+	}
 	if ((vis_args = malloc(strlen(k->ki_args) * 4 + 1)) == NULL)
 		errx(1, "malloc failed");
 	strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH);
-	if (ve->next == NULL) {
-		/* last field */
+	if (STAILQ_NEXT(ve, list) == NULL) { /* last field */
 		if (termwidth == UNLIMITED) {
 			(void)printf("%s", vis_args);
 		} else {
@@ -130,8 +141,15 @@
 	char *cp, *vis_env, *vis_args;
 
 	v = ve->var;
-	if (cflag) {
-		if (ve->next == NULL)	/* last field, don't pad */
+	if (k->ki_p->ki_stat == SZOMB) {
+		if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */
+			(void)printf("%s", defunct);
+		else
+			(void)printf("%-*s", v->width, defunct);
+		return;
+	}
+	if (commandonly) {
+		if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */
 			(void)printf("%s", k->ki_p->ki_comm);
 		else
 			(void)printf("%-*s", v->width, k->ki_p->ki_comm);
@@ -147,8 +165,7 @@
 	} else
 		vis_env = NULL;
 
-	if (ve->next == NULL) {
-		/* last field */
+	if (STAILQ_NEXT(ve, list) == NULL) { /* last field */
 		if (termwidth == UNLIMITED) {
 			if (vis_env)
 				(void)printf("%s ", vis_env);
@@ -161,7 +178,7 @@
 				while (--left >= 0 && *cp)
 					(void)putchar(*cp++);
 				if (--left >= 0)
-					putchar(' ');
+					(void)putchar(' ');
 			}
 			for (cp = vis_args; --left >= 0 && *cp != '\0';)
 				(void)putchar(*cp++);
@@ -180,7 +197,17 @@
 	VAR *v;
 
 	v = ve->var;
-	(void)printf("%-*s", v->width, k->ki_p->ki_comm);
+	if (k->ki_p->ki_stat == SZOMB) {
+		if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */
+			(void)printf("%s", defunct);
+		else
+			(void)printf("%-*s", v->width, defunct);
+	} else {
+		if (STAILQ_NEXT(ve, list) == NULL) /* last field, don't pad */
+			(void)printf("%s", k->ki_p->ki_comm);
+		else
+			(void)printf("%-*s", v->width, k->ki_p->ki_comm);
+	}
 }
 
 void
@@ -221,8 +248,11 @@
 		break;
 
 	case SRUN:
+		if (k->ki_p->ki_oncpu != NOCPU)
+			*cp = 'O';
+		else
 	case SIDL:
-		*cp = 'R';
+			*cp = 'R';
 		break;
 
 	case SWAIT:
@@ -243,6 +273,10 @@
 	cp++;
 	if (!(sflag & PS_INMEM))
 		*cp++ = 'W';
+#if defined(WITH_MAXRSS)
+	else if (pgtok(k->ki_p->ki_rssize) > (k->ki_p->ki_maxrss / 1024))
+		*cp++ = '>';
+#endif
 	if (k->ki_p->ki_nice < NZERO)
 		*cp++ = '<';
 	else if (k->ki_p->ki_nice > NZERO)
@@ -253,10 +287,14 @@
 		*cp++ = 'E';
 	if (flag & P_PPWAIT)
 		*cp++ = 'V';
-	if ((flag & P_SYSTEM) || k->ki_p->ki_lock > 0)
+	if (flag & P_SYSTEM)
+		*cp++ = 'K';
+	else if (k->ki_p->ki_lock > 0)
 		*cp++ = 'L';
 	if (k->ki_p->ki_kiflag & KI_SLEADER)
 		*cp++ = 's';
+	if (flag & P_SA)
+		*cp++ = 'a';
 	if ((flag & P_CONTROLT) && k->ki_p->ki_pgid == k->ki_p->ki_tpgid)
 		*cp++ = '+';
 	if (flag & P_JAILED)
@@ -265,13 +303,23 @@
 	(void)printf("%-*s", v->width, buf);
 }
 
+#define scalepri(x)	((x) - PZERO)
 void
 pri(KINFO *k, VARENT *ve)
 {
 	VAR *v;
 
 	v = ve->var;
-	(void)printf("%*d", v->width, k->ki_p->ki_pri.pri_level - PZERO);
+	(void)printf("%*d", v->width, scalepri(k->ki_p->ki_pri.pri_level));
+}
+
+void
+upr(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+
+	v = ve->var;
+	(void)printf("%*d", v->width, scalepri(k->ki_p->ki_pri.pri_user));
 }
 
 void
@@ -290,7 +338,7 @@
 }
 
 void
-rgroupname(KINFO *k, VARENT *ve)
+rgname(KINFO *k, VARENT *ve)
 {
 	VAR *v;
 
@@ -299,7 +347,7 @@
 }
 
 int
-s_rgroupname(KINFO *k)
+s_rgname(KINFO *k)
 {
 	return (strlen(group_from_gid(k->ki_p->ki_rgid, 0)));
 }
@@ -319,6 +367,117 @@
 	return (strlen(user_from_uid(k->ki_p->ki_ruid, 0)));
 }
 
+void
+gname(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+
+	v = ve->var;
+	(void)printf("%-*s", v->width,
+	    group_from_gid(k->ki_p->ki_groups[0], 0));
+}
+
+int
+s_gname(KINFO *k)
+{
+	return (strlen(group_from_gid(k->ki_p->ki_groups[0], 0)));
+}
+
+void
+groups(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+	char buf[UIDLEN + 2];
+	const char *sep;
+	int left, i, n;
+
+	v = ve->var;
+	if (k->ki_p->ki_ngroups <= 1) {
+		(void)printf("%-*s", v->width, "-");
+		return;
+	}
+	if (STAILQ_NEXT(ve, list) != NULL || termwidth == UNLIMITED) /* last
+									field */
+		left = v->width;
+	else {
+		left = termwidth - (totwidth - v->width);
+		if (left < 1) /* already wrapped, just use std width */
+			left = v->width;
+	}
+	sep = "";
+	for (i = 1; i < k->ki_p->ki_ngroups && left >= 0; i++, left -= n) {
+		n = snprintf(buf, sizeof(buf), "%s%d", sep,
+		    k->ki_p->ki_groups[i]);
+		n = MIN(n, left);
+		(void)printf("%*.*s", n, n, buf);
+		sep = ",";
+	}
+	if (STAILQ_NEXT(ve, list) != NULL && left > 0)
+		(void)printf("%*s", left, "");
+}
+
+void
+groupnames(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+	char buf[USERLEN + 2];
+	const char *sep;
+	int left, i, n;
+
+	v = ve->var;
+	if (k->ki_p->ki_ngroups <= 1) {
+		(void)printf("%-*s", v->width, "-");
+		return;
+	}
+	if (STAILQ_NEXT(ve, list) != NULL || termwidth == UNLIMITED) /* last
+									field */
+		left = v->width;
+	else {
+		left = termwidth - (totwidth - v->width);
+		if (left < 1) /* already wrapped, just use std width */
+			left = v->width;
+	}
+	sep = "";
+	for (i = 1; i < k->ki_p->ki_ngroups && left >= 0; i++, left -= n) {
+		n = snprintf(buf, sizeof(buf), "%s%s", sep,
+		    group_from_gid(k->ki_p->ki_groups[i], 0));
+		n = MIN(n, left);
+		(void)printf("%*.*s", n, n, buf);
+		sep = ",";
+	}
+	if (STAILQ_NEXT(ve, list) != NULL && left > 0)
+		(void)printf("%*s", left, "");
+}
+
+void
+svgname(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+
+	v = ve->var;
+	(void)printf("%-*s", v->width, group_from_gid(k->ki_p->ki_svgid, 0));
+}
+
+int
+s_svgname(KINFO *k)
+{
+	return (strlen(group_from_gid(k->ki_p->ki_svgid, 0)));
+}
+
+void
+svuname(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+
+	v = ve->var;
+	(void)printf("%-*s", v->width, user_from_uid(k->ki_p->ki_svuid, 0));
+}
+
+int
+s_svuname(KINFO *k)
+{
+	return (strlen(user_from_uid(k->ki_p->ki_svuid, 0)));
+}
 
 void
 tdev(KINFO *k, VARENT *ve)
@@ -374,40 +533,89 @@
 }
 
 void
+gettime(time_t *tp, struct timeval *tvp)
+{
+	static time_t	t;
+	static struct timeval	tv;
+
+	if (!t) {
+		(void)time(&t);
+		(void)gettimeofday(&tv, NULL);
+	}
+	if (tp)
+		*tp = t;
+	if (tvp)
+		*tvp = tv;
+}
+
+void
 started(KINFO *k, VARENT *ve)
 {
 	VAR *v;
+	time_t _now;
 	time_t then;
+	time_t _elapsed;
 	struct tm *tp;
-	static int use_ampm = -1;
-	char buf[100];
+	static int  use_ampm = -1;
+	char buf[BUFLEN];
 
 	v = ve->var;
 	if (!k->ki_valid) {
-		(void)printf("%-*s", v->width, "-");
+		(void)printf("%*s", v->width, "-");
 		return;
 	}
 	if (use_ampm < 0)
 		use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0');
 	then = k->ki_p->ki_start.tv_sec;
+	gettime(&_now, NULL);
+	_elapsed = _now - then;
 	tp = localtime(&then);
-	if (now - k->ki_p->ki_start.tv_sec < 24 * 3600) {
+	if (_elapsed < 24 * 3600) {
 		(void)strftime(buf, sizeof(buf),
 		    use_ampm ? "%l:%M%p" : "%k:%M  ", tp);
-	} else if (now - k->ki_p->ki_start.tv_sec < 7 * 86400) {
+	} else if (_elapsed < 7 * 86400) {
 		(void)strftime(buf, sizeof(buf),
 		    use_ampm ? "%a%I%p" : "%a%H  ", tp);
-	} else
-		(void)strftime(buf, sizeof(buf), "%e%b%y", tp);
+	} else {
+		static const char fmt[] = "%e%b%y"; /* avoid gcc warning */
+		(void)strftime(buf, sizeof(buf), fmt, tp);
+	}
 	(void)printf("%-*s", v->width, buf);
 }
 
 void
+stime(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+	time_t _now;
+	time_t then;
+	time_t _elapsed;
+	struct tm *tp;
+	char buf[BUFLEN];
+
+	v = ve->var;
+	if (!k->ki_valid) {
+		(void)printf("%*s", v->width, "-");
+		return;
+	}
+	then = k->ki_p->ki_start.tv_sec;
+	gettime(&_now, NULL);
+	_elapsed = _now - then;
+	tp = localtime(&then);
+	if (_elapsed < 24 * 3600)
+		(void)strftime(buf, sizeof(buf), "%T", tp);
+	else
+		(void)strftime(buf, sizeof(buf), "%b%d", tp);
+	(void)printf("%*s", v->width, buf);
+}
+
+void
 lstarted(KINFO *k, VARENT *ve)
 {
 	VAR *v;
 	time_t then;
-	char buf[100];
+	char buf[BUFLEN];
+	static const char fmt[] = "%c"; /* avoid gcc warning */
 
 	v = ve->var;
 	if (!k->ki_valid) {
@@ -415,7 +623,7 @@
 		return;
 	}
 	then = k->ki_p->ki_start.tv_sec;
-	(void)strftime(buf, sizeof(buf), "%c", localtime(&then));
+	(void)strftime(buf, sizeof(buf), fmt, localtime(&then));
 	(void)printf("%-*s", v->width, buf);
 }
 
@@ -446,7 +654,7 @@
 			(void)printf("%-*.*s", v->width, v->width,
 			    k->ki_p->ki_wmesg);
 		else
-			(void)printf("%-*lx", v->width,
+			(void)printf("%*lx", v->width,
 			    (long)k->ki_p->ki_wchan);
 	} else
 		(void)printf("%-*s", v->width, "-");
@@ -476,7 +684,7 @@
 			(void)printf("%-*.*s", v->width, v->width,
 			    k->ki_p->ki_wmesg);
 		else
-			(void)printf("%-*lx", v->width,
+			(void)printf("%*lx", v->width,
 			    (long)k->ki_p->ki_wchan);
 	} else if (k->ki_p->ki_kiflag & KI_LOCKBLOCK) {
 		if (k->ki_p->ki_lockname[0]) {
@@ -494,7 +702,64 @@
 	VAR *v;
 
 	v = ve->var;
-	(void)printf("%*lu", v->width, (u_long)(k->ki_p->ki_size / 1024));
+	printsize("%*lu", v->width, (u_long)(k->ki_p->ki_size / 1024));
+}
+
+static void
+printtime(time_t secs, time_t psecs, short width, int _etime)
+{
+	time_t mins;
+	char buf[BUFLEN];
+
+	/*
+	 * time adjustments
+	 */
+	if (psecs >= 1000000)
+		secs++, psecs -= 1000000;
+	else if (psecs < 0)
+		secs--, psecs += 1000000;
+	mins = secs / 60;
+	secs %= 60;
+	if (_etime || posix) {
+		time_t hours, days;
+
+		hours = mins / 60;
+		days = hours / 24;
+		hours %= 24;
+		mins %= 60;
+		if (days)
+			(void)snprintf(buf, sizeof(buf),
+			    "%02d-%02d:%02d:%02d", days, hours, mins, secs);
+		else if (!_etime || hours)
+			(void)snprintf(buf, sizeof(buf), "%02d:%02d:%02d",
+			    hours, mins, secs);
+		else
+			(void)snprintf(buf, sizeof(buf), "%02d:%02d",
+			    mins, secs);
+	} else {
+		static char decimal_point;
+
+		if (decimal_point == '\0')
+			decimal_point = localeconv()->decimal_point[0];
+		/*
+		 * round and scale to 100's
+		 */
+		if (secs || psecs) {
+			psecs = (psecs + 5000) / 10000;
+			secs += psecs / 100;
+			psecs %= 100;
+		}
+		(void)snprintf(buf, sizeof(buf), "%3d:%02d.%02d",
+		    mins, secs, psecs);
+	}
+	(void)printf("%*s", width, buf);
+}
+
+/* ARGSUSED */
+int
+s_time(KINFO *k __unused)
+{
+	return (posix ? LTIMELEN : STIMELEN);
 }
 
 void
@@ -503,11 +768,7 @@
 	VAR *v;
 	long secs;
 	long psecs;	/* "parts" of a second. first micro, then centi */
-	char obuff[128];
-	static char decimal_point;
 
-	if (decimal_point == '\0')
-		decimal_point = localeconv()->decimal_point[0];
 	v = ve->var;
 	if (k->ki_p->ki_stat == SZOMB || !k->ki_valid) {
 		secs = 0;
@@ -521,47 +782,114 @@
 		secs = k->ki_p->ki_runtime / 1000000;
 		psecs = k->ki_p->ki_runtime % 1000000;
 		if (sumrusage) {
+#if defined(WITH_CHILDTIMES)
+			secs += k->ki_p->ki_childstime.tv_sec +
+				k->ki_p->ki_childutime.tv_sec;
+			psecs += k->ki_p->ki_childstime.tv_usec +
+				k->ki_p->ki_childutime.tv_usec;
+#else
 			secs += k->ki_p->ki_childtime.tv_sec;
 			psecs += k->ki_p->ki_childtime.tv_usec;
+#endif
 		}
-		/*
-		 * round and scale to 100's
-		 */
-		psecs = (psecs + 5000) / 10000;
-		secs += psecs / 100;
-		psecs = psecs % 100;
-	}
-	(void)snprintf(obuff, sizeof(obuff), "%3ld:%02ld%c%02ld",
-	    secs / 60, secs % 60, decimal_point, psecs);
-	(void)printf("%*s", v->width, obuff);
+	}
+	printtime(secs, psecs, v->width, 0);
+}
+
+void
+cldtime(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+	long secs;
+	long psecs;	/* "parts" of a second. first micro, then centi */
+
+	v = ve->var;
+	if (k->ki_p->ki_stat == SZOMB || !k->ki_valid) {
+		secs = 0;
+		psecs = 0;
+	} else {
+#if defined(WITH_CHILDTIMES)
+		secs = k->ki_p->ki_childstime.tv_sec +
+			k->ki_p->ki_childutime.tv_sec;
+		psecs = k->ki_p->ki_childstime.tv_usec +
+			k->ki_p->ki_childutime.tv_usec;
+#else
+		secs = k->ki_p->ki_childtime.tv_sec;
+		psecs = k->ki_p->ki_childtime.tv_usec;
+#endif
+	}
+	printtime(secs, psecs, v->width, 0);
 }
 
 void
 elapsed(KINFO *k, VARENT *ve)
 {
 	VAR *v;
-	time_t val;
-	int days, hours, mins, secs;
-	char obuff[128];
-
-	v = ve->var;
-	val = now - k->ki_p->ki_start.tv_sec;
-	days = val / (24 * 60 * 60);
-	val %= 24 * 60 * 60;
-	hours = val / (60 * 60);
-	val %= 60 * 60;
-	mins = val / 60;
-	secs = val % 60;
-	if (days != 0)
-		(void)snprintf(obuff, sizeof(obuff), "%3d-%02d:%02d:%02d",
-		    days, hours, mins, secs);
-	else if (hours != 0)
-		(void)snprintf(obuff, sizeof(obuff), "%02d:%02d:%02d",
-		    hours, mins, secs);
-	else
-		(void)snprintf(obuff, sizeof(obuff), "%02d:%02d", mins, secs);
-	(void)printf("%*s", v->width, obuff);
+	struct timeval _now;
+	long psecs, secs;
+
+        v = ve->var;
+        if (!k->ki_valid) {
+                (void)printf("%*s", v->width, "-");
+                return;
+        }
+	gettime(NULL, &_now);
+	secs = _now.tv_sec - k->ki_p->ki_start.tv_sec;
+	psecs = _now.tv_usec - k->ki_p->ki_start.tv_usec;
+	printtime(secs, psecs, v->width, 1);
+}
+
+#if defined(COMPAT_TRU64)
+void
+systime(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+	long secs;
+	long psecs;	/* "parts" of a second. first micro, then centi */
+
+	v = ve->var;
+	if (k->ki_p->ki_stat == SZOMB || !k->ki_valid) {
+		secs = 0;
+		psecs = 0;
+	} else {
+		secs = k->ki_p->ki_rusage.ru_stime.tv_sec;
+		psecs = k->ki_p->ki_rusage.ru_stime.tv_usec;
+#if defined(WITH_CHILDTIMES)
+		if (sumrusage) {
+			secs += k->ki_p->ki_childstime.tv_sec;
+			psecs += k->ki_p->ki_childstime.tv_usec;
+		}
+#endif
+	}
+	printtime(secs, psecs, v->width, 0);
 }
+#endif
+
+#if defined(COMPAT_TRU64)
+void
+usertime(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+	long secs;
+	long psecs;	/* "parts" of a second. first micro, then centi */
+
+	v = ve->var;
+	if (k->ki_p->ki_stat == SZOMB || !k->ki_valid) {
+		secs = 0;
+		psecs = 0;
+	} else {
+		secs = k->ki_p->ki_rusage.ru_utime.tv_sec;
+		psecs = k->ki_p->ki_rusage.ru_utime.tv_usec;
+#if defined(WITH_CHILDTIMES)
+		if (sumrusage) {
+			secs += k->ki_p->ki_childutime.tv_sec;
+			psecs += k->ki_p->ki_childutime.tv_usec;
+		}
+#endif
+	}
+	printtime(secs, psecs, v->width, 0);
+}
+#endif
 
 double
 getpcpu(const KINFO *k)
@@ -633,53 +961,137 @@
 
 /* ARGSUSED */
 void
-maxrss(KINFO *k __unused, VARENT *ve)
+nop(KINFO *k __unused, VARENT *ve)
 {
 	VAR *v;
 
 	v = ve->var;
-	/* XXX not yet */
 	(void)printf("%*s", v->width, "-");
 }
 
+#if defined(WITH_MAXRSS)
+void
+maxrss(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+
+	v = ve->var;
+	if (k->ki_p->ki_maxrss != RLIM_INFINITY)
+		printsize("%*lu", v->width,
+		    (u_long)(k->ki_p->ki_maxrss / 1024));
+	else
+		(void)printf("%*s", v->width, "-");
+}
+#endif
+
+
 void
 priorityr(KINFO *k, VARENT *ve)
 {
 	VAR *v;
-	struct priority *lpri;
 	char str[8];
-	unsigned class, level;
-
+	unsigned _class, level;
+ 
 	v = ve->var;
-	lpri = &k->ki_p->ki_pri;
-	class = lpri->pri_class;
-	level = lpri->pri_level;
-	switch (class) {
+	_class = k->ki_p->ki_pri.pri_class;
+	level = k->ki_p->ki_pri.pri_level;
+	switch (_class) {
+	case PRI_ITHD:
+		(void)strncpy(str, "intr", sizeof(str));
+		break;
 	case PRI_REALTIME:
-		snprintf(str, sizeof(str), "real:%u", level);
+		(void)snprintf(str, sizeof(str), "real:%u", level);
 		break;
 	case PRI_TIMESHARE:
-		strncpy(str, "normal", sizeof(str));
+		(void)strncpy(str, "normal", sizeof(str));
 		break;
 	case PRI_IDLE:
-		snprintf(str, sizeof(str), "idle:%u", level);
+		(void)snprintf(str, sizeof(str), "idle:%u", level);
 		break;
 	default:
-		snprintf(str, sizeof(str), "%u:%u", class, level);
+		(void)snprintf(str, sizeof(str), "%u:%u", _class, level);
 		break;
 	}
 	str[sizeof(str) - 1] = '\0';
 	(void)printf("%*s", v->width, str);
 }
 
+void
+class(KINFO *k, VARENT *ve)
+{
+	VAR *v;
+	char str[8];
+	unsigned _class;
+ 
+	v = ve->var;
+	_class = k->ki_p->ki_pri.pri_class;
+	switch (_class) {
+	case PRI_ITHD:
+		(void)strncpy(str, "intr", sizeof(str)); /* FP? */
+		break;
+	case PRI_REALTIME:
+		(void)strncpy(str, "real", sizeof(str)); /* RT? */
+		break;
+	case PRI_TIMESHARE:
+		(void)strncpy(str, "normal", sizeof(str)); /* TS? */
+		break;
+	case PRI_IDLE:
+		(void)strncpy(str, "idle", sizeof(str)); /* ID? */
+		break;
+	default:
+		(void)snprintf(str, sizeof(str), "%u", _class);
+		break;
+	}
+	str[sizeof(str) - 1] = '\0';
+	(void)printf("%-*s", v->width, str);
+}
+
+void
+printsize(const char *ofmt, short width, u_long kbytes)
+{
+	if (extended) {
+		static const char units[] = "KMGTP";
+		static const char skip[] = "%-*";
+		const char *kp, *fp, *sp;
+		char fmt[BUFLEN], buf[BUFLEN];
+		size_t n;
+		u_long left;
+
+		for (kp = units, n = strlen(units), left = 0; n; --n)
+			if (kbytes >= 1024) {
+				left = kbytes % 1024;
+				kbytes /= 1024;
+				kp++;
+			} else
+				break;
+		for (sp = skip, fp = ofmt;*sp && *fp; sp++)
+			if (*sp == *fp)
+				fp++;
+		if (left) {
+			left *= 100;
+			left /= 1024;
+			if (!(left % 10))
+				left /= 10;
+			(void)snprintf(fmt, sizeof(fmt), "%%%s.%%%s", fp, fp);
+			(void)snprintf(buf, sizeof(buf), fmt, kbytes, left);
+		} else {
+			(void)snprintf(fmt, sizeof(fmt), "%%%s", fp);
+			(void)snprintf(buf, sizeof(buf), fmt, kbytes);
+		}
+		(void)printf("%*s", width-1, buf);
+		(void)putchar(*kp);
+	} else
+		(void)printf(ofmt, width, kbytes);
+}
+
 /*
  * Generic output routines.  Print fields from various prototype
  * structures.
  */
 static void
-printval(void *bp, VAR *v)
+printval(char *bp, VAR *v)
 {
-	static char ofmt[32] = "%";
+	static char ofmt[FMTLEN] = "%";
 	const char *fcp;
 	char *cp;
 
@@ -721,7 +1133,7 @@
 		(void)printf(ofmt, v->width, *(u_long *)bp);
 		break;
 	case PGTOK:
-		(void)printf(ofmt, v->width, ps_pgtok(*(u_long *)bp));
+		printsize(ofmt, v->width, ps_pgtok(*(u_long *)bp));
 		break;
 	default:
 		errx(1, "unknown type %d", v->type);
@@ -774,7 +1186,7 @@
 		(void)printf("%-*s", v->width, string);
 		free(string);
 	} else
-		(void)printf("%-*s", v->width, "");
+		(void)printf("%-*s", v->width, "-");
 	return;
 }
 
Index: ps.1
===================================================================
RCS file: /home/ncvs/src/bin/ps/ps.1,v
retrieving revision 1.75
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.75 ps.1
--- ps.1	6 Apr 2004 20:06:49 -0000	1.75
+++ ps.1	18 Apr 2004 17:23:13 -0000
@@ -57,6 +57,20 @@
 .Op , Ns Ar username Ns No ...
 .Xc
 .Oc
+.Nm ps5
+.Op Fl AaBdefjl
+.Op Fl C Ar cmdlist
+.Op Fl g Ar pgrplist
+.Op Fl G Ar grouplist
+.Op Fl m Ar core
+.Op Fl n Ar system
+.Op Fl O Ar fmt
+.Op Fl o Ar fmt
+.Op Fl p Ar pidlist
+.Op Fl t Ar ttylist
+.Op Fl u Ar userlist
+.Op Fl U Ar userlist
+.Op Fl w Ar swap
 .Nm
 .Op Fl L
 .Sh DESCRIPTION
@@ -95,6 +109,11 @@
 options will change the sort order.
 If more than one sorting option was given, then the selected processes
 will be sorted by the last sorting option which was specified.
+.Pp
+.Nm psx
+utility understands 
+.At v4
+command line options and displays process informations in the related format.
 .Pp
 For the processes which have been selected for display, the information
 to display is selected based on a set of keywords (see the
Index: ps.c
===================================================================
RCS file: /home/ncvs/src/bin/ps/ps.c,v
retrieving revision 1.88
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.88 ps.c
--- ps.c	6 Apr 2004 20:06:49 -0000	1.88
+++ ps.c	20 Apr 2004 01:31:42 -0000
@@ -73,107 +73,145 @@
 
 #include "ps.h"
 
-#define	W_SEP	" \t"		/* "Whitespace" list separators */
-#define	T_SEP	","		/* "Terminate-element" list separators */
-
-#ifdef LAZY_PS
-#define	DEF_UREAD	0
-#define	OPT_LAZY_f	"f"
-#else
-#define	DEF_UREAD	1	/* Always do the more-expensive read. */
-#define	OPT_LAZY_f		/* I.e., the `-f' option is not added. */
+#ifndef PID_MAX
+#define PID_MAX		99999		/* sys/proc.h */
 #endif
-
-int	 cflag;			/* -c */
-int	 eval;			/* Exit value */
-time_t	 now;			/* Current time(3) value */
-int	 rawcpu;		/* -C */
-int	 sumrusage;		/* -S */
-int	 termwidth;		/* Width of the screen (0 == infinity). */
-int	 totwidth;		/* Calculated-width of requested variables. */
-
-struct varent *vhead;
-
-static int	 forceuread = DEF_UREAD; /* Do extra work to get u-area. */
-static kvm_t	*kd;
-static KINFO	*kinfo;
-static int	 needcomm;	/* -o "command" */
-static int	 needenv;	/* -e */
-static int	 needuser;	/* -o "user" */
-static int	 optfatal;	/* Fatal error parsing some list-option. */
-
-static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
-
-struct listinfo;
-typedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
-
-struct listinfo {
+#define BUCKETS		32		/* power of 2 */
+#define COLSENV		"COLUMNS"	/* getenv() */
+#define POSIXENV	"POSIX"		/* getenv() */
+#define OPTLEN		64
+#define FMTLEN		16
+
+enum ltype { CMD, FMT, GID, PGID, PID, RUID, SID, TTY, UID, _LTYPE };
+enum sort { DEFAULT, SORTMEM, SORTCPU };
+
+typedef struct list {
+	enum	ltype	 type;
+	size_t		 size;
+	size_t		 off;
+	int		 op;
+	const	char	*msg;
+	const	char	*msg2;
 	int		 count;
-	int		 maxcount;
-	int		 elemsize;
-	addelem_rtn	*addelem;
-	const char	*lname;
+	int		 noval;
 	union {
+		char   **cmds;
+		char	*fmt;
 		gid_t	*gids;
 		pid_t	*pids;
 		dev_t	*ttys;
 		uid_t	*uids;
 		void	*ptr;
 	} l;
+} LIST;
+
+/*
+ * Global variables
+ */
+
+VARLIST varlist = STAILQ_HEAD_INITIALIZER(varlist);
+
+int	 commandonly;		/* -c */
+int	 eval;			/* Exit value */
+int	 rawcpu;		/* -C */
+int	 sumrusage;		/* -S */
+int	 termwidth;		/* -w : width of screen (0 == infinity) */
+int	 totwidth;		/* Calculated width of requested variables */
+
+int	 posix;			/* -q | atoi(getenv(POSIXENV)) != 0 */
+int	 extended;		/* -Q */
+
+/*
+ * Local variables
+ */
+				/* bsd options | posix options */
+static int	all;		/* -A, -a or -g | -A, -a, -d, or -e */
+static int	noctty;		/* -A, -x or -X | -A, -d or -e */
+static int	leader;		/* -A or -g | -A or -e */
+static int	needenv;	/* -e | nop */
+#if defined(LAZY_PS)
+static int	forceuread;	/* -f | nop */
+#else
+static int	forceuread = 1;	/* Do extra work to get u-area. */
+#endif
+static int	showthreads;	/* -H */
+static int	prtheader;	/* -h */
+static const char *memf;	/* -M | -n -n */
+static enum sort sortby = DEFAULT;	/* -m | -r */
+static const char *nlistf;	/* -N | -n */
+static const char *devnull = _PATH_DEVNULL;
+static int	dropgid;	/* -M or -N | -n */
+static int	needcomm;	/* -o command */
+static int	needuser;	/* -o user */
+
+/* PLEASE KEEP THE TABLE BELOW SORTED ALPHABETICALLY!!! */
+static LIST	lists[] = {
+#define _LIST(t, s, o, k, m1, m2)	\
+		{ t, sizeof(s), KOFF(o), k, m1, m2, 0, 0, { 0 } }
+	_LIST(CMD,  char *, ki_comm, 0,
+		"command", NULL),			/* nop | -C */
+	_LIST(FMT,  char,   ki_sparestrings, 0,
+		"format", NULL),			/* -O or -o | ditto */
+	_LIST(GID,  gid_t,  ki_rgid, 0,
+		"group", " name or id"),		/* -G | -G */
+	_LIST(PGID, pid_t,  ki_pgid, KERN_PROC_PGRP,
+		"process group id", NULL),		/* nop | -g */
+	_LIST(PID,  pid_t,  ki_pid, KERN_PROC_PID,
+		"process id", NULL),			/* -p | -p */
+	_LIST(RUID, uid_t,  ki_ruid, KERN_PROC_RUID,
+		"user", " name or id"),			/* nop | -U */
+	_LIST(SID,  pid_t,  ki_sid, /*KERN_PROC_SESSION*/0,
+		"session id", NULL),			/* nop | -s */
+	_LIST(TTY,  dev_t,  ki_tdev, KERN_PROC_TTY,
+		"terminal", NULL),			/* -T or -t | -t */
+	_LIST(UID,  uid_t,  ki_uid, KERN_PROC_UID,
+		"user", " name or id"),			/* -U | -u */
+	_LIST(_LTYPE, int,  ki_sparestrings, 0, NULL, NULL),
+#undef _LIST
 };
 
-static int	 addelem_gid(struct listinfo *, const char *);
-static int	 addelem_pid(struct listinfo *, const char *);
-static int	 addelem_tty(struct listinfo *, const char *);
-static int	 addelem_uid(struct listinfo *, const char *);
-static void	 add_list(struct listinfo *, const char *);
-static void	 dynsizevars(KINFO *);
-static void	*expand_list(struct listinfo *);
-static const char *
-		 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
-		    KINFO *, char *, int);
-static void	 free_list(struct listinfo *);
-static void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
-static char	*kludge_oldps_options(char *);
-static int	 pscomp(const void *, const void *);
-static void	 saveuser(KINFO *);
-static void	 scanvars(void);
-static void	 sizevars(void);
-static void	 usage(void);
-
-static char dfmt[] = "pid,tt,state,time,command";
-static char jfmt[] = "user,pid,ppid,pgid,jobc,state,tt,time,command";
-static char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
-			"tt,time,command";
-static char   o1[] = "pid";
-static char   o2[] = "tt,state,time,command";
-static char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
-static char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
-			"%cpu,%mem,command";
-static char Zfmt[] = "label";
+static KINFO	*kinfo;
+static struct winsize ws;
+static int	 fatal;		/* fatal error while parsing some list-option */
 
-#define	PS_ARGS	"AaCce" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
+/*
+ * Local functions
+ */
+static const char *format(kvm_t *,
+		    char **(*)(kvm_t *, const struct kinfo_proc *, int),
+		    KINFO *, const char *, int);
+static char    *kludge_oldps_options(char *);
+static int	findproc(const struct kinfo_proc *, int);
+static void	parseopts(int, char *const *);
+static void	parselst(const char *, LIST *);
+static int	pscomp(const void *, const void *);
+static void	saveuser(kvm_t *, KINFO *);
+static void	scanvars(void);
+static void	dynsizevars(KINFO *);
+static void	sizevars(void);
+static void	usage(char);
 
 int
 main(int argc, char *argv[])
 {
-	struct listinfo gidlist, pgrplist, pidlist;
-	struct listinfo ruidlist, sesslist, ttylist, uidlist;
-	struct kinfo_proc *kp;
-	struct varent *vent;
-	struct winsize ws;
-	const char *cp, *nlistf, *memf;
-	char *cols;
-	int all, ch, dropgid, elem, flag, _fmt, i, lineno;
-	int nentries, nocludge, nkept, nselectors;
-	int prtheader, showthreads, wflag, what, xkeep, xkeep_implied;
-	char errbuf[_POSIX2_LINE_MAX];
+	kvm_t *kd;			/* kvm file descriptor */
+	struct kinfo_proc *kp;		/* kvm_getprocs() */
+	VARENT *vent;
+	LIST *list;
+	int i, lineno;			/* indices */
+	int what, flag, nentries;	/* kvm_getprocs() */
+	int nkept, nselectors;
+	char errbuf[_POSIX2_LINE_MAX];	/* kvm_openfiles() */
+	const char *cp;
+	static char optargs[] = "OoptUMN";
+
+	memf = nlistf = devnull;
 
 	(void) setlocale(LC_ALL, "");
-	time(&now);			/* Used by routines in print.c. */
+	gettime(NULL, NULL);		/* Used by routines in print.c. */
 
-	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
-		termwidth = atoi(cols);
+	if ((cp = getenv("COLUMNS")) != NULL && *cp != '\0')
+		termwidth = atoi(cp);	/* XXX ERANGE */
 	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
 	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
 	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
@@ -182,726 +220,1006 @@
 	else
 		termwidth = ws.ws_col - 1;
 
+	if ((cp = strrchr(argv[0], '/')) != NULL)
+		cp++;
+	else
+		cp = argv[0];
+	posix = strchr(cp, 'x') != NULL;	/* as in psx */
+	extended = *cp == 's';			/* as in sps */
+#if defined(WITH_POSIX_ENV)
+	if ((cp = getenv(POSIXENV)) != NULL && *cp != '\0')
+		posix = atoi(cp);	/* XXX ERANGE */
+#endif
 	/*
 	 * Don't apply a kludge if the first argument is an option taking an
 	 * argument
 	 */
-	if (argc > 1) {
-		nocludge = 0;
-		if (argv[1][0] == '-') {
-			for (cp = PS_ARGS; *cp != '\0'; cp++) {
-				if (*cp != ':')
-					continue;
-				if (*(cp - 1) == argv[1][1]) {
-					nocludge = 1;
-					break;
-				}
+	if (argc > 1 && !(argv[1][0] == '-' && strchr(optargs, argv[1][1])))
+		argv[1] = kludge_oldps_options(argv[1]);
+
+	all = noctty = leader = -1;
+	parseopts(argc, argv);
+
+	/*
+	 * Display an error message for options with only empty arguments
+	 * which is fatal or count how many arguments where specified
+	 */
+	nselectors = 0;
+	for (list = lists; list->type != _LTYPE; list++) {
+		if (list->noval) {
+			warnx("no %s%s specified", list->msg,
+			    list->msg2 ? list->msg2 : "");
+			fatal = 1;
+		}
+		if (list->type != FMT)
+			nselectors += list->count;
+	}
+
+	if (fatal)
+		exit(fatal);
+
+#if defined(BACKWARD_COMPATIBILITY)
+	if (!posix && *argv) {
+		nlistf = *argv;
+		if (*++argv) {
+			memf = *argv;
+		}
+	}
+#endif
+	/*
+	 * Discard setgid privileges if not the running kernel so that bad
+	 * guys can't print interesting stuff from kernel memory.
+	 */
+	if (dropgid) {
+		setgid(getgid());
+		setuid(getuid());
+	}
+
+	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
+	if (kd == 0)
+		errx(1, "%s", errbuf);
+
+#if defined(DEBUG)
+fprintf(stderr, "all:noctty:leader:nselectors:extended == %d:%d:%d:%d:%d -> ",
+	all, noctty, leader, nselectors, extended);
+#endif
+	/*
+	 * show processes without a controlling terminal if... see below.
+	 * posix : show them also if -d was given.
+	 * bsd : show them also if -x was given
+	 *	(or don't show them if -X xas given).
+	 */
+	if (noctty < 0)
+		noctty = nselectors;
+	/*
+	 * posix : select leader processes if at least one of -C, -G, -g
+	 *	   -p, -s, -t, -U or -u, where given.
+	 *	   select them also if -A or -e where given.
+	 * bsd : select leader processes if at least one of -G, -p, -t,
+	 *	 -U where given.  select them also if -A is given.
+	 *	 in sunos compat mode, select them if -g is given.
+	 */
+	if (leader < 0)
+		leader = posix ? nselectors : !extended;
+	/*
+	 * find all processes if ... see above.
+	 * posix : find them also if -d was given.
+	 * bsd : find them also if -a or -g where given.
+	 */
+	if (all < 0)
+		all = nselectors;
+#if defined(DEBUG)
+fprintf(stderr, "%d:%d:%d:%d:%d -> ", all, noctty, leader, nselectors, extended);
+#endif
+	if (!all) {
+		char buf[UIDLEN];
+ 
+		snprintf(buf, sizeof(buf), "%d", getuid());
+		parselst(buf, &lists[UID]);
+		nselectors = 1;
+	}
+#if defined(DEBUG)
+fprintf(stderr, "%d:%d:%d:%d:%d\n", all, noctty, leader, nselectors, extended);
+#endif
+	/*
+	 * scan requested variables, noting what structures are needed,
+	 * and adjusting header widths as appropriate.
+	 */
+	scanvars();
+
+	/*
+	 * get proc list
+	 */
+	what = showthreads ? KERN_PROC_ALL : KERN_PROC_PROC;
+	flag = 0;
+	if (nselectors == 1) {
+		for (list = lists; list->type != _LTYPE; list++)
+			if (list->count && list->op) {
+				what = list->op | showthreads;
+				flag = (int)((int *)list->l.ptr)[0];
+				nselectors = 0;
+				break;
 			}
+	}
+
+	/*
+	 * select procs
+	 */
+	nentries = -1;
+	kp = kvm_getprocs(kd, what, flag, &nentries);
+	if (!kp || nentries < 0)
+		errx(1, "%s", kvm_geterr(kd));
+	nkept = 0;
+	if (nentries > 0) {
+		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
+			errx(1, "malloc failed");
+		for (i = nentries; --i >= 0; ++kp)
+			if (findproc(kp, nselectors)) {
+				kinfo[nkept].ki_p = kp;
+				if (needuser)
+					saveuser(kd, &kinfo[nkept]);
+				dynsizevars(&kinfo[nkept]);
+				nkept++;
+			}
+	}
+#if defined(DEBUG)
+fprintf(stderr, "what:flag:nentries:nkept == %d:%d:%d:%d\n",
+	what, flag, nentries, nkept);
+#endif
+	sizevars();
+
+	/*
+	 * print header
+	 */
+	printheader();
+
+	/*
+	 * oops! no proc, goodbye.
+	 */
+	if (nkept == 0)
+		exit(1);
+
+	/*
+	 * sort proc list
+	 */
+	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
+
+	/*
+	 * for each proc, call each variable output function.
+	 */
+	for (i = lineno = 0; i < nkept; i++) {
+		STAILQ_FOREACH(vent, &varlist, list) {
+			(vent->var->oproc)(&kinfo[i], vent);
+			if (STAILQ_NEXT(vent, list) != NULL)
+				(void)putchar(' ');
+		}
+		(void)putchar('\n');
+		if (prtheader && lineno++ == prtheader - 4) {
+			(void)putchar('\n');
+			printheader();
+			lineno = 0;
 		}
-		if (nocludge == 0)
-			argv[1] = kludge_oldps_options(argv[1]);
 	}
+#ifdef notneeded
+	for (i = 0; i < lists[CMD].count; i++)
+		free(lists[CMD].l.cmds[i]);
+	for (list = lists; list->type != _LTYPE; list++)
+		free(list->l.ptr);
+#endif
+	exit(eval);
+}
 
-	all = dropgid = _fmt = nselectors = optfatal = 0;
-	prtheader = showthreads = wflag = xkeep_implied = 0;
-	xkeep = -1;			/* Neither -x nor -X. */
-	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
-	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
-	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
-	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
-	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
-	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
-	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
-	memf = nlistf = _PATH_DEVNULL;
-	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
-		switch((char)ch) {
-		case 'A':
-			/*
-			 * Exactly the same as `-ax'.   This has been
-			 * added for compatability with SUSv3, but for
-			 * now it will not be described in the man page.
-			 */
-			nselectors++;
-			all = xkeep = 1;
+int
+findproc(const struct kinfo_proc *kp, int nselectors)
+{
+	int i;
+
+	for (i = 0; i < lists[PID].count; i++)
+		if (kp->ki_pid == lists[PID].l.pids[i])
+			return(1);
+	if (
+#if defined(WITH_CTTY)
+	    (!noctty && !(kp->ki_kiflag & KI_CTTY)) ||
+#else
+	    (!posix && !noctty &&
+	     (kp->ki_tdev == NODEV || !(kp->ki_flag & P_CONTROLT))) ||
+	    (posix && !noctty && !(kp->ki_kiflag & KI_CTTY)) ||
+#endif
+	    (!leader && (kp->ki_kiflag & KI_SLEADER)))
+		return(0);
+	if (!nselectors)
+		return(1);
+#if 1
+	{
+	LIST *list;
+
+	for (list = lists; list->type != _LTYPE; list++)
+		switch (list->type) {
+		case FMT:
+		case PID:
+			continue;
+		case CMD:
+			for (i = 0; i < list->count; i++)
+				if (!strcmp((const char *)((const char *)kp +
+					    list->off),
+					    ((const char **)list->l.ptr)[i]))
+					return(1);
 			break;
+		default:
+			for (i = 0; i < list->count; i++)
+				if (*(const u_int *)((const char *)kp +
+				    list->off)
+				    == ((const u_int *)list->l.ptr)[i])
+					return(1);
+		}
+	}
+#else
+	for (i = 0; i < lists[UID].count; i++)
+		if (kp->ki_uid == lists[UID].l.uids[i])
+			return(1);
+	for (i = 0; i < lists[TTY].count; i++)
+		if (kp->ki_tdev == lists[TTY].l.ttys[i])
+			return(1);
+	for (i = 0; i < lists[GID].count; i++)
+		if (kp->ki_rgid == lists[GID].l.gids[i])
+			return(1);
+	for (i = 0; i < lists[PGID].count; i++)
+		if (kp->ki_pgid == lists[PGID].l.pids[i])
+			return(1);
+	for (i = 0; i < lists[RUID].count; i++)
+		if (kp->ki_ruid == lists[RUID].l.uids[i])
+			return(1);
+	for (i = 0; i < lists[SID].count; i++)
+		if (kp->ki_sid == lists[SID].l.pids[i])
+			return(1);
+	for (i = 0; i < lists[CMD].count; i++)
+		if (!strcmp(kp->ki_comm, lists[CMD].l.cmds[i]))
+			return(1);
+#endif
+	return(0);
+}
+
+static void
+parseopts(int argc, char *const *argv)
+{
+	static char bsd_opts[] = "AaCcefG:gHhjLlM:mN:nO:o:p:QqrSsTt:U:uvwxXZ";
+	static char psx_opts[] = "AaC:cdefG:g:jLlMmn:O:o:Pp:Qqs:Tt:u:U:xyz";
+	/* internal format syntax:
+	 *	keyword[:flags in reverse polish notation][=header]
+	 * reverse polish notation:
+	 *	x by itself, * means any, x! means not x,
+	 *	xy| means x | y, xy& means x & y, xy^ means x ^ y.
+	 *	[xyz] is a more compact and readable form of xy|z|
+	 *	[a-z] isn't supported and is understood as a-|z|
+	 *	() may be used for readability but are no-op.
+	 * bsd flags: d12jlsuv, bsd modifiers: nZ
+	 * posix flags: d12fjl, posix modifiers: cMPy
+	 * both modifiers: X if extended.
+	 */
+/*
+Z: prepend label
+d: pid tt state time command
+j: user pid ppid pgid (sid) jobc state tt time command
+l: uid pid ppid cpu pri nice vsz rss mwchan state tt time command
+n: user -> uid, mwchan -> nwchan
+u: user pid %cpu %mem vsz rss tt state start time command
+s: uid pid sig sigmask sigignore sigcatch state tt command
+v: pid (tt) state time sl re pagein vsz rss lim tsiz %cpu %mem command
+*/
+	static char bsd_fmtstr[] = "\
+label:Z user:[ju](n!)& uid:([ju]n&)([ls]([ju]!)&)| pid:2! ppid:[jl] \
+pgid:j sid:jX& tpgid:jX& jobc:j sig:s sigmask:s sigignore:s sigcatch:s \
+cpu:l pri:l nice:l %cpu:u %mem:u vsz:[lu] rss:[lu] mwchan:l(n!)& \
+nwchan:ln& tt:[d2u](vX&)| state:1! tt:[jls]([du]!)& start:u time:1! \
+sl:v re:v pagein:v vsz:v([lu]!)& rss:v([lu]!)& lim:v tsiz:v %cpu:v(u!)& \
+%mem:v(u!)& command:1! \
+";
+/*
+M: prepend label
+P: add psr
+c: omit nice, c -> cls
+d: pid tty time comm
+f: user pid ppid c stime tty time args
+j: pid ppid pgid sid tty time comm
+l: f s uid pid ppid c pri nice addr sz wchan tty time comm
+y: omit f, addr -> rss
+*/
+	static char psx_fmtstr[] = "\
+label:M f:l(y!)& s:l user:f uid:l(f!)& pid:2! ppid:[fjl] pgid:j \
+sid:j tpgid:jX& c:[fl](c!)& class:[fl]c& psr:P pri:l nice:l(c!)& \
+addr:l(y!)& rss:ly& sz:l wchan:l stime:f tty:1! time:1! comm:[1f]! \
+args:f \
+";
+
+	int	c;
+	int	cflag;			/* -c */
+	int	fflag;			/* -f */
+	int	jflag;			/* -j */
+	int	lflag;			/* -l */
+	int	Mflag;			/* -M */
+	int	nflag;			/* -n */
+	int	Oflag;			/* -O */
+	int	oflag;			/* -o */
+	int	Pflag;			/* -P */
+	int	sflag;			/* -s */
+	int	uflag;			/* -u */
+	int	vflag;			/* -v */
+	int	wflag;			/* -w */
+	int	yflag;			/* -y */
+	int	Zflag;			/* -Z */
+
+	cflag = fflag = jflag = lflag = Mflag = nflag = Oflag = oflag = 0;
+	Pflag = sflag = uflag = vflag = wflag = yflag = Zflag = 0;
+
+	while ((c = getopt(argc, argv, posix ? psx_opts : bsd_opts)) != -1)
+		switch(c) {
 		case 'a':
-			nselectors++;
+			/*
+			 * posix : show all processes with a controlling
+			 *	terminal except session leader processes.
+			 * bsd : show all processes with a controlling
+			 *	terminal. in sunos compat mode (-Q),
+			 *	don't show session leader processes.
+			 */
 			all = 1;
 			break;
 		case 'C':
-			rawcpu = 1;
+			/*
+			 * posix (hpux) : -C cmdlist
+			 *	select processes by command name.
+			 * bsd : raw cpu usage instead of the average one.
+			 */
+			if (posix)
+				parselst(optarg, &lists[CMD]);
+			else
+				rawcpu = 1;
 			break;
 		case 'c':
-			cflag = 1;
+			/*
+			 * posix (!susv3) : omits the nice field and
+			 *	replaces the c field with the class field.
+			 * bsd : a sort of replaces the command field
+			 *	with the ucomm field.
+			 */
+			if (posix)
+				cflag = 1;
+			else
+				commandonly = 1;
 			break;
-		case 'e':			/* XXX set ufmt */
-			needenv = 1;
+		case 'd':
+			/*
+			 * posix only : show all processes except session
+			 *	leader processes.
+			 */
+			all = noctty = 1;
 			break;
-#ifdef LAZY_PS
-		case 'f':
-			if (getuid() == 0 || getgid() == 0)
-				forceuread = 1;
+		case 'e':
+			/*
+			 * posix : show all processes.
+			 * bsd : display the environment.
+			 */
+			if (posix)
+		case 'A':
+			/*
+			 * posix / bsd : show all processes.
+			 */
+				all = noctty = leader = 1;
+			else
+				needenv = 1;
 			break;
+		case 'f':
+			/*
+			 * posix : user oriented output.
+			 * bsd : read from swapped out processes.
+			 */
+			if (posix) {
+				fflag = 1;
+				if (termwidth < 131)
+					termwidth = 131;
+			} else
+#if defined(LAZY_PS)
+				if (getuid() == 0 || getgid() == 0)
+					forceuread = 1;
+#else
+				usage(c);
 #endif
+			break;
 		case 'G':
-			add_list(&gidlist, optarg);
-			xkeep_implied = 1;
-			nselectors++;
+			/*
+			 * posix / bsd : -G grouplist
+			 *	select processes by effective group name(s)
+			 *	or effective group id(s).
+			 */
+			parselst(optarg, &lists[GID]);
 			break;
 		case 'g':
-#if 0
-			/*-
-			 * XXX - This SUSv3 behavior is still under debate
-			 *	since it conflicts with the (undocumented)
-			 *	`-g' option.  So we skip it for now.
-			 */
-			add_list(&pgrplist, optarg);
-			xkeep_implied = 1;
-			nselectors++;
-			break;
-#else
+			/*
+			 * posix : -g pgidlist
+			 *	select processes by process group id(s).
+			 * bsd : show all processes with a controlling
+			 *	terminal and session leader processes.
+			 */
+			if (posix)
+				parselst(optarg, &lists[PGID]);
+			else
 			/* The historical BSD-ish (from SunOS) behavior. */
-			break;			/* no-op */
-#endif
+				all = leader = 1;
+			break;
 		case 'H':
+			/*
+			 * bsd only (freebsd) : show threads.
+			 */
 			showthreads = KERN_PROC_INC_THREAD;
 			break;
 		case 'h':
+			/*
+			 * bsd only : repeats the header line.
+			 */
 			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
 			break;
 		case 'j':
-			parsefmt(jfmt, 0);
-			_fmt = 1;
-			jfmt[0] = '\0';
+			/*
+			 * posix (!susv3) / bsd : job control oriented output.
+			 */
+			jflag = 1;
 			break;
+#if 0
+		case 'k':
+			/*
+			 * bsd only : conflicts!
+			 * netbsd : -k keylist
+			 *	sort by the specified keyword(s).
+			 * openbsd : don't show kernel thread.
+			 * proposed : -k keylist and -K ?
+			 */
+			break;
+#endif
 		case 'L':
-			showkey();
-			exit(0);
+			/*
+			 * posix (solaris) : show threads.
+			 * bsd : show keywords.
+			 */
+			if (posix)
+				showthreads = KERN_PROC_INC_THREAD;
+			else {
+				showkey();
+				exit(0);
+			}
 		case 'l':
-			parsefmt(lfmt, 0);
-			_fmt = 1;
-			lfmt[0] = '\0';
+			/*
+			 * posix / bsd : long format output.
+			 */
+			lflag = 1;
 			break;
 		case 'M':
-			memf = optarg;
-			dropgid = 1;
+			/*
+			 * posix (irix) : prepend the label field.
+			 * bsd : -M core
+			 *	specifies an alternative core file.
+			 */
+			if (posix)
+				Mflag = 1;
+			else {
+				memf = optarg;
+				dropgid = 1;
+			}
 			break;
 		case 'm':
-			sortby = SORTMEM;
+			/*
+			 * posix (tru64) : show threads.
+			 * bsd : sort by memory usage.
+			 */
+			if (posix)
+				showthreads = KERN_PROC_INC_THREAD;
+			else
+				sortby = SORTMEM;
+			break;
+		case 'n':
+			/*
+			 * posix : -n nlist [-n core]
+			 *	specifies an alternative system namelist file
+			 *	(and core file).
+			 * bsd : numerical output of the user and wchan fields.
+			 */
+			if (posix) {
+				if (nlistf == devnull)
+					nlistf = optarg;
+				else if (memf == devnull)
+					memf = optarg;
+				dropgid = 1;
+			} else
+				nflag = 1;
 			break;
 		case 'N':
+			/*
+			 * bsd only : -N nlist
+			 *	specifies an alternative system namelist file.
+			 */
 			nlistf = optarg;
 			dropgid = 1;
 			break;
 		case 'O':
-			parsefmt(o1, 1);
-			parsefmt(optarg, 1);
-			parsefmt(o2, 1);
-			o1[0] = o2[0] = '\0';
-			_fmt = 1;
-			break;
+			/*
+			 * user defined output.
+			 * posix : same as -o pid,fmtlist,tty,time,comm
+			 * bsd : same as -o pid,fmtlist,tt,state,time,command
+			 */
+			Oflag = 1;
+			/* FALLTHROUGH */
 		case 'o':
-			parsefmt(optarg, 1);
-			_fmt = 1;
+			/*
+			 * user defined output.
+			 * posix / bsd : -o fmtlist
+			 * posix only : -o help behaves as bsd -L.
+			 */
+			if (posix && !strcmp(optarg, "help")) {
+				showkey();
+				exit(0);
+			}
+			parselst(optarg, &lists[FMT]);
+			oflag = 1;
+			break;
+		case 'P':
+			/*
+			 * posix (solaris) : includes the psr field.
+			 */
+			Pflag = 1;
 			break;
 		case 'p':
-			add_list(&pidlist, optarg);
 			/*
-			 * Note: `-p' does not *set* xkeep, but any values
-			 * from pidlist are checked before xkeep is.  That
-			 * way they are always matched, even if the user
-			 * specifies `-X'.
+			 * posix / bsd : -p pidlist
+			 *	select processes by process id(s).
 			 */
-			nselectors++;
+			parselst(optarg, &lists[PID]);
 			break;
-#if 0
-		case 'R':
-			/*-
-			 * XXX - This un-standard option is still under
-			 *	debate.  This is what SUSv3 defines as
-			 *	the `-U' option, and while it would be
-			 *	nice to have, it could cause even more
-			 *	confusion to implement it as `-R'.
-			 */
-			add_list(&ruidlist, optarg);
-			xkeep_implied = 1;
-			nselectors++;
+		case 'Q':
+			/*
+			 * enable or disable compatibility with sunos and tru64
+			 * sunos : leader processes aren't selected by default,
+			 *	   uses -g to select them.
+			 * tru64 : displays size in KMG format.
+			 *	   ex. : 1.54M instead of 1580
+			 */
+			extended = !extended;
+			break;
+		case 'q':
+			/*
+			 * enable or disable the posix compatibility.
+			 */
+			posix = !posix;
 			break;
-#endif
 		case 'r':
+			/*
+			 * bsd only : sort by cpu usage.
+			 */
 			sortby = SORTCPU;
 			break;
 		case 'S':
+			/*
+			 * bsd only : accumulates children times.
+			 */
 			sumrusage = 1;
 			break;
-#if 0
 		case 's':
-			/*-
-			 * XXX - This non-standard option is still under
-			 *	debate.  This *is* supported on Solaris,
-			 *	Linux, and IRIX, but conflicts with `-s'
-			 *	on NetBSD and maybe some older BSD's.
-			 */
-			add_list(&sesslist, optarg);
-			xkeep_implied = 1;
-			nselectors++;
+			/*
+			 * posix : -s sidlist
+			 *	select processes by session id(s).
+			 * bsd (bsd43) : same as -o SFMT
+			 *	signal oriented output.
+			 * PS : conflict w/ NetBSD -s (show thread).
+			 */
+			if (posix)
+				parselst(optarg, &lists[SID]);
+			else
+				sflag = 1;
 			break;
-#endif
 		case 'T':
+			/*
+			 * posix (irix) : show threads.
+			 * bsd : same as -t `tty`
+			 */
+			if (posix) {
+				showthreads = KERN_PROC_INC_THREAD;
+				break;
+			}
 			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
 				errx(1, "stdin: not a terminal");
 			/* FALLTHROUGH */
 		case 't':
-			add_list(&ttylist, optarg);
-			xkeep_implied = 1;
-			nselectors++;
+			/*
+			 * posix/bsd : -t ttylist
+			 * 	select processes by terminal name(s).
+			 */
+			parselst(optarg, &lists[TTY]);
 			break;
 		case 'U':
-			/* This is what SUSv3 defines as the `-u' option. */
-			add_list(&uidlist, optarg);
-			xkeep_implied = 1;
-			nselectors++;
+			/*
+			 * posix : -U ruserlist
+			 *	select processes by real user name(s)
+			 *	or real user id(s).
+			 * bsd : -U userlist
+			 *	select processes by effective user name(s)
+			 *	or effective user id(s).
+			 */
+			if (posix)
+				parselst(optarg, &lists[RUID]);
+			else
+				parselst(optarg, &lists[UID]);
 			break;
 		case 'u':
-			parsefmt(ufmt, 0);
-			sortby = SORTCPU;
-			_fmt = 1;
-			ufmt[0] = '\0';
+			/*
+			 * posix : -U userlist
+			 *	select processes by effective user name(s)
+			 *	or effective user id(s).
+			 * bsd : same as -o UFMT -r
+			 *	user oriented output.
+			 */
+			if (posix)
+				parselst(optarg, &lists[RUID]);
+			else {
+				sortby = SORTCPU;
+				uflag = 1;
+			}
 			break;
 		case 'v':
-			parsefmt(vfmt, 0);
+			/*
+			 * bsd only : same as -o VFMT -m
+			 *	virtual memory oriented output.
+			 */
 			sortby = SORTMEM;
-			_fmt = 1;
-			vfmt[0] = '\0';
+			vflag = 1;
 			break;
 		case 'w':
+			/*
+			 * bsd only : wide format output.
+			 */
 			if (wflag)
 				termwidth = UNLIMITED;
 			else if (termwidth < 131)
 				termwidth = 131;
-			wflag++;
+			wflag = 1;
 			break;
 		case 'X':
 			/*
-			 * Note that `-X' and `-x' are not standard "selector"
-			 * options. For most selector-options, we check *all*
-			 * processes to see if any are matched by the given
-			 * value(s).  After we have a set of all the matched
-			 * processes, then `-X' and `-x' govern whether we
-			 * modify that *matched* set for processes which do
-			 * not have a controlling terminal.  `-X' causes
-			 * those processes to be deleted from the matched
-			 * set, while `-x' causes them to be kept.
+			 * bsd only : don't show processes without a
+			 *	controlling terminal (opposite of -x).
 			 */
-			xkeep = 0;
+			noctty = 0;
 			break;
 		case 'x':
-			xkeep = 1;
+			/*
+			 * posix (hpux) : wide format output.
+			 * bsd : show processes without a controlling
+			 *	terminal.
+			 */
+			if (posix)
+				termwidth = UNLIMITED;
+			else
+				noctty = 1;
+			break;
+		case 'y':
+			/*
+			 * posix (solaris) : omits the f field and replaces
+			 *	the addr field with the rss field.
+			 */
+			yflag = 1;
 			break;
 		case 'Z':
-			parsefmt(Zfmt, 0);
-			Zfmt[0] = '\0';
+			/*
+			 * bsd only : prepend the label field.
+			 */
+			Zflag = 1;
 			break;
 		case '?':
 		default:
-			usage();
+			usage(0);
 		}
+
 	argc -= optind;
 	argv += optind;
-	if (optfatal)
-		exit(1);		/* Error messages already printed. */
-	if (xkeep < 0)			/* Neither -X nor -x was specified. */
-		xkeep = xkeep_implied;
-
-#define	BACKWARD_COMPATIBILITY
-#ifdef	BACKWARD_COMPATIBILITY
-	if (*argv) {
-		nlistf = *argv;
-		if (*++argv)
-			memf = *argv;
-	}
-#endif
-	/*
-	 * Discard setgid privileges if not the running kernel so that bad
-	 * guys can't print interesting stuff from kernel memory.
-	 */
-	if (dropgid) {
-		setgid(getgid());
-		setuid(getuid());
-	}
-
-	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
-	if (kd == 0)
-		errx(1, "%s", errbuf);
 
-	if (!_fmt)
-		parsefmt(dfmt, 0);
+	if (oflag) {
+		if ((posix && Mflag) || (!posix && Zflag))
+			parsefmt(posix ? psx_fmtstr : bsd_fmtstr,
+			    posix ? "M" : "Z");
+		if (Oflag)
+			parsefmt(posix ? psx_fmtstr : bsd_fmtstr, "1");
+		parsefmt(lists[FMT].l.fmt, "");
+		if (Oflag)
+			parsefmt(posix ? psx_fmtstr : bsd_fmtstr, "2");
+	} else {
+		char	flags[FMTLEN];	/* lsuvjnZT\0 | fljcMPyT\0 */
+		char   *cp;
 
-	if (nselectors == 0) {
-		uidlist.l.ptr = malloc(sizeof(uid_t));
-		if (uidlist.l.ptr == NULL)
-			errx(1, "malloc failed");
-		nselectors = 1;
-		uidlist.count = uidlist.maxcount = 1;
-		*uidlist.l.uids = getuid();
+		cp = flags;
+		if (posix) {
+			if (fflag)
+				*cp++ = 'f';
+			if (lflag)
+				*cp++ = 'l';
+		} else {
+			if (lflag)
+				*cp++ = 'l';
+			if (sflag)
+				*cp++ = 's';
+			if (uflag)
+				*cp++ = 'u';
+			if (vflag)
+				*cp++ = 'v';
+		}
+		if (jflag)
+			*cp++ = 'j';
+		if (cp == flags)
+			*cp++ = 'd';
+		if (posix) {
+			if (cflag)
+				*cp++ = 'c';
+			if (Mflag)
+				*cp++ = 'M';
+			if (Pflag)
+				*cp++ = 'P';
+			if (yflag)
+				*cp++ = 'y';
+		} else {
+			if (nflag)
+				*cp++ = 'n';
+			if (Zflag)
+				*cp++ = 'Z';
+		}
+		if (extended)
+			*cp++ = 'X';
+		*cp = '\0';
+		parsefmt(posix ? psx_fmtstr : bsd_fmtstr, flags);
 	}
+}
 
-	/*
-	 * scan requested variables, noting what structures are needed,
-	 * and adjusting header widths as appropriate.
-	 */
-	scanvars();
+static void
+parselst(const char *arg, LIST *list)
+{
+	const char *sep;
+	char *cmd;
+	/* const char *fmt; */
+	/* gid_t gid; */
+	struct group *grp;
+	pid_t pid;
+	/* dev_t tty; */
+	struct stat sb;
+	const char *ttypath;
+	/* uid_t uid; */
+	struct passwd *pwd;
+	size_t len, lo, hi, need;
+	char buf[MAX(MAX(MAXCOMLEN + 1, MAXLOGNAME), SPECNAMELEN + 1)];
+	char pathbuf[SPECNAMELEN + 1];
+	char *endp;
+	int eno;
+	u_long u_tmp;
+	long tmp;
+
+	/* fmt = NULL; gid = 0; tty = 0; uid = 0; */
+	cmd = NULL; grp = NULL; pwd = NULL; pid = 0;
+	sep = fmtsep;
 
 	/*
-	 * Get process list.  If the user requested just one selector-
-	 * option, then kvm_getprocs can be asked to return just those
-	 * processes.  Otherwise, have it return all processes, and
-	 * then this routine will search that full list and select the
-	 * processes which match any of the user's selector-options.
+	 * format string which contains both an equal sign and a comma
+	 * are considered to be comma only separated list. so, both
+	 * -o "pid=process id,ppid=parent pid,command=command line" and
+	 * -o "pid=child ppid=parent command=command" are well parsed,
+	 * unfortunately, mixing "user=user name,pid=pid command" isn't...
 	 */
-	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
-	flag = 0;
-	if (nselectors == 1) {
-		/* XXX - Apparently there's no KERN_PROC_GID flag. */
-		if (pgrplist.count == 1) {
-			what = KERN_PROC_PGRP | showthreads;
-			flag = *pgrplist.l.pids;
-			nselectors = 0;
-		} else if (pidlist.count == 1) {
-			what = KERN_PROC_PID | showthreads;
-			flag = *pidlist.l.pids;
-			nselectors = 0;
-		} else if (ruidlist.count == 1) {
-			what = KERN_PROC_RUID | showthreads;
-			flag = *ruidlist.l.uids;
-			nselectors = 0;
-#if 0
-		/*-
-		 * XXX - KERN_PROC_SESSION causes error in kvm_getprocs?
-		 *	For now, always do sid-matching in this routine.
-		 */
-		} else if (sesslist.count == 1) {
-			what = KERN_PROC_SESSION | showthreads;
-			flag = *sesslist.l.pids;
-			nselectors = 0;
-#endif
-		} else if (ttylist.count == 1) {
-			what = KERN_PROC_TTY | showthreads;
-			flag = *ttylist.l.ttys;
-			nselectors = 0;
-		} else if (uidlist.count == 1) {
-			what = KERN_PROC_UID | showthreads;
-			flag = *uidlist.l.uids;
-			nselectors = 0;
-		} else if (all) {
-			/* No need for this routine to select processes. */
-			nselectors = 0;
-		}
-	}
+	if (list->type == FMT && strchr(arg, '=') && strchr(arg, ','))
+		sep++;			/* comma only, no white space */
 
-	/*
-	 * select procs
-	 */
-	nentries = -1;
-	kp = kvm_getprocs(kd, what, flag, &nentries);
-	if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
-		errx(1, "%s", kvm_geterr(kd));
-	nkept = 0;
-	if (nentries > 0) {
-		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
-			errx(1, "malloc failed");
-		for (i = nentries; --i >= 0; ++kp) {
+	for (arg += strspn(arg, sep);
+	     (len = strcspn(arg, sep)) > 0;
+	     arg += len + strspn(arg + len, sep)) {
+		list->noval = 0;
+		need = 1;
+
+		switch (list->type) {
+		case CMD:
 			/*
-			 * If the user specified multiple selection-criteria,
-			 * then keep any process matched by the inclusive OR
-			 * of all the selection-criteria given.
-			 */
-			if (pidlist.count > 0) {
-				for (elem = 0; elem < pidlist.count; elem++)
-					if (kp->ki_pid == pidlist.l.pids[elem])
-						goto keepit;
+			 * allowed command name must be smaller than MAXCOMLEN.
+			 */
+			if (len > MAXCOMLEN) {
+				warnx("%.*s: %s name too long", (int)len, arg,
+				    list->msg);
+				fatal = 1;
+				continue;
 			}
+			(void)strlcpy(buf, arg, len + 1);
+			cmd = strdup(buf);
+			if (!cmd)
+				errx(1, "malloc failed");
+			break;
+		case FMT:
+	fmt_hack:
+			/* fmt = arg; */
+			need += len + 1;
+			break;
+		case GID:
+		case RUID:
+		case UID:
 			/*
-			 * Note that we had to process pidlist before
-			 * filtering out processes which do not have
-			 * a controlling terminal.
-			 */
-			if (xkeep == 0) {
-				if ((kp->ki_tdev == NODEV ||
-				    (kp->ki_flag & P_CONTROLT) == 0))
-					continue;
+			 * allowed group or user name must be smaller than
+			 * MAXLOGNAME and gid or uid must be a positive integer
+			 * between 0 and GID_MAX or UID_MAX respectively.
+			 */
+			if (len >= MAXLOGNAME) {
+				warnx("%.*s: %s name too long", (int)len, arg,
+				    list->msg);
+				fatal = 1;
+				continue;
 			}
-			if (nselectors == 0)
-				goto keepit;
-			if (gidlist.count > 0) {
-				for (elem = 0; elem < gidlist.count; elem++)
-					if (kp->ki_rgid == gidlist.l.gids[elem])
-						goto keepit;
+			(void)strlcpy(buf, arg, len + 1);
+			errno = 0;
+			u_tmp = strtoul(buf, &endp, 10);
+			eno = errno;
+			if ((list->type == GID &&
+			     !((grp = getgrnam(buf)) != NULL ||
+			       ((endp != buf && *endp == '\0' &&
+			         (gid_t)u_tmp <= GID_MAX) &&
+			        (grp = getgrgid((gid_t)u_tmp)) != NULL))) ||
+			    (list->type != GID &&
+			     !((pwd = getpwnam(buf)) != NULL ||
+			       ((endp != buf && *endp == '\0' &&
+			         (uid_t)u_tmp <= UID_MAX) &&
+			        (pwd = getpwuid((uid_t)u_tmp)) != NULL)))) {
+				if (endp == buf)
+					warnx("%s: no such %s name", buf,
+					    list->msg);
+				else if (eno == ERANGE) {
+					errno = eno;
+					warnx("%s: %s id too large", buf,
+					    list->msg);
+				} else
+					warnx("%s: invalid %s id", buf,
+					    list->msg);
+				fatal = 1;
+				continue;
 			}
-			if (pgrplist.count > 0) {
-				for (elem = 0; elem < pgrplist.count; elem++)
-					if (kp->ki_pgid ==
-					    pgrplist.l.pids[elem])
-						goto keepit;
+			/*
+			if (list->type == GID)
+				gid = grp->gr_gid;
+			else
+				uid = pwd->pw_uid;
+			*/
+			break;
+		case PGID:
+		case PID:
+		case SID:
+			/*
+			 * allowed p*id must be a positive integer
+			 * between 0 and PID_MAX.
+			 */
+			if (len >= sizeof(buf)) {
+				warnx("%.*s: %s too long", (int)len, arg,
+				    list->msg);
+				fatal = 1;
+				continue;
 			}
-			if (ruidlist.count > 0) {
-				for (elem = 0; elem < ruidlist.count; elem++)
-					if (kp->ki_ruid ==
-					    ruidlist.l.uids[elem])
-						goto keepit;
+			(void)strlcpy(buf, arg, len + 1);
+			errno = 0;
+			tmp = strtol(buf, &endp, 10);
+			if (errno == ERANGE) {
+				warnx("%.*s: %s too large", (int)len, arg,
+				    list->msg);
+				fatal = 1;
+				continue;
+			} else if (endp == buf || *endp ||
+				   tmp < 0 || tmp > PID_MAX) {
+				warnx("%.*s: invalid %s", (int)len, arg,
+				    list->msg);
+				fatal = 1;
+				continue;
 			}
-			if (sesslist.count > 0) {
-				for (elem = 0; elem < sesslist.count; elem++)
-					if (kp->ki_sid == sesslist.l.pids[elem])
-						goto keepit;
+			pid = (pid_t)tmp;
+			break;
+		case TTY:
+			/*
+			 * allowed terminal name are :
+			 * co -> /dev/console
+			 * XX -> /dev/ttyXX
+			 * ttyXX -> /dev/ttyXX
+			 * /dev/ttyXX -> /dev/ttyXX
+			 */
+			if ((len + (strlen(_PATH_TTY) * *arg != '/'))
+			    > SPECNAMELEN) {
+				warnx("%.*s: path name too long",
+				    (int)len, arg);
+				fatal = 1;
+				continue;
 			}
-			if (ttylist.count > 0) {
-				for (elem = 0; elem < ttylist.count; elem++)
-					if (kp->ki_tdev == ttylist.l.ttys[elem])
-						goto keepit;
+			(void)strlcpy(buf, arg, len + 1);
+			if (strcmp(buf, "co") == 0)
+				ttypath = _PATH_CONSOLE;
+			else if (*buf != '/') {
+				(void)snprintf(pathbuf,
+				    sizeof(buf), "%s%s",
+				    !strncmp(buf, "tty", 3) ? _PATH_DEV :
+				    _PATH_TTY, buf);
+				    ttypath = pathbuf;
+			} else
+				ttypath = buf;
+			if (stat(ttypath, &sb) == -1) {
+				warn("%s", ttypath);
+				fatal = 1;
+				continue;
 			}
-			if (uidlist.count > 0) {
-				for (elem = 0; elem < uidlist.count; elem++)
-					if (kp->ki_uid == uidlist.l.uids[elem])
-						goto keepit;
+			if (!S_ISCHR(sb.st_mode)) {
+				warnx("%s: not a %s", ttypath, list->msg);
+				fatal = 1;
+				continue;
 			}
-			/*
-			 * This process did not match any of the user's
-			 * selector-options, so skip the process.
-			 */
-			continue;
-
-		keepit:
-			kinfo[nkept].ki_p = kp;
-			if (needuser)
-				saveuser(&kinfo[nkept]);
-			dynsizevars(&kinfo[nkept]);
-			nkept++;
+			/* tty = sb.st_rdev; */
+			break;
+		default:
+			break;
 		}
-	}
 
-	sizevars();
-
-	/*
-	 * print header
-	 */
-	printheader();
-	if (nkept == 0)
-		exit(1);
-
-	/*
-	 * sort proc list
-	 */
-	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
-	/*
-	 * For each process, call each variable output function.
-	 */
-	for (i = lineno = 0; i < nkept; i++) {
-		for (vent = vhead; vent; vent = vent->next) {
-			(vent->var->oproc)(&kinfo[i], vent);
-			if (vent->next != NULL)
-				(void)putchar(' ');
-		}
-		(void)putchar('\n');
-		if (prtheader && lineno++ == prtheader - 4) {
-			(void)putchar('\n');
-			printheader();
-			lineno = 0;
+		lo = rounddown(list->count + need, BUCKETS);
+		hi = roundup2(list->count, BUCKETS);
+		if (lo >= hi) {
+			int n = roundup2(list->count + need, BUCKETS);
+			if ((list->l.ptr = realloc(list->l.ptr, n * list->size))
+			    == NULL)
+				errx(1, "realloc failed");
 		}
-	}
-	free_list(&gidlist);
-	free_list(&pidlist);
-	free_list(&pgrplist);
-	free_list(&ruidlist);
-	free_list(&sesslist);
-	free_list(&ttylist);
-	free_list(&uidlist);
-
-	exit(eval);
-}
-
-static int
-addelem_gid(struct listinfo *inf, const char *elem)
-{
-	struct group *grp;
-	const char *nameorID;
-	char *endp;
-	u_long bigtemp;
-
-	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
-		if (*elem == '\0')
-			warnx("Invalid (zero-length) %s name", inf->lname);
-		else
-			warnx("%s name too long: %s", inf->lname, elem);
-		optfatal = 1;
-		return (0);		/* Do not add this value. */
-	}
-
-	/*
-	 * SUSv3 states that `ps -G grouplist' should match "real-group
-	 * ID numbers", and does not mention group-names.  I do want to
-	 * also support group-names, so this tries for a group-id first,
-	 * and only tries for a name if that doesn't work.  This is the
-	 * opposite order of what is done in addelem_uid(), but in
-	 * practice the order would only matter for group-names which
-	 * are all-numeric.
-	 */
-	grp = NULL;
-	nameorID = "named";
-	errno = 0;
-	bigtemp = strtoul(elem, &endp, 10);
-	if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) {
-		nameorID = "name or ID matches";
-		grp = getgrgid((gid_t)bigtemp);
-	}
-	if (grp == NULL)
-		grp = getgrnam(elem);
-	if (grp == NULL) {
-		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
-		optfatal = 1;
-		return (0);		/* Do not add this value. */
-	}
 
-	if (inf->count >= inf->maxcount)
-		expand_list(inf);
-	inf->l.gids[(inf->count)++] = grp->gr_gid;
-	return (1);
-}
-
-#define	BSD_PID_MAX	99999		/* Copy of PID_MAX from sys/proc.h. */
-static int
-addelem_pid(struct listinfo *inf, const char *elem)
-{
-	char *endp;
-	long tempid;
-
-	if (*elem == '\0')
-		tempid = 0L;
-	else {
-		errno = 0;
-		tempid = strtol(elem, &endp, 10);
-		if (*endp != '\0' || tempid < 0 || elem == endp) {
-			warnx("Invalid %s: %s", inf->lname, elem);
-			errno = ERANGE;
-		} else if (errno != 0 || tempid > BSD_PID_MAX) {
-			warnx("%s too large: %s", inf->lname, elem);
-			errno = ERANGE;
-		}
-		if (errno == ERANGE) {
-			optfatal = 1;
-			return (0);	/* Do not add this value. */
+		switch (list->type) {
+		case CMD:
+			list->l.cmds[list->count++] = cmd;
+			break;
+		case FMT:
+			list->l.fmt[list->count++] = ',';
+			strlcpy(list->l.fmt + list->count, /*fmt*/ arg,
+			    len + 1);
+			list->count += len;
+			break;
+		case GID:
+			list->l.gids[list->count++] = /*gid*/ grp->gr_gid;
+			break;
+		case PGID:
+		case PID:
+		case SID:
+			list->l.pids[list->count++] = pid;
+			break;
+		case TTY:
+			list->l.ttys[list->count++] = /*tty*/ sb.st_rdev;
+			break;
+		case RUID:
+		case UID:
+			list->l.uids[list->count++] = /*uid*/ pwd->pw_uid;
+			break;
+		default:
+			break;
 		}
 	}
 
-	if (inf->count >= inf->maxcount)
-		expand_list(inf);
-	inf->l.pids[(inf->count)++] = tempid;
-	return (1);
-}
-#undef	BSD_PID_MAX
-
-static int
-addelem_tty(struct listinfo *inf, const char *elem)
-{
-	const char *ttypath;
-	struct stat sb;
-	char pathbuf[PATH_MAX];
-
-	if (strcmp(elem, "co") == 0)
-		ttypath = strdup(_PATH_CONSOLE);
-	else if (*elem == '/')
-		ttypath = elem;
-	else {
-		strlcpy(pathbuf, _PATH_TTY, sizeof(pathbuf));
-		strlcat(pathbuf, elem, sizeof(pathbuf));
-		ttypath = pathbuf;
-	}
-
-	if (stat(ttypath, &sb) == -1) {
-		warn("%s", ttypath);
-		optfatal = 1;
-		return (0);		/* Do not add this value. */
-	}
-	if (!S_ISCHR(sb.st_mode)) {
-		warn("%s: Not a terminal", ttypath);
-		optfatal = 1;
-		return (0);		/* Do not add this value. */
+	switch (list->type) {
+	case GID:
+		endgrent();
+		break;
+	case UID:
+	case RUID:
+		endpwent();
+		break;
+	default:
+		break;
 	}
 
-	if (inf->count >= inf->maxcount)
-		expand_list(inf);
-	inf->l.ttys[(inf->count)++] = sb.st_rdev;
-	return (1);
-}
-
-static int
-addelem_uid(struct listinfo *inf, const char *elem)
-{
-	struct passwd *pwd;
-	char *endp;
-	u_long bigtemp;
-
-	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
-		if (*elem == '\0')
-			warnx("Invalid (zero-length) %s name", inf->lname);
-		else
-			warnx("%s name too long: %s", inf->lname, elem);
-		optfatal = 1;
-		return (0);		/* Do not add this value. */
-	}
-
-	pwd = getpwnam(elem);
-	if (pwd == NULL) {
-		errno = 0;
-		bigtemp = strtoul(elem, &endp, 10);
-		if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX)
-			warnx("No %s named '%s'", inf->lname, elem);
-		else {
-			/* The string is all digits, so it might be a userID. */
-			pwd = getpwuid((uid_t)bigtemp);
-			if (pwd == NULL)
-				warnx("No %s name or ID matches '%s'",
-				    inf->lname, elem);
-		}
-	}
-	if (pwd == NULL) {
-		/*
-		 * These used to be treated as minor warnings (and the
-		 * option was simply ignored), but now they are fatal
-		 * errors (and the command will be aborted).
-		 */
-		optfatal = 1;
-		return (0);		/* Do not add this value. */
-	}
-
-	if (inf->count >= inf->maxcount)
-		expand_list(inf);
-	inf->l.uids[(inf->count)++] = pwd->pw_uid;
-	return (1);
-}
-
-static void
-add_list(struct listinfo *inf, const char *argp)
-{
-	const char *savep;
-	char *cp, *endp;
-	int toolong;
-	char elemcopy[PATH_MAX];
-
-	while (*argp != '\0') {
-		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
-			argp++;
-		savep = argp;
-		toolong = 0;
-		cp = elemcopy;
-		if (strchr(T_SEP, *argp) == NULL) {
-			endp = elemcopy + sizeof(elemcopy) - 1;
-			while (*argp != '\0' && cp <= endp &&
-			    strchr(W_SEP T_SEP, *argp) == NULL)
-				*cp++ = *argp++;
-			if (cp > endp)
-				toolong = 1;
-		}
-		if (!toolong) {
-			*cp = '\0';
-#ifndef ADD_PS_LISTRESET
-			/*
-			 * This is how the standard expects lists to
-			 * be handled.
-			 */
-			inf->addelem(inf, elemcopy);
-#else
-			/*-
-			 * This would add a simple non-standard-but-convienent
-			 * feature.
-			 *
-			 * XXX - The first time I tried to add this check,
-			 *	it increased the total size of `ps' by 3940
-			 *	bytes on i386!  That's 12% of the entire
-			 *	program!  The `ps.o' file grew by only about
-			 *	40 bytes, but the final (stripped) executable
-			 *	in /bin/ps grew by 12%.  I have not had time
-			 *	to investigate, so skip the feature for now.
-			 */
-			/*
-			 * We now have a single element.  Add it to the
-			 * list, unless the element is ":".  In that case,
-			 * reset the list so previous entries are ignored.
-			 */
-			if (strcmp(elemcopy, ":") == 0)
-				inf->count = 0;
-			else
-				inf->addelem(inf, elemcopy);
-#endif
-		} else {
-			/*
-			 * The string is too long to copy.  Find the end
-			 * of the string to print out the warning message.
-			 */
-			while (*argp != '\0' && strchr(W_SEP T_SEP,
-			    *argp) == NULL)
-				argp++;
-			warnx("Value too long: %.*s", (int)(argp - savep),
-			    savep);
-			optfatal = 1;
-		}
+	/*
+	 * mark blank only, comma only or empty argument for later check.
+	 */
+	if (!list->count) {
 		/*
-		 * Skip over any number of trailing whitespace characters,
-		 * but only one (at most) trailing element-terminating
-		 * character.
+		 * format string must not be null.
 		 */
-		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
-			argp++;
-		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
-			argp++;
-			/* Catch case where string ended with a comma. */
-			if (*argp == '\0')
-				inf->addelem(inf, argp);
-		}
-	}
-}
-
-static void *
-expand_list(struct listinfo *inf)
-{
-	void *newlist;
-	int newmax;
-
-	newmax = (inf->maxcount + 1) << 1;
-	newlist = realloc(inf->l.ptr, newmax * inf->elemsize);
-	if (newlist == NULL) {
-		free(inf->l.ptr);
-		errx(1, "realloc to %d %ss failed", newmax,
-		    inf->lname);
+		if (list->type == FMT) {
+			need = 1;
+			goto fmt_hack;
+		} else
+			list->noval = 1;
 	}
-	inf->maxcount = newmax;
-	inf->l.ptr = newlist;
-
-	return (newlist);
-}
-
-static void
-free_list(struct listinfo *inf)
-{
-
-	inf->count = inf->elemsize = inf->maxcount = 0;
-	if (inf->l.ptr != NULL)
-		free(inf->l.ptr);
-	inf->addelem = NULL;
-	inf->lname = NULL;
-	inf->l.ptr = NULL;
-}
-
-static void
-init_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
-    const char *lname)
-{
-
-	inf->count = inf->maxcount = 0;
-	inf->elemsize = elemsize;
-	inf->addelem = artn;
-	inf->lname = lname;
-	inf->l.ptr = NULL;
 }
 
 VARENT *
@@ -909,7 +1227,7 @@
 {
 	struct varent *vent;
 
-	for (vent = vhead; vent; vent = vent->next) {
+	STAILQ_FOREACH(vent, &varlist, list) {
 		if (strcmp(vent->var->name, v->name) == 0)
 			return vent;
 	}
@@ -917,12 +1235,12 @@
 }
 
 static void
-scanvars(void)
+scanvars()
 {
-	struct varent *vent;
+	VARENT *vent;
 	VAR *v;
 
-	for (vent = vhead; vent; vent = vent->next) {
+	STAILQ_FOREACH(vent, &varlist, list) {
 		v = vent->var;
 		if (v->flag & DSIZ) {
 			v->dwidth = v->width;
@@ -938,15 +1256,15 @@
 static void
 dynsizevars(KINFO *ki)
 {
-	struct varent *vent;
+	VARENT *vent;
 	VAR *v;
 	int i;
 
-	for (vent = vhead; vent; vent = vent->next) {
+	STAILQ_FOREACH(vent, &varlist, list) {
 		v = vent->var;
 		if (!(v->flag & DSIZ))
 			continue;
-		i = (v->sproc)( ki);
+		i = (v->sproc)(ki);
 		if (v->width < i)
 			v->width = i;
 		if (v->width > v->dwidth)
@@ -957,13 +1275,13 @@
 static void
 sizevars(void)
 {
-	struct varent *vent;
+	VARENT *vent;
 	VAR *v;
 	int i;
 
-	for (vent = vhead; vent; vent = vent->next) {
+	STAILQ_FOREACH(vent, &varlist, list) {
 		v = vent->var;
-		i = strlen(vent->header);
+		i = strlen(v->header);
 		if (v->width < i)
 			v->width = i;
 		totwidth += v->width + 1;	/* +1 for space */
@@ -972,21 +1290,19 @@
 }
 
 static const char *
-fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
-    char *comm, int maxlen)
+format(kvm_t *kd,
+    char **(*fn) (kvm_t *, const struct kinfo_proc *, int),
+    KINFO *ki, const char *comm, int maxlen)
 {
-	const char *s;
-
-	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen);
-	return (s);
+	return (fmt_argv((const char **)(*fn)(kd, ki->ki_p, termwidth),
+	    comm, maxlen));
 }
 
 #define UREADOK(ki)	(forceuread || (ki->ki_p->ki_sflag & PS_INMEM))
 
 static void
-saveuser(KINFO *ki)
+saveuser(kvm_t *kd, KINFO *ki)
 {
-
 	if (ki->ki_p->ki_sflag & PS_INMEM) {
 		/*
 		 * The u-area might be swapped out, and we can't get
@@ -1000,70 +1316,58 @@
 	/*
 	 * save arguments if needed
 	 */
-	if (needcomm && (UREADOK(ki) || (ki->ki_p->ki_args != NULL))) {
-		ki->ki_args = strdup(fmt(kvm_getargv, ki, ki->ki_p->ki_comm,
-		    MAXCOMLEN));
-	} else if (needcomm) {
-		asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
-	} else {
+	if (needcomm) {
+		if (UREADOK(ki) || (ki->ki_p->ki_args != NULL))
+			ki->ki_args = strdup(format(kd, kvm_getargv,
+			    ki, ki->ki_p->ki_comm, MAXCOMLEN));
+		else if (ki->ki_p->ki_flag & P_SYSTEM)
+			(void)asprintf(&ki->ki_args, "[%s]", ki->ki_p->ki_comm);
+		else
+			(void)asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
+		if (!ki->ki_args)
+			errx(1, "malloc failed");
+	} else
 		ki->ki_args = NULL;
-	}
-	if (needenv && UREADOK(ki)) {
-		ki->ki_env = strdup(fmt(kvm_getenvv, ki, (char *)NULL, 0));
-	} else if (needenv) {
-		ki->ki_env = malloc(3);
-		strcpy(ki->ki_env, "()");
-	} else {
+	if (needenv) {
+		if (UREADOK(ki))
+			ki->ki_env = strdup(format(kd, kvm_getenvv,
+			    ki, (char *)NULL, 0));
+		else
+			(void)asprintf(&ki->ki_env, "()");
+		if (!ki->ki_env)
+			errx(1, "malloc failed");
+	} else
 		ki->ki_env = NULL;
-	}
 }
 
 static int
 pscomp(const void *a, const void *b)
 {
 	const KINFO *ka, *kb;
-	double cpua, cpub;
-	segsz_t sizea, sizeb;
+	int i;
+#define VSIZE(k) ((k)->ki_p->ki_dsize + (k)->ki_p->ki_ssize + \
+		  (k)->ki_p->ki_tsize)
 
 	ka = a;
 	kb = b;
 	/* SORTCPU and SORTMEM are sorted in descending order. */
-	if (sortby == SORTCPU) {
-		cpua = getpcpu(ka);
-		cpub = getpcpu(kb);
-		if (cpua < cpub)
-			return (1);
-		if (cpua > cpub)
-			return (-1);
-	}
-	if (sortby == SORTMEM) {
-		sizea = ka->ki_p->ki_tsize + ka->ki_p->ki_dsize +
-		    ka->ki_p->ki_ssize;
-		sizeb = kb->ki_p->ki_tsize + kb->ki_p->ki_dsize +
-		    kb->ki_p->ki_ssize;
-		if (sizea < sizeb)
-			return (1);
-		if (sizea > sizeb)
-			return (-1);
-	}
+	if (sortby == SORTCPU && (i = (int)(getpcpu(kb) - getpcpu(ka))) != 0)
+		return(i);
+	if (sortby == SORTMEM &&
+	    (i = (int)(VSIZE(kb) - VSIZE(ka))) != 0)
+		return(i);
 	/*
 	 * TTY's are sorted in ascending order, except that all NODEV
 	 * processes come before all processes with a device.
 	 */
-	if (ka->ki_p->ki_tdev == NODEV && kb->ki_p->ki_tdev != NODEV)
-		return (-1);
-	if (ka->ki_p->ki_tdev != NODEV && kb->ki_p->ki_tdev == NODEV)
-		return (1);
-	if (ka->ki_p->ki_tdev < kb->ki_p->ki_tdev)
-		return (-1);
-	if (ka->ki_p->ki_tdev > kb->ki_p->ki_tdev)
-		return (1);
+        if (ka->ki_p->ki_tdev == NODEV && kb->ki_p->ki_tdev != NODEV)
+                return(-1);
+        if (ka->ki_p->ki_tdev != NODEV && kb->ki_p->ki_tdev == NODEV)
+                return(1);
+	if ((i = (int)(ka->ki_p->ki_tdev - kb->ki_p->ki_tdev)) != 0)
+		return(i);
 	/* PID's are sorted in ascending order. */
-	if (ka->ki_p->ki_pid < kb->ki_p->ki_pid)
-		return (-1);
-	if (ka->ki_p->ki_pid > kb->ki_p->ki_pid)
-		return (1);
-	return (0);
+	return((int)(ka->ki_p->ki_pid - kb->ki_p->ki_pid));
 }
 
 /*
@@ -1096,8 +1400,10 @@
 	/*
 	 * options begin with '-'
 	 */
-	if (*s != '-')
+	if (*s != '-') {
+		posix = 0;
 		*ns++ = '-';	/* add option flag */
+	}
 	/*
 	 * gaze to end of argv[1]
 	 */
@@ -1138,14 +1444,28 @@
 }
 
 static void
-usage(void)
+usage(char c)
 {
-#define	SINGLE_OPTS	"[-aC" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
+#if defined(LAZY_PS)
+	static char	optf[] = "f";
+#else
+	static char	optf[] = "";
+#endif
 
-	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
-	    "usage: ps " SINGLE_OPTS " [-G gid[,gid]] [-O|o fmt]",
-	    "          [-p pid[,pid]] [-t tty[,tty]] [-U user[,user]]",
-	    "          [-M core] [-N system]",
-	    "       ps [-L]");
+	if (c)
+		warnx("illegal option -- %c", c);
+	if (posix)
+		(void)fprintf(stderr, "\
+usage: ps [-AacdefjLlMmPQqTxy] [-C cmdlist] [-G grouplist] [-g pgrplist]\n\
+          [-O|-o fmt] [-p pidlist] [-s sidlist] [-t ttylist] [-U userlist]\n\
+	  [-u userlist] [-n system [-n core]]\n\
+       ps [-o help]\n\
+");
+	else
+		(void)fprintf(stderr, "\
+usage: ps [-AaCce%sgHhjlmnQqrSsTuvwxXZ] [-G grouplist] [-O|-o fmtlist]\n\
+          [-p pidlist] [-t ttylist] [-U userlist] [-M core] [-N system]\n\
+       ps [-L]\n\
+", optf);
 	exit(1);
 }
Index: ps.h
===================================================================
RCS file: /home/ncvs/src/bin/ps/ps.h,v
retrieving revision 1.17
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.17 ps.h
--- ps.h	6 Apr 2004 20:06:49 -0000	1.17
+++ ps.h	17 Apr 2004 21:06:16 -0000
@@ -27,10 +27,20 @@
  * SUCH DAMAGE.
  *
  *	@(#)ps.h	8.1 (Berkeley) 5/31/93
- * $FreeBSD$
+ * $FreeBSD: src/bin/ps/ps.h,v 1.7 1999/08/27 23:14:52 peter Exp $
  */
 
+#include <stddef.h>
+#include <sys/queue.h>
+
+#define LTIMELEN	12
+#define STIMELEN	9
+#define UIDLEN		5
+#define USERLEN		(MAXLOGNAME - 1)
 #define	UNLIMITED	0	/* unlimited terminal width */
+
+#define KOFF(x)		offsetof(struct kinfo_proc, x)
+
 enum type { CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG, KPTR, PGTOK };
 
 typedef struct kinfo {
@@ -41,9 +51,10 @@
 } KINFO;
 
 /* Variables. */
+typedef STAILQ_HEAD(varlist, varent) VARLIST;
+
 typedef struct varent {
-	const char *header;
-	struct varent *next;
+	STAILQ_ENTRY(varent) list;
 	struct var *var;
 } VARENT;
 
Index: sys/kern/kern_proc.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/kern_proc.c,v
retrieving revision 1.202
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.202 kern_proc.c
--- sys/kern/kern_proc.c	5 Apr 2004 21:03:34 -0000	1.202
+++ sys/kern/kern_proc.c	17 Apr 2004 19:27:08 -0000
@@ -647,6 +647,7 @@
 	mtx_unlock(&ktrace_mtx);
 #endif
 	kp->ki_fd = p->p_fd;
+	kp->ki_cmask = p->p_fd ? p->p_fd->fd_cmask : CMASK;
 	kp->ki_vmspace = p->p_vmspace;
 	if (p->p_ucred) {
 		kp->ki_uid = p->p_ucred->cr_uid;
@@ -671,6 +672,7 @@
 	    p->p_state != PRS_ZOMBIE &&
 	    p->p_vmspace != NULL) {
 		struct vmspace *vm = p->p_vmspace;
+		struct rlimit rsslim;
 
 		kp->ki_size = vm->vm_map.size;
 		kp->ki_rssize = vmspace_resident_count(vm); /*XXX*/
@@ -686,15 +688,16 @@
 		kp->ki_tsize = vm->vm_tsize;
 		kp->ki_dsize = vm->vm_dsize;
 		kp->ki_ssize = vm->vm_ssize;
+		/* from sys/vm/vm_pageout.c */
+		lim_rlimit(p, RLIMIT_RSS, &rsslim);
+		kp->ki_maxrss = qmin(rsslim.rlim_cur, rsslim.rlim_max);
 	}
 	if ((p->p_sflag & PS_INMEM) && p->p_stats) {
 		kp->ki_start = p->p_stats->p_start;
 		timevaladd(&kp->ki_start, &boottime);
 		kp->ki_rusage = p->p_stats->p_ru;
-		kp->ki_childtime.tv_sec = p->p_stats->p_cru.ru_utime.tv_sec +
-		    p->p_stats->p_cru.ru_stime.tv_sec;
-		kp->ki_childtime.tv_usec = p->p_stats->p_cru.ru_utime.tv_usec +
-		    p->p_stats->p_cru.ru_stime.tv_usec;
+		kp->ki_childstime = p->p_stats->p_cru.ru_stime;
+		kp->ki_childutime = p->p_stats->p_cru.ru_utime;
 	}
 	if (p->p_state != PRS_ZOMBIE) {
 #if 0
@@ -802,6 +805,7 @@
 		strlcpy(kp->ki_comm, p->p_comm, sizeof(kp->ki_comm));
 		strlcpy(kp->ki_ocomm, p->p_comm, sizeof(kp->ki_ocomm));
 	}
+	kp->ki_cursig = p->p_sig;
 	kp->ki_siglist = p->p_siglist;
         SIGSETOR(kp->ki_siglist, td->td_siglist);
 	kp->ki_sigmask = td->td_sigmask;
Index: sys/sys/user.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/user.h,v
retrieving revision 1.55
diff -u -I$Id.*$ -I$.+BSD.*$ -r1.55 user.h
--- sys/sys/user.h	7 Apr 2004 04:19:50 -0000	1.55
+++ sys/sys/user.h	15 Apr 2004 22:52:24 -0000
@@ -134,7 +134,7 @@
 	u_int	ki_swtime;	 	/* Time swapped in or out */
 	u_int64_t ki_runtime;		/* Real time in microsec */
 	struct	timeval ki_start;	/* starting time */
-	struct	timeval ki_childtime;	/* time used by process children */
+	struct	timeval ki_childstime;	/*system time used by process children*/
 	long	ki_flag;		/* P_* flags */
 	long	ki_kiflag;		/* KI_* flags (below) */
 	int	ki_traceflag;		/* Kernel trace points */
@@ -156,7 +156,11 @@
 	long	ki_tdflags;		/* XXXKSE kthread flag */
 	struct	pcb *ki_pcb;		/* kernel virtual addr of pcb */
 	void	*ki_kstack;		/* kernel virtual addr of stack */
-	long	ki_spare[22];		/* spare constants */
+	u_short	ki_cmask;		/* mask for file creation */
+	int	ki_cursig;		/* signals pending to this process */
+	rlim_t	ki_maxrss;		/* maximum resident set size */
+	struct	timeval ki_childutime;	/* user time used by process children */
+	long	ki_spare[16+(sizeof(long)!=sizeof(int))*2]; /* spare constants */
 };
 void fill_kinfo_proc(struct proc *, struct kinfo_proc *);
 
>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20040420020525.GA66658>