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