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