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