xref: /openbsd-src/usr.bin/top/display.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /* $OpenBSD: display.c,v 1.47 2014/01/14 02:44:57 guenther Exp $	 */
2 
3 /*
4  *  Top users/processes display for Unix
5  *  Version 3
6  *
7  * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8  * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
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  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  *  This file contains the routines that display information on the screen.
33  *  Each section of the screen has two routines:  one for initially writing
34  *  all constant and dynamic text, and one for only updating the text that
35  *  changes.  The prefix "i_" is used on all the "initial" routines and the
36  *  prefix "u_" is used for all the "updating" routines.
37  *
38  *  ASSUMPTIONS:
39  *        None of the "i_" routines use any of the termcap capabilities.
40  *        In this way, those routines can be safely used on terminals that
41  *        have minimal (or nonexistent) terminal capabilities.
42  *
43  *        The routines are called in this order:  *_loadave, i_timeofday,
44  *        *_procstates, *_cpustates, *_memory, *_message, *_header,
45  *        *_process, u_endscreen.
46  */
47 
48 #include <sys/types.h>
49 #include <sys/sched.h>
50 #include <curses.h>
51 #include <errno.h>
52 #include <stdio.h>
53 #include <ctype.h>
54 #include <err.h>
55 #include <signal.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 
60 #include "screen.h"		/* interface to screen package */
61 #include "layout.h"		/* defines for screen position layout */
62 #include "display.h"
63 #include "top.h"
64 #include "boolean.h"
65 #include "machine.h"		/* we should eliminate this!!! */
66 #include "utils.h"
67 
68 #ifdef DEBUG
69 FILE           *debug;
70 #endif
71 
72 static int      display_width = MAX_COLS;
73 
74 static char    *cpustates_tag(int);
75 static int      string_count(char **);
76 static void     summary_format(char *, size_t, int *, char **);
77 static int	readlinedumb(char *, int);
78 
79 #define lineindex(l) ((l)*display_width)
80 
81 /* things initialized by display_init and used throughout */
82 
83 /* buffer of proc information lines for display updating */
84 char           *screenbuf = NULL;
85 
86 static char   **procstate_names;
87 static char   **cpustate_names;
88 static char   **memory_names;
89 
90 static int      num_cpustates;
91 
92 static int     *cpustate_columns;
93 static int      cpustate_total_length;
94 
95 /* display ips */
96 int y_mem;
97 int y_message;
98 int y_header;
99 int y_idlecursor;
100 int y_procs;
101 extern int ncpu;
102 extern int combine_cpus;
103 
104 int header_status = Yes;
105 
106 static int
107 empty(void)
108 {
109 	return OK;
110 }
111 
112 static int
113 myfputs(const char *s)
114 {
115 	return fputs(s, stdout);
116 }
117 
118 static int (*addstrp)(const char *);
119 static int (*printwp)(const char *, ...);
120 static int (*standoutp)(void);
121 static int (*standendp)(void);
122 
123 int
124 display_resize(void)
125 {
126 	int display_lines;
127 	int cpu_lines = (combine_cpus ? 1 : ncpu);
128 
129 	y_mem = 2 + cpu_lines;
130 	y_header = 4 + cpu_lines;
131 	y_procs = 5 + cpu_lines;
132 
133 	/* calculate the current dimensions */
134 	/* if operating in "dumb" mode, we only need one line */
135 	display_lines = smart_terminal ? screen_length - y_procs : 1;
136 
137 	y_idlecursor = y_message = 3 + (combine_cpus ? 1 : ncpu);
138 	if (screen_length <= y_message)
139 		y_idlecursor = y_message = screen_length - 1;
140 
141 	/*
142 	 * we don't want more than MAX_COLS columns, since the
143 	 * machine-dependent modules make static allocations based on
144 	 * MAX_COLS and we don't want to run off the end of their buffers
145 	 */
146 	display_width = screen_width;
147 	if (display_width >= MAX_COLS)
148 		display_width = MAX_COLS - 1;
149 
150 	if (display_lines < 0)
151 		display_lines = 0;
152 
153 	/* return number of lines available */
154 	/* for dumb terminals, pretend like we can show any amount */
155 	return (smart_terminal ? display_lines : Largest);
156 }
157 
158 int
159 display_init(struct statics * statics)
160 {
161 	int display_lines, *ip, i;
162 	char **pp;
163 
164 	if (smart_terminal) {
165 		addstrp = addstr;
166 		printwp = printw;
167 		standoutp = standout;
168 		standendp = standend;
169 	} else {
170 		addstrp = myfputs;
171 		printwp = printf;
172 		standoutp = empty;
173 		standendp = empty;
174 	}
175 
176 	/* call resize to do the dirty work */
177 	display_lines = display_resize();
178 
179 	/* only do the rest if we need to */
180 	/* save pointers and allocate space for names */
181 	procstate_names = statics->procstate_names;
182 
183 	cpustate_names = statics->cpustate_names;
184 	num_cpustates = string_count(cpustate_names);
185 
186 	cpustate_columns = calloc(num_cpustates, sizeof(int));
187 	if (cpustate_columns == NULL)
188 		err(1, NULL);
189 
190 	memory_names = statics->memory_names;
191 
192 	/* calculate starting columns where needed */
193 	cpustate_total_length = 0;
194 	pp = cpustate_names;
195 	ip = cpustate_columns;
196 	while (*pp != NULL) {
197 		if ((i = strlen(*pp++)) > 0) {
198 			*ip++ = cpustate_total_length;
199 			cpustate_total_length += i + 8;
200 		}
201 	}
202 
203 	/* return number of lines available */
204 	return (display_lines);
205 }
206 
207 void
208 i_loadave(pid_t mpid, double *avenrun)
209 {
210 	if (screen_length > 1 || !smart_terminal) {
211 		int i;
212 
213 		move(0, 0);
214 		clrtoeol();
215 
216 		addstrp("load averages");
217 		/* mpid == -1 implies this system doesn't have an _mpid */
218 		if (mpid != -1)
219 			printwp("last pid: %5ld;  ", (long) mpid);
220 
221 		for (i = 0; i < 3; i++)
222 			printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]);
223 	}
224 }
225 
226 /*
227  *  Display the current time.
228  *  "ctime" always returns a string that looks like this:
229  *
230  *	Sun Sep 16 01:03:52 1973
231  *      012345678901234567890123
232  *	          1         2
233  *
234  *  We want indices 11 thru 18 (length 8).
235  */
236 
237 void
238 i_timeofday(time_t * tod)
239 {
240 	static char buf[30];
241 
242 	if (buf[0] == '\0')
243 		gethostname(buf, sizeof(buf));
244 
245 	if (screen_length > 1 || !smart_terminal) {
246 		if (smart_terminal) {
247 			move(0, screen_width - 8 - strlen(buf) - 1);
248 		} else {
249 			if (fputs("    ", stdout) == EOF)
250 				exit(1);
251 		}
252 #ifdef DEBUG
253 		{
254 			char *foo;
255 			foo = ctime(tod);
256 			addstrp(foo);
257 		}
258 #endif
259 		printwp("%s %-8.8s", buf, &(ctime(tod)[11]));
260 		putn();
261 	}
262 }
263 
264 /*
265  *  *_procstates(total, states, threads) - print the process/thread summary line
266  *
267  *  Assumptions:  cursor is at the beginning of the line on entry
268  */
269 void
270 i_procstates(int total, int *states, int threads)
271 {
272 	if (screen_length > 2 || !smart_terminal) {
273 		char procstates_buffer[MAX_COLS];
274 
275 		move(1, 0);
276 		clrtoeol();
277 		/* write current number of procs and remember the value */
278 		if (threads == Yes)
279 			printwp("%d threads: ", total);
280 		else
281 			printwp("%d processes: ", total);
282 
283 		/* format and print the process state summary */
284 		summary_format(procstates_buffer, sizeof(procstates_buffer),
285 		    states, procstate_names);
286 
287 		addstrp(procstates_buffer);
288 		putn();
289 	}
290 }
291 
292 /*
293  *  *_cpustates(states) - print the cpu state percentages
294  *
295  *  Assumptions:  cursor is on the PREVIOUS line
296  */
297 
298 /* cpustates_tag() calculates the correct tag to use to label the line */
299 
300 static char *
301 cpustates_tag(int cpu)
302 {
303 	if (screen_length > 3 || !smart_terminal) {
304 		static char *tag;
305 		static int cpulen, old_width;
306 		int i;
307 
308 		if (cpulen == 0 && ncpu > 1) {
309 			/* compute length of the cpu string */
310 			for (i = ncpu; i > 0; cpulen++, i /= 10)
311 				continue;
312 		}
313 
314 		if (old_width == screen_width) {
315 			if (ncpu > 1) {
316 				/* just store the cpu number in the tag */
317 				i = tag[3 + cpulen];
318 				snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu);
319 				tag[3 + cpulen] = i;
320 			}
321 		} else {
322 			/*
323 			 * use a long tag if it will fit, otherwise use short one.
324 			 */
325 			free(tag);
326 			if (cpustate_total_length + 10 + cpulen >= screen_width)
327 				i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu);
328 			else
329 				i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu);
330 			if (i == -1)
331 				tag = NULL;
332 			else
333 				old_width = screen_width;
334 		}
335 		return (tag);
336 	} else
337 		return ("\0");
338 }
339 
340 void
341 i_cpustates(int64_t *ostates)
342 {
343 	int i, first, cpu;
344 	double value;
345 	int64_t *states;
346 	char **names, *thisname;
347 
348 	if (combine_cpus) {
349 		static double *values;
350 		if (!values) {
351 			values = calloc(num_cpustates, sizeof(*values));
352 			if (!values)
353 				err(1, NULL);
354 		}
355 		memset(values, 0, num_cpustates * sizeof(*values));
356 		for (cpu = 0; cpu < ncpu; cpu++) {
357 			names = cpustate_names;
358 			states = ostates + (CPUSTATES * cpu);
359 			i = 0;
360 			while ((thisname = *names++) != NULL) {
361 				if (*thisname != '\0') {
362 					/* retrieve the value and remember it */
363 					values[i++] += *states++;
364 				}
365 			}
366 		}
367 		if (screen_length > 2 || !smart_terminal) {
368 			names = cpustate_names;
369 			i = 0;
370 			first = 0;
371 			move(2, 0);
372 			clrtoeol();
373 			addstrp("All CPUs: ");
374 
375 			while ((thisname = *names++) != NULL) {
376 				if (*thisname != '\0') {
377 					value = values[i++] / ncpu;
378 					/* if percentage is >= 1000, print it as 100% */
379 					printwp((value >= 1000 ? "%s%4.0f%% %s" :
380 					    "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
381 					    value / 10., thisname);
382 				}
383 			}
384 			putn();
385 		}
386 		return;
387 	}
388 	for (cpu = 0; cpu < ncpu; cpu++) {
389 		/* now walk thru the names and print the line */
390 		names = cpustate_names;
391 		first = 0;
392 		states = ostates + (CPUSTATES * cpu);
393 
394 		if (screen_length > 2 + cpu || !smart_terminal) {
395 			move(2 + cpu, 0);
396 			clrtoeol();
397 			addstrp(cpustates_tag(cpu));
398 
399 			while ((thisname = *names++) != NULL) {
400 				if (*thisname != '\0') {
401 					/* retrieve the value and remember it */
402 					value = *states++;
403 
404 					/* if percentage is >= 1000, print it as 100% */
405 					printwp((value >= 1000 ? "%s%4.0f%% %s" :
406 					    "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
407 					    value / 10., thisname);
408 				}
409 			}
410 			putn();
411 		}
412 	}
413 }
414 
415 /*
416  *  *_memory(stats) - print "Memory: " followed by the memory summary string
417  */
418 void
419 i_memory(int *stats)
420 {
421 	if (screen_length > y_mem || !smart_terminal) {
422 		char memory_buffer[MAX_COLS];
423 
424 		move(y_mem, 0);
425 		clrtoeol();
426 		addstrp("Memory: ");
427 
428 		/* format and print the memory summary */
429 		summary_format(memory_buffer, sizeof(memory_buffer), stats,
430 		    memory_names);
431 		addstrp(memory_buffer);
432 		putn();
433 	}
434 }
435 
436 /*
437  *  *_message() - print the next pending message line, or erase the one
438  *                that is there.
439  */
440 
441 /*
442  *  i_message is funny because it gets its message asynchronously (with
443  *	respect to screen updates).
444  */
445 
446 static char     next_msg[MAX_COLS + 5];
447 static int      msgon = 0;
448 
449 void
450 i_message(void)
451 {
452 	move(y_message, 0);
453 	if (next_msg[0] != '\0') {
454 		standoutp();
455 		addstrp(next_msg);
456 		standendp();
457 		clrtoeol();
458 		msgon = TRUE;
459 		next_msg[0] = '\0';
460 	} else if (msgon) {
461 		clrtoeol();
462 		msgon = FALSE;
463 	}
464 }
465 
466 /*
467  *  *_header(text) - print the header for the process area
468  */
469 
470 void
471 i_header(char *text)
472 {
473 	if (header_status == Yes && (screen_length > y_header
474               || !smart_terminal)) {
475 		if (!smart_terminal) {
476 			putn();
477 			if (fputs(text, stdout) == EOF)
478 				exit(1);
479 			putn();
480 		} else {
481 			move(y_header, 0);
482 			clrtoeol();
483 			addstrp(text);
484 		}
485 	}
486 }
487 
488 /*
489  *  *_process(line, thisline) - print one process line
490  */
491 
492 void
493 i_process(int line, char *thisline, int hl)
494 {
495 	/* make sure we are on the correct line */
496 	move(y_procs + line, 0);
497 
498 	/* truncate the line to conform to our current screen width */
499 	thisline[display_width] = '\0';
500 
501 	/* write the line out */
502 	if (hl && smart_terminal)
503 		standoutp();
504 	addstrp(thisline);
505 	if (hl && smart_terminal)
506 		standendp();
507 	putn();
508 	clrtoeol();
509 }
510 
511 void
512 u_endscreen(void)
513 {
514 	if (smart_terminal) {
515 		clrtobot();
516 		/* move the cursor to a pleasant place */
517 		move(y_idlecursor, x_idlecursor);
518 	} else {
519 		/*
520 		 * separate this display from the next with some vertical
521 		 * room
522 		 */
523 		if (fputs("\n\n", stdout) == EOF)
524 			exit(1);
525 	}
526 }
527 
528 void
529 display_header(int status)
530 {
531 	header_status = status;
532 }
533 
534 void
535 new_message(int type, const char *msgfmt,...)
536 {
537 	va_list ap;
538 
539 	va_start(ap, msgfmt);
540 	/* first, format the message */
541 	vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);
542 	va_end(ap);
543 
544 	if (next_msg[0] != '\0') {
545 		/* message there already -- can we clear it? */
546 		/* yes -- write it and clear to end */
547 		if ((type & MT_delayed) == 0) {
548 			move(y_message, 0);
549 			if (type & MT_standout)
550 				standoutp();
551 			addstrp(next_msg);
552 			if (type & MT_standout)
553 				standendp();
554 			clrtoeol();
555 			msgon = TRUE;
556 			next_msg[0] = '\0';
557 			if (smart_terminal)
558 				refresh();
559 		}
560 	}
561 }
562 
563 void
564 clear_message(void)
565 {
566 	move(y_message, 0);
567 	clrtoeol();
568 }
569 
570 
571 static int
572 readlinedumb(char *buffer, int size)
573 {
574 	char *ptr = buffer, ch, cnt = 0, maxcnt = 0;
575 	extern volatile sig_atomic_t leaveflag;
576 	ssize_t len;
577 
578 	/* allow room for null terminator */
579 	size -= 1;
580 
581 	/* read loop */
582 	while ((fflush(stdout), (len = read(STDIN_FILENO, ptr, 1)) > 0)) {
583 
584 		if (len == 0 || leaveflag) {
585 			end_screen();
586 			exit(0);
587 		}
588 
589 		/* newline means we are done */
590 		if ((ch = *ptr) == '\n')
591 			break;
592 
593 		/* handle special editing characters */
594 		if (ch == ch_kill) {
595 			/* return null string */
596 			*buffer = '\0';
597 			putr();
598 			return (-1);
599 		} else if (ch == ch_erase) {
600 			/* erase previous character */
601 			if (cnt <= 0) {
602 				/* none to erase! */
603 				if (putchar('\7') == EOF)
604 					exit(1);
605 			} else {
606 				if (fputs("\b \b", stdout) == EOF)
607 					exit(1);
608 				ptr--;
609 				cnt--;
610 			}
611 		}
612 		/* check for character validity and buffer overflow */
613 		else if (cnt == size || !isprint((unsigned char)ch)) {
614 			/* not legal */
615 			if (putchar('\7') == EOF)
616 				exit(1);
617 		} else {
618 			/* echo it and store it in the buffer */
619 			if (putchar(ch) == EOF)
620 				exit(1);
621 			ptr++;
622 			cnt++;
623 			if (cnt > maxcnt)
624 				maxcnt = cnt;
625 		}
626 	}
627 
628 	/* all done -- null terminate the string */
629 	*ptr = '\0';
630 
631 	/* return either inputted number or string length */
632 	putr();
633 	return (cnt == 0 ? -1 : cnt);
634 }
635 
636 int
637 readline(char *buffer, int size)
638 {
639 	size_t cnt;
640 
641 	/* allow room for null terminator */
642 	size -= 1;
643 
644 	if (smart_terminal) {
645 		int y, x;
646 		getyx(stdscr, y, x);
647 		while (getnstr(buffer, size) == KEY_RESIZE)
648 			move(y, x);
649 	} else
650 		return readlinedumb(buffer, size);
651 
652 	cnt = strlen(buffer);
653 	if (cnt > 0 && buffer[cnt - 1] == '\n')
654 		buffer[cnt - 1] = '\0';
655 	return (cnt == 0 ? -1 : cnt);
656 }
657 
658 /* internal support routines */
659 static int
660 string_count(char **pp)
661 {
662 	int cnt;
663 
664 	cnt = 0;
665 	while (*pp++ != NULL)
666 		cnt++;
667 	return (cnt);
668 }
669 
670 #define	COPYLEFT(to, from)				\
671 	do {						\
672 		len = strlcpy((to), (from), left);	\
673 		if (len >= left)			\
674 			return;				\
675 		p += len;				\
676 		left -= len;				\
677 	} while (0)
678 
679 static void
680 summary_format(char *buf, size_t left, int *numbers, char **names)
681 {
682 	char *p, *thisname;
683 	size_t len;
684 	int num;
685 
686 	/* format each number followed by its string */
687 	p = buf;
688 	while ((thisname = *names++) != NULL) {
689 		/* get the number to format */
690 		num = *numbers++;
691 
692 		if (num >= 0) {
693 			/* is this number in kilobytes? */
694 			if (thisname[0] == 'K') {
695 				/* yes: format it as a memory value */
696 				COPYLEFT(p, format_k(num));
697 
698 				/*
699 				 * skip over the K, since it was included by
700 				 * format_k
701 				 */
702 				COPYLEFT(p, thisname + 1);
703 			} else if (num > 0) {
704 				len = snprintf(p, left, "%d%s", num, thisname);
705 				if (len == (size_t)-1 || len >= left)
706 					return;
707 				p += len;
708 				left -= len;
709 			}
710 		} else {
711 			/*
712 			 * Ignore negative numbers, but display corresponding
713 			 * string.
714 			 */
715 			COPYLEFT(p, thisname);
716 		}
717 	}
718 
719 	/* if the last two characters in the string are ", ", delete them */
720 	p -= 2;
721 	if (p >= buf && p[0] == ',' && p[1] == ' ')
722 		*p = '\0';
723 }
724 
725 /*
726  *  printable(str) - make the string pointed to by "str" into one that is
727  *	printable (i.e.: all ascii), by converting all non-printable
728  *	characters into '?'.  Replacements are done in place and a pointer
729  *	to the original buffer is returned.
730  */
731 char *
732 printable(char *str)
733 {
734 	char *ptr, ch;
735 
736 	ptr = str;
737 	while ((ch = *ptr) != '\0') {
738 		if (!isprint((unsigned char)ch))
739 			*ptr = '?';
740 		ptr++;
741 	}
742 	return (str);
743 }
744 
745 
746 /*
747  *  show_help() - display the help screen; invoked in response to
748  *		either 'h' or '?'.
749  */
750 void
751 show_help(void)
752 {
753 	if (smart_terminal) {
754 		clear();
755 		nl();
756 	}
757 	printwp("These single-character commands are available:\n"
758 	    "\n"
759 	    "^L           - redraw screen\n"
760 	    "<space>      - update screen\n"
761 	    "+            - reset any g, p, or u filters\n"
762 	    "1            - display CPU statistics on a single line\n"
763 	    "C            - toggle the display of command line arguments\n"
764 	    "d count      - show `count' displays, then exit\n"
765 	    "e            - list errors generated by last \"kill\" or \"renice\" command\n"
766 	    "g string     - filter on command name (g+ selects all commands)\n"
767 	    "h | ?        - help; show this text\n"
768 	    "H            - toggle the display of threads\n"
769 	    "I | i        - toggle the display of idle processes\n"
770 	    "k [-sig] pid - send signal `-sig' to process `pid'\n"
771 	    "n|# count    - show `count' processes\n"
772 	    "o field      - specify sort order (size, res, cpu, time, pri, pid, command)\n"
773 	    "P pid        - highlight process `pid' (P+ switches highlighting off)\n"
774 	    "p pid        - display process by `pid' (p+ selects all processes)\n"
775 	    "q            - quit\n"
776 	    "r count pid  - renice process `pid' to nice value `count'\n"
777 	    "S            - toggle the display of system processes\n"
778 	    "s time       - change delay between displays to `time' seconds\n"
779 	    "u [-]user    - show processes for `user' (u+ shows all, u -user hides user)\n"
780 	    "\n");
781 
782 	if (smart_terminal) {
783 		nonl();
784 		refresh();
785 	}
786 }
787 
788 /*
789  *  show_errors() - display on stdout the current log of errors.
790  */
791 void
792 show_errors(void)
793 {
794 	struct errs *errp = errs;
795 	int cnt = 0;
796 
797 	if (smart_terminal) {
798 		clear();
799 		nl();
800 	}
801 	printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
802 	while (cnt++ < errcnt) {
803 		printwp("%5s: %s\n", errp->arg,
804 		    errp->err == 0 ? "Not a number" : strerror(errp->err));
805 		errp++;
806 	}
807 	printwp("\n");
808 	if (smart_terminal) {
809 		nonl();
810 		refresh();
811 	}
812 }
813 
814 void
815 anykey(void)
816 {
817 	int ch;
818 	ssize_t len;
819 
820 	standoutp();
821 	addstrp("Hit any key to continue: ");
822 	standendp();
823 	if (smart_terminal)
824 		refresh();
825 	else
826 		fflush(stdout);
827 	while (1) {
828 		len = read(STDIN_FILENO, &ch, 1);
829 		if (len == -1 && errno == EINTR)
830 			continue;
831 		if (len == 0)
832 			exit(1);
833 		break;
834 	}
835 }
836