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