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