1 /* $OpenBSD: display.c,v 1.5 2001/07/27 17:13:42 deraadt 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 if (fputs(" ", stdout) == EOF) 266 exit(1); 267 } 268 #ifdef DEBUG 269 { 270 char *foo; 271 foo = ctime(tod); 272 if (fputs(foo, stdout) == EOF) 273 exit(1); 274 } 275 #endif 276 printf("%-8.8s\n", &(ctime(tod)[11])); 277 lastline = 1; 278 } 279 280 static int ltotal = 0; 281 static char procstates_buffer[128]; 282 283 /* 284 * *_procstates(total, brkdn, names) - print the process summary line 285 * 286 * Assumptions: cursor is at the beginning of the line on entry 287 * lastline is valid 288 */ 289 290 void i_procstates(total, brkdn) 291 292 int total; 293 int *brkdn; 294 295 { 296 register int i; 297 298 /* write current number of processes and remember the value */ 299 printf("%d processes:", total); 300 ltotal = total; 301 302 /* put out enough spaces to get to column 15 */ 303 i = digits(total); 304 while (i++ < 4) 305 { 306 if (putchar(' ') == EOF) 307 exit(1); 308 } 309 310 /* format and print the process state summary */ 311 summary_format(procstates_buffer, brkdn, procstate_names); 312 if (fputs(procstates_buffer, stdout) == EOF) 313 exit(1); 314 315 /* save the numbers for next time */ 316 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 317 } 318 319 void u_procstates(total, brkdn) 320 321 int total; 322 int *brkdn; 323 324 { 325 static char new[128]; 326 register int i; 327 328 /* update number of processes only if it has changed */ 329 if (ltotal != total) 330 { 331 /* move and overwrite */ 332 #if (x_procstate == 0) 333 Move_to(x_procstate, y_procstate); 334 #else 335 /* cursor is already there...no motion needed */ 336 /* assert(lastline == 1); */ 337 #endif 338 printf("%d", total); 339 340 /* if number of digits differs, rewrite the label */ 341 if (digits(total) != digits(ltotal)) 342 { 343 if (fputs(" processes:", stdout) == EOF) 344 exit(1); 345 /* put out enough spaces to get to column 15 */ 346 i = digits(total); 347 while (i++ < 4) 348 { 349 if (putchar(' ') == EOF) 350 exit(1); 351 } 352 /* cursor may end up right where we want it!!! */ 353 } 354 355 /* save new total */ 356 ltotal = total; 357 } 358 359 /* see if any of the state numbers has changed */ 360 if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) 361 { 362 /* format and update the line */ 363 summary_format(new, brkdn, procstate_names); 364 line_update(procstates_buffer, new, x_brkdn, y_brkdn); 365 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 366 } 367 } 368 369 /* 370 * *_cpustates(states, names) - print the cpu state percentages 371 * 372 * Assumptions: cursor is on the PREVIOUS line 373 */ 374 375 static int cpustates_column; 376 377 /* cpustates_tag() calculates the correct tag to use to label the line */ 378 379 static char *cpustates_tag() 380 381 { 382 register char *use; 383 384 static char *short_tag = "CPU: "; 385 static char *long_tag = "CPU states: "; 386 387 /* if length + strlen(long_tag) >= screen_width, then we have to 388 use the shorter tag (we subtract 2 to account for ": ") */ 389 if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width) 390 { 391 use = short_tag; 392 } 393 else 394 { 395 use = long_tag; 396 } 397 398 /* set cpustates_column accordingly then return result */ 399 cpustates_column = strlen(use); 400 return(use); 401 } 402 403 void i_cpustates(states) 404 405 register int *states; 406 407 { 408 register int i = 0; 409 register int value; 410 register char **names = cpustate_names; 411 register char *thisname; 412 413 /* print tag and bump lastline */ 414 printf("\n%s", cpustates_tag()); 415 lastline++; 416 417 /* now walk thru the names and print the line */ 418 while ((thisname = *names++) != NULL) 419 { 420 if (*thisname != '\0') 421 { 422 /* retrieve the value and remember it */ 423 value = *states++; 424 425 /* if percentage is >= 1000, print it as 100% */ 426 printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), 427 i++ == 0 ? "" : ", ", 428 ((float)value)/10., 429 thisname); 430 } 431 } 432 433 /* copy over values into "last" array */ 434 memcpy(lcpustates, states, num_cpustates * sizeof(int)); 435 } 436 437 void u_cpustates(states) 438 439 register int *states; 440 441 { 442 register int value; 443 register char **names = cpustate_names; 444 register char *thisname; 445 register int *lp; 446 register int *colp; 447 448 Move_to(cpustates_column, y_cpustates); 449 lastline = y_cpustates; 450 lp = lcpustates; 451 colp = cpustate_columns; 452 453 /* we could be much more optimal about this */ 454 while ((thisname = *names++) != NULL) 455 { 456 if (*thisname != '\0') 457 { 458 /* did the value change since last time? */ 459 if (*lp != *states) 460 { 461 /* yes, move and change */ 462 Move_to(cpustates_column + *colp, y_cpustates); 463 lastline = y_cpustates; 464 465 /* retrieve value and remember it */ 466 value = *states; 467 468 /* if percentage is >= 1000, print it as 100% */ 469 printf((value >= 1000 ? "%4.0f" : "%4.1f"), 470 ((double)value)/10.); 471 472 /* remember it for next time */ 473 *lp = *states; 474 } 475 } 476 477 /* increment and move on */ 478 lp++; 479 states++; 480 colp++; 481 } 482 } 483 484 void z_cpustates() 485 486 { 487 register int i = 0; 488 register char **names = cpustate_names; 489 register char *thisname; 490 register int *lp; 491 492 /* show tag and bump lastline */ 493 printf("\n%s", cpustates_tag()); 494 lastline++; 495 496 while ((thisname = *names++) != NULL) 497 { 498 if (*thisname != '\0') 499 { 500 printf("%s %% %s", i++ == 0 ? "" : ", ", thisname); 501 } 502 } 503 504 /* fill the "last" array with all -1s, to insure correct updating */ 505 lp = lcpustates; 506 i = num_cpustates; 507 while (--i >= 0) 508 { 509 *lp++ = -1; 510 } 511 } 512 513 /* 514 * *_memory(stats) - print "Memory: " followed by the memory summary string 515 * 516 * Assumptions: cursor is on "lastline" 517 * for i_memory ONLY: cursor is on the previous line 518 */ 519 520 static char memory_buffer[MAX_COLS]; 521 522 void i_memory(stats) 523 524 int *stats; 525 526 { 527 if (fputs("\nMemory: ", stdout) == EOF) 528 exit(1); 529 lastline++; 530 531 /* format and print the memory summary */ 532 summary_format(memory_buffer, stats, memory_names); 533 if (fputs(memory_buffer, stdout) == EOF) 534 exit(1); 535 } 536 537 void u_memory(stats) 538 539 int *stats; 540 541 { 542 static char new[MAX_COLS]; 543 544 /* format the new line */ 545 summary_format(new, stats, memory_names); 546 line_update(memory_buffer, new, x_mem, y_mem); 547 } 548 549 /* 550 * *_message() - print the next pending message line, or erase the one 551 * that is there. 552 * 553 * Note that u_message is (currently) the same as i_message. 554 * 555 * Assumptions: lastline is consistent 556 */ 557 558 /* 559 * i_message is funny because it gets its message asynchronously (with 560 * respect to screen updates). 561 */ 562 563 static char next_msg[MAX_COLS + 5]; 564 static int msglen = 0; 565 /* Invariant: msglen is always the length of the message currently displayed 566 on the screen (even when next_msg doesn't contain that message). */ 567 568 void i_message() 569 570 { 571 while (lastline < y_message) 572 { 573 if (fputc('\n', stdout) == EOF) 574 exit(1); 575 lastline++; 576 } 577 if (next_msg[0] != '\0') 578 { 579 standout(next_msg); 580 msglen = strlen(next_msg); 581 next_msg[0] = '\0'; 582 } 583 else if (msglen > 0) 584 { 585 (void) clear_eol(msglen); 586 msglen = 0; 587 } 588 } 589 590 void u_message() 591 592 { 593 i_message(); 594 } 595 596 static int header_length; 597 598 /* 599 * *_header(text) - print the header for the process area 600 * 601 * Assumptions: cursor is on the previous line and lastline is consistent 602 */ 603 604 void i_header(text) 605 606 char *text; 607 608 { 609 header_length = strlen(text); 610 if (header_status == ON) 611 { 612 if (putchar('\n') == EOF) 613 exit(1); 614 if (fputs(text, stdout) == EOF) 615 exit(1); 616 lastline++; 617 } 618 else if (header_status == ERASE) 619 { 620 header_status = OFF; 621 } 622 } 623 624 /*ARGSUSED*/ 625 void u_header(text) 626 627 char *text; /* ignored */ 628 629 { 630 if (header_status == ERASE) 631 { 632 if (putchar('\n') == EOF) 633 exit(1); 634 lastline++; 635 clear_eol(header_length); 636 header_status = OFF; 637 } 638 } 639 640 /* 641 * *_process(line, thisline) - print one process line 642 * 643 * Assumptions: lastline is consistent 644 */ 645 646 void i_process(line, thisline) 647 648 int line; 649 char *thisline; 650 651 { 652 register char *p; 653 register char *base; 654 655 /* make sure we are on the correct line */ 656 while (lastline < y_procs + line) 657 { 658 if (putchar('\n') == EOF) 659 exit(1); 660 lastline++; 661 } 662 663 /* truncate the line to conform to our current screen width */ 664 thisline[display_width] = '\0'; 665 666 /* write the line out */ 667 if (fputs(thisline, stdout) == EOF) 668 exit(1); 669 670 /* copy it in to our buffer */ 671 base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; 672 p = strecpy(base, thisline); 673 674 /* zero fill the rest of it */ 675 memset(p, 0, display_width - (p - base)); 676 } 677 678 void u_process(linenum, linebuf) 679 680 int linenum; 681 char *linebuf; 682 683 { 684 register char *optr; 685 register int screen_line = linenum + Header_lines; 686 register char *bufferline; 687 688 /* remember a pointer to the current line in the screen buffer */ 689 bufferline = &screenbuf[lineindex(linenum)]; 690 691 /* truncate the line to conform to our current screen width */ 692 linebuf[display_width] = '\0'; 693 694 /* is line higher than we went on the last display? */ 695 if (linenum >= last_hi) 696 { 697 /* yes, just ignore screenbuf and write it out directly */ 698 /* get positioned on the correct line */ 699 if (screen_line - lastline == 1) 700 { 701 if (putchar('\n') == EOF) 702 exit(1); 703 lastline++; 704 } 705 else 706 { 707 Move_to(0, screen_line); 708 lastline = screen_line; 709 } 710 711 /* now write the line */ 712 if (fputs(linebuf, stdout) == EOF) 713 exit(1); 714 715 /* copy it in to the buffer */ 716 optr = strecpy(bufferline, linebuf); 717 718 /* zero fill the rest of it */ 719 memset(optr, 0, display_width - (optr - bufferline)); 720 } 721 else 722 { 723 line_update(bufferline, linebuf, 0, linenum + Header_lines); 724 } 725 } 726 727 void u_endscreen(hi) 728 729 register int hi; 730 731 { 732 register int screen_line = hi + Header_lines; 733 register int i; 734 735 if (smart_terminal) 736 { 737 if (hi < last_hi) 738 { 739 /* need to blank the remainder of the screen */ 740 /* but only if there is any screen left below this line */ 741 if (lastline + 1 < screen_length) 742 { 743 /* efficiently move to the end of currently displayed info */ 744 if (screen_line - lastline < 5) 745 { 746 while (lastline < screen_line) 747 { 748 if (putchar('\n') == EOF) 749 exit(1); 750 lastline++; 751 } 752 } 753 else 754 { 755 Move_to(0, screen_line); 756 lastline = screen_line; 757 } 758 759 if (clear_to_end) 760 { 761 /* we can do this the easy way */ 762 putcap(clear_to_end); 763 } 764 else 765 { 766 /* use clear_eol on each line */ 767 i = hi; 768 while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) 769 { 770 if (putchar('\n') == EOF) 771 exit(1); 772 } 773 } 774 } 775 } 776 last_hi = hi; 777 778 /* move the cursor to a pleasant place */ 779 Move_to(x_idlecursor, y_idlecursor); 780 lastline = y_idlecursor; 781 } 782 else 783 { 784 /* separate this display from the next with some vertical room */ 785 if (fputs("\n\n", stdout) == EOF) 786 exit(1); 787 } 788 } 789 790 void display_header(t) 791 792 int t; 793 794 { 795 if (t) 796 { 797 header_status = ON; 798 } 799 else if (header_status == ON) 800 { 801 header_status = ERASE; 802 } 803 } 804 805 /*VARARGS2*/ 806 void new_message(type, msgfmt, a1, a2, a3) 807 808 int type; 809 char *msgfmt; 810 caddr_t a1, a2, a3; 811 812 { 813 register int i; 814 815 /* first, format the message */ 816 (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); 817 818 if (msglen > 0) 819 { 820 /* message there already -- can we clear it? */ 821 if (!overstrike) 822 { 823 /* yes -- write it and clear to end */ 824 i = strlen(next_msg); 825 if ((type & MT_delayed) == 0) 826 { 827 if (type & MT_standout) 828 standout(next_msg); 829 else { 830 if (fputs(next_msg, stdout) == EOF) 831 exit(1); 832 } 833 (void) clear_eol(msglen - i); 834 msglen = i; 835 next_msg[0] = '\0'; 836 } 837 } 838 } 839 else 840 { 841 if ((type & MT_delayed) == 0) 842 { 843 if (type & MT_standout) 844 standout(next_msg); 845 else { 846 if (fputs(next_msg, stdout) == EOF) 847 exit(1); 848 } 849 msglen = strlen(next_msg); 850 next_msg[0] = '\0'; 851 } 852 } 853 } 854 855 void clear_message() 856 857 { 858 if (clear_eol(msglen) == 1) 859 { 860 if (putchar('\r') == EOF) 861 exit(1); 862 } 863 } 864 865 int readline(buffer, size, numeric) 866 867 char *buffer; 868 int size; 869 int numeric; 870 871 { 872 register char *ptr = buffer; 873 register char ch; 874 register char cnt = 0; 875 register char maxcnt = 0; 876 877 /* allow room for null terminator */ 878 size -= 1; 879 880 /* read loop */ 881 while ((fflush(stdout), read(0, ptr, 1) > 0)) 882 { 883 /* newline means we are done */ 884 if ((ch = *ptr) == '\n') 885 { 886 break; 887 } 888 889 /* handle special editing characters */ 890 if (ch == ch_kill) 891 { 892 /* kill line -- account for overstriking */ 893 if (overstrike) 894 { 895 msglen += maxcnt; 896 } 897 898 /* return null string */ 899 *buffer = '\0'; 900 if (putchar('\r') == EOF) 901 exit(1); 902 return(-1); 903 } 904 else if (ch == ch_erase) 905 { 906 /* erase previous character */ 907 if (cnt <= 0) 908 { 909 /* none to erase! */ 910 if (putchar('\7') == EOF) 911 exit(1); 912 } 913 else 914 { 915 if (fputs("\b \b", stdout) == EOF) 916 exit(1); 917 ptr--; 918 cnt--; 919 } 920 } 921 /* check for character validity and buffer overflow */ 922 else if (cnt == size || (numeric && !isdigit(ch)) || 923 !isprint(ch)) 924 { 925 /* not legal */ 926 if (putchar('\7') == EOF) 927 exit(1); 928 } 929 else 930 { 931 /* echo it and store it in the buffer */ 932 if (putchar(ch) == EOF) 933 exit(1); 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 if (putchar('\r') == EOF) 952 exit(1); 953 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 954 } 955 956 /* internal support routines */ 957 958 static int string_count(pp) 959 960 register char **pp; 961 962 { 963 register int cnt; 964 965 cnt = 0; 966 while (*pp++ != NULL) 967 { 968 cnt++; 969 } 970 return(cnt); 971 } 972 973 static void summary_format(str, numbers, names) 974 975 char *str; 976 int *numbers; 977 register char **names; 978 979 { 980 register char *p; 981 register int num; 982 register char *thisname; 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 1025 static void line_update(old, new, start, line) 1026 1027 register char *old; 1028 register char *new; 1029 int start; 1030 int 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 if (putchar('\n') == EOF) 1057 exit(1); 1058 } 1059 else 1060 { 1061 Move_to(start, line); 1062 } 1063 cursor_on_line = Yes; 1064 if (putchar(ch) == EOF) 1065 exit(1); 1066 *old = ch; 1067 lastcol = 1; 1068 } 1069 old++; 1070 1071 /* 1072 * main loop -- check each character. If the old and new aren't the 1073 * same, then update the display. When the distance from the 1074 * current cursor position to the new change is small enough, 1075 * the characters that belong there are written to move the 1076 * cursor over. 1077 * 1078 * Invariants: 1079 * lastcol is the column where the cursor currently is sitting 1080 * (always one beyond the end of the last mismatch). 1081 */ 1082 do /* yes, a do...while */ 1083 { 1084 if ((ch = *new++) != *old) 1085 { 1086 /* new character is different from old */ 1087 /* make sure the cursor is on top of this character */ 1088 diff = newcol - lastcol; 1089 if (diff > 0) 1090 { 1091 /* some motion is required--figure out which is shorter */ 1092 if (diff < 6 && cursor_on_line) 1093 { 1094 /* overwrite old stuff--get it out of the old buffer */ 1095 printf("%.*s", diff, ¤t[lastcol-start]); 1096 } 1097 else 1098 { 1099 /* use cursor addressing */ 1100 Move_to(newcol, line); 1101 cursor_on_line = Yes; 1102 } 1103 /* remember where the cursor is */ 1104 lastcol = newcol + 1; 1105 } 1106 else 1107 { 1108 /* already there, update position */ 1109 lastcol++; 1110 } 1111 1112 /* write what we need to */ 1113 if (ch == '\0') 1114 { 1115 /* at the end--terminate with a clear-to-end-of-line */ 1116 (void) clear_eol(strlen(old)); 1117 } 1118 else 1119 { 1120 /* write the new character */ 1121 if (putchar(ch) == EOF) 1122 exit(1); 1123 } 1124 /* put the new character in the screen buffer */ 1125 *old = ch; 1126 } 1127 1128 /* update working column and screen buffer pointer */ 1129 newcol++; 1130 old++; 1131 1132 } while (ch != '\0'); 1133 1134 /* zero out the rest of the line buffer -- MUST BE DONE! */ 1135 diff = display_width - newcol; 1136 if (diff > 0) 1137 { 1138 memset(old, 0, diff); 1139 } 1140 1141 /* remember where the current line is */ 1142 if (cursor_on_line) 1143 { 1144 lastline = line; 1145 } 1146 } 1147 1148 /* 1149 * printable(str) - make the string pointed to by "str" into one that is 1150 * printable (i.e.: all ascii), by converting all non-printable 1151 * characters into '?'. Replacements are done in place and a pointer 1152 * to the original buffer is returned. 1153 */ 1154 1155 char *printable(str) 1156 1157 char *str; 1158 1159 { 1160 register char *ptr; 1161 register char ch; 1162 1163 ptr = str; 1164 while ((ch = *ptr) != '\0') 1165 { 1166 if (!isprint(ch)) 1167 { 1168 *ptr = '?'; 1169 } 1170 ptr++; 1171 } 1172 return(str); 1173 } 1174