1 /* $OpenBSD: display.c,v 1.2 1997/08/22 07:16:27 downsj Exp $ */ 2 3 /* 4 * Top users/processes display for Unix 5 * Version 3 6 * 7 * This program may be freely redistributed, 8 * but this entire comment MUST remain intact. 9 * 10 * Copyright (c) 1984, 1989, William LeFebvre, Rice University 11 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University 12 */ 13 14 /* 15 * This file contains the routines that display information on the screen. 16 * Each section of the screen has two routines: one for initially writing 17 * all constant and dynamic text, and one for only updating the text that 18 * changes. The prefix "i_" is used on all the "initial" routines and the 19 * prefix "u_" is used for all the "updating" routines. 20 * 21 * ASSUMPTIONS: 22 * None of the "i_" routines use any of the termcap capabilities. 23 * In this way, those routines can be safely used on terminals that 24 * have minimal (or nonexistant) terminal capabilities. 25 * 26 * The routines are called in this order: *_loadave, i_timeofday, 27 * *_procstates, *_cpustates, *_memory, *_message, *_header, 28 * *_process, u_endscreen. 29 */ 30 31 #include <sys/types.h> 32 #include <stdio.h> 33 #include <ctype.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <term.h> 37 #include <time.h> 38 #include <unistd.h> 39 40 #include "screen.h" /* interface to screen package */ 41 #include "layout.h" /* defines for screen position layout */ 42 #include "display.h" 43 #include "top.h" 44 #include "top.local.h" 45 #include "boolean.h" 46 #include "machine.h" /* we should eliminate this!!! */ 47 #include "utils.h" 48 49 #ifdef DEBUG 50 FILE *debug; 51 #endif 52 53 static int lmpid = 0; 54 static int last_hi = 0; /* used in u_process and u_endscreen */ 55 static int lastline = 0; 56 static int display_width = MAX_COLS; 57 58 static char *cpustates_tag __P((void)); 59 static int string_count __P((char **)); 60 static void summary_format __P((char *, int *, char **)); 61 static void line_update __P((char *, char *, int, int)); 62 63 #define lineindex(l) ((l)*display_width) 64 65 /* things initialized by display_init and used thruout */ 66 67 /* buffer of proc information lines for display updating */ 68 char *screenbuf = NULL; 69 70 static char **procstate_names; 71 static char **cpustate_names; 72 static char **memory_names; 73 74 static int num_procstates; 75 static int num_cpustates; 76 static int num_memory; 77 78 static int *lprocstates; 79 static int *lcpustates; 80 static int *lmemory; 81 82 static int *cpustate_columns; 83 static int cpustate_total_length; 84 85 static enum { OFF, ON, ERASE } header_status = ON; 86 87 static int string_count(); 88 static void summary_format(); 89 static void line_update(); 90 91 int display_resize() 92 93 { 94 register int display_lines; 95 96 /* first, deallocate any previous buffer that may have been there */ 97 if (screenbuf != NULL) 98 { 99 free(screenbuf); 100 } 101 102 /* calculate the current dimensions */ 103 /* if operating in "dumb" mode, we only need one line */ 104 display_lines = smart_terminal ? screen_length - Header_lines : 1; 105 106 /* we don't want more than MAX_COLS columns, since the machine-dependent 107 modules make static allocations based on MAX_COLS and we don't want 108 to run off the end of their buffers */ 109 display_width = screen_width; 110 if (display_width >= MAX_COLS) 111 { 112 display_width = MAX_COLS - 1; 113 } 114 115 /* now, allocate space for the screen buffer */ 116 screenbuf = (char *)malloc(display_lines * display_width); 117 if (screenbuf == (char *)NULL) 118 { 119 /* oops! */ 120 return(-1); 121 } 122 123 /* return number of lines available */ 124 /* for dumb terminals, pretend like we can show any amount */ 125 return(smart_terminal ? display_lines : Largest); 126 } 127 128 int display_init(statics) 129 130 struct statics *statics; 131 132 { 133 register int display_lines; 134 register char **pp; 135 register int *ip; 136 register int i; 137 138 /* call resize to do the dirty work */ 139 display_lines = display_resize(); 140 141 /* only do the rest if we need to */ 142 if (display_lines > -1) 143 { 144 /* save pointers and allocate space for names */ 145 procstate_names = statics->procstate_names; 146 num_procstates = string_count(procstate_names); 147 lprocstates = (int *)malloc(num_procstates * sizeof(int)); 148 149 cpustate_names = statics->cpustate_names; 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(display_lines); 174 } 175 176 void i_loadave(mpid, avenrun) 177 178 int mpid; 179 double *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 204 void u_loadave(mpid, avenrun) 205 206 int mpid; 207 double *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 243 void i_timeofday(tod) 244 245 time_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 278 static int ltotal = 0; 279 static 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 288 void i_procstates(total, brkdn) 289 290 int total; 291 int *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 315 void u_procstates(total, brkdn) 316 317 int total; 318 int *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 369 static int cpustates_column; 370 371 /* cpustates_tag() calculates the correct tag to use to label the line */ 372 373 static char *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 397 void i_cpustates(states) 398 399 register 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 431 void u_cpustates(states) 432 433 register 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 478 void z_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 514 static char memory_buffer[MAX_COLS]; 515 516 void i_memory(stats) 517 518 int *stats; 519 520 { 521 fputs("\nMemory: ", 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 529 void u_memory(stats) 530 531 int *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 * *_message() - print the next pending message line, or erase the one 543 * that is there. 544 * 545 * Note that u_message is (currently) the same as i_message. 546 * 547 * Assumptions: lastline is consistent 548 */ 549 550 /* 551 * i_message is funny because it gets its message asynchronously (with 552 * respect to screen updates). 553 */ 554 555 static char next_msg[MAX_COLS + 5]; 556 static int msglen = 0; 557 /* Invariant: msglen is always the length of the message currently displayed 558 on the screen (even when next_msg doesn't contain that message). */ 559 560 void i_message() 561 562 { 563 while (lastline < y_message) 564 { 565 fputc('\n', stdout); 566 lastline++; 567 } 568 if (next_msg[0] != '\0') 569 { 570 standout(next_msg); 571 msglen = strlen(next_msg); 572 next_msg[0] = '\0'; 573 } 574 else if (msglen > 0) 575 { 576 (void) clear_eol(msglen); 577 msglen = 0; 578 } 579 } 580 581 void u_message() 582 583 { 584 i_message(); 585 } 586 587 static int header_length; 588 589 /* 590 * *_header(text) - print the header for the process area 591 * 592 * Assumptions: cursor is on the previous line and lastline is consistent 593 */ 594 595 void i_header(text) 596 597 char *text; 598 599 { 600 header_length = strlen(text); 601 if (header_status == ON) 602 { 603 putchar('\n'); 604 fputs(text, stdout); 605 lastline++; 606 } 607 else if (header_status == ERASE) 608 { 609 header_status = OFF; 610 } 611 } 612 613 /*ARGSUSED*/ 614 void u_header(text) 615 616 char *text; /* ignored */ 617 618 { 619 if (header_status == ERASE) 620 { 621 putchar('\n'); 622 lastline++; 623 clear_eol(header_length); 624 header_status = OFF; 625 } 626 } 627 628 /* 629 * *_process(line, thisline) - print one process line 630 * 631 * Assumptions: lastline is consistent 632 */ 633 634 void i_process(line, thisline) 635 636 int line; 637 char *thisline; 638 639 { 640 register char *p; 641 register char *base; 642 643 /* make sure we are on the correct line */ 644 while (lastline < y_procs + line) 645 { 646 putchar('\n'); 647 lastline++; 648 } 649 650 /* truncate the line to conform to our current screen width */ 651 thisline[display_width] = '\0'; 652 653 /* write the line out */ 654 fputs(thisline, stdout); 655 656 /* copy it in to our buffer */ 657 base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; 658 p = strecpy(base, thisline); 659 660 /* zero fill the rest of it */ 661 memset(p, 0, display_width - (p - base)); 662 } 663 664 void u_process(linenum, linebuf) 665 666 int linenum; 667 char *linebuf; 668 669 { 670 register char *optr; 671 register int screen_line = linenum + Header_lines; 672 register char *bufferline; 673 674 /* remember a pointer to the current line in the screen buffer */ 675 bufferline = &screenbuf[lineindex(linenum)]; 676 677 /* truncate the line to conform to our current screen width */ 678 linebuf[display_width] = '\0'; 679 680 /* is line higher than we went on the last display? */ 681 if (linenum >= last_hi) 682 { 683 /* yes, just ignore screenbuf and write it out directly */ 684 /* get positioned on the correct line */ 685 if (screen_line - lastline == 1) 686 { 687 putchar('\n'); 688 lastline++; 689 } 690 else 691 { 692 Move_to(0, screen_line); 693 lastline = screen_line; 694 } 695 696 /* now write the line */ 697 fputs(linebuf, stdout); 698 699 /* copy it in to the buffer */ 700 optr = strecpy(bufferline, linebuf); 701 702 /* zero fill the rest of it */ 703 memset(optr, 0, display_width - (optr - bufferline)); 704 } 705 else 706 { 707 line_update(bufferline, linebuf, 0, linenum + Header_lines); 708 } 709 } 710 711 void u_endscreen(hi) 712 713 register int hi; 714 715 { 716 register int screen_line = hi + Header_lines; 717 register int i; 718 719 if (smart_terminal) 720 { 721 if (hi < last_hi) 722 { 723 /* need to blank the remainder of the screen */ 724 /* but only if there is any screen left below this line */ 725 if (lastline + 1 < screen_length) 726 { 727 /* efficiently move to the end of currently displayed info */ 728 if (screen_line - lastline < 5) 729 { 730 while (lastline < screen_line) 731 { 732 putchar('\n'); 733 lastline++; 734 } 735 } 736 else 737 { 738 Move_to(0, screen_line); 739 lastline = screen_line; 740 } 741 742 if (clear_to_end) 743 { 744 /* we can do this the easy way */ 745 putcap(clear_to_end); 746 } 747 else 748 { 749 /* use clear_eol on each line */ 750 i = hi; 751 while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) 752 { 753 putchar('\n'); 754 } 755 } 756 } 757 } 758 last_hi = hi; 759 760 /* move the cursor to a pleasant place */ 761 Move_to(x_idlecursor, y_idlecursor); 762 lastline = y_idlecursor; 763 } 764 else 765 { 766 /* separate this display from the next with some vertical room */ 767 fputs("\n\n", stdout); 768 } 769 } 770 771 void display_header(t) 772 773 int t; 774 775 { 776 if (t) 777 { 778 header_status = ON; 779 } 780 else if (header_status == ON) 781 { 782 header_status = ERASE; 783 } 784 } 785 786 /*VARARGS2*/ 787 void new_message(type, msgfmt, a1, a2, a3) 788 789 int type; 790 char *msgfmt; 791 caddr_t a1, a2, a3; 792 793 { 794 register int i; 795 796 /* first, format the message */ 797 (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); 798 799 if (msglen > 0) 800 { 801 /* message there already -- can we clear it? */ 802 if (!overstrike) 803 { 804 /* yes -- write it and clear to end */ 805 i = strlen(next_msg); 806 if ((type & MT_delayed) == 0) 807 { 808 type & MT_standout ? standout(next_msg) : 809 fputs(next_msg, stdout); 810 (void) clear_eol(msglen - i); 811 msglen = i; 812 next_msg[0] = '\0'; 813 } 814 } 815 } 816 else 817 { 818 if ((type & MT_delayed) == 0) 819 { 820 type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); 821 msglen = strlen(next_msg); 822 next_msg[0] = '\0'; 823 } 824 } 825 } 826 827 void clear_message() 828 829 { 830 if (clear_eol(msglen) == 1) 831 { 832 putchar('\r'); 833 } 834 } 835 836 int readline(buffer, size, numeric) 837 838 char *buffer; 839 int size; 840 int numeric; 841 842 { 843 register char *ptr = buffer; 844 register char ch; 845 register char cnt = 0; 846 register char maxcnt = 0; 847 848 /* allow room for null terminator */ 849 size -= 1; 850 851 /* read loop */ 852 while ((fflush(stdout), read(0, ptr, 1) > 0)) 853 { 854 /* newline means we are done */ 855 if ((ch = *ptr) == '\n') 856 { 857 break; 858 } 859 860 /* handle special editing characters */ 861 if (ch == ch_kill) 862 { 863 /* kill line -- account for overstriking */ 864 if (overstrike) 865 { 866 msglen += maxcnt; 867 } 868 869 /* return null string */ 870 *buffer = '\0'; 871 putchar('\r'); 872 return(-1); 873 } 874 else if (ch == ch_erase) 875 { 876 /* erase previous character */ 877 if (cnt <= 0) 878 { 879 /* none to erase! */ 880 putchar('\7'); 881 } 882 else 883 { 884 fputs("\b \b", stdout); 885 ptr--; 886 cnt--; 887 } 888 } 889 /* check for character validity and buffer overflow */ 890 else if (cnt == size || (numeric && !isdigit(ch)) || 891 !isprint(ch)) 892 { 893 /* not legal */ 894 putchar('\7'); 895 } 896 else 897 { 898 /* echo it and store it in the buffer */ 899 putchar(ch); 900 ptr++; 901 cnt++; 902 if (cnt > maxcnt) 903 { 904 maxcnt = cnt; 905 } 906 } 907 } 908 909 /* all done -- null terminate the string */ 910 *ptr = '\0'; 911 912 /* account for the extra characters in the message area */ 913 /* (if terminal overstrikes, remember the furthest they went) */ 914 msglen += overstrike ? maxcnt : cnt; 915 916 /* return either inputted number or string length */ 917 putchar('\r'); 918 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 919 } 920 921 /* internal support routines */ 922 923 static int string_count(pp) 924 925 register char **pp; 926 927 { 928 register int cnt; 929 930 cnt = 0; 931 while (*pp++ != NULL) 932 { 933 cnt++; 934 } 935 return(cnt); 936 } 937 938 static void summary_format(str, numbers, names) 939 940 char *str; 941 int *numbers; 942 register char **names; 943 944 { 945 register char *p; 946 register int num; 947 register char *thisname; 948 949 /* format each number followed by its string */ 950 p = str; 951 while ((thisname = *names++) != NULL) 952 { 953 /* get the number to format */ 954 num = *numbers++; 955 956 /* display only non-zero numbers */ 957 if (num > 0) 958 { 959 /* is this number in kilobytes? */ 960 if (thisname[0] == 'K') 961 { 962 /* yes: format it as a memory value */ 963 p = strecpy(p, format_k(num)); 964 965 /* skip over the K, since it was included by format_k */ 966 p = strecpy(p, thisname+1); 967 } 968 else 969 { 970 p = strecpy(p, itoa(num)); 971 p = strecpy(p, thisname); 972 } 973 } 974 975 /* ignore negative numbers, but display corresponding string */ 976 else if (num < 0) 977 { 978 p = strecpy(p, thisname); 979 } 980 } 981 982 /* if the last two characters in the string are ", ", delete them */ 983 p -= 2; 984 if (p >= str && p[0] == ',' && p[1] == ' ') 985 { 986 *p = '\0'; 987 } 988 } 989 990 static void line_update(old, new, start, line) 991 992 register char *old; 993 register char *new; 994 int start; 995 int line; 996 997 { 998 register int ch; 999 register int diff; 1000 register int newcol = start + 1; 1001 register int lastcol = start; 1002 char cursor_on_line = No; 1003 char *current; 1004 1005 /* compare the two strings and only rewrite what has changed */ 1006 current = old; 1007 #ifdef DEBUG 1008 fprintf(debug, "line_update, starting at %d\n", start); 1009 fputs(old, debug); 1010 fputc('\n', debug); 1011 fputs(new, debug); 1012 fputs("\n-\n", debug); 1013 #endif 1014 1015 /* start things off on the right foot */ 1016 /* this is to make sure the invariants get set up right */ 1017 if ((ch = *new++) != *old) 1018 { 1019 if (line - lastline == 1 && start == 0) 1020 { 1021 putchar('\n'); 1022 } 1023 else 1024 { 1025 Move_to(start, line); 1026 } 1027 cursor_on_line = Yes; 1028 putchar(ch); 1029 *old = ch; 1030 lastcol = 1; 1031 } 1032 old++; 1033 1034 /* 1035 * main loop -- check each character. If the old and new aren't the 1036 * same, then update the display. When the distance from the 1037 * current cursor position to the new change is small enough, 1038 * the characters that belong there are written to move the 1039 * cursor over. 1040 * 1041 * Invariants: 1042 * lastcol is the column where the cursor currently is sitting 1043 * (always one beyond the end of the last mismatch). 1044 */ 1045 do /* yes, a do...while */ 1046 { 1047 if ((ch = *new++) != *old) 1048 { 1049 /* new character is different from old */ 1050 /* make sure the cursor is on top of this character */ 1051 diff = newcol - lastcol; 1052 if (diff > 0) 1053 { 1054 /* some motion is required--figure out which is shorter */ 1055 if (diff < 6 && cursor_on_line) 1056 { 1057 /* overwrite old stuff--get it out of the old buffer */ 1058 printf("%.*s", diff, ¤t[lastcol-start]); 1059 } 1060 else 1061 { 1062 /* use cursor addressing */ 1063 Move_to(newcol, line); 1064 cursor_on_line = Yes; 1065 } 1066 /* remember where the cursor is */ 1067 lastcol = newcol + 1; 1068 } 1069 else 1070 { 1071 /* already there, update position */ 1072 lastcol++; 1073 } 1074 1075 /* write what we need to */ 1076 if (ch == '\0') 1077 { 1078 /* at the end--terminate with a clear-to-end-of-line */ 1079 (void) clear_eol(strlen(old)); 1080 } 1081 else 1082 { 1083 /* write the new character */ 1084 putchar(ch); 1085 } 1086 /* put the new character in the screen buffer */ 1087 *old = ch; 1088 } 1089 1090 /* update working column and screen buffer pointer */ 1091 newcol++; 1092 old++; 1093 1094 } while (ch != '\0'); 1095 1096 /* zero out the rest of the line buffer -- MUST BE DONE! */ 1097 diff = display_width - newcol; 1098 if (diff > 0) 1099 { 1100 memset(old, 0, diff); 1101 } 1102 1103 /* remember where the current line is */ 1104 if (cursor_on_line) 1105 { 1106 lastline = line; 1107 } 1108 } 1109 1110 /* 1111 * printable(str) - make the string pointed to by "str" into one that is 1112 * printable (i.e.: all ascii), by converting all non-printable 1113 * characters into '?'. Replacements are done in place and a pointer 1114 * to the original buffer is returned. 1115 */ 1116 1117 char *printable(str) 1118 1119 char *str; 1120 1121 { 1122 register char *ptr; 1123 register char ch; 1124 1125 ptr = str; 1126 while ((ch = *ptr) != '\0') 1127 { 1128 if (!isprint(ch)) 1129 { 1130 *ptr = '?'; 1131 } 1132 ptr++; 1133 } 1134 return(str); 1135 } 1136