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