Home | History | Annotate | Line # | Download | only in ps
print.c revision 1.87
      1 /*	$NetBSD: print.c,v 1.87 2004/03/27 14:49:13 simonb 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.87 2004/03/27 14:49:13 simonb 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 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 				}
    310 				if (titlecmp(name, argv)) {
    311 					/*
    312 					 * append the real command name within
    313 					 * parentheses, if the command name
    314 					 * does not match the one in the
    315 					 * argument vector
    316 					 */
    317 					fmt_putc('(', &left);
    318 					fmt_puts(name, &left);
    319 					fmt_putc(')', &left);
    320 				}
    321 			} else {
    322 				/*
    323 				 * Commands that don't set an argv vector
    324 				 * are printed with square brackets if they
    325 				 * are system commands.  Otherwise they are
    326 				 * printed within parentheses.
    327 				 */
    328 				if (ki->p_flag & P_SYSTEM) {
    329 					fmt_putc('[', &left);
    330 					fmt_puts(name, &left);
    331 					fmt_putc(']', &left);
    332 				} else {
    333 					fmt_putc('(', &left);
    334 					fmt_puts(name, &left);
    335 					fmt_putc(')', &left);
    336 				}
    337 			}
    338 		} else {
    339 			fmt_puts(name, &left);
    340 		}
    341 	}
    342 	if (ve->next && left > 0)
    343 		(void)printf("%*s", left, "");
    344 }
    345 
    346 void
    347 groups(void *arg, VARENT *ve, int mode)
    348 {
    349 	struct kinfo_proc2 *ki;
    350 	VAR *v;
    351 	int left, i;
    352 	char buf[16], *p;
    353 
    354 	if (mode == WIDTHMODE)
    355 		return;
    356 
    357 	ki = arg;
    358 	v = ve->var;
    359 	if (ve->next != NULL || termwidth != UNLIMITED) {
    360 		if (ve->next == NULL) {
    361 			left = termwidth - (totwidth - v->width);
    362 			if (left < 1) /* already wrapped, just use std width */
    363 				left = v->width;
    364 		} else
    365 			left = v->width;
    366 	} else
    367 		left = -1;
    368 
    369 	if (ki->p_ngroups == 0) {
    370 		fmt_putc('-', &left);
    371 		return;
    372 	}
    373 
    374 	for (i = 0; i < ki->p_ngroups; i++) {
    375 		(void)snprintf(buf, sizeof(buf), "%d", ki->p_groups[i]);
    376 		if (i)
    377 			fmt_putc(' ', &left);
    378 		for (p = &buf[0]; *p; p++)
    379 			fmt_putc(*p, &left);
    380 	}
    381 
    382 	if (ve->next && left > 0)
    383 		(void)printf("%*s", left, "");
    384 }
    385 
    386 void
    387 groupnames(void *arg, VARENT *ve, int mode)
    388 {
    389 	struct kinfo_proc2 *ki;
    390 	VAR *v;
    391 	int left, i;
    392 	const char *p;
    393 
    394 	if (mode == WIDTHMODE)
    395 		return;
    396 
    397 	ki = arg;
    398 	v = ve->var;
    399 	if (ve->next != NULL || termwidth != UNLIMITED) {
    400 		if (ve->next == NULL) {
    401 			left = termwidth - (totwidth - v->width);
    402 			if (left < 1) /* already wrapped, just use std width */
    403 				left = v->width;
    404 		} else
    405 			left = v->width;
    406 	} else
    407 		left = -1;
    408 
    409 	if (ki->p_ngroups == 0) {
    410 		fmt_putc('-', &left);
    411 		return;
    412 	}
    413 
    414 	for (i = 0; i < ki->p_ngroups; i++) {
    415 		if (i)
    416 			fmt_putc(' ', &left);
    417 		for (p = group_from_gid(ki->p_groups[i], 0); *p; p++)
    418 			fmt_putc(*p, &left);
    419 	}
    420 
    421 	if (ve->next && left > 0)
    422 		(void)printf("%*s", left, "");
    423 }
    424 
    425 void
    426 ucomm(void *arg, VARENT *ve, int mode)
    427 {
    428 	struct kinfo_proc2 *k;
    429 	VAR *v;
    430 
    431 	k = arg;
    432 	v = ve->var;
    433 	strprintorsetwidth(v, k->p_comm, mode);
    434 }
    435 
    436 void
    437 logname(void *arg, VARENT *ve, int mode)
    438 {
    439 	struct kinfo_proc2 *k;
    440 	VAR *v;
    441 
    442 	k = arg;
    443 	v = ve->var;
    444 	strprintorsetwidth(v, k->p_login, mode);
    445 }
    446 
    447 void
    448 state(void *arg, VARENT *ve, int mode)
    449 {
    450 	struct kinfo_proc2 *k;
    451 	int flag, is_zombie;
    452 	char *cp;
    453 	VAR *v;
    454 	char buf[16];
    455 
    456 	k = arg;
    457 	is_zombie = 0;
    458 	v = ve->var;
    459 	flag = k->p_flag;
    460 	cp = buf;
    461 
    462 	switch (k->p_stat) {
    463 
    464 	case LSSTOP:
    465 		*cp = 'T';
    466 		break;
    467 
    468 	case LSSLEEP:
    469 		if (flag & L_SINTR)	/* interruptable (long) */
    470 			*cp = k->p_slptime >= maxslp ? 'I' : 'S';
    471 		else
    472 			*cp = 'D';
    473 		break;
    474 
    475 	case LSRUN:
    476 	case LSIDL:
    477 	case LSONPROC:
    478 		*cp = 'R';
    479 		break;
    480 
    481 	case LSZOMB:
    482 	case LSDEAD:
    483 		*cp = 'Z';
    484 		is_zombie = 1;
    485 		break;
    486 
    487 	case LSSUSPENDED:
    488 		*cp = 'U';
    489 		break;
    490 
    491 	default:
    492 		*cp = '?';
    493 	}
    494 	cp++;
    495 	if (flag & L_INMEM) {
    496 	} else
    497 		*cp++ = 'W';
    498 	if (k->p_nice < NZERO)
    499 		*cp++ = '<';
    500 	else if (k->p_nice > NZERO)
    501 		*cp++ = 'N';
    502 	if (flag & P_TRACED)
    503 		*cp++ = 'X';
    504 	if (flag & P_SYSTRACE)
    505 		*cp++ = 'x';
    506 	if (flag & P_WEXIT && !is_zombie)
    507 		*cp++ = 'E';
    508 	if (flag & P_PPWAIT)
    509 		*cp++ = 'V';
    510 	if (flag & P_SYSTEM)
    511 		*cp++ = 'K';
    512 	/* system process might have this too, don't need to double up */
    513 	else if (k->p_holdcnt)
    514 		*cp++ = 'L';
    515 	if (k->p_eflag & EPROC_SLEADER)
    516 		*cp++ = 's';
    517 	if (flag & P_SA)
    518 		*cp++ = 'a';
    519 	else if (k->p_nlwps > 1)
    520 		*cp++ = 'l';
    521 	if ((flag & P_CONTROLT) && k->p__pgid == k->p_tpgid)
    522 		*cp++ = '+';
    523 	*cp = '\0';
    524 	strprintorsetwidth(v, buf, mode);
    525 }
    526 
    527 void
    528 lstate(void *arg, VARENT *ve, int mode)
    529 {
    530 	struct kinfo_lwp *k;
    531 	int flag, is_zombie;
    532 	char *cp;
    533 	VAR *v;
    534 	char buf[16];
    535 
    536 	k = arg;
    537 	is_zombie = 0;
    538 	v = ve->var;
    539 	flag = k->l_flag;
    540 	cp = buf;
    541 
    542 	switch (k->l_stat) {
    543 
    544 	case LSSTOP:
    545 		*cp = 'T';
    546 		break;
    547 
    548 	case LSSLEEP:
    549 		if (flag & L_SINTR)	/* interruptible (long) */
    550 			*cp = k->l_slptime >= maxslp ? 'I' : 'S';
    551 		else
    552 			*cp = 'D';
    553 		break;
    554 
    555 	case LSRUN:
    556 	case LSIDL:
    557 	case LSONPROC:
    558 		*cp = 'R';
    559 		break;
    560 
    561 	case LSZOMB:
    562 	case LSDEAD:
    563 		*cp = 'Z';
    564 		is_zombie = 1;
    565 		break;
    566 
    567 	case LSSUSPENDED:
    568 		*cp = 'U';
    569 		break;
    570 
    571 	default:
    572 		*cp = '?';
    573 	}
    574 	cp++;
    575 	if (flag & L_INMEM) {
    576 	} else
    577 		*cp++ = 'W';
    578 	if (k->l_holdcnt)
    579 		*cp++ = 'L';
    580 	if (flag & L_DETACHED)
    581 		*cp++ = '-';
    582 	*cp = '\0';
    583 	strprintorsetwidth(v, buf, mode);
    584 }
    585 
    586 void
    587 pnice(void *arg, VARENT *ve, int mode)
    588 {
    589 	struct kinfo_proc2 *k;
    590 	VAR *v;
    591 
    592 	k = arg;
    593 	v = ve->var;
    594 	intprintorsetwidth(v, k->p_nice - NZERO, mode);
    595 }
    596 
    597 void
    598 pri(void *arg, VARENT *ve, int mode)
    599 {
    600 	struct kinfo_lwp *l;
    601 	VAR *v;
    602 
    603 	l = arg;
    604 	v = ve->var;
    605 	intprintorsetwidth(v, l->l_priority - PZERO, mode);
    606 }
    607 
    608 void
    609 uname(void *arg, VARENT *ve, int mode)
    610 {
    611 	struct kinfo_proc2 *k;
    612 	VAR *v;
    613 
    614 	k = arg;
    615 	v = ve->var;
    616 	strprintorsetwidth(v, user_from_uid(k->p_uid, 0), mode);
    617 }
    618 
    619 void
    620 runame(void *arg, VARENT *ve, int mode)
    621 {
    622 	struct kinfo_proc2 *k;
    623 	VAR *v;
    624 
    625 	k = arg;
    626 	v = ve->var;
    627 	strprintorsetwidth(v, user_from_uid(k->p_ruid, 0), mode);
    628 }
    629 
    630 void
    631 svuname(void *arg, VARENT *ve, int mode)
    632 {
    633 	struct kinfo_proc2 *k;
    634 	VAR *v;
    635 
    636 	k = arg;
    637 	v = ve->var;
    638 	strprintorsetwidth(v, user_from_uid(k->p_svuid, 0), mode);
    639 }
    640 
    641 void
    642 gname(void *arg, VARENT *ve, int mode)
    643 {
    644 	struct kinfo_proc2 *k;
    645 	VAR *v;
    646 
    647 	k = arg;
    648 	v = ve->var;
    649 	strprintorsetwidth(v, group_from_gid(k->p_gid, 0), mode);
    650 }
    651 
    652 void
    653 rgname(void *arg, VARENT *ve, int mode)
    654 {
    655 	struct kinfo_proc2 *k;
    656 	VAR *v;
    657 
    658 	k = arg;
    659 	v = ve->var;
    660 	strprintorsetwidth(v, group_from_gid(k->p_rgid, 0), mode);
    661 }
    662 
    663 void
    664 svgname(void *arg, VARENT *ve, int mode)
    665 {
    666 	struct kinfo_proc2 *k;
    667 	VAR *v;
    668 
    669 	k = arg;
    670 	v = ve->var;
    671 	strprintorsetwidth(v, group_from_gid(k->p_svgid, 0), mode);
    672 }
    673 
    674 void
    675 tdev(void *arg, VARENT *ve, int mode)
    676 {
    677 	struct kinfo_proc2 *k;
    678 	VAR *v;
    679 	dev_t dev;
    680 	char buff[16];
    681 
    682 	k = arg;
    683 	v = ve->var;
    684 	dev = k->p_tdev;
    685 	if (dev == NODEV) {
    686 		if (mode == PRINTMODE)
    687 			(void)printf("%*s", v->width, "??");
    688 		else
    689 			if (v->width < 2)
    690 				v->width = 2;
    691 	} else {
    692 		(void)snprintf(buff, sizeof(buff),
    693 		    "%d/%d", major(dev), minor(dev));
    694 		strprintorsetwidth(v, buff, mode);
    695 	}
    696 }
    697 
    698 void
    699 tname(void *arg, VARENT *ve, int mode)
    700 {
    701 	struct kinfo_proc2 *k;
    702 	VAR *v;
    703 	dev_t dev;
    704 	const char *ttname;
    705 	int noctty;
    706 
    707 	k = arg;
    708 	v = ve->var;
    709 	dev = k->p_tdev;
    710 	if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
    711 		if (mode == PRINTMODE)
    712 			(void)printf("%-*s", v->width, "??");
    713 		else
    714 			if (v->width < 2)
    715 				v->width = 2;
    716 	} else {
    717 		if (strncmp(ttname, "tty", 3) == 0 ||
    718 		    strncmp(ttname, "dty", 3) == 0)
    719 			ttname += 3;
    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 		fmtlen = v->width;
    881 
    882 		if (days > 0) {
    883 			(void)printf("%*d", fmtlen - 9, days);
    884 			printed_something = 1;
    885 		} else if (fmtlen > 9) {
    886 			(void)printf("%*s", fmtlen - 9, "");
    887 		}
    888 		if (fmtlen > 9)
    889 			fmtlen = 9;
    890 
    891 		if (printed_something) {
    892 			(void)printf("-%.*d", fmtlen - 7, hours);
    893 			printed_something = 1;
    894 		} else if (hours > 0) {
    895 			(void)printf("%*d", fmtlen - 6, hours);
    896 			printed_something = 1;
    897 		} else if (fmtlen > 6) {
    898 			(void)printf("%*s", fmtlen - 6, "");
    899 		}
    900 		if (fmtlen > 6)
    901 			fmtlen = 6;
    902 
    903 		/* Don't need to set fmtlen or printed_something any more... */
    904 		if (printed_something) {
    905 			(void)printf(":%.*d", fmtlen - 4, mins);
    906 		} else if (mins > 0) {
    907 			(void)printf("%*d", fmtlen - 3, mins);
    908 		} else if (fmtlen > 3) {
    909 			(void)printf("%*s", fmtlen - 3, "0");
    910 		}
    911 
    912 		(void)printf(":%.2d", secs);
    913 	}
    914 }
    915 
    916 void
    917 wchan(void *arg, VARENT *ve, int mode)
    918 {
    919 	struct kinfo_lwp *l;
    920 	VAR *v;
    921 	char *buf;
    922 
    923 	l = arg;
    924 	v = ve->var;
    925 	if (l->l_wchan) {
    926 		if (l->l_wmesg) {
    927 			strprintorsetwidth(v, l->l_wmesg, mode);
    928 			v->width = min(v->width, KI_WMESGLEN);
    929 		} else {
    930 			(void)asprintf(&buf, "%-*" PRIx64, v->width,
    931 			    l->l_wchan);
    932 			if (buf == NULL)
    933 				err(1, "%s", "");
    934 			strprintorsetwidth(v, buf, mode);
    935 			v->width = min(v->width, KI_WMESGLEN);
    936 			free(buf);
    937 		}
    938 	} else {
    939 		if (mode == PRINTMODE)
    940 			(void)printf("%-*s", v->width, "-");
    941 	}
    942 }
    943 
    944 #define	pgtok(a)        (((a)*getpagesize())/1024)
    945 
    946 void
    947 vsize(void *arg, VARENT *ve, int mode)
    948 {
    949 	struct kinfo_proc2 *k;
    950 	VAR *v;
    951 
    952 	k = arg;
    953 	v = ve->var;
    954 	intprintorsetwidth(v,
    955 	    pgtok(k->p_vm_dsize + k->p_vm_ssize + k->p_vm_tsize), mode);
    956 }
    957 
    958 void
    959 rssize(void *arg, VARENT *ve, int mode)
    960 {
    961 	struct kinfo_proc2 *k;
    962 	VAR *v;
    963 
    964 	k = arg;
    965 	v = ve->var;
    966 	/* XXX don't have info about shared */
    967 	intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode);
    968 }
    969 
    970 void
    971 p_rssize(void *arg, VARENT *ve, int mode)	/* doesn't account for text */
    972 {
    973 	struct kinfo_proc2 *k;
    974 	VAR *v;
    975 
    976 	k = arg;
    977 	v = ve->var;
    978 	intprintorsetwidth(v, pgtok(k->p_vm_rssize), mode);
    979 }
    980 
    981 void
    982 cputime(void *arg, VARENT *ve, int mode)
    983 {
    984 	struct kinfo_proc2 *k;
    985 	VAR *v;
    986 	int32_t secs;
    987 	int32_t psecs;	/* "parts" of a second. first micro, then centi */
    988 	int fmtlen;
    989 
    990 	k = arg;
    991 	v = ve->var;
    992 	if (P_ZOMBIE(k) || k->p_uvalid == 0) {
    993 		secs = 0;
    994 		psecs = 0;
    995 	} else {
    996 		/*
    997 		 * This counts time spent handling interrupts.  We could
    998 		 * fix this, but it is not 100% trivial (and interrupt
    999 		 * time fractions only work on the sparc anyway).	XXX
   1000 		 */
   1001 		secs = k->p_rtime_sec;
   1002 		psecs = k->p_rtime_usec;
   1003 		if (sumrusage) {
   1004 			secs += k->p_uctime_sec;
   1005 			psecs += k->p_uctime_usec;
   1006 		}
   1007 		/*
   1008 		 * round and scale to 100's
   1009 		 */
   1010 		psecs = (psecs + 5000) / 10000;
   1011 		secs += psecs / 100;
   1012 		psecs = psecs % 100;
   1013 	}
   1014 	if (mode == WIDTHMODE) {
   1015 		/*
   1016 		 * Ugg, this is the only field where a value of 0 is longer
   1017 		 * than the column title.
   1018 		 * Use SECSPERMIN, because secs is divided by that when
   1019 		 * passed to iwidth().
   1020 		 */
   1021 		if (secs == 0)
   1022 			secs = SECSPERMIN;
   1023 
   1024 		if (secs > v->longestp) {
   1025 			v->longestp = secs;
   1026 			/* "+6" for the ":%02ld.%02ld" in the printf() below */
   1027 			fmtlen = iwidth(secs / SECSPERMIN) + 6;
   1028 			if (fmtlen > v->width)
   1029 				v->width = fmtlen;
   1030 		}
   1031 	} else {
   1032 		(void)printf("%*ld:%02ld.%02ld", v->width - 6,
   1033 		    (long)(secs / SECSPERMIN), (long)(secs % SECSPERMIN),
   1034 		    (long)psecs);
   1035 	}
   1036 }
   1037 
   1038 double
   1039 getpcpu(k)
   1040 	struct kinfo_proc2 *k;
   1041 {
   1042 	static int failure;
   1043 
   1044 	if (!nlistread)
   1045 		failure = (kd) ? donlist() : 1;
   1046 	if (failure)
   1047 		return (0.0);
   1048 
   1049 #define	fxtofl(fixpt)	((double)(fixpt) / fscale)
   1050 
   1051 	/* XXX - I don't like this */
   1052 	if (k->p_swtime == 0 || (k->p_flag & L_INMEM) == 0 ||
   1053 	    k->p_stat == SZOMB)
   1054 		return (0.0);
   1055 	if (rawcpu)
   1056 		return (100.0 * fxtofl(k->p_pctcpu));
   1057 	return (100.0 * fxtofl(k->p_pctcpu) /
   1058 		(1.0 - exp(k->p_swtime * log(ccpu))));
   1059 }
   1060 
   1061 void
   1062 pcpu(void *arg, VARENT *ve, int mode)
   1063 {
   1064 	struct kinfo_proc2 *k;
   1065 	VAR *v;
   1066 
   1067 	k = arg;
   1068 	v = ve->var;
   1069 	doubleprintorsetwidth(v, getpcpu(k), 1, mode);
   1070 }
   1071 
   1072 double
   1073 getpmem(k)
   1074 	struct kinfo_proc2 *k;
   1075 {
   1076 	static int failure;
   1077 	double fracmem;
   1078 	int szptudot;
   1079 
   1080 	if (!nlistread)
   1081 		failure = (kd) ? donlist() : 1;
   1082 	if (failure)
   1083 		return (0.0);
   1084 
   1085 	if ((k->p_flag & L_INMEM) == 0)
   1086 		return (0.0);
   1087 	/* XXX want pmap ptpages, segtab, etc. (per architecture) */
   1088 	szptudot = uspace/getpagesize();
   1089 	/* XXX don't have info about shared */
   1090 	fracmem = ((float)k->p_vm_rssize + szptudot)/mempages;
   1091 	return (100.0 * fracmem);
   1092 }
   1093 
   1094 void
   1095 pmem(void *arg, VARENT *ve, int mode)
   1096 {
   1097 	struct kinfo_proc2 *k;
   1098 	VAR *v;
   1099 
   1100 	k = arg;
   1101 	v = ve->var;
   1102 	doubleprintorsetwidth(v, getpmem(k), 1, mode);
   1103 }
   1104 
   1105 void
   1106 pagein(void *arg, VARENT *ve, int mode)
   1107 {
   1108 	struct kinfo_proc2 *k;
   1109 	VAR *v;
   1110 
   1111 	k = arg;
   1112 	v = ve->var;
   1113 	intprintorsetwidth(v, k->p_uvalid ? k->p_uru_majflt : 0, mode);
   1114 }
   1115 
   1116 void
   1117 maxrss(void *arg, VARENT *ve, int mode)
   1118 {
   1119 	VAR *v;
   1120 
   1121 	v = ve->var;
   1122 	/* No need to check width! */
   1123 	if (mode == PRINTMODE)
   1124 		(void)printf("%*s", v->width, "-");
   1125 }
   1126 
   1127 void
   1128 tsize(void *arg, VARENT *ve, int mode)
   1129 {
   1130 	struct kinfo_proc2 *k;
   1131 	VAR *v;
   1132 
   1133 	k = arg;
   1134 	v = ve->var;
   1135 	intprintorsetwidth(v, pgtok(k->p_vm_tsize), mode);
   1136 }
   1137 
   1138 /*
   1139  * Generic output routines.  Print fields from various prototype
   1140  * structures.
   1141  */
   1142 static void
   1143 printval(bp, v, mode)
   1144 	void *bp;
   1145 	VAR *v;
   1146 	int mode;
   1147 {
   1148 	static char ofmt[32] = "%";
   1149 	int width, vok, fmtlen;
   1150 	char *fcp, *cp;
   1151 	int64_t val;
   1152 	u_int64_t uval;
   1153 
   1154 	/*
   1155 	 * Note that the "INF127" check is nonsensical for types
   1156 	 * that are or can be signed.
   1157 	 */
   1158 #define	GET(type)		(*(type *)bp)
   1159 #define	CHK_INF127(n)		(((n) > 127) && (v->flag & INF127) ? 127 : (n))
   1160 
   1161 #define	VSIGN	1
   1162 #define	VUNSIGN	2
   1163 #define	VPTR	3
   1164 
   1165 	if (mode == WIDTHMODE) {
   1166 		vok = 0;
   1167 		switch (v->type) {
   1168 		case CHAR:
   1169 			val = GET(char);
   1170 			vok = VSIGN;
   1171 			break;
   1172 		case UCHAR:
   1173 			uval = CHK_INF127(GET(u_char));
   1174 			vok = VUNSIGN;
   1175 			break;
   1176 		case SHORT:
   1177 			val = GET(short);
   1178 			vok = VSIGN;
   1179 			break;
   1180 		case USHORT:
   1181 			uval = CHK_INF127(GET(u_short));
   1182 			vok = VUNSIGN;
   1183 			break;
   1184 		case INT32:
   1185 			val = GET(int32_t);
   1186 			vok = VSIGN;
   1187 			break;
   1188 		case INT:
   1189 			val = GET(int);
   1190 			vok = VSIGN;
   1191 			break;
   1192 		case UINT:
   1193 		case UINT32:
   1194 			uval = CHK_INF127(GET(u_int));
   1195 			vok = VUNSIGN;
   1196 			break;
   1197 		case LONG:
   1198 			val = GET(long);
   1199 			vok = VSIGN;
   1200 			break;
   1201 		case ULONG:
   1202 			uval = CHK_INF127(GET(u_long));
   1203 			vok = VUNSIGN;
   1204 			break;
   1205 		case KPTR:
   1206 			uval = GET(u_int64_t);
   1207 			vok = VPTR;
   1208 			break;
   1209 		case KPTR24:
   1210 			uval = GET(u_int64_t);
   1211 			uval &= 0xffffff;
   1212 			vok = VPTR;
   1213 			break;
   1214 		case INT64:
   1215 			val = GET(int64_t);
   1216 			vok = VSIGN;
   1217 			break;
   1218 		case UINT64:
   1219 			uval = CHK_INF127(GET(u_int64_t));
   1220 			vok = VUNSIGN;
   1221 			break;
   1222 
   1223 		case SIGLIST:
   1224 		default:
   1225 			/* nothing... */;
   1226 		}
   1227 		switch (vok) {
   1228 		case VSIGN:
   1229 			if (val < 0 && val < v->longestn) {
   1230 				v->longestn = val;
   1231 				fmtlen = iwidth(-val) + 1;
   1232 				if (fmtlen > v->width)
   1233 					v->width = fmtlen;
   1234 			} else if (val > 0 && val > v->longestp) {
   1235 				v->longestp = val;
   1236 				fmtlen = iwidth(val);
   1237 				if (fmtlen > v->width)
   1238 					v->width = fmtlen;
   1239 			}
   1240 			return;
   1241 		case VUNSIGN:
   1242 			if (uval > v->longestu) {
   1243 				v->longestu = uval;
   1244 				v->width = iwidth(uval);
   1245 			}
   1246 			return;
   1247 		case VPTR:
   1248 			fmtlen = 0;
   1249 			while (uval > 0) {
   1250 				uval >>= 4;
   1251 				fmtlen++;
   1252 			}
   1253 			if (fmtlen > v->width)
   1254 				v->width = fmtlen;
   1255 			return;
   1256 		}
   1257 	}
   1258 
   1259 	width = v->width;
   1260 	cp = ofmt + 1;
   1261 	fcp = v->fmt;
   1262 	if (v->flag & LJUST)
   1263 		*cp++ = '-';
   1264 	*cp++ = '*';
   1265 	while ((*cp++ = *fcp++) != '\0')
   1266 		continue;
   1267 
   1268 	switch (v->type) {
   1269 	case CHAR:
   1270 		(void)printf(ofmt, width, GET(char));
   1271 		return;
   1272 	case UCHAR:
   1273 		(void)printf(ofmt, width, CHK_INF127(GET(u_char)));
   1274 		return;
   1275 	case SHORT:
   1276 		(void)printf(ofmt, width, GET(short));
   1277 		return;
   1278 	case USHORT:
   1279 		(void)printf(ofmt, width, CHK_INF127(GET(u_short)));
   1280 		return;
   1281 	case INT:
   1282 		(void)printf(ofmt, width, GET(int));
   1283 		return;
   1284 	case UINT:
   1285 		(void)printf(ofmt, width, CHK_INF127(GET(u_int)));
   1286 		return;
   1287 	case LONG:
   1288 		(void)printf(ofmt, width, GET(long));
   1289 		return;
   1290 	case ULONG:
   1291 		(void)printf(ofmt, width, CHK_INF127(GET(u_long)));
   1292 		return;
   1293 	case KPTR:
   1294 		(void)printf(ofmt, width, GET(u_int64_t));
   1295 		return;
   1296 	case KPTR24:
   1297 		(void)printf(ofmt, width, GET(u_int64_t) & 0xffffff);
   1298 		return;
   1299 	case INT32:
   1300 		(void)printf(ofmt, width, GET(int32_t));
   1301 		return;
   1302 	case UINT32:
   1303 		(void)printf(ofmt, width, CHK_INF127(GET(u_int32_t)));
   1304 		return;
   1305 	case SIGLIST:
   1306 		{
   1307 			sigset_t *s = (sigset_t *)(void *)bp;
   1308 			size_t i;
   1309 #define	SIGSETSIZE	(sizeof(s->__bits) / sizeof(s->__bits[0]))
   1310 			char buf[SIGSETSIZE * 8 + 1];
   1311 
   1312 			for (i = 0; i < SIGSETSIZE; i++)
   1313 				(void)snprintf(&buf[i * 8], 9, "%.8x",
   1314 				    s->__bits[(SIGSETSIZE - 1) - i]);
   1315 
   1316 			/* Skip leading zeroes */
   1317 			for (i = 0; buf[i] == '0'; i++)
   1318 				continue;
   1319 
   1320 			if (buf[i] == '\0')
   1321 				i--;
   1322 			strprintorsetwidth(v, buf + i, mode);
   1323 #undef SIGSETSIZE
   1324 		}
   1325 		return;
   1326 	case INT64:
   1327 		(void)printf(ofmt, width, GET(int64_t));
   1328 		return;
   1329 	case UINT64:
   1330 		(void)printf(ofmt, width, CHK_INF127(GET(u_int64_t)));
   1331 		return;
   1332 	default:
   1333 		errx(1, "unknown type %d", v->type);
   1334 	}
   1335 #undef GET
   1336 #undef CHK_INF127
   1337 }
   1338 
   1339 void
   1340 pvar(void *arg, VARENT *ve, int mode)
   1341 {
   1342 	VAR *v;
   1343 
   1344 	v = ve->var;
   1345 	if (v->flag & UAREA && !((struct kinfo_proc2 *)arg)->p_uvalid) {
   1346 		if (mode == PRINTMODE)
   1347 			(void)printf("%*s", v->width, "-");
   1348 		return;
   1349 	}
   1350 
   1351 	(void)printval((char *)arg + v->off, v, mode);
   1352 }
   1353 
   1354 void
   1355 putimeval(void *arg, VARENT *ve, int mode)
   1356 {
   1357 	VAR *v = ve->var;
   1358 	struct kinfo_proc2 *k = arg;
   1359 	ulong secs = *(uint32_t *)((char *)arg + v->off);
   1360 	ulong usec = *(uint32_t *)((char *)arg + v->off + sizeof (uint32_t));
   1361 	int fmtlen;
   1362 
   1363 	if (!k->p_uvalid) {
   1364 		if (mode == PRINTMODE)
   1365 			(void)printf("%*s", v->width, "-");
   1366 		return;
   1367 	}
   1368 
   1369 	if (mode == WIDTHMODE) {
   1370 		if (secs == 0)
   1371 			/* non-zero so fmtlen is calculated at least once */
   1372 			secs = 1;
   1373 		if (secs > v->longestu) {
   1374 			v->longestu = secs;
   1375 			if (secs <= 999)
   1376 				/* sss.ssssss */
   1377 				fmtlen = iwidth(secs) + 6 + 1;
   1378 			else
   1379 				/* hh:mm:ss.ss */
   1380 				fmtlen = iwidth((secs + 1) / SECSPERHOUR)
   1381 					+ 2 + 1 + 2 + 1 + 2 + 1;
   1382 			if (fmtlen > v->width)
   1383 				v->width = fmtlen;
   1384 		}
   1385 		return;
   1386 	}
   1387 
   1388 	if (secs < 999)
   1389 		(void)printf( "%*lu.%.6lu", v->width - 6 - 1, secs, usec);
   1390 	else {
   1391 		uint h, m;
   1392 		usec += 5000;
   1393 		if (usec >= 1000000) {
   1394 			usec -= 1000000;
   1395 			secs++;
   1396 		}
   1397 		m = secs / SECSPERMIN;
   1398 		secs -= m * SECSPERMIN;
   1399 		h = m / MINSPERHOUR;
   1400 		m -= h * MINSPERHOUR;
   1401 		(void)printf( "%*u:%.2u:%.2lu.%.2lu", v->width - 9, h, m, secs,
   1402 		    usec / 10000u );
   1403 	}
   1404 }
   1405