display.c revision 214857
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 214857 2010-11-06 03:59:21Z delphij $ 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 printf("\nCPU %d: ", cpu); 452 lastline++; 453 454 /* now walk thru the names and print the line */ 455 Move_to(cpustates_column, y_cpustates + cpu); 456 while ((thisname = *names++) != NULL) 457 { 458 if (*thisname != '\0') 459 { 460 /* retrieve the value and remember it */ 461 value = *states++; 462 463 /* if percentage is >= 1000, print it as 100% */ 464 printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), 465 (i++ % num_cpustates) == 0 ? "" : ", ", 466 ((float)value)/10., 467 thisname); 468 } 469 } 470} 471 472 /* copy over values into "last" array */ 473 memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus); 474} 475 476u_cpustates(states) 477 478register int *states; 479 480{ 481 register int value; 482 register char **names; 483 register char *thisname; 484 register int *lp; 485 register int *colp; 486 int cpu; 487 488for (cpu = 0; cpu < num_cpus; cpu++) { 489 names = cpustate_names; 490 491 Move_to(cpustates_column, y_cpustates + cpu); 492 lastline = y_cpustates + cpu; 493 lp = lcpustates + (cpu * num_cpustates); 494 colp = cpustate_columns; 495 496 /* we could be much more optimal about this */ 497 while ((thisname = *names++) != NULL) 498 { 499 if (*thisname != '\0') 500 { 501 /* did the value change since last time? */ 502 if (*lp != *states) 503 { 504 /* yes, move and change */ 505 Move_to(cpustates_column + *colp, y_cpustates + cpu); 506 lastline = y_cpustates + cpu; 507 508 /* retrieve value and remember it */ 509 value = *states; 510 511 /* if percentage is >= 1000, print it as 100% */ 512 printf((value >= 1000 ? "%4.0f" : "%4.1f"), 513 ((double)value)/10.); 514 515 /* remember it for next time */ 516 *lp = value; 517 } 518 } 519 520 /* increment and move on */ 521 lp++; 522 states++; 523 colp++; 524 } 525} 526} 527 528z_cpustates() 529 530{ 531 register int i = 0; 532 register char **names; 533 register char *thisname; 534 register int *lp; 535 int cpu; 536 537for (cpu = 0; cpu < num_cpus; cpu++) { 538 names = cpustate_names; 539 540 /* show tag and bump lastline */ 541 if (num_cpus == 1) 542 printf("\nCPU: "); 543 else 544 printf("\nCPU %d: ", cpu); 545 lastline++; 546 547 Move_to(cpustates_column, y_cpustates + cpu); 548 while ((thisname = *names++) != NULL) 549 { 550 if (*thisname != '\0') 551 { 552 printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname); 553 } 554 } 555} 556 557 /* fill the "last" array with all -1s, to insure correct updating */ 558 lp = lcpustates; 559 i = num_cpustates * num_cpus; 560 while (--i >= 0) 561 { 562 *lp++ = -1; 563 } 564} 565 566/* 567 * *_memory(stats) - print "Memory: " followed by the memory summary string 568 * 569 * Assumptions: cursor is on "lastline" 570 * for i_memory ONLY: cursor is on the previous line 571 */ 572 573char memory_buffer[MAX_COLS]; 574 575i_memory(stats) 576 577int *stats; 578 579{ 580 fputs("\nMem: ", stdout); 581 lastline++; 582 583 /* format and print the memory summary */ 584 summary_format(memory_buffer, stats, memory_names); 585 fputs(memory_buffer, stdout); 586} 587 588u_memory(stats) 589 590int *stats; 591 592{ 593 static char new[MAX_COLS]; 594 595 /* format the new line */ 596 summary_format(new, stats, memory_names); 597 line_update(memory_buffer, new, x_mem, y_mem); 598} 599 600/* 601 * *_swap(stats) - print "Swap: " followed by the swap summary string 602 * 603 * Assumptions: cursor is on "lastline" 604 * for i_swap ONLY: cursor is on the previous line 605 */ 606 607char swap_buffer[MAX_COLS]; 608 609i_swap(stats) 610 611int *stats; 612 613{ 614 fputs("\nSwap: ", stdout); 615 lastline++; 616 617 /* format and print the swap summary */ 618 summary_format(swap_buffer, stats, swap_names); 619 fputs(swap_buffer, stdout); 620} 621 622u_swap(stats) 623 624int *stats; 625 626{ 627 static char new[MAX_COLS]; 628 629 /* format the new line */ 630 summary_format(new, stats, swap_names); 631 line_update(swap_buffer, new, x_swap, y_swap); 632} 633 634/* 635 * *_message() - print the next pending message line, or erase the one 636 * that is there. 637 * 638 * Note that u_message is (currently) the same as i_message. 639 * 640 * Assumptions: lastline is consistent 641 */ 642 643/* 644 * i_message is funny because it gets its message asynchronously (with 645 * respect to screen updates). 646 */ 647 648static char next_msg[MAX_COLS + 5]; 649static int msglen = 0; 650/* Invariant: msglen is always the length of the message currently displayed 651 on the screen (even when next_msg doesn't contain that message). */ 652 653i_message() 654 655{ 656 while (lastline < y_message) 657 { 658 fputc('\n', stdout); 659 lastline++; 660 } 661 if (next_msg[0] != '\0') 662 { 663 standout(next_msg); 664 msglen = strlen(next_msg); 665 next_msg[0] = '\0'; 666 } 667 else if (msglen > 0) 668 { 669 (void) clear_eol(msglen); 670 msglen = 0; 671 } 672} 673 674u_message() 675 676{ 677 i_message(); 678} 679 680static int header_length; 681 682/* 683 * Trim a header string to the current display width and return a newly 684 * allocated area with the trimmed header. 685 */ 686 687char * 688trim_header(text) 689 690char *text; 691 692{ 693 char *s; 694 int width; 695 696 s = NULL; 697 width = screen_width; 698 header_length = strlen(text); 699 if (header_length >= width) { 700 s = malloc((width + 1) * sizeof(char)); 701 if (s == NULL) 702 return (NULL); 703 strncpy(s, text, width); 704 s[width] = '\0'; 705 } else { 706 s = malloc((width + 1) * sizeof(char)); 707 if (s == NULL) 708 return (NULL); 709 strncpy(s, text, width); 710 while (screen_width > header_length) 711 s[header_length++] = ' '; 712 s[width] = '\0'; 713 } 714 return (s); 715} 716 717/* 718 * *_header(text) - print the header for the process area 719 * 720 * Assumptions: cursor is on the previous line and lastline is consistent 721 */ 722 723i_header(text) 724 725char *text; 726 727{ 728 char *s; 729 730 s = trim_header(text); 731 if (s != NULL) 732 text = s; 733 734 if (header_status == ON) 735 { 736 putchar('\n'); 737 standout(text, stdout); 738 lastline++; 739 } 740 else if (header_status == ERASE) 741 { 742 header_status = OFF; 743 } 744 free(s); 745} 746 747/*ARGSUSED*/ 748u_header(text) 749 750char *text; /* ignored */ 751 752{ 753 754 if (header_status == ERASE) 755 { 756 putchar('\n'); 757 lastline++; 758 clear_eol(header_length); 759 header_status = OFF; 760 } 761} 762 763/* 764 * *_process(line, thisline) - print one process line 765 * 766 * Assumptions: lastline is consistent 767 */ 768 769i_process(line, thisline) 770 771int line; 772char *thisline; 773 774{ 775 register char *p; 776 register char *base; 777 778 /* make sure we are on the correct line */ 779 while (lastline < y_procs + line) 780 { 781 putchar('\n'); 782 lastline++; 783 } 784 785 /* truncate the line to conform to our current screen width */ 786 thisline[display_width] = '\0'; 787 788 /* write the line out */ 789 fputs(thisline, stdout); 790 791 /* copy it in to our buffer */ 792 base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; 793 p = strecpy(base, thisline); 794 795 /* zero fill the rest of it */ 796 memzero(p, display_width - (p - base)); 797} 798 799u_process(line, newline) 800 801int line; 802char *newline; 803 804{ 805 register char *optr; 806 register int screen_line = line + Header_lines; 807 register char *bufferline; 808 809 /* remember a pointer to the current line in the screen buffer */ 810 bufferline = &screenbuf[lineindex(line)]; 811 812 /* truncate the line to conform to our current screen width */ 813 newline[display_width] = '\0'; 814 815 /* is line higher than we went on the last display? */ 816 if (line >= last_hi) 817 { 818 /* yes, just ignore screenbuf and write it out directly */ 819 /* get positioned on the correct line */ 820 if (screen_line - lastline == 1) 821 { 822 putchar('\n'); 823 lastline++; 824 } 825 else 826 { 827 Move_to(0, screen_line); 828 lastline = screen_line; 829 } 830 831 /* now write the line */ 832 fputs(newline, stdout); 833 834 /* copy it in to the buffer */ 835 optr = strecpy(bufferline, newline); 836 837 /* zero fill the rest of it */ 838 memzero(optr, display_width - (optr - bufferline)); 839 } 840 else 841 { 842 line_update(bufferline, newline, 0, line + Header_lines); 843 } 844} 845 846u_endscreen(hi) 847 848register int hi; 849 850{ 851 register int screen_line = hi + Header_lines; 852 register int i; 853 854 if (smart_terminal) 855 { 856 if (hi < last_hi) 857 { 858 /* need to blank the remainder of the screen */ 859 /* but only if there is any screen left below this line */ 860 if (lastline + 1 < screen_length) 861 { 862 /* efficiently move to the end of currently displayed info */ 863 if (screen_line - lastline < 5) 864 { 865 while (lastline < screen_line) 866 { 867 putchar('\n'); 868 lastline++; 869 } 870 } 871 else 872 { 873 Move_to(0, screen_line); 874 lastline = screen_line; 875 } 876 877 if (clear_to_end) 878 { 879 /* we can do this the easy way */ 880 putcap(clear_to_end); 881 } 882 else 883 { 884 /* use clear_eol on each line */ 885 i = hi; 886 while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) 887 { 888 putchar('\n'); 889 } 890 } 891 } 892 } 893 last_hi = hi; 894 895 /* move the cursor to a pleasant place */ 896 Move_to(x_idlecursor, y_idlecursor); 897 lastline = y_idlecursor; 898 } 899 else 900 { 901 /* separate this display from the next with some vertical room */ 902 fputs("\n\n", stdout); 903 } 904} 905 906display_header(t) 907 908int t; 909 910{ 911 if (t) 912 { 913 header_status = ON; 914 } 915 else if (header_status == ON) 916 { 917 header_status = ERASE; 918 } 919} 920 921/*VARARGS2*/ 922new_message(type, msgfmt, a1, a2, a3) 923 924int type; 925char *msgfmt; 926caddr_t a1, a2, a3; 927 928{ 929 register int i; 930 931 /* first, format the message */ 932 (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); 933 934 if (msglen > 0) 935 { 936 /* message there already -- can we clear it? */ 937 if (!overstrike) 938 { 939 /* yes -- write it and clear to end */ 940 i = strlen(next_msg); 941 if ((type & MT_delayed) == 0) 942 { 943 type & MT_standout ? standout(next_msg) : 944 fputs(next_msg, stdout); 945 (void) clear_eol(msglen - i); 946 msglen = i; 947 next_msg[0] = '\0'; 948 } 949 } 950 } 951 else 952 { 953 if ((type & MT_delayed) == 0) 954 { 955 type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); 956 msglen = strlen(next_msg); 957 next_msg[0] = '\0'; 958 } 959 } 960} 961 962clear_message() 963 964{ 965 if (clear_eol(msglen) == 1) 966 { 967 putchar('\r'); 968 } 969} 970 971readline(buffer, size, numeric) 972 973char *buffer; 974int size; 975int numeric; 976 977{ 978 register char *ptr = buffer; 979 register char ch; 980 register char cnt = 0; 981 register char maxcnt = 0; 982 983 /* allow room for null terminator */ 984 size -= 1; 985 986 /* read loop */ 987 while ((fflush(stdout), read(0, ptr, 1) > 0)) 988 { 989 /* newline means we are done */ 990 if ((ch = *ptr) == '\n' || ch == '\r') 991 { 992 break; 993 } 994 995 /* handle special editing characters */ 996 if (ch == ch_kill) 997 { 998 /* kill line -- account for overstriking */ 999 if (overstrike) 1000 { 1001 msglen += maxcnt; 1002 } 1003 1004 /* return null string */ 1005 *buffer = '\0'; 1006 putchar('\r'); 1007 return(-1); 1008 } 1009 else if (ch == ch_erase) 1010 { 1011 /* erase previous character */ 1012 if (cnt <= 0) 1013 { 1014 /* none to erase! */ 1015 putchar('\7'); 1016 } 1017 else 1018 { 1019 fputs("\b \b", stdout); 1020 ptr--; 1021 cnt--; 1022 } 1023 } 1024 /* check for character validity and buffer overflow */ 1025 else if (cnt == size || (numeric && !isdigit(ch)) || 1026 !isprint(ch)) 1027 { 1028 /* not legal */ 1029 putchar('\7'); 1030 } 1031 else 1032 { 1033 /* echo it and store it in the buffer */ 1034 putchar(ch); 1035 ptr++; 1036 cnt++; 1037 if (cnt > maxcnt) 1038 { 1039 maxcnt = cnt; 1040 } 1041 } 1042 } 1043 1044 /* all done -- null terminate the string */ 1045 *ptr = '\0'; 1046 1047 /* account for the extra characters in the message area */ 1048 /* (if terminal overstrikes, remember the furthest they went) */ 1049 msglen += overstrike ? maxcnt : cnt; 1050 1051 /* return either inputted number or string length */ 1052 putchar('\r'); 1053 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 1054} 1055 1056/* internal support routines */ 1057 1058static int string_count(pp) 1059 1060register char **pp; 1061 1062{ 1063 register int cnt; 1064 1065 cnt = 0; 1066 while (*pp++ != NULL) 1067 { 1068 cnt++; 1069 } 1070 return(cnt); 1071} 1072 1073static void summary_format(str, numbers, names) 1074 1075char *str; 1076int *numbers; 1077register char **names; 1078 1079{ 1080 register char *p; 1081 register int num; 1082 register char *thisname; 1083 register int useM = No; 1084 1085 /* format each number followed by its string */ 1086 p = str; 1087 while ((thisname = *names++) != NULL) 1088 { 1089 /* get the number to format */ 1090 num = *numbers++; 1091 1092 /* display only non-zero numbers */ 1093 if (num > 0) 1094 { 1095 /* is this number in kilobytes? */ 1096 if (thisname[0] == 'K') 1097 { 1098 /* yes: format it as a memory value */ 1099 p = strecpy(p, format_k(num)); 1100 1101 /* skip over the K, since it was included by format_k */ 1102 p = strecpy(p, thisname+1); 1103 } 1104 else 1105 { 1106 p = strecpy(p, itoa(num)); 1107 p = strecpy(p, thisname); 1108 } 1109 } 1110 1111 /* ignore negative numbers, but display corresponding string */ 1112 else if (num < 0) 1113 { 1114 p = strecpy(p, thisname); 1115 } 1116 } 1117 1118 /* if the last two characters in the string are ", ", delete them */ 1119 p -= 2; 1120 if (p >= str && p[0] == ',' && p[1] == ' ') 1121 { 1122 *p = '\0'; 1123 } 1124} 1125 1126static void line_update(old, new, start, line) 1127 1128register char *old; 1129register char *new; 1130int start; 1131int line; 1132 1133{ 1134 register int ch; 1135 register int diff; 1136 register int newcol = start + 1; 1137 register int lastcol = start; 1138 char cursor_on_line = No; 1139 char *current; 1140 1141 /* compare the two strings and only rewrite what has changed */ 1142 current = old; 1143#ifdef DEBUG 1144 fprintf(debug, "line_update, starting at %d\n", start); 1145 fputs(old, debug); 1146 fputc('\n', debug); 1147 fputs(new, debug); 1148 fputs("\n-\n", debug); 1149#endif 1150 1151 /* start things off on the right foot */ 1152 /* this is to make sure the invariants get set up right */ 1153 if ((ch = *new++) != *old) 1154 { 1155 if (line - lastline == 1 && start == 0) 1156 { 1157 putchar('\n'); 1158 } 1159 else 1160 { 1161 Move_to(start, line); 1162 } 1163 cursor_on_line = Yes; 1164 putchar(ch); 1165 *old = ch; 1166 lastcol = 1; 1167 } 1168 old++; 1169 1170 /* 1171 * main loop -- check each character. If the old and new aren't the 1172 * same, then update the display. When the distance from the 1173 * current cursor position to the new change is small enough, 1174 * the characters that belong there are written to move the 1175 * cursor over. 1176 * 1177 * Invariants: 1178 * lastcol is the column where the cursor currently is sitting 1179 * (always one beyond the end of the last mismatch). 1180 */ 1181 do /* yes, a do...while */ 1182 { 1183 if ((ch = *new++) != *old) 1184 { 1185 /* new character is different from old */ 1186 /* make sure the cursor is on top of this character */ 1187 diff = newcol - lastcol; 1188 if (diff > 0) 1189 { 1190 /* some motion is required--figure out which is shorter */ 1191 if (diff < 6 && cursor_on_line) 1192 { 1193 /* overwrite old stuff--get it out of the old buffer */ 1194 printf("%.*s", diff, ¤t[lastcol-start]); 1195 } 1196 else 1197 { 1198 /* use cursor addressing */ 1199 Move_to(newcol, line); 1200 cursor_on_line = Yes; 1201 } 1202 /* remember where the cursor is */ 1203 lastcol = newcol + 1; 1204 } 1205 else 1206 { 1207 /* already there, update position */ 1208 lastcol++; 1209 } 1210 1211 /* write what we need to */ 1212 if (ch == '\0') 1213 { 1214 /* at the end--terminate with a clear-to-end-of-line */ 1215 (void) clear_eol(strlen(old)); 1216 } 1217 else 1218 { 1219 /* write the new character */ 1220 putchar(ch); 1221 } 1222 /* put the new character in the screen buffer */ 1223 *old = ch; 1224 } 1225 1226 /* update working column and screen buffer pointer */ 1227 newcol++; 1228 old++; 1229 1230 } while (ch != '\0'); 1231 1232 /* zero out the rest of the line buffer -- MUST BE DONE! */ 1233 diff = display_width - newcol; 1234 if (diff > 0) 1235 { 1236 memzero(old, diff); 1237 } 1238 1239 /* remember where the current line is */ 1240 if (cursor_on_line) 1241 { 1242 lastline = line; 1243 } 1244} 1245 1246/* 1247 * printable(str) - make the string pointed to by "str" into one that is 1248 * printable (i.e.: all ascii), by converting all non-printable 1249 * characters into '?'. Replacements are done in place and a pointer 1250 * to the original buffer is returned. 1251 */ 1252 1253char *printable(str) 1254 1255char *str; 1256 1257{ 1258 register char *ptr; 1259 register char ch; 1260 1261 ptr = str; 1262 while ((ch = *ptr) != '\0') 1263 { 1264 if (!isprint(ch)) 1265 { 1266 *ptr = '?'; 1267 } 1268 ptr++; 1269 } 1270 return(str); 1271} 1272 1273i_uptime(bt, tod) 1274 1275struct timeval* bt; 1276time_t *tod; 1277 1278{ 1279 time_t uptime; 1280 int days, hrs, mins, secs; 1281 1282 if (bt->tv_sec != -1) { 1283 uptime = *tod - bt->tv_sec; 1284 days = uptime / 86400; 1285 uptime %= 86400; 1286 hrs = uptime / 3600; 1287 uptime %= 3600; 1288 mins = uptime / 60; 1289 secs = uptime % 60; 1290 1291 /* 1292 * Display the uptime. 1293 */ 1294 1295 if (smart_terminal) 1296 { 1297 Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); 1298 } 1299 else 1300 { 1301 fputs(" ", stdout); 1302 } 1303 printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); 1304 } 1305} 1306