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