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