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
84 /* cursor positions of key points on the screen are maintained here */
85 /* layout.h has static definitions, but we may change our minds on some
86 of the positions as we make decisions about what needs to be displayed */
87
88 static int x_lastpid = X_LASTPID;
89 static int y_lastpid = Y_LASTPID;
90 static int x_loadave = X_LOADAVE;
91 static int y_loadave = Y_LOADAVE;
92 static int x_minibar = X_MINIBAR;
93 static int y_minibar = Y_MINIBAR;
94 static int x_uptime = X_UPTIME;
95 static int y_uptime = Y_UPTIME;
96 static int x_procstate = X_PROCSTATE;
97 static int y_procstate = Y_PROCSTATE;
98 static int x_cpustates = X_CPUSTATES;
99 static int y_cpustates = Y_CPUSTATES;
100 static int x_kernel = X_KERNEL;
101 static int y_kernel = Y_KERNEL;
102 static int x_mem = X_MEM;
103 static int y_mem = Y_MEM;
104 static int x_swap = X_SWAP;
105 static int y_swap = Y_SWAP;
106 static int y_message = Y_MESSAGE;
107 static int x_header = X_HEADER;
108 static int y_header = Y_HEADER;
109 static int x_idlecursor = X_IDLECURSOR;
110 static int y_idlecursor = Y_IDLECURSOR;
111 static int y_procs = Y_PROCS;
112
113 /* buffer and colormask that describes the content of the screen */
114 /* these are singly dimensioned arrays -- the row boundaries are
115 determined on the fly.
116 */
117 static char *screenbuf = NULL;
118 static char *colorbuf = NULL;
119 static char scratchbuf[MAX_COLS];
120 static int bufsize = 0;
121
122 /* lineindex tells us where the beginning of a line is in the buffer */
123 #define lineindex(l) ((l)*MAX_COLS)
124
125 /* screen's cursor */
126 static int curr_x, curr_y;
127 static int curr_color;
128
129 /* virtual cursor */
130 static int virt_x, virt_y;
131
132 static char **procstate_names;
133 static char **cpustate_names;
134 static char **memory_names;
135 static char **swap_names;
136 static char **kernel_names;
137
138 static int num_procstates;
139 static int num_cpustates;
140 static int num_memory;
141 static int num_swap;
142 static int num_kernel;
143
144 static int *lprocstates;
145 static int *lcpustates;
146
147 static int *cpustate_columns;
148 static int cpustate_total_length;
149
150 static int header_status = Yes;
151
152 /* pending messages are stored in a circular buffer, where message_first
153 is the next one to display, and message_last is the last one
154 in the buffer. Counters wrap around at MAX_MESSAGES. The buffer is
155 empty when message_first == message_last and full when
156 message_last + 1 == message_first. The pointer message_current holds
157 the message currently being displayed, or "" if there is none.
158 */
159 #define MAX_MESSAGES 16
160 static char *message_buf[MAX_MESSAGES];
161 static int message_first = 0;
162 static int message_last = 0;
163 static struct timeval message_time = {0, 0};
164 static char *message_current = NULL;
165 static int message_length = 0;
166 static int message_hold = 1;
167 static int message_barrier = No;
168
169 #ifdef ENABLE_COLOR
170 static int load_cidx[3];
171 static int header_cidx;
172 static int *cpustate_cidx;
173 static int *memory_cidx;
174 static int *swap_cidx;
175 static int *kernel_cidx;
176 #else
177 #define memory_cidx NULL
178 #define swap_cidx NULL
179 #define kernel_cidx NULL
180 #endif
181
182
183 /* internal support routines */
184
185 /*
186 * static int string_count(char **pp)
187 *
188 * Pointer "pp" points to an array of string pointers, which is
189 * terminated by a NULL. Return the number of string pointers in
190 * this array.
191 */
192
193 static int
string_count(char ** pp)194 string_count(char **pp)
195
196 {
197 register int cnt = 0;
198
199 if (pp != NULL)
200 {
201 while (*pp++ != NULL)
202 {
203 cnt++;
204 }
205 }
206 return(cnt);
207 }
208
209 void
display_clear()210 display_clear()
211
212 {
213 dprintf("display_clear\n");
214 screen_clear();
215 memzero(screenbuf, bufsize);
216 memzero(colorbuf, bufsize);
217 curr_x = curr_y = 0;
218 }
219
220 /*
221 * void display_move(int x, int y)
222 *
223 * Efficiently move the cursor to x, y. This assumes the cursor is
224 * currently located at curr_x, curr_y, and will only use cursor
225 * addressing when it is less expensive than overstriking what's
226 * already on the screen.
227 */
228
229 void
display_move(int x,int y)230 display_move(int x, int y)
231
232 {
233 char buff[128];
234 char *p;
235 char *bufp;
236 char *colorp;
237 int cnt = 0;
238 int color = curr_color;
239
240 dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y);
241
242 /* are we in a position to do this without cursor addressing? */
243 if (curr_y < y || (curr_y == y && curr_x <= x))
244 {
245 /* start buffering up what it would take to move there by rewriting
246 what's on the screen */
247 cnt = CURSOR_COST;
248 p = buff;
249
250 /* one newline for every line */
251 while (cnt > 0 && curr_y < y)
252 {
253 #ifdef ENABLE_COLOR
254 if (color != 0)
255 {
256 p = strcpyend(p, color_setstr(0));
257 color = 0;
258 cnt -= 5;
259 }
260 #endif
261 *p++ = '\n';
262 curr_y++;
263 curr_x = 0;
264 cnt--;
265 }
266
267 /* write whats in the screenbuf */
268 bufp = &screenbuf[lineindex(curr_y) + curr_x];
269 colorp = &colorbuf[lineindex(curr_y) + curr_x];
270 while (cnt > 0 && curr_x < x)
271 {
272 #ifdef ENABLE_COLOR
273 if (color != *colorp)
274 {
275 color = *colorp;
276 p = strcpyend(p, color_setstr(color));
277 cnt -= 5;
278 }
279 #endif
280 if ((*p = *bufp) == '\0')
281 {
282 /* somwhere on screen we haven't been before */
283 *p = *bufp = ' ';
284 }
285 p++;
286 bufp++;
287 colorp++;
288 curr_x++;
289 cnt--;
290 }
291 }
292
293 /* move the cursor */
294 if (cnt > 0)
295 {
296 /* screen rewrite is cheaper */
297 *p = '\0';
298 fputs(buff, stdout);
299 curr_color = color;
300 }
301 else
302 {
303 screen_move(x, y);
304 }
305
306 /* update our position */
307 curr_x = x;
308 curr_y = y;
309 }
310
311 /*
312 * display_write(int x, int y, int newcolor, int eol, char *new)
313 *
314 * Optimized write to the display. This writes characters to the
315 * screen in a way that optimizes the number of characters actually
316 * sent, by comparing what is being written to what is already on
317 * the screen (according to screenbuf and colorbuf). The string to
318 * write is "new", the first character of "new" should appear at
319 * screen position x, y. If x is -1 then "new" begins wherever the
320 * cursor is currently positioned. The string is written with color
321 * "newcolor". If "eol" is true then the remainder of the line is
322 * cleared. It is expected that "new" will have no newlines and no
323 * escape sequences.
324 */
325
326 void
display_write(int x,int y,int newcolor,int eol,char * new)327 display_write(int x, int y, int newcolor, int eol, char *new)
328
329 {
330 char *bufp;
331 char *colorp;
332 int ch;
333 int diff;
334
335 dprintf("display_write(%d, %d, %d, %d, \"%s\")\n",
336 x, y, newcolor, eol, new);
337
338 /* dumb terminal handling here */
339 if (!smart_terminal)
340 {
341 if (x != -1)
342 {
343 /* make sure we are on the right line */
344 while (curr_y < y)
345 {
346 putchar('\n');
347 curr_y++;
348 curr_x = 0;
349 }
350
351 /* make sure we are on the right column */
352 while (curr_x < x)
353 {
354 putchar(' ');
355 curr_x++;
356 }
357 }
358
359 /* write */
360 fputs(new, stdout);
361 curr_x += strlen(new);
362
363 return;
364 }
365
366 /* adjust for "here" */
367 if (x == -1)
368 {
369 x = virt_x;
370 y = virt_y;
371 }
372 else
373 {
374 virt_x = x;
375 virt_y = y;
376 }
377
378 /* a pointer to where we start */
379 bufp = &screenbuf[lineindex(y) + x];
380 colorp = &colorbuf[lineindex(y) + x];
381
382 /* main loop */
383 while ((ch = *new++) != '\0')
384 {
385 /* if either character or color are different, an update is needed */
386 /* but only when the screen is wide enough */
387 if (y < (smart_terminal ? screen_length : Largest) && x < display_width &&
388 (ch != *bufp || newcolor != *colorp))
389 {
390 /* check cursor */
391 if (y != curr_y || x != curr_x)
392 {
393 /* have to move the cursor */
394 display_move(x, y);
395 }
396
397 /* write character */
398 #ifdef ENABLE_COLOR
399 if (curr_color != newcolor)
400 {
401 fputs(color_setstr(newcolor), stdout);
402 curr_color = newcolor;
403 }
404 #endif
405 putchar(ch);
406 *bufp = ch;
407 *colorp = curr_color;
408 curr_x++;
409 }
410
411 /* move */
412 x++;
413 virt_x++;
414 bufp++;
415 colorp++;
416 }
417
418 /* eol handling */
419 if (eol && *bufp != '\0')
420 {
421 dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp);
422 /* make sure we are color 0 */
423 #ifdef ENABLE_COLOR
424 if (curr_color != 0)
425 {
426 fputs(color_setstr(0), stdout);
427 curr_color = 0;
428 }
429 #endif
430
431 /* make sure we are at the end */
432 if (x != curr_x || y != curr_y)
433 {
434 screen_move(x, y);
435 curr_x = x;
436 curr_y = y;
437 }
438
439 /* clear to end */
440 screen_cleareol(strlen(bufp));
441
442 /* clear out whats left of this line's buffer */
443 diff = display_width - x;
444 if (diff > 0)
445 {
446 memzero(bufp, diff);
447 memzero(colorp, diff);
448 }
449 }
450 }
451
452 void
display_fmt(int x,int y,int newcolor,int eol,char * fmt,...)453 display_fmt(int x, int y, int newcolor, int eol, char *fmt, ...)
454
455 {
456 va_list argp;
457
458 va_start(argp, fmt);
459
460 vsnprintf(scratchbuf, MAX_COLS, fmt, argp);
461 display_write(x, y, newcolor, eol, scratchbuf);
462 }
463
464 void
display_cte()465 display_cte()
466
467 {
468 int len;
469 int y;
470 char *p;
471 int need_clear = 0;
472
473 /* is there anything out there that needs to be cleared? */
474 p = &screenbuf[lineindex(virt_y) + virt_x];
475 if (*p != '\0')
476 {
477 need_clear = 1;
478 }
479 else
480 {
481 /* this line is clear, what about the rest? */
482 y = virt_y;
483 while (++y < screen_length)
484 {
485 if (screenbuf[lineindex(y)] != '\0')
486 {
487 need_clear = 1;
488 break;
489 }
490 }
491 }
492
493 if (need_clear)
494 {
495 dprintf("display_cte: clearing\n");
496
497 /* we will need this later */
498 len = lineindex(virt_y) + virt_x;
499
500 /* move to x and y, then clear to end */
501 display_move(virt_x, virt_y);
502 if (!screen_cte())
503 {
504 /* screen has no clear to end, so do it by hand */
505 p = &screenbuf[len];
506 len = strlen(p);
507 if (len > 0)
508 {
509 screen_cleareol(len);
510 }
511 while (++virt_y < screen_length)
512 {
513 display_move(0, virt_y);
514 p = &screenbuf[lineindex(virt_y)];
515 len = strlen(p);
516 if (len > 0)
517 {
518 screen_cleareol(len);
519 }
520 }
521 }
522
523 /* clear the screenbuf */
524 memzero(&screenbuf[len], bufsize - len);
525 memzero(&colorbuf[len], bufsize - len);
526 }
527 }
528
529 static void
summary_format(int x,int y,int * numbers,char ** names,int * cidx)530 summary_format(int x, int y, int *numbers, char **names, int *cidx)
531
532 {
533 register int num;
534 register char *thisname;
535 register char *lastname = NULL;
536 register int color;
537
538 /* format each number followed by its string */
539 while ((thisname = *names++) != NULL)
540 {
541 /* get the number to format */
542 num = *numbers++;
543 color = 0;
544
545 /* display only non-zero numbers */
546 if (num != 0)
547 {
548 /* write the previous name */
549 if (lastname != NULL)
550 {
551 display_write(-1, -1, 0, 0, lastname);
552 }
553
554 #ifdef ENABLE_COLOR
555 if (cidx != NULL)
556 {
557 /* choose a color */
558 color = color_test(*cidx++, num);
559 }
560 #endif
561
562 /* write this number if positive */
563 if (num > 0)
564 {
565 display_write(x, y, color, 0, itoa(num));
566 }
567
568 /* defer writing this name */
569 lastname = thisname;
570
571 /* next iteration will not start at x, y */
572 x = y = -1;
573 }
574 }
575
576 /* if the last string has a separator on the end, it has to be
577 written with care */
578 if (lastname != NULL)
579 {
580 if ((num = strlen(lastname)) > 1 &&
581 lastname[num-2] == ',' && lastname[num-1] == ' ')
582 {
583 display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname);
584 }
585 else
586 {
587 display_write(-1, -1, 0, 1, lastname);
588 }
589 }
590 }
591
592 static void
summary_format_memory(int x,int y,long * numbers,char ** names,int * cidx)593 summary_format_memory(int x, int y, long *numbers, char **names, int *cidx)
594
595 {
596 register long num;
597 register int color;
598 register char *thisname;
599 register char *lastname = NULL;
600
601 /* format each number followed by its string */
602 while ((thisname = *names++) != NULL)
603 {
604 /* get the number to format */
605 num = *numbers++;
606 color = 0;
607
608 /* display only non-zero numbers */
609 if (num != 0)
610 {
611 /* write the previous name */
612 if (lastname != NULL)
613 {
614 display_write(-1, -1, 0, 0, lastname);
615 }
616
617 /* defer writing this name */
618 lastname = thisname;
619
620 #ifdef ENABLE_COLOR
621 /* choose a color */
622 color = color_test(*cidx++, num);
623 #endif
624
625 /* is this number in kilobytes? */
626 if (thisname[0] == 'K')
627 {
628 display_write(x, y, color, 0, format_k(num));
629 lastname++;
630 }
631 else
632 {
633 display_write(x, y, color, 0, itoa((int)num));
634 }
635
636 /* next iteration will not start at x, y */
637 x = y = -1;
638 }
639 }
640
641 /* if the last string has a separator on the end, it has to be
642 written with care */
643 if (lastname != NULL)
644 {
645 if ((num = strlen(lastname)) > 1 &&
646 lastname[num-2] == ',' && lastname[num-1] == ' ')
647 {
648 display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname);
649 }
650 else
651 {
652 display_write(-1, -1, 0, 1, lastname);
653 }
654 }
655 }
656
657 /*
658 * int display_resize()
659 *
660 * Reallocate buffer space needed by the display package to accomodate
661 * a new screen size. Must be called whenever the screen's size has
662 * changed. Returns the number of lines available for displaying
663 * processes or -1 if there was a problem allocating space.
664 */
665
666 int
display_resize()667 display_resize()
668
669 {
670 register int top_lines;
671 register int newsize;
672
673 /* calculate the current dimensions */
674 /* if operating in "dumb" mode, we only need one line */
675 top_lines = smart_terminal ? screen_length : 1;
676
677 /* we don't want more than MAX_COLS columns, since the machine-dependent
678 modules make static allocations based on MAX_COLS and we don't want
679 to run off the end of their buffers */
680 display_width = screen_width;
681 if (display_width >= MAX_COLS)
682 {
683 display_width = MAX_COLS - 1;
684 }
685
686 /* see how much space we need */
687 newsize = top_lines * (MAX_COLS + 1);
688
689 /* reallocate only if we need more than we already have */
690 if (newsize > bufsize)
691 {
692 /* deallocate any previous buffer that may have been there */
693 if (screenbuf != NULL)
694 {
695 free(screenbuf);
696 }
697 if (colorbuf != NULL)
698 {
699 free(colorbuf);
700 }
701
702 /* allocate space for the screen and color buffers */
703 bufsize = newsize;
704 screenbuf = (char *)calloc(bufsize, sizeof(char));
705 colorbuf = (char *)calloc(bufsize, sizeof(char));
706 if (screenbuf == NULL || colorbuf == NULL)
707 {
708 /* oops! */
709 return(-1);
710 }
711 }
712 else
713 {
714 /* just clear them out */
715 memzero(screenbuf, bufsize);
716 memzero(colorbuf, bufsize);
717 }
718
719 /* adjust total lines on screen to lines available for procs */
720 if (top_lines > y_procs)
721 top_lines -= y_procs;
722 else
723 top_lines = 0;
724
725 /* return number of lines available */
726 /* for dumb terminals, pretend like we can show any amount */
727 return(smart_terminal ? top_lines : Largest);
728 }
729
730 int
display_lines()731 display_lines()
732
733 {
734 return(smart_terminal ? screen_length : Largest);
735 }
736
737 int
display_columns()738 display_columns()
739
740 {
741 return(display_width);
742 }
743
744 /*
745 * int display_init(struct statics *statics)
746 *
747 * Initialize the display system based on information in the statics
748 * structure. Returns the number of lines available for displaying
749 * processes or -1 if there was an error.
750 */
751
752 int
display_init(struct statics * statics)753 display_init(struct statics *statics)
754
755 {
756 register int top_lines;
757 register char **pp;
758 register char *p;
759 register int *ip;
760 register int i;
761
762 /* certain things may influence the screen layout,
763 so look at those first */
764
765 /* More than one core will shif the parts of the display down */
766 if (enable_ncpus != 0 && n_cpus > 1)
767 {
768 /* adjust screen placements */
769 y_mem = y_mem + n_cpus -1;
770 y_swap = y_swap + n_cpus -1;
771 y_message = y_message + n_cpus -1;
772 y_header = y_header + n_cpus -1;
773 y_idlecursor = y_idlecursor + n_cpus -1;
774 y_procs = y_procs + n_cpus -1;
775 }
776
777 /* a kernel line shifts parts of the display down */
778 kernel_names = statics->kernel_names;
779 if ((num_kernel = string_count(kernel_names)) > 0)
780 {
781 /* adjust screen placements */
782 y_mem++;
783 y_swap++;
784 y_message++;
785 y_header++;
786 y_idlecursor++;
787 y_procs++;
788 }
789
790 /* a swap line shifts parts of the display down one */
791 swap_names = statics->swap_names;
792 if ((num_swap = string_count(swap_names)) > 0)
793 {
794 /* adjust screen placements */
795 y_message++;
796 y_header++;
797 y_idlecursor++;
798 y_procs++;
799 }
800
801 /* call resize to do the dirty work */
802 top_lines = display_resize();
803
804 /*
805 * save pointers and allocate space for names. Even if top_lines <= -1
806 * the code will dereference many of these pointers and arrays.
807 */
808 procstate_names = statics->procstate_names;
809 num_procstates = string_count(procstate_names);
810
811 lprocstates = (int *)calloc(num_procstates, sizeof(int));
812
813 cpustate_names = statics->cpustate_names;
814 num_cpustates = string_count(cpustate_names);
815 lcpustates = (int *)calloc(num_cpustates, sizeof(int));
816 cpustate_columns = (int *)calloc(num_cpustates, sizeof(int));
817 memory_names = statics->memory_names;
818 num_memory = string_count(memory_names);
819
820 /* calculate starting columns where needed */
821 cpustate_total_length = 0;
822 pp = cpustate_names;
823 ip = cpustate_columns;
824 while (*pp != NULL)
825 {
826 *ip++ = cpustate_total_length;
827 if ((i = strlen(*pp++)) > 0)
828 {
829 cpustate_total_length += i + 8;
830 }
831 }
832
833 #ifdef ENABLE_COLOR
834 /* set up color tags for loadavg */
835 load_cidx[0] = color_tag("1min");
836 load_cidx[1] = color_tag("5min");
837 load_cidx[2] = color_tag("15min");
838
839 /* find header color */
840 header_cidx = color_tag("header");
841
842 /* color tags for cpu states */
843 cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int));
844 i = 0;
845 p = strcpyend(scratchbuf, "cpu.");
846 while (i < num_cpustates)
847 {
848 strcpy(p, cpustate_names[i]);
849 cpustate_cidx[i++] = color_tag(scratchbuf);
850 }
851
852 /* color tags for kernel */
853 if (num_kernel > 0)
854 {
855 kernel_cidx = (int *)malloc(num_kernel * sizeof(int));
856 i = 0;
857 p = strcpyend(scratchbuf, "kernel.");
858 while (i < num_kernel)
859 {
860 strcpy(p, homogenize(kernel_names[i]+1));
861 kernel_cidx[i++] = color_tag(scratchbuf);
862 }
863 }
864
865 /* color tags for memory */
866 memory_cidx = (int *)malloc(num_memory * sizeof(int));
867 i = 0;
868 p = strcpyend(scratchbuf, "memory.");
869 while (i < num_memory)
870 {
871 strcpy(p, homogenize(memory_names[i]+1));
872 memory_cidx[i++] = color_tag(scratchbuf);
873 }
874
875 /* color tags for swap */
876 if (num_swap > 0)
877 {
878 swap_cidx = (int *)malloc(num_swap * sizeof(int));
879 i = 0;
880 p = strcpyend(scratchbuf, "swap.");
881 while (i < num_swap)
882 {
883 strcpy(p, homogenize(swap_names[i]+1));
884 swap_cidx[i++] = color_tag(scratchbuf);
885 }
886 }
887 #endif
888
889 /* return number of lines available (or error) */
890 return(top_lines);
891 }
892
893 static void
pr_loadavg(double avg,int i)894 pr_loadavg(double avg, int i)
895
896 {
897 int color = 0;
898
899 #ifdef ENABLE_COLOR
900 color = color_test(load_cidx[i], (int)(avg * 100));
901 #endif
902 display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0,
903 avg < 10.0 ? " %5.2f" : " %5.1f", avg);
904 display_write(-1, -1, 0, 0, (i < 2 ? "," : ";"));
905 }
906
907 void
i_loadave(int mpid,double * avenrun)908 i_loadave(int mpid, double *avenrun)
909
910 {
911 register int i;
912
913 /* mpid == -1 implies this system doesn't have an _mpid */
914 if (mpid != -1)
915 {
916 display_fmt(0, 0, 0, 0,
917 "last pid: %5d; load avg:", mpid);
918 x_loadave = X_LOADAVE;
919 }
920 else
921 {
922 display_write(0, 0, 0, 0, "load averages:");
923 x_loadave = X_LOADAVE - X_LASTPIDWIDTH;
924 }
925 for (i = 0; i < 3; i++)
926 {
927 pr_loadavg(avenrun[i], i);
928 }
929
930 lmpid = mpid;
931 }
932
933 void
u_loadave(int mpid,double * avenrun)934 u_loadave(int mpid, double *avenrun)
935
936 {
937 register int i;
938
939 if (mpid != -1)
940 {
941 /* change screen only when value has really changed */
942 if (mpid != lmpid)
943 {
944 display_fmt(x_lastpid, y_lastpid, 0, 0,
945 "%5d", mpid);
946 lmpid = mpid;
947 }
948 }
949
950 /* display new load averages */
951 for (i = 0; i < 3; i++)
952 {
953 pr_loadavg(avenrun[i], i);
954 }
955 }
956
957 static char minibar_buffer[64];
958 #define MINIBAR_WIDTH 20
959
960 void
i_minibar(int (* formatter)(char *,int))961 i_minibar(int (*formatter)(char *, int))
962 {
963 (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
964
965 display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
966 }
967
968 void
u_minibar(int (* formatter)(char *,int))969 u_minibar(int (*formatter)(char *, int))
970 {
971 (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
972
973 display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
974 }
975
976 static int uptime_days;
977 static int uptime_hours;
978 static int uptime_mins;
979 static int uptime_secs;
980
981 void
i_uptime(time_t uptime)982 i_uptime(time_t uptime)
983 {
984 uptime += 30;
985 uptime_days = uptime / 86400;
986 uptime %= 86400;
987 uptime_hours = uptime / 3600;
988 uptime %= 3600;
989 uptime_mins = uptime / 60;
990 uptime_secs = uptime % 60;
991
992 /*
993 * Display the uptime.
994 */
995
996 display_fmt(x_uptime, y_uptime, 0, 0,
997 " up %d+%02d:%02d:%02d",
998 uptime_days, uptime_hours, uptime_mins, uptime_secs);
999 }
1000
1001 void
u_uptime(time_t uptime)1002 u_uptime(time_t uptime)
1003 {
1004 i_uptime(uptime);
1005 }
1006
1007
1008 void
i_timeofday(time_t * tod)1009 i_timeofday(time_t *tod)
1010
1011 {
1012 /*
1013 * Display the current time.
1014 * "ctime" always returns a string that looks like this:
1015 *
1016 * Sun Sep 16 01:03:52 1973
1017 * 012345678901234567890123
1018 * 1 2
1019 *
1020 * We want indices 11 thru 18 (length 8).
1021 */
1022
1023 int x;
1024
1025 /* where on the screen do we start? */
1026 x = (smart_terminal ? screen_width : 79) - 8;
1027
1028 /* but don't bump in to uptime */
1029 if (x < x_uptime + 19)
1030 {
1031 x = x_uptime + 19;
1032 }
1033
1034 /* display it */
1035 display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11]));
1036 }
1037
1038 static int ltotal = 0;
1039 static int lthreads = 0;
1040
1041 /*
1042 * *_procstates(total, brkdn, names) - print the process summary line
1043 */
1044
1045
1046 void
i_procstates(int total,int * brkdn,int threads)1047 i_procstates(int total, int *brkdn, int threads)
1048
1049 {
1050 /* write current number of processes and remember the value */
1051 display_fmt(0, y_procstate, 0, 0,
1052 "%d %s: ", total, threads ? "threads" : "processes");
1053 ltotal = total;
1054
1055 /* remember where the summary starts */
1056 x_procstate = virt_x;
1057
1058 if (total > 0)
1059 {
1060 /* format and print the process state summary */
1061 summary_format(-1, -1, brkdn, procstate_names, NULL);
1062
1063 /* save the numbers for next time */
1064 memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
1065 lthreads = threads;
1066 }
1067 }
1068
1069 void
u_procstates(int total,int * brkdn,int threads)1070 u_procstates(int total, int *brkdn, int threads)
1071
1072 {
1073 /* if threads state has changed, do a full update */
1074 if (lthreads != threads)
1075 {
1076 i_procstates(total, brkdn, threads);
1077 return;
1078 }
1079
1080 /* update number of processes only if it has changed */
1081 if (ltotal != total)
1082 {
1083 display_fmt(0, y_procstate, 0, 0,
1084 "%d", total);
1085
1086 /* if number of digits differs, rewrite the label */
1087 if (digits(total) != digits(ltotal))
1088 {
1089 display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes");
1090 x_procstate = virt_x;
1091 }
1092
1093 /* save new total */
1094 ltotal = total;
1095 }
1096
1097 /* see if any of the state numbers has changed */
1098 if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
1099 {
1100 /* format and update the line */
1101 summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL);
1102 memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
1103 }
1104 }
1105
1106 /*
1107 * *_cpustates(states, names) - print the cpu state percentages
1108 */
1109
1110 /* cpustates_tag() calculates the correct tag to use to label the line */
1111
1112 char *
cpustates_tag()1113 cpustates_tag()
1114
1115 {
1116 register char *use;
1117
1118 static char *short_tag = "CPU: ";
1119 static char *long_tag = "CPU states: ";
1120
1121 /* if length + strlen(long_tag) >= screen_width, then we have to
1122 use the shorter tag (we subtract 2 to account for ": ") */
1123 if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
1124 {
1125 use = short_tag;
1126 }
1127 else
1128 {
1129 use = long_tag;
1130 }
1131
1132 /* set x_cpustates accordingly then return result */
1133 x_cpustates = strlen(use);
1134 return(use);
1135 }
1136
1137 void
i_cpustates(int * states)1138 i_cpustates(int *states)
1139
1140 {
1141 int value;
1142 char **names;
1143 char *thisname;
1144 int *colp;
1145 int color = 0;
1146 int cpu;
1147 #ifdef ENABLE_COLOR
1148 int *cidx = cpustate_cidx;
1149 #endif
1150
1151 /* initialize */
1152 names = cpustate_names;
1153 colp = cpustate_columns;
1154
1155 /* print tag */
1156 if (enable_ncpus !=0 && n_cpus > 1) {
1157 for (cpu = 0; cpu < n_cpus; ++cpu) {
1158 int y_pos = y_cpustates;
1159 y_pos = y_pos + cpu;
1160 colp = cpustate_columns;
1161 names = cpustate_names;
1162 display_write(0, y_cpustates+cpu, 0, 0, cpustates_tag());
1163
1164 /* now walk thru the names and print the line */
1165 while ((thisname = *names++) != NULL) {
1166 if (*thisname != '\0') {
1167 /* retrieve the value and remember it */
1168 value = *states;
1169
1170 #ifdef ENABLE_COLOR
1171 /* determine color number to use */
1172 color = color_test(*cidx++, value/10);
1173 #endif
1174 /* if percentage is >= 1000, print it as 100% */
1175 display_fmt(x_cpustates + *colp, y_pos,
1176 color, 0,
1177 (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
1178 ((float)value)/10.,
1179 thisname,
1180 *names != NULL ? ", " : "");
1181
1182 }
1183 /* increment */
1184 colp++;
1185 states++;
1186 }
1187 /* copy over values into "last" array */
1188 memcpy(lcpustates, states, num_cpustates * sizeof(int));
1189 }
1190 } else {
1191 display_write(0, y_cpustates, 0, 0, cpustates_tag());
1192
1193 /* now walk thru the names and print the line */
1194 while ((thisname = *names++) != NULL)
1195 {
1196 if (*thisname != '\0')
1197 {
1198 /* retrieve the value and remember it */
1199 value = *states;
1200
1201 #ifdef ENABLE_COLOR
1202 /* determine color number to use */
1203 color = color_test(*cidx++, value/10);
1204 #endif
1205
1206 /* if percentage is >= 1000, print it as 100% */
1207 display_fmt(x_cpustates + *colp, y_cpustates,
1208 color, 0,
1209 (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
1210 ((float)value)/10.,
1211 thisname,
1212 *names != NULL ? ", " : "");
1213
1214 }
1215 /* increment */
1216 colp++;
1217 states++;
1218 }
1219
1220 /* copy over values into "last" array */
1221 memcpy(lcpustates, states, num_cpustates * sizeof(int));
1222 }
1223
1224 }
1225
1226 void
u_cpustates(int * states)1227 u_cpustates(int *states)
1228
1229 {
1230 int value;
1231 char **names = cpustate_names;
1232 char *thisname;
1233 int *lp;
1234 int *colp;
1235 int color = 0;
1236 int cpu;
1237 #ifdef ENABLE_COLOR
1238 int *cidx = cpustate_cidx;
1239 #endif
1240
1241
1242 if (enable_ncpus != 0 && n_cpus > 1 ) {
1243 for (cpu = 0; cpu < n_cpus; ++cpu) {
1244 lp = lcpustates;
1245 int y_pos = y_cpustates;
1246 y_pos = y_pos + cpu;
1247 colp = cpustate_columns;
1248 char **names = cpustate_names;
1249 /* we could be much more optimal about this */
1250 while ((thisname = *names++) != NULL) {
1251 if (*thisname != '\0') {
1252 /* yes, change it */
1253 /* retrieve value and remember it */
1254 value = *states;
1255
1256 #ifdef ENABLE_COLOR
1257 /* determine color number to use */
1258 color = color_test(*cidx, value/10);
1259 #endif
1260 /* if percentage is >= 1000, print it as 100% */
1261 display_fmt(x_cpustates + *colp, y_pos, color, 0,
1262 (value >= 1000 ? "%4.0f" : "%4.1f"),
1263 ((double)value)/10.);
1264
1265 #ifdef ENABLE_COLOR
1266 cidx++;
1267 #endif
1268 }
1269 /* increment and move on */
1270 lp++;
1271 states++;
1272 colp++;
1273 }
1274 }
1275 } else {
1276 lp = lcpustates;
1277 colp = cpustate_columns;
1278
1279 /* we could be much more optimal about this */
1280 while ((thisname = *names++) != NULL)
1281 {
1282 if (*thisname != '\0')
1283 {
1284 /* did the value change since last time? */
1285 if (*lp != *states)
1286 {
1287 /* yes, change it */
1288 /* retrieve value and remember it */
1289 value = *states;
1290
1291 #ifdef ENABLE_COLOR
1292 /* determine color number to use */
1293 color = color_test(*cidx, value/10);
1294 #endif
1295
1296 /* if percentage is >= 1000, print it as 100% */
1297 display_fmt(x_cpustates + *colp, y_cpustates, color, 0,
1298 (value >= 1000 ? "%4.0f" : "%4.1f"),
1299 ((double)value)/10.);
1300
1301 /* remember it for next time */
1302 *lp = value;
1303 }
1304 #ifdef ENABLE_COLOR
1305 cidx++;
1306 #endif
1307 }
1308
1309 /* increment and move on */
1310 lp++;
1311 states++;
1312 colp++;
1313 }
1314 }
1315 }
1316
1317 void
z_cpustates()1318 z_cpustates()
1319
1320 {
1321 register int i = 0;
1322 register char **names = cpustate_names;
1323 register char *thisname;
1324 register int *lp;
1325 int cpu;
1326
1327 /* print tag */
1328 if (enable_ncpus != 0 && n_cpus > 1) {
1329 for (cpu = 0; cpu < n_cpus; ++cpu) {
1330 display_write(0, y_cpustates + cpu, 0, 0, cpustates_tag());
1331 char **names = cpustate_names;
1332 i = 0;
1333 while ((thisname = *names++) != NULL) {
1334 if (*thisname != '\0') {
1335 display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ",
1336 thisname);
1337 }
1338 }
1339 /* fill the "last" array with all -1s, to insure correct updating */
1340 lp = lcpustates;
1341 i = num_cpustates;
1342 while (--i >= 0) {
1343 *lp++ = -1;
1344 }
1345 }
1346 } else {
1347 display_write(0, y_cpustates, 0, 0, cpustates_tag());
1348
1349 while ((thisname = *names++) != NULL)
1350 {
1351 if (*thisname != '\0')
1352 {
1353 display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ",
1354 thisname);
1355 }
1356 }
1357
1358 /* fill the "last" array with all -1s, to insure correct updating */
1359 lp = lcpustates;
1360 i = num_cpustates;
1361 while (--i >= 0)
1362 {
1363 *lp++ = -1;
1364 }
1365 }
1366 }
1367
1368 /*
1369 * *_kernel(stats) - print "Kernel: " followed by the kernel summary string
1370 *
1371 * Assumptions: cursor is on "lastline", the previous line
1372 */
1373
1374 void
i_kernel(int * stats)1375 i_kernel(int *stats)
1376
1377 {
1378 if (num_kernel > 0)
1379 {
1380 display_write(0, y_kernel, 0, 0, "Kernel: ");
1381
1382 /* format and print the kernel summary */
1383 summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
1384 }
1385 }
1386
1387 void
u_kernel(int * stats)1388 u_kernel(int *stats)
1389
1390 {
1391 if (num_kernel > 0)
1392 {
1393 /* format the new line */
1394 summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
1395 }
1396 }
1397
1398 /*
1399 * *_memory(stats) - print "Memory: " followed by the memory summary string
1400 *
1401 * Assumptions: cursor is on "lastline", the previous line
1402 */
1403
1404 void
i_memory(long * stats)1405 i_memory(long *stats)
1406
1407 {
1408 display_write(0, y_mem, 0, 0, "Memory: ");
1409
1410 /* format and print the memory summary */
1411 summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
1412 }
1413
1414 void
u_memory(long * stats)1415 u_memory(long *stats)
1416
1417 {
1418 /* format the new line */
1419 summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
1420 }
1421
1422 /*
1423 * *_swap(stats) - print "Swap: " followed by the swap summary string
1424 *
1425 * Assumptions: cursor is on "lastline", the previous line
1426 *
1427 * These functions only print something when num_swap > 0
1428 */
1429
1430 void
i_swap(long * stats)1431 i_swap(long *stats)
1432
1433 {
1434 if (num_swap > 0)
1435 {
1436 /* print the tag */
1437 display_write(0, y_swap, 0, 0, "Swap: ");
1438
1439 /* format and print the swap summary */
1440 summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
1441 }
1442 }
1443
1444 void
u_swap(long * stats)1445 u_swap(long *stats)
1446
1447 {
1448 if (num_swap > 0)
1449 {
1450 /* format the new line */
1451 summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
1452 }
1453 }
1454
1455 /*
1456 * *_message() - print the next pending message line, or erase the one
1457 * that is there.
1458 *
1459 * Note that u_message is (currently) the same as i_message.
1460 *
1461 * Assumptions: lastline is consistent
1462 */
1463
1464 /*
1465 * i_message is funny because it gets its message asynchronously (with
1466 * respect to screen updates). Messages are taken out of the
1467 * circular message_buf and displayed one at a time.
1468 */
1469
1470 void
i_message(struct timeval * now)1471 i_message(struct timeval *now)
1472
1473 {
1474 struct timeval my_now;
1475 int i = 0;
1476
1477 dprintf("i_message(%08x)\n", now);
1478
1479 /* if now is NULL we have to get it ourselves */
1480 if (now == NULL)
1481 {
1482 time_get(&my_now);
1483 now = &my_now;
1484 }
1485
1486 /* now that we have been called, messages no longer need to be held */
1487 message_hold = 0;
1488
1489 dprintf("i_message: now %d, message_time %d\n",
1490 now->tv_sec, message_time.tv_sec);
1491
1492 if (smart_terminal)
1493 {
1494 /* is it time to change the message? */
1495 if (timercmp(now, &message_time, > ))
1496 {
1497 /* yes, free the current message */
1498 dprintf("i_message: timer expired\n");
1499 if (message_current != NULL)
1500 {
1501 free(message_current);
1502 message_current = NULL;
1503 }
1504
1505 /* is there a new message to be displayed? */
1506 if (message_first != message_last)
1507 {
1508 /* move index to next message */
1509 if (++message_first == MAX_MESSAGES) message_first = 0;
1510
1511 /* make the next message the current one */
1512 message_current = message_buf[message_first];
1513
1514 /* show it */
1515 dprintf("i_message: showing \"%s\"\n", message_current);
1516 display_move(0, y_message);
1517 screen_standout(message_current);
1518 i = strlen(message_current);
1519
1520 /* set the expiration timer */
1521 message_time = *now;
1522 message_time.tv_sec += MESSAGE_DISPLAY_TIME;
1523
1524 /* clear the rest of the line */
1525 screen_cleareol(message_length - i);
1526 putchar('\r');
1527 message_length = i;
1528 }
1529 else
1530 {
1531 /* just clear what was there before, if anything */
1532 if (message_length > 0)
1533 {
1534 display_move(0, y_message);
1535 screen_cleareol(message_length);
1536 putchar('\r');
1537 message_length = 0;
1538 }
1539 }
1540 }
1541 }
1542 }
1543
1544 void
u_message(struct timeval * now)1545 u_message(struct timeval *now)
1546
1547 {
1548 i_message(now);
1549 }
1550
1551 static int header_length;
1552
1553 /*
1554 * *_header(text) - print the header for the process area
1555 *
1556 * Assumptions: cursor is on the previous line and lastline is consistent
1557 */
1558
1559 void
i_header(char * text)1560 i_header(char *text)
1561
1562 {
1563 int header_color = 0;
1564
1565 #ifdef ENABLE_COLOR
1566 header_color = color_test(header_cidx, 0);
1567 #endif
1568 header_length = strlen(text);
1569 if (header_status)
1570 {
1571 display_write(x_header, y_header, header_color, 1, text);
1572 }
1573 }
1574
1575 /*ARGSUSED*/
1576 void
u_header(char * text)1577 u_header(char *text)
1578
1579 {
1580 int header_color = 0;
1581
1582 #ifdef ENABLE_COLOR
1583 header_color = color_test(header_cidx, 0);
1584 #endif
1585 display_write(x_header, y_header, header_color, 1,
1586 header_status ? text : "");
1587 }
1588
1589 /*
1590 * *_process(line, thisline) - print one process line
1591 *
1592 * Assumptions: lastline is consistent
1593 */
1594
1595 void
i_process(int line,char * thisline)1596 i_process(int line, char *thisline)
1597
1598 {
1599 /* truncate the line to conform to our current screen width */
1600 thisline[display_width] = '\0';
1601
1602 /* write the line out */
1603 display_write(0, y_procs + line, 0, 1, thisline);
1604 }
1605
1606 void
u_process(int line,char * new_line)1607 u_process(int line, char *new_line)
1608
1609 {
1610 i_process(line, new_line);
1611 }
1612
1613 void
i_endscreen()1614 i_endscreen()
1615
1616 {
1617 if (smart_terminal)
1618 {
1619 /* move the cursor to a pleasant place */
1620 display_move(x_idlecursor, y_idlecursor);
1621 }
1622 else
1623 {
1624 /* separate this display from the next with some vertical room */
1625 fputs("\n\n", stdout);
1626 }
1627 fflush(stdout);
1628 }
1629
1630 void
u_endscreen()1631 u_endscreen()
1632
1633 {
1634 if (smart_terminal)
1635 {
1636 /* clear-to-end the display */
1637 display_cte();
1638
1639 /* move the cursor to a pleasant place */
1640 display_move(x_idlecursor, y_idlecursor);
1641 fflush(stdout);
1642 }
1643 else
1644 {
1645 /* separate this display from the next with some vertical room */
1646 fputs("\n\n", stdout);
1647 }
1648 }
1649
1650 void
display_header(int t)1651 display_header(int t)
1652
1653 {
1654 header_status = t != 0;
1655 }
1656
1657 void
message_mark()1658 message_mark()
1659
1660 {
1661 message_barrier = Yes;
1662 }
1663
1664 void
message_expire()1665 message_expire()
1666
1667 {
1668 message_time.tv_sec = 0;
1669 message_time.tv_usec = 0;
1670 }
1671
1672 void
message_flush()1673 message_flush()
1674
1675 {
1676 message_first = message_last;
1677 message_time.tv_sec = 0;
1678 message_time.tv_usec = 0;
1679 }
1680
1681 /*
1682 * void new_message_v(char *msgfmt, va_list ap)
1683 *
1684 * Display a message in the message area. This function takes a va_list for
1685 * the arguments. Safe to call before display_init. This function only
1686 * queues a message for display, and allowed for multiple messages to be
1687 * queued. The i_message function drains the queue and actually writes the
1688 * messages on the display.
1689 */
1690
1691
1692 void
new_message_v(char * msgfmt,va_list ap)1693 new_message_v(char *msgfmt, va_list ap)
1694
1695 {
1696 int i;
1697 int empty;
1698 char msg[MAX_COLS];
1699
1700 /* if message_barrier is active, remove all pending messages */
1701 if (message_barrier)
1702 {
1703 message_flush();
1704 message_barrier = No;
1705 }
1706
1707 /* first, format the message */
1708 (void) vsnprintf(msg, sizeof(msg), msgfmt, ap);
1709
1710 /* where in the buffer will it go? */
1711 i = message_last + 1;
1712 if (i >= MAX_MESSAGES) i = 0;
1713
1714 /* make sure the buffer is not full */
1715 if (i != message_first)
1716 {
1717 /* insert it in to message_buf */
1718 message_buf[i] = strdup(msg);
1719 dprintf("new_message_v: new message inserted in slot %d\n", i);
1720
1721 /* remember if the buffer is empty and set the index */
1722 empty = message_last == message_first;
1723 message_last = i;
1724
1725 /* is message_buf otherwise empty and have we started displaying? */
1726 if (empty && !message_hold)
1727 {
1728 /* we can display the message now */
1729 i_message(NULL);
1730 }
1731 }
1732 }
1733
1734 /*
1735 * void new_message(int type, char *msgfmt, ...)
1736 *
1737 * Display a message in the message area. It is safe to call this function
1738 * before display_init. Messages logged before the display is drawn will be
1739 * held and displayed later.
1740 */
1741
1742 void
new_message(char * msgfmt,...)1743 new_message(char *msgfmt, ...)
1744
1745 {
1746 va_list ap;
1747
1748 va_start(ap, msgfmt);
1749 new_message_v(msgfmt, ap);
1750 va_end(ap);
1751 }
1752
1753 /*
1754 * void message_error(char *msgfmt, ...)
1755 *
1756 * Put an error message in the message area. It is safe to call this function
1757 * before display_init. Messages logged before the display is drawn will be
1758 * held and displayed later.
1759 */
1760
1761 void
message_error(char * msgfmt,...)1762 message_error(char *msgfmt, ...)
1763
1764 {
1765 va_list ap;
1766
1767 va_start(ap, msgfmt);
1768 new_message_v(msgfmt, ap);
1769 fflush(stdout);
1770 va_end(ap);
1771 }
1772
1773 /*
1774 * void message_clear()
1775 *
1776 * Clear message area and flush all pending messages.
1777 */
1778
1779 void
message_clear()1780 message_clear()
1781
1782 {
1783 /* remove any existing message */
1784 if (message_current != NULL)
1785 {
1786 display_move(0, y_message);
1787 screen_cleareol(message_length);
1788 free(message_current);
1789 message_current = NULL;
1790 }
1791
1792 /* flush all pending messages */
1793 message_flush();
1794 }
1795
1796 /*
1797 * void message_prompt_v(int so, char *msgfmt, va_list ap)
1798 *
1799 * Place a prompt in the message area. A prompt is different from a
1800 * message as follows: it is displayed immediately, overwriting any
1801 * message that may already be there, it may be highlighted in standout
1802 * mode (if "so" is true), the cursor is left to rest at the end of the
1803 * prompt. This call causes all pending messages to be flushed.
1804 */
1805
1806 void
message_prompt_v(int so,char * msgfmt,va_list ap)1807 message_prompt_v(int so, char *msgfmt, va_list ap)
1808
1809 {
1810 char msg[MAX_COLS];
1811 int i;
1812
1813 /* clear out the message buffer */
1814 message_flush();
1815
1816 /* format the message */
1817 i = vsnprintf(msg, sizeof(msg), msgfmt, ap);
1818
1819 /* this goes over any existing message */
1820 display_move(0, y_message);
1821
1822 /* clear the entire line */
1823 screen_cleareol(message_length);
1824
1825 /* show the prompt */
1826 if (so)
1827 {
1828 screen_standout(msg);
1829 }
1830 else
1831 {
1832 fputs(msg, stdout);
1833 }
1834
1835 /* make it all visible */
1836 fflush(stdout);
1837
1838 /* even though we dont keep a copy of the prompt, track its length */
1839 message_length = i < MAX_COLS ? i : MAX_COLS;
1840 }
1841
1842 /*
1843 * void message_prompt(char *msgfmt, ...)
1844 *
1845 * Place a prompt in the message area (see message_prompt_v).
1846 */
1847
1848 void
message_prompt(char * msgfmt,...)1849 message_prompt(char *msgfmt, ...)
1850
1851 {
1852 va_list ap;
1853
1854 va_start(ap, msgfmt);
1855 message_prompt_v(Yes, msgfmt, ap);
1856 va_end(ap);
1857 }
1858
1859 void
message_prompt_plain(char * msgfmt,...)1860 message_prompt_plain(char *msgfmt, ...)
1861
1862 {
1863 va_list ap;
1864
1865 va_start(ap, msgfmt);
1866 message_prompt_v(No, msgfmt, ap);
1867 va_end(ap);
1868 }
1869
1870 /*
1871 * int readline(char *buffer, int size, int numeric)
1872 *
1873 * Read a line of input from the terminal. The line is placed in
1874 * "buffer" not to exceed "size". If "numeric" is true then the input
1875 * can only consist of digits. This routine handles all character
1876 * editing while keeping the terminal in cbreak mode. If "numeric"
1877 * is true then the number entered is returned. Otherwise the number
1878 * of character read in to "buffer" is returned.
1879 */
1880
1881 int
readline(char * buffer,int size,int numeric)1882 readline(char *buffer, int size, int numeric)
1883
1884 {
1885 register char *ptr = buffer;
1886 register char ch;
1887 register char cnt = 0;
1888
1889 /* allow room for null terminator */
1890 size -= 1;
1891
1892 /* read loop */
1893 while ((fflush(stdout), read(0, ptr, 1) > 0))
1894 {
1895 /* newline or return means we are done */
1896 if ((ch = *ptr) == '\n' || ch == '\r')
1897 {
1898 break;
1899 }
1900
1901 /* handle special editing characters */
1902 if (ch == ch_kill)
1903 {
1904 /* return null string */
1905 *buffer = '\0';
1906 putchar('\r');
1907 return(-1);
1908 }
1909 else if (ch == ch_werase)
1910 {
1911 /* erase previous word */
1912 if (cnt <= 0)
1913 {
1914 /* none to erase! */
1915 putchar('\7');
1916 }
1917 else
1918 {
1919 /*
1920 * First: remove all spaces till the first-non-space
1921 * Second: remove all non-spaces till the first-space
1922 */
1923 while(cnt > 0 && ptr[-1] == ' ')
1924 {
1925 fputs("\b \b", stdout);
1926 ptr--;
1927 cnt--;
1928 }
1929 while(cnt > 0 && ptr[-1] != ' ')
1930 {
1931 fputs("\b \b", stdout);
1932 ptr--;
1933 cnt--;
1934 }
1935 }
1936 }
1937 else if (ch == ch_erase)
1938 {
1939 /* erase previous character */
1940 if (cnt <= 0)
1941 {
1942 /* none to erase! */
1943 putchar('\7');
1944 }
1945 else
1946 {
1947 fputs("\b \b", stdout);
1948 ptr--;
1949 cnt--;
1950 }
1951 }
1952 /* check for character validity and buffer overflow */
1953 else if (cnt == size || (numeric && !isdigit((int)ch)) ||
1954 !isprint((int)ch))
1955 {
1956 /* not legal */
1957 putchar('\7');
1958 }
1959 else
1960 {
1961 /* echo it and store it in the buffer */
1962 putchar(ch);
1963 ptr++;
1964 cnt++;
1965 }
1966 }
1967
1968 /* all done -- null terminate the string */
1969 *ptr = '\0';
1970
1971 /* add response length to message_length */
1972 message_length += cnt;
1973
1974 /* return either inputted number or string length */
1975 putchar('\r');
1976 return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
1977 }
1978
1979 void
display_pagerstart()1980 display_pagerstart()
1981
1982 {
1983 display_clear();
1984 }
1985
1986 void
display_pagerend()1987 display_pagerend()
1988
1989 {
1990 char ch;
1991
1992 screen_standout("Hit any key to continue: ");
1993 fflush(stdout);
1994 (void) read(0, &ch, 1);
1995 }
1996
1997 void
display_pager(char * fmt,...)1998 display_pager(char *fmt, ...)
1999
2000 {
2001 va_list ap;
2002
2003 int ch;
2004 char readch;
2005 char buffer[MAX_COLS];
2006 char *data;
2007
2008 /* format into buffer */
2009 va_start(ap, fmt);
2010 (void) vsnprintf(buffer, MAX_COLS, fmt, ap);
2011 va_end(ap);
2012 data = buffer;
2013
2014 while ((ch = *data++) != '\0')
2015 {
2016 putchar(ch);
2017 if (ch == '\n')
2018 {
2019 if (++curr_y >= screen_length - 1)
2020 {
2021 screen_standout("...More...");
2022 fflush(stdout);
2023 (void) read(0, &readch, 1);
2024 putchar('\r');
2025 switch(readch)
2026 {
2027 case '\r':
2028 case '\n':
2029 curr_y--;
2030 break;
2031
2032 case 'q':
2033 return;
2034
2035 default:
2036 curr_y = 0;
2037 }
2038 }
2039 }
2040 }
2041 }
2042