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