ps.c revision 1.60 1 /* $NetBSD: ps.c,v 1.60 2006/09/02 20:00:09 christos Exp $ */
2
3 /*
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Simon Burge.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Copyright (c) 1990, 1993, 1994
41 * The Regents of the University of California. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 */
67
68 #include <sys/cdefs.h>
69 #ifndef lint
70 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\n\
71 The Regents of the University of California. All rights reserved.\n");
72 #endif /* not lint */
73
74 #ifndef lint
75 #if 0
76 static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94";
77 #else
78 __RCSID("$NetBSD: ps.c,v 1.60 2006/09/02 20:00:09 christos Exp $");
79 #endif
80 #endif /* not lint */
81
82 #include <sys/param.h>
83 #include <sys/user.h>
84 #include <sys/time.h>
85 #include <sys/resource.h>
86 #include <sys/lwp.h>
87 #include <sys/proc.h>
88 #include <sys/stat.h>
89 #include <sys/ioctl.h>
90 #include <sys/sysctl.h>
91
92 #include <stddef.h>
93 #include <ctype.h>
94 #include <err.h>
95 #include <errno.h>
96 #include <fcntl.h>
97 #include <kvm.h>
98 #include <limits.h>
99 #include <nlist.h>
100 #include <paths.h>
101 #include <pwd.h>
102 #include <stdio.h>
103 #include <stdlib.h>
104 #include <string.h>
105 #include <unistd.h>
106
107 #include "ps.h"
108
109 /*
110 * ARGOPTS must contain all option characters that take arguments
111 * (except for 't'!) - it is used in kludge_oldps_options()
112 */
113 #define GETOPTSTR "acCeghjk:LlM:mN:O:o:p:rSsTt:U:uvW:wx"
114 #define ARGOPTS "kMNOopUW"
115
116 struct kinfo_proc2 *kinfo;
117 struct varent *vhead, *sorthead;
118
119 int eval; /* exit value */
120 int rawcpu; /* -C */
121 int sumrusage; /* -S */
122 int termwidth; /* width of screen (0 == infinity) */
123 int totwidth; /* calculated width of requested variables */
124
125 int needcomm, needenv, commandonly;
126 uid_t myuid;
127
128 static struct kinfo_lwp
129 *pick_representative_lwp(struct kinfo_proc2 *,
130 struct kinfo_lwp *, int);
131 static struct kinfo_proc2
132 *getkinfo_kvm(kvm_t *, int, int, int *);
133 static char *kludge_oldps_options(char *);
134 static int pscomp(const void *, const void *);
135 static void scanvars(void);
136 static void usage(void);
137 static int parsenum(const char *, const char *);
138 int main(int, char *[]);
139
140 char dfmt[] = "pid tt state time command";
141 char jfmt[] = "user pid ppid pgid sess jobc state tt time command";
142 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command";
143 char o1[] = "pid";
144 char o2[] = "tt state time command";
145 char sfmt[] = "uid pid ppid cpu lid nlwp pri nice vsz rss wchan lstate tt "
146 "time command";
147 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command";
148 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command";
149
150 kvm_t *kd;
151
152 int
153 main(int argc, char *argv[])
154 {
155 struct varent *vent;
156 struct winsize ws;
157 struct kinfo_lwp *kl, *l;
158 int ch, flag, i, j, fmt, lineno, nentries, nlwps;
159 int prtheader, wflag, what, xflg, mode, showlwps;
160 char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX];
161 char *ttname;
162
163 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
164 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
165 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) ||
166 ws.ws_col == 0)
167 termwidth = 79;
168 else
169 termwidth = ws.ws_col - 1;
170
171 if (argc > 1)
172 argv[1] = kludge_oldps_options(argv[1]);
173
174 fmt = prtheader = wflag = xflg = showlwps = 0;
175 what = KERN_PROC_UID;
176 flag = myuid = getuid();
177 memf = nlistf = swapf = NULL;
178 mode = PRINTMODE;
179 while ((ch = getopt(argc, argv, GETOPTSTR)) != -1)
180 switch((char)ch) {
181 case 'a':
182 what = KERN_PROC_ALL;
183 flag = 0;
184 break;
185 case 'c':
186 commandonly = 1;
187 break;
188 case 'e': /* XXX set ufmt */
189 needenv = 1;
190 break;
191 case 'C':
192 rawcpu = 1;
193 break;
194 case 'g':
195 break; /* no-op */
196 case 'h':
197 prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
198 break;
199 case 'j':
200 parsefmt(jfmt);
201 fmt = 1;
202 jfmt[0] = '\0';
203 break;
204 case 'k':
205 parsesort(optarg);
206 break;
207 case 'K':
208 break; /* no-op - was dontuseprocfs */
209 case 'L':
210 showkey();
211 exit(0);
212 /* NOTREACHED */
213 case 'l':
214 parsefmt(lfmt);
215 fmt = 1;
216 lfmt[0] = '\0';
217 break;
218 case 'M':
219 memf = optarg;
220 break;
221 case 'm':
222 parsesort("vsz");
223 break;
224 case 'N':
225 nlistf = optarg;
226 break;
227 case 'O':
228 parsefmt(o1);
229 parsefmt(optarg);
230 parsefmt(o2);
231 o1[0] = o2[0] = '\0';
232 fmt = 1;
233 break;
234 case 'o':
235 parsefmt(optarg);
236 fmt = 1;
237 break;
238 case 'p':
239 what = KERN_PROC_PID;
240 flag = parsenum(optarg, "process id");
241 xflg = 1;
242 break;
243 case 'r':
244 parsesort("%cpu");
245 break;
246 case 'S':
247 sumrusage = 1;
248 break;
249 case 's':
250 /* -L was already taken... */
251 showlwps = 1;
252 parsefmt(sfmt);
253 fmt = 1;
254 sfmt[0] = '\0';
255 break;
256 case 'T':
257 if ((ttname = ttyname(STDIN_FILENO)) == NULL)
258 errx(1, "stdin: not a terminal");
259 goto tty;
260 case 't':
261 ttname = optarg;
262 tty: {
263 struct stat sb;
264 const char *ttypath;
265 char pathbuf[MAXPATHLEN];
266
267 flag = 0;
268 ttypath = NULL;
269 if (strcmp(ttname, "?") == 0)
270 flag = KERN_PROC_TTY_NODEV;
271 else if (strcmp(ttname, "-") == 0)
272 flag = KERN_PROC_TTY_REVOKE;
273 else if (strcmp(ttname, "co") == 0)
274 ttypath = _PATH_CONSOLE;
275 else if (strncmp(ttname, "pts/", 4) == 0 ||
276 strncmp(ttname, "tty", 3) == 0) {
277 (void)snprintf(pathbuf,
278 sizeof(pathbuf), "%s%s", _PATH_DEV, ttname);
279 ttypath = pathbuf;
280 } else if (*ttname != '/') {
281 (void)snprintf(pathbuf,
282 sizeof(pathbuf), "%s%s", _PATH_TTY, ttname);
283 ttypath = pathbuf;
284 } else
285 ttypath = ttname;
286 what = KERN_PROC_TTY;
287 if (flag == 0) {
288 if (stat(ttypath, &sb) == -1)
289 err(1, "%s", ttypath);
290 if (!S_ISCHR(sb.st_mode))
291 errx(1, "%s: not a terminal", ttypath);
292 flag = sb.st_rdev;
293 }
294 break;
295 }
296 case 'U':
297 if (*optarg != '\0') {
298 struct passwd *pw;
299
300 what = KERN_PROC_UID;
301 pw = getpwnam(optarg);
302 if (pw == NULL) {
303 flag = parsenum(optarg, "user name");
304 } else
305 flag = pw->pw_uid;
306 }
307 break;
308 case 'u':
309 parsefmt(ufmt);
310 parsesort("%cpu");
311 fmt = 1;
312 ufmt[0] = '\0';
313 break;
314 case 'v':
315 parsefmt(vfmt);
316 parsesort("vsz");
317 fmt = 1;
318 vfmt[0] = '\0';
319 break;
320 case 'W':
321 swapf = optarg;
322 break;
323 case 'w':
324 if (wflag)
325 termwidth = UNLIMITED;
326 else if (termwidth < 131)
327 termwidth = 131;
328 wflag++;
329 break;
330 case 'x':
331 xflg = 1;
332 break;
333 case '?':
334 default:
335 usage();
336 }
337 argc -= optind;
338 argv += optind;
339
340 #define BACKWARD_COMPATIBILITY
341 #ifdef BACKWARD_COMPATIBILITY
342 if (*argv) {
343 nlistf = *argv;
344 if (*++argv) {
345 memf = *argv;
346 if (*++argv)
347 swapf = *argv;
348 }
349 }
350 #endif
351
352 if (memf == NULL) {
353 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
354 donlist_sysctl();
355 } else
356 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf);
357
358 if (kd == 0)
359 errx(1, "%s", errbuf);
360
361 if (!fmt)
362 parsefmt(dfmt);
363
364 /* Add default sort criteria */
365 parsesort("tdev,pid");
366 for (vent = sorthead; vent; vent = vent->next) {
367 if (vent->var->flag & LWP || vent->var->type == UNSPECIFIED)
368 warnx("Cannot sort on %s, sort key ignored\n",
369 vent->var->name);
370 }
371
372 /*
373 * scan requested variables, noting what structures are needed.
374 */
375 scanvars();
376
377 /*
378 * select procs
379 */
380 if (!(kinfo = getkinfo_kvm(kd, what, flag, &nentries)))
381 err(1, "%s", kvm_geterr(kd));
382 if (nentries == 0) {
383 printheader();
384 exit(1);
385 }
386 /*
387 * sort proc list
388 */
389 qsort(kinfo, nentries, sizeof(struct kinfo_proc2), pscomp);
390 /*
391 * For each proc, call each variable output function in
392 * "setwidth" mode to determine the widest element of
393 * the column.
394 */
395 if (mode == PRINTMODE)
396 for (i = 0; i < nentries; i++) {
397 struct kinfo_proc2 *ki = &kinfo[i];
398
399 if (xflg == 0 && (ki->p_tdev == NODEV ||
400 (ki->p_flag & P_CONTROLT) == 0))
401 continue;
402
403 kl = kvm_getlwps(kd, ki->p_pid, ki->p_paddr,
404 sizeof(struct kinfo_lwp), &nlwps);
405 if (kl == 0)
406 nlwps = 0;
407 if (showlwps == 0) {
408 l = pick_representative_lwp(ki, kl, nlwps);
409 for (vent = vhead; vent; vent = vent->next)
410 OUTPUT(vent, ki, l, WIDTHMODE);
411 } else {
412 /* The printing is done with the loops
413 * reversed, but here we don't need that,
414 * and this improves the code locality a bit.
415 */
416 for (vent = vhead; vent; vent = vent->next)
417 for (j = 0; j < nlwps; j++)
418 OUTPUT(vent, ki, &kl[j],
419 WIDTHMODE);
420 }
421 }
422 /*
423 * Print header - AFTER determining process field widths.
424 * printheader() also adds up the total width of all
425 * fields the first time it's called.
426 */
427 printheader();
428 /*
429 * For each proc, call each variable output function in
430 * print mode.
431 */
432 for (i = lineno = 0; i < nentries; i++) {
433 struct kinfo_proc2 *ki = &kinfo[i];
434
435 if (xflg == 0 && (ki->p_tdev == NODEV ||
436 (ki->p_flag & P_CONTROLT ) == 0))
437 continue;
438 kl = kvm_getlwps(kd, ki->p_pid, (u_long)ki->p_paddr,
439 sizeof(struct kinfo_lwp), &nlwps);
440 if (kl == 0)
441 nlwps = 0;
442 if (showlwps == 0) {
443 l = pick_representative_lwp(ki, kl, nlwps);
444 for (vent = vhead; vent; vent = vent->next) {
445 OUTPUT(vent, ki, l, mode);
446 if (vent->next != NULL)
447 (void)putchar(' ');
448 }
449 (void)putchar('\n');
450 if (prtheader && lineno++ == prtheader - 4) {
451 (void)putchar('\n');
452 printheader();
453 lineno = 0;
454 }
455 } else {
456 for (j = 0; j < nlwps; j++) {
457 for (vent = vhead; vent; vent = vent->next) {
458 OUTPUT(vent, ki, &kl[j], mode);
459 if (vent->next != NULL)
460 (void)putchar(' ');
461 }
462 (void)putchar('\n');
463 if (prtheader && lineno++ == prtheader - 4) {
464 (void)putchar('\n');
465 printheader();
466 lineno = 0;
467 }
468 }
469 }
470 }
471 exit(eval);
472 /* NOTREACHED */
473 }
474
475 static struct kinfo_lwp *
476 pick_representative_lwp(struct kinfo_proc2 *ki, struct kinfo_lwp *kl, int nlwps)
477 {
478 int i, onproc, running, sleeping, stopped, suspended;
479 static struct kinfo_lwp zero_lwp;
480
481 if (kl == 0)
482 return &zero_lwp;
483
484 /* Trivial case: only one LWP */
485 if (nlwps == 1)
486 return kl;
487
488 switch (ki->p_realstat) {
489 case SSTOP:
490 case SACTIVE:
491 /* Pick the most live LWP */
492 onproc = running = sleeping = stopped = suspended = -1;
493 for (i = 0; i < nlwps; i++) {
494 switch (kl[i].l_stat) {
495 case LSONPROC:
496 onproc = i;
497 break;
498 case LSRUN:
499 running = i;
500 break;
501 case LSSLEEP:
502 sleeping = i;
503 break;
504 case LSSTOP:
505 stopped = i;
506 break;
507 case LSSUSPENDED:
508 suspended = i;
509 break;
510 }
511 }
512 if (onproc != -1)
513 return &kl[onproc];
514 if (running != -1)
515 return &kl[running];
516 if (sleeping != -1)
517 return &kl[sleeping];
518 if (stopped != -1)
519 return &kl[stopped];
520 if (suspended != -1)
521 return &kl[suspended];
522 break;
523 case SZOMB:
524 /* First will do */
525 return kl;
526 break;
527 }
528 /* Error condition! */
529 warnx("Inconsistent LWP state for process %d\n", ki->p_pid);
530 return kl;
531 }
532
533
534 static struct kinfo_proc2 *
535 getkinfo_kvm(kvm_t *kdp, int what, int flag, int *nentriesp)
536 {
537
538 return (kvm_getproc2(kdp, what, flag, sizeof(struct kinfo_proc2),
539 nentriesp));
540 }
541
542 static void
543 scanvars(void)
544 {
545 struct varent *vent;
546 VAR *v;
547
548 for (vent = vhead; vent; vent = vent->next) {
549 v = vent->var;
550 if (v->flag & COMM) {
551 needcomm = 1;
552 break;
553 }
554 }
555 }
556
557 static int
558 pscomp(const void *a, const void *b)
559 {
560 const struct kinfo_proc2 *ka = (const struct kinfo_proc2 *)a;
561 const struct kinfo_proc2 *kb = (const struct kinfo_proc2 *)b;
562
563 int i;
564 int64_t i64;
565 VAR *v;
566 struct varent *ve;
567 const sigset_t *sa, *sb;
568
569 #define V_SIZE(k) (k->p_vm_dsize + k->p_vm_ssize + k->p_vm_tsize)
570 #define RDIFF_N(t, n) \
571 if (((const t *)((const char *)ka + v->off))[n] > ((const t *)((const char *)kb + v->off))[n]) \
572 return 1; \
573 if (((const t *)((const char *)ka + v->off))[n] < ((const t *)((const char *)kb + v->off))[n]) \
574 return -1;
575
576 #define RDIFF(type) RDIFF_N(type, 0); continue
577
578 for (ve = sorthead; ve != NULL; ve = ve->next) {
579 v = ve->var;
580 if (v->flag & LWP)
581 /* LWP structure not available (yet) */
582 continue;
583 /* Sort on pvar() fields, + a few others */
584 switch (v->type) {
585 case CHAR:
586 RDIFF(char);
587 case UCHAR:
588 RDIFF(u_char);
589 case SHORT:
590 RDIFF(short);
591 case USHORT:
592 RDIFF(ushort);
593 case INT:
594 RDIFF(int);
595 case UINT:
596 RDIFF(uint);
597 case LONG:
598 RDIFF(long);
599 case ULONG:
600 RDIFF(ulong);
601 case INT32:
602 RDIFF(int32_t);
603 case UINT32:
604 RDIFF(uint32_t);
605 case SIGLIST:
606 sa = (const void *)((const char *)a + v->off);
607 sb = (const void *)((const char *)b + v->off);
608 i = 0;
609 do {
610 if (sa->__bits[i] > sb->__bits[i])
611 return 1;
612 if (sa->__bits[i] < sb->__bits[i])
613 return -1;
614 i++;
615 } while (i < sizeof sa->__bits / sizeof sa->__bits[0]);
616 continue;
617 case INT64:
618 RDIFF(int64_t);
619 case KPTR:
620 case KPTR24:
621 case UINT64:
622 RDIFF(uint64_t);
623 case TIMEVAL:
624 /* compare xxx_sec then xxx_usec */
625 RDIFF_N(uint32_t, 0);
626 RDIFF_N(uint32_t, 1);
627 continue;
628 case CPUTIME:
629 i64 = ka->p_rtime_sec * 1000000 + ka->p_rtime_usec;
630 i64 -= kb->p_rtime_sec * 1000000 + kb->p_rtime_usec;
631 if (sumrusage) {
632 i64 += ka->p_uctime_sec * 1000000
633 + ka->p_uctime_usec;
634 i64 -= kb->p_uctime_sec * 1000000
635 + kb->p_uctime_usec;
636 }
637 if (i64 != 0)
638 return i64 > 0 ? 1 : -1;
639 continue;
640 case PCPU:
641 i = getpcpu(kb) - getpcpu(ka);
642 if (i != 0)
643 return i;
644 continue;
645 case VSIZE:
646 i = V_SIZE(kb) - V_SIZE(ka);
647 if (i != 0)
648 return i;
649 continue;
650
651 default:
652 /* Ignore everything else */
653 break;
654 }
655 }
656 return 0;
657
658 #undef VSIZE
659 }
660
661 /*
662 * ICK (all for getopt), would rather hide the ugliness
663 * here than taint the main code.
664 *
665 * ps foo -> ps -foo
666 * ps 34 -> ps -p34
667 *
668 * The old convention that 't' with no trailing tty arg means the user's
669 * tty, is only supported if argv[1] doesn't begin with a '-'. This same
670 * feature is available with the option 'T', which takes no argument.
671 */
672 static char *
673 kludge_oldps_options(char *s)
674 {
675 size_t len;
676 char *newopts, *ns, *cp;
677
678 len = strlen(s);
679 if ((newopts = ns = malloc(len + 3)) == NULL)
680 err(1, NULL);
681 /*
682 * options begin with '-'
683 */
684 if (*s != '-')
685 *ns++ = '-'; /* add option flag */
686 /*
687 * gaze to end of argv[1]
688 */
689 cp = s + len - 1;
690 /*
691 * if the last letter is a 't' flag and there are no other option
692 * characters that take arguments (eg U, p, o) in the option
693 * string and the option string doesn't start with a '-' then
694 * convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
695 */
696 if (*cp == 't' && *s != '-' && strpbrk(s, ARGOPTS) == NULL)
697 *cp = 'T';
698 else {
699 /*
700 * otherwise check for trailing number, which *may* be a
701 * pid.
702 */
703 while (cp >= s && isdigit((unsigned char)*cp))
704 --cp;
705 }
706 cp++;
707 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */
708 ns += cp - s;
709 /*
710 * if there's a trailing number, and not a preceding 'p' (pid) or
711 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
712 */
713 if (isdigit((unsigned char)*cp) &&
714 (cp == s || (cp[-1] != 'U' && cp[-1] != 't' && cp[-1] != 'p' &&
715 cp[-1] != '/' && (cp - 1 == s || cp[-2] != 't'))))
716 *ns++ = 'p';
717 /* and append the number */
718 (void)strcpy(ns, cp); /* XXX strcpy is safe here */
719
720 return (newopts);
721 }
722
723 static int
724 parsenum(const char *str, const char *msg)
725 {
726 char *ep;
727 unsigned long ul;
728
729 ul = strtoul(str, &ep, 0);
730
731 if (*str == '\0' || *ep != '\0')
732 errx(1, "Invalid %s: `%s'", msg, str);
733
734 if (ul > INT_MAX)
735 errx(1, "Out of range %s: `%s'", msg, str);
736
737 return (int)ul;
738 }
739
740 static void
741 usage(void)
742 {
743
744 (void)fprintf(stderr,
745 "usage:\t%s\n\t %s\n\t%s\n",
746 "ps [-acCehjlmrsSTuvwx] [-k key] [-O|o fmt] [-p pid] [-t tty]",
747 "[-M core] [-N system] [-W swap] [-U username]",
748 "ps [-L]");
749 exit(1);
750 /* NOTREACHED */
751 }
752