display.c revision 223936
1326949Sdim/* 2326949Sdim * Top users/processes display for Unix 3353358Sdim * Version 3 4353358Sdim * 5353358Sdim * This program may be freely redistributed, 6326949Sdim * but this entire comment MUST remain intact. 7326949Sdim * 8326949Sdim * Copyright (c) 1984, 1989, William LeFebvre, Rice University 9326949Sdim * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University 10326949Sdim * 11326949Sdim * $FreeBSD: head/contrib/top/display.c 223936 2011-07-11 16:48:52Z jhb $ 12326949Sdim */ 13326949Sdim 14326949Sdim/* 15326949Sdim * This file contains the routines that display information on the screen. 16326949Sdim * Each section of the screen has two routines: one for initially writing 17326949Sdim * all constant and dynamic text, and one for only updating the text that 18326949Sdim * changes. The prefix "i_" is used on all the "initial" routines and the 19326949Sdim * prefix "u_" is used for all the "updating" routines. 20326949Sdim * 21326949Sdim * ASSUMPTIONS: 22326949Sdim * None of the "i_" routines use any of the termcap capabilities. 23344779Sdim * In this way, those routines can be safely used on terminals that 24326949Sdim * have minimal (or nonexistant) terminal capabilities. 25326949Sdim * 26326949Sdim * The routines are called in this order: *_loadave, i_timeofday, 27326949Sdim * *_procstates, *_cpustates, *_memory, *_message, *_header, 28326949Sdim * *_process, u_endscreen. 29326949Sdim */ 30326949Sdim 31326949Sdim#include "os.h" 32326949Sdim#include <ctype.h> 33326949Sdim#include <time.h> 34326949Sdim#include <sys/time.h> 35326949Sdim 36326949Sdim#include "screen.h" /* interface to screen package */ 37326949Sdim#include "layout.h" /* defines for screen position layout */ 38326949Sdim#include "display.h" 39326949Sdim#include "top.h" 40326949Sdim#include "top.local.h" 41326949Sdim#include "boolean.h" 42326949Sdim#include "machine.h" /* we should eliminate this!!! */ 43326949Sdim#include "utils.h" 44326949Sdim 45326949Sdim#ifdef DEBUG 46326949SdimFILE *debug; 47326949Sdim#endif 48326949Sdim 49326949Sdim/* imported from screen.c */ 50326949Sdimextern int overstrike; 51326949Sdim 52326949Sdimstatic int lmpid = 0; 53326949Sdimstatic int last_hi = 0; /* used in u_process and u_endscreen */ 54326949Sdimstatic int lastline = 0; 55326949Sdimstatic int display_width = MAX_COLS; 56326949Sdim 57326949Sdim#define lineindex(l) ((l)*display_width) 58326949Sdim 59326949Sdimchar *printable(); 60326949Sdim 61326949Sdim/* things initialized by display_init and used thruout */ 62326949Sdim 63326949Sdim/* buffer of proc information lines for display updating */ 64326949Sdimchar *screenbuf = NULL; 65326949Sdim 66326949Sdimstatic char **procstate_names; 67326949Sdimstatic char **cpustate_names; 68326949Sdimstatic char **memory_names; 69326949Sdimstatic char **swap_names; 70326949Sdim 71326949Sdimstatic int num_procstates; 72326949Sdimstatic int num_cpustates; 73326949Sdimstatic int num_memory; 74326949Sdimstatic int num_swap; 75326949Sdim 76326949Sdimstatic int *lprocstates; 77326949Sdimstatic int *lcpustates; 78341825Sdimstatic int *lmemory; 79341825Sdimstatic int *lswap; 80326949Sdim 81326949Sdimstatic int num_cpus; 82326949Sdimstatic int *cpustate_columns; 83326949Sdimstatic int cpustate_total_length; 84326949Sdimstatic int cpustates_column; 85326949Sdim 86326949Sdimstatic enum { OFF, ON, ERASE } header_status = ON; 87326949Sdim 88326949Sdimstatic int string_count(); 89326949Sdimstatic void summary_format(); 90326949Sdimstatic void line_update(); 91326949Sdim 92326949Sdimint x_lastpid = 10; 93326949Sdimint y_lastpid = 0; 94326949Sdimint x_loadave = 33; 95326949Sdimint x_loadave_nompid = 15; 96326949Sdimint y_loadave = 0; 97326949Sdimint x_procstate = 0; 98326949Sdimint y_procstate = 1; 99326949Sdimint x_brkdn = 15; 100326949Sdimint y_brkdn = 1; 101326949Sdimint x_mem = 5; 102326949Sdimint y_mem = 3; 103326949Sdimint x_swap = 6; 104326949Sdimint y_swap = 4; 105326949Sdimint y_message = 5; 106326949Sdimint x_header = 0; 107326949Sdimint y_header = 6; 108326949Sdimint x_idlecursor = 0; 109326949Sdimint y_idlecursor = 5; 110326949Sdimint y_procs = 7; 111326949Sdim 112326949Sdimint y_cpustates = 2; 113326949Sdimint Header_lines = 7; 114326949Sdim 115326949Sdimint display_resize() 116326949Sdim 117326949Sdim{ 118326949Sdim register int lines; 119326949Sdim 120326949Sdim /* first, deallocate any previous buffer that may have been there */ 121326949Sdim if (screenbuf != NULL) 122326949Sdim { 123326949Sdim free(screenbuf); 124326949Sdim } 125326949Sdim 126326949Sdim /* calculate the current dimensions */ 127326949Sdim /* if operating in "dumb" mode, we only need one line */ 128326949Sdim lines = smart_terminal ? screen_length - Header_lines : 1; 129326949Sdim 130326949Sdim if (lines < 0) 131326949Sdim lines = 0; 132326949Sdim /* we don't want more than MAX_COLS columns, since the machine-dependent 133326949Sdim modules make static allocations based on MAX_COLS and we don't want 134326949Sdim to run off the end of their buffers */ 135326949Sdim display_width = screen_width; 136326949Sdim if (display_width >= MAX_COLS) 137326949Sdim { 138326949Sdim display_width = MAX_COLS - 1; 139353358Sdim } 140326949Sdim 141326949Sdim /* now, allocate space for the screen buffer */ 142326949Sdim screenbuf = (char *)malloc(lines * display_width); 143326949Sdim if (screenbuf == (char *)NULL) 144326949Sdim { 145353358Sdim /* oops! */ 146326949Sdim return(-1); 147326949Sdim } 148326949Sdim 149326949Sdim /* return number of lines available */ 150326949Sdim /* for dumb terminals, pretend like we can show any amount */ 151326949Sdim return(smart_terminal ? lines : Largest); 152326949Sdim} 153326949Sdim 154326949Sdimint display_updatecpus(statics) 155326949Sdim 156326949Sdimstruct statics *statics; 157326949Sdim 158326949Sdim{ 159353358Sdim register int lines; 160326949Sdim register int i; 161326949Sdim 162353358Sdim /* call resize to do the dirty work */ 163326949Sdim lines = display_resize(); 164326949Sdim num_cpus = statics->ncpus; 165326949Sdim cpustates_column = 5; /* CPU: */ 166326949Sdim if (num_cpus != 1) 167326949Sdim cpustates_column += 2; /* CPU 0: */ 168326949Sdim for (i = num_cpus; i > 9; i /= 10) 169326949Sdim cpustates_column++; 170326949Sdim 171326949Sdim return(lines); 172326949Sdim} 173326949Sdim 174326949Sdimint display_init(statics) 175326949Sdim 176326949Sdimstruct statics *statics; 177326949Sdim 178326949Sdim{ 179326949Sdim register int lines; 180326949Sdim register char **pp; 181326949Sdim register int *ip; 182326949Sdim register int i; 183326949Sdim 184326949Sdim lines = display_updatecpus(statics); 185326949Sdim 186326949Sdim /* only do the rest if we need to */ 187326949Sdim if (lines > -1) 188326949Sdim { 189326949Sdim /* save pointers and allocate space for names */ 190326949Sdim procstate_names = statics->procstate_names; 191326949Sdim num_procstates = string_count(procstate_names); 192326949Sdim lprocstates = (int *)malloc(num_procstates * sizeof(int)); 193326949Sdim 194326949Sdim cpustate_names = statics->cpustate_names; 195326949Sdim 196326949Sdim swap_names = statics->swap_names; 197326949Sdim num_swap = string_count(swap_names); 198326949Sdim lswap = (int *)malloc(num_swap * sizeof(int)); 199326949Sdim num_cpustates = string_count(cpustate_names); 200326949Sdim lcpustates = (int *)malloc(num_cpustates * sizeof(int) * num_cpus); 201326949Sdim cpustate_columns = (int *)malloc(num_cpustates * sizeof(int)); 202326949Sdim 203326949Sdim memory_names = statics->memory_names; 204326949Sdim num_memory = string_count(memory_names); 205326949Sdim lmemory = (int *)malloc(num_memory * sizeof(int)); 206326949Sdim 207326949Sdim /* calculate starting columns where needed */ 208326949Sdim cpustate_total_length = 0; 209 pp = cpustate_names; 210 ip = cpustate_columns; 211 while (*pp != NULL) 212 { 213 *ip++ = cpustate_total_length; 214 if ((i = strlen(*pp++)) > 0) 215 { 216 cpustate_total_length += i + 8; 217 } 218 } 219 } 220 221 /* return number of lines available */ 222 return(lines); 223} 224 225i_loadave(mpid, avenrun) 226 227int mpid; 228double *avenrun; 229 230{ 231 register int i; 232 233 /* i_loadave also clears the screen, since it is first */ 234 clear(); 235 236 /* mpid == -1 implies this system doesn't have an _mpid */ 237 if (mpid != -1) 238 { 239 printf("last pid: %5d; ", mpid); 240 } 241 242 printf("load averages"); 243 244 for (i = 0; i < 3; i++) 245 { 246 printf("%c %5.2f", 247 i == 0 ? ':' : ',', 248 avenrun[i]); 249 } 250 lmpid = mpid; 251} 252 253u_loadave(mpid, avenrun) 254 255int mpid; 256double *avenrun; 257 258{ 259 register int i; 260 261 if (mpid != -1) 262 { 263 /* change screen only when value has really changed */ 264 if (mpid != lmpid) 265 { 266 Move_to(x_lastpid, y_lastpid); 267 printf("%5d", mpid); 268 lmpid = mpid; 269 } 270 271 /* i remembers x coordinate to move to */ 272 i = x_loadave; 273 } 274 else 275 { 276 i = x_loadave_nompid; 277 } 278 279 /* move into position for load averages */ 280 Move_to(i, y_loadave); 281 282 /* display new load averages */ 283 /* we should optimize this and only display changes */ 284 for (i = 0; i < 3; i++) 285 { 286 printf("%s%5.2f", 287 i == 0 ? "" : ", ", 288 avenrun[i]); 289 } 290} 291 292i_timeofday(tod) 293 294time_t *tod; 295 296{ 297 /* 298 * Display the current time. 299 * "ctime" always returns a string that looks like this: 300 * 301 * Sun Sep 16 01:03:52 1973 302 * 012345678901234567890123 303 * 1 2 304 * 305 * We want indices 11 thru 18 (length 8). 306 */ 307 308 if (smart_terminal) 309 { 310 Move_to(screen_width - 8, 0); 311 } 312 else 313 { 314 fputs(" ", stdout); 315 } 316#ifdef DEBUG 317 { 318 char *foo; 319 foo = ctime(tod); 320 fputs(foo, stdout); 321 } 322#endif 323 printf("%-8.8s\n", &(ctime(tod)[11])); 324 lastline = 1; 325} 326 327static int ltotal = 0; 328static char procstates_buffer[MAX_COLS]; 329 330/* 331 * *_procstates(total, brkdn, names) - print the process summary line 332 * 333 * Assumptions: cursor is at the beginning of the line on entry 334 * lastline is valid 335 */ 336 337i_procstates(total, brkdn) 338 339int total; 340int *brkdn; 341 342{ 343 register int i; 344 345 /* write current number of processes and remember the value */ 346 printf("%d processes:", total); 347 ltotal = total; 348 349 /* put out enough spaces to get to column 15 */ 350 i = digits(total); 351 while (i++ < 4) 352 { 353 putchar(' '); 354 } 355 356 /* format and print the process state summary */ 357 summary_format(procstates_buffer, brkdn, procstate_names); 358 fputs(procstates_buffer, stdout); 359 360 /* save the numbers for next time */ 361 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 362} 363 364u_procstates(total, brkdn) 365 366int total; 367int *brkdn; 368 369{ 370 static char new[MAX_COLS]; 371 register int i; 372 373 /* update number of processes only if it has changed */ 374 if (ltotal != total) 375 { 376 /* move and overwrite */ 377#if (x_procstate == 0) 378 Move_to(x_procstate, y_procstate); 379#else 380 /* cursor is already there...no motion needed */ 381 /* assert(lastline == 1); */ 382#endif 383 printf("%d", total); 384 385 /* if number of digits differs, rewrite the label */ 386 if (digits(total) != digits(ltotal)) 387 { 388 fputs(" processes:", stdout); 389 /* put out enough spaces to get to column 15 */ 390 i = digits(total); 391 while (i++ < 4) 392 { 393 putchar(' '); 394 } 395 /* cursor may end up right where we want it!!! */ 396 } 397 398 /* save new total */ 399 ltotal = total; 400 } 401 402 /* see if any of the state numbers has changed */ 403 if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) 404 { 405 /* format and update the line */ 406 summary_format(new, brkdn, procstate_names); 407 line_update(procstates_buffer, new, x_brkdn, y_brkdn); 408 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 409 } 410} 411 412#ifdef no_more 413/* 414 * *_cpustates(states, names) - print the cpu state percentages 415 * 416 * Assumptions: cursor is on the PREVIOUS line 417 */ 418 419/* cpustates_tag() calculates the correct tag to use to label the line */ 420 421char *cpustates_tag() 422 423{ 424 register char *use; 425 426 static char *short_tag = "CPU: "; 427 static char *long_tag = "CPU states: "; 428 429 /* if length + strlen(long_tag) >= screen_width, then we have to 430 use the shorter tag (we subtract 2 to account for ": ") */ 431 if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width) 432 { 433 use = short_tag; 434 } 435 else 436 { 437 use = long_tag; 438 } 439 440 /* set cpustates_column accordingly then return result */ 441 cpustates_column = strlen(use); 442 return(use); 443} 444#endif 445 446i_cpustates(states) 447 448register int *states; 449 450{ 451 register int i = 0; 452 register int value; 453 register char **names; 454 register char *thisname; 455 int cpu; 456 457for (cpu = 0; cpu < num_cpus; cpu++) { 458 names = cpustate_names; 459 460 /* print tag and bump lastline */ 461 if (num_cpus == 1) 462 printf("\nCPU: "); 463 else { 464 value = printf("\nCPU %d: ", cpu); 465 while (value++ <= cpustates_column) 466 printf(" "); 467 } 468 lastline++; 469 470 /* now walk thru the names and print the line */ 471 while ((thisname = *names++) != NULL) 472 { 473 if (*thisname != '\0') 474 { 475 /* retrieve the value and remember it */ 476 value = *states++; 477 478 /* if percentage is >= 1000, print it as 100% */ 479 printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), 480 (i++ % num_cpustates) == 0 ? "" : ", ", 481 ((float)value)/10., 482 thisname); 483 } 484 } 485} 486 487 /* copy over values into "last" array */ 488 memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus); 489} 490 491u_cpustates(states) 492 493register int *states; 494 495{ 496 register int value; 497 register char **names; 498 register char *thisname; 499 register int *lp; 500 register int *colp; 501 int cpu; 502 503for (cpu = 0; cpu < num_cpus; cpu++) { 504 names = cpustate_names; 505 506 Move_to(cpustates_column, y_cpustates + cpu); 507 lastline = y_cpustates + cpu; 508 lp = lcpustates + (cpu * num_cpustates); 509 colp = cpustate_columns; 510 511 /* we could be much more optimal about this */ 512 while ((thisname = *names++) != NULL) 513 { 514 if (*thisname != '\0') 515 { 516 /* did the value change since last time? */ 517 if (*lp != *states) 518 { 519 /* yes, move and change */ 520 Move_to(cpustates_column + *colp, y_cpustates + cpu); 521 lastline = y_cpustates + cpu; 522 523 /* retrieve value and remember it */ 524 value = *states; 525 526 /* if percentage is >= 1000, print it as 100% */ 527 printf((value >= 1000 ? "%4.0f" : "%4.1f"), 528 ((double)value)/10.); 529 530 /* remember it for next time */ 531 *lp = value; 532 } 533 } 534 535 /* increment and move on */ 536 lp++; 537 states++; 538 colp++; 539 } 540} 541} 542 543z_cpustates() 544 545{ 546 register int i = 0; 547 register char **names; 548 register char *thisname; 549 register int *lp; 550 int cpu, value; 551 552for (cpu = 0; cpu < num_cpus; cpu++) { 553 names = cpustate_names; 554 555 /* show tag and bump lastline */ 556 if (num_cpus == 1) 557 printf("\nCPU: "); 558 else { 559 value = printf("\nCPU %d: ", cpu); 560 while (value++ <= cpustates_column) 561 printf(" "); 562 } 563 lastline++; 564 565 while ((thisname = *names++) != NULL) 566 { 567 if (*thisname != '\0') 568 { 569 printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname); 570 } 571 } 572} 573 574 /* fill the "last" array with all -1s, to insure correct updating */ 575 lp = lcpustates; 576 i = num_cpustates * num_cpus; 577 while (--i >= 0) 578 { 579 *lp++ = -1; 580 } 581} 582 583/* 584 * *_memory(stats) - print "Memory: " followed by the memory summary string 585 * 586 * Assumptions: cursor is on "lastline" 587 * for i_memory ONLY: cursor is on the previous line 588 */ 589 590char memory_buffer[MAX_COLS]; 591 592i_memory(stats) 593 594int *stats; 595 596{ 597 fputs("\nMem: ", stdout); 598 lastline++; 599 600 /* format and print the memory summary */ 601 summary_format(memory_buffer, stats, memory_names); 602 fputs(memory_buffer, stdout); 603} 604 605u_memory(stats) 606 607int *stats; 608 609{ 610 static char new[MAX_COLS]; 611 612 /* format the new line */ 613 summary_format(new, stats, memory_names); 614 line_update(memory_buffer, new, x_mem, y_mem); 615} 616 617/* 618 * *_swap(stats) - print "Swap: " followed by the swap summary string 619 * 620 * Assumptions: cursor is on "lastline" 621 * for i_swap ONLY: cursor is on the previous line 622 */ 623 624char swap_buffer[MAX_COLS]; 625 626i_swap(stats) 627 628int *stats; 629 630{ 631 fputs("\nSwap: ", stdout); 632 lastline++; 633 634 /* format and print the swap summary */ 635 summary_format(swap_buffer, stats, swap_names); 636 fputs(swap_buffer, stdout); 637} 638 639u_swap(stats) 640 641int *stats; 642 643{ 644 static char new[MAX_COLS]; 645 646 /* format the new line */ 647 summary_format(new, stats, swap_names); 648 line_update(swap_buffer, new, x_swap, y_swap); 649} 650 651/* 652 * *_message() - print the next pending message line, or erase the one 653 * that is there. 654 * 655 * Note that u_message is (currently) the same as i_message. 656 * 657 * Assumptions: lastline is consistent 658 */ 659 660/* 661 * i_message is funny because it gets its message asynchronously (with 662 * respect to screen updates). 663 */ 664 665static char next_msg[MAX_COLS + 5]; 666static int msglen = 0; 667/* Invariant: msglen is always the length of the message currently displayed 668 on the screen (even when next_msg doesn't contain that message). */ 669 670i_message() 671 672{ 673 while (lastline < y_message) 674 { 675 fputc('\n', stdout); 676 lastline++; 677 } 678 if (next_msg[0] != '\0') 679 { 680 standout(next_msg); 681 msglen = strlen(next_msg); 682 next_msg[0] = '\0'; 683 } 684 else if (msglen > 0) 685 { 686 (void) clear_eol(msglen); 687 msglen = 0; 688 } 689} 690 691u_message() 692 693{ 694 i_message(); 695} 696 697static int header_length; 698 699/* 700 * Trim a header string to the current display width and return a newly 701 * allocated area with the trimmed header. 702 */ 703 704char * 705trim_header(text) 706 707char *text; 708 709{ 710 char *s; 711 int width; 712 713 s = NULL; 714 width = display_width; 715 header_length = strlen(text); 716 if (header_length >= width) { 717 s = malloc((width + 1) * sizeof(char)); 718 if (s == NULL) 719 return (NULL); 720 strncpy(s, text, width); 721 s[width] = '\0'; 722 } 723 return (s); 724} 725 726/* 727 * *_header(text) - print the header for the process area 728 * 729 * Assumptions: cursor is on the previous line and lastline is consistent 730 */ 731 732i_header(text) 733 734char *text; 735 736{ 737 char *s; 738 739 s = trim_header(text); 740 if (s != NULL) 741 text = s; 742 743 if (header_status == ON) 744 { 745 putchar('\n'); 746 fputs(text, stdout); 747 lastline++; 748 } 749 else if (header_status == ERASE) 750 { 751 header_status = OFF; 752 } 753 free(s); 754} 755 756/*ARGSUSED*/ 757u_header(text) 758 759char *text; /* ignored */ 760 761{ 762 763 if (header_status == ERASE) 764 { 765 putchar('\n'); 766 lastline++; 767 clear_eol(header_length); 768 header_status = OFF; 769 } 770} 771 772/* 773 * *_process(line, thisline) - print one process line 774 * 775 * Assumptions: lastline is consistent 776 */ 777 778i_process(line, thisline) 779 780int line; 781char *thisline; 782 783{ 784 register char *p; 785 register char *base; 786 787 /* make sure we are on the correct line */ 788 while (lastline < y_procs + line) 789 { 790 putchar('\n'); 791 lastline++; 792 } 793 794 /* truncate the line to conform to our current screen width */ 795 thisline[display_width] = '\0'; 796 797 /* write the line out */ 798 fputs(thisline, stdout); 799 800 /* copy it in to our buffer */ 801 base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; 802 p = strecpy(base, thisline); 803 804 /* zero fill the rest of it */ 805 memzero(p, display_width - (p - base)); 806} 807 808u_process(line, newline) 809 810int line; 811char *newline; 812 813{ 814 register char *optr; 815 register int screen_line = line + Header_lines; 816 register char *bufferline; 817 818 /* remember a pointer to the current line in the screen buffer */ 819 bufferline = &screenbuf[lineindex(line)]; 820 821 /* truncate the line to conform to our current screen width */ 822 newline[display_width] = '\0'; 823 824 /* is line higher than we went on the last display? */ 825 if (line >= last_hi) 826 { 827 /* yes, just ignore screenbuf and write it out directly */ 828 /* get positioned on the correct line */ 829 if (screen_line - lastline == 1) 830 { 831 putchar('\n'); 832 lastline++; 833 } 834 else 835 { 836 Move_to(0, screen_line); 837 lastline = screen_line; 838 } 839 840 /* now write the line */ 841 fputs(newline, stdout); 842 843 /* copy it in to the buffer */ 844 optr = strecpy(bufferline, newline); 845 846 /* zero fill the rest of it */ 847 memzero(optr, display_width - (optr - bufferline)); 848 } 849 else 850 { 851 line_update(bufferline, newline, 0, line + Header_lines); 852 } 853} 854 855u_endscreen(hi) 856 857register int hi; 858 859{ 860 register int screen_line = hi + Header_lines; 861 register int i; 862 863 if (smart_terminal) 864 { 865 if (hi < last_hi) 866 { 867 /* need to blank the remainder of the screen */ 868 /* but only if there is any screen left below this line */ 869 if (lastline + 1 < screen_length) 870 { 871 /* efficiently move to the end of currently displayed info */ 872 if (screen_line - lastline < 5) 873 { 874 while (lastline < screen_line) 875 { 876 putchar('\n'); 877 lastline++; 878 } 879 } 880 else 881 { 882 Move_to(0, screen_line); 883 lastline = screen_line; 884 } 885 886 if (clear_to_end) 887 { 888 /* we can do this the easy way */ 889 putcap(clear_to_end); 890 } 891 else 892 { 893 /* use clear_eol on each line */ 894 i = hi; 895 while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) 896 { 897 putchar('\n'); 898 } 899 } 900 } 901 } 902 last_hi = hi; 903 904 /* move the cursor to a pleasant place */ 905 Move_to(x_idlecursor, y_idlecursor); 906 lastline = y_idlecursor; 907 } 908 else 909 { 910 /* separate this display from the next with some vertical room */ 911 fputs("\n\n", stdout); 912 } 913} 914 915display_header(t) 916 917int t; 918 919{ 920 if (t) 921 { 922 header_status = ON; 923 } 924 else if (header_status == ON) 925 { 926 header_status = ERASE; 927 } 928} 929 930/*VARARGS2*/ 931new_message(type, msgfmt, a1, a2, a3) 932 933int type; 934char *msgfmt; 935caddr_t a1, a2, a3; 936 937{ 938 register int i; 939 940 /* first, format the message */ 941 (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); 942 943 if (msglen > 0) 944 { 945 /* message there already -- can we clear it? */ 946 if (!overstrike) 947 { 948 /* yes -- write it and clear to end */ 949 i = strlen(next_msg); 950 if ((type & MT_delayed) == 0) 951 { 952 type & MT_standout ? standout(next_msg) : 953 fputs(next_msg, stdout); 954 (void) clear_eol(msglen - i); 955 msglen = i; 956 next_msg[0] = '\0'; 957 } 958 } 959 } 960 else 961 { 962 if ((type & MT_delayed) == 0) 963 { 964 type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); 965 msglen = strlen(next_msg); 966 next_msg[0] = '\0'; 967 } 968 } 969} 970 971clear_message() 972 973{ 974 if (clear_eol(msglen) == 1) 975 { 976 putchar('\r'); 977 } 978} 979 980readline(buffer, size, numeric) 981 982char *buffer; 983int size; 984int numeric; 985 986{ 987 register char *ptr = buffer; 988 register char ch; 989 register char cnt = 0; 990 register char maxcnt = 0; 991 992 /* allow room for null terminator */ 993 size -= 1; 994 995 /* read loop */ 996 while ((fflush(stdout), read(0, ptr, 1) > 0)) 997 { 998 /* newline means we are done */ 999 if ((ch = *ptr) == '\n' || ch == '\r') 1000 { 1001 break; 1002 } 1003 1004 /* handle special editing characters */ 1005 if (ch == ch_kill) 1006 { 1007 /* kill line -- account for overstriking */ 1008 if (overstrike) 1009 { 1010 msglen += maxcnt; 1011 } 1012 1013 /* return null string */ 1014 *buffer = '\0'; 1015 putchar('\r'); 1016 return(-1); 1017 } 1018 else if (ch == ch_erase) 1019 { 1020 /* erase previous character */ 1021 if (cnt <= 0) 1022 { 1023 /* none to erase! */ 1024 putchar('\7'); 1025 } 1026 else 1027 { 1028 fputs("\b \b", stdout); 1029 ptr--; 1030 cnt--; 1031 } 1032 } 1033 /* check for character validity and buffer overflow */ 1034 else if (cnt == size || (numeric && !isdigit(ch)) || 1035 !isprint(ch)) 1036 { 1037 /* not legal */ 1038 putchar('\7'); 1039 } 1040 else 1041 { 1042 /* echo it and store it in the buffer */ 1043 putchar(ch); 1044 ptr++; 1045 cnt++; 1046 if (cnt > maxcnt) 1047 { 1048 maxcnt = cnt; 1049 } 1050 } 1051 } 1052 1053 /* all done -- null terminate the string */ 1054 *ptr = '\0'; 1055 1056 /* account for the extra characters in the message area */ 1057 /* (if terminal overstrikes, remember the furthest they went) */ 1058 msglen += overstrike ? maxcnt : cnt; 1059 1060 /* return either inputted number or string length */ 1061 putchar('\r'); 1062 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 1063} 1064 1065/* internal support routines */ 1066 1067static int string_count(pp) 1068 1069register char **pp; 1070 1071{ 1072 register int cnt; 1073 1074 cnt = 0; 1075 while (*pp++ != NULL) 1076 { 1077 cnt++; 1078 } 1079 return(cnt); 1080} 1081 1082static void summary_format(str, numbers, names) 1083 1084char *str; 1085int *numbers; 1086register char **names; 1087 1088{ 1089 register char *p; 1090 register int num; 1091 register char *thisname; 1092 register int useM = No; 1093 1094 /* format each number followed by its string */ 1095 p = str; 1096 while ((thisname = *names++) != NULL) 1097 { 1098 /* get the number to format */ 1099 num = *numbers++; 1100 1101 /* display only non-zero numbers */ 1102 if (num > 0) 1103 { 1104 /* is this number in kilobytes? */ 1105 if (thisname[0] == 'K') 1106 { 1107 /* yes: format it as a memory value */ 1108 p = strecpy(p, format_k(num)); 1109 1110 /* skip over the K, since it was included by format_k */ 1111 p = strecpy(p, thisname+1); 1112 } 1113 else 1114 { 1115 p = strecpy(p, itoa(num)); 1116 p = strecpy(p, thisname); 1117 } 1118 } 1119 1120 /* ignore negative numbers, but display corresponding string */ 1121 else if (num < 0) 1122 { 1123 p = strecpy(p, thisname); 1124 } 1125 } 1126 1127 /* if the last two characters in the string are ", ", delete them */ 1128 p -= 2; 1129 if (p >= str && p[0] == ',' && p[1] == ' ') 1130 { 1131 *p = '\0'; 1132 } 1133} 1134 1135static void line_update(old, new, start, line) 1136 1137register char *old; 1138register char *new; 1139int start; 1140int line; 1141 1142{ 1143 register int ch; 1144 register int diff; 1145 register int newcol = start + 1; 1146 register int lastcol = start; 1147 char cursor_on_line = No; 1148 char *current; 1149 1150 /* compare the two strings and only rewrite what has changed */ 1151 current = old; 1152#ifdef DEBUG 1153 fprintf(debug, "line_update, starting at %d\n", start); 1154 fputs(old, debug); 1155 fputc('\n', debug); 1156 fputs(new, debug); 1157 fputs("\n-\n", debug); 1158#endif 1159 1160 /* start things off on the right foot */ 1161 /* this is to make sure the invariants get set up right */ 1162 if ((ch = *new++) != *old) 1163 { 1164 if (line - lastline == 1 && start == 0) 1165 { 1166 putchar('\n'); 1167 } 1168 else 1169 { 1170 Move_to(start, line); 1171 } 1172 cursor_on_line = Yes; 1173 putchar(ch); 1174 *old = ch; 1175 lastcol = 1; 1176 } 1177 old++; 1178 1179 /* 1180 * main loop -- check each character. If the old and new aren't the 1181 * same, then update the display. When the distance from the 1182 * current cursor position to the new change is small enough, 1183 * the characters that belong there are written to move the 1184 * cursor over. 1185 * 1186 * Invariants: 1187 * lastcol is the column where the cursor currently is sitting 1188 * (always one beyond the end of the last mismatch). 1189 */ 1190 do /* yes, a do...while */ 1191 { 1192 if ((ch = *new++) != *old) 1193 { 1194 /* new character is different from old */ 1195 /* make sure the cursor is on top of this character */ 1196 diff = newcol - lastcol; 1197 if (diff > 0) 1198 { 1199 /* some motion is required--figure out which is shorter */ 1200 if (diff < 6 && cursor_on_line) 1201 { 1202 /* overwrite old stuff--get it out of the old buffer */ 1203 printf("%.*s", diff, ¤t[lastcol-start]); 1204 } 1205 else 1206 { 1207 /* use cursor addressing */ 1208 Move_to(newcol, line); 1209 cursor_on_line = Yes; 1210 } 1211 /* remember where the cursor is */ 1212 lastcol = newcol + 1; 1213 } 1214 else 1215 { 1216 /* already there, update position */ 1217 lastcol++; 1218 } 1219 1220 /* write what we need to */ 1221 if (ch == '\0') 1222 { 1223 /* at the end--terminate with a clear-to-end-of-line */ 1224 (void) clear_eol(strlen(old)); 1225 } 1226 else 1227 { 1228 /* write the new character */ 1229 putchar(ch); 1230 } 1231 /* put the new character in the screen buffer */ 1232 *old = ch; 1233 } 1234 1235 /* update working column and screen buffer pointer */ 1236 newcol++; 1237 old++; 1238 1239 } while (ch != '\0'); 1240 1241 /* zero out the rest of the line buffer -- MUST BE DONE! */ 1242 diff = display_width - newcol; 1243 if (diff > 0) 1244 { 1245 memzero(old, diff); 1246 } 1247 1248 /* remember where the current line is */ 1249 if (cursor_on_line) 1250 { 1251 lastline = line; 1252 } 1253} 1254 1255/* 1256 * printable(str) - make the string pointed to by "str" into one that is 1257 * printable (i.e.: all ascii), by converting all non-printable 1258 * characters into '?'. Replacements are done in place and a pointer 1259 * to the original buffer is returned. 1260 */ 1261 1262char *printable(str) 1263 1264char *str; 1265 1266{ 1267 register char *ptr; 1268 register char ch; 1269 1270 ptr = str; 1271 while ((ch = *ptr) != '\0') 1272 { 1273 if (!isprint(ch)) 1274 { 1275 *ptr = '?'; 1276 } 1277 ptr++; 1278 } 1279 return(str); 1280} 1281 1282i_uptime(bt, tod) 1283 1284struct timeval* bt; 1285time_t *tod; 1286 1287{ 1288 time_t uptime; 1289 int days, hrs, mins, secs; 1290 1291 if (bt->tv_sec != -1) { 1292 uptime = *tod - bt->tv_sec; 1293 days = uptime / 86400; 1294 uptime %= 86400; 1295 hrs = uptime / 3600; 1296 uptime %= 3600; 1297 mins = uptime / 60; 1298 secs = uptime % 60; 1299 1300 /* 1301 * Display the uptime. 1302 */ 1303 1304 if (smart_terminal) 1305 { 1306 Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); 1307 } 1308 else 1309 { 1310 fputs(" ", stdout); 1311 } 1312 printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); 1313 } 1314} 1315