xref: /freebsd/bin/ps/ps.c (revision 8d20be1e22095c27faf8fe8b2f0d089739cc742e)
1 /*-
2  * Copyright (c) 1990, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  * ------+---------+---------+-------- + --------+---------+---------+---------*
29  * Copyright (c) 2004  - Garance Alistair Drosehn <gad@FreeBSD.org>.
30  * All rights reserved.
31  *
32  * Significant modifications made to bring `ps' options somewhat closer
33  * to the standard for `ps' as described in SingleUnixSpec-v3.
34  * ------+---------+---------+-------- + --------+---------+---------+---------*
35  */
36 
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1990, 1993, 1994\n\
40 	The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #if 0
44 #ifndef lint
45 static char sccsid[] = "@(#)ps.c	8.4 (Berkeley) 4/2/94";
46 #endif /* not lint */
47 #endif
48 
49 #include <sys/cdefs.h>
50 __FBSDID("$FreeBSD$");
51 
52 #include <sys/param.h>
53 #include <sys/proc.h>
54 #include <sys/user.h>
55 #include <sys/stat.h>
56 #include <sys/ioctl.h>
57 #include <sys/sysctl.h>
58 #include <sys/mount.h>
59 
60 #include <ctype.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <fcntl.h>
64 #include <grp.h>
65 #include <kvm.h>
66 #include <limits.h>
67 #include <locale.h>
68 #include <paths.h>
69 #include <pwd.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <unistd.h>
74 
75 #include "ps.h"
76 
77 #define	_PATH_PTS	"/dev/pts/"
78 
79 #define	W_SEP	" \t"		/* "Whitespace" list separators */
80 #define	T_SEP	","		/* "Terminate-element" list separators */
81 
82 #ifdef LAZY_PS
83 #define	DEF_UREAD	0
84 #define	OPT_LAZY_f	"f"
85 #else
86 #define	DEF_UREAD	1	/* Always do the more-expensive read. */
87 #define	OPT_LAZY_f		/* I.e., the `-f' option is not added. */
88 #endif
89 
90 /*
91  * isdigit takes an `int', but expects values in the range of unsigned char.
92  * This wrapper ensures that values from a 'char' end up in the correct range.
93  */
94 #define	isdigitch(Anychar) isdigit((u_char)(Anychar))
95 
96 int	 cflag;			/* -c */
97 int	 eval;			/* Exit value */
98 time_t	 now;			/* Current time(3) value */
99 int	 rawcpu;		/* -C */
100 int	 sumrusage;		/* -S */
101 int	 termwidth;		/* Width of the screen (0 == infinity). */
102 int	 showthreads;		/* will threads be shown? */
103 
104 struct velisthead varlist = STAILQ_HEAD_INITIALIZER(varlist);
105 
106 static int	 forceuread = DEF_UREAD; /* Do extra work to get u-area. */
107 static kvm_t	*kd;
108 static int	 needcomm;	/* -o "command" */
109 static int	 needenv;	/* -e */
110 static int	 needuser;	/* -o "user" */
111 static int	 optfatal;	/* Fatal error parsing some list-option. */
112 static int	 pid_max;	/* kern.max_pid */
113 
114 static enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
115 
116 struct listinfo;
117 typedef	int	addelem_rtn(struct listinfo *_inf, const char *_elem);
118 
119 struct listinfo {
120 	int		 count;
121 	int		 maxcount;
122 	int		 elemsize;
123 	addelem_rtn	*addelem;
124 	const char	*lname;
125 	union {
126 		gid_t	*gids;
127 		pid_t	*pids;
128 		dev_t	*ttys;
129 		uid_t	*uids;
130 		void	*ptr;
131 	} l;
132 };
133 
134 static int	 addelem_gid(struct listinfo *, const char *);
135 static int	 addelem_pid(struct listinfo *, const char *);
136 static int	 addelem_tty(struct listinfo *, const char *);
137 static int	 addelem_uid(struct listinfo *, const char *);
138 static void	 add_list(struct listinfo *, const char *);
139 static void	 descendant_sort(KINFO *, int);
140 static void	 format_output(KINFO *);
141 static void	*expand_list(struct listinfo *);
142 static const char *
143 		 fmt(char **(*)(kvm_t *, const struct kinfo_proc *, int),
144 		    KINFO *, char *, char *, int);
145 static void	 free_list(struct listinfo *);
146 static void	 init_list(struct listinfo *, addelem_rtn, int, const char *);
147 static char	*kludge_oldps_options(const char *, char *, const char *);
148 static int	 pscomp(const void *, const void *);
149 static void	 saveuser(KINFO *);
150 static void	 scanvars(void);
151 static void	 sizevars(void);
152 static void	 pidmax_init(void);
153 static void	 usage(void);
154 
155 static char dfmt[] = "pid,tt,state,time,command";
156 static char jfmt[] = "user,pid,ppid,pgid,sid,jobc,state,tt,time,command";
157 static char lfmt[] = "uid,pid,ppid,cpu,pri,nice,vsz,rss,mwchan,state,"
158 			"tt,time,command";
159 static char   o1[] = "pid";
160 static char   o2[] = "tt,state,time,command";
161 static char ufmt[] = "user,pid,%cpu,%mem,vsz,rss,tt,state,start,time,command";
162 static char vfmt[] = "pid,state,time,sl,re,pagein,vsz,rss,lim,tsiz,"
163 			"%cpu,%mem,command";
164 static char Zfmt[] = "label";
165 
166 #define	PS_ARGS	"AaCcde" OPT_LAZY_f "G:gHhjLlM:mN:O:o:p:rSTt:U:uvwXxZ"
167 
168 int
169 main(int argc, char *argv[])
170 {
171 	struct listinfo gidlist, pgrplist, pidlist;
172 	struct listinfo ruidlist, sesslist, ttylist, uidlist;
173 	struct kinfo_proc *kp;
174 	KINFO *kinfo = NULL, *next_KINFO;
175 	KINFO_STR *ks;
176 	struct varent *vent;
177 	struct winsize ws;
178 	const char *nlistf, *memf, *fmtstr, *str;
179 	char *cols;
180 	int all, ch, elem, flag, _fmt, i, lineno, linelen, left;
181 	int descendancy, nentries, nkept, nselectors;
182 	int prtheader, wflag, what, xkeep, xkeep_implied;
183 	char errbuf[_POSIX2_LINE_MAX];
184 
185 	(void) setlocale(LC_ALL, "");
186 	time(&now);			/* Used by routines in print.c. */
187 
188 	if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0')
189 		termwidth = atoi(cols);
190 	else if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
191 	     ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
192 	     ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) == -1) ||
193 	     ws.ws_col == 0)
194 		termwidth = 79;
195 	else
196 		termwidth = ws.ws_col - 1;
197 
198 	/*
199 	 * Hide a number of option-processing kludges in a separate routine,
200 	 * to support some historical BSD behaviors, such as `ps axu'.
201 	 */
202 	if (argc > 1)
203 		argv[1] = kludge_oldps_options(PS_ARGS, argv[1], argv[2]);
204 
205 	pidmax_init();
206 
207 	all = descendancy = _fmt = nselectors = optfatal = 0;
208 	prtheader = showthreads = wflag = xkeep_implied = 0;
209 	xkeep = -1;			/* Neither -x nor -X. */
210 	init_list(&gidlist, addelem_gid, sizeof(gid_t), "group");
211 	init_list(&pgrplist, addelem_pid, sizeof(pid_t), "process group");
212 	init_list(&pidlist, addelem_pid, sizeof(pid_t), "process id");
213 	init_list(&ruidlist, addelem_uid, sizeof(uid_t), "ruser");
214 	init_list(&sesslist, addelem_pid, sizeof(pid_t), "session id");
215 	init_list(&ttylist, addelem_tty, sizeof(dev_t), "tty");
216 	init_list(&uidlist, addelem_uid, sizeof(uid_t), "user");
217 	memf = _PATH_DEVNULL;
218 	nlistf = NULL;
219 	while ((ch = getopt(argc, argv, PS_ARGS)) != -1)
220 		switch (ch) {
221 		case 'A':
222 			/*
223 			 * Exactly the same as `-ax'.   This has been
224 			 * added for compatibility with SUSv3, but for
225 			 * now it will not be described in the man page.
226 			 */
227 			nselectors++;
228 			all = xkeep = 1;
229 			break;
230 		case 'a':
231 			nselectors++;
232 			all = 1;
233 			break;
234 		case 'C':
235 			rawcpu = 1;
236 			break;
237 		case 'c':
238 			cflag = 1;
239 			break;
240 		case 'd':
241 			descendancy = 1;
242 			break;
243 		case 'e':			/* XXX set ufmt */
244 			needenv = 1;
245 			break;
246 #ifdef LAZY_PS
247 		case 'f':
248 			if (getuid() == 0 || getgid() == 0)
249 				forceuread = 1;
250 			break;
251 #endif
252 		case 'G':
253 			add_list(&gidlist, optarg);
254 			xkeep_implied = 1;
255 			nselectors++;
256 			break;
257 		case 'g':
258 #if 0
259 			/*-
260 			 * XXX - This SUSv3 behavior is still under debate
261 			 *	since it conflicts with the (undocumented)
262 			 *	`-g' option.  So we skip it for now.
263 			 */
264 			add_list(&pgrplist, optarg);
265 			xkeep_implied = 1;
266 			nselectors++;
267 			break;
268 #else
269 			/* The historical BSD-ish (from SunOS) behavior. */
270 			break;			/* no-op */
271 #endif
272 		case 'H':
273 			showthreads = KERN_PROC_INC_THREAD;
274 			break;
275 		case 'h':
276 			prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
277 			break;
278 		case 'j':
279 			parsefmt(jfmt, 0);
280 			_fmt = 1;
281 			jfmt[0] = '\0';
282 			break;
283 		case 'L':
284 			showkey();
285 			exit(0);
286 		case 'l':
287 			parsefmt(lfmt, 0);
288 			_fmt = 1;
289 			lfmt[0] = '\0';
290 			break;
291 		case 'M':
292 			memf = optarg;
293 			break;
294 		case 'm':
295 			sortby = SORTMEM;
296 			break;
297 		case 'N':
298 			nlistf = optarg;
299 			break;
300 		case 'O':
301 			parsefmt(o1, 1);
302 			parsefmt(optarg, 1);
303 			parsefmt(o2, 1);
304 			o1[0] = o2[0] = '\0';
305 			_fmt = 1;
306 			break;
307 		case 'o':
308 			parsefmt(optarg, 1);
309 			_fmt = 1;
310 			break;
311 		case 'p':
312 			add_list(&pidlist, optarg);
313 			/*
314 			 * Note: `-p' does not *set* xkeep, but any values
315 			 * from pidlist are checked before xkeep is.  That
316 			 * way they are always matched, even if the user
317 			 * specifies `-X'.
318 			 */
319 			nselectors++;
320 			break;
321 #if 0
322 		case 'R':
323 			/*-
324 			 * XXX - This un-standard option is still under
325 			 *	debate.  This is what SUSv3 defines as
326 			 *	the `-U' option, and while it would be
327 			 *	nice to have, it could cause even more
328 			 *	confusion to implement it as `-R'.
329 			 */
330 			add_list(&ruidlist, optarg);
331 			xkeep_implied = 1;
332 			nselectors++;
333 			break;
334 #endif
335 		case 'r':
336 			sortby = SORTCPU;
337 			break;
338 		case 'S':
339 			sumrusage = 1;
340 			break;
341 #if 0
342 		case 's':
343 			/*-
344 			 * XXX - This non-standard option is still under
345 			 *	debate.  This *is* supported on Solaris,
346 			 *	Linux, and IRIX, but conflicts with `-s'
347 			 *	on NetBSD and maybe some older BSD's.
348 			 */
349 			add_list(&sesslist, optarg);
350 			xkeep_implied = 1;
351 			nselectors++;
352 			break;
353 #endif
354 		case 'T':
355 			if ((optarg = ttyname(STDIN_FILENO)) == NULL)
356 				errx(1, "stdin: not a terminal");
357 			/* FALLTHROUGH */
358 		case 't':
359 			add_list(&ttylist, optarg);
360 			xkeep_implied = 1;
361 			nselectors++;
362 			break;
363 		case 'U':
364 			/* This is what SUSv3 defines as the `-u' option. */
365 			add_list(&uidlist, optarg);
366 			xkeep_implied = 1;
367 			nselectors++;
368 			break;
369 		case 'u':
370 			parsefmt(ufmt, 0);
371 			sortby = SORTCPU;
372 			_fmt = 1;
373 			ufmt[0] = '\0';
374 			break;
375 		case 'v':
376 			parsefmt(vfmt, 0);
377 			sortby = SORTMEM;
378 			_fmt = 1;
379 			vfmt[0] = '\0';
380 			break;
381 		case 'w':
382 			if (wflag)
383 				termwidth = UNLIMITED;
384 			else if (termwidth < 131)
385 				termwidth = 131;
386 			wflag++;
387 			break;
388 		case 'X':
389 			/*
390 			 * Note that `-X' and `-x' are not standard "selector"
391 			 * options. For most selector-options, we check *all*
392 			 * processes to see if any are matched by the given
393 			 * value(s).  After we have a set of all the matched
394 			 * processes, then `-X' and `-x' govern whether we
395 			 * modify that *matched* set for processes which do
396 			 * not have a controlling terminal.  `-X' causes
397 			 * those processes to be deleted from the matched
398 			 * set, while `-x' causes them to be kept.
399 			 */
400 			xkeep = 0;
401 			break;
402 		case 'x':
403 			xkeep = 1;
404 			break;
405 		case 'Z':
406 			parsefmt(Zfmt, 0);
407 			Zfmt[0] = '\0';
408 			break;
409 		case '?':
410 		default:
411 			usage();
412 		}
413 	argc -= optind;
414 	argv += optind;
415 
416 	/*
417 	 * If there arguments after processing all the options, attempt
418 	 * to treat them as a list of process ids.
419 	 */
420 	while (*argv) {
421 		if (!isdigitch(**argv))
422 			break;
423 		add_list(&pidlist, *argv);
424 		argv++;
425 	}
426 	if (*argv) {
427 		fprintf(stderr, "%s: illegal argument: %s\n",
428 		    getprogname(), *argv);
429 		usage();
430 	}
431 	if (optfatal)
432 		exit(1);		/* Error messages already printed. */
433 	if (xkeep < 0)			/* Neither -X nor -x was specified. */
434 		xkeep = xkeep_implied;
435 
436 	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
437 	if (kd == 0)
438 		errx(1, "%s", errbuf);
439 
440 	if (!_fmt)
441 		parsefmt(dfmt, 0);
442 
443 	if (nselectors == 0) {
444 		uidlist.l.ptr = malloc(sizeof(uid_t));
445 		if (uidlist.l.ptr == NULL)
446 			errx(1, "malloc failed");
447 		nselectors = 1;
448 		uidlist.count = uidlist.maxcount = 1;
449 		*uidlist.l.uids = getuid();
450 	}
451 
452 	/*
453 	 * scan requested variables, noting what structures are needed,
454 	 * and adjusting header widths as appropriate.
455 	 */
456 	scanvars();
457 
458 	/*
459 	 * Get process list.  If the user requested just one selector-
460 	 * option, then kvm_getprocs can be asked to return just those
461 	 * processes.  Otherwise, have it return all processes, and
462 	 * then this routine will search that full list and select the
463 	 * processes which match any of the user's selector-options.
464 	 */
465 	what = showthreads != 0 ? KERN_PROC_ALL : KERN_PROC_PROC;
466 	flag = 0;
467 	if (nselectors == 1) {
468 		if (gidlist.count == 1) {
469 			what = KERN_PROC_RGID | showthreads;
470 			flag = *gidlist.l.gids;
471 			nselectors = 0;
472 		} else if (pgrplist.count == 1) {
473 			what = KERN_PROC_PGRP | showthreads;
474 			flag = *pgrplist.l.pids;
475 			nselectors = 0;
476 		} else if (pidlist.count == 1) {
477 			what = KERN_PROC_PID | showthreads;
478 			flag = *pidlist.l.pids;
479 			nselectors = 0;
480 		} else if (ruidlist.count == 1) {
481 			what = KERN_PROC_RUID | showthreads;
482 			flag = *ruidlist.l.uids;
483 			nselectors = 0;
484 		} else if (sesslist.count == 1) {
485 			what = KERN_PROC_SESSION | showthreads;
486 			flag = *sesslist.l.pids;
487 			nselectors = 0;
488 		} else if (ttylist.count == 1) {
489 			what = KERN_PROC_TTY | showthreads;
490 			flag = *ttylist.l.ttys;
491 			nselectors = 0;
492 		} else if (uidlist.count == 1) {
493 			what = KERN_PROC_UID | showthreads;
494 			flag = *uidlist.l.uids;
495 			nselectors = 0;
496 		} else if (all) {
497 			/* No need for this routine to select processes. */
498 			nselectors = 0;
499 		}
500 	}
501 
502 	/*
503 	 * select procs
504 	 */
505 	nentries = -1;
506 	kp = kvm_getprocs(kd, what, flag, &nentries);
507 	if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0))
508 		errx(1, "%s", kvm_geterr(kd));
509 	nkept = 0;
510 	if (nentries > 0) {
511 		if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
512 			errx(1, "malloc failed");
513 		for (i = nentries; --i >= 0; ++kp) {
514 			/*
515 			 * If the user specified multiple selection-criteria,
516 			 * then keep any process matched by the inclusive OR
517 			 * of all the selection-criteria given.
518 			 */
519 			if (pidlist.count > 0) {
520 				for (elem = 0; elem < pidlist.count; elem++)
521 					if (kp->ki_pid == pidlist.l.pids[elem])
522 						goto keepit;
523 			}
524 			/*
525 			 * Note that we had to process pidlist before
526 			 * filtering out processes which do not have
527 			 * a controlling terminal.
528 			 */
529 			if (xkeep == 0) {
530 				if ((kp->ki_tdev == NODEV ||
531 				    (kp->ki_flag & P_CONTROLT) == 0))
532 					continue;
533 			}
534 			if (nselectors == 0)
535 				goto keepit;
536 			if (gidlist.count > 0) {
537 				for (elem = 0; elem < gidlist.count; elem++)
538 					if (kp->ki_rgid == gidlist.l.gids[elem])
539 						goto keepit;
540 			}
541 			if (pgrplist.count > 0) {
542 				for (elem = 0; elem < pgrplist.count; elem++)
543 					if (kp->ki_pgid ==
544 					    pgrplist.l.pids[elem])
545 						goto keepit;
546 			}
547 			if (ruidlist.count > 0) {
548 				for (elem = 0; elem < ruidlist.count; elem++)
549 					if (kp->ki_ruid ==
550 					    ruidlist.l.uids[elem])
551 						goto keepit;
552 			}
553 			if (sesslist.count > 0) {
554 				for (elem = 0; elem < sesslist.count; elem++)
555 					if (kp->ki_sid == sesslist.l.pids[elem])
556 						goto keepit;
557 			}
558 			if (ttylist.count > 0) {
559 				for (elem = 0; elem < ttylist.count; elem++)
560 					if (kp->ki_tdev == ttylist.l.ttys[elem])
561 						goto keepit;
562 			}
563 			if (uidlist.count > 0) {
564 				for (elem = 0; elem < uidlist.count; elem++)
565 					if (kp->ki_uid == uidlist.l.uids[elem])
566 						goto keepit;
567 			}
568 			/*
569 			 * This process did not match any of the user's
570 			 * selector-options, so skip the process.
571 			 */
572 			continue;
573 
574 		keepit:
575 			next_KINFO = &kinfo[nkept];
576 			next_KINFO->ki_p = kp;
577 			next_KINFO->ki_d.level = 0;
578 			next_KINFO->ki_d.prefix = NULL;
579 			next_KINFO->ki_pcpu = getpcpu(next_KINFO);
580 			if (sortby == SORTMEM)
581 				next_KINFO->ki_memsize = kp->ki_tsize +
582 				    kp->ki_dsize + kp->ki_ssize;
583 			if (needuser)
584 				saveuser(next_KINFO);
585 			nkept++;
586 		}
587 	}
588 
589 	sizevars();
590 
591 	if (nkept == 0) {
592 		printheader();
593 		exit(1);
594 	}
595 
596 	/*
597 	 * sort proc list
598 	 */
599 	qsort(kinfo, nkept, sizeof(KINFO), pscomp);
600 
601 	/*
602 	 * We want things in descendant order
603 	 */
604 	if (descendancy)
605 		descendant_sort(kinfo, nkept);
606 
607 
608 	/*
609 	 * Prepare formatted output.
610 	 */
611 	for (i = 0; i < nkept; i++)
612 		format_output(&kinfo[i]);
613 
614 	/*
615 	 * Print header.
616 	 */
617 	printheader();
618 
619 	/*
620 	 * Output formatted lines.
621 	 */
622 	for (i = lineno = 0; i < nkept; i++) {
623 		linelen = 0;
624 		STAILQ_FOREACH(vent, &varlist, next_ve) {
625 	        	if (vent->var->flag & LJUST)
626 				fmtstr = "%-*s";
627 			else
628 				fmtstr = "%*s";
629 
630 			ks = STAILQ_FIRST(&kinfo[i].ki_ks);
631 			STAILQ_REMOVE_HEAD(&kinfo[i].ki_ks, ks_next);
632 			/* Truncate rightmost column if necessary.  */
633 			if (STAILQ_NEXT(vent, next_ve) == NULL &&
634 			   termwidth != UNLIMITED && ks->ks_str != NULL) {
635 				left = termwidth - linelen;
636 				if (left > 0 && left < (int)strlen(ks->ks_str))
637 					ks->ks_str[left] = '\0';
638 			}
639 			str = ks->ks_str;
640 			if (str == NULL)
641 				str = "-";
642 			/* No padding for the last column, if it's LJUST. */
643 			if (STAILQ_NEXT(vent, next_ve) == NULL &&
644 			    vent->var->flag & LJUST)
645 				linelen += printf(fmtstr, 0, str);
646 			else
647 				linelen += printf(fmtstr, vent->var->width, str);
648 
649 			if (ks->ks_str != NULL) {
650 				free(ks->ks_str);
651 				ks->ks_str = NULL;
652 			}
653 			free(ks);
654 			ks = NULL;
655 
656 			if (STAILQ_NEXT(vent, next_ve) != NULL) {
657 				(void)putchar(' ');
658 				linelen++;
659 			}
660 		}
661 		(void)putchar('\n');
662 		if (prtheader && lineno++ == prtheader - 4) {
663 			(void)putchar('\n');
664 			printheader();
665 			lineno = 0;
666 		}
667 	}
668 	free_list(&gidlist);
669 	free_list(&pidlist);
670 	free_list(&pgrplist);
671 	free_list(&ruidlist);
672 	free_list(&sesslist);
673 	free_list(&ttylist);
674 	free_list(&uidlist);
675 	for (i = 0; i < nkept; i++)
676 		free(kinfo[i].ki_d.prefix);
677 	free(kinfo);
678 
679 	exit(eval);
680 }
681 
682 static int
683 addelem_gid(struct listinfo *inf, const char *elem)
684 {
685 	struct group *grp;
686 	const char *nameorID;
687 	char *endp;
688 	u_long bigtemp;
689 
690 	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
691 		if (*elem == '\0')
692 			warnx("Invalid (zero-length) %s name", inf->lname);
693 		else
694 			warnx("%s name too long: %s", inf->lname, elem);
695 		optfatal = 1;
696 		return (0);		/* Do not add this value. */
697 	}
698 
699 	/*
700 	 * SUSv3 states that `ps -G grouplist' should match "real-group
701 	 * ID numbers", and does not mention group-names.  I do want to
702 	 * also support group-names, so this tries for a group-id first,
703 	 * and only tries for a name if that doesn't work.  This is the
704 	 * opposite order of what is done in addelem_uid(), but in
705 	 * practice the order would only matter for group-names which
706 	 * are all-numeric.
707 	 */
708 	grp = NULL;
709 	nameorID = "named";
710 	errno = 0;
711 	bigtemp = strtoul(elem, &endp, 10);
712 	if (errno == 0 && *endp == '\0' && bigtemp <= GID_MAX) {
713 		nameorID = "name or ID matches";
714 		grp = getgrgid((gid_t)bigtemp);
715 	}
716 	if (grp == NULL)
717 		grp = getgrnam(elem);
718 	if (grp == NULL) {
719 		warnx("No %s %s '%s'", inf->lname, nameorID, elem);
720 		optfatal = 1;
721 		return (0);
722 	}
723 	if (inf->count >= inf->maxcount)
724 		expand_list(inf);
725 	inf->l.gids[(inf->count)++] = grp->gr_gid;
726 	return (1);
727 }
728 
729 static int
730 addelem_pid(struct listinfo *inf, const char *elem)
731 {
732 	char *endp;
733 	long tempid;
734 
735 	if (*elem == '\0') {
736 		warnx("Invalid (zero-length) process id");
737 		optfatal = 1;
738 		return (0);		/* Do not add this value. */
739 	}
740 
741 	errno = 0;
742 	tempid = strtol(elem, &endp, 10);
743 	if (*endp != '\0' || tempid < 0 || elem == endp) {
744 		warnx("Invalid %s: %s", inf->lname, elem);
745 		errno = ERANGE;
746 	} else if (errno != 0 || tempid > pid_max) {
747 		warnx("%s too large: %s", inf->lname, elem);
748 		errno = ERANGE;
749 	}
750 	if (errno == ERANGE) {
751 		optfatal = 1;
752 		return (0);
753 	}
754 	if (inf->count >= inf->maxcount)
755 		expand_list(inf);
756 	inf->l.pids[(inf->count)++] = tempid;
757 	return (1);
758 }
759 
760 /*-
761  * The user can specify a device via one of three formats:
762  *     1) fully qualified, e.g.:     /dev/ttyp0 /dev/console	/dev/pts/0
763  *     2) missing "/dev", e.g.:      ttyp0      console		pts/0
764  *     3) two-letters, e.g.:         p0         co		0
765  *        (matching letters that would be seen in the "TT" column)
766  */
767 static int
768 addelem_tty(struct listinfo *inf, const char *elem)
769 {
770 	const char *ttypath;
771 	struct stat sb;
772 	char pathbuf[PATH_MAX], pathbuf2[PATH_MAX], pathbuf3[PATH_MAX];
773 
774 	ttypath = NULL;
775 	pathbuf2[0] = '\0';
776 	pathbuf3[0] = '\0';
777 	switch (*elem) {
778 	case '/':
779 		ttypath = elem;
780 		break;
781 	case 'c':
782 		if (strcmp(elem, "co") == 0) {
783 			ttypath = _PATH_CONSOLE;
784 			break;
785 		}
786 		/* FALLTHROUGH */
787 	default:
788 		strlcpy(pathbuf, _PATH_DEV, sizeof(pathbuf));
789 		strlcat(pathbuf, elem, sizeof(pathbuf));
790 		ttypath = pathbuf;
791 		if (strncmp(pathbuf, _PATH_TTY, strlen(_PATH_TTY)) == 0)
792 			break;
793 		if (strncmp(pathbuf, _PATH_PTS, strlen(_PATH_PTS)) == 0)
794 			break;
795 		if (strcmp(pathbuf, _PATH_CONSOLE) == 0)
796 			break;
797 		/* Check to see if /dev/tty${elem} exists */
798 		strlcpy(pathbuf2, _PATH_TTY, sizeof(pathbuf2));
799 		strlcat(pathbuf2, elem, sizeof(pathbuf2));
800 		if (stat(pathbuf2, &sb) == 0 && S_ISCHR(sb.st_mode)) {
801 			/* No need to repeat stat() && S_ISCHR() checks */
802 			ttypath = NULL;
803 			break;
804 		}
805 		/* Check to see if /dev/pts/${elem} exists */
806 		strlcpy(pathbuf3, _PATH_PTS, sizeof(pathbuf3));
807 		strlcat(pathbuf3, elem, sizeof(pathbuf3));
808 		if (stat(pathbuf3, &sb) == 0 && S_ISCHR(sb.st_mode)) {
809 			/* No need to repeat stat() && S_ISCHR() checks */
810 			ttypath = NULL;
811 			break;
812 		}
813 		break;
814 	}
815 	if (ttypath) {
816 		if (stat(ttypath, &sb) == -1) {
817 			if (pathbuf3[0] != '\0')
818 				warn("%s, %s, and %s", pathbuf3, pathbuf2,
819 				    ttypath);
820 			else
821 				warn("%s", ttypath);
822 			optfatal = 1;
823 			return (0);
824 		}
825 		if (!S_ISCHR(sb.st_mode)) {
826 			if (pathbuf3[0] != '\0')
827 				warnx("%s, %s, and %s: Not a terminal",
828 				    pathbuf3, pathbuf2, ttypath);
829 			else
830 				warnx("%s: Not a terminal", ttypath);
831 			optfatal = 1;
832 			return (0);
833 		}
834 	}
835 	if (inf->count >= inf->maxcount)
836 		expand_list(inf);
837 	inf->l.ttys[(inf->count)++] = sb.st_rdev;
838 	return (1);
839 }
840 
841 static int
842 addelem_uid(struct listinfo *inf, const char *elem)
843 {
844 	struct passwd *pwd;
845 	char *endp;
846 	u_long bigtemp;
847 
848 	if (*elem == '\0' || strlen(elem) >= MAXLOGNAME) {
849 		if (*elem == '\0')
850 			warnx("Invalid (zero-length) %s name", inf->lname);
851 		else
852 			warnx("%s name too long: %s", inf->lname, elem);
853 		optfatal = 1;
854 		return (0);		/* Do not add this value. */
855 	}
856 
857 	pwd = getpwnam(elem);
858 	if (pwd == NULL) {
859 		errno = 0;
860 		bigtemp = strtoul(elem, &endp, 10);
861 		if (errno != 0 || *endp != '\0' || bigtemp > UID_MAX)
862 			warnx("No %s named '%s'", inf->lname, elem);
863 		else {
864 			/* The string is all digits, so it might be a userID. */
865 			pwd = getpwuid((uid_t)bigtemp);
866 			if (pwd == NULL)
867 				warnx("No %s name or ID matches '%s'",
868 				    inf->lname, elem);
869 		}
870 	}
871 	if (pwd == NULL) {
872 		/*
873 		 * These used to be treated as minor warnings (and the
874 		 * option was simply ignored), but now they are fatal
875 		 * errors (and the command will be aborted).
876 		 */
877 		optfatal = 1;
878 		return (0);
879 	}
880 	if (inf->count >= inf->maxcount)
881 		expand_list(inf);
882 	inf->l.uids[(inf->count)++] = pwd->pw_uid;
883 	return (1);
884 }
885 
886 static void
887 add_list(struct listinfo *inf, const char *argp)
888 {
889 	const char *savep;
890 	char *cp, *endp;
891 	int toolong;
892 	char elemcopy[PATH_MAX];
893 
894 	if (*argp == '\0')
895 		inf->addelem(inf, argp);
896 	while (*argp != '\0') {
897 		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
898 			argp++;
899 		savep = argp;
900 		toolong = 0;
901 		cp = elemcopy;
902 		if (strchr(T_SEP, *argp) == NULL) {
903 			endp = elemcopy + sizeof(elemcopy) - 1;
904 			while (*argp != '\0' && cp <= endp &&
905 			    strchr(W_SEP T_SEP, *argp) == NULL)
906 				*cp++ = *argp++;
907 			if (cp > endp)
908 				toolong = 1;
909 		}
910 		if (!toolong) {
911 			*cp = '\0';
912 			/*
913 			 * Add this single element to the given list.
914 			 */
915 			inf->addelem(inf, elemcopy);
916 		} else {
917 			/*
918 			 * The string is too long to copy.  Find the end
919 			 * of the string to print out the warning message.
920 			 */
921 			while (*argp != '\0' && strchr(W_SEP T_SEP,
922 			    *argp) == NULL)
923 				argp++;
924 			warnx("Value too long: %.*s", (int)(argp - savep),
925 			    savep);
926 			optfatal = 1;
927 		}
928 		/*
929 		 * Skip over any number of trailing whitespace characters,
930 		 * but only one (at most) trailing element-terminating
931 		 * character.
932 		 */
933 		while (*argp != '\0' && strchr(W_SEP, *argp) != NULL)
934 			argp++;
935 		if (*argp != '\0' && strchr(T_SEP, *argp) != NULL) {
936 			argp++;
937 			/* Catch case where string ended with a comma. */
938 			if (*argp == '\0')
939 				inf->addelem(inf, argp);
940 		}
941 	}
942 }
943 
944 static void
945 descendant_sort(KINFO *ki, int items)
946 {
947 	int dst, lvl, maxlvl, n, ndst, nsrc, siblings, src;
948 	unsigned char *path;
949 	KINFO kn;
950 
951 	/*
952 	 * First, sort the entries by descendancy, tracking the descendancy
953 	 * depth in the ki_d.level field.
954 	 */
955 	src = 0;
956 	maxlvl = 0;
957 	while (src < items) {
958 		if (ki[src].ki_d.level) {
959 			src++;
960 			continue;
961 		}
962 		for (nsrc = 1; src + nsrc < items; nsrc++)
963 			if (!ki[src + nsrc].ki_d.level)
964 				break;
965 
966 		for (dst = 0; dst < items; dst++) {
967 			if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_pid)
968 				continue;
969 			if (ki[dst].ki_p->ki_pid == ki[src].ki_p->ki_ppid)
970 				break;
971 		}
972 
973 		if (dst == items) {
974 			src += nsrc;
975 			continue;
976 		}
977 
978 		for (ndst = 1; dst + ndst < items; ndst++)
979 			if (ki[dst + ndst].ki_d.level <= ki[dst].ki_d.level)
980 				break;
981 
982 		for (n = src; n < src + nsrc; n++) {
983 			ki[n].ki_d.level += ki[dst].ki_d.level + 1;
984 			if (maxlvl < ki[n].ki_d.level)
985 				maxlvl = ki[n].ki_d.level;
986 		}
987 
988 		while (nsrc) {
989 			if (src < dst) {
990 				kn = ki[src];
991 				memmove(ki + src, ki + src + 1,
992 				    (dst - src + ndst - 1) * sizeof *ki);
993 				ki[dst + ndst - 1] = kn;
994 				nsrc--;
995 				dst--;
996 				ndst++;
997 			} else if (src != dst + ndst) {
998 				kn = ki[src];
999 				memmove(ki + dst + ndst + 1, ki + dst + ndst,
1000 				    (src - dst - ndst) * sizeof *ki);
1001 				ki[dst + ndst] = kn;
1002 				ndst++;
1003 				nsrc--;
1004 				src++;
1005 			} else {
1006 				ndst += nsrc;
1007 				src += nsrc;
1008 				nsrc = 0;
1009 			}
1010 		}
1011 	}
1012 
1013 	/*
1014 	 * Now populate ki_d.prefix (instead of ki_d.level) with the command
1015 	 * prefix used to show descendancies.
1016 	 */
1017 	path = malloc((maxlvl + 7) / 8);
1018 	memset(path, '\0', (maxlvl + 7) / 8);
1019 	for (src = 0; src < items; src++) {
1020 		if ((lvl = ki[src].ki_d.level) == 0) {
1021 			ki[src].ki_d.prefix = NULL;
1022 			continue;
1023 		}
1024 		if ((ki[src].ki_d.prefix = malloc(lvl * 2 + 1)) == NULL)
1025 			errx(1, "malloc failed");
1026 		for (n = 0; n < lvl - 2; n++) {
1027 			ki[src].ki_d.prefix[n * 2] =
1028 			    path[n / 8] & 1 << (n % 8) ? '|' : ' ';
1029 			ki[src].ki_d.prefix[n * 2 + 1] = ' ';
1030 		}
1031 		if (n == lvl - 2) {
1032 			/* Have I any more siblings? */
1033 			for (siblings = 0, dst = src + 1; dst < items; dst++) {
1034 				if (ki[dst].ki_d.level > lvl)
1035 					continue;
1036 				if (ki[dst].ki_d.level == lvl)
1037 					siblings = 1;
1038 				break;
1039 			}
1040 			if (siblings)
1041 				path[n / 8] |= 1 << (n % 8);
1042 			else
1043 				path[n / 8] &= ~(1 << (n % 8));
1044 			ki[src].ki_d.prefix[n * 2] = siblings ? '|' : '`';
1045 			ki[src].ki_d.prefix[n * 2 + 1] = '-';
1046 			n++;
1047 		}
1048 		strcpy(ki[src].ki_d.prefix + n * 2, "- ");
1049 	}
1050 	free(path);
1051 }
1052 
1053 static void *
1054 expand_list(struct listinfo *inf)
1055 {
1056 	void *newlist;
1057 	int newmax;
1058 
1059 	newmax = (inf->maxcount + 1) << 1;
1060 	newlist = realloc(inf->l.ptr, newmax * inf->elemsize);
1061 	if (newlist == NULL) {
1062 		free(inf->l.ptr);
1063 		errx(1, "realloc to %d %ss failed", newmax, inf->lname);
1064 	}
1065 	inf->maxcount = newmax;
1066 	inf->l.ptr = newlist;
1067 
1068 	return (newlist);
1069 }
1070 
1071 static void
1072 free_list(struct listinfo *inf)
1073 {
1074 
1075 	inf->count = inf->elemsize = inf->maxcount = 0;
1076 	if (inf->l.ptr != NULL)
1077 		free(inf->l.ptr);
1078 	inf->addelem = NULL;
1079 	inf->lname = NULL;
1080 	inf->l.ptr = NULL;
1081 }
1082 
1083 static void
1084 init_list(struct listinfo *inf, addelem_rtn artn, int elemsize,
1085     const char *lname)
1086 {
1087 
1088 	inf->count = inf->maxcount = 0;
1089 	inf->elemsize = elemsize;
1090 	inf->addelem = artn;
1091 	inf->lname = lname;
1092 	inf->l.ptr = NULL;
1093 }
1094 
1095 VARENT *
1096 find_varentry(VAR *v)
1097 {
1098 	struct varent *vent;
1099 
1100 	STAILQ_FOREACH(vent, &varlist, next_ve) {
1101 		if (strcmp(vent->var->name, v->name) == 0)
1102 			return vent;
1103 	}
1104 	return NULL;
1105 }
1106 
1107 static void
1108 scanvars(void)
1109 {
1110 	struct varent *vent;
1111 	VAR *v;
1112 
1113 	STAILQ_FOREACH(vent, &varlist, next_ve) {
1114 		v = vent->var;
1115 		if (v->flag & USER)
1116 			needuser = 1;
1117 		if (v->flag & COMM)
1118 			needcomm = 1;
1119 	}
1120 }
1121 
1122 static void
1123 format_output(KINFO *ki)
1124 {
1125 	struct varent *vent;
1126 	VAR *v;
1127 	KINFO_STR *ks;
1128 	char *str;
1129 	int len;
1130 
1131 	STAILQ_INIT(&ki->ki_ks);
1132 	STAILQ_FOREACH(vent, &varlist, next_ve) {
1133 		v = vent->var;
1134 		str = (v->oproc)(ki, vent);
1135 		ks = malloc(sizeof(*ks));
1136 		if (ks == NULL)
1137 			errx(1, "malloc failed");
1138 		ks->ks_str = str;
1139 		STAILQ_INSERT_TAIL(&ki->ki_ks, ks, ks_next);
1140 		if (str != NULL) {
1141 			len = strlen(str);
1142 		} else
1143 			len = 1; /* "-" */
1144 		if (v->width < len)
1145 			v->width = len;
1146 	}
1147 }
1148 
1149 static void
1150 sizevars(void)
1151 {
1152 	struct varent *vent;
1153 	VAR *v;
1154 	int i;
1155 
1156 	STAILQ_FOREACH(vent, &varlist, next_ve) {
1157 		v = vent->var;
1158 		i = strlen(vent->header);
1159 		if (v->width < i)
1160 			v->width = i;
1161 	}
1162 }
1163 
1164 static const char *
1165 fmt(char **(*fn)(kvm_t *, const struct kinfo_proc *, int), KINFO *ki,
1166     char *comm, char *thread, int maxlen)
1167 {
1168 	const char *s;
1169 
1170 	s = fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm,
1171 	    showthreads && ki->ki_p->ki_numthreads > 1 ? thread : NULL, maxlen);
1172 	return (s);
1173 }
1174 
1175 #define UREADOK(ki)	(forceuread || (ki->ki_p->ki_flag & P_INMEM))
1176 
1177 static void
1178 saveuser(KINFO *ki)
1179 {
1180 
1181 	if (ki->ki_p->ki_flag & P_INMEM) {
1182 		/*
1183 		 * The u-area might be swapped out, and we can't get
1184 		 * at it because we have a crashdump and no swap.
1185 		 * If it's here fill in these fields, otherwise, just
1186 		 * leave them 0.
1187 		 */
1188 		ki->ki_valid = 1;
1189 	} else
1190 		ki->ki_valid = 0;
1191 	/*
1192 	 * save arguments if needed
1193 	 */
1194 	if (needcomm) {
1195 		if (ki->ki_p->ki_stat == SZOMB)
1196 			ki->ki_args = strdup("<defunct>");
1197 		else if (UREADOK(ki) || (ki->ki_p->ki_args != NULL))
1198 			ki->ki_args = strdup(fmt(kvm_getargv, ki,
1199 			    ki->ki_p->ki_comm, ki->ki_p->ki_tdname, MAXCOMLEN));
1200 		else
1201 			asprintf(&ki->ki_args, "(%s)", ki->ki_p->ki_comm);
1202 		if (ki->ki_args == NULL)
1203 			errx(1, "malloc failed");
1204 	} else {
1205 		ki->ki_args = NULL;
1206 	}
1207 	if (needenv) {
1208 		if (UREADOK(ki))
1209 			ki->ki_env = strdup(fmt(kvm_getenvv, ki,
1210 			    (char *)NULL, (char *)NULL, 0));
1211 		else
1212 			ki->ki_env = strdup("()");
1213 		if (ki->ki_env == NULL)
1214 			errx(1, "malloc failed");
1215 	} else {
1216 		ki->ki_env = NULL;
1217 	}
1218 }
1219 
1220 /* A macro used to improve the readability of pscomp(). */
1221 #define	DIFF_RETURN(a, b, field) do {	\
1222 	if ((a)->field != (b)->field)	\
1223 		return (((a)->field < (b)->field) ? -1 : 1); 	\
1224 } while (0)
1225 
1226 static int
1227 pscomp(const void *a, const void *b)
1228 {
1229 	const KINFO *ka, *kb;
1230 
1231 	ka = a;
1232 	kb = b;
1233 	/* SORTCPU and SORTMEM are sorted in descending order. */
1234 	if (sortby == SORTCPU)
1235 		DIFF_RETURN(kb, ka, ki_pcpu);
1236 	if (sortby == SORTMEM)
1237 		DIFF_RETURN(kb, ka, ki_memsize);
1238 	/*
1239 	 * TTY's are sorted in ascending order, except that all NODEV
1240 	 * processes come before all processes with a device.
1241 	 */
1242 	if (ka->ki_p->ki_tdev != kb->ki_p->ki_tdev) {
1243 		if (ka->ki_p->ki_tdev == NODEV)
1244 			return (-1);
1245 		if (kb->ki_p->ki_tdev == NODEV)
1246 			return (1);
1247 		DIFF_RETURN(ka, kb, ki_p->ki_tdev);
1248 	}
1249 
1250 	/* PID's and TID's (threads) are sorted in ascending order. */
1251 	DIFF_RETURN(ka, kb, ki_p->ki_pid);
1252 	DIFF_RETURN(ka, kb, ki_p->ki_tid);
1253 	return (0);
1254 }
1255 #undef DIFF_RETURN
1256 
1257 /*
1258  * ICK (all for getopt), would rather hide the ugliness
1259  * here than taint the main code.
1260  *
1261  *  ps foo -> ps -foo
1262  *  ps 34 -> ps -p34
1263  *
1264  * The old convention that 't' with no trailing tty arg means the users
1265  * tty, is only supported if argv[1] doesn't begin with a '-'.  This same
1266  * feature is available with the option 'T', which takes no argument.
1267  */
1268 static char *
1269 kludge_oldps_options(const char *optlist, char *origval, const char *nextarg)
1270 {
1271 	size_t len;
1272 	char *argp, *cp, *newopts, *ns, *optp, *pidp;
1273 
1274 	/*
1275 	 * See if the original value includes any option which takes an
1276 	 * argument (and will thus use up the remainder of the string).
1277 	 */
1278 	argp = NULL;
1279 	if (optlist != NULL) {
1280 		for (cp = origval; *cp != '\0'; cp++) {
1281 			optp = strchr(optlist, *cp);
1282 			if ((optp != NULL) && *(optp + 1) == ':') {
1283 				argp = cp;
1284 				break;
1285 			}
1286 		}
1287 	}
1288 	if (argp != NULL && *origval == '-')
1289 		return (origval);
1290 
1291 	/*
1292 	 * if last letter is a 't' flag with no argument (in the context
1293 	 * of the oldps options -- option string NOT starting with a '-' --
1294 	 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
1295 	 *
1296 	 * However, if a flag accepting a string argument is found earlier
1297 	 * in the option string (including a possible `t' flag), then the
1298 	 * remainder of the string must be the argument to that flag; so
1299 	 * do not modify that argument.  Note that a trailing `t' would
1300 	 * cause argp to be set, if argp was not already set by some
1301 	 * earlier option.
1302 	 */
1303 	len = strlen(origval);
1304 	cp = origval + len - 1;
1305 	pidp = NULL;
1306 	if (*cp == 't' && *origval != '-' && cp == argp) {
1307 		if (nextarg == NULL || *nextarg == '-' || isdigitch(*nextarg))
1308 			*cp = 'T';
1309 	} else if (argp == NULL) {
1310 		/*
1311 		 * The original value did not include any option which takes
1312 		 * an argument (and that would include `p' and `t'), so check
1313 		 * the value for trailing number, or comma-separated list of
1314 		 * numbers, which we will treat as a pid request.
1315 		 */
1316 		if (isdigitch(*cp)) {
1317 			while (cp >= origval && (*cp == ',' || isdigitch(*cp)))
1318 				--cp;
1319 			pidp = cp + 1;
1320 		}
1321 	}
1322 
1323 	/*
1324 	 * If nothing needs to be added to the string, then return
1325 	 * the "original" (although possibly modified) value.
1326 	 */
1327 	if (*origval == '-' && pidp == NULL)
1328 		return (origval);
1329 
1330 	/*
1331 	 * Create a copy of the string to add '-' and/or 'p' to the
1332 	 * original value.
1333 	 */
1334 	if ((newopts = ns = malloc(len + 3)) == NULL)
1335 		errx(1, "malloc failed");
1336 
1337 	if (*origval != '-')
1338 		*ns++ = '-';	/* add option flag */
1339 
1340 	if (pidp == NULL)
1341 		strcpy(ns, origval);
1342 	else {
1343 		/*
1344 		 * Copy everything before the pid string, add the `p',
1345 		 * and then copy the pid string.
1346 		 */
1347 		len = pidp - origval;
1348 		memcpy(ns, origval, len);
1349 		ns += len;
1350 		*ns++ = 'p';
1351 		strcpy(ns, pidp);
1352 	}
1353 
1354 	return (newopts);
1355 }
1356 
1357 static void
1358 pidmax_init(void)
1359 {
1360 	size_t intsize;
1361 
1362 	intsize = sizeof(pid_max);
1363 	if (sysctlbyname("kern.pid_max", &pid_max, &intsize, NULL, 0) < 0) {
1364 		warn("unable to read kern.pid_max");
1365 		pid_max = 99999;
1366 	}
1367 }
1368 
1369 static void
1370 usage(void)
1371 {
1372 #define	SINGLE_OPTS	"[-aCcde" OPT_LAZY_f "HhjlmrSTuvwXxZ]"
1373 
1374 	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
1375 	    "usage: ps " SINGLE_OPTS " [-O fmt | -o fmt] [-G gid[,gid...]]",
1376 	    "          [-M core] [-N system]",
1377 	    "          [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]",
1378 	    "       ps [-L]");
1379 	exit(1);
1380 }
1381