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