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