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