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