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