xref: /illumos-gate/usr/src/cmd/ps/ps.c (revision 5328fc53d11d7151861fa272e4fb0248b8f0e145)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2013 Gary Mills
24  *
25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 /*
30  * Copyright (c) 2018, Joyent, Inc.
31  */
32 
33 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
34 /*	  All Rights Reserved	*/
35 
36 /*
37  * ps -- print things about processes.
38  */
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <pwd.h>
45 #include <grp.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/mkdev.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <limits.h>
52 #include <dirent.h>
53 #include <sys/signal.h>
54 #include <sys/fault.h>
55 #include <sys/syscall.h>
56 #include <sys/time.h>
57 #include <procfs.h>
58 #include <locale.h>
59 #include <wctype.h>
60 #include <wchar.h>
61 #include <libw.h>
62 #include <stdarg.h>
63 #include <sys/proc.h>
64 #include <sys/pset.h>
65 #include <project.h>
66 #include <zone.h>
67 
68 #define	min(a, b)	((a) > (b) ? (b) : (a))
69 #define	max(a, b)	((a) < (b) ? (b) : (a))
70 
71 #define	NTTYS	20	/* initial size of table for -t option  */
72 #define	SIZ	30	/* initial size of tables for -p, -s, -g, -h and -z */
73 
74 /*
75  * Size of buffer holding args for t, p, s, g, u, U, G, z options.
76  * Set to ZONENAME_MAX, the minimum value needed to allow any
77  * zone to be specified.
78  */
79 #define	ARGSIZ ZONENAME_MAX
80 
81 /* Max chars in a user/group name or printed u/g id */
82 #define	MAXUGNAME (LOGNAME_MAX+2)
83 
84 /* Structure for storing user or group info */
85 struct ugdata {
86 	id_t	id;			/* numeric user-id or group-id */
87 	char	name[MAXUGNAME+1];	/* user/group name, null terminated */
88 };
89 
90 struct ughead {
91 	size_t	size;		/* number of ugdata structs allocated */
92 	size_t	nent;		/* number of active entries */
93 	struct ugdata *ent;	/* pointer to array of actual entries */
94 };
95 
96 enum fname {	/* enumeration of field names */
97 	F_USER,		/* effective user of the process */
98 	F_RUSER,	/* real user of the process */
99 	F_GROUP,	/* effective group of the process */
100 	F_RGROUP,	/* real group of the process */
101 	F_UID,		/* numeric effective uid of the process */
102 	F_RUID,		/* numeric real uid of the process */
103 	F_GID,		/* numeric effective gid of the process */
104 	F_RGID,		/* numeric real gid of the process */
105 	F_PID,		/* process id */
106 	F_PPID,		/* parent process id */
107 	F_PGID,		/* process group id */
108 	F_SID,		/* session id */
109 	F_PSR,		/* bound processor */
110 	F_LWP,		/* lwp-id */
111 	F_LWPNAME,	/* lwp name */
112 	F_NLWP,		/* number of lwps */
113 	F_OPRI,		/* old priority (obsolete) */
114 	F_PRI,		/* new priority */
115 	F_F,		/* process flags */
116 	F_S,		/* letter indicating the state */
117 	F_C,		/* processor utilization (obsolete) */
118 	F_PCPU,		/* percent of recently used cpu time */
119 	F_PMEM,		/* percent of physical memory used (rss) */
120 	F_OSZ,		/* virtual size of the process in pages */
121 	F_VSZ,		/* virtual size of the process in kilobytes */
122 	F_RSS,		/* resident set size of the process in kilobytes */
123 	F_NICE,		/* "nice" value of the process */
124 	F_CLASS,	/* scheduler class */
125 	F_STIME,	/* start time of the process, hh:mm:ss or Month Day */
126 	F_ETIME,	/* elapsed time of the process, [[dd-]hh:]mm:ss */
127 	F_TIME,		/* cpu time of the process, [[dd-]hh:]mm:ss */
128 	F_TTY,		/* name of the controlling terminal */
129 	F_ADDR,		/* address of the process (obsolete) */
130 	F_WCHAN,	/* wait channel (sleep condition variable) */
131 	F_FNAME,	/* file name of command */
132 	F_COMM,		/* name of command (argv[0] value) */
133 	F_ARGS,		/* name of command plus all its arguments */
134 	F_TASKID,	/* task id */
135 	F_PROJID,	/* project id */
136 	F_PROJECT,	/* project name of the process */
137 	F_PSET,		/* bound processor set */
138 	F_ZONE,		/* zone name */
139 	F_ZONEID,	/* zone id */
140 	F_CTID,		/* process contract id */
141 	F_LGRP,		/* process home lgroup */
142 	F_DMODEL	/* process data model */
143 };
144 
145 struct field {
146 	struct field	*next;		/* linked list */
147 	int		fname;		/* field index */
148 	const char	*header;	/* header to use */
149 	int		width;		/* width of field */
150 };
151 
152 static	struct field *fields = NULL;	/* fields selected via -o */
153 static	struct field *last_field = NULL;
154 static	int do_header = 0;
155 static	struct timeval now;
156 
157 /* array of defined fields, in fname order */
158 struct def_field {
159 	const char *fname;
160 	const char *header;
161 	int width;
162 	int minwidth;
163 };
164 
165 static struct def_field fname[] = {
166 	/* fname	header		width	minwidth */
167 	{ "user",	"USER",		8,	8	},
168 	{ "ruser",	"RUSER",	8,	8	},
169 	{ "group",	"GROUP",	8,	8	},
170 	{ "rgroup",	"RGROUP",	8,	8	},
171 	{ "uid",	"UID",		5,	5	},
172 	{ "ruid",	"RUID",		5,	5	},
173 	{ "gid",	"GID",		5,	5	},
174 	{ "rgid",	"RGID",		5,	5	},
175 	{ "pid",	"PID",		5,	5	},
176 	{ "ppid",	"PPID",		5,	5	},
177 	{ "pgid",	"PGID",		5,	5	},
178 	{ "sid",	"SID",		5,	5	},
179 	{ "psr",	"PSR",		3,	2	},
180 	{ "lwp",	"LWP",		6,	2	},
181 	{ "lwpname",	"LWPNAME",	32,	8	},
182 	{ "nlwp",	"NLWP",		4,	2	},
183 	{ "opri",	"PRI",		3,	2	},
184 	{ "pri",	"PRI",		3,	2	},
185 	{ "f",		"F",		2,	2	},
186 	{ "s",		"S",		1,	1	},
187 	{ "c",		"C",		2,	2	},
188 	{ "pcpu",	"%CPU",		4,	4	},
189 	{ "pmem",	"%MEM",		4,	4	},
190 	{ "osz",	"SZ",		4,	4	},
191 	{ "vsz",	"VSZ",		4,	4	},
192 	{ "rss",	"RSS",		4,	4	},
193 	{ "nice",	"NI",		2,	2	},
194 	{ "class",	"CLS",		4,	2	},
195 	{ "stime",	"STIME",	8,	8	},
196 	{ "etime",	"ELAPSED",	11,	7	},
197 	{ "time",	"TIME",		11,	5	},
198 	{ "tty",	"TT",		7,	7	},
199 #ifdef _LP64
200 	{ "addr",	"ADDR",		16,	8	},
201 	{ "wchan",	"WCHAN",	16,	8	},
202 #else
203 	{ "addr",	"ADDR",		8,	8	},
204 	{ "wchan",	"WCHAN",	8,	8	},
205 #endif
206 	{ "fname",	"COMMAND",	8,	8	},
207 	{ "comm",	"COMMAND",	80,	8	},
208 	{ "args",	"COMMAND",	80,	80	},
209 	{ "taskid",	"TASKID",	5,	5	},
210 	{ "projid",	"PROJID",	5,	5	},
211 	{ "project",	"PROJECT",	8,	8	},
212 	{ "pset",	"PSET",		3,	3	},
213 	{ "zone",	"ZONE",		8,	8	},
214 	{ "zoneid",	"ZONEID",	5,	5	},
215 	{ "ctid",	"CTID",		5,	5	},
216 	{ "lgrp",	"LGRP",		4,	2	},
217 	{ "dmodel",	"DMODEL",	6,	6	},
218 };
219 
220 #define	NFIELDS	(sizeof (fname) / sizeof (fname[0]))
221 
222 static	int	retcode = 1;
223 static	int	lflg;
224 static	int	Aflg;
225 static	int	uflg;
226 static	int	Uflg;
227 static	int	Gflg;
228 static	int	aflg;
229 static	int	dflg;
230 static	int	Lflg;
231 static	int	Pflg;
232 static	int	Wflg;
233 static	int	yflg;
234 static	int	pflg;
235 static	int	fflg;
236 static	int	cflg;
237 static	int	jflg;
238 static	int	gflg;
239 static	int	sflg;
240 static	int	tflg;
241 static	int	zflg;
242 static	int	Zflg;
243 static	int	hflg;
244 static	int	Hflg;
245 static	uid_t	tuid = (uid_t)-1;
246 static	int	errflg;
247 
248 static	int	ndev;		/* number of devices */
249 static	int	maxdev;		/* number of devl structures allocated */
250 
251 #define	DNINCR	100
252 #define	DNSIZE	14
253 static struct devl {		/* device list   */
254 	char	dname[DNSIZE];	/* device name   */
255 	dev_t	ddev;		/* device number */
256 } *devl;
257 
258 static	struct tty {
259 	char *tname;
260 	dev_t tdev;
261 } *tty = NULL;			/* for t option */
262 static	size_t	ttysz = 0;
263 static	int	ntty = 0;
264 
265 static	pid_t	*pid = NULL;	/* for p option */
266 static	size_t	pidsz = 0;
267 static	size_t	npid = 0;
268 
269 static	int	*lgrps = NULL;	/* list of lgroup IDs for for h option */
270 static	size_t	lgrps_size = 0;	/* size of the lgrps list */
271 static	size_t	nlgrps = 0;	/* number elements in the list */
272 
273 /* Maximum possible lgroup ID value */
274 #define	MAX_LGRP_ID 256
275 
276 static	pid_t	*grpid = NULL;	/* for g option */
277 static	size_t	grpidsz = 0;
278 static	int	ngrpid = 0;
279 
280 static	pid_t	*sessid = NULL;	/* for s option */
281 static	size_t	sessidsz = 0;
282 static	int	nsessid = 0;
283 
284 static	zoneid_t *zoneid = NULL; /* for z option */
285 static	size_t	zoneidsz = 0;
286 static	int	nzoneid = 0;
287 
288 static	int	kbytes_per_page;
289 static	int	pidwidth;
290 
291 static	char	*procdir = "/proc";	/* standard /proc directory */
292 
293 static struct ughead	euid_tbl;	/* table to store selected euid's */
294 static struct ughead	ruid_tbl;	/* table to store selected real uid's */
295 static struct ughead	egid_tbl;	/* table to store selected egid's */
296 static struct ughead	rgid_tbl;	/* table to store selected real gid's */
297 static prheader_t *lpsinfobuf;		/* buffer to contain lpsinfo */
298 static size_t	lpbufsize;
299 
300 /*
301  * This constant defines the sentinal number of process IDs below which we
302  * only examine individual entries in /proc rather than scanning through
303  * /proc. This optimization is a huge win in the common case.
304  */
305 #define	PTHRESHOLD	40
306 
307 #define	UCB_OPTS	"-aceglnrtuvwxSU"
308 
309 static	void	usage(void);
310 static	char	*getarg(char **);
311 static	char	*parse_format(char *);
312 static	char	*gettty(psinfo_t *);
313 static	int	prfind(int, psinfo_t *, char **);
314 static	void	prcom(psinfo_t *, char *);
315 static	void	prtpct(ushort_t, int);
316 static	void	print_time(time_t, int);
317 static	void	print_field(psinfo_t *, struct field *, const char *);
318 static	void	print_zombie_field(psinfo_t *, struct field *, const char *);
319 static	void	pr_fields(psinfo_t *, const char *,
320 		void (*print_fld)(psinfo_t *, struct field *, const char *));
321 static	int	search(pid_t *, int, pid_t);
322 static	void	add_ugentry(struct ughead *, char *);
323 static	int	uconv(struct ughead *);
324 static	int	gconv(struct ughead *);
325 static	int	ugfind(id_t, struct ughead *);
326 static	void	prtime(timestruc_t, int, int);
327 static	void	przom(psinfo_t *);
328 static	int	namencnt(char *, int, int);
329 static	char	*err_string(int);
330 static	int	print_proc(char *pname);
331 static	time_t	delta_secs(const timestruc_t *);
332 static	int	str2id(const char *, pid_t *, long, long);
333 static	int	str2uid(const char *,  uid_t *, unsigned long, unsigned long);
334 static	void	*Realloc(void *, size_t);
335 static	int	pidcmp(const void *p1, const void *p2);
336 
337 extern	int	ucbmain(int, char **);
338 static	int	stdmain(int, char **);
339 
340 int
341 main(int argc, char **argv)
342 {
343 	const char *me;
344 
345 	/*
346 	 * The original two ps'es are linked in a single binary;
347 	 * their main()s are renamed to stdmain for /usr/bin/ps and
348 	 * ucbmain for /usr/ucb/ps.
349 	 * We try to figure out which instance of ps the user wants to run.
350 	 * Traditionally, the UCB variant doesn't require the flag argument
351 	 * start with a "-".  If the first argument doesn't start with a
352 	 * "-", we call "ucbmain".
353 	 * If there's a first argument and it starts with a "-", we check
354 	 * whether any of the options isn't acceptable to "ucbmain"; in that
355 	 * case we run "stdmain".
356 	 * If we can't tell from the options which main to call, we check
357 	 * the binary we are running.  We default to "stdmain" but
358 	 * any mention in the executable name of "ucb" causes us to call
359 	 * ucbmain.
360 	 */
361 	if (argv[1] != NULL) {
362 		if (argv[1][0] != '-')
363 			return (ucbmain(argc, argv));
364 		else if (argv[1][strspn(argv[1], UCB_OPTS)] != '\0')
365 			return (stdmain(argc, argv));
366 	}
367 
368 	me = getexecname();
369 
370 	if (me != NULL && strstr(me, "ucb") != NULL)
371 		return (ucbmain(argc, argv));
372 	else
373 		return (stdmain(argc, argv));
374 }
375 
376 static int
377 stdmain(int argc, char **argv)
378 {
379 	char	*p;
380 	char	*p1;
381 	char	*parg;
382 	int	c;
383 	int	i;
384 	int	pgerrflg = 0;	/* err flg: non-numeric arg w/p & g options */
385 	size_t	size, len;
386 	DIR	*dirp;
387 	struct dirent *dentp;
388 	pid_t	maxpid;
389 	pid_t	id;
390 	int	ret;
391 	char	loc_stime_str[32];
392 
393 	(void) setlocale(LC_ALL, "");
394 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
395 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
396 #endif
397 	(void) textdomain(TEXT_DOMAIN);
398 
399 	(void) memset(&euid_tbl, 0, sizeof (euid_tbl));
400 	(void) memset(&ruid_tbl, 0, sizeof (ruid_tbl));
401 	(void) memset(&egid_tbl, 0, sizeof (egid_tbl));
402 	(void) memset(&rgid_tbl, 0, sizeof (rgid_tbl));
403 
404 	kbytes_per_page = sysconf(_SC_PAGESIZE) / 1024;
405 
406 	(void) gettimeofday(&now, NULL);
407 
408 	/*
409 	 * calculate width of pid fields based on configured MAXPID
410 	 * (must be at least 5 to retain output format compatibility)
411 	 */
412 	id = maxpid = (pid_t)sysconf(_SC_MAXPID);
413 	pidwidth = 1;
414 	while ((id /= 10) > 0)
415 		++pidwidth;
416 	pidwidth = pidwidth < 5 ? 5 : pidwidth;
417 
418 	fname[F_PID].width = fname[F_PPID].width = pidwidth;
419 	fname[F_PGID].width = fname[F_SID].width = pidwidth;
420 
421 	/*
422 	 * TRANSLATION_NOTE
423 	 * Specify the printf format with width and precision for
424 	 * the STIME field.
425 	 */
426 	len = snprintf(loc_stime_str, sizeof (loc_stime_str),
427 	    dcgettext(NULL, "%8.8s", LC_TIME), "STIME");
428 	if (len >= sizeof (loc_stime_str))
429 		len = sizeof (loc_stime_str) - 1;
430 
431 	fname[F_STIME].width = fname[F_STIME].minwidth = len;
432 
433 	while ((c = getopt(argc, argv, "jlfceAadLPWyZHh:t:p:g:u:U:G:n:s:o:z:"))
434 	    != EOF)
435 		switch (c) {
436 		case 'H':		/* Show home lgroups */
437 			Hflg++;
438 			break;
439 		case 'h':
440 			/*
441 			 * Show processes/threads with given home lgroups
442 			 */
443 			hflg++;
444 			p1 = optarg;
445 			do {
446 				int id;
447 
448 				/*
449 				 * Get all IDs in the list, verify for
450 				 * correctness and place in lgrps array.
451 				 */
452 				parg = getarg(&p1);
453 				/* Convert string to integer */
454 				ret = str2id(parg, (pid_t *)&id, 0,
455 				    MAX_LGRP_ID);
456 				/* Complain if ID didn't parse correctly */
457 				if (ret != 0) {
458 					pgerrflg++;
459 					(void) fprintf(stderr,
460 					    gettext("ps: %s "), parg);
461 					if (ret == EINVAL)
462 						(void) fprintf(stderr,
463 						    gettext("is an invalid "
464 						    "non-numeric argument"));
465 					else
466 						(void) fprintf(stderr,
467 						    gettext("exceeds valid "
468 						    "range"));
469 					(void) fprintf(stderr,
470 					    gettext(" for -h option\n"));
471 					continue;
472 				}
473 
474 				/* Extend lgrps array if needed */
475 				if (nlgrps == lgrps_size) {
476 					/* Double the size of the lgrps array */
477 					if (lgrps_size == 0)
478 						lgrps_size = SIZ;
479 					lgrps_size *= 2;
480 					lgrps = Realloc(lgrps,
481 					    lgrps_size * sizeof (int));
482 				}
483 				/* place the id in the lgrps table */
484 				lgrps[nlgrps++] = id;
485 			} while (*p1);
486 			break;
487 		case 'l':		/* long listing */
488 			lflg++;
489 			break;
490 		case 'f':		/* full listing */
491 			fflg++;
492 			break;
493 		case 'j':
494 			jflg++;
495 			break;
496 		case 'c':
497 			/*
498 			 * Format output to reflect scheduler changes:
499 			 * high numbers for high priorities and don't
500 			 * print nice or p_cpu values.  'c' option only
501 			 * effective when used with 'l' or 'f' options.
502 			 */
503 			cflg++;
504 			break;
505 		case 'A':		/* list every process */
506 		case 'e':		/* (obsolete) list every process */
507 			Aflg++;
508 			tflg = Gflg = Uflg = uflg = pflg = gflg = sflg = 0;
509 			zflg = hflg = 0;
510 			break;
511 		case 'a':
512 			/*
513 			 * Same as 'e' except no session group leaders
514 			 * and no non-terminal processes.
515 			 */
516 			aflg++;
517 			break;
518 		case 'd':	/* same as e except no session leaders */
519 			dflg++;
520 			break;
521 		case 'L':	/* show lwps */
522 			Lflg++;
523 			break;
524 		case 'P':	/* show bound processor */
525 			Pflg++;
526 			break;
527 		case 'W':	/* truncate long names */
528 			Wflg++;
529 			break;
530 		case 'y':	/* omit F & ADDR, report RSS & SZ in Kby */
531 			yflg++;
532 			break;
533 		case 'n':	/* no longer needed; retain as no-op */
534 			(void) fprintf(stderr,
535 			    gettext("ps: warning: -n option ignored\n"));
536 			break;
537 		case 't':		/* terminals */
538 #define	TSZ	30
539 			tflg++;
540 			p1 = optarg;
541 			do {
542 				char nambuf[TSZ+6];	/* for "/dev/" + '\0' */
543 				struct stat64 s;
544 				parg = getarg(&p1);
545 				p = Realloc(NULL, TSZ+1);	/* for '\0' */
546 				/* zero the buffer before using it */
547 				p[0] = '\0';
548 				size = TSZ;
549 				if (isdigit(*parg)) {
550 					(void) strcpy(p, "tty");
551 					size -= 3;
552 				}
553 				(void) strncat(p, parg, size);
554 				if (ntty == ttysz) {
555 					if ((ttysz *= 2) == 0)
556 						ttysz = NTTYS;
557 					tty = Realloc(tty,
558 					    (ttysz + 1) * sizeof (struct tty));
559 				}
560 				tty[ntty].tdev = PRNODEV;
561 				(void) strcpy(nambuf, "/dev/");
562 				(void) strcat(nambuf, p);
563 				if (stat64(nambuf, &s) == 0)
564 					tty[ntty].tdev = s.st_rdev;
565 				tty[ntty++].tname = p;
566 			} while (*p1);
567 			break;
568 		case 'p':		/* proc ids */
569 			pflg++;
570 			p1 = optarg;
571 			do {
572 				pid_t id;
573 
574 				parg = getarg(&p1);
575 				if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
576 					pgerrflg++;
577 					(void) fprintf(stderr,
578 					    gettext("ps: %s "), parg);
579 					if (ret == EINVAL)
580 						(void) fprintf(stderr,
581 						    gettext("is an invalid "
582 						    "non-numeric argument"));
583 					else
584 						(void) fprintf(stderr,
585 						    gettext("exceeds valid "
586 						    "range"));
587 					(void) fprintf(stderr,
588 					    gettext(" for -p option\n"));
589 					continue;
590 				}
591 
592 				if (npid == pidsz) {
593 					if ((pidsz *= 2) == 0)
594 						pidsz = SIZ;
595 					pid = Realloc(pid,
596 					    pidsz * sizeof (pid_t));
597 				}
598 				pid[npid++] = id;
599 			} while (*p1);
600 			break;
601 		case 's':		/* session */
602 			sflg++;
603 			p1 = optarg;
604 			do {
605 				pid_t id;
606 
607 				parg = getarg(&p1);
608 				if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
609 					pgerrflg++;
610 					(void) fprintf(stderr,
611 					    gettext("ps: %s "), parg);
612 					if (ret == EINVAL)
613 						(void) fprintf(stderr,
614 						    gettext("is an invalid "
615 						    "non-numeric argument"));
616 					else
617 						(void) fprintf(stderr,
618 						    gettext("exceeds valid "
619 						    "range"));
620 					(void) fprintf(stderr,
621 					    gettext(" for -s option\n"));
622 					continue;
623 				}
624 
625 				if (nsessid == sessidsz) {
626 					if ((sessidsz *= 2) == 0)
627 						sessidsz = SIZ;
628 					sessid = Realloc(sessid,
629 					    sessidsz * sizeof (pid_t));
630 				}
631 				sessid[nsessid++] = id;
632 			} while (*p1);
633 			break;
634 		case 'g':		/* proc group */
635 			gflg++;
636 			p1 = optarg;
637 			do {
638 				pid_t id;
639 
640 				parg = getarg(&p1);
641 				if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
642 					pgerrflg++;
643 					(void) fprintf(stderr,
644 					    gettext("ps: %s "), parg);
645 					if (ret == EINVAL)
646 						(void) fprintf(stderr,
647 						    gettext("is an invalid "
648 						    "non-numeric argument"));
649 					else
650 						(void) fprintf(stderr,
651 						    gettext("exceeds valid "
652 						    "range"));
653 					(void) fprintf(stderr,
654 					    gettext(" for -g option\n"));
655 					continue;
656 				}
657 
658 				if (ngrpid == grpidsz) {
659 					if ((grpidsz *= 2) == 0)
660 						grpidsz = SIZ;
661 					grpid = Realloc(grpid,
662 					    grpidsz * sizeof (pid_t));
663 				}
664 				grpid[ngrpid++] = id;
665 			} while (*p1);
666 			break;
667 		case 'u':		/* effective user name or number */
668 			uflg++;
669 			p1 = optarg;
670 			do {
671 				parg = getarg(&p1);
672 				add_ugentry(&euid_tbl, parg);
673 			} while (*p1);
674 			break;
675 		case 'U':		/* real user name or number */
676 			Uflg++;
677 			p1 = optarg;
678 			do {
679 				parg = getarg(&p1);
680 				add_ugentry(&ruid_tbl, parg);
681 			} while (*p1);
682 			break;
683 		case 'G':		/* real group name or number */
684 			Gflg++;
685 			p1 = optarg;
686 			do {
687 				parg = getarg(&p1);
688 				add_ugentry(&rgid_tbl, parg);
689 			} while (*p1);
690 			break;
691 		case 'o':		/* output format */
692 			p = optarg;
693 			while ((p = parse_format(p)) != NULL)
694 				;
695 			break;
696 		case 'z':		/* zone name or number */
697 			zflg++;
698 			p1 = optarg;
699 			do {
700 				zoneid_t id;
701 
702 				parg = getarg(&p1);
703 				if (zone_get_id(parg, &id) != 0) {
704 					pgerrflg++;
705 					(void) fprintf(stderr,
706 					    gettext("ps: unknown zone %s\n"),
707 					    parg);
708 					continue;
709 				}
710 
711 				if (nzoneid == zoneidsz) {
712 					if ((zoneidsz *= 2) == 0)
713 						zoneidsz = SIZ;
714 					zoneid = Realloc(zoneid,
715 					    zoneidsz * sizeof (zoneid_t));
716 				}
717 				zoneid[nzoneid++] = id;
718 			} while (*p1);
719 			break;
720 		case 'Z':		/* show zone name */
721 			Zflg++;
722 			break;
723 		default:			/* error on ? */
724 			errflg++;
725 			break;
726 		}
727 
728 	if (errflg || optind < argc || pgerrflg)
729 		usage();
730 
731 	if (tflg)
732 		tty[ntty].tname = NULL;
733 	/*
734 	 * If an appropriate option has not been specified, use the
735 	 * current terminal and effective uid as the default.
736 	 */
737 	if (!(aflg|Aflg|dflg|Gflg|hflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) {
738 		psinfo_t info;
739 		int procfd;
740 		char *name;
741 		char pname[100];
742 
743 		/* get our own controlling tty name using /proc */
744 		(void) snprintf(pname, sizeof (pname),
745 		    "%s/self/psinfo", procdir);
746 		if ((procfd = open(pname, O_RDONLY)) < 0 ||
747 		    read(procfd, (char *)&info, sizeof (info)) < 0 ||
748 		    info.pr_ttydev == PRNODEV) {
749 			(void) fprintf(stderr,
750 			    gettext("ps: no controlling terminal\n"));
751 			exit(1);
752 		}
753 		(void) close(procfd);
754 
755 		i = 0;
756 		name = gettty(&info);
757 		if (*name == '?') {
758 			(void) fprintf(stderr,
759 			    gettext("ps: can't find controlling terminal\n"));
760 			exit(1);
761 		}
762 		if (ntty == ttysz) {
763 			if ((ttysz *= 2) == 0)
764 				ttysz = NTTYS;
765 			tty = Realloc(tty, (ttysz + 1) * sizeof (struct tty));
766 		}
767 		tty[ntty].tdev = info.pr_ttydev;
768 		tty[ntty++].tname = name;
769 		tty[ntty].tname = NULL;
770 		tflg++;
771 		tuid = getuid();
772 	}
773 	if (Aflg) {
774 		Gflg = Uflg = uflg = pflg = sflg = gflg = aflg = dflg = 0;
775 		zflg = hflg = 0;
776 	}
777 	if (Aflg | aflg | dflg)
778 		tflg = 0;
779 
780 	i = 0;		/* prepare to exit on name lookup errors */
781 	i += uconv(&euid_tbl);
782 	i += uconv(&ruid_tbl);
783 	i += gconv(&egid_tbl);
784 	i += gconv(&rgid_tbl);
785 	if (i)
786 		exit(1);
787 
788 	/* allocate a buffer for lwpsinfo structures */
789 	lpbufsize = 4096;
790 	if (Lflg && (lpsinfobuf = malloc(lpbufsize)) == NULL) {
791 		(void) fprintf(stderr,
792 		    gettext("ps: no memory\n"));
793 		exit(1);
794 	}
795 
796 	if (fields) {	/* print user-specified header */
797 		if (do_header) {
798 			struct field *f;
799 
800 			for (f = fields; f != NULL; f = f->next) {
801 				if (f != fields)
802 					(void) printf(" ");
803 				switch (f->fname) {
804 				case F_TTY:
805 					(void) printf("%-*s",
806 					    f->width, f->header);
807 					break;
808 				case F_LWPNAME:
809 				case F_FNAME:
810 				case F_COMM:
811 				case F_ARGS:
812 					/*
813 					 * Print these headers full width
814 					 * unless they appear at the end.
815 					 */
816 					if (f->next != NULL) {
817 						(void) printf("%-*s",
818 						    f->width, f->header);
819 					} else {
820 						(void) printf("%s",
821 						    f->header);
822 					}
823 					break;
824 				default:
825 					(void) printf("%*s",
826 					    f->width, f->header);
827 					break;
828 				}
829 			}
830 			(void) printf("\n");
831 		}
832 	} else {	/* print standard header */
833 		/*
834 		 * All fields before 'PID' are printed with a trailing space
835 		 * as a separator and that is how we print the headers too.
836 		 */
837 		if (lflg) {
838 			if (yflg)
839 				(void) printf("S ");
840 			else
841 				(void) printf(" F S ");
842 		}
843 		if (Zflg)
844 			(void) printf("    ZONE ");
845 		if (fflg) {
846 			(void) printf("     UID ");
847 		} else if (lflg)
848 			(void) printf("   UID ");
849 
850 		(void) printf("%*s", pidwidth,  "PID");
851 		if (lflg || fflg)
852 			(void) printf(" %*s", pidwidth, "PPID");
853 		if (jflg)
854 			(void) printf(" %*s %*s", pidwidth, "PGID",
855 			    pidwidth, "SID");
856 		if (Lflg)
857 			(void) printf("   LWP");
858 		if (Pflg)
859 			(void) printf(" PSR");
860 		if (Lflg && fflg)
861 			(void) printf("  NLWP");
862 		if (cflg)
863 			(void) printf("  CLS PRI");
864 		else if (lflg || fflg) {
865 			(void) printf("   C");
866 			if (lflg)
867 				(void) printf(" PRI NI");
868 		}
869 		if (lflg) {
870 			if (yflg)
871 				(void) printf("   RSS     SZ    WCHAN");
872 			else
873 				(void) printf("     ADDR     SZ    WCHAN");
874 		}
875 		if (fflg)
876 			(void) printf(" %s", loc_stime_str);
877 		if (Hflg)
878 			(void) printf(" LGRP");
879 		if (Lflg)
880 			(void) printf(" TTY        LTIME CMD\n");
881 		else
882 			(void) printf(" TTY         TIME CMD\n");
883 	}
884 
885 
886 	if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|hflg|tflg|gflg|sflg|zflg) &&
887 	    npid <= PTHRESHOLD) {
888 		/*
889 		 * If we are looking at specific processes go straight
890 		 * to their /proc entries and don't scan /proc.
891 		 */
892 		int i;
893 
894 		(void) qsort(pid, npid, sizeof (pid_t), pidcmp);
895 		for (i = 0; i < npid; i++) {
896 			char pname[12];
897 
898 			if (i >= 1 && pid[i] == pid[i - 1])
899 				continue;
900 			(void) sprintf(pname, "%d", (int)pid[i]);
901 			if (print_proc(pname) == 0)
902 				retcode = 0;
903 		}
904 	} else {
905 		/*
906 		 * Determine which processes to print info about by searching
907 		 * the /proc directory and looking at each process.
908 		 */
909 		if ((dirp = opendir(procdir)) == NULL) {
910 			(void) fprintf(stderr,
911 			    gettext("ps: cannot open PROC directory %s\n"),
912 			    procdir);
913 			exit(1);
914 		}
915 
916 		/* for each active process --- */
917 		while ((dentp = readdir(dirp)) != NULL) {
918 			if (dentp->d_name[0] == '.')    /* skip . and .. */
919 				continue;
920 			if (print_proc(dentp->d_name) == 0)
921 				retcode = 0;
922 		}
923 
924 		(void) closedir(dirp);
925 	}
926 	return (retcode);
927 }
928 
929 
930 int
931 print_proc(char *pid_name)
932 {
933 	char	pname[PATH_MAX];
934 	int	pdlen;
935 	int	found;
936 	int	procfd; /* filedescriptor for /proc/nnnnn/psinfo */
937 	char	*tp;    /* ptr to ttyname,  if any */
938 	psinfo_t info;  /* process information from /proc */
939 	lwpsinfo_t *lwpsinfo;   /* array of lwpsinfo structs */
940 
941 	pdlen = snprintf(pname, sizeof (pname), "%s/%s/", procdir, pid_name);
942 	if (pdlen >= sizeof (pname) - 10)
943 		return (1);
944 retry:
945 	(void) strcpy(&pname[pdlen], "psinfo");
946 	if ((procfd = open(pname, O_RDONLY)) == -1) {
947 		/* Process may have exited meanwhile. */
948 		return (1);
949 	}
950 	/*
951 	 * Get the info structure for the process and close quickly.
952 	 */
953 	if (read(procfd, (char *)&info, sizeof (info)) < 0) {
954 		int	saverr = errno;
955 
956 		(void) close(procfd);
957 		if (saverr == EAGAIN)
958 			goto retry;
959 		if (saverr != ENOENT)
960 			(void) fprintf(stderr,
961 			    gettext("ps: read() on %s: %s\n"),
962 			    pname, err_string(saverr));
963 		return (1);
964 	}
965 	(void) close(procfd);
966 
967 	found = 0;
968 	if (info.pr_lwp.pr_state == 0)	/* can't happen? */
969 		return (1);
970 
971 	/*
972 	 * Omit session group leaders for 'a' and 'd' options.
973 	 */
974 	if ((info.pr_pid == info.pr_sid) && (dflg || aflg))
975 		return (1);
976 	if (Aflg || dflg)
977 		found++;
978 	else if (pflg && search(pid, npid, info.pr_pid))
979 		found++;	/* ppid in p option arg list */
980 	else if (uflg && ugfind((id_t)info.pr_euid, &euid_tbl))
981 		found++;	/* puid in u option arg list */
982 	else if (Uflg && ugfind((id_t)info.pr_uid, &ruid_tbl))
983 		found++;	/* puid in U option arg list */
984 #ifdef NOT_YET
985 	else if (gflg && ugfind((id_t)info.pr_egid, &egid_tbl))
986 		found++;	/* pgid in g option arg list */
987 #endif	/* NOT_YET */
988 	else if (Gflg && ugfind((id_t)info.pr_gid, &rgid_tbl))
989 		found++;	/* pgid in G option arg list */
990 	else if (gflg && search(grpid, ngrpid, info.pr_pgid))
991 		found++;	/* grpid in g option arg list */
992 	else if (sflg && search(sessid, nsessid, info.pr_sid))
993 		found++;	/* sessid in s option arg list */
994 	else if (zflg && search(zoneid, nzoneid, info.pr_zoneid))
995 		found++;	/* zoneid in z option arg list */
996 	else if (hflg && search((pid_t *)lgrps, nlgrps, info.pr_lwp.pr_lgrp))
997 		found++;	/* home lgroup in h option arg list */
998 	if (!found && !tflg && !aflg)
999 		return (1);
1000 	if (!prfind(found, &info, &tp))
1001 		return (1);
1002 	if (Lflg && (info.pr_nlwp + info.pr_nzomb) > 1) {
1003 		ssize_t prsz;
1004 
1005 		(void) strcpy(&pname[pdlen], "lpsinfo");
1006 		if ((procfd = open(pname, O_RDONLY)) == -1)
1007 			return (1);
1008 		/*
1009 		 * Get the info structures for the lwps.
1010 		 */
1011 		prsz = read(procfd, lpsinfobuf, lpbufsize);
1012 		if (prsz == -1) {
1013 			int	saverr = errno;
1014 
1015 			(void) close(procfd);
1016 			if (saverr == EAGAIN)
1017 				goto retry;
1018 			if (saverr != ENOENT)
1019 				(void) fprintf(stderr,
1020 				    gettext("ps: read() on %s: %s\n"),
1021 				    pname, err_string(saverr));
1022 			return (1);
1023 		}
1024 		(void) close(procfd);
1025 		if (prsz == lpbufsize) {
1026 			/*
1027 			 * buffer overflow. Realloc new buffer.
1028 			 * Error handling is done in Realloc().
1029 			 */
1030 			lpbufsize *= 2;
1031 			lpsinfobuf = Realloc(lpsinfobuf, lpbufsize);
1032 			goto retry;
1033 		}
1034 		if (lpsinfobuf->pr_nent != (info.pr_nlwp + info.pr_nzomb))
1035 			goto retry;
1036 		lwpsinfo = (lwpsinfo_t *)(lpsinfobuf + 1);
1037 	}
1038 	if (!Lflg || (info.pr_nlwp + info.pr_nzomb) <= 1) {
1039 		prcom(&info, tp);
1040 	} else {
1041 		int nlwp = 0;
1042 
1043 		do {
1044 			info.pr_lwp = *lwpsinfo;
1045 			prcom(&info, tp);
1046 			/* LINTED improper alignment */
1047 			lwpsinfo = (lwpsinfo_t *)((char *)lwpsinfo +
1048 			    lpsinfobuf->pr_entsize);
1049 		} while (++nlwp < lpsinfobuf->pr_nent);
1050 	}
1051 	return (0);
1052 }
1053 
1054 static int
1055 field_cmp(const void *l, const void *r)
1056 {
1057 	struct def_field *lhs = *((struct def_field **)l);
1058 	struct def_field *rhs = *((struct def_field **)r);
1059 
1060 	return (strcmp(lhs->fname, rhs->fname));
1061 }
1062 
1063 static void
1064 usage(void)		/* print usage message and quit */
1065 {
1066 	struct def_field *df, *sorted[NFIELDS];
1067 	int pos = 80, i = 0;
1068 
1069 	static char usage1[] =
1070 	    "ps [ -aAdefHlcjLPWyZ ] [ -o format ] [ -t termlist ]";
1071 	static char usage2[] =
1072 	    "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]";
1073 	static char usage3[] =
1074 	    "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ]";
1075 	static char usage4[] =
1076 	    "\t[ -z zonelist ] [-h lgrplist]";
1077 	static char usage5[] =
1078 	    "  'format' is one or more of:";
1079 
1080 	(void) fprintf(stderr,
1081 	    gettext("usage: %s\n%s\n%s\n%s\n%s"),
1082 	    gettext(usage1), gettext(usage2), gettext(usage3),
1083 	    gettext(usage4), gettext(usage5));
1084 
1085 	/*
1086 	 * Now print out the possible output formats such that they neatly fit
1087 	 * into eighty columns.  Note that the fact that we are determining
1088 	 * this output programmatically means that a gettext() is impossible --
1089 	 * but it would be a mistake to localize the output formats anyway as
1090 	 * they are tokens for input, not output themselves.
1091 	 */
1092 	for (df = &fname[0]; df < &fname[NFIELDS]; df++)
1093 		sorted[i++] = df;
1094 
1095 	(void) qsort(sorted, NFIELDS, sizeof (void *), field_cmp);
1096 
1097 	for (i = 0; i < NFIELDS; i++) {
1098 		if (pos + strlen((df = sorted[i])->fname) + 1 >= 80) {
1099 			(void) fprintf(stderr, "\n\t");
1100 			pos = 8;
1101 		}
1102 
1103 		(void) fprintf(stderr, "%s%s", pos > 8 ? " " : "", df->fname);
1104 		pos += strlen(df->fname) + 1;
1105 	}
1106 
1107 	(void) fprintf(stderr, "\n");
1108 
1109 	exit(1);
1110 }
1111 
1112 /*
1113  * getarg() finds the next argument in list and copies arg into argbuf.
1114  * p1 first pts to arg passed back from getopt routine.  p1 is then
1115  * bumped to next character that is not a comma or blank -- p1 NULL
1116  * indicates end of list.
1117  */
1118 static char *
1119 getarg(char **pp1)
1120 {
1121 	static char argbuf[ARGSIZ];
1122 	char *p1 = *pp1;
1123 	char *parga = argbuf;
1124 	int c;
1125 
1126 	while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
1127 		p1++;
1128 
1129 	while ((c = *p1) != '\0' && c != ',' && !isspace(c)) {
1130 		if (parga < argbuf + ARGSIZ - 1)
1131 			*parga++ = c;
1132 		p1++;
1133 	}
1134 	*parga = '\0';
1135 
1136 	while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
1137 		p1++;
1138 
1139 	*pp1 = p1;
1140 
1141 	return (argbuf);
1142 }
1143 
1144 /*
1145  * parse_format() takes the argument to the -o option,
1146  * sets up the next output field structure, and returns
1147  * a pointer to any further output field specifier(s).
1148  * As a side-effect, it increments errflg if encounters a format error.
1149  */
1150 static char *
1151 parse_format(char *arg)
1152 {
1153 	int c;
1154 	char *name;
1155 	char *header = NULL;
1156 	int width = 0;
1157 	struct def_field *df;
1158 	struct field *f;
1159 
1160 	while ((c = *arg) != '\0' && (c == ',' || isspace(c)))
1161 		arg++;
1162 	if (c == '\0')
1163 		return (NULL);
1164 	name = arg;
1165 	arg = strpbrk(arg, " \t\r\v\f\n,=");
1166 	if (arg != NULL) {
1167 		c = *arg;
1168 		*arg++ = '\0';
1169 		if (c == '=') {
1170 			char *s;
1171 
1172 			header = arg;
1173 			arg = NULL;
1174 			width = strlen(header);
1175 			s = header + width;
1176 			while (s > header && isspace(*--s))
1177 				*s = '\0';
1178 			while (isspace(*header))
1179 				header++;
1180 		}
1181 	}
1182 	for (df = &fname[0]; df < &fname[NFIELDS]; df++)
1183 		if (strcmp(name, df->fname) == 0) {
1184 			if (strcmp(name, "lwp") == 0 ||
1185 			    strcmp(name, "lwpname") == 0)
1186 				Lflg++;
1187 			break;
1188 		}
1189 	if (df >= &fname[NFIELDS]) {
1190 		(void) fprintf(stderr,
1191 		    gettext("ps: unknown output format: -o %s\n"),
1192 		    name);
1193 		errflg++;
1194 		return (arg);
1195 	}
1196 	if ((f = malloc(sizeof (*f))) == NULL) {
1197 		(void) fprintf(stderr,
1198 		    gettext("ps: malloc() for output format failed, %s\n"),
1199 		    err_string(errno));
1200 		exit(1);
1201 	}
1202 	f->next = NULL;
1203 	f->fname = df - &fname[0];
1204 	f->header = header? header : df->header;
1205 	if (width == 0)
1206 		width = df->width;
1207 	if (*f->header != '\0')
1208 		do_header = 1;
1209 	f->width = max(width, df->minwidth);
1210 
1211 	if (fields == NULL)
1212 		fields = last_field = f;
1213 	else {
1214 		last_field->next = f;
1215 		last_field = f;
1216 	}
1217 
1218 	return (arg);
1219 }
1220 
1221 static char *
1222 devlookup(dev_t ddev)
1223 {
1224 	struct devl *dp;
1225 	int i;
1226 
1227 	for (dp = devl, i = 0; i < ndev; dp++, i++) {
1228 		if (dp->ddev == ddev)
1229 			return (dp->dname);
1230 	}
1231 	return (NULL);
1232 }
1233 
1234 static char *
1235 devadd(char *name, dev_t ddev)
1236 {
1237 	struct devl *dp;
1238 	int leng, start, i;
1239 
1240 	if (ndev == maxdev) {
1241 		maxdev += DNINCR;
1242 		devl = Realloc(devl, maxdev * sizeof (struct devl));
1243 	}
1244 	dp = &devl[ndev++];
1245 
1246 	dp->ddev = ddev;
1247 	if (name == NULL) {
1248 		(void) strcpy(dp->dname, "??");
1249 		return (dp->dname);
1250 	}
1251 
1252 	leng = strlen(name);
1253 	/* Strip off /dev/ */
1254 	if (leng < DNSIZE + 4)
1255 		(void) strcpy(dp->dname, &name[5]);
1256 	else {
1257 		start = leng - DNSIZE - 1;
1258 
1259 		for (i = start; i < leng && name[i] != '/'; i++)
1260 				;
1261 		if (i == leng)
1262 			(void) strncpy(dp->dname, &name[start], DNSIZE);
1263 		else
1264 			(void) strncpy(dp->dname, &name[i+1], DNSIZE);
1265 	}
1266 	return (dp->dname);
1267 }
1268 
1269 /*
1270  * gettty returns the user's tty number or ? if none.
1271  */
1272 static char *
1273 gettty(psinfo_t *psinfo)
1274 {
1275 	extern char *_ttyname_dev(dev_t, char *, size_t);
1276 	static zoneid_t zid = -1;
1277 	char devname[TTYNAME_MAX];
1278 	char *retval;
1279 
1280 	if (zid == -1)
1281 		zid = getzoneid();
1282 
1283 	if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid)
1284 		return ("?");
1285 
1286 	if ((retval = devlookup(psinfo->pr_ttydev)) != NULL)
1287 		return (retval);
1288 
1289 	retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname));
1290 
1291 	return (devadd(retval, psinfo->pr_ttydev));
1292 }
1293 
1294 /*
1295  * Find the process's tty and return 1 if process is to be printed.
1296  */
1297 static int
1298 prfind(int found, psinfo_t *psinfo, char **tpp)
1299 {
1300 	char	*tp;
1301 	struct tty *ttyp;
1302 
1303 	if (psinfo->pr_nlwp == 0) {
1304 		/* process is a zombie */
1305 		*tpp = "?";
1306 		if (tflg && !found)
1307 			return (0);
1308 		return (1);
1309 	}
1310 
1311 	/*
1312 	 * Get current terminal.  If none ("?") and 'a' is set, don't print
1313 	 * info.  If 't' is set, check if term is in list of desired terminals
1314 	 * and print it if it is.
1315 	 */
1316 	tp = gettty(psinfo);
1317 	if (aflg && *tp == '?') {
1318 		*tpp = tp;
1319 		return (0);
1320 	}
1321 	if (tflg && !found) {
1322 		int match = 0;
1323 		char *other = NULL;
1324 		for (ttyp = tty; ttyp->tname != NULL; ttyp++) {
1325 			/*
1326 			 * Look for a name match
1327 			 */
1328 			if (strcmp(tp, ttyp->tname) == 0) {
1329 				match = 1;
1330 				break;
1331 			}
1332 			/*
1333 			 * Look for same device under different names.
1334 			 */
1335 			if ((other == NULL) &&
1336 			    (ttyp->tdev != PRNODEV) &&
1337 			    (psinfo->pr_ttydev == ttyp->tdev))
1338 				other = ttyp->tname;
1339 		}
1340 		if (!match && (other != NULL)) {
1341 			/*
1342 			 * found under a different name
1343 			 */
1344 			match = 1;
1345 			tp = other;
1346 		}
1347 		if (!match || (tuid != (uid_t)-1 && tuid != psinfo->pr_euid)) {
1348 			/*
1349 			 * not found OR not matching euid
1350 			 */
1351 			*tpp = tp;
1352 			return (0);
1353 		}
1354 	}
1355 	*tpp = tp;
1356 	return (1);
1357 }
1358 
1359 /*
1360  * Print info about the process.
1361  */
1362 static void
1363 prcom(psinfo_t *psinfo, char *ttyp)
1364 {
1365 	char	*cp;
1366 	long	tm;
1367 	int	bytesleft;
1368 	int	wcnt, length;
1369 	wchar_t	wchar;
1370 	struct passwd *pwd;
1371 	int	zombie_lwp;
1372 	char	zonename[ZONENAME_MAX];
1373 
1374 	/*
1375 	 * If process is zombie, call zombie print routine and return.
1376 	 */
1377 	if (psinfo->pr_nlwp == 0) {
1378 		if (fields != NULL)
1379 			pr_fields(psinfo, ttyp, print_zombie_field);
1380 		else
1381 			przom(psinfo);
1382 		return;
1383 	}
1384 
1385 	zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');
1386 
1387 	/*
1388 	 * If user specified '-o format', print requested fields and return.
1389 	 */
1390 	if (fields != NULL) {
1391 		pr_fields(psinfo, ttyp, print_field);
1392 		return;
1393 	}
1394 
1395 	/*
1396 	 * All fields before 'PID' are printed with a trailing space as a
1397 	 * separator, rather than keeping track of which column is first.  All
1398 	 * other fields are printed with a leading space.
1399 	 */
1400 	if (lflg) {
1401 		if (!yflg)
1402 			(void) printf("%2x ", psinfo->pr_flag & 0377); /* F */
1403 		(void) printf("%c ", psinfo->pr_lwp.pr_sname);	/* S */
1404 	}
1405 
1406 	if (Zflg) {						/* ZONE */
1407 		if (getzonenamebyid(psinfo->pr_zoneid, zonename,
1408 		    sizeof (zonename)) < 0) {
1409 			if (snprintf(NULL, 0, "%d",
1410 			    ((int)psinfo->pr_zoneid)) > 7)
1411 				(void) printf(" %6.6d%c ",
1412 				    ((int)psinfo->pr_zoneid), '*');
1413 			else
1414 				(void) printf(" %7.7d ",
1415 				    ((int)psinfo->pr_zoneid));
1416 		} else {
1417 			size_t nw;
1418 
1419 			nw = mbstowcs(NULL, zonename, 0);
1420 			if (nw == (size_t)-1)
1421 				(void) printf("%8.8s ", "ERROR");
1422 			else if (nw > 8)
1423 				(void) wprintf(L"%7.7s%c ", zonename, '*');
1424 			else
1425 				(void) wprintf(L"%8.8s ", zonename);
1426 		}
1427 	}
1428 
1429 	if (fflg) {						/* UID */
1430 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
1431 			size_t nw;
1432 
1433 			nw = mbstowcs(NULL, pwd->pw_name, 0);
1434 			if (nw == (size_t)-1)
1435 				(void) printf("%8.8s ", "ERROR");
1436 			else if (nw > 8)
1437 				(void) wprintf(L"%7.7s%c ", pwd->pw_name, '*');
1438 			else
1439 				(void) wprintf(L"%8.8s ", pwd->pw_name);
1440 		} else {
1441 			if (snprintf(NULL, 0, "%u",
1442 			    (psinfo->pr_euid)) > 7)
1443 				(void) printf(" %6.6u%c ", psinfo->pr_euid,
1444 				    '*');
1445 			else
1446 				(void) printf(" %7.7u ", psinfo->pr_euid);
1447 		}
1448 	} else if (lflg) {
1449 		if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6)
1450 			(void) printf("%5.5u%c ", psinfo->pr_euid, '*');
1451 		else
1452 			(void) printf("%6u ", psinfo->pr_euid);
1453 	}
1454 	(void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */
1455 	if (lflg || fflg)
1456 		(void) printf(" %*d", pidwidth,
1457 		    (int)psinfo->pr_ppid); /* PPID */
1458 	if (jflg) {
1459 		(void) printf(" %*d", pidwidth,
1460 		    (int)psinfo->pr_pgid);	/* PGID */
1461 		(void) printf(" %*d", pidwidth,
1462 		    (int)psinfo->pr_sid);	/* SID  */
1463 	}
1464 	if (Lflg)
1465 		(void) printf(" %5d", (int)psinfo->pr_lwp.pr_lwpid); /* LWP */
1466 	if (Pflg) {
1467 		if (psinfo->pr_lwp.pr_bindpro == PBIND_NONE)	/* PSR */
1468 			(void) printf("   -");
1469 		else
1470 			(void) printf(" %3d", psinfo->pr_lwp.pr_bindpro);
1471 	}
1472 	if (Lflg && fflg)					/* NLWP */
1473 		(void) printf(" %5d", psinfo->pr_nlwp + psinfo->pr_nzomb);
1474 	if (cflg) {
1475 		if (zombie_lwp)					/* CLS */
1476 			(void) printf("     ");
1477 		else
1478 			(void) printf(" %4s", psinfo->pr_lwp.pr_clname);
1479 		(void) printf(" %3d", psinfo->pr_lwp.pr_pri);	/* PRI */
1480 	} else if (lflg || fflg) {
1481 		(void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C   */
1482 		if (lflg) {					    /* PRI NI */
1483 			/*
1484 			 * Print priorities the old way (lower numbers
1485 			 * mean higher priority) and print nice value
1486 			 * for time sharing procs.
1487 			 */
1488 			(void) printf(" %3d", psinfo->pr_lwp.pr_oldpri);
1489 			if (psinfo->pr_lwp.pr_oldpri != 0)
1490 				(void) printf(" %2d", psinfo->pr_lwp.pr_nice);
1491 			else
1492 				(void) printf(" %2.2s",
1493 				    psinfo->pr_lwp.pr_clname);
1494 		}
1495 	}
1496 	if (lflg) {
1497 		if (yflg) {
1498 			if (psinfo->pr_flag & SSYS)		/* RSS */
1499 				(void) printf("     0");
1500 			else if (psinfo->pr_rssize)
1501 				(void) printf(" %5lu",
1502 				    (ulong_t)psinfo->pr_rssize);
1503 			else
1504 				(void) printf("     ?");
1505 			if (psinfo->pr_flag & SSYS)		/* SZ */
1506 				(void) printf("      0");
1507 			else if (psinfo->pr_size)
1508 				(void) printf(" %6lu",
1509 				    (ulong_t)psinfo->pr_size);
1510 			else
1511 				(void) printf("      ?");
1512 		} else {
1513 #ifndef _LP64
1514 			if (psinfo->pr_addr)			/* ADDR */
1515 				(void) printf(" %8lx",
1516 				    (ulong_t)psinfo->pr_addr);
1517 			else
1518 #endif
1519 				(void) printf("        ?");
1520 			if (psinfo->pr_flag & SSYS)		/* SZ */
1521 				(void) printf("      0");
1522 			else if (psinfo->pr_size)
1523 				(void) printf(" %6lu",
1524 				    (ulong_t)psinfo->pr_size / kbytes_per_page);
1525 			else
1526 				(void) printf("      ?");
1527 		}
1528 		if (psinfo->pr_lwp.pr_sname != 'S')		/* WCHAN */
1529 			(void) printf("         ");
1530 #ifndef _LP64
1531 		else if (psinfo->pr_lwp.pr_wchan)
1532 			(void) printf(" %8lx",
1533 			    (ulong_t)psinfo->pr_lwp.pr_wchan);
1534 #endif
1535 		else
1536 			(void) printf("        ?");
1537 	}
1538 	if (fflg) {						/* STIME */
1539 		int width = fname[F_STIME].width;
1540 		if (Lflg)
1541 			prtime(psinfo->pr_lwp.pr_start, width + 1, 1);
1542 		else
1543 			prtime(psinfo->pr_start, width + 1, 1);
1544 	}
1545 
1546 	if (Hflg) {
1547 		/* Display home lgroup */
1548 		(void) printf(" %4d", (int)psinfo->pr_lwp.pr_lgrp);
1549 	}
1550 
1551 	(void) printf(" %-8.14s", ttyp);			/* TTY */
1552 	if (Lflg) {
1553 		tm = psinfo->pr_lwp.pr_time.tv_sec;
1554 		if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000)
1555 			tm++;
1556 	} else {
1557 		tm = psinfo->pr_time.tv_sec;
1558 		if (psinfo->pr_time.tv_nsec > 500000000)
1559 			tm++;
1560 	}
1561 	(void) printf(" %4ld:%.2ld", tm / 60, tm % 60);		/* [L]TIME */
1562 
1563 	if (zombie_lwp) {
1564 		(void) printf(" <defunct>\n");
1565 		return;
1566 	}
1567 
1568 	if (!fflg) {						/* CMD */
1569 		wcnt = namencnt(psinfo->pr_fname, 16, 8);
1570 		(void) printf(" %.*s\n", wcnt, psinfo->pr_fname);
1571 		return;
1572 	}
1573 
1574 
1575 	/*
1576 	 * PRARGSZ == length of cmd arg string.
1577 	 */
1578 	psinfo->pr_psargs[PRARGSZ-1] = '\0';
1579 	bytesleft = PRARGSZ;
1580 	for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
1581 		length = mbtowc(&wchar, cp, MB_LEN_MAX);
1582 		if (length == 0)
1583 			break;
1584 		if (length < 0 || !iswprint(wchar)) {
1585 			if (length < 0)
1586 				length = 1;
1587 			if (bytesleft <= length) {
1588 				*cp = '\0';
1589 				break;
1590 			}
1591 			/* omit the unprintable character */
1592 			(void) memmove(cp, cp+length, bytesleft-length);
1593 			length = 0;
1594 		}
1595 		bytesleft -= length;
1596 	}
1597 	wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ);
1598 	(void) printf(" %.*s\n", wcnt, psinfo->pr_psargs);
1599 }
1600 
1601 /*
1602  * Print percent from 16-bit binary fraction [0 .. 1]
1603  * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
1604  */
1605 static void
1606 prtpct(ushort_t pct, int width)
1607 {
1608 	uint_t value = pct;	/* need 32 bits to compute with */
1609 
1610 	value = ((value * 1000) + 0x7000) >> 15;	/* [0 .. 1000] */
1611 	if (value >= 1000)
1612 		value = 999;
1613 	if ((width -= 2) < 2)
1614 		width = 2;
1615 	(void) printf("%*u.%u", width, value / 10, value % 10);
1616 }
1617 
1618 static void
1619 print_time(time_t tim, int width)
1620 {
1621 	char buf[30];
1622 	time_t seconds;
1623 	time_t minutes;
1624 	time_t hours;
1625 	time_t days;
1626 
1627 	if (tim < 0) {
1628 		(void) printf("%*s", width, "-");
1629 		return;
1630 	}
1631 
1632 	seconds = tim % 60;
1633 	tim /= 60;
1634 	minutes = tim % 60;
1635 	tim /= 60;
1636 	hours = tim % 24;
1637 	days = tim / 24;
1638 
1639 	if (days > 0) {
1640 		(void) snprintf(buf, sizeof (buf), "%ld-%2.2ld:%2.2ld:%2.2ld",
1641 		    days, hours, minutes, seconds);
1642 	} else if (hours > 0) {
1643 		(void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld:%2.2ld",
1644 		    hours, minutes, seconds);
1645 	} else {
1646 		(void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld",
1647 		    minutes, seconds);
1648 	}
1649 
1650 	(void) printf("%*s", width, buf);
1651 }
1652 
1653 static void
1654 print_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
1655 {
1656 	int width = f->width;
1657 	struct passwd *pwd;
1658 	struct group *grp;
1659 	time_t cputime;
1660 	int bytesleft;
1661 	int wcnt;
1662 	wchar_t	wchar;
1663 	char *cp;
1664 	int length;
1665 	ulong_t mask;
1666 	char c = '\0', *csave = NULL;
1667 	int zombie_lwp;
1668 
1669 	zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');
1670 
1671 	switch (f->fname) {
1672 	case F_RUSER:
1673 		if ((pwd = getpwuid(psinfo->pr_uid)) != NULL) {
1674 			size_t nw;
1675 
1676 			nw = mbstowcs(NULL, pwd->pw_name, 0);
1677 			if (nw == (size_t)-1)
1678 				(void) printf("%*s ", width, "ERROR");
1679 			else if (Wflg && nw > width)
1680 				(void) wprintf(L"%.*s%c", width - 1,
1681 				    pwd->pw_name, '*');
1682 			else
1683 				(void) wprintf(L"%*s", width, pwd->pw_name);
1684 		} else {
1685 			if (Wflg && snprintf(NULL, 0, "%u",
1686 			    (psinfo->pr_uid)) > width)
1687 
1688 				(void) printf("%*u%c", width - 1,
1689 				    psinfo->pr_uid, '*');
1690 			else
1691 				(void) printf("%*u", width, psinfo->pr_uid);
1692 		}
1693 		break;
1694 	case F_USER:
1695 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
1696 			size_t nw;
1697 
1698 			nw = mbstowcs(NULL, pwd->pw_name, 0);
1699 			if (nw == (size_t)-1)
1700 				(void) printf("%*s ", width, "ERROR");
1701 			else if (Wflg && nw > width)
1702 				(void) wprintf(L"%.*s%c", width - 1,
1703 				    pwd->pw_name, '*');
1704 			else
1705 				(void) wprintf(L"%*s", width, pwd->pw_name);
1706 		} else {
1707 			if (Wflg && snprintf(NULL, 0, "%u",
1708 			    (psinfo->pr_euid)) > width)
1709 
1710 				(void) printf("%*u%c", width - 1,
1711 				    psinfo->pr_euid, '*');
1712 			else
1713 				(void) printf("%*u", width, psinfo->pr_euid);
1714 		}
1715 		break;
1716 	case F_RGROUP:
1717 		if ((grp = getgrgid(psinfo->pr_gid)) != NULL)
1718 			(void) printf("%*s", width, grp->gr_name);
1719 		else
1720 			(void) printf("%*u", width, psinfo->pr_gid);
1721 		break;
1722 	case F_GROUP:
1723 		if ((grp = getgrgid(psinfo->pr_egid)) != NULL)
1724 			(void) printf("%*s", width, grp->gr_name);
1725 		else
1726 			(void) printf("%*u", width, psinfo->pr_egid);
1727 		break;
1728 	case F_RUID:
1729 		(void) printf("%*u", width, psinfo->pr_uid);
1730 		break;
1731 	case F_UID:
1732 		(void) printf("%*u", width, psinfo->pr_euid);
1733 		break;
1734 	case F_RGID:
1735 		(void) printf("%*u", width, psinfo->pr_gid);
1736 		break;
1737 	case F_GID:
1738 		(void) printf("%*u", width, psinfo->pr_egid);
1739 		break;
1740 	case F_PID:
1741 		(void) printf("%*d", width, (int)psinfo->pr_pid);
1742 		break;
1743 	case F_PPID:
1744 		(void) printf("%*d", width, (int)psinfo->pr_ppid);
1745 		break;
1746 	case F_PGID:
1747 		(void) printf("%*d", width, (int)psinfo->pr_pgid);
1748 		break;
1749 	case F_SID:
1750 		(void) printf("%*d", width, (int)psinfo->pr_sid);
1751 		break;
1752 	case F_PSR:
1753 		if (zombie_lwp || psinfo->pr_lwp.pr_bindpro == PBIND_NONE)
1754 			(void) printf("%*s", width, "-");
1755 		else
1756 			(void) printf("%*d", width, psinfo->pr_lwp.pr_bindpro);
1757 		break;
1758 	case F_LWP:
1759 		(void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lwpid);
1760 		break;
1761 	case F_LWPNAME: {
1762 		char lwpname[THREAD_NAME_MAX] = "";
1763 		char *path = NULL;
1764 		int fd;
1765 
1766 		if (asprintf(&path, "%s/%d/lwp/%d/lwpname", procdir,
1767 		    (int)psinfo->pr_pid, (int)psinfo->pr_lwp.pr_lwpid) != -1 &&
1768 		    (fd = open(path, O_RDONLY)) != -1) {
1769 			(void) read(fd, lwpname, sizeof (lwpname));
1770 			lwpname[THREAD_NAME_MAX - 1] = '\0';
1771 			(void) close(fd);
1772 		}
1773 
1774 		free(path);
1775 
1776 		if (f->next != NULL)
1777 			(void) printf("%-*s", width, lwpname);
1778 		else
1779 			(void) printf("%s", lwpname);
1780 		break;
1781 	}
1782 	case F_NLWP:
1783 		(void) printf("%*d", width, psinfo->pr_nlwp + psinfo->pr_nzomb);
1784 		break;
1785 	case F_OPRI:
1786 		if (zombie_lwp)
1787 			(void) printf("%*s", width, "-");
1788 		else
1789 			(void) printf("%*d", width, psinfo->pr_lwp.pr_oldpri);
1790 		break;
1791 	case F_PRI:
1792 		if (zombie_lwp)
1793 			(void) printf("%*s", width, "-");
1794 		else
1795 			(void) printf("%*d", width, psinfo->pr_lwp.pr_pri);
1796 		break;
1797 	case F_F:
1798 		mask = 0xffffffffUL;
1799 		if (width < 8)
1800 			mask >>= (8 - width) * 4;
1801 		(void) printf("%*lx", width, psinfo->pr_flag & mask);
1802 		break;
1803 	case F_S:
1804 		(void) printf("%*c", width, psinfo->pr_lwp.pr_sname);
1805 		break;
1806 	case F_C:
1807 		if (zombie_lwp)
1808 			(void) printf("%*s", width, "-");
1809 		else
1810 			(void) printf("%*d", width, psinfo->pr_lwp.pr_cpu);
1811 		break;
1812 	case F_PCPU:
1813 		if (zombie_lwp)
1814 			(void) printf("%*s", width, "-");
1815 		else if (Lflg)
1816 			prtpct(psinfo->pr_lwp.pr_pctcpu, width);
1817 		else
1818 			prtpct(psinfo->pr_pctcpu, width);
1819 		break;
1820 	case F_PMEM:
1821 		prtpct(psinfo->pr_pctmem, width);
1822 		break;
1823 	case F_OSZ:
1824 		(void) printf("%*lu", width,
1825 		    (ulong_t)psinfo->pr_size / kbytes_per_page);
1826 		break;
1827 	case F_VSZ:
1828 		(void) printf("%*lu", width, (ulong_t)psinfo->pr_size);
1829 		break;
1830 	case F_RSS:
1831 		(void) printf("%*lu", width, (ulong_t)psinfo->pr_rssize);
1832 		break;
1833 	case F_NICE:
1834 		/* if pr_oldpri is zero, then this class has no nice */
1835 		if (zombie_lwp)
1836 			(void) printf("%*s", width, "-");
1837 		else if (psinfo->pr_lwp.pr_oldpri != 0)
1838 			(void) printf("%*d", width, psinfo->pr_lwp.pr_nice);
1839 		else
1840 			(void) printf("%*.*s", width, width,
1841 			    psinfo->pr_lwp.pr_clname);
1842 		break;
1843 	case F_CLASS:
1844 		if (zombie_lwp)
1845 			(void) printf("%*s", width, "-");
1846 		else
1847 			(void) printf("%*.*s", width, width,
1848 			    psinfo->pr_lwp.pr_clname);
1849 		break;
1850 	case F_STIME:
1851 		if (Lflg)
1852 			prtime(psinfo->pr_lwp.pr_start, width, 0);
1853 		else
1854 			prtime(psinfo->pr_start, width, 0);
1855 		break;
1856 	case F_ETIME:
1857 		if (Lflg)
1858 			print_time(delta_secs(&psinfo->pr_lwp.pr_start),
1859 			    width);
1860 		else
1861 			print_time(delta_secs(&psinfo->pr_start), width);
1862 		break;
1863 	case F_TIME:
1864 		if (Lflg) {
1865 			cputime = psinfo->pr_lwp.pr_time.tv_sec;
1866 			if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000)
1867 				cputime++;
1868 		} else {
1869 			cputime = psinfo->pr_time.tv_sec;
1870 			if (psinfo->pr_time.tv_nsec > 500000000)
1871 				cputime++;
1872 		}
1873 		print_time(cputime, width);
1874 		break;
1875 	case F_TTY:
1876 		(void) printf("%-*s", width, ttyp);
1877 		break;
1878 	case F_ADDR:
1879 		if (zombie_lwp)
1880 			(void) printf("%*s", width, "-");
1881 		else if (Lflg)
1882 			(void) printf("%*lx", width,
1883 			    (long)psinfo->pr_lwp.pr_addr);
1884 		else
1885 			(void) printf("%*lx", width, (long)psinfo->pr_addr);
1886 		break;
1887 	case F_WCHAN:
1888 		if (!zombie_lwp && psinfo->pr_lwp.pr_wchan)
1889 			(void) printf("%*lx", width,
1890 			    (long)psinfo->pr_lwp.pr_wchan);
1891 		else
1892 			(void) printf("%*.*s", width, width, "-");
1893 		break;
1894 	case F_FNAME:
1895 		/*
1896 		 * Print full width unless this is the last output format.
1897 		 */
1898 		if (zombie_lwp) {
1899 			if (f->next != NULL)
1900 				(void) printf("%-*s", width, "<defunct>");
1901 			else
1902 				(void) printf("%s", "<defunct>");
1903 			break;
1904 		}
1905 		wcnt = namencnt(psinfo->pr_fname, 16, width);
1906 		if (f->next != NULL)
1907 			(void) printf("%-*.*s", width, wcnt, psinfo->pr_fname);
1908 		else
1909 			(void) printf("%-.*s", wcnt, psinfo->pr_fname);
1910 		break;
1911 	case F_COMM:
1912 		if (zombie_lwp) {
1913 			if (f->next != NULL)
1914 				(void) printf("%-*s", width, "<defunct>");
1915 			else
1916 				(void) printf("%s", "<defunct>");
1917 			break;
1918 		}
1919 		csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n");
1920 		if (csave) {
1921 			c = *csave;
1922 			*csave = '\0';
1923 		}
1924 		/* FALLTHROUGH */
1925 	case F_ARGS:
1926 		/*
1927 		 * PRARGSZ == length of cmd arg string.
1928 		 */
1929 		if (zombie_lwp) {
1930 			(void) printf("%-*s", width, "<defunct>");
1931 			break;
1932 		}
1933 		psinfo->pr_psargs[PRARGSZ-1] = '\0';
1934 		bytesleft = PRARGSZ;
1935 		for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
1936 			length = mbtowc(&wchar, cp, MB_LEN_MAX);
1937 			if (length == 0)
1938 				break;
1939 			if (length < 0 || !iswprint(wchar)) {
1940 				if (length < 0)
1941 					length = 1;
1942 				if (bytesleft <= length) {
1943 					*cp = '\0';
1944 					break;
1945 				}
1946 				/* omit the unprintable character */
1947 				(void) memmove(cp, cp+length, bytesleft-length);
1948 				length = 0;
1949 			}
1950 			bytesleft -= length;
1951 		}
1952 		wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width);
1953 		/*
1954 		 * Print full width unless this is the last format.
1955 		 */
1956 		if (f->next != NULL)
1957 			(void) printf("%-*.*s", width, wcnt,
1958 			    psinfo->pr_psargs);
1959 		else
1960 			(void) printf("%-.*s", wcnt,
1961 			    psinfo->pr_psargs);
1962 		if (f->fname == F_COMM && csave)
1963 			*csave = c;
1964 		break;
1965 	case F_TASKID:
1966 		(void) printf("%*d", width, (int)psinfo->pr_taskid);
1967 		break;
1968 	case F_PROJID:
1969 		(void) printf("%*d", width, (int)psinfo->pr_projid);
1970 		break;
1971 	case F_PROJECT:
1972 		{
1973 			struct project cproj;
1974 			char proj_buf[PROJECT_BUFSZ];
1975 
1976 			if ((getprojbyid(psinfo->pr_projid, &cproj,
1977 			    (void *)&proj_buf, PROJECT_BUFSZ)) == NULL) {
1978 				if (Wflg && snprintf(NULL, 0, "%d",
1979 				    ((int)psinfo->pr_projid)) > width)
1980 					(void) printf("%.*d%c", width - 1,
1981 					    ((int)psinfo->pr_projid), '*');
1982 				else
1983 					(void) printf("%*d", width,
1984 					    (int)psinfo->pr_projid);
1985 			} else {
1986 				size_t nw;
1987 
1988 				if (cproj.pj_name != NULL)
1989 					nw = mbstowcs(NULL, cproj.pj_name, 0);
1990 				if (cproj.pj_name == NULL)
1991 					(void) printf("%*s ", width, "---");
1992 				else if (nw == (size_t)-1)
1993 					(void) printf("%*s ", width, "ERROR");
1994 				else if (Wflg && nw > width)
1995 					(void) wprintf(L"%.*s%c", width - 1,
1996 					    cproj.pj_name, '*');
1997 				else
1998 					(void) wprintf(L"%*s", width,
1999 					    cproj.pj_name);
2000 			}
2001 		}
2002 		break;
2003 	case F_PSET:
2004 		if (zombie_lwp || psinfo->pr_lwp.pr_bindpset == PS_NONE)
2005 			(void) printf("%*s", width, "-");
2006 		else
2007 			(void) printf("%*d", width, psinfo->pr_lwp.pr_bindpset);
2008 		break;
2009 	case F_ZONEID:
2010 		(void) printf("%*d", width, (int)psinfo->pr_zoneid);
2011 		break;
2012 	case F_ZONE:
2013 		{
2014 			char zonename[ZONENAME_MAX];
2015 
2016 			if (getzonenamebyid(psinfo->pr_zoneid, zonename,
2017 			    sizeof (zonename)) < 0) {
2018 				if (Wflg && snprintf(NULL, 0, "%d",
2019 				    ((int)psinfo->pr_zoneid)) > width)
2020 					(void) printf("%.*d%c", width - 1,
2021 					    ((int)psinfo->pr_zoneid), '*');
2022 				else
2023 					(void) printf("%*d", width,
2024 					    (int)psinfo->pr_zoneid);
2025 			} else {
2026 				size_t nw;
2027 
2028 				nw = mbstowcs(NULL, zonename, 0);
2029 				if (nw == (size_t)-1)
2030 					(void) printf("%*s ", width, "ERROR");
2031 				else if (Wflg && nw > width)
2032 					(void) wprintf(L"%.*s%c", width - 1,
2033 					    zonename, '*');
2034 				else
2035 					(void) wprintf(L"%*s", width, zonename);
2036 			}
2037 		}
2038 		break;
2039 	case F_CTID:
2040 		if (psinfo->pr_contract == -1)
2041 			(void) printf("%*s", width, "-");
2042 		else
2043 			(void) printf("%*ld", width, (long)psinfo->pr_contract);
2044 		break;
2045 	case F_LGRP:
2046 		/* Display home lgroup */
2047 		(void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lgrp);
2048 		break;
2049 
2050 	case F_DMODEL:
2051 		(void) printf("%*s", width,
2052 		    psinfo->pr_dmodel == PR_MODEL_LP64 ? "_LP64" : "_ILP32");
2053 		break;
2054 	}
2055 }
2056 
2057 static void
2058 print_zombie_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
2059 {
2060 	int wcnt;
2061 	int width = f->width;
2062 
2063 	switch (f->fname) {
2064 	case F_FNAME:
2065 	case F_COMM:
2066 	case F_ARGS:
2067 		/*
2068 		 * Print full width unless this is the last output format.
2069 		 */
2070 		wcnt = min(width, sizeof ("<defunct>"));
2071 		if (f->next != NULL)
2072 			(void) printf("%-*.*s", width, wcnt, "<defunct>");
2073 		else
2074 			(void) printf("%-.*s", wcnt, "<defunct>");
2075 		break;
2076 
2077 	case F_PSR:
2078 	case F_PCPU:
2079 	case F_PMEM:
2080 	case F_NICE:
2081 	case F_CLASS:
2082 	case F_STIME:
2083 	case F_ETIME:
2084 	case F_WCHAN:
2085 	case F_PSET:
2086 		(void) printf("%*s", width, "-");
2087 		break;
2088 
2089 	case F_OPRI:
2090 	case F_PRI:
2091 	case F_OSZ:
2092 	case F_VSZ:
2093 	case F_RSS:
2094 		(void) printf("%*d", width, 0);
2095 		break;
2096 
2097 	default:
2098 		print_field(psinfo, f, ttyp);
2099 		break;
2100 	}
2101 }
2102 
2103 static void
2104 pr_fields(psinfo_t *psinfo, const char *ttyp,
2105     void (*print_fld)(psinfo_t *, struct field *, const char *))
2106 {
2107 	struct field *f;
2108 
2109 	for (f = fields; f != NULL; f = f->next) {
2110 		print_fld(psinfo, f, ttyp);
2111 		if (f->next != NULL)
2112 			(void) printf(" ");
2113 	}
2114 	(void) printf("\n");
2115 }
2116 
2117 /*
2118  * Returns 1 if arg is found in array arr, of length num; 0 otherwise.
2119  */
2120 static int
2121 search(pid_t *arr, int number, pid_t arg)
2122 {
2123 	int i;
2124 
2125 	for (i = 0; i < number; i++)
2126 		if (arg == arr[i])
2127 			return (1);
2128 	return (0);
2129 }
2130 
2131 /*
2132  * Add an entry (user, group) to the specified table.
2133  */
2134 static void
2135 add_ugentry(struct ughead *tbl, char *name)
2136 {
2137 	struct ugdata *entp;
2138 
2139 	if (tbl->size == tbl->nent) {	/* reallocate the table entries */
2140 		if ((tbl->size *= 2) == 0)
2141 			tbl->size = 32;		/* first time */
2142 		tbl->ent = Realloc(tbl->ent, tbl->size*sizeof (struct ugdata));
2143 	}
2144 	entp = &tbl->ent[tbl->nent++];
2145 	entp->id = 0;
2146 	(void) strncpy(entp->name, name, MAXUGNAME);
2147 	entp->name[MAXUGNAME] = '\0';
2148 }
2149 
2150 static int
2151 uconv(struct ughead *uhead)
2152 {
2153 	struct ugdata *utbl = uhead->ent;
2154 	int n = uhead->nent;
2155 	struct passwd *pwd;
2156 	int i;
2157 	int fnd = 0;
2158 	uid_t uid;
2159 
2160 	/*
2161 	 * Ask the name service for names.
2162 	 */
2163 	for (i = 0; i < n; i++) {
2164 		/*
2165 		 * If name is numeric, ask for numeric id
2166 		 */
2167 		if (str2uid(utbl[i].name, &uid, 0, MAXEPHUID) == 0)
2168 			pwd = getpwuid(uid);
2169 		else
2170 			pwd = getpwnam(utbl[i].name);
2171 
2172 		/*
2173 		 * If found, enter found index into tbl array.
2174 		 */
2175 		if (pwd == NULL) {
2176 			(void) fprintf(stderr,
2177 			    gettext("ps: unknown user %s\n"), utbl[i].name);
2178 			continue;
2179 		}
2180 
2181 		utbl[fnd].id = pwd->pw_uid;
2182 		(void) strncpy(utbl[fnd].name, pwd->pw_name, MAXUGNAME);
2183 		fnd++;
2184 	}
2185 
2186 	uhead->nent = fnd;	/* in case it changed */
2187 	return (n - fnd);
2188 }
2189 
2190 static int
2191 gconv(struct ughead *ghead)
2192 {
2193 	struct ugdata *gtbl = ghead->ent;
2194 	int n = ghead->nent;
2195 	struct group *grp;
2196 	gid_t gid;
2197 	int i;
2198 	int fnd = 0;
2199 
2200 	/*
2201 	 * Ask the name service for names.
2202 	 */
2203 	for (i = 0; i < n; i++) {
2204 		/*
2205 		 * If name is numeric, ask for numeric id
2206 		 */
2207 		if (str2uid(gtbl[i].name, (uid_t *)&gid, 0, MAXEPHUID) == 0)
2208 			grp = getgrgid(gid);
2209 		else
2210 			grp = getgrnam(gtbl[i].name);
2211 		/*
2212 		 * If found, enter found index into tbl array.
2213 		 */
2214 		if (grp == NULL) {
2215 			(void) fprintf(stderr,
2216 			    gettext("ps: unknown group %s\n"), gtbl[i].name);
2217 			continue;
2218 		}
2219 
2220 		gtbl[fnd].id = grp->gr_gid;
2221 		(void) strncpy(gtbl[fnd].name, grp->gr_name, MAXUGNAME);
2222 		fnd++;
2223 	}
2224 
2225 	ghead->nent = fnd;	/* in case it changed */
2226 	return (n - fnd);
2227 }
2228 
2229 /*
2230  * Return 1 if puid is in table, otherwise 0.
2231  */
2232 static int
2233 ugfind(id_t id, struct ughead *ughead)
2234 {
2235 	struct ugdata *utbl = ughead->ent;
2236 	int n = ughead->nent;
2237 	int i;
2238 
2239 	for (i = 0; i < n; i++)
2240 		if (utbl[i].id == id)
2241 			return (1);
2242 	return (0);
2243 }
2244 
2245 /*
2246  * Print starting time of process unless process started more than 24 hours
2247  * ago, in which case the date is printed.  The date is printed in the form
2248  * "MMM dd" if old format, else the blank is replaced with an '_' so
2249  * it appears as a single word (for parseability).
2250  */
2251 static void
2252 prtime(timestruc_t st, int width, int old)
2253 {
2254 	char sttim[26];
2255 	time_t starttime;
2256 
2257 	starttime = st.tv_sec;
2258 	if (st.tv_nsec > 500000000)
2259 		starttime++;
2260 	if ((now.tv_sec - starttime) >= 24*60*60) {
2261 		(void) strftime(sttim, sizeof (sttim), old?
2262 		/*
2263 		 * TRANSLATION_NOTE
2264 		 * This time format is used by STIME field when -f option
2265 		 * is specified.  Used for processes that begun more than
2266 		 * 24 hours.
2267 		 */
2268 		    dcgettext(NULL, "%b %d", LC_TIME) :
2269 		/*
2270 		 * TRANSLATION_NOTE
2271 		 * This time format is used by STIME field when -o option
2272 		 * is specified.  Used for processes that begun more than
2273 		 * 24 hours.
2274 		 */
2275 		    dcgettext(NULL, "%b_%d", LC_TIME), localtime(&starttime));
2276 	} else {
2277 		/*
2278 		 * TRANSLATION_NOTE
2279 		 * This time format is used by STIME field when -f or -o option
2280 		 * is specified.  Used for processes that begun less than
2281 		 * 24 hours.
2282 		 */
2283 		(void) strftime(sttim, sizeof (sttim),
2284 		    dcgettext(NULL, "%H:%M:%S", LC_TIME),
2285 		    localtime(&starttime));
2286 	}
2287 	(void) printf("%*.*s", width, width, sttim);
2288 }
2289 
2290 static void
2291 przom(psinfo_t *psinfo)
2292 {
2293 	long	tm;
2294 	struct passwd *pwd;
2295 	char zonename[ZONENAME_MAX];
2296 
2297 	/*
2298 	 * All fields before 'PID' are printed with a trailing space as a
2299 	 * spearator, rather than keeping track of which column is first.  All
2300 	 * other fields are printed with a leading space.
2301 	 */
2302 	if (lflg) {	/* F S */
2303 		if (!yflg)
2304 			(void) printf("%2x ", psinfo->pr_flag & 0377); /* F */
2305 		(void) printf("%c ", psinfo->pr_lwp.pr_sname);	/* S */
2306 	}
2307 	if (Zflg) {
2308 		if (getzonenamebyid(psinfo->pr_zoneid, zonename,
2309 		    sizeof (zonename)) < 0) {
2310 			if (snprintf(NULL, 0, "%d",
2311 			    ((int)psinfo->pr_zoneid)) > 7)
2312 				(void) printf(" %6.6d%c ",
2313 				    ((int)psinfo->pr_zoneid), '*');
2314 			else
2315 				(void) printf(" %7.7d ",
2316 				    ((int)psinfo->pr_zoneid));
2317 		} else {
2318 			size_t nw;
2319 
2320 			nw = mbstowcs(NULL, zonename, 0);
2321 			if (nw == (size_t)-1)
2322 				(void) printf("%8.8s ", "ERROR");
2323 			else if (nw > 8)
2324 				(void) wprintf(L"%7.7s%c ", zonename, '*');
2325 			else
2326 				(void) wprintf(L"%8.8s ", zonename);
2327 		}
2328 	}
2329 	if (Hflg) {
2330 		/* Display home lgroup */
2331 		(void) printf(" %6d", (int)psinfo->pr_lwp.pr_lgrp); /* LGRP */
2332 	}
2333 	if (fflg) {
2334 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
2335 			size_t nw;
2336 
2337 			nw = mbstowcs(NULL, pwd->pw_name, 0);
2338 			if (nw == (size_t)-1)
2339 				(void) printf("%8.8s ", "ERROR");
2340 			else if (nw > 8)
2341 				(void) wprintf(L"%7.7s%c ", pwd->pw_name, '*');
2342 			else
2343 				(void) wprintf(L"%8.8s ", pwd->pw_name);
2344 		} else {
2345 			if (snprintf(NULL, 0, "%u",
2346 			    (psinfo->pr_euid)) > 7)
2347 				(void) printf(" %6.6u%c ", psinfo->pr_euid,
2348 				    '*');
2349 			else
2350 				(void) printf(" %7.7u ", psinfo->pr_euid);
2351 		}
2352 	} else if (lflg) {
2353 		if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6)
2354 			(void) printf("%5.5u%c ", psinfo->pr_euid, '*');
2355 		else
2356 			(void) printf("%6u ", psinfo->pr_euid);
2357 	}
2358 
2359 	(void) printf("%*d", pidwidth, (int)psinfo->pr_pid);	/* PID */
2360 	if (lflg || fflg)
2361 		(void) printf(" %*d", pidwidth,
2362 		    (int)psinfo->pr_ppid);			/* PPID */
2363 
2364 	if (jflg) {
2365 		(void) printf(" %*d", pidwidth,
2366 		    (int)psinfo->pr_pgid);			/* PGID */
2367 		(void) printf(" %*d", pidwidth,
2368 		    (int)psinfo->pr_sid);			/* SID  */
2369 	}
2370 
2371 	if (Lflg)
2372 		(void) printf(" %5d", 0);			/* LWP */
2373 	if (Pflg)
2374 		(void) printf("   -");				/* PSR */
2375 	if (Lflg && fflg)
2376 		(void) printf(" %5d", 0);			/* NLWP */
2377 
2378 	if (cflg) {
2379 		(void) printf(" %4s", "-");	/* zombies have no class */
2380 		(void) printf(" %3d", psinfo->pr_lwp.pr_pri);	/* PRI	*/
2381 	} else if (lflg || fflg) {
2382 		(void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C   */
2383 		if (lflg)
2384 			(void) printf(" %3d %2s",
2385 			    psinfo->pr_lwp.pr_oldpri, "-");	/* PRI NI */
2386 	}
2387 	if (lflg) {
2388 		if (yflg)				/* RSS SZ WCHAN */
2389 			(void) printf(" %5d %6d %8s", 0, 0, "-");
2390 		else					/* ADDR SZ WCHAN */
2391 			(void) printf(" %8s %6d %8s", "-", 0, "-");
2392 	}
2393 	if (fflg) {
2394 		int width = fname[F_STIME].width;
2395 		(void) printf(" %*.*s", width, width, "-");	/* STIME */
2396 	}
2397 	(void) printf(" %-8.14s", "?");				/* TTY */
2398 
2399 	tm = psinfo->pr_time.tv_sec;
2400 	if (psinfo->pr_time.tv_nsec > 500000000)
2401 		tm++;
2402 	(void) printf(" %4ld:%.2ld", tm / 60, tm % 60);	/* TIME */
2403 	(void) printf(" <defunct>\n");
2404 }
2405 
2406 /*
2407  * Function to compute the number of printable bytes in a multibyte
2408  * command string ("internationalization").
2409  */
2410 static int
2411 namencnt(char *cmd, int csisize, int scrsize)
2412 {
2413 	int csiwcnt = 0, scrwcnt = 0;
2414 	int ncsisz, nscrsz;
2415 	wchar_t  wchar;
2416 	int	 len;
2417 
2418 	while (*cmd != '\0') {
2419 		if ((len = csisize - csiwcnt) > (int)MB_CUR_MAX)
2420 			len = MB_CUR_MAX;
2421 		if ((ncsisz = mbtowc(&wchar, cmd, len)) < 0)
2422 			return (8); /* default to use for illegal chars */
2423 		if ((nscrsz = wcwidth(wchar)) <= 0)
2424 			return (8);
2425 		if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize)
2426 			break;
2427 		csiwcnt += ncsisz;
2428 		scrwcnt += nscrsz;
2429 		cmd += ncsisz;
2430 	}
2431 	return (csiwcnt);
2432 }
2433 
2434 static char *
2435 err_string(int err)
2436 {
2437 	static char buf[32];
2438 	char *str = strerror(err);
2439 
2440 	if (str == NULL)
2441 		(void) snprintf(str = buf, sizeof (buf), "Errno #%d", err);
2442 
2443 	return (str);
2444 }
2445 
2446 /* If allocation fails, die */
2447 static void *
2448 Realloc(void *ptr, size_t size)
2449 {
2450 	ptr = realloc(ptr, size);
2451 	if (ptr == NULL) {
2452 		(void) fprintf(stderr, gettext("ps: no memory\n"));
2453 		exit(1);
2454 	}
2455 	return (ptr);
2456 }
2457 
2458 static time_t
2459 delta_secs(const timestruc_t *start)
2460 {
2461 	time_t seconds = now.tv_sec - start->tv_sec;
2462 	long nanosecs = now.tv_usec * 1000 - start->tv_nsec;
2463 
2464 	if (nanosecs >= (NANOSEC / 2))
2465 		seconds++;
2466 	else if (nanosecs < -(NANOSEC / 2))
2467 		seconds--;
2468 
2469 	return (seconds);
2470 }
2471 
2472 /*
2473  * Returns the following:
2474  *
2475  *	0	No error
2476  *	EINVAL	Invalid number
2477  *	ERANGE	Value exceeds (min, max) range
2478  */
2479 static int
2480 str2id(const char *p, pid_t *val, long min, long max)
2481 {
2482 	char *q;
2483 	long number;
2484 	int error;
2485 
2486 	errno = 0;
2487 	number = strtol(p, &q, 10);
2488 
2489 	if (errno != 0 || q == p || *q != '\0') {
2490 		if ((error = errno) == 0) {
2491 			/*
2492 			 * strtol() can fail without setting errno, or it can
2493 			 * set it to EINVAL or ERANGE.  In the case errno is
2494 			 * still zero, return EINVAL.
2495 			 */
2496 			error = EINVAL;
2497 		}
2498 	} else if (number < min || number > max) {
2499 		error = ERANGE;
2500 	} else {
2501 		error = 0;
2502 	}
2503 
2504 	*val = number;
2505 
2506 	return (error);
2507 }
2508 
2509 /*
2510  * Returns the following:
2511  *
2512  *	0	No error
2513  *	EINVAL	Invalid number
2514  *	ERANGE	Value exceeds (min, max) range
2515  */
2516 static int
2517 str2uid(const char *p, uid_t *val, unsigned long min, unsigned long max)
2518 {
2519 	char *q;
2520 	unsigned long number;
2521 	int error;
2522 
2523 	errno = 0;
2524 	number = strtoul(p, &q, 10);
2525 
2526 	if (errno != 0 || q == p || *q != '\0') {
2527 		if ((error = errno) == 0) {
2528 			/*
2529 			 * strtoul() can fail without setting errno, or it can
2530 			 * set it to EINVAL or ERANGE.  In the case errno is
2531 			 * still zero, return EINVAL.
2532 			 */
2533 			error = EINVAL;
2534 		}
2535 	} else if (number < min || number > max) {
2536 		error = ERANGE;
2537 	} else {
2538 		error = 0;
2539 	}
2540 
2541 	*val = number;
2542 
2543 	return (error);
2544 }
2545 
2546 static int
2547 pidcmp(const void *p1, const void *p2)
2548 {
2549 	pid_t i = *((pid_t *)p1);
2550 	pid_t j = *((pid_t *)p2);
2551 
2552 	return (i - j);
2553 }
2554