Subject: sorting ps output
To: None <tech-userlevel@netbsd.org>
From: David Laight <david@l8s.co.uk>
List: tech-userlevel
Date: 03/04/2003 17:25:12
I would like to add some code to allow 'ps' output to be sorted on
(almost) any field.  The code is easy, but there are no obvious
option letters left :-(

The obvious one 's' was only used recently, and isn't ideal for
its use anyway (listing all lwps on a process).

Would it cause significant grief if that were changed to something else?

Then we can have:
$ ps -stime
to sort by 'time' (etc), sharing some code with -o (etc).

Code below implements it - but is using -z ('cos it was unused).

	David

Index: extern.h
===================================================================
RCS file: /cvsroot/src/bin/ps/extern.h,v
retrieving revision 1.23
diff -u -p -r1.23 extern.h
--- extern.h	2003/03/01 05:41:56	1.23
+++ extern.h	2003/03/04 17:14:19
@@ -48,6 +48,7 @@ extern uid_t myuid;
 extern kvm_t *kd;
 extern VAR var[];
 extern VARENT *vhead;
+extern VARENT *sorthead;
 
 __BEGIN_DECLS
 void	 command __P((void *, VARENT *, int));
@@ -69,12 +70,14 @@ void	 maxrss __P((void *, VARENT *, int)
 void	 nlisterr __P((struct nlist *));
 void	 p_rssize __P((void *, VARENT *, int));
 void	 pagein __P((void *, VARENT *, int));
-void	 parsefmt __P((char *));
+void	 parsefmt __P((const char *));
+void	 parsesort __P((const char *));
 void	 pcpu __P((void *, VARENT *, int));
 void	 pmem __P((void *, VARENT *, int));
 void	 pnice __P((void *, VARENT *, int));
 void	 pri __P((void *, VARENT *, int));
 void	 printheader __P((void));
+void	 putimeval __P((void *, VARENT *, int));
 void	 pvar __P((void *, VARENT *, int));
 void	 rgname __P((void *, VARENT *, int));
 void	 rssize __P((void *, VARENT *, int));
Index: keyword.c
===================================================================
RCS file: /cvsroot/src/bin/ps/keyword.c,v
retrieving revision 1.30
diff -u -p -r1.30 keyword.c
--- keyword.c	2003/03/01 05:41:56	1.30
+++ keyword.c	2003/03/04 17:14:22
@@ -61,142 +61,144 @@ __RCSID("$NetBSD: keyword.c,v 1.30 2003/
 
 #include "ps.h"
 
-static VAR *findvar __P((char *));
-static int  vcmp __P((const void *, const void *));
+static VAR *findvar(const char *);
+static int  vcmp(const void *, const void *);
 
-#ifdef NOTINUSE
-	{"stime", "STIME", NULL, 0, cputime},	/* XXX add stime, utime ... */
-	{"utime", "UTIME", NULL, 0, cputime},	/* ... display to cputime() */
-	{"idrss", "IDRSS", NULL, 0, pvar, 0, POFF(p_uru_idrss), ULONG, "d"},
-	{"isrss", "ISRSS", NULL, 0, pvar, 0, POFF(p_uru_isrss), ULONG, "d"},
-	{"ixrss", "IXRSS", NULL, 0, pvar, 0, POFF(p_uru_ixrss), ULONG, "d"},
-#endif
-
 /* Compute offset in common structures. */
 #define	POFF(x)	offsetof(struct kinfo_proc2, x)
 #define	LOFF(x)	offsetof(struct kinfo_lwp, x)
 
 #define	UIDFMT	"u"
-#define	UID(n1, n2, fn, off) \
-	{ n1, n2, NULL, 0, fn, 0, off, UINT32, UIDFMT }
-#define	GID(n1, n2, fn, off)	UID(n1, n2, fn, off)
+#define	UID(n1, n2, off) \
+	{ n1, n2, NULL, 0, pvar, POFF(off), UINT32, UIDFMT }
+#define	GID(n1, n2, off)	UID(n1, n2, off)
 
 #define	PIDFMT	"d"
-#define	PID(n1, n2, fn, off) \
-	{ n1, n2, NULL, 0, fn, 0, off, INT32, PIDFMT }
+#define	PID(n1, n2, off) \
+	{ n1, n2, NULL, 0, pvar, POFF(off), INT32, PIDFMT }
 
+#define	LVAR(n1, n2, fl, off, type, fmt) \
+	{ n1, n2, NULL, (fl) | LWP, pvar, LOFF(off), type, fmt }
+#define	PVAR(n1, n2, fl, off, type, fmt) \
+	{ n1, n2, NULL, (fl) | 0, pvar, POFF(off), type, fmt }
+#define	PUVAR(n1, n2, fl, off, type, fmt) \
+	{ n1, n2, NULL, (fl) | UAREA, pvar, POFF(off), type, fmt }
+
+/* NB: table must be sorted, in vi use:
+ *	:/^VAR/,/end_sort/! sort -t\" +1
+ * breaking long lines just makes the sort harder
+ */
 VAR var[] = {
-	{"%cpu", "%CPU", NULL, 0, pcpu},
+	{"%cpu", "%CPU", NULL, 0, pcpu, 0, PCPU},
 	{"%mem", "%MEM", NULL, 0, pmem},
-	{"acflag", "ACFLG", NULL, 0, pvar, 0, POFF(p_acflag), USHORT, "x"},
+	PVAR("acflag", "ACFLG", 0, p_acflag, USHORT, "x"),
 	{"acflg", "", "acflag"},
 	{"blocked", "", "sigmask"},
 	{"caught", "", "sigcatch"},
 	{"command", "COMMAND", NULL, COMM|LJUST, command},
-	{"cpu", "CPU", NULL, 0, pvar, 0, POFF(p_estcpu), UINT, "u"},
+	PVAR("cpu", "CPU", 0, p_estcpu, UINT, "u"),
 	{"cputime", "", "time"},
-	GID("egid", "EGID", pvar, POFF(p_gid)),
+	{"ctime", "CTIME", NULL, 0, putimeval, POFF(p_uctime_sec), TIMEVAL},
+	GID("egid", "EGID", p_gid),
 	{"egroup", "EGROUP", NULL, LJUST, gname},
-	UID("euid", "EUID", pvar, POFF(p_uid)),
+	UID("euid", "EUID", p_uid),
 	{"euser", "EUSER", NULL, LJUST, uname},
-	{"f", "F", NULL, 0, pvar, 0, POFF(p_flag), INT, "x"},
+	PVAR("f", "F", 0, p_flag, INT, "x"),
 	{"flags", "", "f"},
-	GID("gid", "GID", pvar, POFF(p_gid)),
+	GID("gid", "GID", p_gid),
 	{"group", "GROUP", NULL, LJUST, gname},
 	{"groupnames", "GROUPNAMES", NULL, LJUST, groupnames},
 	{"groups", "GROUPS", NULL, LJUST, groups},
-	{"holdcnt", "HOLDCNT", NULL, LWP, pvar, 0, LOFF(l_holdcnt), INT, "d"},
+	LVAR("holdcnt", "HOLDCNT", 0, l_holdcnt, INT, "d"),
+	PUVAR("idrss", "IDRSS", 0, p_uru_idrss, UINT64, "llu"),
 	{"ignored", "", "sigignore"},
-	{"inblk", "INBLK", NULL, 0, pvar, 0, POFF(p_uru_inblock), UINT64, "llu"},
+	PUVAR("inblk", "INBLK", 0, p_uru_inblock, UINT64, "llu"),
 	{"inblock", "", "inblk"},
-	{"jobc", "JOBC", NULL, 0, pvar, 0, POFF(p_jobc), SHORT, "d"},
-	{"ktrace", "KTRACE", NULL, 0, pvar, 0, POFF(p_traceflag), INT, "x"},
-	/* XXX */
-	{"ktracep", "KTRACEP", NULL, 0, pvar, 0, POFF(p_tracep), KPTR, "llx"},
-	{"lid", "LID", NULL, LWP, pvar, 0, LOFF(l_lid), ULONG, "d"},
+	PUVAR("isrss", "ISRSS", 0, p_uru_isrss, UINT64, "lld"),
+	PUVAR("ixrss", "IXRSS", 0, p_uru_ixrss, UINT64, "lld"),
+	PVAR("jobc", "JOBC", 0, p_jobc, SHORT, "d"),
+	PVAR("ktrace", "KTRACE", 0, p_traceflag, INT, "x"),
+/*XXX*/	PVAR("ktracep", "KTRACEP", 0, p_tracep, KPTR, "llx"),
+	LVAR("lid", "LID", 0, l_lid, ULONG, "d"),
 	{"lim", "LIM", NULL, 0, maxrss},
 	{"login", "LOGIN", NULL, LJUST, logname},
 	{"logname", "", "login"},
 	{"lstart", "STARTED", NULL, LJUST, lstarted},
 	{"lstate", "STAT", NULL, LJUST|LWP, lstate},
-	{"majflt", "MAJFLT", NULL, 0, pvar, 0, POFF(p_uru_majflt), UINT64, "llu"},
-	{"minflt", "MINFLT", NULL, 0, pvar, 0, POFF(p_uru_minflt), UINT64, "llu"},
-	{"msgrcv", "MSGRCV", NULL, 0, pvar, 0, POFF(p_uru_msgrcv), UINT64, "llu"},
-	{"msgsnd", "MSGSND", NULL, 0, pvar, 0, POFF(p_uru_msgsnd), UINT64, "llu"},
+	PUVAR("majflt", "MAJFLT", 0, p_uru_majflt, UINT64, "llu"),
+	PUVAR("maxrss", "MAXRSS", 0, p_uru_maxrss, UINT64, "llu"),
+	PUVAR("minflt", "MINFLT", 0, p_uru_minflt, UINT64, "llu"),
+	PUVAR("msgrcv", "MSGRCV", 0, p_uru_msgrcv, UINT64, "llu"),
+	PUVAR("msgsnd", "MSGSND", 0, p_uru_msgsnd, UINT64, "llu"),
 	{"ni", "", "nice"},
 	{"nice", "NI", NULL, 0, pnice},
-	{"nivcsw", "NIVCSW", NULL, 0, pvar, 0, POFF(p_uru_nivcsw), UINT64, "llu"},
-	{"nlwp", "NLWP", NULL, 0, pvar, 0, POFF(p_nlwps), UINT64, "lld"},
+	PUVAR("nivcsw", "NIVCSW", 0, p_uru_nivcsw, UINT64, "llu"),
+	PVAR("nlwp", "NLWP", 0, p_nlwps, UINT64, "lld"),
 	{"nsignals", "", "nsigs"},
-	{"nsigs", "NSIGS", NULL, 0, pvar, 0, POFF(p_uru_nsignals), UINT64, "llu"},
-	{"nswap", "NSWAP", NULL, 0, pvar, 0, POFF(p_uru_nswap), UINT64, "llu"},
-	{"nvcsw", "NVCSW", NULL, 0, pvar, 0, POFF(p_uru_nvcsw), UINT64, "llu"},
-	/* XXX */
-	{"nwchan", "WCHAN", NULL, LWP, pvar, 0, LOFF(l_wchan), KPTR, "llx"},
-	{"oublk", "OUBLK", NULL, 0, pvar, 0, POFF(p_uru_oublock), UINT64, "llu"},
+	PUVAR("nsigs", "NSIGS", 0, p_uru_nsignals, UINT64, "llu"),
+	PUVAR("nswap", "NSWAP", 0, p_uru_nswap, UINT64, "llu"),
+	PUVAR("nvcsw", "NVCSW", 0, p_uru_nvcsw, UINT64, "llu"),
+/*XXX*/	LVAR("nwchan", "WCHAN", 0, l_wchan, KPTR, "llx"),
+	PUVAR("oublk", "OUBLK", 0, p_uru_oublock, UINT64, "llu"),
 	{"oublock", "", "oublk"},
-	/* XXX */
-	{"p_ru", "P_RU", NULL, 0, pvar, 0, POFF(p_ru), KPTR, "llx"},
-	/* XXX */
-	{"paddr", "PADDR", NULL, 0, pvar, 0, POFF(p_paddr), KPTR, "llx"},
-	{"pagein", "PAGEIN", NULL, 0, pagein},
+/*XXX*/	PVAR("p_ru", "P_RU", 0, p_ru, KPTR, "llx"),
+/*XXX*/	PVAR("paddr", "PADDR", 0, p_paddr, KPTR, "llx"),
+	PUVAR("pagein", "PAGEIN", 0, p_uru_majflt, UINT64, "llu"),
 	{"pcpu", "", "%cpu"},
 	{"pending", "", "sig"},
-	PID("pgid", "PGID", pvar, POFF(p__pgid)),
-	PID("pid", "PID", pvar, POFF(p_pid)),
+	PID("pgid", "PGID", p__pgid),
+	PID("pid", "PID", p_pid),
 	{"pmem", "", "%mem"},
-	PID("ppid", "PPID", pvar, POFF(p_ppid)),
+	PID("ppid", "PPID", p_ppid),
 	{"pri", "PRI", NULL, LWP, pri},
-	{"re", "RE", NULL, INF127|LWP, pvar, 0, LOFF(l_swtime), UINT, "u"},
-	GID("rgid", "RGID", pvar, POFF(p_rgid)),
+	LVAR("re", "RE", INF127, l_swtime, UINT, "u"),
+	GID("rgid", "RGID", p_rgid),
 	{"rgroup", "RGROUP", NULL, LJUST, rgname},
-	/* XXX */
-	{"rlink", "RLINK", NULL, LWP, pvar, 0, LOFF(l_back), KPTR, "llx"},
-	{"rlwp", "RLWP", NULL, 0, pvar, 0, POFF(p_nrlwps), UINT64, "lld"},
+/*XXX*/	LVAR("rlink", "RLINK", 0, l_back, KPTR, "llx"),
+	PVAR("rlwp", "RLWP", 0, p_nrlwps, UINT64, "lld"),
 	{"rss", "RSS", NULL, 0, p_rssize},
 	{"rssize", "", "rsz"},
 	{"rsz", "RSZ", NULL, 0, rssize},
-	UID("ruid", "RUID", pvar, POFF(p_ruid)),
+	UID("ruid", "RUID", p_ruid),
 	{"ruser", "RUSER", NULL, LJUST, runame},
-	{"sess", "SESS", NULL, 0, pvar, 0, POFF(p_sess), KPTR24, "llx"},
-	PID("sid", "SID", pvar, POFF(p_sid)),
-	{"sig", "PENDING",
-	    NULL, 0, pvar, 0, POFF(p_siglist), SIGLIST, "s"},
-	{"sigcatch", "CAUGHT",
-	    NULL, 0, pvar, 0, POFF(p_sigcatch), SIGLIST, "s"},
-	{"sigignore", "IGNORED",
-	    NULL, 0, pvar, 0, POFF(p_sigignore), SIGLIST, "s"},
-	{"sigmask", "BLOCKED",
-	    NULL, 0, pvar, 0, POFF(p_sigmask), SIGLIST, "s"},
-	{"sl", "SL", NULL, INF127|LWP, pvar, 0, LOFF(l_slptime), UINT, "u"},
+	PVAR("sess", "SESS", 0, p_sess, KPTR24, "llx"),
+	PID("sid", "SID", p_sid),
+	PVAR("sig", "PENDING", 0, p_siglist, SIGLIST, "s"),
+	PVAR("sigcatch", "CAUGHT", 0, p_sigcatch, SIGLIST, "s"),
+	PVAR("sigignore", "IGNORED", 0, p_sigignore, SIGLIST, "s"),
+	PVAR("sigmask", "BLOCKED", 0, p_sigmask, SIGLIST, "s"),
+	LVAR("sl", "SL", INF127, l_slptime, UINT, "u"),
 	{"start", "STARTED", NULL, 0, started},
 	{"stat", "", "state"},
 	{"state", "STAT", NULL, LJUST, state},
-	GID("svgid", "SVGID", pvar, POFF(p_gid)),
+	{"stime", "STIME", NULL, 0, putimeval, POFF(p_ustime_sec), TIMEVAL},
+	GID("svgid", "SVGID", p_svgid),
 	{"svgroup", "SVGROUP", NULL, LJUST, svgname},
-	UID("svuid", "SVUID", pvar, POFF(p_uid)),
+	UID("svuid", "SVUID", p_svuid),
 	{"svuser", "SVUSER", NULL, LJUST, svuname},
-	{"tdev", "TDEV", NULL, 0, tdev},
-	{"time", "TIME", NULL, 0, cputime},
-	PID("tpgid", "TGPID", pvar, POFF(p_tpgid)),
-	{"tsess", "TSESS", NULL, 0, pvar, 0, POFF(p_tsess), KPTR, "llx"},
+	{"tdev", "TDEV", NULL, 0, tdev, POFF(p_tdev), UINT32},
+	{"time", "TIME", NULL, 0, cputime, 0, CPUTIME},
+	PID("tpgid", "TGPID", p_tpgid),
+	PVAR("tsess", "TSESS", 0, p_tsess, KPTR, "llx"),
 	{"tsiz", "TSIZ", NULL, 0, tsize},
 	{"tt", "TT", NULL, LJUST, tname},
 	{"tty", "TTY", NULL, LJUST, longtname},
 	{"ucomm", "UCOMM", NULL, LJUST, ucomm},
-	UID("uid", "UID", pvar, POFF(p_uid)),
-	{"upr", "UPR", NULL, LWP, pvar, 0, LOFF(l_usrpri), UCHAR, "u"},
+	UID("uid", "UID", p_uid),
+	LVAR("upr", "UPR", 0, l_usrpri, UCHAR, "u"),
 	{"user", "USER", NULL, LJUST, uname},
 	{"usrpri", "", "upr"},
+	{"utime", "UTIME", NULL, 0, putimeval, POFF(p_uutime_sec), TIMEVAL},
 	{"vsize", "", "vsz"},
-	{"vsz", "VSZ", NULL, 0, vsize},
+	{"vsz", "VSZ", NULL, 0, vsize, 0, VSIZE},
 	{"wchan", "WCHAN", NULL, LJUST|LWP, wchan},
-	{"xstat", "XSTAT", NULL, 0, pvar, 0, POFF(p_xstat), USHORT, "x"},
+	PVAR("xstat", "XSTAT", 0, p_xstat, USHORT, "x"),
+/* "zzzz" end_sort */
 	{""},
 };
 
 void
-showkey()
+showkey(void)
 {
 	VAR *v;
 	int i;
@@ -216,11 +218,14 @@ showkey()
 	(void) printf("\n");
 }
 
-void
-parsefmt(p)
-	char *p;
+static void
+parsevarlist(const char *pp, struct varent **head, struct varent **tail)
 {
-	static struct varent *vtail;
+	char *p;
+
+	/* dup to avoid zapping arguments, can't free because it
+	   might contain a header. */
+	p = strdup(pp);
 
 #define	FMTSEP	" \t,\n"
 	while (p && *p) {
@@ -236,54 +241,58 @@ parsefmt(p)
 			err(1, NULL);
 		vent->var = v;
 		vent->next = NULL;
-		if (vhead == NULL)
-			vhead = vtail = vent;
-		else {
-			vtail->next = vent;
-			vtail = vent;
-		}
+		if (*head == NULL)
+			*head = vent;
+		else
+			(*tail)->next = vent;
+		*tail = vent;
 	}
-	if (!vhead)
+	if (!*head)
 		errx(1, "no valid keywords");
 }
 
+void
+parsefmt(const char *p)
+{
+	static struct varent *vtail;
+
+	parsevarlist(p, &vhead, &vtail);
+}
+
+void
+parsesort(const char *p)
+{
+	static struct varent *sorttail;
+
+	parsevarlist(p, &sorthead, &sorttail);
+}
+
 static VAR *
-findvar(p)
-	char *p;
+findvar(const char *p)
 {
-	VAR *v, key;
+	VAR *v;
 	char *hp;
 
-	key.name = p;
-
 	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);
-		return ((VAR *)NULL);
-	}
+	v = bsearch(p, var, sizeof(var)/sizeof(VAR) - 1, sizeof(VAR), vcmp);
+	if (v && v->alias)
+		v = findvar(v->alias);
 	if (!v) {
 		warnx("%s: keyword not found", p);
 		eval = 1;
-		return ((VAR *)NULL);
+		return NULL;
 	}
-	if (hp)
+
+	if (v && hp)
 		v->header = hp;
-	return (v);
+	return v;
 }
 
 static int
-vcmp(a, b)
-        const void *a, *b;
+vcmp(const void *a, const void *b)
 {
-        return (strcmp(((VAR *)a)->name, ((VAR *)b)->name));
+        return strcmp(a, ((VAR *)b)->name);
 }
Index: print.c
===================================================================
RCS file: /cvsroot/src/bin/ps/print.c,v
retrieving revision 1.77
diff -u -p -r1.77 print.c
--- print.c	2003/03/01 05:41:56	1.77
+++ print.c	2003/03/04 17:14:33
@@ -112,8 +112,22 @@ static void  intprintorsetwidth __P((VAR
 static void  strprintorsetwidth __P((VAR *, const char *, int));
 
 #define	min(a,b)	((a) <= (b) ? (a) : (b))
-#define	max(a,b)	((a) >= (b) ? (a) : (b))
 
+static int
+iwidth(u_quad_t v)
+{
+	u_quad_t nlim, lim;
+	int w = 1;
+
+	for (lim = 10; v >= lim; lim = nlim) {
+		nlim = lim * 10;
+		w++;
+		if (nlim < lim)
+			break;
+	}
+	return w;
+}
+
 static char *
 cmdpart(arg0)
 	char *arg0;
@@ -232,13 +246,13 @@ intprintorsetwidth(v, val, mode)
 
 	if (mode == WIDTHMODE) {
 		if (val < 0 && val < v->longestn) {
-			fmtlen = (int)log10((double)-val) + 2;
 			v->longestn = val;
+			fmtlen = iwidth(-val) + 1;
 			if (fmtlen > v->width)
 				v->width = fmtlen;
 		} else if (val > 0 && val > v->longestp) {
-			fmtlen = (int)log10((double)val) + 1;
 			v->longestp = val;
+			fmtlen = iwidth(val);
 			if (fmtlen > v->width)
 				v->width = fmtlen;
 		}
@@ -900,14 +914,14 @@ wchan(arg, ve, mode)
 	if (l->l_wchan) {
 		if (l->l_wmesg) {
 			strprintorsetwidth(v, l->l_wmesg, mode);
-			v->width = min(v->width, WMESGLEN);
+			v->width = min(v->width, KI_WMESGLEN);
 		} else {
 			(void)asprintf(&buf, "%-*llx", v->width,
 			    (long long)l->l_wchan);
 			if (buf == NULL)
 				err(1, "%s", "");
 			strprintorsetwidth(v, buf, mode);
-			v->width = min(v->width, WMESGLEN);
+			v->width = min(v->width, KI_WMESGLEN);
 			free(buf);
 		}
 	} else {
@@ -1001,16 +1015,17 @@ cputime(arg, ve, mode)
 	if (mode == WIDTHMODE) {
 		/*
 		 * Ugg, this is the only field where a value of 0 longer
-		 * than the column title, and log10(0) isn't good enough.
+		 * than the column title.
 		 * Use SECSPERMIN, because secs is divided by that when
-		 * passed to log10().
+		 * passed to iwidth().
 		 */
-		if (secs == 0 && v->longestp == 0)
+		if (secs == 0)
 			secs = SECSPERMIN;
+
 		if (secs > v->longestp) {
-			/* "+6" for the "%02ld.%02ld" in the printf() below */
-			fmtlen = (int)log10((double)secs / SECSPERMIN) + 1 + 6;
 			v->longestp = secs;
+			/* "+6" for the ":%02ld.%02ld" in the printf() below */
+			fmtlen = iwidth(secs / SECSPERMIN) + 6;
 			if (fmtlen > v->width)
 				v->width = fmtlen;
 		}
@@ -1147,7 +1162,7 @@ printval(bp, v, mode)
 {
 	static char ofmt[32] = "%";
 	int width, vok, fmtlen;
-	char *fcp, *cp, *obuf;
+	char *fcp, *cp;
 	enum type type;
 	long long val;
 	unsigned long long uval;
@@ -1221,28 +1236,28 @@ printval(bp, v, mode)
 			vok = VUNSIGN;
 			break;
 
+		case SIGLIST:
 		default:
 			/* nothing... */;
 		}
 		switch (vok) {
 		case VSIGN:
-			if (val < 0  && val < v->longestn) {
-				fmtlen = (int)log10((double)-val) + 2;
+			if (val < 0 && val < v->longestn) {
 				v->longestn = val;
+				fmtlen = iwidth(-val) + 1;
 				if (fmtlen > v->width)
 					v->width = fmtlen;
 			} else if (val > 0 && val > v->longestp) {
-				fmtlen = (int)log10((double)val) + 1;
 				v->longestp = val;
+				fmtlen = iwidth(val);
 				if (fmtlen > v->width)
 					v->width = fmtlen;
 			}
 			return;
 		case VUNSIGN:
 			if (uval > v->longestu) {
-				fmtlen = (int)log10((double)uval) + 1;
 				v->longestu = uval;
-				v->width = fmtlen;
+				v->width = iwidth(uval);
 			}
 			return;
 		case VPTR:
@@ -1332,15 +1347,15 @@ printval(bp, v, mode)
 				    s->__bits[(SIGSETSIZE - 1) - i]);
 
 			/* Skip leading zeroes */
-			for (i = 0; buf[i]; i++)
-				if (buf[i] != '0')
-					break;
+			for (i = 0; buf[i] == '0'; i++)
+				continue;
 
 			if (buf[i] == '\0')
 				i--;
-			(void)asprintf(&obuf, ofmt, width, &buf[i]);
+			strprintorsetwidth(v, buf + i, mode);
+#undef SIGSETSIZE
 		}
-		break;
+		return;
 	case INT64:
 		(void)printf(ofmt, width, (long long)GET(int64_t));
 		return;
@@ -1350,20 +1365,6 @@ printval(bp, v, mode)
 	default:
 		errx(1, "unknown type %d", v->type);
 	}
-	if (obuf == NULL)
-		err(1, "%s", "");
-	if (mode == WIDTHMODE) {
-		/* Skip leading spaces. */
-		cp = strrchr(obuf, ' ');
-		if (cp == NULL)
-			cp = obuf;
-		else
-			cp++;	/* skip last space */
-	}
-	else
-		cp = obuf;
-	strprintorsetwidth(v, cp, mode);
-	free(obuf);
 #undef GET
 #undef CHK_INF127
 }
@@ -1377,5 +1378,65 @@ pvar(arg, ve, mode)
 	VAR *v;
 
 	v = ve->var;
+	if (v->flag & UAREA && !((struct kinfo_proc2 *)arg)->p_uvalid) {
+		if (mode == PRINTMODE)
+			(void)printf("%*s", v->width, "-");
+		return;
+	}
+
 	printval((char *)arg + v->off, v, mode);
+}
+
+void
+putimeval(arg, ve, mode)
+	void *arg;
+	VARENT *ve;
+	int mode;
+{
+	VAR *v = ve->var;
+	struct kinfo_proc2 *k = arg;
+	ulong secs = *(uint32_t *)((char *)arg + v->off);
+	ulong usec = *(uint32_t *)((char *)arg + v->off + sizeof (uint32_t));
+	int fmtlen;
+
+	if (!k->p_uvalid) {
+		if (mode == PRINTMODE)
+			(void)printf("%*s", v->width, "-");
+		return;
+	}
+
+	if (mode == WIDTHMODE) {
+		if (!secs)
+			/* zero doesn't give correct width... */
+			secs = 1;
+		if (secs > v->longestu) {
+			v->longestu = secs;
+			if (secs <= 999)
+				/* sss.ssssss */
+				fmtlen = iwidth(secs) + 6 + 1;
+			else
+				/* hh:mm:ss.ss */
+				fmtlen = iwidth((secs+1)/3600)
+					+ 2 + 1 + 2 + 1 + 2 + 1;
+			if (fmtlen > v->width)
+				v->width = fmtlen;
+		}
+		return;
+	}
+
+	if (secs < 999)
+		(void)printf( "%*lu.%.6lu", v->width - 6 - 1, secs, usec);
+	else {
+		uint h, m;
+		usec += 5000;
+		if (usec >= 1000000) {
+			usec -= 1000000;
+			secs++;
+		}
+		m = secs / 60u;
+		secs -= m * 60u;
+		h = m / 60u;
+		m -= h * 60u;
+		(void)printf( "%*u:%.2u:%.2lu.%.2lu", v->width - 9, h, m, secs, usec / 10000u );
+	}
 }
Index: ps.c
===================================================================
RCS file: /cvsroot/src/bin/ps/ps.c,v
retrieving revision 1.48
diff -u -p -r1.48 ps.c
--- ps.c	2003/01/18 10:52:17	1.48
+++ ps.c	2003/03/04 17:14:38
@@ -93,6 +93,7 @@ __RCSID("$NetBSD: ps.c,v 1.48 2003/01/18
 #include <sys/ioctl.h>
 #include <sys/sysctl.h>
 
+#include <stddef.h>
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
@@ -113,11 +114,11 @@ __RCSID("$NetBSD: ps.c,v 1.48 2003/01/18
  * ARGOPTS must contain all option characters that take arguments
  * (except for 't'!) - it is used in kludge_oldps_options()
  */
-#define	GETOPTSTR	"acCeghjLlM:mN:O:o:p:rSsTt:U:uvW:wx"
-#define	ARGOPTS		"MNOopUW"
+#define	GETOPTSTR	"acCeghjLlM:mN:O:o:p:rSsTt:U:uvW:wxz:"
+#define	ARGOPTS		"MNOopUWz"
 
 struct kinfo_proc2 *kinfo;
-struct varent *vhead, *vtail;
+struct varent *vhead, *sorthead;
 
 int	eval;			/* exit value */
 int	rawcpu;			/* -C */
@@ -128,8 +129,6 @@ int	totwidth;		/* calculated width of re
 int	needcomm, needenv, commandonly;
 uid_t	myuid;
 
-enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
-
 static struct kinfo_lwp
 		*pick_representative_lwp __P((struct kinfo_proc2 *, 
 		    struct kinfo_lwp *, int));
@@ -221,7 +220,7 @@ main(argc, argv)
 			memf = optarg;
 			break;
 		case 'm':
-			sortby = SORTMEM;
+			parsesort("vsz");
 			break;
 		case 'N':
 			nlistf = optarg;
@@ -243,7 +242,7 @@ main(argc, argv)
 			xflg = 1;
 			break;
 		case 'r':
-			sortby = SORTCPU;
+			parsesort("%cpu");
 			break;
 		case 'S':
 			sumrusage = 1;
@@ -308,13 +307,13 @@ main(argc, argv)
 			break;
 		case 'u':
 			parsefmt(ufmt);
-			sortby = SORTCPU;
+			parsesort("%cpu");
 			fmt = 1;
 			ufmt[0] = '\0';
 			break;
 		case 'v':
 			parsefmt(vfmt);
-			sortby = SORTMEM;
+			parsesort("vsz");
 			fmt = 1;
 			vfmt[0] = '\0';
 			break;
@@ -331,6 +330,9 @@ main(argc, argv)
 		case 'x':
 			xflg = 1;
 			break;
+		case 'z':
+			parsesort(optarg);
+			break;
 		case '?':
 		default:
 			usage();
@@ -361,6 +363,8 @@ main(argc, argv)
 
 	if (!fmt)
 		parsefmt(dfmt);
+	/* add default sort criteria */
+	parsesort("tdev,pid");
 
 	/*
 	 * scan requested variables, noting what structures are needed.
@@ -553,24 +557,107 @@ scanvars()
 }
 
 static int
-pscomp(a, b)
-	const void *a, *b;
+pscomp(const void *a, const void *b)
 {
 	struct kinfo_proc2 *ka = (struct kinfo_proc2 *)a;
 	struct kinfo_proc2 *kb = (struct kinfo_proc2 *)b;
 
 	int i;
-#define VSIZE(k) (k->p_vm_dsize + k->p_vm_ssize + k->p_vm_tsize)
+	int64_t i64;
+	VAR *v;
+	struct varent *ve;
+	sigset_t *sa, *sb;
+
+#define V_SIZE(k) (k->p_vm_dsize + k->p_vm_ssize + k->p_vm_tsize)
+#define RDIFF_N(t, n) \
+	if (((t *)((char *)ka + v->off))[n] > ((t *)((char *)kb + v->off))[n]) \
+		return 1; \
+	if (((t *)((char *)ka + v->off))[n] < ((t *)((char *)kb + v->off))[n]) \
+		return -1;
+
+#define RDIFF(type) RDIFF_N(type, 0); continue
+
+	for (ve = sorthead; ve != NULL; ve = ve->next) {
+		v = ve->var;
+		if (v->flag & LWP)
+			/* LWP structure not available (yet) */
+			continue;
+		/* Sort on pvar() fields, + a few others */
+		switch (v->type) {
+		case CHAR:
+			RDIFF(char);
+		case UCHAR:
+			RDIFF(u_char);
+		case SHORT:
+			RDIFF(short);
+		case USHORT:
+			RDIFF(ushort);
+		case INT:
+			RDIFF(int);
+		case UINT:
+			RDIFF(uint);
+		case LONG:
+			RDIFF(long);
+		case ULONG:
+			RDIFF(ulong);
+		case INT32:
+			RDIFF(int32_t);
+		case UINT32:
+			RDIFF(uint32_t);
+		case SIGLIST:
+			sa = (void *)((char *)a + v->off);
+			sb = (void *)((char *)b + v->off);
+			i = 0;
+			do {
+				if (sa->__bits[i] > sb->__bits[i])
+					return 1;
+				if (sa->__bits[i] < sb->__bits[i])
+					return -1;
+				i++;
+			} while (i < sizeof sa->__bits / sizeof sa->__bits[0]);
+			continue;
+		case INT64:
+			RDIFF(int64_t);
+		case KPTR:
+		case KPTR24:
+		case UINT64:
+			RDIFF(uint64_t);
+		case TIMEVAL:
+			/* compare xxx_sec then xxx_usec */
+			RDIFF_N(uint32_t, 0);
+			RDIFF_N(uint32_t, 1);
+			continue;
+		case CPUTIME:
+			i64 = ka->p_rtime_sec * 1000000 + ka->p_rtime_usec;
+			i64 -= kb->p_rtime_sec * 1000000 + kb->p_rtime_usec;
+			if (sumrusage) {
+				i64 += ka->p_uctime_sec * 1000000
+				    + ka->p_uctime_usec;
+				i64 -= kb->p_uctime_sec * 1000000
+				    + kb->p_uctime_usec;
+			}
+			if (i64 != 0)
+				return i64 > 0 ? 1 : -1;
+			continue;
+		case PCPU:
+			i = getpcpu(kb) - getpcpu(ka);
+			if (i != 0)
+				return i;
+			continue;
+		case VSIZE:
+			i = V_SIZE(kb) - V_SIZE(ka);
+			if (i != 0)
+				return i;
+			continue;
+
+		default:
+			/* Ignore everything else */
+			break;
+		}
+	}
+	return 0;
 
-	if (sortby == SORTCPU)
-		return (getpcpu(kb) - getpcpu(ka));
-	if (sortby == SORTMEM)
-		return (VSIZE(kb) - VSIZE(ka));
-	i =  ka->p_tdev - kb->p_tdev;
-
-	if (i == 0)
-		i = ka->p_pid - kb->p_pid;
-	return (i);
+#undef VSIZE
 }
 
 /*
Index: ps.h
===================================================================
RCS file: /cvsroot/src/bin/ps/ps.h,v
retrieving revision 1.19
diff -u -p -r1.19 ps.h
--- ps.h	2003/01/18 10:52:18	1.19
+++ ps.h	2003/03/04 17:14:38
@@ -41,8 +41,10 @@
 #define WIDTHMODE	1	/* determine width of column */
 
 enum type {
-	CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG, KPTR, KPTR24,
-	INT32, UINT32, SIGLIST, INT64, UINT64
+	UNSPECIFIED,
+	CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG,
+	KPTR, KPTR24, INT32, UINT32, SIGLIST, INT64, UINT64,
+	TIMEVAL, CPUTIME, PCPU, VSIZE
 };
 
 /* Variables. */
@@ -59,10 +61,10 @@ typedef struct var {
 #define	LJUST	0x02		/* left adjust on output (trailing blanks) */
 #define	INF127	0x04		/* 127 = infinity: if > 127, print 127. */
 #define LWP	0x08		/* dispatch to kinfo_lwp routine */
+#define UAREA	0x10		/* need to check p_uvalid */
 	u_int	flag;
 				/* output routine */
 	void	(*oproc) __P((void *, struct varent *, int));
-	short	width;		/* printing width */
 	/*
 	 * The following (optional) elements are hooks for passing information
 	 * to the generic output routine: pvar (that which prints simple
@@ -71,7 +73,9 @@ typedef struct var {
 	int	off;		/* offset in structure */
 	enum	type type;	/* type of element */
 	char	*fmt;		/* printf format */
+
 	/* current longest element */
+	int	width;		/* printing width */
 	int64_t	longestp;	/* longest positive signed value */
 	int64_t	longestn;	/* longest negative signed value */
 	u_int64_t longestu;	/* longest unsigned value */
-- 
David Laight: david@l8s.co.uk