xref: /netbsd-src/bin/ps/ps.c (revision 9dc307c463b91e3a555c37d1a194d70b298911c5)
1*9dc307c4Schristos /*	$NetBSD: ps.c,v 1.97 2021/09/14 22:01:17 christos Exp $	*/
249f0ad86Scgd 
3d530ee00Ssimonb /*
4b35fbd45Sapb  * Copyright (c) 2000-2008 The NetBSD Foundation, Inc.
5d530ee00Ssimonb  * All rights reserved.
6d530ee00Ssimonb  *
7d530ee00Ssimonb  * This code is derived from software contributed to The NetBSD Foundation
8d530ee00Ssimonb  * by Simon Burge.
9d530ee00Ssimonb  *
10d530ee00Ssimonb  * Redistribution and use in source and binary forms, with or without
11d530ee00Ssimonb  * modification, are permitted provided that the following conditions
12d530ee00Ssimonb  * are met:
13d530ee00Ssimonb  * 1. Redistributions of source code must retain the above copyright
14d530ee00Ssimonb  *    notice, this list of conditions and the following disclaimer.
15d530ee00Ssimonb  * 2. Redistributions in binary form must reproduce the above copyright
16d530ee00Ssimonb  *    notice, this list of conditions and the following disclaimer in the
17d530ee00Ssimonb  *    documentation and/or other materials provided with the distribution.
18d530ee00Ssimonb  *
19d530ee00Ssimonb  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20d530ee00Ssimonb  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21d530ee00Ssimonb  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22d530ee00Ssimonb  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23d530ee00Ssimonb  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24d530ee00Ssimonb  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25d530ee00Ssimonb  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26d530ee00Ssimonb  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27d530ee00Ssimonb  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28d530ee00Ssimonb  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29d530ee00Ssimonb  * POSSIBILITY OF SUCH DAMAGE.
30d530ee00Ssimonb  */
31d530ee00Ssimonb 
32d530ee00Ssimonb /*
334d1457ceScgd  * Copyright (c) 1990, 1993, 1994
344d1457ceScgd  *	The Regents of the University of California.  All rights reserved.
3561f28255Scgd  *
3661f28255Scgd  * Redistribution and use in source and binary forms, with or without
3761f28255Scgd  * modification, are permitted provided that the following conditions
3861f28255Scgd  * are met:
3961f28255Scgd  * 1. Redistributions of source code must retain the above copyright
4061f28255Scgd  *    notice, this list of conditions and the following disclaimer.
4161f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
4261f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
4361f28255Scgd  *    documentation and/or other materials provided with the distribution.
44b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
4561f28255Scgd  *    may be used to endorse or promote products derived from this software
4661f28255Scgd  *    without specific prior written permission.
4761f28255Scgd  *
4861f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
4961f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5061f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5161f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5261f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5361f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5461f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5561f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5661f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5761f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5861f28255Scgd  * SUCH DAMAGE.
5961f28255Scgd  */
6061f28255Scgd 
6178295c8bSchristos #include <sys/cdefs.h>
6261f28255Scgd #ifndef lint
632fe2731dSlukem __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
642fe2731dSlukem  The Regents of the University of California.  All rights reserved.");
6561f28255Scgd #endif /* not lint */
6661f28255Scgd 
6761f28255Scgd #ifndef lint
6849f0ad86Scgd #if 0
694d1457ceScgd static char sccsid[] = "@(#)ps.c	8.4 (Berkeley) 4/2/94";
7049f0ad86Scgd #else
71*9dc307c4Schristos __RCSID("$NetBSD: ps.c,v 1.97 2021/09/14 22:01:17 christos Exp $");
7249f0ad86Scgd #endif
7361f28255Scgd #endif /* not lint */
7461f28255Scgd 
7561f28255Scgd #include <sys/param.h>
7661f28255Scgd #include <sys/time.h>
7761f28255Scgd #include <sys/resource.h>
783fdac2b8Sthorpej #include <sys/lwp.h>
7961f28255Scgd #include <sys/proc.h>
8061f28255Scgd #include <sys/stat.h>
8161f28255Scgd #include <sys/ioctl.h>
824d1457ceScgd #include <sys/sysctl.h>
834d1457ceScgd 
84036e2657Sdsl #include <stddef.h>
854d1457ceScgd #include <ctype.h>
864d1457ceScgd #include <err.h>
8761f28255Scgd #include <errno.h>
884d1457ceScgd #include <fcntl.h>
89e31566ddScjep #include <grp.h>
904d1457ceScgd #include <kvm.h>
91bf18a93aSpk #include <limits.h>
929655f5c2Schristos #include <locale.h>
93cc1877baSrin #include <math.h>
944d1457ceScgd #include <nlist.h>
954d1457ceScgd #include <paths.h>
96975ed852Smycroft #include <pwd.h>
9761f28255Scgd #include <stdio.h>
9861f28255Scgd #include <stdlib.h>
9961f28255Scgd #include <string.h>
1004d1457ceScgd #include <unistd.h>
101*9dc307c4Schristos #include <util.h>
1024d1457ceScgd 
10361f28255Scgd #include "ps.h"
10461f28255Scgd 
105d530ee00Ssimonb /*
106d530ee00Ssimonb  * ARGOPTS must contain all option characters that take arguments
107d530ee00Ssimonb  * (except for 't'!) - it is used in kludge_oldps_options()
108d530ee00Ssimonb  */
109e31566ddScjep #define	GETOPTSTR	"aAcCdegG:hjk:LlM:mN:O:o:p:rSsTt:U:uvW:wx"
110e31566ddScjep #define	ARGOPTS		"GkMNOopUW"
111d530ee00Ssimonb 
112d9463bc1Sapb struct varlist displaylist = SIMPLEQ_HEAD_INITIALIZER(displaylist);
113d9463bc1Sapb struct varlist sortlist = SIMPLEQ_HEAD_INITIALIZER(sortlist);
11461f28255Scgd 
11561f28255Scgd int	eval;			/* exit value */
11661f28255Scgd int	sumrusage;		/* -S */
11761f28255Scgd int	termwidth;		/* width of screen (0 == infinity) */
11861f28255Scgd int	totwidth;		/* calculated width of requested variables */
11961f28255Scgd 
1203fdac2b8Sthorpej int	needcomm, needenv, commandonly;
1215801c247Ssimonb uid_t	myuid;
12261f28255Scgd 
1233fdac2b8Sthorpej static struct kinfo_lwp
12453474900Ssimonb 		*pick_representative_lwp(struct kinfo_proc2 *,
12553474900Ssimonb 		    struct kinfo_lwp *, int);
126fd521aefSsimonb static struct kinfo_proc2
12753474900Ssimonb 		*getkinfo_kvm(kvm_t *, int, int, int *);
128cc1877baSrin static struct pinfo
129cc1877baSrin 		*setpinfo(struct kinfo_proc2 *, int, int, int);
13053474900Ssimonb static char	*kludge_oldps_options(char *);
13153474900Ssimonb static int	 pscomp(const void *, const void *);
13253474900Ssimonb static void	 scanvars(void);
1335bb1ddccSjoerg __dead static void	 usage(void);
13410ed3a58Schristos static int	 parsenum(const char *, const char *);
1355a6cfab1Schristos static void	 descendant_sort(struct pinfo *, int);
13661f28255Scgd 
13761f28255Scgd char dfmt[] = "pid tt state time command";
13861f28255Scgd char jfmt[] = "user pid ppid pgid sess jobc state tt time command";
13961f28255Scgd char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command";
140abb32017Ssimonb char sfmt[] = "uid pid ppid cpu lid nlwp pri nice vsz rss wchan lstate tt "
141067314c6Smlelstv 		"ltime command";
14261f28255Scgd char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command";
1434d1457ceScgd char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command";
14461f28255Scgd 
145ea03f830Syamt const char *default_fmt = dfmt;
146ea03f830Syamt 
147d9463bc1Sapb struct varent *Opos = NULL; /* -O flag inserts after this point */
148d9463bc1Sapb 
1494d1457ceScgd kvm_t *kd;
1504d1457ceScgd 
151763e5791Sjoerg static long long
ttyname2dev(const char * ttname,int * xflg,int * what)152763e5791Sjoerg ttyname2dev(const char *ttname, int *xflg, int *what)
153763e5791Sjoerg {
154763e5791Sjoerg 	struct stat sb;
155763e5791Sjoerg 	const char *ttypath;
156763e5791Sjoerg 	char pathbuf[MAXPATHLEN];
157763e5791Sjoerg 
158763e5791Sjoerg 	ttypath = NULL;
159763e5791Sjoerg 	if (strcmp(ttname, "?") == 0) {
160763e5791Sjoerg 		*xflg = 1;
161763e5791Sjoerg 		return KERN_PROC_TTY_NODEV;
162763e5791Sjoerg 	}
163763e5791Sjoerg 	if (strcmp(ttname, "-") == 0)
164763e5791Sjoerg 		return KERN_PROC_TTY_REVOKE;
165763e5791Sjoerg 
166763e5791Sjoerg 	if (strcmp(ttname, "co") == 0)
167763e5791Sjoerg 		ttypath = _PATH_CONSOLE;
168763e5791Sjoerg 	else if (strncmp(ttname, "pts/", 4) == 0 ||
169763e5791Sjoerg 		strncmp(ttname, "tty", 3) == 0) {
170763e5791Sjoerg 		(void)snprintf(pathbuf,
171763e5791Sjoerg 		    sizeof(pathbuf), "%s%s", _PATH_DEV, ttname);
172763e5791Sjoerg 		ttypath = pathbuf;
173763e5791Sjoerg 	} else if (*ttname != '/') {
174763e5791Sjoerg 		(void)snprintf(pathbuf,
175763e5791Sjoerg 		    sizeof(pathbuf), "%s%s", _PATH_TTY, ttname);
176763e5791Sjoerg 		ttypath = pathbuf;
177763e5791Sjoerg 	} else
178763e5791Sjoerg 		ttypath = ttname;
179763e5791Sjoerg 	*what = KERN_PROC_TTY;
180763e5791Sjoerg 	if (stat(ttypath, &sb) == -1) {
181f86ebf39Sdholland 		devmajor_t pts;
182f86ebf39Sdholland 		int serrno;
183763e5791Sjoerg 
184f86ebf39Sdholland 		serrno = errno;
185f86ebf39Sdholland 		pts = getdevmajor("pts", S_IFCHR);
186763e5791Sjoerg 		if (pts != NODEVMAJOR && strncmp(ttname, "pts/", 4) == 0) {
187763e5791Sjoerg 			int ptsminor = atoi(ttname + 4);
188763e5791Sjoerg 
189763e5791Sjoerg 			snprintf(pathbuf, sizeof(pathbuf), "pts/%d", ptsminor);
190763e5791Sjoerg 			if (strcmp(pathbuf, ttname) == 0 && ptsminor >= 0)
191763e5791Sjoerg 				return makedev(pts, ptsminor);
192763e5791Sjoerg 		}
193f86ebf39Sdholland 		errno = serrno;
194b02b35c9Schristos 		err(EXIT_FAILURE, "%s", ttypath);
195763e5791Sjoerg 	}
196763e5791Sjoerg 	if (!S_ISCHR(sb.st_mode))
197b02b35c9Schristos 		errx(EXIT_FAILURE, "%s: not a terminal", ttypath);
198763e5791Sjoerg 	return sb.st_rdev;
199763e5791Sjoerg }
200763e5791Sjoerg 
2014d1457ceScgd int
main(int argc,char * argv[])20253474900Ssimonb main(int argc, char *argv[])
20361f28255Scgd {
204cc1877baSrin 	struct kinfo_proc2 *kinfo;
205cc1877baSrin 	struct pinfo *pinfo;
2064d1457ceScgd 	struct varent *vent;
20761f28255Scgd 	struct winsize ws;
2083fdac2b8Sthorpej 	struct kinfo_lwp *kl, *l;
2095a6cfab1Schristos 	int ch, i, j, fmt, lineno, descendancy, nentries, nlwps;
210a5c6617dSchristos 	long long flag;
211cc1877baSrin 	int calc_pcpu, prtheader, wflag, what, xflg, rawcpu, showlwps;
212bf18a93aSpk 	char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX];
21326cc4401Skim 	char *ttname;
21461f28255Scgd 
2159655f5c2Schristos 	setprogname(argv[0]);
2169655f5c2Schristos 	(void)setlocale(LC_ALL, "");
2179655f5c2Schristos 
218fd521aefSsimonb 	if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
219fd521aefSsimonb 	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
220fd521aefSsimonb 	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
22161f28255Scgd 	     ws.ws_col == 0)
22261f28255Scgd 		termwidth = 79;
22361f28255Scgd 	else
22461f28255Scgd 		termwidth = ws.ws_col - 1;
22561f28255Scgd 
22661f28255Scgd 	if (argc > 1)
22761f28255Scgd 		argv[1] = kludge_oldps_options(argv[1]);
22861f28255Scgd 
2290e68b604Skamil 	descendancy = fmt = prtheader = wflag = xflg = rawcpu = showlwps = 0;
230975ed852Smycroft 	what = KERN_PROC_UID;
2315801c247Ssimonb 	flag = myuid = getuid();
23261f28255Scgd 	memf = nlistf = swapf = NULL;
233a468ec49Sdholland 
234d530ee00Ssimonb 	while ((ch = getopt(argc, argv, GETOPTSTR)) != -1)
23561f28255Scgd 		switch((char)ch) {
236b35fbd45Sapb 		case 'A':
237b35fbd45Sapb 			/* "-A" shows all processes, like "-ax" */
238b35fbd45Sapb 			xflg = 1;
239b35fbd45Sapb 			/*FALLTHROUGH*/
24061f28255Scgd 		case 'a':
241975ed852Smycroft 			what = KERN_PROC_ALL;
242975ed852Smycroft 			flag = 0;
24361f28255Scgd 			break;
24407cdfa9cSmycroft 		case 'c':
24507cdfa9cSmycroft 			commandonly = 1;
24607cdfa9cSmycroft 			break;
2475a6cfab1Schristos 		case 'd':
2485a6cfab1Schristos 			descendancy = 1;
2495a6cfab1Schristos 			break;
2504d1457ceScgd 		case 'e':			/* XXX set ufmt */
2514d1457ceScgd 			needenv = 1;
2524d1457ceScgd 			break;
25361f28255Scgd 		case 'C':
25461f28255Scgd 			rawcpu = 1;
25561f28255Scgd 			break;
25661f28255Scgd 		case 'g':
25761f28255Scgd 			break;			/* no-op */
258e31566ddScjep 		case 'G':
259e31566ddScjep 			if (*optarg != '\0') {
260e31566ddScjep 				struct group *gr;
261e31566ddScjep 
262e31566ddScjep 				what = KERN_PROC_GID;
263e31566ddScjep 				gr = getgrnam(optarg);
264e31566ddScjep 				if (gr == NULL) {
2657c3f3593Schristos 					flag = parsenum(optarg, "group id");
266e31566ddScjep 				} else
267e31566ddScjep 					flag = gr->gr_gid;
268e31566ddScjep 			}
269e31566ddScjep 			break;
270e31566ddScjep 
27161f28255Scgd 		case 'h':
27261f28255Scgd 			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
27361f28255Scgd 			break;
27461f28255Scgd 		case 'j':
27561f28255Scgd 			parsefmt(jfmt);
2763541700dSmycroft 			fmt = 1;
27761f28255Scgd 			jfmt[0] = '\0';
27861f28255Scgd 			break;
279036e2657Sdsl 		case 'k':
280036e2657Sdsl 			parsesort(optarg);
281036e2657Sdsl 			break;
28205e0706aSbgrayson 		case 'K':
283fc257046Sjdolecek 			break;			/* no-op - was dontuseprocfs */
28461f28255Scgd 		case 'L':
28561f28255Scgd 			showkey();
286078edf9cSrin 			return 0;
28761f28255Scgd 		case 'l':
28861f28255Scgd 			parsefmt(lfmt);
2893541700dSmycroft 			fmt = 1;
29061f28255Scgd 			lfmt[0] = '\0';
29161f28255Scgd 			break;
29261f28255Scgd 		case 'M':
29361f28255Scgd 			memf = optarg;
29461f28255Scgd 			break;
29561f28255Scgd 		case 'm':
296036e2657Sdsl 			parsesort("vsz");
29761f28255Scgd 			break;
29861f28255Scgd 		case 'N':
29961f28255Scgd 			nlistf = optarg;
30061f28255Scgd 			break;
30161f28255Scgd 		case 'O':
302d9463bc1Sapb 			/*
303d9463bc1Sapb 			 * If this is not the first -O option, insert
304d9463bc1Sapb 			 * just after the previous one.
305d9463bc1Sapb 			 *
306d9463bc1Sapb 			 * If there is no format yet, start with the default
307d9463bc1Sapb 			 * format, and insert after the pid column.
308d9463bc1Sapb 			 *
309d9463bc1Sapb 			 * If there is already a format, insert after
310d9463bc1Sapb 			 * the pid column, or at the end if there's no
311d9463bc1Sapb 			 * pid column.
312d9463bc1Sapb 			 */
313d9463bc1Sapb 			if (!Opos) {
314d9463bc1Sapb 				if (!fmt)
315ea03f830Syamt 					parsefmt(default_fmt);
316d9463bc1Sapb 				Opos = varlist_find(&displaylist, "pid");
317d9463bc1Sapb 			}
318d9463bc1Sapb 			parsefmt_insert(optarg, &Opos);
3193541700dSmycroft 			fmt = 1;
32061f28255Scgd 			break;
32161f28255Scgd 		case 'o':
32261f28255Scgd 			parsefmt(optarg);
3233541700dSmycroft 			fmt = 1;
32461f28255Scgd 			break;
32561f28255Scgd 		case 'p':
326975ed852Smycroft 			what = KERN_PROC_PID;
32710ed3a58Schristos 			flag = parsenum(optarg, "process id");
32861f28255Scgd 			xflg = 1;
32961f28255Scgd 			break;
33061f28255Scgd 		case 'r':
331036e2657Sdsl 			parsesort("%cpu");
33261f28255Scgd 			break;
33361f28255Scgd 		case 'S':
33461f28255Scgd 			sumrusage = 1;
33561f28255Scgd 			break;
3363fdac2b8Sthorpej 		case 's':
3373fdac2b8Sthorpej 			/* -L was already taken... */
3383fdac2b8Sthorpej 			showlwps = 1;
339ea03f830Syamt 			default_fmt = sfmt;
3403fdac2b8Sthorpej 			break;
34161f28255Scgd 		case 'T':
3420e2f9ea9Smycroft 			if ((ttname = ttyname(STDIN_FILENO)) == NULL)
343b02b35c9Schristos 				errx(EXIT_FAILURE, "stdin: not a terminal");
344763e5791Sjoerg 			flag = ttyname2dev(ttname, &xflg, &what);
34561f28255Scgd 			break;
346763e5791Sjoerg 		case 't':
347763e5791Sjoerg 			flag = ttyname2dev(optarg, &xflg, &what);
348763e5791Sjoerg 			break;
349975ed852Smycroft 		case 'U':
350975ed852Smycroft 			if (*optarg != '\0') {
351975ed852Smycroft 				struct passwd *pw;
352975ed852Smycroft 
353975ed852Smycroft 				what = KERN_PROC_UID;
354975ed852Smycroft 				pw = getpwnam(optarg);
355975ed852Smycroft 				if (pw == NULL) {
3567c3f3593Schristos 					flag = parsenum(optarg, "user id");
357975ed852Smycroft 				} else
358975ed852Smycroft 					flag = pw->pw_uid;
359975ed852Smycroft 			}
360975ed852Smycroft 			break;
36161f28255Scgd 		case 'u':
36261f28255Scgd 			parsefmt(ufmt);
363036e2657Sdsl 			parsesort("%cpu");
3643541700dSmycroft 			fmt = 1;
36561f28255Scgd 			ufmt[0] = '\0';
36661f28255Scgd 			break;
36761f28255Scgd 		case 'v':
36861f28255Scgd 			parsefmt(vfmt);
369036e2657Sdsl 			parsesort("vsz");
3703541700dSmycroft 			fmt = 1;
37161f28255Scgd 			vfmt[0] = '\0';
37261f28255Scgd 			break;
37361f28255Scgd 		case 'W':
37461f28255Scgd 			swapf = optarg;
37561f28255Scgd 			break;
37661f28255Scgd 		case 'w':
3774434a6edScgd 			if (wflag)
37861f28255Scgd 				termwidth = UNLIMITED;
3794434a6edScgd 			else if (termwidth < 131)
3804434a6edScgd 				termwidth = 131;
3814434a6edScgd 			wflag++;
38261f28255Scgd 			break;
38361f28255Scgd 		case 'x':
38461f28255Scgd 			xflg = 1;
38561f28255Scgd 			break;
38661f28255Scgd 		case '?':
38761f28255Scgd 		default:
38861f28255Scgd 			usage();
38961f28255Scgd 		}
39061f28255Scgd 	argc -= optind;
39161f28255Scgd 	argv += optind;
39261f28255Scgd 
39361f28255Scgd #define	BACKWARD_COMPATIBILITY
39461f28255Scgd #ifdef	BACKWARD_COMPATIBILITY
39561f28255Scgd 	if (*argv) {
39661f28255Scgd 		nlistf = *argv;
39761f28255Scgd 		if (*++argv) {
39861f28255Scgd 			memf = *argv;
39961f28255Scgd 			if (*++argv)
40061f28255Scgd 				swapf = *argv;
40161f28255Scgd 		}
40261f28255Scgd 	}
40361f28255Scgd #endif
4044d1457ceScgd 
405036e2657Sdsl 	if (memf == NULL) {
406036e2657Sdsl 		kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
407fd521aefSsimonb 		donlist_sysctl();
408fd521aefSsimonb 	} else
4094d1457ceScgd 		kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf);
410fd521aefSsimonb 
411078edf9cSrin 	if (kd == NULL)
412b02b35c9Schristos 		errx(EXIT_FAILURE, "%s", errbuf);
41361f28255Scgd 
4143541700dSmycroft 	if (!fmt)
415ea03f830Syamt 		parsefmt(default_fmt);
41661f28255Scgd 
417036e2657Sdsl 	/* Add default sort criteria */
418036e2657Sdsl 	parsesort("tdev,pid");
419cc1877baSrin 	calc_pcpu = 0;
420d9463bc1Sapb 	SIMPLEQ_FOREACH(vent, &sortlist, next) {
421036e2657Sdsl 		if (vent->var->flag & LWP || vent->var->type == UNSPECIFIED)
4227da746abSchristos 			warnx("Cannot sort on %s, sort key ignored",
423036e2657Sdsl 				vent->var->name);
424cc1877baSrin 		if (vent->var->type == PCPU)
425cc1877baSrin 			calc_pcpu = 1;
426cc1877baSrin 	}
427cc1877baSrin 	if (!calc_pcpu)
428cc1877baSrin 		SIMPLEQ_FOREACH(vent, &displaylist, next)
429cc1877baSrin 			if (vent->var->type == PCPU) {
430cc1877baSrin 				calc_pcpu = 1;
431cc1877baSrin 				break;
432036e2657Sdsl 			}
433036e2657Sdsl 
43461f28255Scgd 	/*
435a98dd470Ssimonb 	 * scan requested variables, noting what structures are needed.
43661f28255Scgd 	 */
43761f28255Scgd 	scanvars();
438f848d2ecSjdolecek 
43961f28255Scgd 	/*
44061f28255Scgd 	 * select procs
44161f28255Scgd 	 */
4423fdac2b8Sthorpej 	if (!(kinfo = getkinfo_kvm(kd, what, flag, &nentries)))
443ea7b2892Schristos 		errx(EXIT_FAILURE, "%s", kvm_geterr(kd));
444a98dd470Ssimonb 	if (nentries == 0) {
44561f28255Scgd 		printheader();
446078edf9cSrin 		return 1;
447a98dd470Ssimonb 	}
448cc1877baSrin 	pinfo = setpinfo(kinfo, nentries, calc_pcpu, rawcpu);
449cc1877baSrin 
45061f28255Scgd 	/*
45161f28255Scgd 	 * sort proc list
45261f28255Scgd 	 */
453*9dc307c4Schristos 	qsort(pinfo, nentries, sizeof(*pinfo), pscomp);
4545a6cfab1Schristos 
4555a6cfab1Schristos 	/*
4565a6cfab1Schristos 	 * We want things in descendant order
4575a6cfab1Schristos 	 */
4585a6cfab1Schristos 	if (descendancy)
4595a6cfab1Schristos 		descendant_sort(pinfo, nentries);
4605a6cfab1Schristos 
46161f28255Scgd 	/*
462a98dd470Ssimonb 	 * For each proc, call each variable output function in
463a98dd470Ssimonb 	 * "setwidth" mode to determine the widest element of
464a98dd470Ssimonb 	 * the column.
465a98dd470Ssimonb 	 */
466a468ec49Sdholland 
467a98dd470Ssimonb 	for (i = 0; i < nentries; i++) {
468cc1877baSrin 		struct pinfo *pi = &pinfo[i];
469cc1877baSrin 		struct kinfo_proc2 *ki = pi->ki;
470a98dd470Ssimonb 
471a5c6617dSchristos 		if (xflg == 0 && (ki->p_tdev == (uint32_t)NODEV ||
472f5e7ca24Spavel 		    (ki->p_flag & P_CONTROLT) == 0))
473a98dd470Ssimonb 			continue;
4743fdac2b8Sthorpej 
4753fdac2b8Sthorpej 		kl = kvm_getlwps(kd, ki->p_pid, ki->p_paddr,
476*9dc307c4Schristos 		    sizeof(*kl), &nlwps);
4773fdac2b8Sthorpej 		if (kl == 0)
4783fdac2b8Sthorpej 			nlwps = 0;
4793fdac2b8Sthorpej 		if (showlwps == 0) {
4803fdac2b8Sthorpej 			l = pick_representative_lwp(ki, kl, nlwps);
481d9463bc1Sapb 			SIMPLEQ_FOREACH(vent, &displaylist, next)
482cc1877baSrin 				OUTPUT(vent, l, pi, ki, WIDTHMODE);
4833fdac2b8Sthorpej 		} else {
4843fdac2b8Sthorpej 			/* The printing is done with the loops
4853fdac2b8Sthorpej 			 * reversed, but here we don't need that,
4863fdac2b8Sthorpej 			 * and this improves the code locality a bit.
4873fdac2b8Sthorpej 			 */
488d9463bc1Sapb 			SIMPLEQ_FOREACH(vent, &displaylist, next)
4893fdac2b8Sthorpej 				for (j = 0; j < nlwps; j++)
490cc1877baSrin 					OUTPUT(vent, &kl[j], pi, ki, WIDTHMODE);
4913fdac2b8Sthorpej 		}
492a98dd470Ssimonb 	}
493a98dd470Ssimonb 	/*
494a98dd470Ssimonb 	 * Print header - AFTER determining process field widths.
495a98dd470Ssimonb 	 * printheader() also adds up the total width of all
496a98dd470Ssimonb 	 * fields the first time it's called.
497a98dd470Ssimonb 	 */
498a98dd470Ssimonb 	printheader();
499a98dd470Ssimonb 	/*
500a98dd470Ssimonb 	 * For each proc, call each variable output function in
501a98dd470Ssimonb 	 * print mode.
50261f28255Scgd 	 */
50361f28255Scgd 	for (i = lineno = 0; i < nentries; i++) {
504cc1877baSrin 		struct pinfo *pi = &pinfo[i];
505cc1877baSrin 		struct kinfo_proc2 *ki = pi->ki;
506d70850c6Smycroft 
507a5c6617dSchristos 		if (xflg == 0 && (ki->p_tdev == (uint32_t)NODEV ||
508f5e7ca24Spavel 		    (ki->p_flag & P_CONTROLT ) == 0))
50961f28255Scgd 			continue;
5103fdac2b8Sthorpej 		kl = kvm_getlwps(kd, ki->p_pid, (u_long)ki->p_paddr,
511*9dc307c4Schristos 		    sizeof(*kl), &nlwps);
5123fdac2b8Sthorpej 		if (kl == 0)
5133fdac2b8Sthorpej 			nlwps = 0;
5143fdac2b8Sthorpej 		if (showlwps == 0) {
5153fdac2b8Sthorpej 			l = pick_representative_lwp(ki, kl, nlwps);
516d9463bc1Sapb 			SIMPLEQ_FOREACH(vent, &displaylist, next) {
517cc1877baSrin 				OUTPUT(vent, l, pi, ki, PRINTMODE);
518d9463bc1Sapb 				if (SIMPLEQ_NEXT(vent, next) != NULL)
5193fdac2b8Sthorpej 					(void)putchar(' ');
5203fdac2b8Sthorpej 			}
5213fdac2b8Sthorpej 			(void)putchar('\n');
5223fdac2b8Sthorpej 			if (prtheader && lineno++ == prtheader - 4) {
5233fdac2b8Sthorpej 				(void)putchar('\n');
5243fdac2b8Sthorpej 				printheader();
5253fdac2b8Sthorpej 				lineno = 0;
5263fdac2b8Sthorpej 			}
5273fdac2b8Sthorpej 		} else {
5283fdac2b8Sthorpej 			for (j = 0; j < nlwps; j++) {
529d9463bc1Sapb 				SIMPLEQ_FOREACH(vent, &displaylist, next) {
530cc1877baSrin 					OUTPUT(vent, &kl[j], pi, ki, PRINTMODE);
531d9463bc1Sapb 					if (SIMPLEQ_NEXT(vent, next) != NULL)
53261f28255Scgd 						(void)putchar(' ');
53361f28255Scgd 				}
53461f28255Scgd 				(void)putchar('\n');
53561f28255Scgd 				if (prtheader && lineno++ == prtheader - 4) {
53661f28255Scgd 					(void)putchar('\n');
53761f28255Scgd 					printheader();
53861f28255Scgd 					lineno = 0;
53961f28255Scgd 				}
54061f28255Scgd 			}
5413fdac2b8Sthorpej 		}
5423fdac2b8Sthorpej 	}
543fd0c6ecfSkamil 
5445626006fSkamil #ifdef __NO_LEAKS
545fd0c6ecfSkamil 	free(pinfo);
5465626006fSkamil #endif
547fd0c6ecfSkamil 
548078edf9cSrin 	return eval;
54961f28255Scgd }
55061f28255Scgd 
5513fdac2b8Sthorpej static struct kinfo_lwp *
pick_representative_lwp(struct kinfo_proc2 * ki,struct kinfo_lwp * kl,int nlwps)55253474900Ssimonb pick_representative_lwp(struct kinfo_proc2 *ki, struct kinfo_lwp *kl, int nlwps)
5533fdac2b8Sthorpej {
5543fdac2b8Sthorpej 	int i, onproc, running, sleeping, stopped, suspended;
5553fdac2b8Sthorpej 	static struct kinfo_lwp zero_lwp;
5563fdac2b8Sthorpej 
5573fdac2b8Sthorpej 	if (kl == 0)
5583fdac2b8Sthorpej 		return &zero_lwp;
5593fdac2b8Sthorpej 
5603fdac2b8Sthorpej 	/* Trivial case: only one LWP */
5613fdac2b8Sthorpej 	if (nlwps == 1)
5623fdac2b8Sthorpej 		return kl;
5633fdac2b8Sthorpej 
5643fdac2b8Sthorpej 	switch (ki->p_realstat) {
5653fdac2b8Sthorpej 	case SSTOP:
5663fdac2b8Sthorpej 	case SACTIVE:
5673fdac2b8Sthorpej 		/* Pick the most live LWP */
5683fdac2b8Sthorpej 		onproc = running = sleeping = stopped = suspended = -1;
5693fdac2b8Sthorpej 		for (i = 0; i < nlwps; i++) {
5703fdac2b8Sthorpej 			switch (kl[i].l_stat) {
5713fdac2b8Sthorpej 			case LSONPROC:
5723fdac2b8Sthorpej 				onproc = i;
5733fdac2b8Sthorpej 				break;
5743fdac2b8Sthorpej 			case LSRUN:
5753fdac2b8Sthorpej 				running = i;
5763fdac2b8Sthorpej 				break;
5773fdac2b8Sthorpej 			case LSSLEEP:
5783fdac2b8Sthorpej 				sleeping = i;
5793fdac2b8Sthorpej 				break;
5803fdac2b8Sthorpej 			case LSSTOP:
5813fdac2b8Sthorpej 				stopped = i;
5823fdac2b8Sthorpej 				break;
5833fdac2b8Sthorpej 			case LSSUSPENDED:
5843fdac2b8Sthorpej 				suspended = i;
5853fdac2b8Sthorpej 				break;
5863fdac2b8Sthorpej 			}
5873fdac2b8Sthorpej 		}
5883fdac2b8Sthorpej 		if (onproc != -1)
5893fdac2b8Sthorpej 			return &kl[onproc];
5903fdac2b8Sthorpej 		if (running != -1)
5913fdac2b8Sthorpej 			return &kl[running];
5923fdac2b8Sthorpej 		if (sleeping != -1)
5933fdac2b8Sthorpej 			return &kl[sleeping];
5943fdac2b8Sthorpej 		if (stopped != -1)
5953fdac2b8Sthorpej 			return &kl[stopped];
5963fdac2b8Sthorpej 		if (suspended != -1)
5973fdac2b8Sthorpej 			return &kl[suspended];
5983fdac2b8Sthorpej 		break;
5993fdac2b8Sthorpej 	case SZOMB:
6003fdac2b8Sthorpej 		/* First will do */
6013fdac2b8Sthorpej 		return kl;
6023fdac2b8Sthorpej 		break;
6033fdac2b8Sthorpej 	}
6043fdac2b8Sthorpej 	/* Error condition! */
6057da746abSchristos 	warnx("Inconsistent LWP state for process %d", ki->p_pid);
6063fdac2b8Sthorpej 	return kl;
6073fdac2b8Sthorpej }
6083fdac2b8Sthorpej 
609fd521aefSsimonb static struct kinfo_proc2 *
getkinfo_kvm(kvm_t * kdp,int what,int flag,int * nentriesp)61053474900Ssimonb getkinfo_kvm(kvm_t *kdp, int what, int flag, int *nentriesp)
611f848d2ecSjdolecek {
61253474900Ssimonb 
613*9dc307c4Schristos 	return kvm_getproc2(kdp, what, flag, sizeof(struct kinfo_proc2),
614*9dc307c4Schristos 	    nentriesp);
615f848d2ecSjdolecek }
616f848d2ecSjdolecek 
617cc1877baSrin static struct pinfo *
setpinfo(struct kinfo_proc2 * ki,int nentries,int calc_pcpu,int rawcpu)618cc1877baSrin setpinfo(struct kinfo_proc2 *ki, int nentries, int calc_pcpu, int rawcpu)
619cc1877baSrin {
620cc1877baSrin 	struct pinfo *pi;
621cc1877baSrin 	int i;
622cc1877baSrin 
623*9dc307c4Schristos 	pi = ecalloc(nentries, sizeof(*pi));
624cc1877baSrin 	if (calc_pcpu && !nlistread)
625cc1877baSrin 		donlist();
626cc1877baSrin 
627cc1877baSrin 	for (i = 0; i < nentries; i++) {
628cc1877baSrin 		pi[i].ki = &ki[i];
629cc1877baSrin 		if (!calc_pcpu)
630cc1877baSrin 			continue;
631f637bb05Srin 		if (ki[i].p_swtime == 0 || ki[i].p_realstat == SZOMB) {
632cc1877baSrin 			pi[i].pcpu = 0.0;
633cc1877baSrin 			continue;
634cc1877baSrin 		}
635cc1877baSrin 		pi[i].pcpu = 100.0 * (double)ki[i].p_pctcpu / fscale;
636cc1877baSrin 		if (!rawcpu)
637cc1877baSrin 			pi[i].pcpu /= 1.0 - exp(ki[i].p_swtime * log_ccpu);
638cc1877baSrin 	}
639cc1877baSrin 
640cc1877baSrin 	return pi;
641cc1877baSrin }
642cc1877baSrin 
6434d1457ceScgd static void
scanvars(void)64453474900Ssimonb scanvars(void)
64561f28255Scgd {
6464d1457ceScgd 	struct varent *vent;
6474d1457ceScgd 	VAR *v;
64861f28255Scgd 
649d9463bc1Sapb 	SIMPLEQ_FOREACH(vent, &displaylist, next) {
65061f28255Scgd 		v = vent->var;
651c6458f33Smatt 		if (v->flag & COMM) {
65261f28255Scgd 			needcomm = 1;
653c6458f33Smatt 			break;
654c6458f33Smatt 		}
65561f28255Scgd 	}
65661f28255Scgd }
65761f28255Scgd 
6584d1457ceScgd static int
pscomp(const void * a,const void * b)659036e2657Sdsl pscomp(const void *a, const void *b)
66061f28255Scgd {
661cc1877baSrin 	const struct pinfo *pa = (const struct pinfo *)a;
662cc1877baSrin 	const struct pinfo *pb = (const struct pinfo *)b;
663cc1877baSrin 	const struct kinfo_proc2 *ka = pa->ki;
664cc1877baSrin 	const struct kinfo_proc2 *kb = pb->ki;
665fd521aefSsimonb 
66661f28255Scgd 	int i;
667036e2657Sdsl 	int64_t i64;
668036e2657Sdsl 	VAR *v;
669036e2657Sdsl 	struct varent *ve;
6706310b596Schristos 	const sigset_t *sa, *sb;
67161f28255Scgd 
672fcc02354Smrg #define	V_SIZE(k) ((k)->p_vm_msize)
673036e2657Sdsl #define	RDIFF_N(t, n) \
6746310b596Schristos 	if (((const t *)((const char *)ka + v->off))[n] > ((const t *)((const char *)kb + v->off))[n]) \
675036e2657Sdsl 		return 1; \
6766310b596Schristos 	if (((const t *)((const char *)ka + v->off))[n] < ((const t *)((const char *)kb + v->off))[n]) \
677036e2657Sdsl 		return -1;
678fd521aefSsimonb 
679036e2657Sdsl #define	RDIFF(type) RDIFF_N(type, 0); continue
680036e2657Sdsl 
681d9463bc1Sapb 	SIMPLEQ_FOREACH(ve, &sortlist, next) {
682036e2657Sdsl 		v = ve->var;
683036e2657Sdsl 		if (v->flag & LWP)
684036e2657Sdsl 			/* LWP structure not available (yet) */
685036e2657Sdsl 			continue;
686036e2657Sdsl 		/* Sort on pvar() fields, + a few others */
687036e2657Sdsl 		switch (v->type) {
688036e2657Sdsl 		case CHAR:
689036e2657Sdsl 			RDIFF(char);
690036e2657Sdsl 		case UCHAR:
691036e2657Sdsl 			RDIFF(u_char);
692036e2657Sdsl 		case SHORT:
693036e2657Sdsl 			RDIFF(short);
694036e2657Sdsl 		case USHORT:
695036e2657Sdsl 			RDIFF(ushort);
696036e2657Sdsl 		case INT:
697036e2657Sdsl 			RDIFF(int);
698036e2657Sdsl 		case UINT:
699036e2657Sdsl 			RDIFF(uint);
700036e2657Sdsl 		case LONG:
701036e2657Sdsl 			RDIFF(long);
702036e2657Sdsl 		case ULONG:
703036e2657Sdsl 			RDIFF(ulong);
704036e2657Sdsl 		case INT32:
705036e2657Sdsl 			RDIFF(int32_t);
706036e2657Sdsl 		case UINT32:
707036e2657Sdsl 			RDIFF(uint32_t);
708036e2657Sdsl 		case SIGLIST:
709cc1877baSrin 			sa = (const void *)((const char *)ka + v->off);
710cc1877baSrin 			sb = (const void *)((const char *)kb + v->off);
711036e2657Sdsl 			i = 0;
712036e2657Sdsl 			do {
713036e2657Sdsl 				if (sa->__bits[i] > sb->__bits[i])
714036e2657Sdsl 					return 1;
715036e2657Sdsl 				if (sa->__bits[i] < sb->__bits[i])
716036e2657Sdsl 					return -1;
717036e2657Sdsl 				i++;
718990d25a9Slukem 			} while (i < (int)__arraycount(sa->__bits));
719036e2657Sdsl 			continue;
720036e2657Sdsl 		case INT64:
721036e2657Sdsl 			RDIFF(int64_t);
722036e2657Sdsl 		case KPTR:
723036e2657Sdsl 		case KPTR24:
724036e2657Sdsl 		case UINT64:
725036e2657Sdsl 			RDIFF(uint64_t);
726036e2657Sdsl 		case TIMEVAL:
727036e2657Sdsl 			/* compare xxx_sec then xxx_usec */
728036e2657Sdsl 			RDIFF_N(uint32_t, 0);
729036e2657Sdsl 			RDIFF_N(uint32_t, 1);
730036e2657Sdsl 			continue;
731036e2657Sdsl 		case CPUTIME:
732036e2657Sdsl 			i64 = ka->p_rtime_sec * 1000000 + ka->p_rtime_usec;
733036e2657Sdsl 			i64 -= kb->p_rtime_sec * 1000000 + kb->p_rtime_usec;
734036e2657Sdsl 			if (sumrusage) {
735036e2657Sdsl 				i64 += ka->p_uctime_sec * 1000000
736036e2657Sdsl 				    + ka->p_uctime_usec;
737036e2657Sdsl 				i64 -= kb->p_uctime_sec * 1000000
738036e2657Sdsl 				    + kb->p_uctime_usec;
739036e2657Sdsl 			}
740036e2657Sdsl 			if (i64 != 0)
741036e2657Sdsl 				return i64 > 0 ? 1 : -1;
742036e2657Sdsl 			continue;
743036e2657Sdsl 		case PCPU:
744cc1877baSrin 			i = pb->pcpu - pa->pcpu;
745036e2657Sdsl 			if (i != 0)
746036e2657Sdsl 				return i;
747036e2657Sdsl 			continue;
748036e2657Sdsl 		case VSIZE:
749036e2657Sdsl 			i = V_SIZE(kb) - V_SIZE(ka);
750036e2657Sdsl 			if (i != 0)
751036e2657Sdsl 				return i;
752036e2657Sdsl 			continue;
753036e2657Sdsl 
754036e2657Sdsl 		default:
755036e2657Sdsl 			/* Ignore everything else */
756036e2657Sdsl 			break;
757036e2657Sdsl 		}
758036e2657Sdsl 	}
759036e2657Sdsl 	return 0;
760036e2657Sdsl 
761036e2657Sdsl #undef VSIZE
76261f28255Scgd }
76361f28255Scgd 
76461f28255Scgd /*
76561f28255Scgd  * ICK (all for getopt), would rather hide the ugliness
76661f28255Scgd  * here than taint the main code.
76761f28255Scgd  *
76861f28255Scgd  *  ps foo -> ps -foo
76961f28255Scgd  *  ps 34 -> ps -p34
77061f28255Scgd  *
771975ed852Smycroft  * The old convention that 't' with no trailing tty arg means the user's
77261f28255Scgd  * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
77361f28255Scgd  * feature is available with the option 'T', which takes no argument.
77461f28255Scgd  */
7754d1457ceScgd static char *
kludge_oldps_options(char * s)77653474900Ssimonb kludge_oldps_options(char *s)
77761f28255Scgd {
77861f28255Scgd 	size_t len;
77961f28255Scgd 	char *newopts, *ns, *cp;
78061f28255Scgd 
78161f28255Scgd 	len = strlen(s);
782*9dc307c4Schristos 	newopts = ns = emalloc(len + 3);
78361f28255Scgd 	/*
78461f28255Scgd 	 * options begin with '-'
78561f28255Scgd 	 */
78661f28255Scgd 	if (*s != '-')
78761f28255Scgd 		*ns++ = '-';	/* add option flag */
78861f28255Scgd 	/*
78961f28255Scgd 	 * gaze to end of argv[1]
79061f28255Scgd 	 */
79161f28255Scgd 	cp = s + len - 1;
79261f28255Scgd 	/*
793d530ee00Ssimonb 	 * if the last letter is a 't' flag and there are no other option
794d530ee00Ssimonb 	 * characters that take arguments (eg U, p, o) in the option
795d530ee00Ssimonb 	 * string and the option string doesn't start with a '-' then
796d530ee00Ssimonb 	 * convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
79761f28255Scgd 	 */
798d530ee00Ssimonb 	if (*cp == 't' && *s != '-' && strpbrk(s, ARGOPTS) == NULL)
79961f28255Scgd 		*cp = 'T';
80061f28255Scgd 	else {
80161f28255Scgd 		/*
80261f28255Scgd 		 * otherwise check for trailing number, which *may* be a
80361f28255Scgd 		 * pid.
80461f28255Scgd 		 */
805238960afSdsl 		while (cp >= s && isdigit((unsigned char)*cp))
80661f28255Scgd 			--cp;
80761f28255Scgd 	}
80861f28255Scgd 	cp++;
8094d1457ceScgd 	memmove(ns, s, (size_t)(cp - s));	/* copy up to trailing number */
81061f28255Scgd 	ns += cp - s;
81161f28255Scgd 	/*
81261f28255Scgd 	 * if there's a trailing number, and not a preceding 'p' (pid) or
81361f28255Scgd 	 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
81461f28255Scgd 	 */
815238960afSdsl 	if (isdigit((unsigned char)*cp) &&
816975ed852Smycroft 	    (cp == s || (cp[-1] != 'U' && cp[-1] != 't' && cp[-1] != 'p' &&
817fde0dde8Satatat 	    cp[-1] != '/' && (cp - 1 == s || cp[-2] != 't'))))
81861f28255Scgd 		*ns++ = 'p';
819ecdc5967Smrg 	/* and append the number */
820d530ee00Ssimonb 	(void)strcpy(ns, cp);		/* XXX strcpy is safe here */
82161f28255Scgd 
82261f28255Scgd 	return (newopts);
82361f28255Scgd }
82461f28255Scgd 
82510ed3a58Schristos static int
parsenum(const char * str,const char * msg)82610ed3a58Schristos parsenum(const char *str, const char *msg)
82710ed3a58Schristos {
82810ed3a58Schristos 	char *ep;
82910ed3a58Schristos 	unsigned long ul;
83010ed3a58Schristos 
83110ed3a58Schristos 	ul = strtoul(str, &ep, 0);
83210ed3a58Schristos 
83310ed3a58Schristos 	if (*str == '\0' || *ep != '\0')
834b02b35c9Schristos 		errx(EXIT_FAILURE, "Invalid %s: `%s'", msg, str);
83510ed3a58Schristos 
83610ed3a58Schristos 	if (ul > INT_MAX)
837b02b35c9Schristos 		errx(EXIT_FAILURE, "Out of range %s: `%s'", msg, str);
83810ed3a58Schristos 
83910ed3a58Schristos 	return (int)ul;
84010ed3a58Schristos }
84110ed3a58Schristos 
8424d1457ceScgd static void
descendant_sort(struct pinfo * ki,int items)8435a6cfab1Schristos descendant_sort(struct pinfo *ki, int items)
8445a6cfab1Schristos {
8455a6cfab1Schristos 	int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
8465a6cfab1Schristos 	unsigned char *path;
8475a6cfab1Schristos 	struct pinfo kn;
8485a6cfab1Schristos 
8495a6cfab1Schristos 	/*
8505a6cfab1Schristos 	 * First, sort the entries by descendancy, tracking the descendancy
8515a6cfab1Schristos 	 * depth in the level field.
8525a6cfab1Schristos 	 */
8535a6cfab1Schristos 	src = 0;
8545a6cfab1Schristos 	maxlvl = 0;
8555a6cfab1Schristos 	while (src < items) {
8565a6cfab1Schristos 		if (ki[src].level) {
8575a6cfab1Schristos 			src++;
8585a6cfab1Schristos 			continue;
8595a6cfab1Schristos 		}
8605a6cfab1Schristos 		for (nsrc = 1; src + nsrc < items; nsrc++)
8615a6cfab1Schristos 			if (!ki[src + nsrc].level)
8625a6cfab1Schristos 				break;
8635a6cfab1Schristos 
8645a6cfab1Schristos 		for (dst = 0; dst < items; dst++) {
8655a6cfab1Schristos 			if (ki[dst].ki->p_pid == ki[src].ki->p_pid)
8665a6cfab1Schristos 				continue;
8675a6cfab1Schristos 			if (ki[dst].ki->p_pid == ki[src].ki->p_ppid)
8685a6cfab1Schristos 				break;
8695a6cfab1Schristos 		}
8705a6cfab1Schristos 
8715a6cfab1Schristos 		if (dst == items) {
8725a6cfab1Schristos 			src += nsrc;
8735a6cfab1Schristos 			continue;
8745a6cfab1Schristos 		}
8755a6cfab1Schristos 
8765a6cfab1Schristos 		for (ndst = 1; dst + ndst < items; ndst++)
8775a6cfab1Schristos 			if (ki[dst + ndst].level <= ki[dst].level)
8785a6cfab1Schristos 				break;
8795a6cfab1Schristos 
8805a6cfab1Schristos 		for (n = src; n < src + nsrc; n++) {
8815a6cfab1Schristos 			ki[n].level += ki[dst].level + 1;
8825a6cfab1Schristos 			if (maxlvl < ki[n].level)
8835a6cfab1Schristos 				maxlvl = ki[n].level;
8845a6cfab1Schristos 		}
8855a6cfab1Schristos 
8865a6cfab1Schristos 		while (nsrc) {
8875a6cfab1Schristos 			if (src < dst) {
8885a6cfab1Schristos 				kn = ki[src];
8895a6cfab1Schristos 				memmove(ki + src, ki + src + 1,
890*9dc307c4Schristos 				    (dst - src + ndst - 1) * sizeof(*ki));
8915a6cfab1Schristos 				ki[dst + ndst - 1] = kn;
8925a6cfab1Schristos 				nsrc--;
8935a6cfab1Schristos 				dst--;
8945a6cfab1Schristos 				ndst++;
8955a6cfab1Schristos 			} else if (src != dst + ndst) {
8965a6cfab1Schristos 				kn = ki[src];
8975a6cfab1Schristos 				memmove(ki + dst + ndst + 1, ki + dst + ndst,
898*9dc307c4Schristos 				    (src - dst - ndst) * sizeof(*ki));
8995a6cfab1Schristos 				ki[dst + ndst] = kn;
9005a6cfab1Schristos 				ndst++;
9015a6cfab1Schristos 				nsrc--;
9025a6cfab1Schristos 				src++;
9035a6cfab1Schristos 			} else {
9045a6cfab1Schristos 				ndst += nsrc;
9055a6cfab1Schristos 				src += nsrc;
9065a6cfab1Schristos 				nsrc = 0;
9075a6cfab1Schristos 			}
9085a6cfab1Schristos 		}
9095a6cfab1Schristos 	}
9105a6cfab1Schristos 
9115a6cfab1Schristos 	/*
9125a6cfab1Schristos 	 * Now populate prefix (instead of level) with the command
9135a6cfab1Schristos 	 * prefix used to show descendancies.
9145a6cfab1Schristos 	 */
915*9dc307c4Schristos 	path = ecalloc((maxlvl + 7) / 8, 1);
9165a6cfab1Schristos 	for (src = 0; src < items; src++) {
9175a6cfab1Schristos 		if ((lvl = ki[src].level) == 0) {
9185a6cfab1Schristos 			ki[src].prefix = NULL;
9195a6cfab1Schristos 			continue;
9205a6cfab1Schristos 		}
921*9dc307c4Schristos 		ki[src].prefix = emalloc(lvl * 2 + 1);
9225a6cfab1Schristos 		for (n = 0; n < lvl - 2; n++) {
9235a6cfab1Schristos 			ki[src].prefix[n * 2] =
9245a6cfab1Schristos 			    path[n / 8] & 1 << (n % 8) ? '|' : ' ';
9255a6cfab1Schristos 			ki[src].prefix[n * 2 + 1] = ' ';
9265a6cfab1Schristos 
9275a6cfab1Schristos 		}
9285a6cfab1Schristos 		if (n == lvl - 2) {
9295a6cfab1Schristos 			/* Have I any more siblings? */
9305a6cfab1Schristos 			for (siblings = 0, dst = src + 1; dst < items; dst++) {
9315a6cfab1Schristos 				if (ki[dst].level > lvl)
9325a6cfab1Schristos 					continue;
9335a6cfab1Schristos 				if (ki[dst].level == lvl)
9345a6cfab1Schristos 					siblings = 1;
9355a6cfab1Schristos 				break;
9365a6cfab1Schristos 			}
9375a6cfab1Schristos 			if (siblings)
9385a6cfab1Schristos 				path[n / 8] |= 1 << (n % 8);
9395a6cfab1Schristos 			else
9405a6cfab1Schristos 				path[n / 8] &= ~(1 << (n % 8));
9415a6cfab1Schristos 			ki[src].prefix[n * 2] = siblings ? '|' : '`';
9425a6cfab1Schristos 			ki[src].prefix[n * 2 + 1] = '-';
9435a6cfab1Schristos 			n++;
9445a6cfab1Schristos 		}
9455a6cfab1Schristos 		strcpy(ki[src].prefix + n * 2, "- ");
9465a6cfab1Schristos 	}
9475a6cfab1Schristos 	free(path);
9485a6cfab1Schristos }
9495a6cfab1Schristos 
9505a6cfab1Schristos static void
usage(void)95153474900Ssimonb usage(void)
95261f28255Scgd {
9534d1457ceScgd 
95461f28255Scgd 	(void)fprintf(stderr,
9554d1457ceScgd 	    "usage:\t%s\n\t   %s\n\t%s\n",
956de71cbe1Swiz 	    "ps [-AaCcdehjlmrSsTuvwx] [-G group] [-k key] [-M core] [-N system]",
957de71cbe1Swiz 	    "[-O fmt] [-o fmt] [-p pid] [-t tty] [-U user] [-W swap]",
9582869cb06Swiz 	    "ps -L");
95961f28255Scgd 	exit(1);
9609dc385beSmycroft 	/* NOTREACHED */
96161f28255Scgd }
962