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