root/usr/src/cmd/ps/ps.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2013 Gary Mills
 *
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright (c) 2018, Joyent, Inc.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * ps -- print things about processes.
 */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <sys/signal.h>
#include <sys/fault.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <procfs.h>
#include <locale.h>
#include <wctype.h>
#include <wchar.h>
#include <libw.h>
#include <stdarg.h>
#include <sys/proc.h>
#include <sys/pset.h>
#include <project.h>
#include <zone.h>

#define min(a, b)       ((a) > (b) ? (b) : (a))
#define max(a, b)       ((a) < (b) ? (b) : (a))

#define NTTYS   20      /* initial size of table for -t option  */
#define SIZ     30      /* initial size of tables for -p, -s, -g, -h and -z */

/*
 * Size of buffer holding args for t, p, s, g, u, U, G, z options.
 * Set to ZONENAME_MAX, the minimum value needed to allow any
 * zone to be specified.
 */
#define ARGSIZ ZONENAME_MAX

/* Max chars in a user/group name or printed u/g id */
#define MAXUGNAME (LOGNAME_MAX+2)

/* Structure for storing user or group info */
struct ugdata {
        id_t    id;                     /* numeric user-id or group-id */
        char    name[MAXUGNAME+1];      /* user/group name, null terminated */
};

struct ughead {
        size_t  size;           /* number of ugdata structs allocated */
        size_t  nent;           /* number of active entries */
        struct ugdata *ent;     /* pointer to array of actual entries */
};

enum fname {    /* enumeration of field names */
        F_USER,         /* effective user of the process */
        F_RUSER,        /* real user of the process */
        F_GROUP,        /* effective group of the process */
        F_RGROUP,       /* real group of the process */
        F_UID,          /* numeric effective uid of the process */
        F_RUID,         /* numeric real uid of the process */
        F_GID,          /* numeric effective gid of the process */
        F_RGID,         /* numeric real gid of the process */
        F_PID,          /* process id */
        F_PPID,         /* parent process id */
        F_PGID,         /* process group id */
        F_SID,          /* session id */
        F_PSR,          /* bound processor */
        F_LWP,          /* lwp-id */
        F_LWPNAME,      /* lwp name */
        F_NLWP,         /* number of lwps */
        F_OPRI,         /* old priority (obsolete) */
        F_PRI,          /* new priority */
        F_F,            /* process flags */
        F_S,            /* letter indicating the state */
        F_C,            /* processor utilization (obsolete) */
        F_PCPU,         /* percent of recently used cpu time */
        F_PMEM,         /* percent of physical memory used (rss) */
        F_OSZ,          /* virtual size of the process in pages */
        F_VSZ,          /* virtual size of the process in kilobytes */
        F_RSS,          /* resident set size of the process in kilobytes */
        F_NICE,         /* "nice" value of the process */
        F_CLASS,        /* scheduler class */
        F_STIME,        /* start time of the process, hh:mm:ss or Month Day */
        F_ETIME,        /* elapsed time of the process, [[dd-]hh:]mm:ss */
        F_TIME,         /* cpu time of the process, [[dd-]hh:]mm:ss */
        F_TTY,          /* name of the controlling terminal */
        F_ADDR,         /* address of the process (obsolete) */
        F_WCHAN,        /* wait channel (sleep condition variable) */
        F_FNAME,        /* file name of command */
        F_COMM,         /* name of command (argv[0] value) */
        F_ARGS,         /* name of command plus all its arguments */
        F_TASKID,       /* task id */
        F_PROJID,       /* project id */
        F_PROJECT,      /* project name of the process */
        F_PSET,         /* bound processor set */
        F_ZONE,         /* zone name */
        F_ZONEID,       /* zone id */
        F_CTID,         /* process contract id */
        F_LGRP,         /* process home lgroup */
        F_DMODEL        /* process data model */
};

struct field {
        struct field    *next;          /* linked list */
        int             fname;          /* field index */
        const char      *header;        /* header to use */
        int             width;          /* width of field */
};

static  struct field *fields = NULL;    /* fields selected via -o */
static  struct field *last_field = NULL;
static  int do_header = 0;
static  struct timeval now;

/* array of defined fields, in fname order */
struct def_field {
        const char *fname;
        const char *header;
        int width;
        int minwidth;
};

static struct def_field fname[] = {
        /* fname        header          width   minwidth */
        { "user",       "USER",         8,      8       },
        { "ruser",      "RUSER",        8,      8       },
        { "group",      "GROUP",        8,      8       },
        { "rgroup",     "RGROUP",       8,      8       },
        { "uid",        "UID",          5,      5       },
        { "ruid",       "RUID",         5,      5       },
        { "gid",        "GID",          5,      5       },
        { "rgid",       "RGID",         5,      5       },
        { "pid",        "PID",          5,      5       },
        { "ppid",       "PPID",         5,      5       },
        { "pgid",       "PGID",         5,      5       },
        { "sid",        "SID",          5,      5       },
        { "psr",        "PSR",          3,      2       },
        { "lwp",        "LWP",          6,      2       },
        { "lwpname",    "LWPNAME",      32,     8       },
        { "nlwp",       "NLWP",         4,      2       },
        { "opri",       "PRI",          3,      2       },
        { "pri",        "PRI",          3,      2       },
        { "f",          "F",            2,      2       },
        { "s",          "S",            1,      1       },
        { "c",          "C",            2,      2       },
        { "pcpu",       "%CPU",         4,      4       },
        { "pmem",       "%MEM",         4,      4       },
        { "osz",        "SZ",           4,      4       },
        { "vsz",        "VSZ",          4,      4       },
        { "rss",        "RSS",          4,      4       },
        { "nice",       "NI",           2,      2       },
        { "class",      "CLS",          4,      2       },
        { "stime",      "STIME",        8,      8       },
        { "etime",      "ELAPSED",      11,     7       },
        { "time",       "TIME",         11,     5       },
        { "tty",        "TT",           7,      7       },
#ifdef _LP64
        { "addr",       "ADDR",         16,     8       },
        { "wchan",      "WCHAN",        16,     8       },
#else
        { "addr",       "ADDR",         8,      8       },
        { "wchan",      "WCHAN",        8,      8       },
#endif
        { "fname",      "COMMAND",      8,      8       },
        { "comm",       "COMMAND",      80,     8       },
        { "args",       "COMMAND",      80,     80      },
        { "taskid",     "TASKID",       5,      5       },
        { "projid",     "PROJID",       5,      5       },
        { "project",    "PROJECT",      8,      8       },
        { "pset",       "PSET",         3,      3       },
        { "zone",       "ZONE",         8,      8       },
        { "zoneid",     "ZONEID",       5,      5       },
        { "ctid",       "CTID",         5,      5       },
        { "lgrp",       "LGRP",         4,      2       },
        { "dmodel",     "DMODEL",       6,      6       },
};

#define NFIELDS (sizeof (fname) / sizeof (fname[0]))

static  int     retcode = 1;
static  int     lflg;
static  int     Aflg;
static  int     uflg;
static  int     Uflg;
static  int     Gflg;
static  int     aflg;
static  int     dflg;
static  int     Lflg;
static  int     Pflg;
static  int     Wflg;
static  int     yflg;
static  int     pflg;
static  int     fflg;
static  int     cflg;
static  int     jflg;
static  int     gflg;
static  int     sflg;
static  int     tflg;
static  int     zflg;
static  int     Zflg;
static  int     hflg;
static  int     Hflg;
static  uid_t   tuid = (uid_t)-1;
static  int     errflg;

static  int     ndev;           /* number of devices */
static  int     maxdev;         /* number of devl structures allocated */

#define DNINCR  100
#define DNSIZE  14
static struct devl {            /* device list   */
        char    dname[DNSIZE];  /* device name   */
        dev_t   ddev;           /* device number */
} *devl;

static  struct tty {
        char *tname;
        dev_t tdev;
} *tty = NULL;                  /* for t option */
static  size_t  ttysz = 0;
static  int     ntty = 0;

static  pid_t   *pid = NULL;    /* for p option */
static  size_t  pidsz = 0;
static  size_t  npid = 0;

static  int     *lgrps = NULL;  /* list of lgroup IDs for for h option */
static  size_t  lgrps_size = 0; /* size of the lgrps list */
static  size_t  nlgrps = 0;     /* number elements in the list */

/* Maximum possible lgroup ID value */
#define MAX_LGRP_ID 256

static  pid_t   *grpid = NULL;  /* for g option */
static  size_t  grpidsz = 0;
static  int     ngrpid = 0;

static  pid_t   *sessid = NULL; /* for s option */
static  size_t  sessidsz = 0;
static  int     nsessid = 0;

static  zoneid_t *zoneid = NULL; /* for z option */
static  size_t  zoneidsz = 0;
static  int     nzoneid = 0;

static  int     kbytes_per_page;
static  int     pidwidth;

static  char    *procdir = "/proc";     /* standard /proc directory */

static struct ughead    euid_tbl;       /* table to store selected euid's */
static struct ughead    ruid_tbl;       /* table to store selected real uid's */
static struct ughead    egid_tbl;       /* table to store selected egid's */
static struct ughead    rgid_tbl;       /* table to store selected real gid's */
static prheader_t *lpsinfobuf;          /* buffer to contain lpsinfo */
static size_t   lpbufsize;

/*
 * This constant defines the sentinal number of process IDs below which we
 * only examine individual entries in /proc rather than scanning through
 * /proc. This optimization is a huge win in the common case.
 */
#define PTHRESHOLD      40

#define UCB_OPTS        "-aceglnrtuvwxSU"

static  void    usage(void);
static  char    *getarg(char **);
static  char    *parse_format(char *);
static  char    *gettty(psinfo_t *);
static  int     prfind(int, psinfo_t *, char **);
static  void    prcom(psinfo_t *, char *);
static  void    prtpct(ushort_t, int);
static  void    print_time(time_t, int);
static  void    print_field(psinfo_t *, struct field *, const char *);
static  void    print_zombie_field(psinfo_t *, struct field *, const char *);
static  void    pr_fields(psinfo_t *, const char *,
                void (*print_fld)(psinfo_t *, struct field *, const char *));
static  int     search(pid_t *, int, pid_t);
static  void    add_ugentry(struct ughead *, char *);
static  int     uconv(struct ughead *);
static  int     gconv(struct ughead *);
static  int     ugfind(id_t, struct ughead *);
static  void    prtime(timestruc_t, int, int);
static  void    przom(psinfo_t *);
static  int     namencnt(char *, int, int);
static  char    *err_string(int);
static  int     print_proc(char *pname);
static  time_t  delta_secs(const timestruc_t *);
static  int     str2id(const char *, pid_t *, long, long);
static  int     str2uid(const char *,  uid_t *, unsigned long, unsigned long);
static  void    *Realloc(void *, size_t);
static  int     pidcmp(const void *p1, const void *p2);

extern  int     ucbmain(int, char **);
static  int     stdmain(int, char **);

int
main(int argc, char **argv)
{
        const char *me;

        /*
         * The original two ps'es are linked in a single binary;
         * their main()s are renamed to stdmain for /usr/bin/ps and
         * ucbmain for /usr/ucb/ps.
         * We try to figure out which instance of ps the user wants to run.
         * Traditionally, the UCB variant doesn't require the flag argument
         * start with a "-".  If the first argument doesn't start with a
         * "-", we call "ucbmain".
         * If there's a first argument and it starts with a "-", we check
         * whether any of the options isn't acceptable to "ucbmain"; in that
         * case we run "stdmain".
         * If we can't tell from the options which main to call, we check
         * the binary we are running.  We default to "stdmain" but
         * any mention in the executable name of "ucb" causes us to call
         * ucbmain.
         */
        if (argv[1] != NULL) {
                if (argv[1][0] != '-')
                        return (ucbmain(argc, argv));
                else if (argv[1][strspn(argv[1], UCB_OPTS)] != '\0')
                        return (stdmain(argc, argv));
        }

        me = getexecname();

        if (me != NULL && strstr(me, "ucb") != NULL)
                return (ucbmain(argc, argv));
        else
                return (stdmain(argc, argv));
}

static int
stdmain(int argc, char **argv)
{
        char    *p;
        char    *p1;
        char    *parg;
        int     c;
        int     i;
        int     pgerrflg = 0;   /* err flg: non-numeric arg w/p & g options */
        size_t  size, len;
        DIR     *dirp;
        struct dirent *dentp;
        pid_t   maxpid;
        pid_t   id;
        int     ret;
        char    loc_stime_str[32];

        (void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
#define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it weren't */
#endif
        (void) textdomain(TEXT_DOMAIN);

        (void) memset(&euid_tbl, 0, sizeof (euid_tbl));
        (void) memset(&ruid_tbl, 0, sizeof (ruid_tbl));
        (void) memset(&egid_tbl, 0, sizeof (egid_tbl));
        (void) memset(&rgid_tbl, 0, sizeof (rgid_tbl));

        kbytes_per_page = sysconf(_SC_PAGESIZE) / 1024;

        (void) gettimeofday(&now, NULL);

        /*
         * calculate width of pid fields based on configured MAXPID
         * (must be at least 5 to retain output format compatibility)
         */
        id = maxpid = (pid_t)sysconf(_SC_MAXPID);
        pidwidth = 1;
        while ((id /= 10) > 0)
                ++pidwidth;
        pidwidth = pidwidth < 5 ? 5 : pidwidth;

        fname[F_PID].width = fname[F_PPID].width = pidwidth;
        fname[F_PGID].width = fname[F_SID].width = pidwidth;

        /*
         * TRANSLATION_NOTE
         * Specify the printf format with width and precision for
         * the STIME field.
         */
        len = snprintf(loc_stime_str, sizeof (loc_stime_str),
            dcgettext(NULL, "%8.8s", LC_TIME), "STIME");
        if (len >= sizeof (loc_stime_str))
                len = sizeof (loc_stime_str) - 1;

        fname[F_STIME].width = fname[F_STIME].minwidth = len;

        while ((c = getopt(argc, argv, "jlfceAadLPWyZHh:t:p:g:u:U:G:n:s:o:z:"))
            != EOF)
                switch (c) {
                case 'H':               /* Show home lgroups */
                        Hflg++;
                        break;
                case 'h':
                        /*
                         * Show processes/threads with given home lgroups
                         */
                        hflg++;
                        p1 = optarg;
                        do {
                                int id;

                                /*
                                 * Get all IDs in the list, verify for
                                 * correctness and place in lgrps array.
                                 */
                                parg = getarg(&p1);
                                /* Convert string to integer */
                                ret = str2id(parg, (pid_t *)&id, 0,
                                    MAX_LGRP_ID);
                                /* Complain if ID didn't parse correctly */
                                if (ret != 0) {
                                        pgerrflg++;
                                        (void) fprintf(stderr,
                                            gettext("ps: %s "), parg);
                                        if (ret == EINVAL)
                                                (void) fprintf(stderr,
                                                    gettext("is an invalid "
                                                    "non-numeric argument"));
                                        else
                                                (void) fprintf(stderr,
                                                    gettext("exceeds valid "
                                                    "range"));
                                        (void) fprintf(stderr,
                                            gettext(" for -h option\n"));
                                        continue;
                                }

                                /* Extend lgrps array if needed */
                                if (nlgrps == lgrps_size) {
                                        /* Double the size of the lgrps array */
                                        if (lgrps_size == 0)
                                                lgrps_size = SIZ;
                                        lgrps_size *= 2;
                                        lgrps = Realloc(lgrps,
                                            lgrps_size * sizeof (int));
                                }
                                /* place the id in the lgrps table */
                                lgrps[nlgrps++] = id;
                        } while (*p1);
                        break;
                case 'l':               /* long listing */
                        lflg++;
                        break;
                case 'f':               /* full listing */
                        fflg++;
                        break;
                case 'j':
                        jflg++;
                        break;
                case 'c':
                        /*
                         * Format output to reflect scheduler changes:
                         * high numbers for high priorities and don't
                         * print nice or p_cpu values.  'c' option only
                         * effective when used with 'l' or 'f' options.
                         */
                        cflg++;
                        break;
                case 'A':               /* list every process */
                case 'e':               /* (obsolete) list every process */
                        Aflg++;
                        tflg = Gflg = Uflg = uflg = pflg = gflg = sflg = 0;
                        zflg = hflg = 0;
                        break;
                case 'a':
                        /*
                         * Same as 'e' except no session group leaders
                         * and no non-terminal processes.
                         */
                        aflg++;
                        break;
                case 'd':       /* same as e except no session leaders */
                        dflg++;
                        break;
                case 'L':       /* show lwps */
                        Lflg++;
                        break;
                case 'P':       /* show bound processor */
                        Pflg++;
                        break;
                case 'W':       /* truncate long names */
                        Wflg++;
                        break;
                case 'y':       /* omit F & ADDR, report RSS & SZ in Kby */
                        yflg++;
                        break;
                case 'n':       /* no longer needed; retain as no-op */
                        (void) fprintf(stderr,
                            gettext("ps: warning: -n option ignored\n"));
                        break;
                case 't':               /* terminals */
#define TSZ     30
                        tflg++;
                        p1 = optarg;
                        do {
                                char nambuf[TSZ+6];     /* for "/dev/" + '\0' */
                                struct stat64 s;
                                parg = getarg(&p1);
                                p = Realloc(NULL, TSZ+1);       /* for '\0' */
                                /* zero the buffer before using it */
                                p[0] = '\0';
                                size = TSZ;
                                if (isdigit(*parg)) {
                                        (void) strcpy(p, "tty");
                                        size -= 3;
                                }
                                (void) strncat(p, parg, size);
                                if (ntty == ttysz) {
                                        if ((ttysz *= 2) == 0)
                                                ttysz = NTTYS;
                                        tty = Realloc(tty,
                                            (ttysz + 1) * sizeof (struct tty));
                                }
                                tty[ntty].tdev = PRNODEV;
                                (void) strcpy(nambuf, "/dev/");
                                (void) strcat(nambuf, p);
                                if (stat64(nambuf, &s) == 0)
                                        tty[ntty].tdev = s.st_rdev;
                                tty[ntty++].tname = p;
                        } while (*p1);
                        break;
                case 'p':               /* proc ids */
                        pflg++;
                        p1 = optarg;
                        do {
                                pid_t id;

                                parg = getarg(&p1);
                                if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
                                        pgerrflg++;
                                        (void) fprintf(stderr,
                                            gettext("ps: %s "), parg);
                                        if (ret == EINVAL)
                                                (void) fprintf(stderr,
                                                    gettext("is an invalid "
                                                    "non-numeric argument"));
                                        else
                                                (void) fprintf(stderr,
                                                    gettext("exceeds valid "
                                                    "range"));
                                        (void) fprintf(stderr,
                                            gettext(" for -p option\n"));
                                        continue;
                                }

                                if (npid == pidsz) {
                                        if ((pidsz *= 2) == 0)
                                                pidsz = SIZ;
                                        pid = Realloc(pid,
                                            pidsz * sizeof (pid_t));
                                }
                                pid[npid++] = id;
                        } while (*p1);
                        break;
                case 's':               /* session */
                        sflg++;
                        p1 = optarg;
                        do {
                                pid_t id;

                                parg = getarg(&p1);
                                if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
                                        pgerrflg++;
                                        (void) fprintf(stderr,
                                            gettext("ps: %s "), parg);
                                        if (ret == EINVAL)
                                                (void) fprintf(stderr,
                                                    gettext("is an invalid "
                                                    "non-numeric argument"));
                                        else
                                                (void) fprintf(stderr,
                                                    gettext("exceeds valid "
                                                    "range"));
                                        (void) fprintf(stderr,
                                            gettext(" for -s option\n"));
                                        continue;
                                }

                                if (nsessid == sessidsz) {
                                        if ((sessidsz *= 2) == 0)
                                                sessidsz = SIZ;
                                        sessid = Realloc(sessid,
                                            sessidsz * sizeof (pid_t));
                                }
                                sessid[nsessid++] = id;
                        } while (*p1);
                        break;
                case 'g':               /* proc group */
                        gflg++;
                        p1 = optarg;
                        do {
                                pid_t id;

                                parg = getarg(&p1);
                                if ((ret = str2id(parg, &id, 0, maxpid)) != 0) {
                                        pgerrflg++;
                                        (void) fprintf(stderr,
                                            gettext("ps: %s "), parg);
                                        if (ret == EINVAL)
                                                (void) fprintf(stderr,
                                                    gettext("is an invalid "
                                                    "non-numeric argument"));
                                        else
                                                (void) fprintf(stderr,
                                                    gettext("exceeds valid "
                                                    "range"));
                                        (void) fprintf(stderr,
                                            gettext(" for -g option\n"));
                                        continue;
                                }

                                if (ngrpid == grpidsz) {
                                        if ((grpidsz *= 2) == 0)
                                                grpidsz = SIZ;
                                        grpid = Realloc(grpid,
                                            grpidsz * sizeof (pid_t));
                                }
                                grpid[ngrpid++] = id;
                        } while (*p1);
                        break;
                case 'u':               /* effective user name or number */
                        uflg++;
                        p1 = optarg;
                        do {
                                parg = getarg(&p1);
                                add_ugentry(&euid_tbl, parg);
                        } while (*p1);
                        break;
                case 'U':               /* real user name or number */
                        Uflg++;
                        p1 = optarg;
                        do {
                                parg = getarg(&p1);
                                add_ugentry(&ruid_tbl, parg);
                        } while (*p1);
                        break;
                case 'G':               /* real group name or number */
                        Gflg++;
                        p1 = optarg;
                        do {
                                parg = getarg(&p1);
                                add_ugentry(&rgid_tbl, parg);
                        } while (*p1);
                        break;
                case 'o':               /* output format */
                        p = optarg;
                        while ((p = parse_format(p)) != NULL)
                                ;
                        break;
                case 'z':               /* zone name or number */
                        zflg++;
                        p1 = optarg;
                        do {
                                zoneid_t id;

                                parg = getarg(&p1);
                                if (zone_get_id(parg, &id) != 0) {
                                        pgerrflg++;
                                        (void) fprintf(stderr,
                                            gettext("ps: unknown zone %s\n"),
                                            parg);
                                        continue;
                                }

                                if (nzoneid == zoneidsz) {
                                        if ((zoneidsz *= 2) == 0)
                                                zoneidsz = SIZ;
                                        zoneid = Realloc(zoneid,
                                            zoneidsz * sizeof (zoneid_t));
                                }
                                zoneid[nzoneid++] = id;
                        } while (*p1);
                        break;
                case 'Z':               /* show zone name */
                        Zflg++;
                        break;
                default:                        /* error on ? */
                        errflg++;
                        break;
                }

        if (errflg || optind < argc || pgerrflg)
                usage();

        if (tflg)
                tty[ntty].tname = NULL;
        /*
         * If an appropriate option has not been specified, use the
         * current terminal and effective uid as the default.
         */
        if (!(aflg|Aflg|dflg|Gflg|hflg|Uflg|uflg|tflg|pflg|gflg|sflg|zflg)) {
                psinfo_t info;
                int procfd;
                char *name;
                char pname[100];

                /* get our own controlling tty name using /proc */
                (void) snprintf(pname, sizeof (pname),
                    "%s/self/psinfo", procdir);
                if ((procfd = open(pname, O_RDONLY)) < 0 ||
                    read(procfd, (char *)&info, sizeof (info)) < 0 ||
                    info.pr_ttydev == PRNODEV) {
                        (void) fprintf(stderr,
                            gettext("ps: no controlling terminal\n"));
                        exit(1);
                }
                (void) close(procfd);

                i = 0;
                name = gettty(&info);
                if (*name == '?') {
                        (void) fprintf(stderr,
                            gettext("ps: can't find controlling terminal\n"));
                        exit(1);
                }
                if (ntty == ttysz) {
                        if ((ttysz *= 2) == 0)
                                ttysz = NTTYS;
                        tty = Realloc(tty, (ttysz + 1) * sizeof (struct tty));
                }
                tty[ntty].tdev = info.pr_ttydev;
                tty[ntty++].tname = name;
                tty[ntty].tname = NULL;
                tflg++;
                tuid = getuid();
        }
        if (Aflg) {
                Gflg = Uflg = uflg = pflg = sflg = gflg = aflg = dflg = 0;
                zflg = hflg = 0;
        }
        if (Aflg | aflg | dflg)
                tflg = 0;

        i = 0;          /* prepare to exit on name lookup errors */
        i += uconv(&euid_tbl);
        i += uconv(&ruid_tbl);
        i += gconv(&egid_tbl);
        i += gconv(&rgid_tbl);
        if (i)
                exit(1);

        /* allocate a buffer for lwpsinfo structures */
        lpbufsize = 4096;
        if (Lflg && (lpsinfobuf = malloc(lpbufsize)) == NULL) {
                (void) fprintf(stderr,
                    gettext("ps: no memory\n"));
                exit(1);
        }

        if (fields) {   /* print user-specified header */
                if (do_header) {
                        struct field *f;

                        for (f = fields; f != NULL; f = f->next) {
                                if (f != fields)
                                        (void) printf(" ");
                                switch (f->fname) {
                                case F_TTY:
                                        (void) printf("%-*s",
                                            f->width, f->header);
                                        break;
                                case F_LWPNAME:
                                case F_FNAME:
                                case F_COMM:
                                case F_ARGS:
                                        /*
                                         * Print these headers full width
                                         * unless they appear at the end.
                                         */
                                        if (f->next != NULL) {
                                                (void) printf("%-*s",
                                                    f->width, f->header);
                                        } else {
                                                (void) printf("%s",
                                                    f->header);
                                        }
                                        break;
                                default:
                                        (void) printf("%*s",
                                            f->width, f->header);
                                        break;
                                }
                        }
                        (void) printf("\n");
                }
        } else {        /* print standard header */
                /*
                 * All fields before 'PID' are printed with a trailing space
                 * as a separator and that is how we print the headers too.
                 */
                if (lflg) {
                        if (yflg)
                                (void) printf("S ");
                        else
                                (void) printf(" F S ");
                }
                if (Zflg)
                        (void) printf("    ZONE ");
                if (fflg) {
                        (void) printf("     UID ");
                } else if (lflg)
                        (void) printf("   UID ");

                (void) printf("%*s", pidwidth,  "PID");
                if (lflg || fflg)
                        (void) printf(" %*s", pidwidth, "PPID");
                if (jflg)
                        (void) printf(" %*s %*s", pidwidth, "PGID",
                            pidwidth, "SID");
                if (Lflg)
                        (void) printf("   LWP");
                if (Pflg)
                        (void) printf(" PSR");
                if (Lflg && fflg)
                        (void) printf("  NLWP");
                if (cflg)
                        (void) printf("  CLS PRI");
                else if (lflg || fflg) {
                        (void) printf("   C");
                        if (lflg)
                                (void) printf(" PRI NI");
                }
                if (lflg) {
                        if (yflg)
                                (void) printf("   RSS     SZ    WCHAN");
                        else
                                (void) printf("     ADDR     SZ    WCHAN");
                }
                if (fflg)
                        (void) printf(" %s", loc_stime_str);
                if (Hflg)
                        (void) printf(" LGRP");
                if (Lflg)
                        (void) printf(" TTY        LTIME CMD\n");
                else
                        (void) printf(" TTY         TIME CMD\n");
        }


        if (pflg && !(aflg|Aflg|dflg|Gflg|Uflg|uflg|hflg|tflg|gflg|sflg|zflg) &&
            npid <= PTHRESHOLD) {
                /*
                 * If we are looking at specific processes go straight
                 * to their /proc entries and don't scan /proc.
                 */
                int i;

                (void) qsort(pid, npid, sizeof (pid_t), pidcmp);
                for (i = 0; i < npid; i++) {
                        char pname[12];

                        if (i >= 1 && pid[i] == pid[i - 1])
                                continue;
                        (void) sprintf(pname, "%d", (int)pid[i]);
                        if (print_proc(pname) == 0)
                                retcode = 0;
                }
        } else {
                /*
                 * Determine which processes to print info about by searching
                 * the /proc directory and looking at each process.
                 */
                if ((dirp = opendir(procdir)) == NULL) {
                        (void) fprintf(stderr,
                            gettext("ps: cannot open PROC directory %s\n"),
                            procdir);
                        exit(1);
                }

                /* for each active process --- */
                while ((dentp = readdir(dirp)) != NULL) {
                        if (dentp->d_name[0] == '.')    /* skip . and .. */
                                continue;
                        if (print_proc(dentp->d_name) == 0)
                                retcode = 0;
                }

                (void) closedir(dirp);
        }
        return (retcode);
}


int
print_proc(char *pid_name)
{
        char    pname[PATH_MAX];
        int     pdlen;
        int     found;
        int     procfd; /* filedescriptor for /proc/nnnnn/psinfo */
        char    *tp;    /* ptr to ttyname,  if any */
        psinfo_t info;  /* process information from /proc */

        pdlen = snprintf(pname, sizeof (pname), "%s/%s/", procdir, pid_name);
        if (pdlen >= sizeof (pname) - 10)
                return (1);
retry:
        (void) strcpy(&pname[pdlen], "psinfo");
        if ((procfd = open(pname, O_RDONLY)) == -1) {
                /* Process may have exited meanwhile. */
                return (1);
        }
        /*
         * Get the info structure for the process and close quickly.
         */
        if (read(procfd, (char *)&info, sizeof (info)) < 0) {
                int     saverr = errno;

                (void) close(procfd);
                if (saverr == EAGAIN)
                        goto retry;
                if (saverr != ENOENT)
                        (void) fprintf(stderr,
                            gettext("ps: read() on %s: %s\n"),
                            pname, err_string(saverr));
                return (1);
        }
        (void) close(procfd);

        found = 0;
        if (info.pr_lwp.pr_state == 0)  /* can't happen? */
                return (1);

        /*
         * Omit session group leaders for 'a' and 'd' options.
         */
        if ((info.pr_pid == info.pr_sid) && (dflg || aflg))
                return (1);
        if (Aflg || dflg)
                found++;
        else if (pflg && search(pid, npid, info.pr_pid))
                found++;        /* ppid in p option arg list */
        else if (uflg && ugfind((id_t)info.pr_euid, &euid_tbl))
                found++;        /* puid in u option arg list */
        else if (Uflg && ugfind((id_t)info.pr_uid, &ruid_tbl))
                found++;        /* puid in U option arg list */
#ifdef NOT_YET
        else if (gflg && ugfind((id_t)info.pr_egid, &egid_tbl))
                found++;        /* pgid in g option arg list */
#endif  /* NOT_YET */
        else if (Gflg && ugfind((id_t)info.pr_gid, &rgid_tbl))
                found++;        /* pgid in G option arg list */
        else if (gflg && search(grpid, ngrpid, info.pr_pgid))
                found++;        /* grpid in g option arg list */
        else if (sflg && search(sessid, nsessid, info.pr_sid))
                found++;        /* sessid in s option arg list */
        else if (zflg && search(zoneid, nzoneid, info.pr_zoneid))
                found++;        /* zoneid in z option arg list */
        else if (hflg && search((pid_t *)lgrps, nlgrps, info.pr_lwp.pr_lgrp))
                found++;        /* home lgroup in h option arg list */
        if (!found && !tflg && !aflg)
                return (1);
        if (!prfind(found, &info, &tp))
                return (1);
        if (Lflg && (info.pr_nlwp + info.pr_nzomb) > 1) {
                ssize_t prsz;
                long nlwp = 0;
                lwpsinfo_t *lwpsinfo;   /* array of lwpsinfo structs */

                (void) strcpy(&pname[pdlen], "lpsinfo");
                if ((procfd = open(pname, O_RDONLY)) == -1)
                        return (1);
                /*
                 * Get the info structures for the lwps.
                 */
                prsz = read(procfd, lpsinfobuf, lpbufsize);
                if (prsz == -1) {
                        int     saverr = errno;

                        (void) close(procfd);
                        if (saverr == EAGAIN)
                                goto retry;
                        if (saverr != ENOENT)
                                (void) fprintf(stderr,
                                    gettext("ps: read() on %s: %s\n"),
                                    pname, err_string(saverr));
                        return (1);
                }
                (void) close(procfd);
                if (prsz == lpbufsize) {
                        /*
                         * buffer overflow. Realloc new buffer.
                         * Error handling is done in Realloc().
                         */
                        lpbufsize *= 2;
                        lpsinfobuf = Realloc(lpsinfobuf, lpbufsize);
                        goto retry;
                }
                if (lpsinfobuf->pr_nent != (info.pr_nlwp + info.pr_nzomb))
                        goto retry;
                lwpsinfo = (lwpsinfo_t *)(lpsinfobuf + 1);
                do {
                        info.pr_lwp = *lwpsinfo;
                        prcom(&info, tp);
                        lwpsinfo = (lwpsinfo_t *)((char *)lwpsinfo +
                            lpsinfobuf->pr_entsize);
                } while (++nlwp < lpsinfobuf->pr_nent);
        } else {
                prcom(&info, tp);
        }
        return (0);
}

static int
field_cmp(const void *l, const void *r)
{
        struct def_field *lhs = *((struct def_field **)l);
        struct def_field *rhs = *((struct def_field **)r);

        return (strcmp(lhs->fname, rhs->fname));
}

static void
usage(void)             /* print usage message and quit */
{
        struct def_field *df, *sorted[NFIELDS];
        int pos = 80, i = 0;

        static char usage1[] =
            "ps [ -aAdefHlcjLPWyZ ] [ -o format ] [ -t termlist ]";
        static char usage2[] =
            "\t[ -u userlist ] [ -U userlist ] [ -G grouplist ]";
        static char usage3[] =
            "\t[ -p proclist ] [ -g pgrplist ] [ -s sidlist ]";
        static char usage4[] =
            "\t[ -z zonelist ] [ -h lgrplist ]";
        static char usage5[] =
            "  'format' is one or more of:";

        (void) fprintf(stderr,
            gettext("usage: %s\n%s\n%s\n%s\n%s"),
            gettext(usage1), gettext(usage2), gettext(usage3),
            gettext(usage4), gettext(usage5));

        /*
         * Now print out the possible output formats such that they neatly fit
         * into eighty columns.  Note that the fact that we are determining
         * this output programmatically means that a gettext() is impossible --
         * but it would be a mistake to localize the output formats anyway as
         * they are tokens for input, not output themselves.
         */
        for (df = &fname[0]; df < &fname[NFIELDS]; df++)
                sorted[i++] = df;

        (void) qsort(sorted, NFIELDS, sizeof (void *), field_cmp);

        for (i = 0; i < NFIELDS; i++) {
                if (pos + strlen((df = sorted[i])->fname) + 1 >= 80) {
                        (void) fprintf(stderr, "\n\t");
                        pos = 8;
                }

                (void) fprintf(stderr, "%s%s", pos > 8 ? " " : "", df->fname);
                pos += strlen(df->fname) + 1;
        }

        (void) fprintf(stderr, "\n");

        exit(1);
}

/*
 * getarg() finds the next argument in list and copies arg into argbuf.
 * p1 first pts to arg passed back from getopt routine.  p1 is then
 * bumped to next character that is not a comma or blank -- p1 NULL
 * indicates end of list.
 */
static char *
getarg(char **pp1)
{
        static char argbuf[ARGSIZ];
        char *p1 = *pp1;
        char *parga = argbuf;
        int c;

        while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
                p1++;

        while ((c = *p1) != '\0' && c != ',' && !isspace(c)) {
                if (parga < argbuf + ARGSIZ - 1)
                        *parga++ = c;
                p1++;
        }
        *parga = '\0';

        while ((c = *p1) != '\0' && (c == ',' || isspace(c)))
                p1++;

        *pp1 = p1;

        return (argbuf);
}

/*
 * parse_format() takes the argument to the -o option,
 * sets up the next output field structure, and returns
 * a pointer to any further output field specifier(s).
 * As a side-effect, it increments errflg if encounters a format error.
 */
static char *
parse_format(char *arg)
{
        int c;
        char *name;
        char *header = NULL;
        int width = 0;
        struct def_field *df;
        struct field *f;

        while ((c = *arg) != '\0' && (c == ',' || isspace(c)))
                arg++;
        if (c == '\0')
                return (NULL);
        name = arg;
        arg = strpbrk(arg, " \t\r\v\f\n,=");
        if (arg != NULL) {
                c = *arg;
                *arg++ = '\0';
                if (c == '=') {
                        char *s;

                        header = arg;
                        arg = NULL;
                        width = strlen(header);
                        s = header + width;
                        while (s > header && isspace(*--s))
                                *s = '\0';
                        while (isspace(*header))
                                header++;
                }
        }
        for (df = &fname[0]; df < &fname[NFIELDS]; df++)
                if (strcmp(name, df->fname) == 0) {
                        if (strcmp(name, "lwp") == 0 ||
                            strcmp(name, "lwpname") == 0)
                                Lflg++;
                        break;
                }
        if (df >= &fname[NFIELDS]) {
                (void) fprintf(stderr,
                    gettext("ps: unknown output format: -o %s\n"),
                    name);
                errflg++;
                return (arg);
        }
        if ((f = malloc(sizeof (*f))) == NULL) {
                (void) fprintf(stderr,
                    gettext("ps: malloc() for output format failed, %s\n"),
                    err_string(errno));
                exit(1);
        }
        f->next = NULL;
        f->fname = df - &fname[0];
        f->header = header? header : df->header;
        if (width == 0)
                width = df->width;
        if (*f->header != '\0')
                do_header = 1;
        f->width = max(width, df->minwidth);

        if (fields == NULL)
                fields = last_field = f;
        else {
                last_field->next = f;
                last_field = f;
        }

        return (arg);
}

static char *
devlookup(dev_t ddev)
{
        struct devl *dp;
        int i;

        for (dp = devl, i = 0; i < ndev; dp++, i++) {
                if (dp->ddev == ddev)
                        return (dp->dname);
        }
        return (NULL);
}

static char *
devadd(char *name, dev_t ddev)
{
        struct devl *dp;
        int leng, start, i;

        if (ndev == maxdev) {
                maxdev += DNINCR;
                devl = Realloc(devl, maxdev * sizeof (struct devl));
        }
        dp = &devl[ndev++];

        dp->ddev = ddev;
        if (name == NULL) {
                (void) strcpy(dp->dname, "??");
                return (dp->dname);
        }

        leng = strlen(name);
        /* Strip off /dev/ */
        if (leng < DNSIZE + 4)
                (void) strcpy(dp->dname, &name[5]);
        else {
                start = leng - DNSIZE - 1;

                for (i = start; i < leng && name[i] != '/'; i++)
                                ;
                if (i == leng)
                        (void) strncpy(dp->dname, &name[start], DNSIZE);
                else
                        (void) strncpy(dp->dname, &name[i+1], DNSIZE);
        }
        return (dp->dname);
}

/*
 * gettty returns the user's tty number or ? if none.
 */
static char *
gettty(psinfo_t *psinfo)
{
        extern char *_ttyname_dev(dev_t, char *, size_t);
        static zoneid_t zid = -1;
        char devname[TTYNAME_MAX];
        char *retval;

        if (zid == -1)
                zid = getzoneid();

        if (psinfo->pr_ttydev == PRNODEV || psinfo->pr_zoneid != zid)
                return ("?");

        if ((retval = devlookup(psinfo->pr_ttydev)) != NULL)
                return (retval);

        retval = _ttyname_dev(psinfo->pr_ttydev, devname, sizeof (devname));

        return (devadd(retval, psinfo->pr_ttydev));
}

/*
 * Find the process's tty and return 1 if process is to be printed.
 */
static int
prfind(int found, psinfo_t *psinfo, char **tpp)
{
        char    *tp;
        struct tty *ttyp;

        if (psinfo->pr_nlwp == 0) {
                /* process is a zombie */
                *tpp = "?";
                if (tflg && !found)
                        return (0);
                return (1);
        }

        /*
         * Get current terminal.  If none ("?") and 'a' is set, don't print
         * info.  If 't' is set, check if term is in list of desired terminals
         * and print it if it is.
         */
        tp = gettty(psinfo);
        if (aflg && *tp == '?') {
                *tpp = tp;
                return (0);
        }
        if (tflg && !found) {
                int match = 0;
                char *other = NULL;
                for (ttyp = tty; ttyp->tname != NULL; ttyp++) {
                        /*
                         * Look for a name match
                         */
                        if (strcmp(tp, ttyp->tname) == 0) {
                                match = 1;
                                break;
                        }
                        /*
                         * Look for same device under different names.
                         */
                        if ((other == NULL) &&
                            (ttyp->tdev != PRNODEV) &&
                            (psinfo->pr_ttydev == ttyp->tdev))
                                other = ttyp->tname;
                }
                if (!match && (other != NULL)) {
                        /*
                         * found under a different name
                         */
                        match = 1;
                        tp = other;
                }
                if (!match || (tuid != (uid_t)-1 && tuid != psinfo->pr_euid)) {
                        /*
                         * not found OR not matching euid
                         */
                        *tpp = tp;
                        return (0);
                }
        }
        *tpp = tp;
        return (1);
}

/*
 * Print info about the process.
 */
static void
prcom(psinfo_t *psinfo, char *ttyp)
{
        char    *cp;
        long    tm;
        int     bytesleft;
        int     wcnt, length;
        wchar_t wchar;
        struct passwd *pwd;
        int     zombie_lwp;
        char    zonename[ZONENAME_MAX];

        /*
         * If process is zombie, call zombie print routine and return.
         */
        if (psinfo->pr_nlwp == 0) {
                if (fields != NULL)
                        pr_fields(psinfo, ttyp, print_zombie_field);
                else
                        przom(psinfo);
                return;
        }

        zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');

        /*
         * If user specified '-o format', print requested fields and return.
         */
        if (fields != NULL) {
                pr_fields(psinfo, ttyp, print_field);
                return;
        }

        /*
         * All fields before 'PID' are printed with a trailing space as a
         * separator, rather than keeping track of which column is first.  All
         * other fields are printed with a leading space.
         */
        if (lflg) {
                if (!yflg)
                        (void) printf("%2x ", psinfo->pr_flag & 0377); /* F */
                (void) printf("%c ", psinfo->pr_lwp.pr_sname);  /* S */
        }

        if (Zflg) {                                             /* ZONE */
                if (getzonenamebyid(psinfo->pr_zoneid, zonename,
                    sizeof (zonename)) < 0) {
                        if (snprintf(NULL, 0, "%d",
                            ((int)psinfo->pr_zoneid)) > 7)
                                (void) printf(" %6.6d%c ",
                                    ((int)psinfo->pr_zoneid), '*');
                        else
                                (void) printf(" %7.7d ",
                                    ((int)psinfo->pr_zoneid));
                } else {
                        size_t nw;

                        nw = mbstowcs(NULL, zonename, 0);
                        if (nw == (size_t)-1)
                                (void) printf("%8.8s ", "ERROR");
                        else if (nw > 8)
                                (void) wprintf(L"%7.7s%c ", zonename, '*');
                        else
                                (void) wprintf(L"%8.8s ", zonename);
                }
        }

        if (fflg) {                                             /* UID */
                if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
                        size_t nw;

                        nw = mbstowcs(NULL, pwd->pw_name, 0);
                        if (nw == (size_t)-1)
                                (void) printf("%8.8s ", "ERROR");
                        else if (nw > 8)
                                (void) wprintf(L"%7.7s%c ", pwd->pw_name, '*');
                        else
                                (void) wprintf(L"%8.8s ", pwd->pw_name);
                } else {
                        if (snprintf(NULL, 0, "%u",
                            (psinfo->pr_euid)) > 7)
                                (void) printf(" %6.6u%c ", psinfo->pr_euid,
                                    '*');
                        else
                                (void) printf(" %7.7u ", psinfo->pr_euid);
                }
        } else if (lflg) {
                if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6)
                        (void) printf("%5.5u%c ", psinfo->pr_euid, '*');
                else
                        (void) printf("%6u ", psinfo->pr_euid);
        }
        (void) printf("%*d", pidwidth, (int)psinfo->pr_pid); /* PID */
        if (lflg || fflg)
                (void) printf(" %*d", pidwidth,
                    (int)psinfo->pr_ppid); /* PPID */
        if (jflg) {
                (void) printf(" %*d", pidwidth,
                    (int)psinfo->pr_pgid);      /* PGID */
                (void) printf(" %*d", pidwidth,
                    (int)psinfo->pr_sid);       /* SID  */
        }
        if (Lflg)
                (void) printf(" %5d", (int)psinfo->pr_lwp.pr_lwpid); /* LWP */
        if (Pflg) {
                if (psinfo->pr_lwp.pr_bindpro == PBIND_NONE)    /* PSR */
                        (void) printf("   -");
                else
                        (void) printf(" %3d", psinfo->pr_lwp.pr_bindpro);
        }
        if (Lflg && fflg)                                       /* NLWP */
                (void) printf(" %5d", psinfo->pr_nlwp + psinfo->pr_nzomb);
        if (cflg) {
                if (zombie_lwp)                                 /* CLS */
                        (void) printf("     ");
                else
                        (void) printf(" %4s", psinfo->pr_lwp.pr_clname);
                (void) printf(" %3d", psinfo->pr_lwp.pr_pri);   /* PRI */
        } else if (lflg || fflg) {
                (void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C   */
                if (lflg) {                                         /* PRI NI */
                        /*
                         * Print priorities the old way (lower numbers
                         * mean higher priority) and print nice value
                         * for time sharing procs.
                         */
                        (void) printf(" %3d", psinfo->pr_lwp.pr_oldpri);
                        if (psinfo->pr_lwp.pr_oldpri != 0)
                                (void) printf(" %2d", psinfo->pr_lwp.pr_nice);
                        else
                                (void) printf(" %2.2s",
                                    psinfo->pr_lwp.pr_clname);
                }
        }
        if (lflg) {
                if (yflg) {
                        if (psinfo->pr_flag & SSYS)             /* RSS */
                                (void) printf("     0");
                        else if (psinfo->pr_rssize)
                                (void) printf(" %5lu",
                                    (ulong_t)psinfo->pr_rssize);
                        else
                                (void) printf("     ?");
                        if (psinfo->pr_flag & SSYS)             /* SZ */
                                (void) printf("      0");
                        else if (psinfo->pr_size)
                                (void) printf(" %6lu",
                                    (ulong_t)psinfo->pr_size);
                        else
                                (void) printf("      ?");
                } else {
#ifndef _LP64
                        if (psinfo->pr_addr)                    /* ADDR */
                                (void) printf(" %8lx",
                                    (ulong_t)psinfo->pr_addr);
                        else
#endif
                                (void) printf("        ?");
                        if (psinfo->pr_flag & SSYS)             /* SZ */
                                (void) printf("      0");
                        else if (psinfo->pr_size)
                                (void) printf(" %6lu",
                                    (ulong_t)psinfo->pr_size / kbytes_per_page);
                        else
                                (void) printf("      ?");
                }
                if (psinfo->pr_lwp.pr_sname != 'S')             /* WCHAN */
                        (void) printf("         ");
#ifndef _LP64
                else if (psinfo->pr_lwp.pr_wchan)
                        (void) printf(" %8lx",
                            (ulong_t)psinfo->pr_lwp.pr_wchan);
#endif
                else
                        (void) printf("        ?");
        }
        if (fflg) {                                             /* STIME */
                int width = fname[F_STIME].width;
                if (Lflg)
                        prtime(psinfo->pr_lwp.pr_start, width + 1, 1);
                else
                        prtime(psinfo->pr_start, width + 1, 1);
        }

        if (Hflg) {
                /* Display home lgroup */
                (void) printf(" %4d", (int)psinfo->pr_lwp.pr_lgrp);
        }

        (void) printf(" %-8.14s", ttyp);                        /* TTY */
        if (Lflg) {
                tm = psinfo->pr_lwp.pr_time.tv_sec;
                if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000)
                        tm++;
        } else {
                tm = psinfo->pr_time.tv_sec;
                if (psinfo->pr_time.tv_nsec > 500000000)
                        tm++;
        }
        (void) printf(" %4ld:%.2ld", tm / 60, tm % 60);         /* [L]TIME */

        if (zombie_lwp) {
                (void) printf(" <defunct>\n");
                return;
        }

        if (!fflg) {                                            /* CMD */
                wcnt = namencnt(psinfo->pr_fname, 16, 8);
                (void) printf(" %.*s\n", wcnt, psinfo->pr_fname);
                return;
        }


        /*
         * PRARGSZ == length of cmd arg string.
         */
        psinfo->pr_psargs[PRARGSZ-1] = '\0';
        bytesleft = PRARGSZ;
        for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
                length = mbtowc(&wchar, cp, MB_LEN_MAX);
                if (length == 0)
                        break;
                if (length < 0 || !iswprint(wchar)) {
                        if (length < 0)
                                length = 1;
                        if (bytesleft <= length) {
                                *cp = '\0';
                                break;
                        }
                        /* omit the unprintable character */
                        (void) memmove(cp, cp+length, bytesleft-length);
                        length = 0;
                }
                bytesleft -= length;
        }
        wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ);
        (void) printf(" %.*s\n", wcnt, psinfo->pr_psargs);
}

/*
 * Print percent from 16-bit binary fraction [0 .. 1]
 * Round up .01 to .1 to indicate some small percentage (the 0x7000 below).
 */
static void
prtpct(ushort_t pct, int width)
{
        uint_t value = pct;     /* need 32 bits to compute with */

        value = ((value * 1000) + 0x7000) >> 15;        /* [0 .. 1000] */
        if (value >= 1000)
                value = 999;
        if ((width -= 2) < 2)
                width = 2;
        (void) printf("%*u.%u", width, value / 10, value % 10);
}

static void
print_time(time_t tim, int width)
{
        char buf[30];
        time_t seconds;
        time_t minutes;
        time_t hours;
        time_t days;

        if (tim < 0) {
                (void) printf("%*s", width, "-");
                return;
        }

        seconds = tim % 60;
        tim /= 60;
        minutes = tim % 60;
        tim /= 60;
        hours = tim % 24;
        days = tim / 24;

        if (days > 0) {
                (void) snprintf(buf, sizeof (buf), "%ld-%2.2ld:%2.2ld:%2.2ld",
                    days, hours, minutes, seconds);
        } else if (hours > 0) {
                (void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld:%2.2ld",
                    hours, minutes, seconds);
        } else {
                (void) snprintf(buf, sizeof (buf), "%2.2ld:%2.2ld",
                    minutes, seconds);
        }

        (void) printf("%*s", width, buf);
}

static void
print_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
{
        int width = f->width;
        struct passwd *pwd;
        struct group *grp;
        time_t cputime;
        int bytesleft;
        int wcnt;
        wchar_t wchar;
        char *cp;
        int length;
        ulong_t mask;
        char c = '\0', *csave = NULL;
        int zombie_lwp;

        zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');

        switch (f->fname) {
        case F_RUSER:
                if ((pwd = getpwuid(psinfo->pr_uid)) != NULL) {
                        size_t nw;

                        nw = mbstowcs(NULL, pwd->pw_name, 0);
                        if (nw == (size_t)-1)
                                (void) printf("%*s ", width, "ERROR");
                        else if (Wflg && nw > width)
                                (void) wprintf(L"%.*s%c", width - 1,
                                    pwd->pw_name, '*');
                        else
                                (void) wprintf(L"%*s", width, pwd->pw_name);
                } else {
                        if (Wflg && snprintf(NULL, 0, "%u",
                            (psinfo->pr_uid)) > width)

                                (void) printf("%*u%c", width - 1,
                                    psinfo->pr_uid, '*');
                        else
                                (void) printf("%*u", width, psinfo->pr_uid);
                }
                break;
        case F_USER:
                if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
                        size_t nw;

                        nw = mbstowcs(NULL, pwd->pw_name, 0);
                        if (nw == (size_t)-1)
                                (void) printf("%*s ", width, "ERROR");
                        else if (Wflg && nw > width)
                                (void) wprintf(L"%.*s%c", width - 1,
                                    pwd->pw_name, '*');
                        else
                                (void) wprintf(L"%*s", width, pwd->pw_name);
                } else {
                        if (Wflg && snprintf(NULL, 0, "%u",
                            (psinfo->pr_euid)) > width)

                                (void) printf("%*u%c", width - 1,
                                    psinfo->pr_euid, '*');
                        else
                                (void) printf("%*u", width, psinfo->pr_euid);
                }
                break;
        case F_RGROUP:
                if ((grp = getgrgid(psinfo->pr_gid)) != NULL)
                        (void) printf("%*s", width, grp->gr_name);
                else
                        (void) printf("%*u", width, psinfo->pr_gid);
                break;
        case F_GROUP:
                if ((grp = getgrgid(psinfo->pr_egid)) != NULL)
                        (void) printf("%*s", width, grp->gr_name);
                else
                        (void) printf("%*u", width, psinfo->pr_egid);
                break;
        case F_RUID:
                (void) printf("%*u", width, psinfo->pr_uid);
                break;
        case F_UID:
                (void) printf("%*u", width, psinfo->pr_euid);
                break;
        case F_RGID:
                (void) printf("%*u", width, psinfo->pr_gid);
                break;
        case F_GID:
                (void) printf("%*u", width, psinfo->pr_egid);
                break;
        case F_PID:
                (void) printf("%*d", width, (int)psinfo->pr_pid);
                break;
        case F_PPID:
                (void) printf("%*d", width, (int)psinfo->pr_ppid);
                break;
        case F_PGID:
                (void) printf("%*d", width, (int)psinfo->pr_pgid);
                break;
        case F_SID:
                (void) printf("%*d", width, (int)psinfo->pr_sid);
                break;
        case F_PSR:
                if (zombie_lwp || psinfo->pr_lwp.pr_bindpro == PBIND_NONE)
                        (void) printf("%*s", width, "-");
                else
                        (void) printf("%*d", width, psinfo->pr_lwp.pr_bindpro);
                break;
        case F_LWP:
                (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lwpid);
                break;
        case F_LWPNAME: {
                char lwpname[THREAD_NAME_MAX] = "";
                char *path = NULL;
                int fd;

                if (asprintf(&path, "%s/%d/lwp/%d/lwpname", procdir,
                    (int)psinfo->pr_pid, (int)psinfo->pr_lwp.pr_lwpid) != -1 &&
                    (fd = open(path, O_RDONLY)) != -1) {
                        (void) read(fd, lwpname, sizeof (lwpname));
                        lwpname[THREAD_NAME_MAX - 1] = '\0';
                        (void) close(fd);
                }

                free(path);

                if (f->next != NULL)
                        (void) printf("%-*s", width, lwpname);
                else
                        (void) printf("%s", lwpname);
                break;
        }
        case F_NLWP:
                (void) printf("%*d", width, psinfo->pr_nlwp + psinfo->pr_nzomb);
                break;
        case F_OPRI:
                if (zombie_lwp)
                        (void) printf("%*s", width, "-");
                else
                        (void) printf("%*d", width, psinfo->pr_lwp.pr_oldpri);
                break;
        case F_PRI:
                if (zombie_lwp)
                        (void) printf("%*s", width, "-");
                else
                        (void) printf("%*d", width, psinfo->pr_lwp.pr_pri);
                break;
        case F_F:
                mask = 0xffffffffUL;
                if (width < 8)
                        mask >>= (8 - width) * 4;
                (void) printf("%*lx", width, psinfo->pr_flag & mask);
                break;
        case F_S:
                (void) printf("%*c", width, psinfo->pr_lwp.pr_sname);
                break;
        case F_C:
                if (zombie_lwp)
                        (void) printf("%*s", width, "-");
                else
                        (void) printf("%*d", width, psinfo->pr_lwp.pr_cpu);
                break;
        case F_PCPU:
                if (zombie_lwp)
                        (void) printf("%*s", width, "-");
                else if (Lflg)
                        prtpct(psinfo->pr_lwp.pr_pctcpu, width);
                else
                        prtpct(psinfo->pr_pctcpu, width);
                break;
        case F_PMEM:
                prtpct(psinfo->pr_pctmem, width);
                break;
        case F_OSZ:
                (void) printf("%*lu", width,
                    (ulong_t)psinfo->pr_size / kbytes_per_page);
                break;
        case F_VSZ:
                (void) printf("%*lu", width, (ulong_t)psinfo->pr_size);
                break;
        case F_RSS:
                (void) printf("%*lu", width, (ulong_t)psinfo->pr_rssize);
                break;
        case F_NICE:
                /* if pr_oldpri is zero, then this class has no nice */
                if (zombie_lwp)
                        (void) printf("%*s", width, "-");
                else if (psinfo->pr_lwp.pr_oldpri != 0)
                        (void) printf("%*d", width, psinfo->pr_lwp.pr_nice);
                else
                        (void) printf("%*.*s", width, width,
                            psinfo->pr_lwp.pr_clname);
                break;
        case F_CLASS:
                if (zombie_lwp)
                        (void) printf("%*s", width, "-");
                else
                        (void) printf("%*.*s", width, width,
                            psinfo->pr_lwp.pr_clname);
                break;
        case F_STIME:
                if (Lflg)
                        prtime(psinfo->pr_lwp.pr_start, width, 0);
                else
                        prtime(psinfo->pr_start, width, 0);
                break;
        case F_ETIME:
                if (Lflg)
                        print_time(delta_secs(&psinfo->pr_lwp.pr_start),
                            width);
                else
                        print_time(delta_secs(&psinfo->pr_start), width);
                break;
        case F_TIME:
                if (Lflg) {
                        cputime = psinfo->pr_lwp.pr_time.tv_sec;
                        if (psinfo->pr_lwp.pr_time.tv_nsec > 500000000)
                                cputime++;
                } else {
                        cputime = psinfo->pr_time.tv_sec;
                        if (psinfo->pr_time.tv_nsec > 500000000)
                                cputime++;
                }
                print_time(cputime, width);
                break;
        case F_TTY:
                (void) printf("%-*s", width, ttyp);
                break;
        case F_ADDR:
                if (zombie_lwp)
                        (void) printf("%*s", width, "-");
                else if (Lflg)
                        (void) printf("%*lx", width,
                            (long)psinfo->pr_lwp.pr_addr);
                else
                        (void) printf("%*lx", width, (long)psinfo->pr_addr);
                break;
        case F_WCHAN:
                if (!zombie_lwp && psinfo->pr_lwp.pr_wchan)
                        (void) printf("%*lx", width,
                            (long)psinfo->pr_lwp.pr_wchan);
                else
                        (void) printf("%*.*s", width, width, "-");
                break;
        case F_FNAME:
                /*
                 * Print full width unless this is the last output format.
                 */
                if (zombie_lwp) {
                        if (f->next != NULL)
                                (void) printf("%-*s", width, "<defunct>");
                        else
                                (void) printf("%s", "<defunct>");
                        break;
                }
                wcnt = namencnt(psinfo->pr_fname, 16, width);
                if (f->next != NULL)
                        (void) printf("%-*.*s", width, wcnt, psinfo->pr_fname);
                else
                        (void) printf("%-.*s", wcnt, psinfo->pr_fname);
                break;
        case F_COMM:
                if (zombie_lwp) {
                        if (f->next != NULL)
                                (void) printf("%-*s", width, "<defunct>");
                        else
                                (void) printf("%s", "<defunct>");
                        break;
                }
                csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n");
                if (csave) {
                        c = *csave;
                        *csave = '\0';
                }
                /* FALLTHROUGH */
        case F_ARGS:
                /*
                 * PRARGSZ == length of cmd arg string.
                 */
                if (zombie_lwp) {
                        (void) printf("%-*s", width, "<defunct>");
                        break;
                }
                psinfo->pr_psargs[PRARGSZ-1] = '\0';
                bytesleft = PRARGSZ;
                for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
                        length = mbtowc(&wchar, cp, MB_LEN_MAX);
                        if (length == 0)
                                break;
                        if (length < 0 || !iswprint(wchar)) {
                                if (length < 0)
                                        length = 1;
                                if (bytesleft <= length) {
                                        *cp = '\0';
                                        break;
                                }
                                /* omit the unprintable character */
                                (void) memmove(cp, cp+length, bytesleft-length);
                                length = 0;
                        }
                        bytesleft -= length;
                }
                wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width);
                /*
                 * Print full width unless this is the last format.
                 */
                if (f->next != NULL)
                        (void) printf("%-*.*s", width, wcnt,
                            psinfo->pr_psargs);
                else
                        (void) printf("%-.*s", wcnt,
                            psinfo->pr_psargs);
                if (f->fname == F_COMM && csave)
                        *csave = c;
                break;
        case F_TASKID:
                (void) printf("%*d", width, (int)psinfo->pr_taskid);
                break;
        case F_PROJID:
                (void) printf("%*d", width, (int)psinfo->pr_projid);
                break;
        case F_PROJECT:
                {
                        struct project cproj;
                        char proj_buf[PROJECT_BUFSZ];

                        if ((getprojbyid(psinfo->pr_projid, &cproj,
                            (void *)&proj_buf, PROJECT_BUFSZ)) == NULL) {
                                if (Wflg && snprintf(NULL, 0, "%d",
                                    ((int)psinfo->pr_projid)) > width)
                                        (void) printf("%.*d%c", width - 1,
                                            ((int)psinfo->pr_projid), '*');
                                else
                                        (void) printf("%*d", width,
                                            (int)psinfo->pr_projid);
                        } else {
                                size_t nw;

                                if (cproj.pj_name != NULL)
                                        nw = mbstowcs(NULL, cproj.pj_name, 0);
                                if (cproj.pj_name == NULL)
                                        (void) printf("%*s ", width, "---");
                                else if (nw == (size_t)-1)
                                        (void) printf("%*s ", width, "ERROR");
                                else if (Wflg && nw > width)
                                        (void) wprintf(L"%.*s%c", width - 1,
                                            cproj.pj_name, '*');
                                else
                                        (void) wprintf(L"%*s", width,
                                            cproj.pj_name);
                        }
                }
                break;
        case F_PSET:
                if (zombie_lwp || psinfo->pr_lwp.pr_bindpset == PS_NONE)
                        (void) printf("%*s", width, "-");
                else
                        (void) printf("%*d", width, psinfo->pr_lwp.pr_bindpset);
                break;
        case F_ZONEID:
                (void) printf("%*d", width, (int)psinfo->pr_zoneid);
                break;
        case F_ZONE:
                {
                        char zonename[ZONENAME_MAX];

                        if (getzonenamebyid(psinfo->pr_zoneid, zonename,
                            sizeof (zonename)) < 0) {
                                if (Wflg && snprintf(NULL, 0, "%d",
                                    ((int)psinfo->pr_zoneid)) > width)
                                        (void) printf("%.*d%c", width - 1,
                                            ((int)psinfo->pr_zoneid), '*');
                                else
                                        (void) printf("%*d", width,
                                            (int)psinfo->pr_zoneid);
                        } else {
                                size_t nw;

                                nw = mbstowcs(NULL, zonename, 0);
                                if (nw == (size_t)-1)
                                        (void) printf("%*s ", width, "ERROR");
                                else if (Wflg && nw > width)
                                        (void) wprintf(L"%.*s%c", width - 1,
                                            zonename, '*');
                                else
                                        (void) wprintf(L"%*s", width, zonename);
                        }
                }
                break;
        case F_CTID:
                if (psinfo->pr_contract == -1)
                        (void) printf("%*s", width, "-");
                else
                        (void) printf("%*ld", width, (long)psinfo->pr_contract);
                break;
        case F_LGRP:
                /* Display home lgroup */
                (void) printf("%*d", width, (int)psinfo->pr_lwp.pr_lgrp);
                break;

        case F_DMODEL:
                (void) printf("%*s", width,
                    psinfo->pr_dmodel == PR_MODEL_LP64 ? "_LP64" : "_ILP32");
                break;
        }
}

static void
print_zombie_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
{
        int wcnt;
        int width = f->width;

        switch (f->fname) {
        case F_FNAME:
        case F_COMM:
        case F_ARGS:
                /*
                 * Print full width unless this is the last output format.
                 */
                wcnt = min(width, sizeof ("<defunct>"));
                if (f->next != NULL)
                        (void) printf("%-*.*s", width, wcnt, "<defunct>");
                else
                        (void) printf("%-.*s", wcnt, "<defunct>");
                break;

        case F_PSR:
        case F_PCPU:
        case F_PMEM:
        case F_NICE:
        case F_CLASS:
        case F_STIME:
        case F_ETIME:
        case F_WCHAN:
        case F_PSET:
                (void) printf("%*s", width, "-");
                break;

        case F_OPRI:
        case F_PRI:
        case F_OSZ:
        case F_VSZ:
        case F_RSS:
                (void) printf("%*d", width, 0);
                break;

        default:
                print_field(psinfo, f, ttyp);
                break;
        }
}

static void
pr_fields(psinfo_t *psinfo, const char *ttyp,
    void (*print_fld)(psinfo_t *, struct field *, const char *))
{
        struct field *f;

        for (f = fields; f != NULL; f = f->next) {
                print_fld(psinfo, f, ttyp);
                if (f->next != NULL)
                        (void) printf(" ");
        }
        (void) printf("\n");
}

/*
 * Returns 1 if arg is found in array arr, of length num; 0 otherwise.
 */
static int
search(pid_t *arr, int number, pid_t arg)
{
        int i;

        for (i = 0; i < number; i++)
                if (arg == arr[i])
                        return (1);
        return (0);
}

/*
 * Add an entry (user, group) to the specified table.
 */
static void
add_ugentry(struct ughead *tbl, char *name)
{
        struct ugdata *entp;

        if (tbl->size == tbl->nent) {   /* reallocate the table entries */
                if ((tbl->size *= 2) == 0)
                        tbl->size = 32;         /* first time */
                tbl->ent = Realloc(tbl->ent, tbl->size*sizeof (struct ugdata));
        }
        entp = &tbl->ent[tbl->nent++];
        entp->id = 0;
        (void) strncpy(entp->name, name, MAXUGNAME);
        entp->name[MAXUGNAME] = '\0';
}

static int
uconv(struct ughead *uhead)
{
        struct ugdata *utbl = uhead->ent;
        int n = uhead->nent;
        struct passwd *pwd;
        int i;
        int fnd = 0;
        uid_t uid;

        /*
         * Ask the name service for names.
         */
        for (i = 0; i < n; i++) {
                /*
                 * If name is numeric, ask for numeric id
                 */
                if (str2uid(utbl[i].name, &uid, 0, MAXEPHUID) == 0)
                        pwd = getpwuid(uid);
                else
                        pwd = getpwnam(utbl[i].name);

                /*
                 * If found, enter found index into tbl array.
                 */
                if (pwd == NULL) {
                        (void) fprintf(stderr,
                            gettext("ps: unknown user %s\n"), utbl[i].name);
                        continue;
                }

                utbl[fnd].id = pwd->pw_uid;
                (void) strncpy(utbl[fnd].name, pwd->pw_name, MAXUGNAME);
                fnd++;
        }

        uhead->nent = fnd;      /* in case it changed */
        return (n - fnd);
}

static int
gconv(struct ughead *ghead)
{
        struct ugdata *gtbl = ghead->ent;
        int n = ghead->nent;
        struct group *grp;
        gid_t gid;
        int i;
        int fnd = 0;

        /*
         * Ask the name service for names.
         */
        for (i = 0; i < n; i++) {
                /*
                 * If name is numeric, ask for numeric id
                 */
                if (str2uid(gtbl[i].name, (uid_t *)&gid, 0, MAXEPHUID) == 0)
                        grp = getgrgid(gid);
                else
                        grp = getgrnam(gtbl[i].name);
                /*
                 * If found, enter found index into tbl array.
                 */
                if (grp == NULL) {
                        (void) fprintf(stderr,
                            gettext("ps: unknown group %s\n"), gtbl[i].name);
                        continue;
                }

                gtbl[fnd].id = grp->gr_gid;
                (void) strncpy(gtbl[fnd].name, grp->gr_name, MAXUGNAME);
                fnd++;
        }

        ghead->nent = fnd;      /* in case it changed */
        return (n - fnd);
}

/*
 * Return 1 if puid is in table, otherwise 0.
 */
static int
ugfind(id_t id, struct ughead *ughead)
{
        struct ugdata *utbl = ughead->ent;
        int n = ughead->nent;
        int i;

        for (i = 0; i < n; i++)
                if (utbl[i].id == id)
                        return (1);
        return (0);
}

/*
 * Print starting time of process unless process started more than 24 hours
 * ago, in which case the date is printed.  The date is printed in the form
 * "MMM dd" if old format, else the blank is replaced with an '_' so
 * it appears as a single word (for parseability).
 */
static void
prtime(timestruc_t st, int width, int old)
{
        char sttim[26];
        time_t starttime;

        starttime = st.tv_sec;
        if (st.tv_nsec > 500000000)
                starttime++;
        if ((now.tv_sec - starttime) >= 24*60*60) {
                (void) strftime(sttim, sizeof (sttim), old?
                /*
                 * TRANSLATION_NOTE
                 * This time format is used by STIME field when -f option
                 * is specified.  Used for processes that begun more than
                 * 24 hours.
                 */
                    dcgettext(NULL, "%b %d", LC_TIME) :
                /*
                 * TRANSLATION_NOTE
                 * This time format is used by STIME field when -o option
                 * is specified.  Used for processes that begun more than
                 * 24 hours.
                 */
                    dcgettext(NULL, "%b_%d", LC_TIME), localtime(&starttime));
        } else {
                /*
                 * TRANSLATION_NOTE
                 * This time format is used by STIME field when -f or -o option
                 * is specified.  Used for processes that begun less than
                 * 24 hours.
                 */
                (void) strftime(sttim, sizeof (sttim),
                    dcgettext(NULL, "%H:%M:%S", LC_TIME),
                    localtime(&starttime));
        }
        (void) printf("%*.*s", width, width, sttim);
}

static void
przom(psinfo_t *psinfo)
{
        long    tm;
        struct passwd *pwd;
        char zonename[ZONENAME_MAX];

        /*
         * All fields before 'PID' are printed with a trailing space as a
         * spearator, rather than keeping track of which column is first.  All
         * other fields are printed with a leading space.
         */
        if (lflg) {     /* F S */
                if (!yflg)
                        (void) printf("%2x ", psinfo->pr_flag & 0377); /* F */
                (void) printf("%c ", psinfo->pr_lwp.pr_sname);  /* S */
        }
        if (Zflg) {
                if (getzonenamebyid(psinfo->pr_zoneid, zonename,
                    sizeof (zonename)) < 0) {
                        if (snprintf(NULL, 0, "%d",
                            ((int)psinfo->pr_zoneid)) > 7)
                                (void) printf(" %6.6d%c ",
                                    ((int)psinfo->pr_zoneid), '*');
                        else
                                (void) printf(" %7.7d ",
                                    ((int)psinfo->pr_zoneid));
                } else {
                        size_t nw;

                        nw = mbstowcs(NULL, zonename, 0);
                        if (nw == (size_t)-1)
                                (void) printf("%8.8s ", "ERROR");
                        else if (nw > 8)
                                (void) wprintf(L"%7.7s%c ", zonename, '*');
                        else
                                (void) wprintf(L"%8.8s ", zonename);
                }
        }
        if (Hflg) {
                /* Display home lgroup */
                (void) printf(" %6d", (int)psinfo->pr_lwp.pr_lgrp); /* LGRP */
        }
        if (fflg) {
                if ((pwd = getpwuid(psinfo->pr_euid)) != NULL) {
                        size_t nw;

                        nw = mbstowcs(NULL, pwd->pw_name, 0);
                        if (nw == (size_t)-1)
                                (void) printf("%8.8s ", "ERROR");
                        else if (nw > 8)
                                (void) wprintf(L"%7.7s%c ", pwd->pw_name, '*');
                        else
                                (void) wprintf(L"%8.8s ", pwd->pw_name);
                } else {
                        if (snprintf(NULL, 0, "%u",
                            (psinfo->pr_euid)) > 7)
                                (void) printf(" %6.6u%c ", psinfo->pr_euid,
                                    '*');
                        else
                                (void) printf(" %7.7u ", psinfo->pr_euid);
                }
        } else if (lflg) {
                if (snprintf(NULL, 0, "%u", (psinfo->pr_euid)) > 6)
                        (void) printf("%5.5u%c ", psinfo->pr_euid, '*');
                else
                        (void) printf("%6u ", psinfo->pr_euid);
        }

        (void) printf("%*d", pidwidth, (int)psinfo->pr_pid);    /* PID */
        if (lflg || fflg)
                (void) printf(" %*d", pidwidth,
                    (int)psinfo->pr_ppid);                      /* PPID */

        if (jflg) {
                (void) printf(" %*d", pidwidth,
                    (int)psinfo->pr_pgid);                      /* PGID */
                (void) printf(" %*d", pidwidth,
                    (int)psinfo->pr_sid);                       /* SID  */
        }

        if (Lflg)
                (void) printf(" %5d", 0);                       /* LWP */
        if (Pflg)
                (void) printf("   -");                          /* PSR */
        if (Lflg && fflg)
                (void) printf(" %5d", 0);                       /* NLWP */

        if (cflg) {
                (void) printf(" %4s", "-");     /* zombies have no class */
                (void) printf(" %3d", psinfo->pr_lwp.pr_pri);   /* PRI  */
        } else if (lflg || fflg) {
                (void) printf(" %3d", psinfo->pr_lwp.pr_cpu & 0377); /* C   */
                if (lflg)
                        (void) printf(" %3d %2s",
                            psinfo->pr_lwp.pr_oldpri, "-");     /* PRI NI */
        }
        if (lflg) {
                if (yflg)                               /* RSS SZ WCHAN */
                        (void) printf(" %5d %6d %8s", 0, 0, "-");
                else                                    /* ADDR SZ WCHAN */
                        (void) printf(" %8s %6d %8s", "-", 0, "-");
        }
        if (fflg) {
                int width = fname[F_STIME].width;
                (void) printf(" %*.*s", width, width, "-");     /* STIME */
        }
        (void) printf(" %-8.14s", "?");                         /* TTY */

        tm = psinfo->pr_time.tv_sec;
        if (psinfo->pr_time.tv_nsec > 500000000)
                tm++;
        (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* TIME */
        (void) printf(" <defunct>\n");
}

/*
 * Function to compute the number of printable bytes in a multibyte
 * command string ("internationalization").
 */
static int
namencnt(char *cmd, int csisize, int scrsize)
{
        int csiwcnt = 0, scrwcnt = 0;
        int ncsisz, nscrsz;
        wchar_t  wchar;
        int      len;

        while (*cmd != '\0') {
                if ((len = csisize - csiwcnt) > (int)MB_CUR_MAX)
                        len = MB_CUR_MAX;
                if ((ncsisz = mbtowc(&wchar, cmd, len)) < 0)
                        return (8); /* default to use for illegal chars */
                if ((nscrsz = wcwidth(wchar)) <= 0)
                        return (8);
                if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize)
                        break;
                csiwcnt += ncsisz;
                scrwcnt += nscrsz;
                cmd += ncsisz;
        }
        return (csiwcnt);
}

static char *
err_string(int err)
{
        static char buf[32];
        char *str = strerror(err);

        if (str == NULL)
                (void) snprintf(str = buf, sizeof (buf), "Errno #%d", err);

        return (str);
}

/* If allocation fails, die */
static void *
Realloc(void *ptr, size_t size)
{
        ptr = realloc(ptr, size);
        if (ptr == NULL) {
                (void) fprintf(stderr, gettext("ps: no memory\n"));
                exit(1);
        }
        return (ptr);
}

static time_t
delta_secs(const timestruc_t *start)
{
        time_t seconds = now.tv_sec - start->tv_sec;
        long nanosecs = now.tv_usec * 1000 - start->tv_nsec;

        if (nanosecs >= (NANOSEC / 2))
                seconds++;
        else if (nanosecs < -(NANOSEC / 2))
                seconds--;

        return (seconds);
}

/*
 * Returns the following:
 *
 *      0       No error
 *      EINVAL  Invalid number
 *      ERANGE  Value exceeds (min, max) range
 */
static int
str2id(const char *p, pid_t *val, long min, long max)
{
        char *q;
        long number;
        int error;

        errno = 0;
        number = strtol(p, &q, 10);

        if (errno != 0 || q == p || *q != '\0') {
                if ((error = errno) == 0) {
                        /*
                         * strtol() can fail without setting errno, or it can
                         * set it to EINVAL or ERANGE.  In the case errno is
                         * still zero, return EINVAL.
                         */
                        error = EINVAL;
                }
        } else if (number < min || number > max) {
                error = ERANGE;
        } else {
                error = 0;
        }

        *val = number;

        return (error);
}

/*
 * Returns the following:
 *
 *      0       No error
 *      EINVAL  Invalid number
 *      ERANGE  Value exceeds (min, max) range
 */
static int
str2uid(const char *p, uid_t *val, unsigned long min, unsigned long max)
{
        char *q;
        unsigned long number;
        int error;

        errno = 0;
        number = strtoul(p, &q, 10);

        if (errno != 0 || q == p || *q != '\0') {
                if ((error = errno) == 0) {
                        /*
                         * strtoul() can fail without setting errno, or it can
                         * set it to EINVAL or ERANGE.  In the case errno is
                         * still zero, return EINVAL.
                         */
                        error = EINVAL;
                }
        } else if (number < min || number > max) {
                error = ERANGE;
        } else {
                error = 0;
        }

        *val = number;

        return (error);
}

static int
pidcmp(const void *p1, const void *p2)
{
        pid_t i = *((pid_t *)p1);
        pid_t j = *((pid_t *)p2);

        return (i - j);
}