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