display.c revision 223342
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 223342 2011-06-20 16:48:00Z 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 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 = display_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 } 710 return (s); 711} 712 713/* 714 * *_header(text) - print the header for the process area 715 * 716 * Assumptions: cursor is on the previous line and lastline is consistent 717 */ 718 719i_header(text) 720 721char *text; 722 723{ 724 char *s; 725 726 s = trim_header(text); 727 if (s != NULL) 728 text = s; 729 730 if (header_status == ON) 731 { 732 putchar('\n'); 733 fputs(text, stdout); 734 lastline++; 735 } 736 else if (header_status == ERASE) 737 { 738 header_status = OFF; 739 } 740 free(s); 741} 742 743/*ARGSUSED*/ 744u_header(text) 745 746char *text; /* ignored */ 747 748{ 749 750 if (header_status == ERASE) 751 { 752 putchar('\n'); 753 lastline++; 754 clear_eol(header_length); 755 header_status = OFF; 756 } 757} 758 759/* 760 * *_process(line, thisline) - print one process line 761 * 762 * Assumptions: lastline is consistent 763 */ 764 765i_process(line, thisline) 766 767int line; 768char *thisline; 769 770{ 771 register char *p; 772 register char *base; 773 774 /* make sure we are on the correct line */ 775 while (lastline < y_procs + line) 776 { 777 putchar('\n'); 778 lastline++; 779 } 780 781 /* truncate the line to conform to our current screen width */ 782 thisline[display_width] = '\0'; 783 784 /* write the line out */ 785 fputs(thisline, stdout); 786 787 /* copy it in to our buffer */ 788 base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; 789 p = strecpy(base, thisline); 790 791 /* zero fill the rest of it */ 792 memzero(p, display_width - (p - base)); 793} 794 795u_process(line, newline) 796 797int line; 798char *newline; 799 800{ 801 register char *optr; 802 register int screen_line = line + Header_lines; 803 register char *bufferline; 804 805 /* remember a pointer to the current line in the screen buffer */ 806 bufferline = &screenbuf[lineindex(line)]; 807 808 /* truncate the line to conform to our current screen width */ 809 newline[display_width] = '\0'; 810 811 /* is line higher than we went on the last display? */ 812 if (line >= last_hi) 813 { 814 /* yes, just ignore screenbuf and write it out directly */ 815 /* get positioned on the correct line */ 816 if (screen_line - lastline == 1) 817 { 818 putchar('\n'); 819 lastline++; 820 } 821 else 822 { 823 Move_to(0, screen_line); 824 lastline = screen_line; 825 } 826 827 /* now write the line */ 828 fputs(newline, stdout); 829 830 /* copy it in to the buffer */ 831 optr = strecpy(bufferline, newline); 832 833 /* zero fill the rest of it */ 834 memzero(optr, display_width - (optr - bufferline)); 835 } 836 else 837 { 838 line_update(bufferline, newline, 0, line + Header_lines); 839 } 840} 841 842u_endscreen(hi) 843 844register int hi; 845 846{ 847 register int screen_line = hi + Header_lines; 848 register int i; 849 850 if (smart_terminal) 851 { 852 if (hi < last_hi) 853 { 854 /* need to blank the remainder of the screen */ 855 /* but only if there is any screen left below this line */ 856 if (lastline + 1 < screen_length) 857 { 858 /* efficiently move to the end of currently displayed info */ 859 if (screen_line - lastline < 5) 860 { 861 while (lastline < screen_line) 862 { 863 putchar('\n'); 864 lastline++; 865 } 866 } 867 else 868 { 869 Move_to(0, screen_line); 870 lastline = screen_line; 871 } 872 873 if (clear_to_end) 874 { 875 /* we can do this the easy way */ 876 putcap(clear_to_end); 877 } 878 else 879 { 880 /* use clear_eol on each line */ 881 i = hi; 882 while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) 883 { 884 putchar('\n'); 885 } 886 } 887 } 888 } 889 last_hi = hi; 890 891 /* move the cursor to a pleasant place */ 892 Move_to(x_idlecursor, y_idlecursor); 893 lastline = y_idlecursor; 894 } 895 else 896 { 897 /* separate this display from the next with some vertical room */ 898 fputs("\n\n", stdout); 899 } 900} 901 902display_header(t) 903 904int t; 905 906{ 907 if (t) 908 { 909 header_status = ON; 910 } 911 else if (header_status == ON) 912 { 913 header_status = ERASE; 914 } 915} 916 917/*VARARGS2*/ 918new_message(type, msgfmt, a1, a2, a3) 919 920int type; 921char *msgfmt; 922caddr_t a1, a2, a3; 923 924{ 925 register int i; 926 927 /* first, format the message */ 928 (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); 929 930 if (msglen > 0) 931 { 932 /* message there already -- can we clear it? */ 933 if (!overstrike) 934 { 935 /* yes -- write it and clear to end */ 936 i = strlen(next_msg); 937 if ((type & MT_delayed) == 0) 938 { 939 type & MT_standout ? standout(next_msg) : 940 fputs(next_msg, stdout); 941 (void) clear_eol(msglen - i); 942 msglen = i; 943 next_msg[0] = '\0'; 944 } 945 } 946 } 947 else 948 { 949 if ((type & MT_delayed) == 0) 950 { 951 type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); 952 msglen = strlen(next_msg); 953 next_msg[0] = '\0'; 954 } 955 } 956} 957 958clear_message() 959 960{ 961 if (clear_eol(msglen) == 1) 962 { 963 putchar('\r'); 964 } 965} 966 967readline(buffer, size, numeric) 968 969char *buffer; 970int size; 971int numeric; 972 973{ 974 register char *ptr = buffer; 975 register char ch; 976 register char cnt = 0; 977 register char maxcnt = 0; 978 979 /* allow room for null terminator */ 980 size -= 1; 981 982 /* read loop */ 983 while ((fflush(stdout), read(0, ptr, 1) > 0)) 984 { 985 /* newline means we are done */ 986 if ((ch = *ptr) == '\n' || ch == '\r') 987 { 988 break; 989 } 990 991 /* handle special editing characters */ 992 if (ch == ch_kill) 993 { 994 /* kill line -- account for overstriking */ 995 if (overstrike) 996 { 997 msglen += maxcnt; 998 } 999 1000 /* return null string */ 1001 *buffer = '\0'; 1002 putchar('\r'); 1003 return(-1); 1004 } 1005 else if (ch == ch_erase) 1006 { 1007 /* erase previous character */ 1008 if (cnt <= 0) 1009 { 1010 /* none to erase! */ 1011 putchar('\7'); 1012 } 1013 else 1014 { 1015 fputs("\b \b", stdout); 1016 ptr--; 1017 cnt--; 1018 } 1019 } 1020 /* check for character validity and buffer overflow */ 1021 else if (cnt == size || (numeric && !isdigit(ch)) || 1022 !isprint(ch)) 1023 { 1024 /* not legal */ 1025 putchar('\7'); 1026 } 1027 else 1028 { 1029 /* echo it and store it in the buffer */ 1030 putchar(ch); 1031 ptr++; 1032 cnt++; 1033 if (cnt > maxcnt) 1034 { 1035 maxcnt = cnt; 1036 } 1037 } 1038 } 1039 1040 /* all done -- null terminate the string */ 1041 *ptr = '\0'; 1042 1043 /* account for the extra characters in the message area */ 1044 /* (if terminal overstrikes, remember the furthest they went) */ 1045 msglen += overstrike ? maxcnt : cnt; 1046 1047 /* return either inputted number or string length */ 1048 putchar('\r'); 1049 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 1050} 1051 1052/* internal support routines */ 1053 1054static int string_count(pp) 1055 1056register char **pp; 1057 1058{ 1059 register int cnt; 1060 1061 cnt = 0; 1062 while (*pp++ != NULL) 1063 { 1064 cnt++; 1065 } 1066 return(cnt); 1067} 1068 1069static void summary_format(str, numbers, names) 1070 1071char *str; 1072int *numbers; 1073register char **names; 1074 1075{ 1076 register char *p; 1077 register int num; 1078 register char *thisname; 1079 register int useM = No; 1080 1081 /* format each number followed by its string */ 1082 p = str; 1083 while ((thisname = *names++) != NULL) 1084 { 1085 /* get the number to format */ 1086 num = *numbers++; 1087 1088 /* display only non-zero numbers */ 1089 if (num > 0) 1090 { 1091 /* is this number in kilobytes? */ 1092 if (thisname[0] == 'K') 1093 { 1094 /* yes: format it as a memory value */ 1095 p = strecpy(p, format_k(num)); 1096 1097 /* skip over the K, since it was included by format_k */ 1098 p = strecpy(p, thisname+1); 1099 } 1100 else 1101 { 1102 p = strecpy(p, itoa(num)); 1103 p = strecpy(p, thisname); 1104 } 1105 } 1106 1107 /* ignore negative numbers, but display corresponding string */ 1108 else if (num < 0) 1109 { 1110 p = strecpy(p, thisname); 1111 } 1112 } 1113 1114 /* if the last two characters in the string are ", ", delete them */ 1115 p -= 2; 1116 if (p >= str && p[0] == ',' && p[1] == ' ') 1117 { 1118 *p = '\0'; 1119 } 1120} 1121 1122static void line_update(old, new, start, line) 1123 1124register char *old; 1125register char *new; 1126int start; 1127int line; 1128 1129{ 1130 register int ch; 1131 register int diff; 1132 register int newcol = start + 1; 1133 register int lastcol = start; 1134 char cursor_on_line = No; 1135 char *current; 1136 1137 /* compare the two strings and only rewrite what has changed */ 1138 current = old; 1139#ifdef DEBUG 1140 fprintf(debug, "line_update, starting at %d\n", start); 1141 fputs(old, debug); 1142 fputc('\n', debug); 1143 fputs(new, debug); 1144 fputs("\n-\n", debug); 1145#endif 1146 1147 /* start things off on the right foot */ 1148 /* this is to make sure the invariants get set up right */ 1149 if ((ch = *new++) != *old) 1150 { 1151 if (line - lastline == 1 && start == 0) 1152 { 1153 putchar('\n'); 1154 } 1155 else 1156 { 1157 Move_to(start, line); 1158 } 1159 cursor_on_line = Yes; 1160 putchar(ch); 1161 *old = ch; 1162 lastcol = 1; 1163 } 1164 old++; 1165 1166 /* 1167 * main loop -- check each character. If the old and new aren't the 1168 * same, then update the display. When the distance from the 1169 * current cursor position to the new change is small enough, 1170 * the characters that belong there are written to move the 1171 * cursor over. 1172 * 1173 * Invariants: 1174 * lastcol is the column where the cursor currently is sitting 1175 * (always one beyond the end of the last mismatch). 1176 */ 1177 do /* yes, a do...while */ 1178 { 1179 if ((ch = *new++) != *old) 1180 { 1181 /* new character is different from old */ 1182 /* make sure the cursor is on top of this character */ 1183 diff = newcol - lastcol; 1184 if (diff > 0) 1185 { 1186 /* some motion is required--figure out which is shorter */ 1187 if (diff < 6 && cursor_on_line) 1188 { 1189 /* overwrite old stuff--get it out of the old buffer */ 1190 printf("%.*s", diff, ¤t[lastcol-start]); 1191 } 1192 else 1193 { 1194 /* use cursor addressing */ 1195 Move_to(newcol, line); 1196 cursor_on_line = Yes; 1197 } 1198 /* remember where the cursor is */ 1199 lastcol = newcol + 1; 1200 } 1201 else 1202 { 1203 /* already there, update position */ 1204 lastcol++; 1205 } 1206 1207 /* write what we need to */ 1208 if (ch == '\0') 1209 { 1210 /* at the end--terminate with a clear-to-end-of-line */ 1211 (void) clear_eol(strlen(old)); 1212 } 1213 else 1214 { 1215 /* write the new character */ 1216 putchar(ch); 1217 } 1218 /* put the new character in the screen buffer */ 1219 *old = ch; 1220 } 1221 1222 /* update working column and screen buffer pointer */ 1223 newcol++; 1224 old++; 1225 1226 } while (ch != '\0'); 1227 1228 /* zero out the rest of the line buffer -- MUST BE DONE! */ 1229 diff = display_width - newcol; 1230 if (diff > 0) 1231 { 1232 memzero(old, diff); 1233 } 1234 1235 /* remember where the current line is */ 1236 if (cursor_on_line) 1237 { 1238 lastline = line; 1239 } 1240} 1241 1242/* 1243 * printable(str) - make the string pointed to by "str" into one that is 1244 * printable (i.e.: all ascii), by converting all non-printable 1245 * characters into '?'. Replacements are done in place and a pointer 1246 * to the original buffer is returned. 1247 */ 1248 1249char *printable(str) 1250 1251char *str; 1252 1253{ 1254 register char *ptr; 1255 register char ch; 1256 1257 ptr = str; 1258 while ((ch = *ptr) != '\0') 1259 { 1260 if (!isprint(ch)) 1261 { 1262 *ptr = '?'; 1263 } 1264 ptr++; 1265 } 1266 return(str); 1267} 1268 1269i_uptime(bt, tod) 1270 1271struct timeval* bt; 1272time_t *tod; 1273 1274{ 1275 time_t uptime; 1276 int days, hrs, mins, secs; 1277 1278 if (bt->tv_sec != -1) { 1279 uptime = *tod - bt->tv_sec; 1280 days = uptime / 86400; 1281 uptime %= 86400; 1282 hrs = uptime / 3600; 1283 uptime %= 3600; 1284 mins = uptime / 60; 1285 secs = uptime % 60; 1286 1287 /* 1288 * Display the uptime. 1289 */ 1290 1291 if (smart_terminal) 1292 { 1293 Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); 1294 } 1295 else 1296 { 1297 fputs(" ", stdout); 1298 } 1299 printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); 1300 } 1301} 1302