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