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 uptime) 983 { 984 uptime += 30; 985 uptime_days = uptime / 86400; 986 uptime %= 86400; 987 uptime_hours = uptime / 3600; 988 uptime %= 3600; 989 uptime_mins = uptime / 60; 990 uptime_secs = uptime % 60; 991 992 /* 993 * Display the uptime. 994 */ 995 996 display_fmt(x_uptime, y_uptime, 0, 0, 997 " up %d+%02d:%02d:%02d", 998 uptime_days, uptime_hours, uptime_mins, uptime_secs); 999 } 1000 1001 void 1002 u_uptime(time_t uptime) 1003 { 1004 i_uptime(uptime); 1005 } 1006 1007 1008 void 1009 i_timeofday(time_t *tod) 1010 1011 { 1012 /* 1013 * Display the current time. 1014 * "ctime" always returns a string that looks like this: 1015 * 1016 * Sun Sep 16 01:03:52 1973 1017 * 012345678901234567890123 1018 * 1 2 1019 * 1020 * We want indices 11 thru 18 (length 8). 1021 */ 1022 1023 int x; 1024 1025 /* where on the screen do we start? */ 1026 x = (smart_terminal ? screen_width : 79) - 8; 1027 1028 /* but don't bump in to uptime */ 1029 if (x < x_uptime + 19) 1030 { 1031 x = x_uptime + 19; 1032 } 1033 1034 /* display it */ 1035 display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11])); 1036 } 1037 1038 static int ltotal = 0; 1039 static int lthreads = 0; 1040 1041 /* 1042 * *_procstates(total, brkdn, names) - print the process summary line 1043 */ 1044 1045 1046 void 1047 i_procstates(int total, int *brkdn, int threads) 1048 1049 { 1050 /* write current number of processes and remember the value */ 1051 display_fmt(0, y_procstate, 0, 0, 1052 "%d %s: ", total, threads ? "threads" : "processes"); 1053 ltotal = total; 1054 1055 /* remember where the summary starts */ 1056 x_procstate = virt_x; 1057 1058 if (total > 0) 1059 { 1060 /* format and print the process state summary */ 1061 summary_format(-1, -1, brkdn, procstate_names, NULL); 1062 1063 /* save the numbers for next time */ 1064 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 1065 lthreads = threads; 1066 } 1067 } 1068 1069 void 1070 u_procstates(int total, int *brkdn, int threads) 1071 1072 { 1073 /* if threads state has changed, do a full update */ 1074 if (lthreads != threads) 1075 { 1076 i_procstates(total, brkdn, threads); 1077 return; 1078 } 1079 1080 /* update number of processes only if it has changed */ 1081 if (ltotal != total) 1082 { 1083 display_fmt(0, y_procstate, 0, 0, 1084 "%d", total); 1085 1086 /* if number of digits differs, rewrite the label */ 1087 if (digits(total) != digits(ltotal)) 1088 { 1089 display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes"); 1090 x_procstate = virt_x; 1091 } 1092 1093 /* save new total */ 1094 ltotal = total; 1095 } 1096 1097 /* see if any of the state numbers has changed */ 1098 if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) 1099 { 1100 /* format and update the line */ 1101 summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL); 1102 memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 1103 } 1104 } 1105 1106 /* 1107 * *_cpustates(states, names) - print the cpu state percentages 1108 */ 1109 1110 /* cpustates_tag() calculates the correct tag to use to label the line */ 1111 1112 char * 1113 cpustates_tag() 1114 1115 { 1116 register char *use; 1117 1118 static char *short_tag = "CPU: "; 1119 static char *long_tag = "CPU states: "; 1120 1121 /* if length + strlen(long_tag) >= screen_width, then we have to 1122 use the shorter tag (we subtract 2 to account for ": ") */ 1123 if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width) 1124 { 1125 use = short_tag; 1126 } 1127 else 1128 { 1129 use = long_tag; 1130 } 1131 1132 /* set x_cpustates accordingly then return result */ 1133 x_cpustates = strlen(use); 1134 return(use); 1135 } 1136 1137 void 1138 i_cpustates(int *states) 1139 1140 { 1141 int value; 1142 char **names; 1143 char *thisname; 1144 int *colp; 1145 int color = 0; 1146 int cpu; 1147 #ifdef ENABLE_COLOR 1148 int *cidx = cpustate_cidx; 1149 #endif 1150 1151 /* initialize */ 1152 names = cpustate_names; 1153 colp = cpustate_columns; 1154 1155 /* print tag */ 1156 if (enable_ncpus !=0 && n_cpus > 1) { 1157 for (cpu = 0; cpu < n_cpus; ++cpu) { 1158 int y_pos = y_cpustates; 1159 y_pos = y_pos + cpu; 1160 colp = cpustate_columns; 1161 names = cpustate_names; 1162 display_write(0, y_cpustates+cpu, 0, 0, cpustates_tag()); 1163 1164 /* now walk thru the names and print the line */ 1165 while ((thisname = *names++) != NULL) { 1166 if (*thisname != '\0') { 1167 /* retrieve the value and remember it */ 1168 value = *states; 1169 1170 #ifdef ENABLE_COLOR 1171 /* determine color number to use */ 1172 color = color_test(*cidx++, value/10); 1173 #endif 1174 /* if percentage is >= 1000, print it as 100% */ 1175 display_fmt(x_cpustates + *colp, y_pos, 1176 color, 0, 1177 (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), 1178 ((float)value)/10., 1179 thisname, 1180 *names != NULL ? ", " : ""); 1181 1182 } 1183 /* increment */ 1184 colp++; 1185 states++; 1186 } 1187 /* copy over values into "last" array */ 1188 memcpy(lcpustates, states, num_cpustates * sizeof(int)); 1189 } 1190 } else { 1191 display_write(0, y_cpustates, 0, 0, cpustates_tag()); 1192 1193 /* now walk thru the names and print the line */ 1194 while ((thisname = *names++) != NULL) 1195 { 1196 if (*thisname != '\0') 1197 { 1198 /* retrieve the value and remember it */ 1199 value = *states; 1200 1201 #ifdef ENABLE_COLOR 1202 /* determine color number to use */ 1203 color = color_test(*cidx++, value/10); 1204 #endif 1205 1206 /* if percentage is >= 1000, print it as 100% */ 1207 display_fmt(x_cpustates + *colp, y_cpustates, 1208 color, 0, 1209 (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), 1210 ((float)value)/10., 1211 thisname, 1212 *names != NULL ? ", " : ""); 1213 1214 } 1215 /* increment */ 1216 colp++; 1217 states++; 1218 } 1219 1220 /* copy over values into "last" array */ 1221 memcpy(lcpustates, states, num_cpustates * sizeof(int)); 1222 } 1223 1224 } 1225 1226 void 1227 u_cpustates(int *states) 1228 1229 { 1230 int value; 1231 char **names = cpustate_names; 1232 char *thisname; 1233 int *lp; 1234 int *colp; 1235 int color = 0; 1236 int cpu; 1237 #ifdef ENABLE_COLOR 1238 int *cidx = cpustate_cidx; 1239 #endif 1240 1241 1242 if (enable_ncpus != 0 && n_cpus > 1 ) { 1243 for (cpu = 0; cpu < n_cpus; ++cpu) { 1244 lp = lcpustates; 1245 int y_pos = y_cpustates; 1246 y_pos = y_pos + cpu; 1247 colp = cpustate_columns; 1248 char **names = cpustate_names; 1249 /* we could be much more optimal about this */ 1250 while ((thisname = *names++) != NULL) { 1251 if (*thisname != '\0') { 1252 /* yes, change it */ 1253 /* retrieve value and remember it */ 1254 value = *states; 1255 1256 #ifdef ENABLE_COLOR 1257 /* determine color number to use */ 1258 color = color_test(*cidx, value/10); 1259 #endif 1260 /* if percentage is >= 1000, print it as 100% */ 1261 display_fmt(x_cpustates + *colp, y_pos, color, 0, 1262 (value >= 1000 ? "%4.0f" : "%4.1f"), 1263 ((double)value)/10.); 1264 1265 #ifdef ENABLE_COLOR 1266 cidx++; 1267 #endif 1268 } 1269 /* increment and move on */ 1270 lp++; 1271 states++; 1272 colp++; 1273 } 1274 } 1275 } else { 1276 lp = lcpustates; 1277 colp = cpustate_columns; 1278 1279 /* we could be much more optimal about this */ 1280 while ((thisname = *names++) != NULL) 1281 { 1282 if (*thisname != '\0') 1283 { 1284 /* did the value change since last time? */ 1285 if (*lp != *states) 1286 { 1287 /* yes, change it */ 1288 /* retrieve value and remember it */ 1289 value = *states; 1290 1291 #ifdef ENABLE_COLOR 1292 /* determine color number to use */ 1293 color = color_test(*cidx, value/10); 1294 #endif 1295 1296 /* if percentage is >= 1000, print it as 100% */ 1297 display_fmt(x_cpustates + *colp, y_cpustates, color, 0, 1298 (value >= 1000 ? "%4.0f" : "%4.1f"), 1299 ((double)value)/10.); 1300 1301 /* remember it for next time */ 1302 *lp = value; 1303 } 1304 #ifdef ENABLE_COLOR 1305 cidx++; 1306 #endif 1307 } 1308 1309 /* increment and move on */ 1310 lp++; 1311 states++; 1312 colp++; 1313 } 1314 } 1315 } 1316 1317 void 1318 z_cpustates() 1319 1320 { 1321 register int i = 0; 1322 register char **names = cpustate_names; 1323 register char *thisname; 1324 register int *lp; 1325 int cpu; 1326 1327 /* print tag */ 1328 if (enable_ncpus != 0 && n_cpus > 1) { 1329 for (cpu = 0; cpu < n_cpus; ++cpu) { 1330 display_write(0, y_cpustates + cpu, 0, 0, cpustates_tag()); 1331 char **names = cpustate_names; 1332 i = 0; 1333 while ((thisname = *names++) != NULL) { 1334 if (*thisname != '\0') { 1335 display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ", 1336 thisname); 1337 } 1338 } 1339 /* fill the "last" array with all -1s, to insure correct updating */ 1340 lp = lcpustates; 1341 i = num_cpustates; 1342 while (--i >= 0) { 1343 *lp++ = -1; 1344 } 1345 } 1346 } else { 1347 display_write(0, y_cpustates, 0, 0, cpustates_tag()); 1348 1349 while ((thisname = *names++) != NULL) 1350 { 1351 if (*thisname != '\0') 1352 { 1353 display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ", 1354 thisname); 1355 } 1356 } 1357 1358 /* fill the "last" array with all -1s, to insure correct updating */ 1359 lp = lcpustates; 1360 i = num_cpustates; 1361 while (--i >= 0) 1362 { 1363 *lp++ = -1; 1364 } 1365 } 1366 } 1367 1368 /* 1369 * *_kernel(stats) - print "Kernel: " followed by the kernel summary string 1370 * 1371 * Assumptions: cursor is on "lastline", the previous line 1372 */ 1373 1374 void 1375 i_kernel(int *stats) 1376 1377 { 1378 if (num_kernel > 0) 1379 { 1380 display_write(0, y_kernel, 0, 0, "Kernel: "); 1381 1382 /* format and print the kernel summary */ 1383 summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); 1384 } 1385 } 1386 1387 void 1388 u_kernel(int *stats) 1389 1390 { 1391 if (num_kernel > 0) 1392 { 1393 /* format the new line */ 1394 summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); 1395 } 1396 } 1397 1398 /* 1399 * *_memory(stats) - print "Memory: " followed by the memory summary string 1400 * 1401 * Assumptions: cursor is on "lastline", the previous line 1402 */ 1403 1404 void 1405 i_memory(long *stats) 1406 1407 { 1408 display_write(0, y_mem, 0, 0, "Memory: "); 1409 1410 /* format and print the memory summary */ 1411 summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); 1412 } 1413 1414 void 1415 u_memory(long *stats) 1416 1417 { 1418 /* format the new line */ 1419 summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); 1420 } 1421 1422 /* 1423 * *_swap(stats) - print "Swap: " followed by the swap summary string 1424 * 1425 * Assumptions: cursor is on "lastline", the previous line 1426 * 1427 * These functions only print something when num_swap > 0 1428 */ 1429 1430 void 1431 i_swap(long *stats) 1432 1433 { 1434 if (num_swap > 0) 1435 { 1436 /* print the tag */ 1437 display_write(0, y_swap, 0, 0, "Swap: "); 1438 1439 /* format and print the swap summary */ 1440 summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); 1441 } 1442 } 1443 1444 void 1445 u_swap(long *stats) 1446 1447 { 1448 if (num_swap > 0) 1449 { 1450 /* format the new line */ 1451 summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); 1452 } 1453 } 1454 1455 /* 1456 * *_message() - print the next pending message line, or erase the one 1457 * that is there. 1458 * 1459 * Note that u_message is (currently) the same as i_message. 1460 * 1461 * Assumptions: lastline is consistent 1462 */ 1463 1464 /* 1465 * i_message is funny because it gets its message asynchronously (with 1466 * respect to screen updates). Messages are taken out of the 1467 * circular message_buf and displayed one at a time. 1468 */ 1469 1470 void 1471 i_message(struct timeval *now) 1472 1473 { 1474 struct timeval my_now; 1475 int i = 0; 1476 1477 dprintf("i_message(%08x)\n", now); 1478 1479 /* if now is NULL we have to get it ourselves */ 1480 if (now == NULL) 1481 { 1482 time_get(&my_now); 1483 now = &my_now; 1484 } 1485 1486 /* now that we have been called, messages no longer need to be held */ 1487 message_hold = 0; 1488 1489 dprintf("i_message: now %d, message_time %d\n", 1490 now->tv_sec, message_time.tv_sec); 1491 1492 if (smart_terminal) 1493 { 1494 /* is it time to change the message? */ 1495 if (timercmp(now, &message_time, > )) 1496 { 1497 /* yes, free the current message */ 1498 dprintf("i_message: timer expired\n"); 1499 if (message_current != NULL) 1500 { 1501 free(message_current); 1502 message_current = NULL; 1503 } 1504 1505 /* is there a new message to be displayed? */ 1506 if (message_first != message_last) 1507 { 1508 /* move index to next message */ 1509 if (++message_first == MAX_MESSAGES) message_first = 0; 1510 1511 /* make the next message the current one */ 1512 message_current = message_buf[message_first]; 1513 1514 /* show it */ 1515 dprintf("i_message: showing \"%s\"\n", message_current); 1516 display_move(0, y_message); 1517 screen_standout(message_current); 1518 i = strlen(message_current); 1519 1520 /* set the expiration timer */ 1521 message_time = *now; 1522 message_time.tv_sec += MESSAGE_DISPLAY_TIME; 1523 1524 /* clear the rest of the line */ 1525 screen_cleareol(message_length - i); 1526 putchar('\r'); 1527 message_length = i; 1528 } 1529 else 1530 { 1531 /* just clear what was there before, if anything */ 1532 if (message_length > 0) 1533 { 1534 display_move(0, y_message); 1535 screen_cleareol(message_length); 1536 putchar('\r'); 1537 message_length = 0; 1538 } 1539 } 1540 } 1541 } 1542 } 1543 1544 void 1545 u_message(struct timeval *now) 1546 1547 { 1548 i_message(now); 1549 } 1550 1551 static int header_length; 1552 1553 /* 1554 * *_header(text) - print the header for the process area 1555 * 1556 * Assumptions: cursor is on the previous line and lastline is consistent 1557 */ 1558 1559 void 1560 i_header(char *text) 1561 1562 { 1563 int header_color = 0; 1564 1565 #ifdef ENABLE_COLOR 1566 header_color = color_test(header_cidx, 0); 1567 #endif 1568 header_length = strlen(text); 1569 if (header_status) 1570 { 1571 display_write(x_header, y_header, header_color, 1, text); 1572 } 1573 } 1574 1575 /*ARGSUSED*/ 1576 void 1577 u_header(char *text) 1578 1579 { 1580 int header_color = 0; 1581 1582 #ifdef ENABLE_COLOR 1583 header_color = color_test(header_cidx, 0); 1584 #endif 1585 display_write(x_header, y_header, header_color, 1, 1586 header_status ? text : ""); 1587 } 1588 1589 /* 1590 * *_process(line, thisline) - print one process line 1591 * 1592 * Assumptions: lastline is consistent 1593 */ 1594 1595 void 1596 i_process(int line, char *thisline) 1597 1598 { 1599 /* truncate the line to conform to our current screen width */ 1600 thisline[display_width] = '\0'; 1601 1602 /* write the line out */ 1603 display_write(0, y_procs + line, 0, 1, thisline); 1604 } 1605 1606 void 1607 u_process(int line, char *new_line) 1608 1609 { 1610 i_process(line, new_line); 1611 } 1612 1613 void 1614 i_endscreen() 1615 1616 { 1617 if (smart_terminal) 1618 { 1619 /* move the cursor to a pleasant place */ 1620 display_move(x_idlecursor, y_idlecursor); 1621 } 1622 else 1623 { 1624 /* separate this display from the next with some vertical room */ 1625 fputs("\n\n", stdout); 1626 } 1627 fflush(stdout); 1628 } 1629 1630 void 1631 u_endscreen() 1632 1633 { 1634 if (smart_terminal) 1635 { 1636 /* clear-to-end the display */ 1637 display_cte(); 1638 1639 /* move the cursor to a pleasant place */ 1640 display_move(x_idlecursor, y_idlecursor); 1641 fflush(stdout); 1642 } 1643 else 1644 { 1645 /* separate this display from the next with some vertical room */ 1646 fputs("\n\n", stdout); 1647 } 1648 } 1649 1650 void 1651 display_header(int t) 1652 1653 { 1654 header_status = t != 0; 1655 } 1656 1657 void 1658 message_mark() 1659 1660 { 1661 message_barrier = Yes; 1662 } 1663 1664 void 1665 message_expire() 1666 1667 { 1668 message_time.tv_sec = 0; 1669 message_time.tv_usec = 0; 1670 } 1671 1672 void 1673 message_flush() 1674 1675 { 1676 message_first = message_last; 1677 message_time.tv_sec = 0; 1678 message_time.tv_usec = 0; 1679 } 1680 1681 /* 1682 * void new_message_v(char *msgfmt, va_list ap) 1683 * 1684 * Display a message in the message area. This function takes a va_list for 1685 * the arguments. Safe to call before display_init. This function only 1686 * queues a message for display, and allowed for multiple messages to be 1687 * queued. The i_message function drains the queue and actually writes the 1688 * messages on the display. 1689 */ 1690 1691 1692 void 1693 new_message_v(char *msgfmt, va_list ap) 1694 1695 { 1696 int i; 1697 int empty; 1698 char msg[MAX_COLS]; 1699 1700 /* if message_barrier is active, remove all pending messages */ 1701 if (message_barrier) 1702 { 1703 message_flush(); 1704 message_barrier = No; 1705 } 1706 1707 /* first, format the message */ 1708 (void) vsnprintf(msg, sizeof(msg), msgfmt, ap); 1709 1710 /* where in the buffer will it go? */ 1711 i = message_last + 1; 1712 if (i >= MAX_MESSAGES) i = 0; 1713 1714 /* make sure the buffer is not full */ 1715 if (i != message_first) 1716 { 1717 /* insert it in to message_buf */ 1718 message_buf[i] = strdup(msg); 1719 dprintf("new_message_v: new message inserted in slot %d\n", i); 1720 1721 /* remember if the buffer is empty and set the index */ 1722 empty = message_last == message_first; 1723 message_last = i; 1724 1725 /* is message_buf otherwise empty and have we started displaying? */ 1726 if (empty && !message_hold) 1727 { 1728 /* we can display the message now */ 1729 i_message(NULL); 1730 } 1731 } 1732 } 1733 1734 /* 1735 * void new_message(int type, char *msgfmt, ...) 1736 * 1737 * Display a message in the message area. It is safe to call this function 1738 * before display_init. Messages logged before the display is drawn will be 1739 * held and displayed later. 1740 */ 1741 1742 void 1743 new_message(char *msgfmt, ...) 1744 1745 { 1746 va_list ap; 1747 1748 va_start(ap, msgfmt); 1749 new_message_v(msgfmt, ap); 1750 va_end(ap); 1751 } 1752 1753 /* 1754 * void message_error(char *msgfmt, ...) 1755 * 1756 * Put an error message in the message area. It is safe to call this function 1757 * before display_init. Messages logged before the display is drawn will be 1758 * held and displayed later. 1759 */ 1760 1761 void 1762 message_error(char *msgfmt, ...) 1763 1764 { 1765 va_list ap; 1766 1767 va_start(ap, msgfmt); 1768 new_message_v(msgfmt, ap); 1769 fflush(stdout); 1770 va_end(ap); 1771 } 1772 1773 /* 1774 * void message_clear() 1775 * 1776 * Clear message area and flush all pending messages. 1777 */ 1778 1779 void 1780 message_clear() 1781 1782 { 1783 /* remove any existing message */ 1784 if (message_current != NULL) 1785 { 1786 display_move(0, y_message); 1787 screen_cleareol(message_length); 1788 free(message_current); 1789 message_current = NULL; 1790 } 1791 1792 /* flush all pending messages */ 1793 message_flush(); 1794 } 1795 1796 /* 1797 * void message_prompt_v(int so, char *msgfmt, va_list ap) 1798 * 1799 * Place a prompt in the message area. A prompt is different from a 1800 * message as follows: it is displayed immediately, overwriting any 1801 * message that may already be there, it may be highlighted in standout 1802 * mode (if "so" is true), the cursor is left to rest at the end of the 1803 * prompt. This call causes all pending messages to be flushed. 1804 */ 1805 1806 void 1807 message_prompt_v(int so, char *msgfmt, va_list ap) 1808 1809 { 1810 char msg[MAX_COLS]; 1811 int i; 1812 1813 /* clear out the message buffer */ 1814 message_flush(); 1815 1816 /* format the message */ 1817 i = vsnprintf(msg, sizeof(msg), msgfmt, ap); 1818 1819 /* this goes over any existing message */ 1820 display_move(0, y_message); 1821 1822 /* clear the entire line */ 1823 screen_cleareol(message_length); 1824 1825 /* show the prompt */ 1826 if (so) 1827 { 1828 screen_standout(msg); 1829 } 1830 else 1831 { 1832 fputs(msg, stdout); 1833 } 1834 1835 /* make it all visible */ 1836 fflush(stdout); 1837 1838 /* even though we dont keep a copy of the prompt, track its length */ 1839 message_length = i < MAX_COLS ? i : MAX_COLS; 1840 } 1841 1842 /* 1843 * void message_prompt(char *msgfmt, ...) 1844 * 1845 * Place a prompt in the message area (see message_prompt_v). 1846 */ 1847 1848 void 1849 message_prompt(char *msgfmt, ...) 1850 1851 { 1852 va_list ap; 1853 1854 va_start(ap, msgfmt); 1855 message_prompt_v(Yes, msgfmt, ap); 1856 va_end(ap); 1857 } 1858 1859 void 1860 message_prompt_plain(char *msgfmt, ...) 1861 1862 { 1863 va_list ap; 1864 1865 va_start(ap, msgfmt); 1866 message_prompt_v(No, msgfmt, ap); 1867 va_end(ap); 1868 } 1869 1870 /* 1871 * int readline(char *buffer, int size, int numeric) 1872 * 1873 * Read a line of input from the terminal. The line is placed in 1874 * "buffer" not to exceed "size". If "numeric" is true then the input 1875 * can only consist of digits. This routine handles all character 1876 * editing while keeping the terminal in cbreak mode. If "numeric" 1877 * is true then the number entered is returned. Otherwise the number 1878 * of character read in to "buffer" is returned. 1879 */ 1880 1881 int 1882 readline(char *buffer, int size, int numeric) 1883 1884 { 1885 register char *ptr = buffer; 1886 register char ch; 1887 register char cnt = 0; 1888 1889 /* allow room for null terminator */ 1890 size -= 1; 1891 1892 /* read loop */ 1893 while ((fflush(stdout), read(0, ptr, 1) > 0)) 1894 { 1895 /* newline or return means we are done */ 1896 if ((ch = *ptr) == '\n' || ch == '\r') 1897 { 1898 break; 1899 } 1900 1901 /* handle special editing characters */ 1902 if (ch == ch_kill) 1903 { 1904 /* return null string */ 1905 *buffer = '\0'; 1906 putchar('\r'); 1907 return(-1); 1908 } 1909 else if (ch == ch_werase) 1910 { 1911 /* erase previous word */ 1912 if (cnt <= 0) 1913 { 1914 /* none to erase! */ 1915 putchar('\7'); 1916 } 1917 else 1918 { 1919 /* 1920 * First: remove all spaces till the first-non-space 1921 * Second: remove all non-spaces till the first-space 1922 */ 1923 while(cnt > 0 && ptr[-1] == ' ') 1924 { 1925 fputs("\b \b", stdout); 1926 ptr--; 1927 cnt--; 1928 } 1929 while(cnt > 0 && ptr[-1] != ' ') 1930 { 1931 fputs("\b \b", stdout); 1932 ptr--; 1933 cnt--; 1934 } 1935 } 1936 } 1937 else if (ch == ch_erase) 1938 { 1939 /* erase previous character */ 1940 if (cnt <= 0) 1941 { 1942 /* none to erase! */ 1943 putchar('\7'); 1944 } 1945 else 1946 { 1947 fputs("\b \b", stdout); 1948 ptr--; 1949 cnt--; 1950 } 1951 } 1952 /* check for character validity and buffer overflow */ 1953 else if (cnt == size || (numeric && !isdigit((int)ch)) || 1954 !isprint((int)ch)) 1955 { 1956 /* not legal */ 1957 putchar('\7'); 1958 } 1959 else 1960 { 1961 /* echo it and store it in the buffer */ 1962 putchar(ch); 1963 ptr++; 1964 cnt++; 1965 } 1966 } 1967 1968 /* all done -- null terminate the string */ 1969 *ptr = '\0'; 1970 1971 /* add response length to message_length */ 1972 message_length += cnt; 1973 1974 /* return either inputted number or string length */ 1975 putchar('\r'); 1976 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 1977 } 1978 1979 void 1980 display_pagerstart() 1981 1982 { 1983 display_clear(); 1984 } 1985 1986 void 1987 display_pagerend() 1988 1989 { 1990 char ch; 1991 1992 screen_standout("Hit any key to continue: "); 1993 fflush(stdout); 1994 (void) read(0, &ch, 1); 1995 } 1996 1997 void 1998 display_pager(char *fmt, ...) 1999 2000 { 2001 va_list ap; 2002 2003 int ch; 2004 char readch; 2005 char buffer[MAX_COLS]; 2006 char *data; 2007 2008 /* format into buffer */ 2009 va_start(ap, fmt); 2010 (void) vsnprintf(buffer, MAX_COLS, fmt, ap); 2011 va_end(ap); 2012 data = buffer; 2013 2014 while ((ch = *data++) != '\0') 2015 { 2016 putchar(ch); 2017 if (ch == '\n') 2018 { 2019 if (++curr_y >= screen_length - 1) 2020 { 2021 screen_standout("...More..."); 2022 fflush(stdout); 2023 (void) read(0, &readch, 1); 2024 putchar('\r'); 2025 switch(readch) 2026 { 2027 case '\r': 2028 case '\n': 2029 curr_y--; 2030 break; 2031 2032 case 'q': 2033 return; 2034 2035 default: 2036 curr_y = 0; 2037 } 2038 } 2039 } 2040 } 2041 } 2042