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