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