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