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