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