xref: /openbsd/usr.bin/top/display.c (revision 891d7ab6)
1 /* $OpenBSD: display.c,v 1.40 2010/04/23 09:26:13 otto 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 	/* return number of lines available */
151 	/* for dumb terminals, pretend like we can show any amount */
152 	return (smart_terminal ? display_lines : Largest);
153 }
154 
155 int
156 display_init(struct statics * statics)
157 {
158 	int display_lines, *ip, i;
159 	char **pp;
160 
161 	if (smart_terminal) {
162 		addstrp = addstr;
163 		printwp = (int(*)(const char *, ...))printw;
164 		standoutp = standout;
165 		standendp = standend;
166 	} else {
167 		addstrp = myfputs;
168 		printwp = printf;
169 		standoutp = empty;
170 		standendp = empty;
171 	}
172 
173 	/* call resize to do the dirty work */
174 	display_lines = display_resize();
175 
176 	/* only do the rest if we need to */
177 	/* save pointers and allocate space for names */
178 	procstate_names = statics->procstate_names;
179 
180 	cpustate_names = statics->cpustate_names;
181 	num_cpustates = string_count(cpustate_names);
182 
183 	cpustate_columns = calloc(num_cpustates, sizeof(int));
184 	if (cpustate_columns == NULL)
185 		err(1, NULL);
186 
187 	memory_names = statics->memory_names;
188 
189 	/* calculate starting columns where needed */
190 	cpustate_total_length = 0;
191 	pp = cpustate_names;
192 	ip = cpustate_columns;
193 	while (*pp != NULL) {
194 		if ((i = strlen(*pp++)) > 0) {
195 			*ip++ = cpustate_total_length;
196 			cpustate_total_length += i + 8;
197 		}
198 	}
199 
200 	if (display_lines < 0)
201 		display_lines = 0;
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, brkdn, names) - print the process summary line
266  *
267  *  Assumptions:  cursor is at the beginning of the line on entry
268  */
269 void
270 i_procstates(int total, int *brkdn)
271 {
272 	if (screen_length > 2 || !smart_terminal) {
273 		int i;
274 		char procstates_buffer[MAX_COLS];
275 
276 		move(1, 0);
277 		clrtoeol();
278 		/* write current number of processes and remember the value */
279 		printwp("%d processes:", total);
280 
281 		if (smart_terminal)
282 			move(1, 15);
283 		else {
284 			/* put out enough spaces to get to column 15 */
285 			i = digits(total);
286 			while (i++ < 4) {
287 				if (putchar(' ') == EOF)
288 					exit(1);
289 			}
290 		}
291 
292 		/* format and print the process state summary */
293 		summary_format(procstates_buffer, sizeof(procstates_buffer), brkdn,
294 		    procstate_names);
295 
296 		addstrp(procstates_buffer);
297 		putn();
298 	}
299 }
300 
301 /*
302  *  *_cpustates(states, names) - print the cpu state percentages
303  *
304  *  Assumptions:  cursor is on the PREVIOUS line
305  */
306 
307 /* cpustates_tag() calculates the correct tag to use to label the line */
308 
309 static char *
310 cpustates_tag(int cpu)
311 {
312 	if (screen_length > 3 || !smart_terminal) {
313 		static char *tag;
314 		static int cpulen, old_width;
315 		int i;
316 
317 		if (cpulen == 0 && ncpu > 1) {
318 			/* compute length of the cpu string */
319 			for (i = ncpu; i > 0; cpulen++, i /= 10)
320 				continue;
321 		}
322 
323 		if (old_width == screen_width) {
324 			if (ncpu > 1) {
325 				/* just store the cpu number in the tag */
326 				i = tag[3 + cpulen];
327 				snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu);
328 				tag[3 + cpulen] = i;
329 			}
330 		} else {
331 			/*
332 			 * use a long tag if it will fit, otherwise use short one.
333 			 */
334 			free(tag);
335 			if (cpustate_total_length + 10 + cpulen >= screen_width)
336 				i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu);
337 			else
338 				i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu);
339 			if (i == -1)
340 				tag = NULL;
341 			else
342 				old_width = screen_width;
343 		}
344 		return (tag);
345 	} else
346 		return ("\0");
347 }
348 
349 void
350 i_cpustates(int64_t *ostates)
351 {
352 	int i, first, cpu;
353 	double value;
354 	int64_t *states;
355 	char **names, *thisname;
356 
357 	if (combine_cpus) {
358 		static double *values;
359 		if (!values) {
360 			values = calloc(num_cpustates, sizeof(*values));
361 			if (!values)
362 				err(1, NULL);
363 		}
364 		memset(values, 0, num_cpustates * sizeof(*values));
365 		for (cpu = 0; cpu < ncpu; cpu++) {
366 			names = cpustate_names;
367 			states = ostates + (CPUSTATES * cpu);
368 			i = 0;
369 			while ((thisname = *names++) != NULL) {
370 				if (*thisname != '\0') {
371 					/* retrieve the value and remember it */
372 					values[i++] += *states++;
373 				}
374 			}
375 		}
376 		if (screen_length > 2 || !smart_terminal) {
377 			names = cpustate_names;
378 			i = 0;
379 			first = 0;
380 			move(2, 0);
381 			clrtoeol();
382 			addstrp("All CPUs: ");
383 
384 			while ((thisname = *names++) != NULL) {
385 				if (*thisname != '\0') {
386 					value = values[i++] / ncpu;
387 					/* if percentage is >= 1000, print it as 100% */
388 					printwp((value >= 1000 ? "%s%4.0f%% %s" :
389 					    "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
390 					    value / 10., thisname);
391 				}
392 			}
393 			putn();
394 		}
395 		return;
396 	}
397 	for (cpu = 0; cpu < ncpu; cpu++) {
398 		/* now walk thru the names and print the line */
399 		names = cpustate_names;
400 		first = 0;
401 		states = ostates + (CPUSTATES * cpu);
402 
403 		if (screen_length > 2 + cpu || !smart_terminal) {
404 			move(2 + cpu, 0);
405 			clrtoeol();
406 			addstrp(cpustates_tag(cpu));
407 
408 			while ((thisname = *names++) != NULL) {
409 				if (*thisname != '\0') {
410 					/* retrieve the value and remember it */
411 					value = *states++;
412 
413 					/* if percentage is >= 1000, print it as 100% */
414 					printwp((value >= 1000 ? "%s%4.0f%% %s" :
415 					    "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
416 					    value / 10., thisname);
417 				}
418 			}
419 			putn();
420 		}
421 	}
422 }
423 
424 /*
425  *  *_memory(stats) - print "Memory: " followed by the memory summary string
426  */
427 void
428 i_memory(int *stats)
429 {
430 	if (screen_length > y_mem || !smart_terminal) {
431 		char memory_buffer[MAX_COLS];
432 
433 		move(y_mem, 0);
434 		clrtoeol();
435 		addstrp("Memory: ");
436 
437 		/* format and print the memory summary */
438 		summary_format(memory_buffer, sizeof(memory_buffer), stats,
439 		    memory_names);
440 		addstrp(memory_buffer);
441 		putn();
442 	}
443 }
444 
445 /*
446  *  *_message() - print the next pending message line, or erase the one
447  *                that is there.
448  */
449 
450 /*
451  *  i_message is funny because it gets its message asynchronously (with
452  *	respect to screen updates).
453  */
454 
455 static char     next_msg[MAX_COLS + 5];
456 static int      msgon = 0;
457 
458 void
459 i_message(void)
460 {
461 	move(y_message, 0);
462 	if (next_msg[0] != '\0') {
463 		standoutp();
464 		addstrp(next_msg);
465 		standendp();
466 		clrtoeol();
467 		msgon = TRUE;
468 		next_msg[0] = '\0';
469 	} else if (msgon) {
470 		clrtoeol();
471 		msgon = FALSE;
472 	}
473 }
474 
475 /*
476  *  *_header(text) - print the header for the process area
477  */
478 
479 void
480 i_header(char *text)
481 {
482 	if (header_status == Yes && (screen_length > y_header
483               || !smart_terminal)) {
484 		if (!smart_terminal) {
485 			putn();
486 			if (fputs(text, stdout) == EOF)
487 				exit(1);
488 			putn();
489 		} else {
490 			move(y_header, 0);
491 			clrtoeol();
492 			addstrp(text);
493 		}
494 	}
495 }
496 
497 /*
498  *  *_process(line, thisline) - print one process line
499  */
500 
501 void
502 i_process(int line, char *thisline, int hl)
503 {
504 	/* make sure we are on the correct line */
505 	move(y_procs + line, 0);
506 
507 	/* truncate the line to conform to our current screen width */
508 	thisline[display_width] = '\0';
509 
510 	/* write the line out */
511 	if (hl && smart_terminal)
512 		standoutp();
513 	addstrp(thisline);
514 	if (hl && smart_terminal)
515 		standendp();
516 	putn();
517 	clrtoeol();
518 }
519 
520 void
521 u_endscreen(void)
522 {
523 	if (smart_terminal) {
524 		clrtobot();
525 		/* move the cursor to a pleasant place */
526 		move(y_idlecursor, x_idlecursor);
527 	} else {
528 		/*
529 		 * separate this display from the next with some vertical
530 		 * room
531 		 */
532 		if (fputs("\n\n", stdout) == EOF)
533 			exit(1);
534 	}
535 }
536 
537 void
538 display_header(int status)
539 {
540 	header_status = status;
541 }
542 
543 void
544 new_message(int type, const char *msgfmt,...)
545 {
546 	va_list ap;
547 
548 	va_start(ap, msgfmt);
549 	/* first, format the message */
550 	vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);
551 	va_end(ap);
552 
553 	if (next_msg[0] != '\0') {
554 		/* message there already -- can we clear it? */
555 		/* yes -- write it and clear to end */
556 		if ((type & MT_delayed) == 0) {
557 			move(y_message, 0);
558 			if (type & MT_standout)
559 				standoutp();
560 			addstrp(next_msg);
561 			if (type & MT_standout)
562 				standendp();
563 			clrtoeol();
564 			msgon = TRUE;
565 			next_msg[0] = '\0';
566 			if (smart_terminal)
567 				refresh();
568 		}
569 	}
570 }
571 
572 void
573 clear_message(void)
574 {
575 	move(y_message, 0);
576 	clrtoeol();
577 }
578 
579 
580 static int
581 readlinedumb(char *buffer, int size)
582 {
583 	char *ptr = buffer, ch, cnt = 0, maxcnt = 0;
584 	extern volatile sig_atomic_t leaveflag;
585 	ssize_t len;
586 
587 	/* allow room for null terminator */
588 	size -= 1;
589 
590 	/* read loop */
591 	while ((fflush(stdout), (len = read(STDIN_FILENO, ptr, 1)) > 0)) {
592 
593 		if (len == 0 || leaveflag) {
594 			end_screen();
595 			exit(0);
596 		}
597 
598 		/* newline means we are done */
599 		if ((ch = *ptr) == '\n')
600 			break;
601 
602 		/* handle special editing characters */
603 		if (ch == ch_kill) {
604 			/* return null string */
605 			*buffer = '\0';
606 			putr();
607 			return (-1);
608 		} else if (ch == ch_erase) {
609 			/* erase previous character */
610 			if (cnt <= 0) {
611 				/* none to erase! */
612 				if (putchar('\7') == EOF)
613 					exit(1);
614 			} else {
615 				if (fputs("\b \b", stdout) == EOF)
616 					exit(1);
617 				ptr--;
618 				cnt--;
619 			}
620 		}
621 		/* check for character validity and buffer overflow */
622 		else if (cnt == size || !isprint(ch)) {
623 			/* not legal */
624 			if (putchar('\7') == EOF)
625 				exit(1);
626 		} else {
627 			/* echo it and store it in the buffer */
628 			if (putchar(ch) == EOF)
629 				exit(1);
630 			ptr++;
631 			cnt++;
632 			if (cnt > maxcnt)
633 				maxcnt = cnt;
634 		}
635 	}
636 
637 	/* all done -- null terminate the string */
638 	*ptr = '\0';
639 
640 	/* return either inputted number or string length */
641 	putr();
642 	return (cnt == 0 ? -1 : cnt);
643 }
644 
645 int
646 readline(char *buffer, int size)
647 {
648 	size_t cnt;
649 
650 	/* allow room for null terminator */
651 	size -= 1;
652 
653 	if (smart_terminal)
654 		getnstr(buffer, size);
655 	else
656 		return readlinedumb(buffer, size);
657 
658 	cnt = strlen(buffer);
659 	if (cnt > 0 && buffer[cnt - 1] == '\n')
660 		buffer[cnt - 1] = '\0';
661 	return (cnt == 0 ? -1 : cnt);
662 }
663 
664 /* internal support routines */
665 static int
666 string_count(char **pp)
667 {
668 	int cnt;
669 
670 	cnt = 0;
671 	while (*pp++ != NULL)
672 		cnt++;
673 	return (cnt);
674 }
675 
676 #define	COPYLEFT(to, from)				\
677 	do {						\
678 		len = strlcpy((to), (from), left);	\
679 		if (len >= left)			\
680 			return;				\
681 		p += len;				\
682 		left -= len;				\
683 	} while (0)
684 
685 static void
686 summary_format(char *buf, size_t left, int *numbers, char **names)
687 {
688 	char *p, *thisname;
689 	size_t len;
690 	int num;
691 
692 	/* format each number followed by its string */
693 	p = buf;
694 	while ((thisname = *names++) != NULL) {
695 		/* get the number to format */
696 		num = *numbers++;
697 
698 		if (num >= 0) {
699 			/* is this number in kilobytes? */
700 			if (thisname[0] == 'K') {
701 				/* yes: format it as a memory value */
702 				COPYLEFT(p, format_k(num));
703 
704 				/*
705 				 * skip over the K, since it was included by
706 				 * format_k
707 				 */
708 				COPYLEFT(p, thisname + 1);
709 			} else if (num > 0) {
710 				len = snprintf(p, left, "%d%s", num, thisname);
711 				if (len == (size_t)-1 || len >= left)
712 					return;
713 				p += len;
714 				left -= len;
715 			}
716 		} else {
717 			/*
718 			 * Ignore negative numbers, but display corresponding
719 			 * string.
720 			 */
721 			COPYLEFT(p, thisname);
722 		}
723 	}
724 
725 	/* if the last two characters in the string are ", ", delete them */
726 	p -= 2;
727 	if (p >= buf && p[0] == ',' && p[1] == ' ')
728 		*p = '\0';
729 }
730 
731 /*
732  *  printable(str) - make the string pointed to by "str" into one that is
733  *	printable (i.e.: all ascii), by converting all non-printable
734  *	characters into '?'.  Replacements are done in place and a pointer
735  *	to the original buffer is returned.
736  */
737 char *
738 printable(char *str)
739 {
740 	char *ptr, ch;
741 
742 	ptr = str;
743 	while ((ch = *ptr) != '\0') {
744 		if (!isprint(ch))
745 			*ptr = '?';
746 		ptr++;
747 	}
748 	return (str);
749 }
750 
751 
752 /*
753  *  show_help() - display the help screen; invoked in response to
754  *		either 'h' or '?'.
755  */
756 void
757 show_help(void)
758 {
759 	if (smart_terminal) {
760 		clear();
761 		nl();
762 	}
763 	printwp("These single-character commands are available:\n"
764 	    "\n"
765 	    "^L           - redraw screen\n"
766 	    "<space>      - update screen\n"
767 	    "+            - reset any g, p, or u filters\n"
768 	    "1            - display CPU statistics on a single line\n"
769 	    "C            - toggle the display of command line arguments\n"
770 	    "d count      - show `count' displays, then exit\n"
771 	    "e            - list errors generated by last \"kill\" or \"renice\" command\n"
772 	    "h | ?        - help; show this text\n"
773 	    "g string     - filter on command name (g+ selects all commands)\n"
774 	    "I | i        - toggle the display of idle processes\n"
775 	    "k [-sig] pid - send signal `-sig' to process `pid'\n"
776 	    "n|# count    - show `count' processes\n"
777 	    "o field      - specify sort order (size, res, cpu, time, pri, pid, command)\n"
778 	    "P pid        - highlight process `pid' (P+ switches highlighting off)\n"
779 	    "p pid        - display process by `pid' (p+ selects all processes)\n"
780 	    "q            - quit\n"
781 	    "r count pid  - renice process `pid' to nice value `count'\n"
782 	    "S            - toggle the display of system processes\n"
783 	    "s time       - change delay between displays to `time' seconds\n"
784 	    "T            - toggle the display of threads\n"
785 	    "u user       - display processes for `user' (u+ selects all users)\n"
786 	    "\n");
787 
788 	if (smart_terminal) {
789 		nonl();
790 		refresh();
791 	}
792 }
793 
794 /*
795  *  show_errors() - display on stdout the current log of errors.
796  */
797 void
798 show_errors(void)
799 {
800 	struct errs *errp = errs;
801 	int cnt = 0;
802 
803 	if (smart_terminal) {
804 		clear();
805 		nl();
806 	}
807 	printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
808 	while (cnt++ < errcnt) {
809 		printwp("%5s: %s\n", errp->arg,
810 		    errp->err == 0 ? "Not a number" : strerror(errp->err));
811 		errp++;
812 	}
813 	printwp("\n");
814 	if (smart_terminal) {
815 		nonl();
816 		refresh();
817 	}
818 }
819 
820 void
821 anykey(void)
822 {
823 	int ch;
824 	ssize_t len;
825 
826 	standoutp();
827 	addstrp("Hit any key to continue: ");
828 	standendp();
829 	if (smart_terminal)
830 		refresh();
831 	else
832 		fflush(stdout);
833 	while (1) {
834 		len = read(STDIN_FILENO, &ch, 1);
835 		if (len == -1 && errno == EINTR)
836 			continue;
837 		if (len == 0)
838 			exit(1);
839 		break;
840 	}
841 }
842