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