xref: /netbsd-src/bin/ps/print.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: print.c,v 1.78 2003/03/06 09:04:21 dsl 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. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *	This product includes software developed by the University of
54  *	California, Berkeley and its contributors.
55  * 4. Neither the name of the University nor the names of its contributors
56  *    may be used to endorse or promote products derived from this software
57  *    without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  */
71 
72 #include <sys/cdefs.h>
73 #ifndef lint
74 #if 0
75 static char sccsid[] = "@(#)print.c	8.6 (Berkeley) 4/16/94";
76 #else
77 __RCSID("$NetBSD: print.c,v 1.78 2003/03/06 09:04:21 dsl Exp $");
78 #endif
79 #endif /* not lint */
80 
81 #include <sys/param.h>
82 #include <sys/time.h>
83 #include <sys/resource.h>
84 #include <sys/lwp.h>
85 #include <sys/proc.h>
86 #include <sys/stat.h>
87 #include <sys/ucred.h>
88 #include <sys/sysctl.h>
89 
90 #include <err.h>
91 #include <grp.h>
92 #include <kvm.h>
93 #include <math.h>
94 #include <nlist.h>
95 #include <pwd.h>
96 #include <stddef.h>
97 #include <stdio.h>
98 #include <stdlib.h>
99 #include <string.h>
100 #include <time.h>
101 #include <tzfile.h>
102 #include <unistd.h>
103 
104 #include "ps.h"
105 
106 static char *cmdpart __P((char *));
107 static void  printval __P((void *, VAR *, int));
108 static int   titlecmp __P((char *, char **));
109 
110 static void  doubleprintorsetwidth __P((VAR *, double, int, int));
111 static void  intprintorsetwidth __P((VAR *, int, int));
112 static void  strprintorsetwidth __P((VAR *, const char *, int));
113 
114 #define	min(a,b)	((a) <= (b) ? (a) : (b))
115 
116 static int
117 iwidth(u_int64_t v)
118 {
119 	u_int64_t nlim, lim;
120 	int w = 1;
121 
122 	for (lim = 10; v >= lim; lim = nlim) {
123 		nlim = lim * 10;
124 		w++;
125 		if (nlim < lim)
126 			break;
127 	}
128 	return w;
129 }
130 
131 static char *
132 cmdpart(arg0)
133 	char *arg0;
134 {
135 	char *cp;
136 
137 	return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0);
138 }
139 
140 void
141 printheader()
142 {
143 	int len;
144 	VAR *v;
145 	struct varent *vent;
146 	static int firsttime = 1;
147 
148 	for (vent = vhead; vent; vent = vent->next) {
149 		v = vent->var;
150 		if (firsttime) {
151 			len = strlen(v->header);
152 			if (len > v->width)
153 				v->width = len;
154 			totwidth += v->width + 1;	/* +1 for space */
155 		}
156 		if (v->flag & LJUST) {
157 			if (vent->next == NULL)	/* last one */
158 				(void)printf("%s", v->header);
159 			else
160 				(void)printf("%-*s", v->width,
161 				    v->header);
162 		} else
163 			(void)printf("%*s", v->width, v->header);
164 		if (vent->next != NULL)
165 			(void)putchar(' ');
166 	}
167 	(void)putchar('\n');
168 	if (firsttime) {
169 		firsttime = 0;
170 		totwidth--;	/* take off last space */
171 	}
172 }
173 
174 /*
175  * Return 1 if the the command name in the argument vector (u-area) does
176  * not match the command name (p_comm)
177  */
178 static int
179 titlecmp(name, argv)
180 	char *name;
181 	char **argv;
182 {
183 	char *title;
184 	int namelen;
185 
186 
187 	/* no argument vector == no match; system processes/threads do that */
188 	if (argv == 0 || argv[0] == 0)
189 		return (1);
190 
191 	title = cmdpart(argv[0]);
192 
193 	/* the basename matches */
194 	if (!strcmp(name, title))
195 		return (0);
196 
197 	/* handle login shells, by skipping the leading - */
198 	if (title[0] == '-' && !strcmp(name, title+1))
199 		return (0);
200 
201 	namelen = strlen(name);
202 
203 	/* handle daemons that report activity as daemonname: activity */
204 	if (argv[1] == 0 &&
205 	    !strncmp(name, title, namelen) &&
206 	    title[namelen + 0] == ':' &&
207 	    title[namelen + 1] == ' ')
208 		return (0);
209 
210 	return (1);
211 }
212 
213 static void
214 doubleprintorsetwidth(v, val, prec, mode)
215 	VAR *v;
216 	double val;
217 	int prec;
218 	int mode;
219 {
220 	int fmtlen;
221 
222 	if (mode == WIDTHMODE) {
223 		if (val < 0.0 && val < v->longestnd) {
224 			fmtlen = (int)log10(-val) + prec + 2;
225 			v->longestnd = val;
226 			if (fmtlen > v->width)
227 				v->width = fmtlen;
228 		} else if (val > 0.0 && val > v->longestpd) {
229 			fmtlen = (int)log10(val) + prec + 1;
230 			v->longestpd = val;
231 			if (fmtlen > v->width)
232 				v->width = fmtlen;
233 		}
234 	} else {
235 		printf("%*.*f", v->width, prec, val);
236 	}
237 }
238 
239 static void
240 intprintorsetwidth(v, val, mode)
241 	VAR *v;
242 	int val;
243 	int mode;
244 {
245 	int fmtlen;
246 
247 	if (mode == WIDTHMODE) {
248 		if (val < 0 && val < v->longestn) {
249 			v->longestn = val;
250 			fmtlen = iwidth(-val) + 1;
251 			if (fmtlen > v->width)
252 				v->width = fmtlen;
253 		} else if (val > 0 && val > v->longestp) {
254 			v->longestp = val;
255 			fmtlen = iwidth(val);
256 			if (fmtlen > v->width)
257 				v->width = fmtlen;
258 		}
259 	} else
260 		printf("%*d", v->width, val);
261 }
262 
263 static void
264 strprintorsetwidth(v, str, mode)
265 	VAR *v;
266 	const char *str;
267 {
268 	int len;
269 
270 	if (mode == WIDTHMODE) {
271 		len = strlen(str);
272 		if (len > v->width)
273 			v->width = len;
274 	} else {
275 		if (v->flag & LJUST)
276 			printf("%-*.*s", v->width, v->width, str);
277 		else
278 			printf("%*.*s", v->width, v->width, str);
279 	}
280 }
281 
282 void
283 command(arg, ve, mode)
284 	void *arg;
285 	VARENT *ve;
286 	int mode;
287 {
288 	struct kinfo_proc2 *ki;
289 	VAR *v;
290 	int left;
291 	char **argv, **p, *name;
292 
293 	if (mode == WIDTHMODE)
294 		return;
295 
296 	ki = arg;
297 	v = ve->var;
298 	if (ve->next != NULL || termwidth != UNLIMITED) {
299 		if (ve->next == NULL) {
300 			left = termwidth - (totwidth - v->width);
301 			if (left < 1) /* already wrapped, just use std width */
302 				left = v->width;
303 		} else
304 			left = v->width;
305 	} else
306 		left = -1;
307 	if (needenv && kd) {
308 		argv = kvm_getenvv2(kd, ki, termwidth);
309 		if ((p = argv) != NULL) {
310 			while (*p) {
311 				fmt_puts(*p, &left);
312 				p++;
313 				fmt_putc(' ', &left);
314 			}
315 		}
316 	}
317 	if (needcomm) {
318 		name = ki->p_comm;
319 		if (!commandonly) {
320 			argv = kvm_getargv2(kd, ki, termwidth);
321 			if ((p = argv) != NULL) {
322 				while (*p) {
323 					fmt_puts(*p, &left);
324 					p++;
325 					fmt_putc(' ', &left);
326 				}
327 				if (titlecmp(name, argv)) {
328 					/*
329 					 * append the real command name within
330 					 * parentheses, if the command name
331 					 * does not match the one in the
332 					 * argument vector
333 					 */
334 					fmt_putc('(', &left);
335 					fmt_puts(name, &left);
336 					fmt_putc(')', &left);
337 				}
338 			} else {
339 				/*
340 				 * Commands that don't set an argv vector
341 				 * are printed with square brackets if they
342 				 * are system commands.  Otherwise they are
343 				 * printed within parentheses.
344 				 */
345 				if (ki->p_flag & P_SYSTEM) {
346 					fmt_putc('[', &left);
347 					fmt_puts(name, &left);
348 					fmt_putc(']', &left);
349 				} else {
350 					fmt_putc('(', &left);
351 					fmt_puts(name, &left);
352 					fmt_putc(')', &left);
353 				}
354 			}
355 		} else {
356 			fmt_puts(name, &left);
357 		}
358 	}
359 	if (ve->next && left > 0)
360 		printf("%*s", left, "");
361 }
362 
363 void
364 groups(arg, ve, mode)
365 	void *arg;
366 	VARENT *ve;
367 	int mode;
368 {
369 	struct kinfo_proc2 *ki;
370 	VAR *v;
371 	int left, i;
372 	char buf[16], *p;
373 
374 	if (mode == WIDTHMODE)
375 		return;
376 
377 	ki = arg;
378 	v = ve->var;
379 	if (ve->next != NULL || termwidth != UNLIMITED) {
380 		if (ve->next == NULL) {
381 			left = termwidth - (totwidth - v->width);
382 			if (left < 1) /* already wrapped, just use std width */
383 				left = v->width;
384 		} else
385 			left = v->width;
386 	} else
387 		left = -1;
388 
389 	if (ki->p_ngroups == 0) {
390 		fmt_putc('-', &left);
391 		return;
392 	}
393 
394 	for (i = 0; i < ki->p_ngroups; i++) {
395 		(void)snprintf(buf, sizeof(buf), "%d", ki->p_groups[i]);
396 		if (i)
397 			fmt_putc(' ', &left);
398 		for (p = &buf[0]; *p; p++)
399 			fmt_putc(*p, &left);
400 	}
401 
402 	if (ve->next && left > 0)
403 		printf("%*s", left, "");
404 }
405 
406 void
407 groupnames(arg, ve, mode)
408 	void *arg;
409 	VARENT *ve;
410 	int mode;
411 {
412 	struct kinfo_proc2 *ki;
413 	VAR *v;
414 	int left, i;
415 	const char *p;
416 
417 	if (mode == WIDTHMODE)
418 		return;
419 
420 	ki = arg;
421 	v = ve->var;
422 	if (ve->next != NULL || termwidth != UNLIMITED) {
423 		if (ve->next == NULL) {
424 			left = termwidth - (totwidth - v->width);
425 			if (left < 1) /* already wrapped, just use std width */
426 				left = v->width;
427 		} else
428 			left = v->width;
429 	} else
430 		left = -1;
431 
432 	if (ki->p_ngroups == 0) {
433 		fmt_putc('-', &left);
434 		return;
435 	}
436 
437 	for (i = 0; i < ki->p_ngroups; i++) {
438 		if (i)
439 			fmt_putc(' ', &left);
440 		for (p = group_from_gid(ki->p_groups[i], 0); *p; p++)
441 			fmt_putc(*p, &left);
442 	}
443 
444 	if (ve->next && left > 0)
445 		printf("%*s", left, "");
446 }
447 
448 void
449 ucomm(arg, ve, mode)
450 	void *arg;
451 	VARENT *ve;
452 	int mode;
453 {
454 	struct kinfo_proc2 *k;
455 	VAR *v;
456 
457 	k = arg;
458 	v = ve->var;
459 	strprintorsetwidth(v, k->p_comm, mode);
460 }
461 
462 void
463 logname(arg, ve, mode)
464 	void *arg;
465 	VARENT *ve;
466 	int mode;
467 {
468 	struct kinfo_proc2 *k;
469 	VAR *v;
470 
471 	k = arg;
472 	v = ve->var;
473 	strprintorsetwidth(v, k->p_login, mode);
474 }
475 
476 void
477 state(arg, ve, mode)
478 	void *arg;
479 	VARENT *ve;
480 	int mode;
481 {
482 	struct kinfo_proc2 *k;
483 	int flag, is_zombie;
484 	char *cp;
485 	VAR *v;
486 	char buf[16];
487 
488 	k = arg;
489 	is_zombie = 0;
490 	v = ve->var;
491 	flag = k->p_flag;
492 	cp = buf;
493 
494 	switch (k->p_stat) {
495 
496 	case LSSTOP:
497 		*cp = 'T';
498 		break;
499 
500 	case LSSLEEP:
501 		if (flag & L_SINTR)	/* interruptable (long) */
502 			*cp = k->p_slptime >= maxslp ? 'I' : 'S';
503 		else
504 			*cp = 'D';
505 		break;
506 
507 	case LSRUN:
508 	case LSIDL:
509 	case LSONPROC:
510 		*cp = 'R';
511 		break;
512 
513 	case LSZOMB:
514 	case LSDEAD:
515 		*cp = 'Z';
516 		is_zombie = 1;
517 		break;
518 
519 	case LSSUSPENDED:
520 		*cp = 'U';
521 		break;
522 
523 	default:
524 		*cp = '?';
525 	}
526 	cp++;
527 	if (flag & L_INMEM) {
528 	} else
529 		*cp++ = 'W';
530 	if (k->p_nice < NZERO)
531 		*cp++ = '<';
532 	else if (k->p_nice > NZERO)
533 		*cp++ = 'N';
534 	if (flag & P_TRACED)
535 		*cp++ = 'X';
536 	if (flag & P_SYSTRACE)
537 		*cp++ = 'x';
538 	if (flag & P_WEXIT && !is_zombie)
539 		*cp++ = 'E';
540 	if (flag & P_PPWAIT)
541 		*cp++ = 'V';
542 	if (flag & P_SYSTEM)
543 		*cp++ = 'K';
544 	/* system process might have this too, don't need to double up */
545 	else if (k->p_holdcnt)
546 		*cp++ = 'L';
547 	if (k->p_eflag & EPROC_SLEADER)
548 		*cp++ = 's';
549 	if (flag & P_SA)
550 		*cp++ = 'a';
551 	else if (k->p_nlwps > 1)
552 		*cp++ = 'l';
553 	if ((flag & P_CONTROLT) && k->p__pgid == k->p_tpgid)
554 		*cp++ = '+';
555 	*cp = '\0';
556 	strprintorsetwidth(v, buf, mode);
557 }
558 
559 void
560 lstate(arg, ve, mode)
561 	void *arg;
562 	VARENT *ve;
563 	int mode;
564 {
565 	struct kinfo_lwp *k;
566 	int flag, is_zombie;
567 	char *cp;
568 	VAR *v;
569 	char buf[16];
570 
571 	k = arg;
572 	is_zombie = 0;
573 	v = ve->var;
574 	flag = k->l_flag;
575 	cp = buf;
576 
577 	switch (k->l_stat) {
578 
579 	case LSSTOP:
580 		*cp = 'T';
581 		break;
582 
583 	case LSSLEEP:
584 		if (flag & L_SINTR)	/* interuptable (long) */
585 			*cp = k->l_slptime >= maxslp ? 'I' : 'S';
586 		else
587 			*cp = 'D';
588 		break;
589 
590 	case LSRUN:
591 	case LSIDL:
592 	case LSONPROC:
593 		*cp = 'R';
594 		break;
595 
596 	case LSZOMB:
597 	case LSDEAD:
598 		*cp = 'Z';
599 		is_zombie = 1;
600 		break;
601 
602 	case LSSUSPENDED:
603 		*cp = 'U';
604 		break;
605 
606 	default:
607 		*cp = '?';
608 	}
609 	cp++;
610 	if (flag & L_INMEM) {
611 	} else
612 		*cp++ = 'W';
613 	if (k->l_holdcnt)
614 		*cp++ = 'L';
615 	if (flag & L_DETACHED)
616 		*cp++ = '-';
617 	*cp = '\0';
618 	strprintorsetwidth(v, buf, mode);
619 }
620 
621 void
622 pnice(arg, ve, mode)
623 	void *arg;
624 	VARENT *ve;
625 	int mode;
626 {
627 	struct kinfo_proc2 *k;
628 	VAR *v;
629 
630 	k = arg;
631 	v = ve->var;
632 	intprintorsetwidth(v, k->p_nice - NZERO, mode);
633 }
634 
635 void
636 pri(arg, ve, mode)
637 	void *arg;
638 	VARENT *ve;
639 	int mode;
640 {
641 	struct kinfo_lwp *l;
642 	VAR *v;
643 
644 	l = arg;
645 	v = ve->var;
646 	intprintorsetwidth(v, l->l_priority - PZERO, mode);
647 }
648 
649 void
650 uname(arg, ve, mode)
651 	void *arg;
652 	VARENT *ve;
653 	int mode;
654 {
655 	struct kinfo_proc2 *k;
656 	VAR *v;
657 
658 	k = arg;
659 	v = ve->var;
660 	strprintorsetwidth(v, user_from_uid(k->p_uid, 0), mode);
661 }
662 
663 void
664 runame(arg, ve, mode)
665 	void *arg;
666 	VARENT *ve;
667 	int mode;
668 {
669 	struct kinfo_proc2 *k;
670 	VAR *v;
671 
672 	k = arg;
673 	v = ve->var;
674 	strprintorsetwidth(v, user_from_uid(k->p_ruid, 0), mode);
675 }
676 
677 void
678 svuname(arg, ve, mode)
679 	void *arg;
680 	VARENT *ve;
681 	int mode;
682 {
683 	struct kinfo_proc2 *k;
684 	VAR *v;
685 
686 	k = arg;
687 	v = ve->var;
688 	strprintorsetwidth(v, user_from_uid(k->p_svuid, 0), mode);
689 }
690 
691 void
692 gname(arg, ve, mode)
693 	void *arg;
694 	VARENT *ve;
695 	int mode;
696 {
697 	struct kinfo_proc2 *k;
698 	VAR *v;
699 
700 	k = arg;
701 	v = ve->var;
702 	strprintorsetwidth(v, group_from_gid(k->p_gid, 0), mode);
703 }
704 
705 void
706 rgname(arg, ve, mode)
707 	void *arg;
708 	VARENT *ve;
709 	int mode;
710 {
711 	struct kinfo_proc2 *k;
712 	VAR *v;
713 
714 	k = arg;
715 	v = ve->var;
716 	strprintorsetwidth(v, group_from_gid(k->p_rgid, 0), mode);
717 }
718 
719 void
720 svgname(arg, ve, mode)
721 	void *arg;
722 	VARENT *ve;
723 	int mode;
724 {
725 	struct kinfo_proc2 *k;
726 	VAR *v;
727 
728 	k = arg;
729 	v = ve->var;
730 	strprintorsetwidth(v, group_from_gid(k->p_svgid, 0), mode);
731 }
732 
733 void
734 tdev(arg, ve, mode)
735 	void *arg;
736 	VARENT *ve;
737 	int mode;
738 {
739 	struct kinfo_proc2 *k;
740 	VAR *v;
741 	dev_t dev;
742 	char buff[16];
743 
744 	k = arg;
745 	v = ve->var;
746 	dev = k->p_tdev;
747 	if (dev == NODEV) {
748 		if (mode == PRINTMODE)
749 			(void)printf("%*s", v->width, "??");
750 		else
751 			if (v->width < 2)
752 				v->width = 2;
753 	} else {
754 		(void)snprintf(buff, sizeof(buff),
755 		    "%d/%d", major(dev), minor(dev));
756 		strprintorsetwidth(v, buff, mode);
757 	}
758 }
759 
760 void
761 tname(arg, ve, mode)
762 	void *arg;
763 	VARENT *ve;
764 	int mode;
765 {
766 	struct kinfo_proc2 *k;
767 	VAR *v;
768 	dev_t dev;
769 	const char *ttname;
770 	int noctty;
771 
772 	k = arg;
773 	v = ve->var;
774 	dev = k->p_tdev;
775 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
776 		if (mode == PRINTMODE)
777 			(void)printf("%-*s", v->width, "??");
778 		else
779 			if (v->width < 2)
780 				v->width = 2;
781 	} else {
782 		if (strncmp(ttname, "tty", 3) == 0 ||
783 		    strncmp(ttname, "dty", 3) == 0)
784 			ttname += 3;
785 		noctty = !(k->p_eflag & EPROC_CTTY) ? 1 : 0;
786 		if (mode == WIDTHMODE) {
787 			int fmtlen;
788 
789 			fmtlen = strlen(ttname) + noctty;
790 			if (v->width < fmtlen)
791 				v->width = fmtlen;
792 		} else {
793 			if (noctty)
794 				printf("%-*s-", v->width - 1, ttname);
795 			else
796 				printf("%-*s", v->width, ttname);
797 		}
798 	}
799 }
800 
801 void
802 longtname(arg, ve, mode)
803 	void *arg;
804 	VARENT *ve;
805 	int mode;
806 {
807 	struct kinfo_proc2 *k;
808 	VAR *v;
809 	dev_t dev;
810 	const char *ttname;
811 
812 	k = arg;
813 	v = ve->var;
814 	dev = k->p_tdev;
815 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
816 		if (mode == PRINTMODE)
817 			(void)printf("%-*s", v->width, "??");
818 		else
819 			if (v->width < 2)
820 				v->width = 2;
821 	} else {
822 		strprintorsetwidth(v, ttname, mode);
823 	}
824 }
825 
826 void
827 started(arg, ve, mode)
828 	void *arg;
829 	VARENT *ve;
830 	int mode;
831 {
832 	struct kinfo_proc2 *k;
833 	VAR *v;
834 	static time_t now;
835 	time_t startt;
836 	struct tm *tp;
837 	char buf[100], *cp;
838 
839 	k = arg;
840 	v = ve->var;
841 	if (!k->p_uvalid) {
842 		if (mode == PRINTMODE)
843 			(void)printf("%*s", v->width, "-");
844 		return;
845 	}
846 
847 	startt = k->p_ustart_sec;
848 	tp = localtime(&startt);
849 	if (!now)
850 		(void)time(&now);
851 	if (now - k->p_ustart_sec < SECSPERDAY)
852 		/* I *hate* SCCS... */
853 		(void)strftime(buf, sizeof(buf) - 1, "%l:%" "M%p", tp);
854 	else if (now - k->p_ustart_sec < DAYSPERWEEK * SECSPERDAY)
855 		/* I *hate* SCCS... */
856 		(void)strftime(buf, sizeof(buf) - 1, "%a%" "I%p", tp);
857 	else
858 		(void)strftime(buf, sizeof(buf) - 1, "%e%b%y", tp);
859 	/* %e and %l can start with a space. */
860 	cp = buf;
861 	if (*cp == ' ')
862 		cp++;
863 	strprintorsetwidth(v, cp, mode);
864 }
865 
866 void
867 lstarted(arg, ve, mode)
868 	void *arg;
869 	VARENT *ve;
870 	int mode;
871 {
872 	struct kinfo_proc2 *k;
873 	VAR *v;
874 	time_t startt;
875 	char buf[100];
876 
877 	k = arg;
878 	v = ve->var;
879 	if (!k->p_uvalid) {
880 		/*
881 		 * Minimum width is less than header - we don't
882 		 * need to check it every time.
883 		 */
884 		if (mode == PRINTMODE)
885 			(void)printf("%*s", v->width, "-");
886 		return;
887 	}
888 	startt = k->p_ustart_sec;
889 
890 	/* assume all times are the same length */
891 	if (mode != WIDTHMODE || v->width == 0) {
892 		(void)strftime(buf, sizeof(buf) -1, "%c",
893 		    localtime(&startt));
894 		strprintorsetwidth(v, buf, mode);
895 	}
896 }
897 
898 void
899 wchan(arg, ve, mode)
900 	void *arg;
901 	VARENT *ve;
902 	int mode;
903 {
904 	struct kinfo_lwp *l;
905 	VAR *v;
906 	char *buf;
907 
908 	l = arg;
909 	v = ve->var;
910 	if (l->l_wchan) {
911 		if (l->l_wmesg) {
912 			strprintorsetwidth(v, l->l_wmesg, mode);
913 			v->width = min(v->width, KI_WMESGLEN);
914 		} else {
915 			(void)asprintf(&buf, "%-*" PRIx64, v->width,
916 			    l->l_wchan);
917 			if (buf == NULL)
918 				err(1, "%s", "");
919 			strprintorsetwidth(v, buf, mode);
920 			v->width = min(v->width, KI_WMESGLEN);
921 			free(buf);
922 		}
923 	} else {
924 		if (mode == PRINTMODE)
925 			(void)printf("%-*s", v->width, "-");
926 	}
927 }
928 
929 #define pgtok(a)        (((a)*getpagesize())/1024)
930 
931 void
932 vsize(arg, ve, mode)
933 	void *arg;
934 	VARENT *ve;
935 	int mode;
936 {
937 	struct kinfo_proc2 *k;
938 	VAR *v;
939 
940 	k = arg;
941 	v = ve->var;
942 	intprintorsetwidth(v,
943 	    pgtok(k->p_vm_dsize + k->p_vm_ssize + k->p_vm_tsize), mode);
944 }
945 
946 void
947 rssize(arg, ve, mode)
948 	void *arg;
949 	VARENT *ve;
950 	int mode;
951 {
952 	struct kinfo_proc2 *k;
953 	VAR *v;
954 
955 	k = arg;
956 	v = ve->var;
957 	/* XXX don't have info about shared */
958 	intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode);
959 }
960 
961 void
962 p_rssize(arg, ve, mode)		/* doesn't account for text */
963 	void *arg;
964 	VARENT *ve;
965 	int mode;
966 {
967 	struct kinfo_proc2 *k;
968 	VAR *v;
969 
970 	k = arg;
971 	v = ve->var;
972 	intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode);
973 }
974 
975 void
976 cputime(arg, ve, mode)
977 	void *arg;
978 	VARENT *ve;
979 	int mode;
980 {
981 	struct kinfo_proc2 *k;
982 	VAR *v;
983 	int32_t secs;
984 	int32_t psecs;	/* "parts" of a second. first micro, then centi */
985 	int fmtlen;
986 
987 	k = arg;
988 	v = ve->var;
989 	if (P_ZOMBIE(k) || k->p_uvalid == 0) {
990 		secs = 0;
991 		psecs = 0;
992 	} else {
993 		/*
994 		 * This counts time spent handling interrupts.  We could
995 		 * fix this, but it is not 100% trivial (and interrupt
996 		 * time fractions only work on the sparc anyway).	XXX
997 		 */
998 		secs = k->p_rtime_sec;
999 		psecs = k->p_rtime_usec;
1000 		if (sumrusage) {
1001 			secs += k->p_uctime_sec;
1002 			psecs += k->p_uctime_usec;
1003 		}
1004 		/*
1005 		 * round and scale to 100's
1006 		 */
1007 		psecs = (psecs + 5000) / 10000;
1008 		secs += psecs / 100;
1009 		psecs = psecs % 100;
1010 	}
1011 	if (mode == WIDTHMODE) {
1012 		/*
1013 		 * Ugg, this is the only field where a value of 0 longer
1014 		 * than the column title.
1015 		 * Use SECSPERMIN, because secs is divided by that when
1016 		 * passed to iwidth().
1017 		 */
1018 		if (secs == 0)
1019 			secs = SECSPERMIN;
1020 
1021 		if (secs > v->longestp) {
1022 			v->longestp = secs;
1023 			/* "+6" for the ":%02ld.%02ld" in the printf() below */
1024 			fmtlen = iwidth(secs / SECSPERMIN) + 6;
1025 			if (fmtlen > v->width)
1026 				v->width = fmtlen;
1027 		}
1028 	} else {
1029 		printf("%*ld:%02ld.%02ld", v->width - 6, (long)(secs / SECSPERMIN),
1030 		    (long)(secs % SECSPERMIN), (long)psecs);
1031 	}
1032 }
1033 
1034 double
1035 getpcpu(k)
1036 	struct kinfo_proc2 *k;
1037 {
1038 	static int failure;
1039 
1040 	if (!nlistread)
1041 		failure = (kd) ? donlist() : 1;
1042 	if (failure)
1043 		return (0.0);
1044 
1045 #define	fxtofl(fixpt)	((double)(fixpt) / fscale)
1046 
1047 	/* XXX - I don't like this */
1048 	if (k->p_swtime == 0 || (k->p_flag & L_INMEM) == 0 ||
1049 	    k->p_stat == SZOMB || k->p_stat == SDEAD)
1050 		return (0.0);
1051 	if (rawcpu)
1052 		return (100.0 * fxtofl(k->p_pctcpu));
1053 	return (100.0 * fxtofl(k->p_pctcpu) /
1054 		(1.0 - exp(k->p_swtime * log(ccpu))));
1055 }
1056 
1057 void
1058 pcpu(arg, ve, mode)
1059 	void *arg;
1060 	VARENT *ve;
1061 	int mode;
1062 {
1063 	struct kinfo_proc2 *k;
1064 	VAR *v;
1065 
1066 	k = arg;
1067 	v = ve->var;
1068 	doubleprintorsetwidth(v, getpcpu(k), 1, mode);
1069 }
1070 
1071 double
1072 getpmem(k)
1073 	struct kinfo_proc2 *k;
1074 {
1075 	static int failure;
1076 	double fracmem;
1077 	int szptudot;
1078 
1079 	if (!nlistread)
1080 		failure = (kd) ? donlist() : 1;
1081 	if (failure)
1082 		return (0.0);
1083 
1084 	if ((k->p_flag & L_INMEM) == 0)
1085 		return (0.0);
1086 	/* XXX want pmap ptpages, segtab, etc. (per architecture) */
1087 	szptudot = uspace/getpagesize();
1088 	/* XXX don't have info about shared */
1089 	fracmem = ((float)k->p_vm_rssize + szptudot)/mempages;
1090 	return (100.0 * fracmem);
1091 }
1092 
1093 void
1094 pmem(arg, ve, mode)
1095 	void *arg;
1096 	VARENT *ve;
1097 	int mode;
1098 {
1099 	struct kinfo_proc2 *k;
1100 	VAR *v;
1101 
1102 	k = arg;
1103 	v = ve->var;
1104 	doubleprintorsetwidth(v, getpmem(k), 1, mode);
1105 }
1106 
1107 void
1108 pagein(arg, ve, mode)
1109 	void *arg;
1110 	VARENT *ve;
1111 	int mode;
1112 {
1113 	struct kinfo_proc2 *k;
1114 	VAR *v;
1115 
1116 	k = arg;
1117 	v = ve->var;
1118 	intprintorsetwidth(v, k->p_uvalid ? k->p_uru_majflt : 0, mode);
1119 }
1120 
1121 void
1122 maxrss(arg, ve, mode)
1123 	void *arg;
1124 	VARENT *ve;
1125 	int mode;
1126 {
1127 	VAR *v;
1128 
1129 	v = ve->var;
1130 	/* No need to check width! */
1131 	if (mode == PRINTMODE)
1132 		(void)printf("%*s", v->width, "-");
1133 }
1134 
1135 void
1136 tsize(arg, ve, mode)
1137 	void *arg;
1138 	VARENT *ve;
1139 	int mode;
1140 {
1141 	struct kinfo_proc2 *k;
1142 	VAR *v;
1143 
1144 	k = arg;
1145 	v = ve->var;
1146 	intprintorsetwidth(v, pgtok(k->p_vm_tsize), mode);
1147 }
1148 
1149 /*
1150  * Generic output routines.  Print fields from various prototype
1151  * structures.
1152  */
1153 static void
1154 printval(bp, v, mode)
1155 	void *bp;
1156 	VAR *v;
1157 	int mode;
1158 {
1159 	static char ofmt[32] = "%";
1160 	int width, vok, fmtlen;
1161 	char *fcp, *cp;
1162 	int64_t val;
1163 	u_int64_t uval;
1164 
1165 	/*
1166 	 * Note that the "INF127" check is nonsensical for types
1167 	 * that are or can be signed.
1168 	 */
1169 #define	GET(type)		(*(type *)bp)
1170 #define	CHK_INF127(n)		(((n) > 127) && (v->flag & INF127) ? 127 : (n))
1171 
1172 #define	VSIGN	1
1173 #define	VUNSIGN	2
1174 #define	VPTR	3
1175 
1176 	if (mode == WIDTHMODE) {
1177 		vok = 0;
1178 		switch (v->type) {
1179 		case CHAR:
1180 			val = GET(char);
1181 			vok = VSIGN;
1182 			break;
1183 		case UCHAR:
1184 			uval = CHK_INF127(GET(u_char));
1185 			vok = VUNSIGN;
1186 			break;
1187 		case SHORT:
1188 			val = GET(short);
1189 			vok = VSIGN;
1190 			break;
1191 		case USHORT:
1192 			uval = CHK_INF127(GET(u_short));
1193 			vok = VUNSIGN;
1194 			break;
1195 		case INT32:
1196 			val = GET(int32_t);
1197 			vok = VSIGN;
1198 			break;
1199 		case INT:
1200 			val = GET(int);
1201 			vok = VSIGN;
1202 			break;
1203 		case UINT:
1204 		case UINT32:
1205 			uval = CHK_INF127(GET(u_int));
1206 			vok = VUNSIGN;
1207 			break;
1208 		case LONG:
1209 			val = GET(long);
1210 			vok = VSIGN;
1211 			break;
1212 		case ULONG:
1213 			uval = CHK_INF127(GET(u_long));
1214 			vok = VUNSIGN;
1215 			break;
1216 		case KPTR:
1217 			uval = GET(u_int64_t);
1218 			vok = VPTR;
1219 			break;
1220 		case KPTR24:
1221 			uval = GET(u_int64_t);
1222 			uval &= 0xffffff;
1223 			vok = VPTR;
1224 			break;
1225 		case INT64:
1226 			val = GET(int64_t);
1227 			vok = VSIGN;
1228 			break;
1229 		case UINT64:
1230 			uval = CHK_INF127(GET(u_int64_t));
1231 			vok = VUNSIGN;
1232 			break;
1233 
1234 		case SIGLIST:
1235 		default:
1236 			/* nothing... */;
1237 		}
1238 		switch (vok) {
1239 		case VSIGN:
1240 			if (val < 0 && val < v->longestn) {
1241 				v->longestn = val;
1242 				fmtlen = iwidth(-val) + 1;
1243 				if (fmtlen > v->width)
1244 					v->width = fmtlen;
1245 			} else if (val > 0 && val > v->longestp) {
1246 				v->longestp = val;
1247 				fmtlen = iwidth(val);
1248 				if (fmtlen > v->width)
1249 					v->width = fmtlen;
1250 			}
1251 			return;
1252 		case VUNSIGN:
1253 			if (uval > v->longestu) {
1254 				v->longestu = uval;
1255 				v->width = iwidth(uval);
1256 			}
1257 			return;
1258 		case VPTR:
1259 			fmtlen = 0;
1260 			while (uval > 0) {
1261 				uval >>= 4;
1262 				fmtlen++;
1263 			}
1264 			if (fmtlen > v->width)
1265 				v->width = fmtlen;
1266 			return;
1267 		}
1268 	}
1269 
1270 	width = v->width;
1271 	cp = ofmt + 1;
1272 	fcp = v->fmt;
1273 	if (v->flag & LJUST)
1274 		*cp++ = '-';
1275 	*cp++ = '*';
1276 	while ((*cp++ = *fcp++) != '\0')
1277 		continue;
1278 
1279 	switch (v->type) {
1280 	case CHAR:
1281 		(void)printf(ofmt, width, GET(char));
1282 		return;
1283 	case UCHAR:
1284 		(void)printf(ofmt, width, CHK_INF127(GET(u_char)));
1285 		return;
1286 	case SHORT:
1287 		(void)printf(ofmt, width, GET(short));
1288 		return;
1289 	case USHORT:
1290 		(void)printf(ofmt, width, CHK_INF127(GET(u_short)));
1291 		return;
1292 	case INT:
1293 		(void)printf(ofmt, width, GET(int));
1294 		return;
1295 	case UINT:
1296 		(void)printf(ofmt, width, CHK_INF127(GET(u_int)));
1297 		return;
1298 	case LONG:
1299 		(void)printf(ofmt, width, GET(long));
1300 		return;
1301 	case ULONG:
1302 		(void)printf(ofmt, width, CHK_INF127(GET(u_long)));
1303 		return;
1304 	case KPTR:
1305 		(void)printf(ofmt, width, GET(u_int64_t));
1306 		return;
1307 	case KPTR24:
1308 		(void)printf(ofmt, width, GET(u_int64_t) & 0xffffff);
1309 		return;
1310 	case INT32:
1311 		(void)printf(ofmt, width, GET(int32_t));
1312 		return;
1313 	case UINT32:
1314 		(void)printf(ofmt, width, CHK_INF127(GET(u_int32_t)));
1315 		return;
1316 	case SIGLIST:
1317 		{
1318 			sigset_t *s = (sigset_t *)(void *)bp;
1319 			size_t i;
1320 #define SIGSETSIZE	(sizeof(s->__bits) / sizeof(s->__bits[0]))
1321 			char buf[SIGSETSIZE * 8 + 1];
1322 
1323 			for (i = 0; i < SIGSETSIZE; i++)
1324 				(void)snprintf(&buf[i * 8], 9, "%.8x",
1325 				    s->__bits[(SIGSETSIZE - 1) - i]);
1326 
1327 			/* Skip leading zeroes */
1328 			for (i = 0; buf[i] == '0'; i++)
1329 				continue;
1330 
1331 			if (buf[i] == '\0')
1332 				i--;
1333 			strprintorsetwidth(v, buf + i, mode);
1334 #undef SIGSETSIZE
1335 		}
1336 		return;
1337 	case INT64:
1338 		(void)printf(ofmt, width, GET(int64_t));
1339 		return;
1340 	case UINT64:
1341 		(void)printf(ofmt, width, CHK_INF127(GET(u_int64_t)));
1342 		return;
1343 	default:
1344 		errx(1, "unknown type %d", v->type);
1345 	}
1346 #undef GET
1347 #undef CHK_INF127
1348 }
1349 
1350 void
1351 pvar(arg, ve, mode)
1352 	void *arg;
1353 	VARENT *ve;
1354 	int mode;
1355 {
1356 	VAR *v;
1357 
1358 	v = ve->var;
1359 	if (v->flag & UAREA && !((struct kinfo_proc2 *)arg)->p_uvalid) {
1360 		if (mode == PRINTMODE)
1361 			(void)printf("%*s", v->width, "-");
1362 		return;
1363 	}
1364 
1365 	printval((char *)arg + v->off, v, mode);
1366 }
1367 
1368 void
1369 putimeval(arg, ve, mode)
1370 	void *arg;
1371 	VARENT *ve;
1372 	int mode;
1373 {
1374 	VAR *v = ve->var;
1375 	struct kinfo_proc2 *k = arg;
1376 	ulong secs = *(uint32_t *)((char *)arg + v->off);
1377 	ulong usec = *(uint32_t *)((char *)arg + v->off + sizeof (uint32_t));
1378 	int fmtlen;
1379 
1380 	if (!k->p_uvalid) {
1381 		if (mode == PRINTMODE)
1382 			(void)printf("%*s", v->width, "-");
1383 		return;
1384 	}
1385 
1386 	if (mode == WIDTHMODE) {
1387 		if (!secs)
1388 			/* zero doesn't give correct width... */
1389 			secs = 1;
1390 		if (secs > v->longestu) {
1391 			v->longestu = secs;
1392 			if (secs <= 999)
1393 				/* sss.ssssss */
1394 				fmtlen = iwidth(secs) + 6 + 1;
1395 			else
1396 				/* hh:mm:ss.ss */
1397 				fmtlen = iwidth((secs+1)/3600)
1398 					+ 2 + 1 + 2 + 1 + 2 + 1;
1399 			if (fmtlen > v->width)
1400 				v->width = fmtlen;
1401 		}
1402 		return;
1403 	}
1404 
1405 	if (secs < 999)
1406 		(void)printf( "%*lu.%.6lu", v->width - 6 - 1, secs, usec);
1407 	else {
1408 		uint h, m;
1409 		usec += 5000;
1410 		if (usec >= 1000000) {
1411 			usec -= 1000000;
1412 			secs++;
1413 		}
1414 		m = secs / 60u;
1415 		secs -= m * 60u;
1416 		h = m / 60u;
1417 		m -= h * 60u;
1418 		(void)printf( "%*u:%.2u:%.2lu.%.2lu", v->width - 9, h, m, secs, usec / 10000u );
1419 	}
1420 }
1421