1 /* 2 * Copyright (c) 1984 through 2008, William LeFebvre 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * * Neither the name of William LeFebvre nor the names of other 17 * contributors may be used to endorse or promote products derived from 18 * this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Top users/processes display for Unix 35 * Version 3 36 */ 37 38 /* 39 * This file contains the routines that display information on the screen. 40 * Each section of the screen has two routines: one for initially writing 41 * all constant and dynamic text, and one for only updating the text that 42 * changes. The prefix "i_" is used on all the "initial" routines and the 43 * prefix "u_" is used for all the "updating" routines. 44 * 45 * ASSUMPTIONS: 46 * None of the "i_" routines use any of the termcap capabilities. 47 * In this way, those routines can be safely used on terminals that 48 * have minimal (or nonexistant) terminal capabilities. 49 * 50 * The routines should be called in this order: *_loadave, *_uptime, 51 * i_timeofday, *_procstates, *_cpustates, *_memory, *_swap, 52 * *_message, *_header, *_process, *_endscreen. 53 */ 54 55 #include "os.h" 56 #include <ctype.h> 57 #include <stdarg.h> 58 #include <sys/types.h> 59 #include <sys/uio.h> 60 #include <unistd.h> 61 62 #include "top.h" 63 #include "machine.h" 64 #include "screen.h" /* interface to screen package */ 65 #include "layout.h" /* defines for screen position layout */ 66 #include "display.h" 67 #include "boolean.h" 68 #include "utils.h" 69 70 #ifdef ENABLE_COLOR 71 #include "color.h" 72 #endif 73 74 #define CURSOR_COST 8 75 76 #define MESSAGE_DISPLAY_TIME 5 77 78 /* imported from screen.c */ 79 extern int overstrike; 80 81 static int lmpid = -1; 82 static int display_width = MAX_COLS; 83 84 /* cursor positions of key points on the screen are maintained here */ 85 /* layout.h has static definitions, but we may change our minds on some 86 of the positions as we make decisions about what needs to be displayed */ 87 88 static int x_lastpid = X_LASTPID; 89 static int y_lastpid = Y_LASTPID; 90 static int x_loadave = X_LOADAVE; 91 static int y_loadave = Y_LOADAVE; 92 static int x_minibar = X_MINIBAR; 93 static int y_minibar = Y_MINIBAR; 94 static int x_uptime = X_UPTIME; 95 static int y_uptime = Y_UPTIME; 96 static int x_procstate = X_PROCSTATE; 97 static int y_procstate = Y_PROCSTATE; 98 static int x_cpustates = X_CPUSTATES; 99 static int y_cpustates = Y_CPUSTATES; 100 static int x_kernel = X_KERNEL; 101 static int y_kernel = Y_KERNEL; 102 static int x_mem = X_MEM; 103 static int y_mem = Y_MEM; 104 static int x_swap = X_SWAP; 105 static int y_swap = Y_SWAP; 106 static int y_message = Y_MESSAGE; 107 static int x_header = X_HEADER; 108 static int y_header = Y_HEADER; 109 static int x_idlecursor = X_IDLECURSOR; 110 static int y_idlecursor = Y_IDLECURSOR; 111 static int y_procs = Y_PROCS; 112 113 /* buffer and colormask that describes the content of the screen */ 114 /* these are singly dimensioned arrays -- the row boundaries are 115 determined on the fly. 116 */ 117 static char *screenbuf = NULL; 118 static char *colorbuf = NULL; 119 static char scratchbuf[MAX_COLS]; 120 static int bufsize = 0; 121 122 /* lineindex tells us where the beginning of a line is in the buffer */ 123 #define lineindex(l) ((l)*MAX_COLS) 124 125 /* screen's cursor */ 126 static int curr_x, curr_y; 127 static int curr_color; 128 129 /* virtual cursor */ 130 static int virt_x, virt_y; 131 132 static char **procstate_names; 133 static char **cpustate_names; 134 static char **memory_names; 135 static char **swap_names; 136 static char **kernel_names; 137 138 static int num_procstates; 139 static int num_cpustates; 140 static int num_memory; 141 static int num_swap; 142 static int num_kernel; 143 144 static int *lprocstates; 145 static int *lcpustates; 146 147 static int *cpustate_columns; 148 static int cpustate_total_length; 149 150 static int header_status = Yes; 151 152 /* pending messages are stored in a circular buffer, where message_first 153 is the next one to display, and message_last is the last one 154 in the buffer. Counters wrap around at MAX_MESSAGES. The buffer is 155 empty when message_first == message_last and full when 156 message_last + 1 == message_first. The pointer message_current holds 157 the message currently being displayed, or "" if there is none. 158 */ 159 #define MAX_MESSAGES 16 160 static char *message_buf[MAX_MESSAGES]; 161 static int message_first = 0; 162 static int message_last = 0; 163 static struct timeval message_time = {0, 0}; 164 static char *message_current = NULL; 165 static int message_length = 0; 166 static int message_hold = 1; 167 static int message_barrier = No; 168 169 #ifdef ENABLE_COLOR 170 static int load_cidx[3]; 171 static int header_cidx; 172 static int *cpustate_cidx; 173 static int *memory_cidx; 174 static int *swap_cidx; 175 static int *kernel_cidx; 176 #else 177 #define memory_cidx NULL 178 #define swap_cidx NULL 179 #define kernel_cidx NULL 180 #endif 181 182 183 /* internal support routines */ 184 185 /* 186 * static int string_count(char **pp) 187 * 188 * Pointer "pp" points to an array of string pointers, which is 189 * terminated by a NULL. Return the number of string pointers in 190 * this array. 191 */ 192 193 static int 194 string_count(char **pp) 195 196 { 197 register int cnt = 0; 198 199 if (pp != NULL) 200 { 201 while (*pp++ != NULL) 202 { 203 cnt++; 204 } 205 } 206 return(cnt); 207 } 208 209 void 210 display_clear() 211 212 { 213 dprintf("display_clear\n"); 214 screen_clear(); 215 memzero(screenbuf, bufsize); 216 memzero(colorbuf, bufsize); 217 curr_x = curr_y = 0; 218 } 219 220 /* 221 * void display_move(int x, int y) 222 * 223 * Efficiently move the cursor to x, y. This assumes the cursor is 224 * currently located at curr_x, curr_y, and will only use cursor 225 * addressing when it is less expensive than overstriking what's 226 * already on the screen. 227 */ 228 229 void 230 display_move(int x, int y) 231 232 { 233 char buff[128]; 234 char *p; 235 char *bufp; 236 char *colorp; 237 int cnt = 0; 238 int color = curr_color; 239 240 dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y); 241 242 /* are we in a position to do this without cursor addressing? */ 243 if (curr_y < y || (curr_y == y && curr_x <= x)) 244 { 245 /* start buffering up what it would take to move there by rewriting 246 what's on the screen */ 247 cnt = CURSOR_COST; 248 p = buff; 249 250 /* one newline for every line */ 251 while (cnt > 0 && curr_y < y) 252 { 253 #ifdef ENABLE_COLOR 254 if (color != 0) 255 { 256 p = strcpyend(p, color_setstr(0)); 257 color = 0; 258 cnt -= 5; 259 } 260 #endif 261 *p++ = '\n'; 262 curr_y++; 263 curr_x = 0; 264 cnt--; 265 } 266 267 /* write whats in the screenbuf */ 268 bufp = &screenbuf[lineindex(curr_y) + curr_x]; 269 colorp = &colorbuf[lineindex(curr_y) + curr_x]; 270 while (cnt > 0 && curr_x < x) 271 { 272 #ifdef ENABLE_COLOR 273 if (color != *colorp) 274 { 275 color = *colorp; 276 p = strcpyend(p, color_setstr(color)); 277 cnt -= 5; 278 } 279 #endif 280 if ((*p = *bufp) == '\0') 281 { 282 /* somwhere on screen we haven't been before */ 283 *p = *bufp = ' '; 284 } 285 p++; 286 bufp++; 287 colorp++; 288 curr_x++; 289 cnt--; 290 } 291 } 292 293 /* move the cursor */ 294 if (cnt > 0) 295 { 296 /* screen rewrite is cheaper */ 297 *p = '\0'; 298 fputs(buff, stdout); 299 curr_color = color; 300 } 301 else 302 { 303 screen_move(x, y); 304 } 305 306 /* update our position */ 307 curr_x = x; 308 curr_y = y; 309 } 310 311 /* 312 * display_write(int x, int y, int newcolor, int eol, char *new) 313 * 314 * Optimized write to the display. This writes characters to the 315 * screen in a way that optimizes the number of characters actually 316 * sent, by comparing what is being written to what is already on 317 * the screen (according to screenbuf and colorbuf). The string to 318 * write is "new", the first character of "new" should appear at 319 * screen position x, y. If x is -1 then "new" begins wherever the 320 * cursor is currently positioned. The string is written with color 321 * "newcolor". If "eol" is true then the remainder of the line is 322 * cleared. It is expected that "new" will have no newlines and no 323 * escape sequences. 324 */ 325 326 void 327 display_write(int x, int y, int newcolor, int eol, char *new) 328 329 { 330 char *bufp; 331 char *colorp; 332 int ch; 333 int diff; 334 335 dprintf("display_write(%d, %d, %d, %d, \"%s\")\n", 336 x, y, newcolor, eol, new); 337 338 /* dumb terminal handling here */ 339 if (!smart_terminal) 340 { 341 if (x != -1) 342 { 343 /* make sure we are on the right line */ 344 while (curr_y < y) 345 { 346 putchar('\n'); 347 curr_y++; 348 curr_x = 0; 349 } 350 351 /* make sure we are on the right column */ 352 while (curr_x < x) 353 { 354 putchar(' '); 355 curr_x++; 356 } 357 } 358 359 /* write */ 360 fputs(new, stdout); 361 curr_x += strlen(new); 362 363 return; 364 } 365 366 /* adjust for "here" */ 367 if (x == -1) 368 { 369 x = virt_x; 370 y = virt_y; 371 } 372 else 373 { 374 virt_x = x; 375 virt_y = y; 376 } 377 378 /* a pointer to where we start */ 379 bufp = &screenbuf[lineindex(y) + x]; 380 colorp = &colorbuf[lineindex(y) + x]; 381 382 /* main loop */ 383 while ((ch = *new++) != '\0') 384 { 385 /* if either character or color are different, an update is needed */ 386 /* but only when the screen is wide enough */ 387 if (y < (smart_terminal ? screen_length : Largest) && x < display_width && 388 (ch != *bufp || newcolor != *colorp)) 389 { 390 /* check cursor */ 391 if (y != curr_y || x != curr_x) 392 { 393 /* have to move the cursor */ 394 display_move(x, y); 395 } 396 397 /* write character */ 398 #ifdef ENABLE_COLOR 399 if (curr_color != newcolor) 400 { 401 fputs(color_setstr(newcolor), stdout); 402 curr_color = newcolor; 403 } 404 #endif 405 putchar(ch); 406 *bufp = ch; 407 *colorp = curr_color; 408 curr_x++; 409 } 410 411 /* move */ 412 x++; 413 virt_x++; 414 bufp++; 415 colorp++; 416 } 417 418 /* eol handling */ 419 if (eol && *bufp != '\0') 420 { 421 dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp); 422 /* make sure we are color 0 */ 423 #ifdef ENABLE_COLOR 424 if (curr_color != 0) 425 { 426 fputs(color_setstr(0), stdout); 427 curr_color = 0; 428 } 429 #endif 430 431 /* make sure we are at the end */ 432 if (x != curr_x || y != curr_y) 433 { 434 screen_move(x, y); 435 curr_x = x; 436 curr_y = y; 437 } 438 439 /* clear to end */ 440 screen_cleareol(strlen(bufp)); 441 442 /* clear out whats left of this line's buffer */ 443 diff = display_width - x; 444 if (diff > 0) 445 { 446 memzero(bufp, diff); 447 memzero(colorp, diff); 448 } 449 } 450 } 451 452 void 453 display_fmt(int x, int y, int newcolor, int eol, char *fmt, ...) 454 455 { 456 va_list argp; 457 458 va_start(argp, fmt); 459 460 vsnprintf(scratchbuf, MAX_COLS, fmt, argp); 461 display_write(x, y, newcolor, eol, scratchbuf); 462 } 463 464 void 465 display_cte() 466 467 { 468 int len; 469 int y; 470 char *p; 471 int need_clear = 0; 472 473 /* is there anything out there that needs to be cleared? */ 474 p = &screenbuf[lineindex(virt_y) + virt_x]; 475 if (*p != '\0') 476 { 477 need_clear = 1; 478 } 479 else 480 { 481 /* this line is clear, what about the rest? */ 482 y = virt_y; 483 while (++y < screen_length) 484 { 485 if (screenbuf[lineindex(y)] != '\0') 486 { 487 need_clear = 1; 488 break; 489 } 490 } 491 } 492 493 if (need_clear) 494 { 495 dprintf("display_cte: clearing\n"); 496 497 /* we will need this later */ 498 len = lineindex(virt_y) + virt_x; 499 500 /* move to x and y, then clear to end */ 501 display_move(virt_x, virt_y); 502 if (!screen_cte()) 503 { 504 /* screen has no clear to end, so do it by hand */ 505 p = &screenbuf[len]; 506 len = strlen(p); 507 if (len > 0) 508 { 509 screen_cleareol(len); 510 } 511 while (++virt_y < screen_length) 512 { 513 display_move(0, virt_y); 514 p = &screenbuf[lineindex(virt_y)]; 515 len = strlen(p); 516 if (len > 0) 517 { 518 screen_cleareol(len); 519 } 520 } 521 } 522 523 /* clear the screenbuf */ 524 memzero(&screenbuf[len], bufsize - len); 525 memzero(&colorbuf[len], bufsize - len); 526 } 527 } 528 529 static void 530 summary_format(int x, int y, int *numbers, char **names, int *cidx) 531 532 { 533 register int num; 534 register char *thisname; 535 register char *lastname = NULL; 536 register int color; 537 538 /* format each number followed by its string */ 539 while ((thisname = *names++) != NULL) 540 { 541 /* get the number to format */ 542 num = *numbers++; 543 color = 0; 544 545 /* display only non-zero numbers */ 546 if (num != 0) 547 { 548 /* write the previous name */ 549 if (lastname != NULL) 550 { 551 display_write(-1, -1, 0, 0, lastname); 552 } 553 554 #ifdef ENABLE_COLOR 555 if (cidx != NULL) 556 { 557 /* choose a color */ 558 color = color_test(*cidx++, num); 559 } 560 #endif 561 562 /* write this number if positive */ 563 if (num > 0) 564 { 565 display_write(x, y, color, 0, itoa(num)); 566 } 567 568 /* defer writing this name */ 569 lastname = thisname; 570 571 /* next iteration will not start at x, y */ 572 x = y = -1; 573 } 574 } 575 576 /* if the last string has a separator on the end, it has to be 577 written with care */ 578 if (lastname != NULL) 579 { 580 if ((num = strlen(lastname)) > 1 && 581 lastname[num-2] == ',' && lastname[num-1] == ' ') 582 { 583 display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname); 584 } 585 else 586 { 587 display_write(-1, -1, 0, 1, lastname); 588 } 589 } 590 } 591 592 static void 593 summary_format_memory(int x, int y, long *numbers, char **names, int *cidx) 594 595 { 596 register long num; 597 register int color; 598 register char *thisname; 599 register char *lastname = NULL; 600 601 /* format each number followed by its string */ 602 while ((thisname = *names++) != NULL) 603 { 604 /* get the number to format */ 605 num = *numbers++; 606 color = 0; 607 608 /* display only non-zero numbers */ 609 if (num != 0) 610 { 611 /* write the previous name */ 612 if (lastname != NULL) 613 { 614 display_write(-1, -1, 0, 0, lastname); 615 } 616 617 /* defer writing this name */ 618 lastname = thisname; 619 620 #ifdef ENABLE_COLOR 621 /* choose a color */ 622 color = color_test(*cidx++, num); 623 #endif 624 625 /* is this number in kilobytes? */ 626 if (thisname[0] == 'K') 627 { 628 display_write(x, y, color, 0, format_k(num)); 629 lastname++; 630 } 631 else 632 { 633 display_write(x, y, color, 0, itoa((int)num)); 634 } 635 636 /* next iteration will not start at x, y */ 637 x = y = -1; 638 } 639 } 640 641 /* if the last string has a separator on the end, it has to be 642 written with care */ 643 if (lastname != NULL) 644 { 645 if ((num = strlen(lastname)) > 1 && 646 lastname[num-2] == ',' && lastname[num-1] == ' ') 647 { 648 display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname); 649 } 650 else 651 { 652 display_write(-1, -1, 0, 1, lastname); 653 } 654 } 655 } 656 657 /* 658 * int display_resize() 659 * 660 * Reallocate buffer space needed by the display package to accomodate 661 * a new screen size. Must be called whenever the screen's size has 662 * changed. Returns the number of lines available for displaying 663 * processes or -1 if there was a problem allocating space. 664 */ 665 666 int 667 display_resize() 668 669 { 670 register int top_lines; 671 register int newsize; 672 673 /* calculate the current dimensions */ 674 /* if operating in "dumb" mode, we only need one line */ 675 top_lines = smart_terminal ? screen_length : 1; 676 677 /* we don't want more than MAX_COLS columns, since the machine-dependent 678 modules make static allocations based on MAX_COLS and we don't want 679 to run off the end of their buffers */ 680 display_width = screen_width; 681 if (display_width >= MAX_COLS) 682 { 683 display_width = MAX_COLS - 1; 684 } 685 686 /* see how much space we need */ 687 newsize = top_lines * (MAX_COLS + 1); 688 689 /* reallocate only if we need more than we already have */ 690 if (newsize > bufsize) 691 { 692 /* deallocate any previous buffer that may have been there */ 693 if (screenbuf != NULL) 694 { 695 free(screenbuf); 696 } 697 if (colorbuf != NULL) 698 { 699 free(colorbuf); 700 } 701 702 /* allocate space for the screen and color buffers */ 703 bufsize = newsize; 704 screenbuf = (char *)calloc(bufsize, sizeof(char)); 705 colorbuf = (char *)calloc(bufsize, sizeof(char)); 706 if (screenbuf == NULL || colorbuf == NULL) 707 { 708 /* oops! */ 709 return(-1); 710 } 711 } 712 else 713 { 714 /* just clear them out */ 715 memzero(screenbuf, bufsize); 716 memzero(colorbuf, bufsize); 717 } 718 719 /* adjust total lines on screen to lines available for procs */ 720 if (top_lines > y_procs) 721 top_lines -= y_procs; 722 else 723 top_lines = 0; 724 725 /* return number of lines available */ 726 /* for dumb terminals, pretend like we can show any amount */ 727 return(smart_terminal ? top_lines : Largest); 728 } 729 730 int 731 display_lines() 732 733 { 734 return(smart_terminal ? screen_length : Largest); 735 } 736 737 int 738 display_columns() 739 740 { 741 return(display_width); 742 } 743 744 /* 745 * int display_init(struct statics *statics) 746 * 747 * Initialize the display system based on information in the statics 748 * structure. Returns the number of lines available for displaying 749 * processes or -1 if there was an error. 750 */ 751 752 int 753 display_init(struct statics *statics) 754 755 { 756 register int top_lines; 757 register char **pp; 758 register char *p; 759 register int *ip; 760 register int i; 761 762 /* certain things may influence the screen layout, 763 so look at those first */ 764 765 /* More than one core will shif the parts of the display down */ 766 if (enable_ncpus != 0 && n_cpus > 1) 767 { 768 /* adjust screen placements */ 769 y_mem = y_mem + n_cpus -1; 770 y_swap = y_swap + n_cpus -1; 771 y_message = y_message + n_cpus -1; 772 y_header = y_header + n_cpus -1; 773 y_idlecursor = y_idlecursor + n_cpus -1; 774 y_procs = y_procs + n_cpus -1; 775 } 776 777 /* a kernel line shifts parts of the display down */ 778 kernel_names = statics->kernel_names; 779 if ((num_kernel = string_count(kernel_names)) > 0) 780 { 781 /* adjust screen placements */ 782 y_mem++; 783 y_swap++; 784 y_message++; 785 y_header++; 786 y_idlecursor++; 787 y_procs++; 788 } 789 790 /* a swap line shifts parts of the display down one */ 791 swap_names = statics->swap_names; 792 if ((num_swap = string_count(swap_names)) > 0) 793 { 794 /* adjust screen placements */ 795 y_message++; 796 y_header++; 797 y_idlecursor++; 798 y_procs++; 799 } 800 801 /* call resize to do the dirty work */ 802 top_lines = display_resize(); 803 804 /* 805 * save pointers and allocate space for names. Even if top_lines <= -1 806 * the code will dereference many of these pointers and arrays. 807 */ 808 procstate_names = statics->procstate_names; 809 num_procstates = string_count(procstate_names); 810 811 lprocstates = (int *)calloc(num_procstates, sizeof(int)); 812 813 cpustate_names = statics->cpustate_names; 814 num_cpustates = string_count(cpustate_names); 815 lcpustates = (int *)calloc(num_cpustates, sizeof(int)); 816 cpustate_columns = (int *)calloc(num_cpustates, sizeof(int)); 817 memory_names = statics->memory_names; 818 num_memory = string_count(memory_names); 819 820 /* calculate starting columns where needed */ 821 cpustate_total_length = 0; 822 pp = cpustate_names; 823 ip = cpustate_columns; 824 while (*pp != NULL) 825 { 826 *ip++ = cpustate_total_length; 827 if ((i = strlen(*pp++)) > 0) 828 { 829 cpustate_total_length += i + 8; 830 } 831 } 832 833 #ifdef ENABLE_COLOR 834 /* set up color tags for loadavg */ 835 load_cidx[0] = color_tag("1min"); 836 load_cidx[1] = color_tag("5min"); 837 load_cidx[2] = color_tag("15min"); 838 839 /* find header color */ 840 header_cidx = color_tag("header"); 841 842 /* color tags for cpu states */ 843 cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int)); 844 i = 0; 845 p = strcpyend(scratchbuf, "cpu."); 846 while (i < num_cpustates) 847 { 848 strcpy(p, cpustate_names[i]); 849 cpustate_cidx[i++] = color_tag(scratchbuf); 850 } 851 852 /* color tags for kernel */ 853 if (num_kernel > 0) 854 { 855 kernel_cidx = (int *)malloc(num_kernel * sizeof(int)); 856 i = 0; 857 p = strcpyend(scratchbuf, "kernel."); 858 while (i < num_kernel) 859 { 860 strcpy(p, homogenize(kernel_names[i]+1)); 861 kernel_cidx[i++] = color_tag(scratchbuf); 862 } 863 } 864 865 /* color tags for memory */ 866 memory_cidx = (int *)malloc(num_memory * sizeof(int)); 867 i = 0; 868 p = strcpyend(scratchbuf, "memory."); 869 while (i < num_memory) 870 { 871 strcpy(p, homogenize(memory_names[i]+1)); 872 memory_cidx[i++] = color_tag(scratchbuf); 873 } 874 875 /* color tags for swap */ 876 if (num_swap > 0) 877 { 878 swap_cidx = (int *)malloc(num_swap * sizeof(int)); 879 i = 0; 880 p = strcpyend(scratchbuf, "swap."); 881 while (i < num_swap) 882 { 883 strcpy(p, homogenize(swap_names[i]+1)); 884 swap_cidx[i++] = color_tag(scratchbuf); 885 } 886 } 887 #endif 888 889 /* return number of lines available (or error) */ 890 return(top_lines); 891 } 892 893 static void 894 pr_loadavg(double avg, int i) 895 896 { 897 int color = 0; 898 899 #ifdef ENABLE_COLOR 900 color = color_test(load_cidx[i], (int)(avg * 100)); 901 #endif 902 display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0, 903 avg < 10.0 ? " %5.2f" : " %5.1f", avg); 904 display_write(-1, -1, 0, 0, (i < 2 ? "," : ";")); 905 } 906 907 void 908 i_loadave(int mpid, double *avenrun) 909 910 { 911 register int i; 912 913 /* mpid == -1 implies this system doesn't have an _mpid */ 914 if (mpid != -1) 915 { 916 display_fmt(0, 0, 0, 0, 917 "last pid: %5d; load avg:", mpid); 918 x_loadave = X_LOADAVE; 919 } 920 else 921 { 922 display_write(0, 0, 0, 0, "load averages:"); 923 x_loadave = X_LOADAVE - X_LASTPIDWIDTH; 924 } 925 for (i = 0; i < 3; i++) 926 { 927 pr_loadavg(avenrun[i], i); 928 } 929 930 lmpid = mpid; 931 } 932 933 void 934 u_loadave(int mpid, double *avenrun) 935 936 { 937 register int i; 938 939 if (mpid != -1) 940 { 941 /* change screen only when value has really changed */ 942 if (mpid != lmpid) 943 { 944 display_fmt(x_lastpid, y_lastpid, 0, 0, 945 "%5d", mpid); 946 lmpid = mpid; 947 } 948 } 949 950 /* display new load averages */ 951 for (i = 0; i < 3; i++) 952 { 953 pr_loadavg(avenrun[i], i); 954 } 955 } 956 957 static char minibar_buffer[64]; 958 #define MINIBAR_WIDTH 20 959 960 void 961 i_minibar(int (*formatter)(char *, int)) 962 { 963 (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); 964 965 display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); 966 } 967 968 void 969 u_minibar(int (*formatter)(char *, int)) 970 { 971 (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); 972 973 display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); 974 } 975 976 static int uptime_days; 977 static int uptime_hours; 978 static int uptime_mins; 979 static int uptime_secs; 980 981 void 982 i_uptime(time_t *bt, time_t *tod) 983 984 { 985 time_t uptime; 986 987 if (*bt != -1) 988 { 989 uptime = *tod - *bt; 990 uptime += 30; 991 uptime_days = uptime / 86400; 992 uptime %= 86400; 993 uptime_hours = uptime / 3600; 994 uptime %= 3600; 995 uptime_mins = uptime / 60; 996 uptime_secs = uptime % 60; 997 998 /* 999 * Display the uptime. 1000 */ 1001 1002 display_fmt(x_uptime, y_uptime, 0, 0, 1003 " up %d+%02d:%02d:%02d", 1004 uptime_days, uptime_hours, uptime_mins, uptime_secs); 1005 } 1006 } 1007 1008 void 1009 u_uptime(time_t *bt, time_t *tod) 1010 1011 { 1012 i_uptime(bt, tod); 1013 } 1014 1015 1016 void 1017 i_timeofday(time_t *tod) 1018 1019 { 1020 /* 1021 * Display the current time. 1022 * "ctime" always returns a string that looks like this: 1023 * 1024 * Sun Sep 16 01:03:52 1973 1025 * 012345678901234567890123 1026 * 1 2 1027 * 1028 * We want indices 11 thru 18 (length 8). 1029 */ 1030 1031 int x; 1032 1033 /* where on the screen do we start? */ 1034 x = (smart_terminal ? screen_width : 79) - 8; 1035 1036 /* but don't bump in to uptime */ 1037 if (x < x_uptime + 19) 1038 { 1039 x = x_uptime + 19; 1040 } 1041 1042 /* display it */ 1043 display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11])); 1044 } 1045 1046 static int ltotal = 0; 1047 static int lthreads = 0; 1048 1049 /* 1050 * *_procstates(total, brkdn, names) - print the process summary line 1051 */ 1052 1053 1054 void 1055 i_procstates(int total, int *brkdn, int threads) 1056 1057 { 1058 /* write current number of processes and remember the value */ 1059 display_fmt(0, y_procstate, 0, 0, 1060 "%d %s: ", total, threads ? "threads" : "processes"); 1061 ltotal = total; 1062 1063 /* remember where the summary starts */ 1064 x_procstate = virt_x; 1065 1066 if (total > 0) 1067 { 1068 /* format and print the process state summary */ 1069 summary_format(-1, -1, brkdn, procstate_names, NULL); 1070 1071 /* save the numbers for next time */ 1072 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 1073 lthreads = threads; 1074 } 1075 } 1076 1077 void 1078 u_procstates(int total, int *brkdn, int threads) 1079 1080 { 1081 /* if threads state has changed, do a full update */ 1082 if (lthreads != threads) 1083 { 1084 i_procstates(total, brkdn, threads); 1085 return; 1086 } 1087 1088 /* update number of processes only if it has changed */ 1089 if (ltotal != total) 1090 { 1091 display_fmt(0, y_procstate, 0, 0, 1092 "%d", total); 1093 1094 /* if number of digits differs, rewrite the label */ 1095 if (digits(total) != digits(ltotal)) 1096 { 1097 display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes"); 1098 x_procstate = virt_x; 1099 } 1100 1101 /* save new total */ 1102 ltotal = total; 1103 } 1104 1105 /* see if any of the state numbers has changed */ 1106 if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) 1107 { 1108 /* format and update the line */ 1109 summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL); 1110 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 1111 } 1112 } 1113 1114 /* 1115 * *_cpustates(states, names) - print the cpu state percentages 1116 */ 1117 1118 /* cpustates_tag() calculates the correct tag to use to label the line */ 1119 1120 char * 1121 cpustates_tag() 1122 1123 { 1124 register char *use; 1125 1126 static char *short_tag = "CPU: "; 1127 static char *long_tag = "CPU states: "; 1128 1129 /* if length + strlen(long_tag) >= screen_width, then we have to 1130 use the shorter tag (we subtract 2 to account for ": ") */ 1131 if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width) 1132 { 1133 use = short_tag; 1134 } 1135 else 1136 { 1137 use = long_tag; 1138 } 1139 1140 /* set x_cpustates accordingly then return result */ 1141 x_cpustates = strlen(use); 1142 return(use); 1143 } 1144 1145 void 1146 i_cpustates(int *states) 1147 1148 { 1149 int value; 1150 char **names; 1151 char *thisname; 1152 int *colp; 1153 int color = 0; 1154 int cpu; 1155 #ifdef ENABLE_COLOR 1156 int *cidx = cpustate_cidx; 1157 #endif 1158 1159 /* initialize */ 1160 names = cpustate_names; 1161 colp = cpustate_columns; 1162 1163 /* print tag */ 1164 if (enable_ncpus !=0 && n_cpus > 1) { 1165 for (cpu = 0; cpu < n_cpus; ++cpu) { 1166 int y_pos = y_cpustates; 1167 y_pos = y_pos + cpu; 1168 colp = cpustate_columns; 1169 names = cpustate_names; 1170 display_write(0, y_cpustates+cpu, 0, 0, cpustates_tag()); 1171 1172 /* now walk thru the names and print the line */ 1173 while ((thisname = *names++) != NULL) { 1174 if (*thisname != '\0') { 1175 /* retrieve the value and remember it */ 1176 value = *states; 1177 1178 #ifdef ENABLE_COLOR 1179 /* determine color number to use */ 1180 color = color_test(*cidx++, value/10); 1181 #endif 1182 /* if percentage is >= 1000, print it as 100% */ 1183 display_fmt(x_cpustates + *colp, y_pos, 1184 color, 0, 1185 (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), 1186 ((float)value)/10., 1187 thisname, 1188 *names != NULL ? ", " : ""); 1189 1190 } 1191 /* increment */ 1192 colp++; 1193 states++; 1194 } 1195 /* copy over values into "last" array */ 1196 memcpy(lcpustates, states, num_cpustates * sizeof(int)); 1197 } 1198 } else { 1199 display_write(0, y_cpustates, 0, 0, cpustates_tag()); 1200 1201 /* now walk thru the names and print the line */ 1202 while ((thisname = *names++) != NULL) 1203 { 1204 if (*thisname != '\0') 1205 { 1206 /* retrieve the value and remember it */ 1207 value = *states; 1208 1209 #ifdef ENABLE_COLOR 1210 /* determine color number to use */ 1211 color = color_test(*cidx++, value/10); 1212 #endif 1213 1214 /* if percentage is >= 1000, print it as 100% */ 1215 display_fmt(x_cpustates + *colp, y_cpustates, 1216 color, 0, 1217 (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), 1218 ((float)value)/10., 1219 thisname, 1220 *names != NULL ? ", " : ""); 1221 1222 } 1223 /* increment */ 1224 colp++; 1225 states++; 1226 } 1227 1228 /* copy over values into "last" array */ 1229 memcpy(lcpustates, states, num_cpustates * sizeof(int)); 1230 } 1231 1232 } 1233 1234 void 1235 u_cpustates(int *states) 1236 1237 { 1238 int value; 1239 char **names = cpustate_names; 1240 char *thisname; 1241 int *lp; 1242 int *colp; 1243 int color = 0; 1244 int cpu; 1245 #ifdef ENABLE_COLOR 1246 int *cidx = cpustate_cidx; 1247 #endif 1248 1249 1250 if (enable_ncpus != 0 && n_cpus > 1 ) { 1251 for (cpu = 0; cpu < n_cpus; ++cpu) { 1252 lp = lcpustates; 1253 int y_pos = y_cpustates; 1254 y_pos = y_pos + cpu; 1255 colp = cpustate_columns; 1256 char **names = cpustate_names; 1257 /* we could be much more optimal about this */ 1258 while ((thisname = *names++) != NULL) { 1259 if (*thisname != '\0') { 1260 /* yes, change it */ 1261 /* retrieve value and remember it */ 1262 value = *states; 1263 1264 #ifdef ENABLE_COLOR 1265 /* determine color number to use */ 1266 color = color_test(*cidx, value/10); 1267 #endif 1268 /* if percentage is >= 1000, print it as 100% */ 1269 display_fmt(x_cpustates + *colp, y_pos, color, 0, 1270 (value >= 1000 ? "%4.0f" : "%4.1f"), 1271 ((double)value)/10.); 1272 1273 #ifdef ENABLE_COLOR 1274 cidx++; 1275 #endif 1276 } 1277 /* increment and move on */ 1278 lp++; 1279 states++; 1280 colp++; 1281 } 1282 } 1283 } else { 1284 lp = lcpustates; 1285 colp = cpustate_columns; 1286 1287 /* we could be much more optimal about this */ 1288 while ((thisname = *names++) != NULL) 1289 { 1290 if (*thisname != '\0') 1291 { 1292 /* did the value change since last time? */ 1293 if (*lp != *states) 1294 { 1295 /* yes, change it */ 1296 /* retrieve value and remember it */ 1297 value = *states; 1298 1299 #ifdef ENABLE_COLOR 1300 /* determine color number to use */ 1301 color = color_test(*cidx, value/10); 1302 #endif 1303 1304 /* if percentage is >= 1000, print it as 100% */ 1305 display_fmt(x_cpustates + *colp, y_cpustates, color, 0, 1306 (value >= 1000 ? "%4.0f" : "%4.1f"), 1307 ((double)value)/10.); 1308 1309 /* remember it for next time */ 1310 *lp = value; 1311 } 1312 #ifdef ENABLE_COLOR 1313 cidx++; 1314 #endif 1315 } 1316 1317 /* increment and move on */ 1318 lp++; 1319 states++; 1320 colp++; 1321 } 1322 } 1323 } 1324 1325 void 1326 z_cpustates() 1327 1328 { 1329 register int i = 0; 1330 register char **names = cpustate_names; 1331 register char *thisname; 1332 register int *lp; 1333 int cpu; 1334 1335 /* print tag */ 1336 if (enable_ncpus != 0 && n_cpus > 1) { 1337 for (cpu = 0; cpu < n_cpus; ++cpu) { 1338 display_write(0, y_cpustates + cpu, 0, 0, cpustates_tag()); 1339 char **names = cpustate_names; 1340 i = 0; 1341 while ((thisname = *names++) != NULL) { 1342 if (*thisname != '\0') { 1343 display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ", 1344 thisname); 1345 } 1346 } 1347 /* fill the "last" array with all -1s, to insure correct updating */ 1348 lp = lcpustates; 1349 i = num_cpustates; 1350 while (--i >= 0) { 1351 *lp++ = -1; 1352 } 1353 } 1354 } else { 1355 display_write(0, y_cpustates, 0, 0, cpustates_tag()); 1356 1357 while ((thisname = *names++) != NULL) 1358 { 1359 if (*thisname != '\0') 1360 { 1361 display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ", 1362 thisname); 1363 } 1364 } 1365 1366 /* fill the "last" array with all -1s, to insure correct updating */ 1367 lp = lcpustates; 1368 i = num_cpustates; 1369 while (--i >= 0) 1370 { 1371 *lp++ = -1; 1372 } 1373 } 1374 } 1375 1376 /* 1377 * *_kernel(stats) - print "Kernel: " followed by the kernel summary string 1378 * 1379 * Assumptions: cursor is on "lastline", the previous line 1380 */ 1381 1382 void 1383 i_kernel(int *stats) 1384 1385 { 1386 if (num_kernel > 0) 1387 { 1388 display_write(0, y_kernel, 0, 0, "Kernel: "); 1389 1390 /* format and print the kernel summary */ 1391 summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); 1392 } 1393 } 1394 1395 void 1396 u_kernel(int *stats) 1397 1398 { 1399 if (num_kernel > 0) 1400 { 1401 /* format the new line */ 1402 summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); 1403 } 1404 } 1405 1406 /* 1407 * *_memory(stats) - print "Memory: " followed by the memory summary string 1408 * 1409 * Assumptions: cursor is on "lastline", the previous line 1410 */ 1411 1412 void 1413 i_memory(long *stats) 1414 1415 { 1416 display_write(0, y_mem, 0, 0, "Memory: "); 1417 1418 /* format and print the memory summary */ 1419 summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); 1420 } 1421 1422 void 1423 u_memory(long *stats) 1424 1425 { 1426 /* format the new line */ 1427 summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); 1428 } 1429 1430 /* 1431 * *_swap(stats) - print "Swap: " followed by the swap summary string 1432 * 1433 * Assumptions: cursor is on "lastline", the previous line 1434 * 1435 * These functions only print something when num_swap > 0 1436 */ 1437 1438 void 1439 i_swap(long *stats) 1440 1441 { 1442 if (num_swap > 0) 1443 { 1444 /* print the tag */ 1445 display_write(0, y_swap, 0, 0, "Swap: "); 1446 1447 /* format and print the swap summary */ 1448 summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); 1449 } 1450 } 1451 1452 void 1453 u_swap(long *stats) 1454 1455 { 1456 if (num_swap > 0) 1457 { 1458 /* format the new line */ 1459 summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); 1460 } 1461 } 1462 1463 /* 1464 * *_message() - print the next pending message line, or erase the one 1465 * that is there. 1466 * 1467 * Note that u_message is (currently) the same as i_message. 1468 * 1469 * Assumptions: lastline is consistent 1470 */ 1471 1472 /* 1473 * i_message is funny because it gets its message asynchronously (with 1474 * respect to screen updates). Messages are taken out of the 1475 * circular message_buf and displayed one at a time. 1476 */ 1477 1478 void 1479 i_message(struct timeval *now) 1480 1481 { 1482 struct timeval my_now; 1483 int i = 0; 1484 1485 dprintf("i_message(%08x)\n", now); 1486 1487 /* if now is NULL we have to get it ourselves */ 1488 if (now == NULL) 1489 { 1490 time_get(&my_now); 1491 now = &my_now; 1492 } 1493 1494 /* now that we have been called, messages no longer need to be held */ 1495 message_hold = 0; 1496 1497 dprintf("i_message: now %d, message_time %d\n", 1498 now->tv_sec, message_time.tv_sec); 1499 1500 if (smart_terminal) 1501 { 1502 /* is it time to change the message? */ 1503 if (timercmp(now, &message_time, > )) 1504 { 1505 /* yes, free the current message */ 1506 dprintf("i_message: timer expired\n"); 1507 if (message_current != NULL) 1508 { 1509 free(message_current); 1510 message_current = NULL; 1511 } 1512 1513 /* is there a new message to be displayed? */ 1514 if (message_first != message_last) 1515 { 1516 /* move index to next message */ 1517 if (++message_first == MAX_MESSAGES) message_first = 0; 1518 1519 /* make the next message the current one */ 1520 message_current = message_buf[message_first]; 1521 1522 /* show it */ 1523 dprintf("i_message: showing \"%s\"\n", message_current); 1524 display_move(0, y_message); 1525 screen_standout(message_current); 1526 i = strlen(message_current); 1527 1528 /* set the expiration timer */ 1529 message_time = *now; 1530 message_time.tv_sec += MESSAGE_DISPLAY_TIME; 1531 1532 /* clear the rest of the line */ 1533 screen_cleareol(message_length - i); 1534 putchar('\r'); 1535 message_length = i; 1536 } 1537 else 1538 { 1539 /* just clear what was there before, if anything */ 1540 if (message_length > 0) 1541 { 1542 display_move(0, y_message); 1543 screen_cleareol(message_length); 1544 putchar('\r'); 1545 message_length = 0; 1546 } 1547 } 1548 } 1549 } 1550 } 1551 1552 void 1553 u_message(struct timeval *now) 1554 1555 { 1556 i_message(now); 1557 } 1558 1559 static int header_length; 1560 1561 /* 1562 * *_header(text) - print the header for the process area 1563 * 1564 * Assumptions: cursor is on the previous line and lastline is consistent 1565 */ 1566 1567 void 1568 i_header(char *text) 1569 1570 { 1571 int header_color = 0; 1572 1573 #ifdef ENABLE_COLOR 1574 header_color = color_test(header_cidx, 0); 1575 #endif 1576 header_length = strlen(text); 1577 if (header_status) 1578 { 1579 display_write(x_header, y_header, header_color, 1, text); 1580 } 1581 } 1582 1583 /*ARGSUSED*/ 1584 void 1585 u_header(char *text) 1586 1587 { 1588 int header_color = 0; 1589 1590 #ifdef ENABLE_COLOR 1591 header_color = color_test(header_cidx, 0); 1592 #endif 1593 display_write(x_header, y_header, header_color, 1, 1594 header_status ? text : ""); 1595 } 1596 1597 /* 1598 * *_process(line, thisline) - print one process line 1599 * 1600 * Assumptions: lastline is consistent 1601 */ 1602 1603 void 1604 i_process(int line, char *thisline) 1605 1606 { 1607 /* truncate the line to conform to our current screen width */ 1608 thisline[display_width] = '\0'; 1609 1610 /* write the line out */ 1611 display_write(0, y_procs + line, 0, 1, thisline); 1612 } 1613 1614 void 1615 u_process(int line, char *new_line) 1616 1617 { 1618 i_process(line, new_line); 1619 } 1620 1621 void 1622 i_endscreen() 1623 1624 { 1625 if (smart_terminal) 1626 { 1627 /* move the cursor to a pleasant place */ 1628 display_move(x_idlecursor, y_idlecursor); 1629 } 1630 else 1631 { 1632 /* separate this display from the next with some vertical room */ 1633 fputs("\n\n", stdout); 1634 } 1635 fflush(stdout); 1636 } 1637 1638 void 1639 u_endscreen() 1640 1641 { 1642 if (smart_terminal) 1643 { 1644 /* clear-to-end the display */ 1645 display_cte(); 1646 1647 /* move the cursor to a pleasant place */ 1648 display_move(x_idlecursor, y_idlecursor); 1649 fflush(stdout); 1650 } 1651 else 1652 { 1653 /* separate this display from the next with some vertical room */ 1654 fputs("\n\n", stdout); 1655 } 1656 } 1657 1658 void 1659 display_header(int t) 1660 1661 { 1662 header_status = t != 0; 1663 } 1664 1665 void 1666 message_mark() 1667 1668 { 1669 message_barrier = Yes; 1670 } 1671 1672 void 1673 message_expire() 1674 1675 { 1676 message_time.tv_sec = 0; 1677 message_time.tv_usec = 0; 1678 } 1679 1680 void 1681 message_flush() 1682 1683 { 1684 message_first = message_last; 1685 message_time.tv_sec = 0; 1686 message_time.tv_usec = 0; 1687 } 1688 1689 /* 1690 * void new_message_v(char *msgfmt, va_list ap) 1691 * 1692 * Display a message in the message area. This function takes a va_list for 1693 * the arguments. Safe to call before display_init. This function only 1694 * queues a message for display, and allowed for multiple messages to be 1695 * queued. The i_message function drains the queue and actually writes the 1696 * messages on the display. 1697 */ 1698 1699 1700 void 1701 new_message_v(char *msgfmt, va_list ap) 1702 1703 { 1704 int i; 1705 int empty; 1706 char msg[MAX_COLS]; 1707 1708 /* if message_barrier is active, remove all pending messages */ 1709 if (message_barrier) 1710 { 1711 message_flush(); 1712 message_barrier = No; 1713 } 1714 1715 /* first, format the message */ 1716 (void) vsnprintf(msg, sizeof(msg), msgfmt, ap); 1717 1718 /* where in the buffer will it go? */ 1719 i = message_last + 1; 1720 if (i >= MAX_MESSAGES) i = 0; 1721 1722 /* make sure the buffer is not full */ 1723 if (i != message_first) 1724 { 1725 /* insert it in to message_buf */ 1726 message_buf[i] = strdup(msg); 1727 dprintf("new_message_v: new message inserted in slot %d\n", i); 1728 1729 /* remember if the buffer is empty and set the index */ 1730 empty = message_last == message_first; 1731 message_last = i; 1732 1733 /* is message_buf otherwise empty and have we started displaying? */ 1734 if (empty && !message_hold) 1735 { 1736 /* we can display the message now */ 1737 i_message(NULL); 1738 } 1739 } 1740 } 1741 1742 /* 1743 * void new_message(int type, char *msgfmt, ...) 1744 * 1745 * Display a message in the message area. It is safe to call this function 1746 * before display_init. Messages logged before the display is drawn will be 1747 * held and displayed later. 1748 */ 1749 1750 void 1751 new_message(char *msgfmt, ...) 1752 1753 { 1754 va_list ap; 1755 1756 va_start(ap, msgfmt); 1757 new_message_v(msgfmt, ap); 1758 va_end(ap); 1759 } 1760 1761 /* 1762 * void message_error(char *msgfmt, ...) 1763 * 1764 * Put an error message in the message area. It is safe to call this function 1765 * before display_init. Messages logged before the display is drawn will be 1766 * held and displayed later. 1767 */ 1768 1769 void 1770 message_error(char *msgfmt, ...) 1771 1772 { 1773 va_list ap; 1774 1775 va_start(ap, msgfmt); 1776 new_message_v(msgfmt, ap); 1777 fflush(stdout); 1778 va_end(ap); 1779 } 1780 1781 /* 1782 * void message_clear() 1783 * 1784 * Clear message area and flush all pending messages. 1785 */ 1786 1787 void 1788 message_clear() 1789 1790 { 1791 /* remove any existing message */ 1792 if (message_current != NULL) 1793 { 1794 display_move(0, y_message); 1795 screen_cleareol(message_length); 1796 free(message_current); 1797 message_current = NULL; 1798 } 1799 1800 /* flush all pending messages */ 1801 message_flush(); 1802 } 1803 1804 /* 1805 * void message_prompt_v(int so, char *msgfmt, va_list ap) 1806 * 1807 * Place a prompt in the message area. A prompt is different from a 1808 * message as follows: it is displayed immediately, overwriting any 1809 * message that may already be there, it may be highlighted in standout 1810 * mode (if "so" is true), the cursor is left to rest at the end of the 1811 * prompt. This call causes all pending messages to be flushed. 1812 */ 1813 1814 void 1815 message_prompt_v(int so, char *msgfmt, va_list ap) 1816 1817 { 1818 char msg[MAX_COLS]; 1819 int i; 1820 1821 /* clear out the message buffer */ 1822 message_flush(); 1823 1824 /* format the message */ 1825 i = vsnprintf(msg, sizeof(msg), msgfmt, ap); 1826 1827 /* this goes over any existing message */ 1828 display_move(0, y_message); 1829 1830 /* clear the entire line */ 1831 screen_cleareol(message_length); 1832 1833 /* show the prompt */ 1834 if (so) 1835 { 1836 screen_standout(msg); 1837 } 1838 else 1839 { 1840 fputs(msg, stdout); 1841 } 1842 1843 /* make it all visible */ 1844 fflush(stdout); 1845 1846 /* even though we dont keep a copy of the prompt, track its length */ 1847 message_length = i < MAX_COLS ? i : MAX_COLS; 1848 } 1849 1850 /* 1851 * void message_prompt(char *msgfmt, ...) 1852 * 1853 * Place a prompt in the message area (see message_prompt_v). 1854 */ 1855 1856 void 1857 message_prompt(char *msgfmt, ...) 1858 1859 { 1860 va_list ap; 1861 1862 va_start(ap, msgfmt); 1863 message_prompt_v(Yes, msgfmt, ap); 1864 va_end(ap); 1865 } 1866 1867 void 1868 message_prompt_plain(char *msgfmt, ...) 1869 1870 { 1871 va_list ap; 1872 1873 va_start(ap, msgfmt); 1874 message_prompt_v(No, msgfmt, ap); 1875 va_end(ap); 1876 } 1877 1878 /* 1879 * int readline(char *buffer, int size, int numeric) 1880 * 1881 * Read a line of input from the terminal. The line is placed in 1882 * "buffer" not to exceed "size". If "numeric" is true then the input 1883 * can only consist of digits. This routine handles all character 1884 * editing while keeping the terminal in cbreak mode. If "numeric" 1885 * is true then the number entered is returned. Otherwise the number 1886 * of character read in to "buffer" is returned. 1887 */ 1888 1889 int 1890 readline(char *buffer, int size, int numeric) 1891 1892 { 1893 register char *ptr = buffer; 1894 register char ch; 1895 register char cnt = 0; 1896 1897 /* allow room for null terminator */ 1898 size -= 1; 1899 1900 /* read loop */ 1901 while ((fflush(stdout), read(0, ptr, 1) > 0)) 1902 { 1903 /* newline or return means we are done */ 1904 if ((ch = *ptr) == '\n' || ch == '\r') 1905 { 1906 break; 1907 } 1908 1909 /* handle special editing characters */ 1910 if (ch == ch_kill) 1911 { 1912 /* return null string */ 1913 *buffer = '\0'; 1914 putchar('\r'); 1915 return(-1); 1916 } 1917 else if (ch == ch_werase) 1918 { 1919 /* erase previous word */ 1920 if (cnt <= 0) 1921 { 1922 /* none to erase! */ 1923 putchar('\7'); 1924 } 1925 else 1926 { 1927 /* 1928 * First: remove all spaces till the first-non-space 1929 * Second: remove all non-spaces till the first-space 1930 */ 1931 while(cnt > 0 && ptr[-1] == ' ') 1932 { 1933 fputs("\b \b", stdout); 1934 ptr--; 1935 cnt--; 1936 } 1937 while(cnt > 0 && ptr[-1] != ' ') 1938 { 1939 fputs("\b \b", stdout); 1940 ptr--; 1941 cnt--; 1942 } 1943 } 1944 } 1945 else if (ch == ch_erase) 1946 { 1947 /* erase previous character */ 1948 if (cnt <= 0) 1949 { 1950 /* none to erase! */ 1951 putchar('\7'); 1952 } 1953 else 1954 { 1955 fputs("\b \b", stdout); 1956 ptr--; 1957 cnt--; 1958 } 1959 } 1960 /* check for character validity and buffer overflow */ 1961 else if (cnt == size || (numeric && !isdigit((int)ch)) || 1962 !isprint((int)ch)) 1963 { 1964 /* not legal */ 1965 putchar('\7'); 1966 } 1967 else 1968 { 1969 /* echo it and store it in the buffer */ 1970 putchar(ch); 1971 ptr++; 1972 cnt++; 1973 } 1974 } 1975 1976 /* all done -- null terminate the string */ 1977 *ptr = '\0'; 1978 1979 /* add response length to message_length */ 1980 message_length += cnt; 1981 1982 /* return either inputted number or string length */ 1983 putchar('\r'); 1984 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 1985 } 1986 1987 void 1988 display_pagerstart() 1989 1990 { 1991 display_clear(); 1992 } 1993 1994 void 1995 display_pagerend() 1996 1997 { 1998 char ch; 1999 2000 screen_standout("Hit any key to continue: "); 2001 fflush(stdout); 2002 (void) read(0, &ch, 1); 2003 } 2004 2005 void 2006 display_pager(char *fmt, ...) 2007 2008 { 2009 va_list ap; 2010 2011 int ch; 2012 char readch; 2013 char buffer[MAX_COLS]; 2014 char *data; 2015 2016 /* format into buffer */ 2017 va_start(ap, fmt); 2018 (void) vsnprintf(buffer, MAX_COLS, fmt, ap); 2019 va_end(ap); 2020 data = buffer; 2021 2022 while ((ch = *data++) != '\0') 2023 { 2024 putchar(ch); 2025 if (ch == '\n') 2026 { 2027 if (++curr_y >= screen_length - 1) 2028 { 2029 screen_standout("...More..."); 2030 fflush(stdout); 2031 (void) read(0, &readch, 1); 2032 putchar('\r'); 2033 switch(readch) 2034 { 2035 case '\r': 2036 case '\n': 2037 curr_y--; 2038 break; 2039 2040 case 'q': 2041 return; 2042 2043 default: 2044 curr_y = 0; 2045 } 2046 } 2047 } 2048 } 2049 } 2050