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
string_count(const char ** pp)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
display_clear(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
display_move(int x,int y)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
display_write(int x,int y,int newcolor,int eol,const char * new)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
display_fmt(int x,int y,int newcolor,int eol,const char * fmt,...)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
display_cte(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
summary_format(int x,int y,int * numbers,const char ** names,int * cidx)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
summary_format_memory(int x,int y,long * numbers,const char ** names,int * cidx)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
display_resize()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
display_lines()735 display_lines()
736 
737 {
738     return(smart_terminal ? screen_length : Largest);
739 }
740 
741 int
display_columns()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
display_setmulti(int m)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
display_init(struct statics * statics,int percpuinfo)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
pr_loadavg(double avg,int i)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
i_loadave(int mpid,double * avenrun)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
u_loadave(int mpid,double * avenrun)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
i_minibar(int (* formatter)(char *,int))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
u_minibar(int (* formatter)(char *,int))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
i_uptime(time_t * bt,time_t * tod)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
u_uptime(time_t * bt,time_t * tod)1040 u_uptime(time_t *bt, time_t *tod)
1041 
1042 {
1043     i_uptime(bt, tod);
1044 }
1045 
1046 
1047 void
i_timeofday(time_t * tod)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
i_procstates(int total,int * brkdn,int threads)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
u_procstates(int total,int * brkdn,int threads)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 *
cpustates_tag(int c)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
i_cpustates(int * states)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
u_cpustates(int * states)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
z_cpustates()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
i_kernel(int * stats)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
u_kernel(int * stats)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
i_memory(long * stats)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
u_memory(long * stats)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
i_swap(long * stats)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
u_swap(long * stats)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
i_message(struct timeval * now)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
u_message(struct timeval * now)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
i_header(char * text)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
u_header(char * text)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
i_process(int line,char * thisline)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
u_process(int line,char * new_line)1587 u_process(int line, char *new_line)
1588 
1589 {
1590     i_process(line, new_line);
1591 }
1592 
1593 void
i_endscreen()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
u_endscreen()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
display_header(int t)1631 display_header(int t)
1632 
1633 {
1634     header_status = t != 0;
1635 }
1636 
1637 void
message_mark(void)1638 message_mark(void)
1639 
1640 {
1641     message_barrier = Yes;
1642 }
1643 
1644 void
message_expire(void)1645 message_expire(void)
1646 
1647 {
1648     message_time.tv_sec = 0;
1649     message_time.tv_usec = 0;
1650 }
1651 
1652 static void
message_flush(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
new_message_v(const char * msgfmt,va_list ap)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
new_message(const char * msgfmt,...)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
message_error(const char * msgfmt,...)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
message_clear()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
message_prompt_v(int so,const char * msgfmt,va_list ap)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
message_prompt(const char * msgfmt,...)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
message_prompt_plain(const char * msgfmt,...)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
readline(char * buffer,int size,int numeric)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
display_pagerstart()1960 display_pagerstart()
1961 
1962 {
1963     display_clear();
1964 }
1965 
1966 void
display_pagerend()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
display_pager(const char * fmt,...)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