1 /* 2 * Top users/processes display for Unix 3 * Version 3 4 * 5 * This program may be freely redistributed, 6 * but this entire comment MUST remain intact. 7 * 8 * Copyright (c) 1984, 1989, William LeFebvre, Rice University 9 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University 10 * 11 * $FreeBSD$ 12 */ 13 14 /* 15 * This file contains the routines that display information on the screen. 16 * Each section of the screen has two routines: one for initially writing 17 * all constant and dynamic text, and one for only updating the text that 18 * changes. The prefix "i_" is used on all the "initial" routines and the 19 * prefix "u_" is used for all the "updating" routines. 20 * 21 * ASSUMPTIONS: 22 * None of the "i_" routines use any of the termcap capabilities. 23 * In this way, those routines can be safely used on terminals that 24 * have minimal (or nonexistant) terminal capabilities. 25 * 26 * The routines are called in this order: *_loadave, i_timeofday, 27 * *_procstates, *_cpustates, *_memory, *_message, *_header, 28 * *_process, u_endscreen. 29 */ 30 31 #include <sys/cdefs.h> 32 #include <sys/resource.h> 33 #include <sys/time.h> 34 35 #include <assert.h> 36 #include <ctype.h> 37 #include <stdarg.h> 38 #include <stdbool.h> 39 #include <stdlib.h> 40 #include <stdio.h> 41 #include <string.h> 42 #include <termcap.h> 43 #include <time.h> 44 #include <unistd.h> 45 46 #include "screen.h" /* interface to screen package */ 47 #include "layout.h" /* defines for screen position layout */ 48 #include "display.h" 49 #include "top.h" 50 #include "machine.h" /* we should eliminate this!!! */ 51 #include "utils.h" 52 53 #ifdef DEBUG 54 FILE *debug; 55 #endif 56 57 static int lmpid = 0; 58 static int last_hi = 0; /* used in u_process and u_endscreen */ 59 static int lastline = 0; 60 static int display_width = MAX_COLS; 61 62 #define lineindex(l) ((l)*display_width) 63 64 65 /* things initialized by display_init and used thruout */ 66 67 /* buffer of proc information lines for display updating */ 68 static char *screenbuf = NULL; 69 70 static const char * const *procstate_names; 71 static const char * const *cpustate_names; 72 static const char * const *memory_names; 73 static const char * const *arc_names; 74 static const char * const *carc_names; 75 static const char * const *swap_names; 76 77 static int num_procstates; 78 static int num_cpustates; 79 static int num_memory; 80 static int num_swap; 81 82 static int *lprocstates; 83 static int *lcpustates; 84 static int *lmemory; 85 static int *lswap; 86 87 static int num_cpus; 88 static int *cpustate_columns; 89 static int cpustate_total_length; 90 static int cpustates_column; 91 92 static enum { OFF, ON, ERASE } header_status = ON; 93 94 static void summary_format(char *, int *, const char * const *); 95 static void line_update(char *, char *, int, int); 96 97 int x_lastpid = 10; 98 int y_lastpid = 0; 99 int x_loadave = 33; 100 int x_loadave_nompid = 15; 101 int y_loadave = 0; 102 int x_procstate = 0; 103 int y_procstate = 1; 104 int x_brkdn = 15; 105 int y_brkdn = 1; 106 int x_mem = 5; 107 int y_mem = 3; 108 int x_arc = 5; 109 int y_arc = 4; 110 int x_carc = 5; 111 int y_carc = 5; 112 int x_swap = 6; 113 int y_swap = 4; 114 int y_message = 5; 115 int x_header = 0; 116 int y_header = 6; 117 int x_idlecursor = 0; 118 int y_idlecursor = 5; 119 int y_procs = 7; 120 121 int y_cpustates = 2; 122 int Header_lines = 7; 123 124 int 125 display_resize(void) 126 { 127 int lines; 128 129 /* first, deallocate any previous buffer that may have been there */ 130 if (screenbuf != NULL) 131 { 132 free(screenbuf); 133 } 134 135 /* calculate the current dimensions */ 136 /* if operating in "dumb" mode, we only need one line */ 137 lines = smart_terminal ? screen_length - Header_lines : 1; 138 139 if (lines < 0) 140 lines = 0; 141 /* we don't want more than MAX_COLS columns, since the machine-dependent 142 modules make static allocations based on MAX_COLS and we don't want 143 to run off the end of their buffers */ 144 display_width = screen_width; 145 if (display_width >= MAX_COLS) 146 { 147 display_width = MAX_COLS - 1; 148 } 149 150 /* now, allocate space for the screen buffer */ 151 screenbuf = calloc(lines, display_width); 152 if (screenbuf == NULL) 153 { 154 /* oops! */ 155 return(-1); 156 } 157 158 /* return number of lines available */ 159 /* for dumb terminals, pretend like we can show any amount */ 160 return(smart_terminal ? lines : Largest); 161 } 162 163 int display_updatecpus(struct statics *statics) 164 { 165 int lines; 166 int i; 167 168 /* call resize to do the dirty work */ 169 lines = display_resize(); 170 if (pcpu_stats) 171 num_cpus = statics->ncpus; 172 else 173 num_cpus = 1; 174 cpustates_column = 5; /* CPU: */ 175 if (num_cpus > 1) { 176 cpustates_column += 1 + digits(num_cpus); /* CPU #: */ 177 } 178 179 /* fill the "last" array with all -1s, to insure correct updating */ 180 for (i = 0; i < num_cpustates * num_cpus; ++i) { 181 lcpustates[i] = -1; 182 } 183 184 return(lines); 185 } 186 187 int display_init(struct statics * statics) 188 { 189 int lines; 190 char **pp; 191 int *ip; 192 int i; 193 194 lines = display_updatecpus(statics); 195 196 /* only do the rest if we need to */ 197 if (lines > -1) 198 { 199 /* save pointers and allocate space for names */ 200 procstate_names = statics->procstate_names; 201 num_procstates = 8; 202 assert(num_procstates > 0); 203 lprocstates = calloc(num_procstates, sizeof(int)); 204 205 cpustate_names = statics->cpustate_names; 206 207 swap_names = statics->swap_names; 208 num_swap = 7; 209 assert(num_swap > 0); 210 lswap = calloc(num_swap, sizeof(int)); 211 num_cpustates = CPUSTATES; 212 assert(num_cpustates > 0); 213 lcpustates = calloc(num_cpustates * sizeof(int), statics->ncpus); 214 cpustate_columns = calloc(num_cpustates, sizeof(int)); 215 216 memory_names = statics->memory_names; 217 num_memory = 7; 218 assert(num_memory > 0); 219 lmemory = calloc(num_memory, sizeof(int)); 220 221 arc_names = statics->arc_names; 222 carc_names = statics->carc_names; 223 224 /* calculate starting columns where needed */ 225 cpustate_total_length = 0; 226 pp = cpustate_names; 227 ip = cpustate_columns; 228 while (*pp != NULL) 229 { 230 *ip++ = cpustate_total_length; 231 if ((i = strlen(*pp++)) > 0) 232 { 233 cpustate_total_length += i + 8; 234 } 235 } 236 } 237 238 /* return number of lines available */ 239 return(lines); 240 } 241 242 void 243 i_loadave(int mpid, double avenrun[]) 244 { 245 int i; 246 247 /* i_loadave also clears the screen, since it is first */ 248 top_clear(); 249 250 /* mpid == -1 implies this system doesn't have an _mpid */ 251 if (mpid != -1) 252 { 253 printf("last pid: %5d; ", mpid); 254 } 255 256 printf("load averages"); 257 258 for (i = 0; i < 3; i++) 259 { 260 printf("%c %5.2f", 261 i == 0 ? ':' : ',', 262 avenrun[i]); 263 } 264 lmpid = mpid; 265 } 266 267 void 268 u_loadave(int mpid, double *avenrun) 269 { 270 int i; 271 272 if (mpid != -1) 273 { 274 /* change screen only when value has really changed */ 275 if (mpid != lmpid) 276 { 277 Move_to(x_lastpid, y_lastpid); 278 printf("%5d", mpid); 279 lmpid = mpid; 280 } 281 282 /* i remembers x coordinate to move to */ 283 i = x_loadave; 284 } 285 else 286 { 287 i = x_loadave_nompid; 288 } 289 290 /* move into position for load averages */ 291 Move_to(i, y_loadave); 292 293 /* display new load averages */ 294 /* we should optimize this and only display changes */ 295 for (i = 0; i < 3; i++) 296 { 297 printf("%s%5.2f", 298 i == 0 ? "" : ", ", 299 avenrun[i]); 300 } 301 } 302 303 void 304 i_timeofday(time_t *tod) 305 { 306 /* 307 * Display the current time. 308 * "ctime" always returns a string that looks like this: 309 * 310 * Sun Sep 16 01:03:52 1973 311 * 012345678901234567890123 312 * 1 2 313 * 314 * We want indices 11 thru 18 (length 8). 315 */ 316 317 if (smart_terminal) 318 { 319 Move_to(screen_width - 8, 0); 320 } 321 else 322 { 323 fputs(" ", stdout); 324 } 325 #ifdef DEBUG 326 { 327 char *foo; 328 foo = ctime(tod); 329 fputs(foo, stdout); 330 } 331 #endif 332 printf("%-8.8s\n", &(ctime(tod)[11])); 333 lastline = 1; 334 } 335 336 static int ltotal = 0; 337 static char procstates_buffer[MAX_COLS]; 338 339 /* 340 * *_procstates(total, brkdn, names) - print the process summary line 341 * 342 * Assumptions: cursor is at the beginning of the line on entry 343 * lastline is valid 344 */ 345 346 void 347 i_procstates(int total, int *brkdn) 348 { 349 int i; 350 351 /* write current number of processes and remember the value */ 352 printf("%d %s:", total, (ps.thread) ? "threads" :"processes"); 353 ltotal = total; 354 355 /* put out enough spaces to get to column 15 */ 356 i = digits(total); 357 while (i++ < 4) 358 { 359 putchar(' '); 360 } 361 362 /* format and print the process state summary */ 363 summary_format(procstates_buffer, brkdn, procstate_names); 364 fputs(procstates_buffer, stdout); 365 366 /* save the numbers for next time */ 367 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 368 } 369 370 void 371 u_procstates(int total, int *brkdn) 372 { 373 static char new[MAX_COLS]; 374 int i; 375 376 /* update number of processes only if it has changed */ 377 if (ltotal != total) 378 { 379 /* move and overwrite */ 380 #if (x_procstate == 0) 381 Move_to(x_procstate, y_procstate); 382 #else 383 /* cursor is already there...no motion needed */ 384 /* assert(lastline == 1); */ 385 #endif 386 printf("%d", total); 387 388 /* if number of digits differs, rewrite the label */ 389 if (digits(total) != digits(ltotal)) 390 { 391 fputs(" processes:", stdout); 392 /* put out enough spaces to get to column 15 */ 393 i = digits(total); 394 while (i++ < 4) 395 { 396 putchar(' '); 397 } 398 /* cursor may end up right where we want it!!! */ 399 } 400 401 /* save new total */ 402 ltotal = total; 403 } 404 405 /* see if any of the state numbers has changed */ 406 if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) 407 { 408 /* format and update the line */ 409 summary_format(new, brkdn, procstate_names); 410 line_update(procstates_buffer, new, x_brkdn, y_brkdn); 411 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 412 } 413 } 414 415 void 416 i_cpustates(int *states) 417 { 418 int i = 0; 419 int value; 420 const char * const *names; 421 const char *thisname; 422 int cpu; 423 424 for (cpu = 0; cpu < num_cpus; cpu++) { 425 names = cpustate_names; 426 427 /* print tag and bump lastline */ 428 if (num_cpus == 1) 429 printf("\nCPU: "); 430 else { 431 value = printf("\nCPU %d: ", cpu); 432 while (value++ <= cpustates_column) 433 printf(" "); 434 } 435 lastline++; 436 437 /* now walk thru the names and print the line */ 438 while ((thisname = *names++) != NULL) 439 { 440 if (*thisname != '\0') 441 { 442 /* retrieve the value and remember it */ 443 value = *states++; 444 445 /* if percentage is >= 1000, print it as 100% */ 446 printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), 447 (i++ % num_cpustates) == 0 ? "" : ", ", 448 ((float)value)/10., 449 thisname); 450 } 451 } 452 } 453 454 /* copy over values into "last" array */ 455 memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus); 456 } 457 458 void 459 u_cpustates(int *states) 460 { 461 int value; 462 const char * const *names; 463 const char *thisname; 464 int *lp; 465 int *colp; 466 int cpu; 467 468 for (cpu = 0; cpu < num_cpus; cpu++) { 469 names = cpustate_names; 470 471 Move_to(cpustates_column, y_cpustates + cpu); 472 lastline = y_cpustates + cpu; 473 lp = lcpustates + (cpu * num_cpustates); 474 colp = cpustate_columns; 475 476 /* we could be much more optimal about this */ 477 while ((thisname = *names++) != NULL) 478 { 479 if (*thisname != '\0') 480 { 481 /* did the value change since last time? */ 482 if (*lp != *states) 483 { 484 /* yes, move and change */ 485 Move_to(cpustates_column + *colp, y_cpustates + cpu); 486 lastline = y_cpustates + cpu; 487 488 /* retrieve value and remember it */ 489 value = *states; 490 491 /* if percentage is >= 1000, print it as 100% */ 492 printf((value >= 1000 ? "%4.0f" : "%4.1f"), 493 ((double)value)/10.); 494 495 /* remember it for next time */ 496 *lp = value; 497 } 498 } 499 500 /* increment and move on */ 501 lp++; 502 states++; 503 colp++; 504 } 505 } 506 } 507 508 void 509 z_cpustates(void) 510 { 511 int i = 0; 512 const char **names; 513 char *thisname; 514 int cpu, value; 515 516 for (cpu = 0; cpu < num_cpus; cpu++) { 517 names = cpustate_names; 518 519 /* show tag and bump lastline */ 520 if (num_cpus == 1) 521 printf("\nCPU: "); 522 else { 523 value = printf("\nCPU %d: ", cpu); 524 while (value++ <= cpustates_column) 525 printf(" "); 526 } 527 lastline++; 528 529 while ((thisname = *names++) != NULL) 530 { 531 if (*thisname != '\0') 532 { 533 printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname); 534 } 535 } 536 } 537 538 /* fill the "last" array with all -1s, to insure correct updating */ 539 for (i = 0; i < num_cpustates * num_cpus; ++i) { 540 lcpustates[i] = -1; 541 } 542 } 543 544 /* 545 * *_memory(stats) - print "Memory: " followed by the memory summary string 546 * 547 * Assumptions: cursor is on "lastline" 548 * for i_memory ONLY: cursor is on the previous line 549 */ 550 551 static char memory_buffer[MAX_COLS]; 552 553 void 554 i_memory(int *stats) 555 { 556 fputs("\nMem: ", stdout); 557 lastline++; 558 559 /* format and print the memory summary */ 560 summary_format(memory_buffer, stats, memory_names); 561 fputs(memory_buffer, stdout); 562 } 563 564 void 565 u_memory(int *stats) 566 { 567 static char new[MAX_COLS]; 568 569 /* format the new line */ 570 summary_format(new, stats, memory_names); 571 line_update(memory_buffer, new, x_mem, y_mem); 572 } 573 574 /* 575 * *_arc(stats) - print "ARC: " followed by the ARC summary string 576 * 577 * Assumptions: cursor is on "lastline" 578 * for i_arc ONLY: cursor is on the previous line 579 */ 580 static char arc_buffer[MAX_COLS]; 581 582 void 583 i_arc(int *stats) 584 { 585 if (arc_names == NULL) 586 return; 587 588 fputs("\nARC: ", stdout); 589 lastline++; 590 591 /* format and print the memory summary */ 592 summary_format(arc_buffer, stats, arc_names); 593 fputs(arc_buffer, stdout); 594 } 595 596 void 597 u_arc(int *stats) 598 { 599 static char new[MAX_COLS]; 600 601 if (arc_names == NULL) 602 return; 603 604 /* format the new line */ 605 summary_format(new, stats, arc_names); 606 line_update(arc_buffer, new, x_arc, y_arc); 607 } 608 609 610 /* 611 * *_carc(stats) - print "Compressed ARC: " followed by the summary string 612 * 613 * Assumptions: cursor is on "lastline" 614 * for i_carc ONLY: cursor is on the previous line 615 */ 616 static char carc_buffer[MAX_COLS]; 617 618 void 619 i_carc(int *stats) 620 { 621 if (carc_names == NULL) 622 return; 623 624 fputs("\n ", stdout); 625 lastline++; 626 627 /* format and print the memory summary */ 628 summary_format(carc_buffer, stats, carc_names); 629 fputs(carc_buffer, stdout); 630 } 631 632 void 633 u_carc(int *stats) 634 { 635 static char new[MAX_COLS]; 636 637 if (carc_names == NULL) 638 return; 639 640 /* format the new line */ 641 summary_format(new, stats, carc_names); 642 line_update(carc_buffer, new, x_carc, y_carc); 643 } 644 645 /* 646 * *_swap(stats) - print "Swap: " followed by the swap summary string 647 * 648 * Assumptions: cursor is on "lastline" 649 * for i_swap ONLY: cursor is on the previous line 650 */ 651 652 static char swap_buffer[MAX_COLS]; 653 654 void 655 i_swap(int *stats) 656 { 657 fputs("\nSwap: ", stdout); 658 lastline++; 659 660 /* format and print the swap summary */ 661 summary_format(swap_buffer, stats, swap_names); 662 fputs(swap_buffer, stdout); 663 } 664 665 void 666 u_swap(int *stats) 667 { 668 static char new[MAX_COLS]; 669 670 /* format the new line */ 671 summary_format(new, stats, swap_names); 672 line_update(swap_buffer, new, x_swap, y_swap); 673 } 674 675 /* 676 * *_message() - print the next pending message line, or erase the one 677 * that is there. 678 * 679 * Note that u_message is (currently) the same as i_message. 680 * 681 * Assumptions: lastline is consistent 682 */ 683 684 /* 685 * i_message is funny because it gets its message asynchronously (with 686 * respect to screen updates). 687 */ 688 689 static char next_msg[MAX_COLS + 5]; 690 static int msglen = 0; 691 /* Invariant: msglen is always the length of the message currently displayed 692 on the screen (even when next_msg doesn't contain that message). */ 693 694 void 695 i_message(void) 696 { 697 698 while (lastline < y_message) 699 { 700 fputc('\n', stdout); 701 lastline++; 702 } 703 if (next_msg[0] != '\0') 704 { 705 top_standout(next_msg); 706 msglen = strlen(next_msg); 707 next_msg[0] = '\0'; 708 } 709 else if (msglen > 0) 710 { 711 (void) clear_eol(msglen); 712 msglen = 0; 713 } 714 } 715 716 void 717 u_message(void) 718 { 719 i_message(); 720 } 721 722 static int header_length; 723 724 /* 725 * Trim a header string to the current display width and return a newly 726 * allocated area with the trimmed header. 727 */ 728 729 const char * 730 trim_header(const char *text) 731 { 732 char *s; 733 int width; 734 735 s = NULL; 736 width = display_width; 737 header_length = strlen(text); 738 if (header_length >= width) { 739 s = strndup(text, width); 740 if (s == NULL) 741 return (NULL); 742 } 743 return (s); 744 } 745 746 /* 747 * *_header(text) - print the header for the process area 748 * 749 * Assumptions: cursor is on the previous line and lastline is consistent 750 */ 751 752 void 753 i_header(const char *text) 754 { 755 char *s; 756 757 s = trim_header(text); 758 if (s != NULL) 759 text = s; 760 761 if (header_status == ON) 762 { 763 putchar('\n'); 764 fputs(text, stdout); 765 lastline++; 766 } 767 else if (header_status == ERASE) 768 { 769 header_status = OFF; 770 } 771 free(s); 772 } 773 774 void 775 u_header(const char *text __unused) 776 { 777 778 if (header_status == ERASE) 779 { 780 putchar('\n'); 781 lastline++; 782 clear_eol(header_length); 783 header_status = OFF; 784 } 785 } 786 787 /* 788 * *_process(line, thisline) - print one process line 789 * 790 * Assumptions: lastline is consistent 791 */ 792 793 void 794 i_process(int line, char *thisline) 795 { 796 char *p; 797 char *base; 798 799 /* make sure we are on the correct line */ 800 while (lastline < y_procs + line) 801 { 802 putchar('\n'); 803 lastline++; 804 } 805 806 /* truncate the line to conform to our current screen width */ 807 thisline[display_width] = '\0'; 808 809 /* write the line out */ 810 fputs(thisline, stdout); 811 812 /* copy it in to our buffer */ 813 base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; 814 p = stpcpy(base, thisline); 815 816 /* zero fill the rest of it */ 817 memset(p, 0, display_width - (p - base)); 818 } 819 820 void 821 u_process(int line, char *newline) 822 { 823 char *optr; 824 int screen_line = line + Header_lines; 825 char *bufferline; 826 827 /* remember a pointer to the current line in the screen buffer */ 828 bufferline = &screenbuf[lineindex(line)]; 829 830 /* truncate the line to conform to our current screen width */ 831 newline[display_width] = '\0'; 832 833 /* is line higher than we went on the last display? */ 834 if (line >= last_hi) 835 { 836 /* yes, just ignore screenbuf and write it out directly */ 837 /* get positioned on the correct line */ 838 if (screen_line - lastline == 1) 839 { 840 putchar('\n'); 841 lastline++; 842 } 843 else 844 { 845 Move_to(0, screen_line); 846 lastline = screen_line; 847 } 848 849 /* now write the line */ 850 fputs(newline, stdout); 851 852 /* copy it in to the buffer */ 853 optr = stpcpy(bufferline, newline); 854 855 /* zero fill the rest of it */ 856 memset(optr, 0, display_width - (optr - bufferline)); 857 } 858 else 859 { 860 line_update(bufferline, newline, 0, line + Header_lines); 861 } 862 } 863 864 void 865 u_endscreen(int hi) 866 { 867 int screen_line = hi + Header_lines; 868 int i; 869 870 if (smart_terminal) 871 { 872 if (hi < last_hi) 873 { 874 /* need to blank the remainder of the screen */ 875 /* but only if there is any screen left below this line */ 876 if (lastline + 1 < screen_length) 877 { 878 /* efficiently move to the end of currently displayed info */ 879 if (screen_line - lastline < 5) 880 { 881 while (lastline < screen_line) 882 { 883 putchar('\n'); 884 lastline++; 885 } 886 } 887 else 888 { 889 Move_to(0, screen_line); 890 lastline = screen_line; 891 } 892 893 if (clear_to_end) 894 { 895 /* we can do this the easy way */ 896 putcap(clear_to_end); 897 } 898 else 899 { 900 /* use clear_eol on each line */ 901 i = hi; 902 while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) 903 { 904 putchar('\n'); 905 } 906 } 907 } 908 } 909 last_hi = hi; 910 911 /* move the cursor to a pleasant place */ 912 Move_to(x_idlecursor, y_idlecursor); 913 lastline = y_idlecursor; 914 } 915 else 916 { 917 /* separate this display from the next with some vertical room */ 918 fputs("\n\n", stdout); 919 } 920 } 921 922 void 923 display_header(int t) 924 { 925 926 if (t) 927 { 928 header_status = ON; 929 } 930 else if (header_status == ON) 931 { 932 header_status = ERASE; 933 } 934 } 935 936 void 937 new_message(int type, const char *msgfmt, ...) 938 { 939 va_list args; 940 size_t i; 941 942 va_start(args, msgfmt); 943 944 /* first, format the message */ 945 vsnprintf(next_msg, sizeof(next_msg), msgfmt, args); 946 947 va_end(args); 948 949 if (msglen > 0) 950 { 951 /* message there already -- can we clear it? */ 952 if (!overstrike) 953 { 954 /* yes -- write it and clear to end */ 955 i = strlen(next_msg); 956 if ((type & MT_delayed) == 0) 957 { 958 type & MT_standout ? top_standout(next_msg) : 959 fputs(next_msg, stdout); 960 (void) clear_eol(msglen - i); 961 msglen = i; 962 next_msg[0] = '\0'; 963 } 964 } 965 } 966 else 967 { 968 if ((type & MT_delayed) == 0) 969 { 970 type & MT_standout ? top_standout(next_msg) : fputs(next_msg, stdout); 971 msglen = strlen(next_msg); 972 next_msg[0] = '\0'; 973 } 974 } 975 } 976 977 void 978 clear_message(void) 979 { 980 if (clear_eol(msglen) == 1) 981 { 982 putchar('\r'); 983 } 984 } 985 986 int 987 readline(char *buffer, int size, int numeric) 988 { 989 char *ptr = buffer; 990 char ch; 991 char cnt = 0; 992 char maxcnt = 0; 993 994 /* allow room for null terminator */ 995 size -= 1; 996 997 /* read loop */ 998 while ((fflush(stdout), read(0, ptr, 1) > 0)) 999 { 1000 /* newline means we are done */ 1001 if ((ch = *ptr) == '\n' || ch == '\r') 1002 { 1003 break; 1004 } 1005 1006 /* handle special editing characters */ 1007 if (ch == ch_kill) 1008 { 1009 /* kill line -- account for overstriking */ 1010 if (overstrike) 1011 { 1012 msglen += maxcnt; 1013 } 1014 1015 /* return null string */ 1016 *buffer = '\0'; 1017 putchar('\r'); 1018 return(-1); 1019 } 1020 else if (ch == ch_erase) 1021 { 1022 /* erase previous character */ 1023 if (cnt <= 0) 1024 { 1025 /* none to erase! */ 1026 putchar('\7'); 1027 } 1028 else 1029 { 1030 fputs("\b \b", stdout); 1031 ptr--; 1032 cnt--; 1033 } 1034 } 1035 /* check for character validity and buffer overflow */ 1036 else if (cnt == size || (numeric && !isdigit(ch)) || 1037 !isprint(ch)) 1038 { 1039 /* not legal */ 1040 putchar('\7'); 1041 } 1042 else 1043 { 1044 /* echo it and store it in the buffer */ 1045 putchar(ch); 1046 ptr++; 1047 cnt++; 1048 if (cnt > maxcnt) 1049 { 1050 maxcnt = cnt; 1051 } 1052 } 1053 } 1054 1055 /* all done -- null terminate the string */ 1056 *ptr = '\0'; 1057 1058 /* account for the extra characters in the message area */ 1059 /* (if terminal overstrikes, remember the furthest they went) */ 1060 msglen += overstrike ? maxcnt : cnt; 1061 1062 /* return either inputted number or string length */ 1063 putchar('\r'); 1064 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 1065 } 1066 1067 /* internal support routines */ 1068 1069 static void summary_format(char *str, int *numbers, const char * const *names) 1070 { 1071 char *p; 1072 int num; 1073 const char *thisname; 1074 char rbuf[6]; 1075 1076 /* format each number followed by its string */ 1077 p = str; 1078 while ((thisname = *names++) != NULL) 1079 { 1080 /* get the number to format */ 1081 num = *numbers++; 1082 1083 /* display only non-zero numbers */ 1084 if (num > 0) 1085 { 1086 /* is this number in kilobytes? */ 1087 if (thisname[0] == 'K') 1088 { 1089 /* yes: format it as a memory value */ 1090 p = stpcpy(p, format_k(num)); 1091 1092 /* skip over the K, since it was included by format_k */ 1093 p = stpcpy(p, thisname+1); 1094 } 1095 /* is this number a ratio? */ 1096 else if (thisname[0] == ':') 1097 { 1098 (void) snprintf(rbuf, sizeof(rbuf), "%.2f", 1099 (float)*(numbers - 2) / (float)num); 1100 p = stpcpy(p, rbuf); 1101 p = stpcpy(p, thisname); 1102 } 1103 else 1104 { 1105 p = stpcpy(p, itoa(num)); 1106 p = stpcpy(p, thisname); 1107 } 1108 } 1109 1110 /* ignore negative numbers, but display corresponding string */ 1111 else if (num < 0) 1112 { 1113 p = stpcpy(p, thisname); 1114 } 1115 } 1116 1117 /* if the last two characters in the string are ", ", delete them */ 1118 p -= 2; 1119 if (p >= str && p[0] == ',' && p[1] == ' ') 1120 { 1121 *p = '\0'; 1122 } 1123 } 1124 1125 static void 1126 line_update(char *old, char *new, int start, int line) 1127 { 1128 int ch; 1129 int diff; 1130 int newcol = start + 1; 1131 int lastcol = start; 1132 char cursor_on_line = false; 1133 char *current; 1134 1135 /* compare the two strings and only rewrite what has changed */ 1136 current = old; 1137 #ifdef DEBUG 1138 fprintf(debug, "line_update, starting at %d\n", start); 1139 fputs(old, debug); 1140 fputc('\n', debug); 1141 fputs(new, debug); 1142 fputs("\n-\n", debug); 1143 #endif 1144 1145 /* start things off on the right foot */ 1146 /* this is to make sure the invariants get set up right */ 1147 if ((ch = *new++) != *old) 1148 { 1149 if (line - lastline == 1 && start == 0) 1150 { 1151 putchar('\n'); 1152 } 1153 else 1154 { 1155 Move_to(start, line); 1156 } 1157 cursor_on_line = true; 1158 putchar(ch); 1159 *old = ch; 1160 lastcol = 1; 1161 } 1162 old++; 1163 1164 /* 1165 * main loop -- check each character. If the old and new aren't the 1166 * same, then update the display. When the distance from the 1167 * current cursor position to the new change is small enough, 1168 * the characters that belong there are written to move the 1169 * cursor over. 1170 * 1171 * Invariants: 1172 * lastcol is the column where the cursor currently is sitting 1173 * (always one beyond the end of the last mismatch). 1174 */ 1175 do /* yes, a do...while */ 1176 { 1177 if ((ch = *new++) != *old) 1178 { 1179 /* new character is different from old */ 1180 /* make sure the cursor is on top of this character */ 1181 diff = newcol - lastcol; 1182 if (diff > 0) 1183 { 1184 /* some motion is required--figure out which is shorter */ 1185 if (diff < 6 && cursor_on_line) 1186 { 1187 /* overwrite old stuff--get it out of the old buffer */ 1188 printf("%.*s", diff, ¤t[lastcol-start]); 1189 } 1190 else 1191 { 1192 /* use cursor addressing */ 1193 Move_to(newcol, line); 1194 cursor_on_line = true; 1195 } 1196 /* remember where the cursor is */ 1197 lastcol = newcol + 1; 1198 } 1199 else 1200 { 1201 /* already there, update position */ 1202 lastcol++; 1203 } 1204 1205 /* write what we need to */ 1206 if (ch == '\0') 1207 { 1208 /* at the end--terminate with a clear-to-end-of-line */ 1209 (void) clear_eol(strlen(old)); 1210 } 1211 else 1212 { 1213 /* write the new character */ 1214 putchar(ch); 1215 } 1216 /* put the new character in the screen buffer */ 1217 *old = ch; 1218 } 1219 1220 /* update working column and screen buffer pointer */ 1221 newcol++; 1222 old++; 1223 1224 } while (ch != '\0'); 1225 1226 /* zero out the rest of the line buffer -- MUST BE DONE! */ 1227 diff = display_width - newcol; 1228 if (diff > 0) 1229 { 1230 memset(old, 0, diff); 1231 } 1232 1233 /* remember where the current line is */ 1234 if (cursor_on_line) 1235 { 1236 lastline = line; 1237 } 1238 } 1239 1240 /* 1241 * printable(str) - make the string pointed to by "str" into one that is 1242 * printable (i.e.: all ascii), by converting all non-printable 1243 * characters into '?'. Replacements are done in place and a pointer 1244 * to the original buffer is returned. 1245 */ 1246 1247 char * 1248 printable(char str[]) 1249 { 1250 char *ptr; 1251 char ch; 1252 1253 ptr = str; 1254 while ((ch = *ptr) != '\0') 1255 { 1256 if (!isprint(ch)) 1257 { 1258 *ptr = '?'; 1259 } 1260 ptr++; 1261 } 1262 return(str); 1263 } 1264 1265 void 1266 i_uptime(struct timeval *bt, time_t *tod) 1267 { 1268 time_t uptime; 1269 int days, hrs, mins, secs; 1270 1271 if (bt->tv_sec != -1) { 1272 uptime = *tod - bt->tv_sec; 1273 days = uptime / 86400; 1274 uptime %= 86400; 1275 hrs = uptime / 3600; 1276 uptime %= 3600; 1277 mins = uptime / 60; 1278 secs = uptime % 60; 1279 1280 /* 1281 * Display the uptime. 1282 */ 1283 1284 if (smart_terminal) 1285 { 1286 Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); 1287 } 1288 else 1289 { 1290 fputs(" ", stdout); 1291 } 1292 printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); 1293 } 1294 } 1295