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