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