xref: /illumos-gate/usr/src/cmd/ps/ps.c (revision ad135b5d644628e791c3188a6ecbd9c257961ef8)
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 	static zoneid_t zid = -1;
1232 	char devname[TTYNAME_MAX];
1233 	char *retval;
1234 
1235 	if (zid == -1)
1236 		zid = getzoneid();
1237 
1238 	if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid)
1239 		return ("?");
1240 
1241 	if ((retval = devlookup(psinfo->pr_ttydev)) != NULL)
1242 		return (retval);
1243 
1244 	retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname));
1245 
1246 	return (devadd(retval, psinfo->pr_ttydev));
1247 }
1248 
1249 /*
1250  * Find the process's tty and return 1 if process is to be printed.
1251  */
1252 static int
1253 prfind(int found, psinfo_t *psinfo, char **tpp)
1254 {
1255 	char	*tp;
1256 	struct tty *ttyp;
1257 
1258 	if (psinfo->pr_nlwp == 0) {
1259 		/* process is a zombie */
1260 		*tpp = "?";
1261 		if (tflg && !found)
1262 			return (0);
1263 		return (1);
1264 	}
1265 
1266 	/*
1267 	 * Get current terminal.  If none ("?") and 'a' is set, don't print
1268 	 * info.  If 't' is set, check if term is in list of desired terminals
1269 	 * and print it if it is.
1270 	 */
1271 	tp = gettty(psinfo);
1272 	if (aflg && *tp == '?') {
1273 		*tpp = tp;
1274 		return (0);
1275 	}
1276 	if (tflg && !found) {
1277 		int match = 0;
1278 		char *other = NULL;
1279 		for (ttyp = tty; ttyp->tname != NULL; ttyp++) {
1280 			/*
1281 			 * Look for a name match
1282 			 */
1283 			if (strcmp(tp, ttyp->tname) == 0) {
1284 				match = 1;
1285 				break;
1286 			}
1287 			/*
1288 			 * Look for same device under different names.
1289 			 */
1290 			if ((other == NULL) &&
1291 			    (ttyp->tdev != PRNODEV) &&
1292 			    (psinfo->pr_ttydev == ttyp->tdev))
1293 				other = ttyp->tname;
1294 		}
1295 		if (!match && (other != NULL)) {
1296 			/*
1297 			 * found under a different name
1298 			 */
1299 			match = 1;
1300 			tp = other;
1301 		}
1302 		if (!match || (tuid != (uid_t)-1 && tuid != psinfo->pr_euid)) {
1303 			/*
1304 			 * not found OR not matching euid
1305 			 */
1306 			*tpp = tp;
1307 			return (0);
1308 		}
1309 	}
1310 	*tpp = tp;
1311 	return (1);
1312 }
1313 
1314 /*
1315  * Print info about the process.
1316  */
1317 static void
1318 prcom(psinfo_t *psinfo, char *ttyp)
1319 {
1320 	char	*cp;
1321 	long	tm;
1322 	int	bytesleft;
1323 	int	wcnt, length;
1324 	wchar_t	wchar;
1325 	struct passwd *pwd;
1326 	int	zombie_lwp;
1327 	char	zonename[ZONENAME_MAX];
1328 
1329 	/*
1330 	 * If process is zombie, call zombie print routine and return.
1331 	 */
1332 	if (psinfo->pr_nlwp == 0) {
1333 		if (fields != NULL)
1334 			pr_fields(psinfo, ttyp, print_zombie_field);
1335 		else
1336 			przom(psinfo);
1337 		return;
1338 	}
1339 
1340 	zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');
1341 
1342 	/*
1343 	 * If user specified '-o format', print requested fields and return.
1344 	 */
1345 	if (fields != NULL) {
1346 		pr_fields(psinfo, ttyp, print_field);
1347 		return;
1348 	}
1349 
1350 	/*
1351 	 * All fields before 'PID' are printed with a trailing space as a
1352 	 * separator, rather than keeping track of which column is first.  All
1353 	 * other fields are printed with a leading space.
1354 	 */
1355 	if (lflg) {
1356 		if (!yflg)
1357 			(void) printf("%2x ", psinfo->pr_flag & 0377); /* F */
1358 		(void) printf("%c ", psinfo->pr_lwp.pr_sname);	/* S */
1359 	}
1360 
1361 	if (Zflg) {						/* ZONE */
1362 		if (getzonenamebyid(psinfo->pr_zoneid, zonename,
1363 		    sizeof (zonename)) < 0) {
1364 			(void) printf(" %7.7d ", ((int)psinfo->pr_zoneid));
1365 		} else {
1366 			(void) printf("%8.8s ", zonename);
1367 		}
1368 	}
1369 
1370 	if (fflg) {						/* UID */
1371 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
1372 			(void) printf("%8.8s ", pwd->pw_name);
1373 		else
1374 			(void) printf(" %7.7u ", psinfo->pr_euid);
1375 	} else if (lflg) {
1376 		(void) printf("%6u ", psinfo->pr_euid);
1377 	}
1378 	(void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */
1379 	if (lflg || fflg)
1380 		(void) printf(" %*d", pidwidth,
1381 		    (int)psinfo->pr_ppid); /* PPID */
1382 	if (jflg) {
1383 		(void) printf(" %*d", pidwidth,
1384 		    (int)psinfo->pr_pgid);	/* PGID */
1385 		(void) printf(" %*d", pidwidth,
1386 		    (int)psinfo->pr_sid);	/* SID  */
1387 	}
1388 	if (Lflg)
1389 		(void) printf(" %5d", (int)psinfo->pr_lwp.pr_lwpid); /* LWP */
1390 	if (Pflg) {
1391 		if (psinfo->pr_lwp.pr_bindpro == PBIND_NONE)	/* PSR */
1392 			(void) printf("   -");
1393 		else
1394 			(void) printf(" %3d", psinfo->pr_lwp.pr_bindpro);
1395 	}
1396 	if (Lflg && fflg)					/* NLWP */
1397 		(void) printf(" %5d", psinfo->pr_nlwp + psinfo->pr_nzomb);
1398 	if (cflg) {
1399 		if (zombie_lwp)					/* CLS */
1400 			(void) printf("     ");
1401 		else
1402 			(void) printf(" %4s", psinfo->pr_lwp.pr_clname);
1403 		(void) printf(" %3d", psinfo->pr_lwp.pr_pri);	/* PRI */
1404 	} else if (lflg || fflg) {
1405 		(void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C   */
1406 		if (lflg) {					    /* PRI NI */
1407 			/*
1408 			 * Print priorities the old way (lower numbers
1409 			 * mean higher priority) and print nice value
1410 			 * for time sharing procs.
1411 			 */
1412 			(void) printf(" %3d", psinfo->pr_lwp.pr_oldpri);
1413 			if (psinfo->pr_lwp.pr_oldpri != 0)
1414 				(void) printf(" %2d", psinfo->pr_lwp.pr_nice);
1415 			else
1416 				(void) printf(" %2.2s",
1417 				    psinfo->pr_lwp.pr_clname);
1418 		}
1419 	}
1420 	if (lflg) {
1421 		if (yflg) {
1422 			if (psinfo->pr_flag & SSYS)		/* RSS */
1423 				(void) printf("     0");
1424 			else if (psinfo->pr_rssize)
1425 				(void) printf(" %5lu",
1426 				    (ulong_t)psinfo->pr_rssize);
1427 			else
1428 				(void) printf("     ?");
1429 			if (psinfo->pr_flag & SSYS)		/* SZ */
1430 				(void) printf("      0");
1431 			else if (psinfo->pr_size)
1432 				(void) printf(" %6lu",
1433 				    (ulong_t)psinfo->pr_size);
1434 			else
1435 				(void) printf("      ?");
1436 		} else {
1437 #ifndef _LP64
1438 			if (psinfo->pr_addr)			/* ADDR */
1439 				(void) printf(" %8lx",
1440 				    (ulong_t)psinfo->pr_addr);
1441 			else
1442 #endif
1443 				(void) printf("        ?");
1444 			if (psinfo->pr_flag & SSYS)		/* SZ */
1445 				(void) printf("      0");
1446 			else if (psinfo->pr_size)
1447 				(void) printf(" %6lu",
1448 				    (ulong_t)psinfo->pr_size / kbytes_per_page);
1449 			else
1450 				(void) printf("      ?");
1451 		}
1452 		if (psinfo->pr_lwp.pr_sname != 'S')		/* WCHAN */
1453 			(void) printf("         ");
1454 #ifndef _LP64
1455 		else if (psinfo->pr_lwp.pr_wchan)
1456 			(void) printf(" %8lx",
1457 			    (ulong_t)psinfo->pr_lwp.pr_wchan);
1458 #endif
1459 		else
1460 			(void) printf("        ?");
1461 	}
1462 	if (fflg) {						/* STIME */
1463 		int width = fname[F_STIME].width;
1464 		if (Lflg)
1465 			prtime(psinfo->pr_lwp.pr_start, width + 1, 1);
1466 		else
1467 			prtime(psinfo->pr_start, width + 1, 1);
1468 	}
1469 
1470 	if (Hflg) {
1471 		/* Display home lgroup */
1472 		(void) printf(" %4d", (int)psinfo->pr_lwp.pr_lgrp);
1473 	}
1474 
1475 	(void) printf(" %-8.14s", ttyp);			/* TTY */
1476 	if (Lflg) {
1477 		tm = psinfo->pr_lwp.pr_time.tv_sec;
1478 		if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000)
1479 			tm++;
1480 	} else {
1481 		tm = psinfo->pr_time.tv_sec;
1482 		if (psinfo->pr_time.tv_nsec > 500000000)
1483 			tm++;
1484 	}
1485 	(void) printf(" %4ld:%.2ld", tm / 60, tm % 60);		/* [L]TIME */
1486 
1487 	if (zombie_lwp) {
1488 		(void) printf(" <defunct>\n");
1489 		return;
1490 	}
1491 
1492 	if (!fflg) {						/* CMD */
1493 		wcnt = namencnt(psinfo->pr_fname, 16, 8);
1494 		(void) printf(" %.*s\n", wcnt, psinfo->pr_fname);
1495 		return;
1496 	}
1497 
1498 
1499 	/*
1500 	 * PRARGSZ == length of cmd arg string.
1501 	 */
1502 	psinfo->pr_psargs[PRARGSZ-1] = '\0';
1503 	bytesleft = PRARGSZ;
1504 	for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
1505 		length = mbtowc(&wchar, cp, MB_LEN_MAX);
1506 		if (length == 0)
1507 			break;
1508 		if (length < 0 || !iswprint(wchar)) {
1509 			if (length < 0)
1510 				length = 1;
1511 			if (bytesleft <= length) {
1512 				*cp = '\0';
1513 				break;
1514 			}
1515 			/* omit the unprintable character */
1516 			(void) memmove(cp, cp+length, bytesleft-length);
1517 			length = 0;
1518 		}
1519 		bytesleft -= length;
1520 	}
1521 	wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ);
1522 	(void) printf(" %.*s\n", wcnt, psinfo->pr_psargs);
1523 }
1524 
1525 /*
1526  * Print percent from 16-bit binary fraction [0 .. 1]
1527  * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
1528  */
1529 static void
1530 prtpct(ushort_t pct, int width)
1531 {
1532 	uint_t value = pct;	/* need 32 bits to compute with */
1533 
1534 	value = ((value * 1000) + 0x7000) >> 15;	/* [0 .. 1000] */
1535 	if (value >= 1000)
1536 		value = 999;
1537 	if ((width -= 2) < 2)
1538 		width = 2;
1539 	(void) printf("%*u.%u", width, value / 10, value % 10);
1540 }
1541 
1542 static void
1543 print_time(time_t tim, int width)
1544 {
1545 	char buf[30];
1546 	time_t seconds;
1547 	time_t minutes;
1548 	time_t hours;
1549 	time_t days;
1550 
1551 	if (tim < 0) {
1552 		(void) printf("%*s", width, "-");
1553 		return;
1554 	}
1555 
1556 	seconds = tim % 60;
1557 	tim /= 60;
1558 	minutes = tim % 60;
1559 	tim /= 60;
1560 	hours = tim % 24;
1561 	days = tim / 24;
1562 
1563 	if (days > 0) {
1564 		(void) snprintf(buf, sizeof (buf), "%ld-%2.2ld:%2.2ld:%2.2ld",
1565 		    days, hours, minutes, seconds);
1566 	} else if (hours > 0) {
1567 		(void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld:%2.2ld",
1568 		    hours, minutes, seconds);
1569 	} else {
1570 		(void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld",
1571 		    minutes, seconds);
1572 	}
1573 
1574 	(void) printf("%*s", width, buf);
1575 }
1576 
1577 static void
1578 print_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
1579 {
1580 	int width = f->width;
1581 	struct passwd *pwd;
1582 	struct group *grp;
1583 	time_t cputime;
1584 	int bytesleft;
1585 	int wcnt;
1586 	wchar_t	wchar;
1587 	char *cp;
1588 	int length;
1589 	ulong_t mask;
1590 	char c, *csave;
1591 	int zombie_lwp;
1592 
1593 	zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');
1594 
1595 	switch (f->fname) {
1596 	case F_RUSER:
1597 		if ((pwd = getpwuid(psinfo->pr_uid)) != NULL)
1598 			(void) printf("%*s", width, pwd->pw_name);
1599 		else
1600 			(void) printf("%*u", width, psinfo->pr_uid);
1601 		break;
1602 	case F_USER:
1603 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
1604 			(void) printf("%*s", width, pwd->pw_name);
1605 		else
1606 			(void) printf("%*u", width, psinfo->pr_euid);
1607 		break;
1608 	case F_RGROUP:
1609 		if ((grp = getgrgid(psinfo->pr_gid)) != NULL)
1610 			(void) printf("%*s", width, grp->gr_name);
1611 		else
1612 			(void) printf("%*u", width, psinfo->pr_gid);
1613 		break;
1614 	case F_GROUP:
1615 		if ((grp = getgrgid(psinfo->pr_egid)) != NULL)
1616 			(void) printf("%*s", width, grp->gr_name);
1617 		else
1618 			(void) printf("%*u", width, psinfo->pr_egid);
1619 		break;
1620 	case F_RUID:
1621 		(void) printf("%*u", width, psinfo->pr_uid);
1622 		break;
1623 	case F_UID:
1624 		(void) printf("%*u", width, psinfo->pr_euid);
1625 		break;
1626 	case F_RGID:
1627 		(void) printf("%*u", width, psinfo->pr_gid);
1628 		break;
1629 	case F_GID:
1630 		(void) printf("%*u", width, psinfo->pr_egid);
1631 		break;
1632 	case F_PID:
1633 		(void) printf("%*d", width, (int)psinfo->pr_pid);
1634 		break;
1635 	case F_PPID:
1636 		(void) printf("%*d", width, (int)psinfo->pr_ppid);
1637 		break;
1638 	case F_PGID:
1639 		(void) printf("%*d", width, (int)psinfo->pr_pgid);
1640 		break;
1641 	case F_SID:
1642 		(void) printf("%*d", width, (int)psinfo->pr_sid);
1643 		break;
1644 	case F_PSR:
1645 		if (zombie_lwp || psinfo->pr_lwp.pr_bindpro == PBIND_NONE)
1646 			(void) printf("%*s", width, "-");
1647 		else
1648 			(void) printf("%*d", width, psinfo->pr_lwp.pr_bindpro);
1649 		break;
1650 	case F_LWP:
1651 		(void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lwpid);
1652 		break;
1653 	case F_NLWP:
1654 		(void) printf("%*d", width, psinfo->pr_nlwp + psinfo->pr_nzomb);
1655 		break;
1656 	case F_OPRI:
1657 		if (zombie_lwp)
1658 			(void) printf("%*s", width, "-");
1659 		else
1660 			(void) printf("%*d", width, psinfo->pr_lwp.pr_oldpri);
1661 		break;
1662 	case F_PRI:
1663 		if (zombie_lwp)
1664 			(void) printf("%*s", width, "-");
1665 		else
1666 			(void) printf("%*d", width, psinfo->pr_lwp.pr_pri);
1667 		break;
1668 	case F_F:
1669 		mask = 0xffffffffUL;
1670 		if (width < 8)
1671 			mask >>= (8 - width) * 4;
1672 		(void) printf("%*lx", width, psinfo->pr_flag & mask);
1673 		break;
1674 	case F_S:
1675 		(void) printf("%*c", width, psinfo->pr_lwp.pr_sname);
1676 		break;
1677 	case F_C:
1678 		if (zombie_lwp)
1679 			(void) printf("%*s", width, "-");
1680 		else
1681 			(void) printf("%*d", width, psinfo->pr_lwp.pr_cpu);
1682 		break;
1683 	case F_PCPU:
1684 		if (zombie_lwp)
1685 			(void) printf("%*s", width, "-");
1686 		else if (Lflg)
1687 			prtpct(psinfo->pr_lwp.pr_pctcpu, width);
1688 		else
1689 			prtpct(psinfo->pr_pctcpu, width);
1690 		break;
1691 	case F_PMEM:
1692 		prtpct(psinfo->pr_pctmem, width);
1693 		break;
1694 	case F_OSZ:
1695 		(void) printf("%*lu", width,
1696 		    (ulong_t)psinfo->pr_size / kbytes_per_page);
1697 		break;
1698 	case F_VSZ:
1699 		(void) printf("%*lu", width, (ulong_t)psinfo->pr_size);
1700 		break;
1701 	case F_RSS:
1702 		(void) printf("%*lu", width, (ulong_t)psinfo->pr_rssize);
1703 		break;
1704 	case F_NICE:
1705 		/* if pr_oldpri is zero, then this class has no nice */
1706 		if (zombie_lwp)
1707 			(void) printf("%*s", width, "-");
1708 		else if (psinfo->pr_lwp.pr_oldpri != 0)
1709 			(void) printf("%*d", width, psinfo->pr_lwp.pr_nice);
1710 		else
1711 			(void) printf("%*.*s", width, width,
1712 			    psinfo->pr_lwp.pr_clname);
1713 		break;
1714 	case F_CLASS:
1715 		if (zombie_lwp)
1716 			(void) printf("%*s", width, "-");
1717 		else
1718 			(void) printf("%*.*s", width, width,
1719 			    psinfo->pr_lwp.pr_clname);
1720 		break;
1721 	case F_STIME:
1722 		if (Lflg)
1723 			prtime(psinfo->pr_lwp.pr_start, width, 0);
1724 		else
1725 			prtime(psinfo->pr_start, width, 0);
1726 		break;
1727 	case F_ETIME:
1728 		if (Lflg)
1729 			print_time(delta_secs(&psinfo->pr_lwp.pr_start),
1730 			    width);
1731 		else
1732 			print_time(delta_secs(&psinfo->pr_start), width);
1733 		break;
1734 	case F_TIME:
1735 		if (Lflg) {
1736 			cputime = psinfo->pr_lwp.pr_time.tv_sec;
1737 			if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000)
1738 				cputime++;
1739 		} else {
1740 			cputime = psinfo->pr_time.tv_sec;
1741 			if (psinfo->pr_time.tv_nsec > 500000000)
1742 				cputime++;
1743 		}
1744 		print_time(cputime, width);
1745 		break;
1746 	case F_TTY:
1747 		(void) printf("%-*s", width, ttyp);
1748 		break;
1749 	case F_ADDR:
1750 		if (zombie_lwp)
1751 			(void) printf("%*s", width, "-");
1752 		else if (Lflg)
1753 			(void) printf("%*lx", width,
1754 			    (long)psinfo->pr_lwp.pr_addr);
1755 		else
1756 			(void) printf("%*lx", width, (long)psinfo->pr_addr);
1757 		break;
1758 	case F_WCHAN:
1759 		if (!zombie_lwp && psinfo->pr_lwp.pr_wchan)
1760 			(void) printf("%*lx", width,
1761 			    (long)psinfo->pr_lwp.pr_wchan);
1762 		else
1763 			(void) printf("%*.*s", width, width, "-");
1764 		break;
1765 	case F_FNAME:
1766 		/*
1767 		 * Print full width unless this is the last output format.
1768 		 */
1769 		if (zombie_lwp) {
1770 			if (f->next != NULL)
1771 				(void) printf("%-*s", width, "<defunct>");
1772 			else
1773 				(void) printf("%s", "<defunct>");
1774 			break;
1775 		}
1776 		wcnt = namencnt(psinfo->pr_fname, 16, width);
1777 		if (f->next != NULL)
1778 			(void) printf("%-*.*s", width, wcnt, psinfo->pr_fname);
1779 		else
1780 			(void) printf("%-.*s", wcnt, psinfo->pr_fname);
1781 		break;
1782 	case F_COMM:
1783 		if (zombie_lwp) {
1784 			if (f->next != NULL)
1785 				(void) printf("%-*s", width, "<defunct>");
1786 			else
1787 				(void) printf("%s", "<defunct>");
1788 			break;
1789 		}
1790 		csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n");
1791 		if (csave) {
1792 			c = *csave;
1793 			*csave = '\0';
1794 		}
1795 		/* FALLTHROUGH */
1796 	case F_ARGS:
1797 		/*
1798 		 * PRARGSZ == length of cmd arg string.
1799 		 */
1800 		if (zombie_lwp) {
1801 			(void) printf("%-*s", width, "<defunct>");
1802 			break;
1803 		}
1804 		psinfo->pr_psargs[PRARGSZ-1] = '\0';
1805 		bytesleft = PRARGSZ;
1806 		for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
1807 			length = mbtowc(&wchar, cp, MB_LEN_MAX);
1808 			if (length == 0)
1809 				break;
1810 			if (length < 0 || !iswprint(wchar)) {
1811 				if (length < 0)
1812 					length = 1;
1813 				if (bytesleft <= length) {
1814 					*cp = '\0';
1815 					break;
1816 				}
1817 				/* omit the unprintable character */
1818 				(void) memmove(cp, cp+length, bytesleft-length);
1819 				length = 0;
1820 			}
1821 			bytesleft -= length;
1822 		}
1823 		wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width);
1824 		/*
1825 		 * Print full width unless this is the last format.
1826 		 */
1827 		if (f->next != NULL)
1828 			(void) printf("%-*.*s", width, wcnt,
1829 			    psinfo->pr_psargs);
1830 		else
1831 			(void) printf("%-.*s", wcnt,
1832 			    psinfo->pr_psargs);
1833 		if (f->fname == F_COMM && csave)
1834 			*csave = c;
1835 		break;
1836 	case F_TASKID:
1837 		(void) printf("%*d", width, (int)psinfo->pr_taskid);
1838 		break;
1839 	case F_PROJID:
1840 		(void) printf("%*d", width, (int)psinfo->pr_projid);
1841 		break;
1842 	case F_PROJECT:
1843 		{
1844 			struct project cproj;
1845 			char proj_buf[PROJECT_BUFSZ];
1846 
1847 			if ((getprojbyid(psinfo->pr_projid, &cproj,
1848 			    (void *)&proj_buf, PROJECT_BUFSZ)) == NULL)
1849 				(void) printf("%*d", width,
1850 				    (int)psinfo->pr_projid);
1851 			else
1852 				(void) printf("%*s", width,
1853 				    (cproj.pj_name != NULL) ?
1854 				    cproj.pj_name : "---");
1855 		}
1856 		break;
1857 	case F_PSET:
1858 		if (zombie_lwp || psinfo->pr_lwp.pr_bindpset == PS_NONE)
1859 			(void) printf("%*s", width, "-");
1860 		else
1861 			(void) printf("%*d", width, psinfo->pr_lwp.pr_bindpset);
1862 		break;
1863 	case F_ZONEID:
1864 		(void) printf("%*d", width, (int)psinfo->pr_zoneid);
1865 		break;
1866 	case F_ZONE:
1867 		{
1868 			char zonename[ZONENAME_MAX];
1869 
1870 			if (getzonenamebyid(psinfo->pr_zoneid, zonename,
1871 			    sizeof (zonename)) < 0) {
1872 				(void) printf("%*d", width,
1873 				    ((int)psinfo->pr_zoneid));
1874 			} else {
1875 				(void) printf("%*s", width, zonename);
1876 			}
1877 		}
1878 		break;
1879 	case F_CTID:
1880 		if (psinfo->pr_contract == -1)
1881 			(void) printf("%*s", width, "-");
1882 		else
1883 			(void) printf("%*ld", width, (long)psinfo->pr_contract);
1884 		break;
1885 	case F_LGRP:
1886 		/* Display home lgroup */
1887 		(void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lgrp);
1888 		break;
1889 	}
1890 }
1891 
1892 static void
1893 print_zombie_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
1894 {
1895 	int wcnt;
1896 	int width = f->width;
1897 
1898 	switch (f->fname) {
1899 	case F_FNAME:
1900 	case F_COMM:
1901 	case F_ARGS:
1902 		/*
1903 		 * Print full width unless this is the last output format.
1904 		 */
1905 		wcnt = min(width, sizeof ("<defunct>"));
1906 		if (f->next != NULL)
1907 			(void) printf("%-*.*s", width, wcnt, "<defunct>");
1908 		else
1909 			(void) printf("%-.*s", wcnt, "<defunct>");
1910 		break;
1911 
1912 	case F_PSR:
1913 	case F_PCPU:
1914 	case F_PMEM:
1915 	case F_NICE:
1916 	case F_CLASS:
1917 	case F_STIME:
1918 	case F_ETIME:
1919 	case F_WCHAN:
1920 	case F_PSET:
1921 		(void) printf("%*s", width, "-");
1922 		break;
1923 
1924 	case F_OPRI:
1925 	case F_PRI:
1926 	case F_OSZ:
1927 	case F_VSZ:
1928 	case F_RSS:
1929 		(void) printf("%*d", width, 0);
1930 		break;
1931 
1932 	default:
1933 		print_field(psinfo, f, ttyp);
1934 		break;
1935 	}
1936 }
1937 
1938 static void
1939 pr_fields(psinfo_t *psinfo, const char *ttyp,
1940 	void (*print_fld)(psinfo_t *, struct field *, const char *))
1941 {
1942 	struct field *f;
1943 
1944 	for (f = fields; f != NULL; f = f->next) {
1945 		print_fld(psinfo, f, ttyp);
1946 		if (f->next != NULL)
1947 			(void) printf(" ");
1948 	}
1949 	(void) printf("\n");
1950 }
1951 
1952 /*
1953  * Returns 1 if arg is found in array arr, of length num; 0 otherwise.
1954  */
1955 static int
1956 search(pid_t *arr, int number, pid_t arg)
1957 {
1958 	int i;
1959 
1960 	for (i = 0; i < number; i++)
1961 		if (arg == arr[i])
1962 			return (1);
1963 	return (0);
1964 }
1965 
1966 /*
1967  * Add an entry (user, group) to the specified table.
1968  */
1969 static void
1970 add_ugentry(struct ughead *tbl, char *name)
1971 {
1972 	struct ugdata *entp;
1973 
1974 	if (tbl->size == tbl->nent) {	/* reallocate the table entries */
1975 		if ((tbl->size *= 2) == 0)
1976 			tbl->size = 32;		/* first time */
1977 		tbl->ent = Realloc(tbl->ent, tbl->size*sizeof (struct ugdata));
1978 	}
1979 	entp = &tbl->ent[tbl->nent++];
1980 	entp->id = 0;
1981 	(void) strncpy(entp->name, name, MAXUGNAME);
1982 	entp->name[MAXUGNAME] = '\0';
1983 }
1984 
1985 static int
1986 uconv(struct ughead *uhead)
1987 {
1988 	struct ugdata *utbl = uhead->ent;
1989 	int n = uhead->nent;
1990 	struct passwd *pwd;
1991 	int i;
1992 	int fnd = 0;
1993 	uid_t uid;
1994 
1995 	/*
1996 	 * Ask the name service for names.
1997 	 */
1998 	for (i = 0; i < n; i++) {
1999 		/*
2000 		 * If name is numeric, ask for numeric id
2001 		 */
2002 		if (str2uid(utbl[i].name, &uid, 0, MAXEPHUID) == 0)
2003 			pwd = getpwuid(uid);
2004 		else
2005 			pwd = getpwnam(utbl[i].name);
2006 
2007 		/*
2008 		 * If found, enter found index into tbl array.
2009 		 */
2010 		if (pwd == NULL) {
2011 			(void) fprintf(stderr,
2012 			    gettext("ps: unknown user %s\n"), utbl[i].name);
2013 			continue;
2014 		}
2015 
2016 		utbl[fnd].id = pwd->pw_uid;
2017 		(void) strncpy(utbl[fnd].name, pwd->pw_name, MAXUGNAME);
2018 		fnd++;
2019 	}
2020 
2021 	uhead->nent = fnd;	/* in case it changed */
2022 	return (n - fnd);
2023 }
2024 
2025 static int
2026 gconv(struct ughead *ghead)
2027 {
2028 	struct ugdata *gtbl = ghead->ent;
2029 	int n = ghead->nent;
2030 	struct group *grp;
2031 	gid_t gid;
2032 	int i;
2033 	int fnd = 0;
2034 
2035 	/*
2036 	 * Ask the name service for names.
2037 	 */
2038 	for (i = 0; i < n; i++) {
2039 		/*
2040 		 * If name is numeric, ask for numeric id
2041 		 */
2042 		if (str2uid(gtbl[i].name, (uid_t *)&gid, 0, MAXEPHUID) == 0)
2043 			grp = getgrgid(gid);
2044 		else
2045 			grp = getgrnam(gtbl[i].name);
2046 		/*
2047 		 * If found, enter found index into tbl array.
2048 		 */
2049 		if (grp == NULL) {
2050 			(void) fprintf(stderr,
2051 			    gettext("ps: unknown group %s\n"), gtbl[i].name);
2052 			continue;
2053 		}
2054 
2055 		gtbl[fnd].id = grp->gr_gid;
2056 		(void) strncpy(gtbl[fnd].name, grp->gr_name, MAXUGNAME);
2057 		fnd++;
2058 	}
2059 
2060 	ghead->nent = fnd;	/* in case it changed */
2061 	return (n - fnd);
2062 }
2063 
2064 /*
2065  * Return 1 if puid is in table, otherwise 0.
2066  */
2067 static int
2068 ugfind(id_t id, struct ughead *ughead)
2069 {
2070 	struct ugdata *utbl = ughead->ent;
2071 	int n = ughead->nent;
2072 	int i;
2073 
2074 	for (i = 0; i < n; i++)
2075 		if (utbl[i].id == id)
2076 			return (1);
2077 	return (0);
2078 }
2079 
2080 /*
2081  * Print starting time of process unless process started more than 24 hours
2082  * ago, in which case the date is printed.  The date is printed in the form
2083  * "MMM dd" if old format, else the blank is replaced with an '_' so
2084  * it appears as a single word (for parseability).
2085  */
2086 static void
2087 prtime(timestruc_t st, int width, int old)
2088 {
2089 	char sttim[26];
2090 	time_t starttime;
2091 
2092 	starttime = st.tv_sec;
2093 	if (st.tv_nsec > 500000000)
2094 		starttime++;
2095 	if ((now.tv_sec - starttime) >= 24*60*60) {
2096 		(void) strftime(sttim, sizeof (sttim), old?
2097 		/*
2098 		 * TRANSLATION_NOTE
2099 		 * This time format is used by STIME field when -f option
2100 		 * is specified.  Used for processes that begun more than
2101 		 * 24 hours.
2102 		 */
2103 		    dcgettext(NULL, "%b %d", LC_TIME) :
2104 		/*
2105 		 * TRANSLATION_NOTE
2106 		 * This time format is used by STIME field when -o option
2107 		 * is specified.  Used for processes that begun more than
2108 		 * 24 hours.
2109 		 */
2110 		    dcgettext(NULL, "%b_%d", LC_TIME), localtime(&starttime));
2111 	} else {
2112 		/*
2113 		 * TRANSLATION_NOTE
2114 		 * This time format is used by STIME field when -f or -o option
2115 		 * is specified.  Used for processes that begun less than
2116 		 * 24 hours.
2117 		 */
2118 		(void) strftime(sttim, sizeof (sttim),
2119 		    dcgettext(NULL, "%H:%M:%S", LC_TIME),
2120 		    localtime(&starttime));
2121 	}
2122 	(void) printf("%*.*s", width, width, sttim);
2123 }
2124 
2125 static void
2126 przom(psinfo_t *psinfo)
2127 {
2128 	long	tm;
2129 	struct passwd *pwd;
2130 	char zonename[ZONENAME_MAX];
2131 
2132 	/*
2133 	 * All fields before 'PID' are printed with a trailing space as a
2134 	 * spearator, rather than keeping track of which column is first.  All
2135 	 * other fields are printed with a leading space.
2136 	 */
2137 	if (lflg) {	/* F S */
2138 		if (!yflg)
2139 			(void) printf("%2x ", psinfo->pr_flag & 0377); /* F */
2140 		(void) printf("%c ", psinfo->pr_lwp.pr_sname);	/* S */
2141 	}
2142 	if (Zflg) {
2143 		if (getzonenamebyid(psinfo->pr_zoneid, zonename,
2144 		    sizeof (zonename)) < 0) {
2145 			(void) printf(" %7.7d ", ((int)psinfo->pr_zoneid));
2146 		} else {
2147 			(void) printf("%8.8s ", zonename);
2148 		}
2149 	}
2150 	if (Hflg) {
2151 		/* Display home lgroup */
2152 		(void) printf(" %6d", (int)psinfo->pr_lwp.pr_lgrp); /* LGRP */
2153 	}
2154 	if (fflg) {
2155 		if ((pwd = getpwuid(psinfo->pr_euid)) != NULL)
2156 			(void) printf("%8.8s ", pwd->pw_name);
2157 		else
2158 			(void) printf(" %7.7u ", psinfo->pr_euid);
2159 	} else if (lflg)
2160 		(void) printf("%6u ", psinfo->pr_euid);
2161 
2162 	(void) printf("%*d", pidwidth, (int)psinfo->pr_pid);	/* PID */
2163 	if (lflg || fflg)
2164 		(void) printf(" %*d", pidwidth,
2165 		    (int)psinfo->pr_ppid);			/* PPID */
2166 
2167 	if (jflg) {
2168 		(void) printf(" %*d", pidwidth,
2169 		    (int)psinfo->pr_pgid);			/* PGID */
2170 		(void) printf(" %*d", pidwidth,
2171 		    (int)psinfo->pr_sid);			/* SID  */
2172 	}
2173 
2174 	if (Lflg)
2175 		(void) printf(" %5d", 0);			/* LWP */
2176 	if (Pflg)
2177 		(void) printf("   -");				/* PSR */
2178 	if (Lflg && fflg)
2179 		(void) printf(" %5d", 0);			/* NLWP */
2180 
2181 	if (cflg) {
2182 		(void) printf(" %4s", "-");	/* zombies have no class */
2183 		(void) printf(" %3d", psinfo->pr_lwp.pr_pri);	/* PRI	*/
2184 	} else if (lflg || fflg) {
2185 		(void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C   */
2186 		if (lflg)
2187 			(void) printf(" %3d %2s",
2188 			    psinfo->pr_lwp.pr_oldpri, "-");	/* PRI NI */
2189 	}
2190 	if (lflg) {
2191 		if (yflg)				/* RSS SZ WCHAN */
2192 			(void) printf(" %5d %6d %8s", 0, 0, "-");
2193 		else					/* ADDR SZ WCHAN */
2194 			(void) printf(" %8s %6d %8s", "-", 0, "-");
2195 	}
2196 	if (fflg) {
2197 		int width = fname[F_STIME].width;
2198 		(void) printf(" %*.*s", width, width, "-"); 	/* STIME */
2199 	}
2200 	(void) printf(" %-8.14s", "?");				/* TTY */
2201 
2202 	tm = psinfo->pr_time.tv_sec;
2203 	if (psinfo->pr_time.tv_nsec > 500000000)
2204 		tm++;
2205 	(void) printf(" %4ld:%.2ld", tm / 60, tm % 60);	/* TIME */
2206 	(void) printf(" <defunct>\n");
2207 }
2208 
2209 /*
2210  * Function to compute the number of printable bytes in a multibyte
2211  * command string ("internationalization").
2212  */
2213 static int
2214 namencnt(char *cmd, int csisize, int scrsize)
2215 {
2216 	int csiwcnt = 0, scrwcnt = 0;
2217 	int ncsisz, nscrsz;
2218 	wchar_t  wchar;
2219 	int	 len;
2220 
2221 	while (*cmd != '\0') {
2222 		if ((len = csisize - csiwcnt) > (int)MB_CUR_MAX)
2223 			len = MB_CUR_MAX;
2224 		if ((ncsisz = mbtowc(&wchar, cmd, len)) < 0)
2225 			return (8); /* default to use for illegal chars */
2226 		if ((nscrsz = wcwidth(wchar)) <= 0)
2227 			return (8);
2228 		if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize)
2229 			break;
2230 		csiwcnt += ncsisz;
2231 		scrwcnt += nscrsz;
2232 		cmd += ncsisz;
2233 	}
2234 	return (csiwcnt);
2235 }
2236 
2237 static char *
2238 err_string(int err)
2239 {
2240 	static char buf[32];
2241 	char *str = strerror(err);
2242 
2243 	if (str == NULL)
2244 		(void) snprintf(str = buf, sizeof (buf), "Errno #%d", err);
2245 
2246 	return (str);
2247 }
2248 
2249 /* If allocation fails, die */
2250 static void *
2251 Realloc(void *ptr, size_t size)
2252 {
2253 	ptr = realloc(ptr, size);
2254 	if (ptr == NULL) {
2255 		(void) fprintf(stderr, gettext("ps: no memory\n"));
2256 		exit(1);
2257 	}
2258 	return (ptr);
2259 }
2260 
2261 static time_t
2262 delta_secs(const timestruc_t *start)
2263 {
2264 	time_t seconds = now.tv_sec - start->tv_sec;
2265 	long nanosecs = now.tv_usec * 1000 - start->tv_nsec;
2266 
2267 	if (nanosecs >= (NANOSEC / 2))
2268 		seconds++;
2269 	else if (nanosecs < -(NANOSEC / 2))
2270 		seconds--;
2271 
2272 	return (seconds);
2273 }
2274 
2275 /*
2276  * Returns the following:
2277  *
2278  * 	0	No error
2279  * 	EINVAL	Invalid number
2280  * 	ERANGE	Value exceeds (min, max) range
2281  */
2282 static int
2283 str2id(const char *p, pid_t *val, long min, long max)
2284 {
2285 	char *q;
2286 	long number;
2287 	int error;
2288 
2289 	errno = 0;
2290 	number = strtol(p, &q, 10);
2291 
2292 	if (errno != 0 || q == p || *q != '\0') {
2293 		if ((error = errno) == 0) {
2294 			/*
2295 			 * strtol() can fail without setting errno, or it can
2296 			 * set it to EINVAL or ERANGE.  In the case errno is
2297 			 * still zero, return EINVAL.
2298 			 */
2299 			error = EINVAL;
2300 		}
2301 	} else if (number < min || number > max) {
2302 		error = ERANGE;
2303 	} else {
2304 		error = 0;
2305 	}
2306 
2307 	*val = number;
2308 
2309 	return (error);
2310 }
2311 
2312 /*
2313  * Returns the following:
2314  *
2315  * 	0	No error
2316  * 	EINVAL	Invalid number
2317  * 	ERANGE	Value exceeds (min, max) range
2318  */
2319 static int
2320 str2uid(const char *p, uid_t *val, unsigned long min, unsigned long max)
2321 {
2322 	char *q;
2323 	unsigned long number;
2324 	int error;
2325 
2326 	errno = 0;
2327 	number = strtoul(p, &q, 10);
2328 
2329 	if (errno != 0 || q == p || *q != '\0') {
2330 		if ((error = errno) == 0) {
2331 			/*
2332 			 * strtoul() can fail without setting errno, or it can
2333 			 * set it to EINVAL or ERANGE.  In the case errno is
2334 			 * still zero, return EINVAL.
2335 			 */
2336 			error = EINVAL;
2337 		}
2338 	} else if (number < min || number > max) {
2339 		error = ERANGE;
2340 	} else {
2341 		error = 0;
2342 	}
2343 
2344 	*val = number;
2345 
2346 	return (error);
2347 }
2348 
2349 static int
2350 pidcmp(const void *p1, const void *p2)
2351 {
2352 	pid_t i = *((pid_t *)p1);
2353 	pid_t j = *((pid_t *)p2);
2354 
2355 	return (i - j);
2356 }
2357