Home | History | Annotate | Line # | Download | only in dist
      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((unsigned char)ch)) ||
   1934 		!isprint((unsigned char)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