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