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