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