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