xref: /NextBSD/contrib/top/display.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /*
2  *  Top users/processes display for Unix
3  *  Version 3
4  *
5  *  This program may be freely redistributed,
6  *  but this entire comment MUST remain intact.
7  *
8  *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
9  *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
10  *
11  * $FreeBSD$
12  */
13 
14 /*
15  *  This file contains the routines that display information on the screen.
16  *  Each section of the screen has two routines:  one for initially writing
17  *  all constant and dynamic text, and one for only updating the text that
18  *  changes.  The prefix "i_" is used on all the "initial" routines and the
19  *  prefix "u_" is used for all the "updating" routines.
20  *
21  *  ASSUMPTIONS:
22  *        None of the "i_" routines use any of the termcap capabilities.
23  *        In this way, those routines can be safely used on terminals that
24  *        have minimal (or nonexistant) terminal capabilities.
25  *
26  *        The routines are called in this order:  *_loadave, i_timeofday,
27  *        *_procstates, *_cpustates, *_memory, *_message, *_header,
28  *        *_process, u_endscreen.
29  */
30 
31 #include "os.h"
32 #include <ctype.h>
33 #include <time.h>
34 #include <sys/time.h>
35 
36 #include "screen.h"		/* interface to screen package */
37 #include "layout.h"		/* defines for screen position layout */
38 #include "display.h"
39 #include "top.h"
40 #include "top.local.h"
41 #include "boolean.h"
42 #include "machine.h"		/* we should eliminate this!!! */
43 #include "utils.h"
44 
45 #ifdef DEBUG
46 FILE *debug;
47 #endif
48 
49 /* imported from screen.c */
50 extern int overstrike;
51 
52 static int lmpid = 0;
53 static int last_hi = 0;		/* used in u_process and u_endscreen */
54 static int lastline = 0;
55 static int display_width = MAX_COLS;
56 
57 #define lineindex(l) ((l)*display_width)
58 
59 char *printable();
60 
61 /* things initialized by display_init and used thruout */
62 
63 /* buffer of proc information lines for display updating */
64 char *screenbuf = NULL;
65 
66 static char **procstate_names;
67 static char **cpustate_names;
68 static char **memory_names;
69 static char **arc_names;
70 static char **swap_names;
71 
72 static int num_procstates;
73 static int num_cpustates;
74 static int num_memory;
75 static int num_swap;
76 
77 static int *lprocstates;
78 static int *lcpustates;
79 static int *lmemory;
80 static int *lswap;
81 
82 static int num_cpus;
83 static int *cpustate_columns;
84 static int cpustate_total_length;
85 static int cpustates_column;
86 
87 static enum { OFF, ON, ERASE } header_status = ON;
88 
89 static int string_count();
90 static void summary_format();
91 static void line_update();
92 
93 int  x_lastpid =	10;
94 int  y_lastpid =	0;
95 int  x_loadave =	33;
96 int  x_loadave_nompid =	15;
97 int  y_loadave =	0;
98 int  x_procstate =	0;
99 int  y_procstate =	1;
100 int  x_brkdn =		15;
101 int  y_brkdn =		1;
102 int  x_mem =		5;
103 int  y_mem =		3;
104 int  x_arc =		5;
105 int  y_arc =		4;
106 int  x_swap =		6;
107 int  y_swap =		4;
108 int  y_message =	5;
109 int  x_header =		0;
110 int  y_header =		6;
111 int  x_idlecursor =	0;
112 int  y_idlecursor =	5;
113 int  y_procs =		7;
114 
115 int  y_cpustates =	2;
116 int  Header_lines =	7;
117 
display_resize()118 int display_resize()
119 
120 {
121     register int lines;
122 
123     /* first, deallocate any previous buffer that may have been there */
124     if (screenbuf != NULL)
125     {
126 	free(screenbuf);
127     }
128 
129     /* calculate the current dimensions */
130     /* if operating in "dumb" mode, we only need one line */
131     lines = smart_terminal ? screen_length - Header_lines : 1;
132 
133     if (lines < 0)
134 	lines = 0;
135     /* we don't want more than MAX_COLS columns, since the machine-dependent
136        modules make static allocations based on MAX_COLS and we don't want
137        to run off the end of their buffers */
138     display_width = screen_width;
139     if (display_width >= MAX_COLS)
140     {
141 	display_width = MAX_COLS - 1;
142     }
143 
144     /* now, allocate space for the screen buffer */
145     screenbuf = (char *)malloc(lines * display_width);
146     if (screenbuf == (char *)NULL)
147     {
148 	/* oops! */
149 	return(-1);
150     }
151 
152     /* return number of lines available */
153     /* for dumb terminals, pretend like we can show any amount */
154     return(smart_terminal ? lines : Largest);
155 }
156 
display_updatecpus(statics)157 int display_updatecpus(statics)
158 
159 struct statics *statics;
160 
161 {
162     register int *lp;
163     register int lines;
164     register int i;
165 
166     /* call resize to do the dirty work */
167     lines = display_resize();
168     if (pcpu_stats)
169 	num_cpus = statics->ncpus;
170     else
171 	num_cpus = 1;
172     cpustates_column = 5;	/* CPU: */
173     if (num_cpus != 1)
174     cpustates_column += 2;	/* CPU 0: */
175     for (i = num_cpus; i > 9; i /= 10)
176 	cpustates_column++;
177 
178     /* fill the "last" array with all -1s, to insure correct updating */
179     lp = lcpustates;
180     i = num_cpustates * num_cpus;
181     while (--i >= 0)
182     {
183 	*lp++ = -1;
184     }
185 
186     return(lines);
187 }
188 
display_init(statics)189 int display_init(statics)
190 
191 struct statics *statics;
192 
193 {
194     register int lines;
195     register char **pp;
196     register int *ip;
197     register int i;
198 
199     lines = display_updatecpus(statics);
200 
201     /* only do the rest if we need to */
202     if (lines > -1)
203     {
204 	/* save pointers and allocate space for names */
205 	procstate_names = statics->procstate_names;
206 	num_procstates = string_count(procstate_names);
207 	lprocstates = (int *)malloc(num_procstates * sizeof(int));
208 
209 	cpustate_names = statics->cpustate_names;
210 
211 	swap_names = statics->swap_names;
212 	num_swap = string_count(swap_names);
213 	lswap = (int *)malloc(num_swap * sizeof(int));
214 	num_cpustates = string_count(cpustate_names);
215 	lcpustates = (int *)malloc(num_cpustates * sizeof(int) * statics->ncpus);
216 	cpustate_columns = (int *)malloc(num_cpustates * sizeof(int));
217 
218 	memory_names = statics->memory_names;
219 	num_memory = string_count(memory_names);
220 	lmemory = (int *)malloc(num_memory * sizeof(int));
221 
222 	arc_names = statics->arc_names;
223 
224 	/* calculate starting columns where needed */
225 	cpustate_total_length = 0;
226 	pp = cpustate_names;
227 	ip = cpustate_columns;
228 	while (*pp != NULL)
229 	{
230 	    *ip++ = cpustate_total_length;
231 	    if ((i = strlen(*pp++)) > 0)
232 	    {
233 		cpustate_total_length += i + 8;
234 	    }
235 	}
236     }
237 
238     /* return number of lines available */
239     return(lines);
240 }
241 
i_loadave(mpid,avenrun)242 i_loadave(mpid, avenrun)
243 
244 int mpid;
245 double *avenrun;
246 
247 {
248     register int i;
249 
250     /* i_loadave also clears the screen, since it is first */
251     clear();
252 
253     /* mpid == -1 implies this system doesn't have an _mpid */
254     if (mpid != -1)
255     {
256 	printf("last pid: %5d;  ", mpid);
257     }
258 
259     printf("load averages");
260 
261     for (i = 0; i < 3; i++)
262     {
263 	printf("%c %5.2f",
264 	    i == 0 ? ':' : ',',
265 	    avenrun[i]);
266     }
267     lmpid = mpid;
268 }
269 
u_loadave(mpid,avenrun)270 u_loadave(mpid, avenrun)
271 
272 int mpid;
273 double *avenrun;
274 
275 {
276     register int i;
277 
278     if (mpid != -1)
279     {
280 	/* change screen only when value has really changed */
281 	if (mpid != lmpid)
282 	{
283 	    Move_to(x_lastpid, y_lastpid);
284 	    printf("%5d", mpid);
285 	    lmpid = mpid;
286 	}
287 
288 	/* i remembers x coordinate to move to */
289 	i = x_loadave;
290     }
291     else
292     {
293 	i = x_loadave_nompid;
294     }
295 
296     /* move into position for load averages */
297     Move_to(i, y_loadave);
298 
299     /* display new load averages */
300     /* we should optimize this and only display changes */
301     for (i = 0; i < 3; i++)
302     {
303 	printf("%s%5.2f",
304 	    i == 0 ? "" : ", ",
305 	    avenrun[i]);
306     }
307 }
308 
i_timeofday(tod)309 i_timeofday(tod)
310 
311 time_t *tod;
312 
313 {
314     /*
315      *  Display the current time.
316      *  "ctime" always returns a string that looks like this:
317      *
318      *	Sun Sep 16 01:03:52 1973
319      *      012345678901234567890123
320      *	          1         2
321      *
322      *  We want indices 11 thru 18 (length 8).
323      */
324 
325     if (smart_terminal)
326     {
327 	Move_to(screen_width - 8, 0);
328     }
329     else
330     {
331 	fputs("    ", stdout);
332     }
333 #ifdef DEBUG
334     {
335 	char *foo;
336 	foo = ctime(tod);
337 	fputs(foo, stdout);
338     }
339 #endif
340     printf("%-8.8s\n", &(ctime(tod)[11]));
341     lastline = 1;
342 }
343 
344 static int ltotal = 0;
345 static char procstates_buffer[MAX_COLS];
346 
347 /*
348  *  *_procstates(total, brkdn, names) - print the process summary line
349  *
350  *  Assumptions:  cursor is at the beginning of the line on entry
351  *		  lastline is valid
352  */
353 
i_procstates(total,brkdn)354 i_procstates(total, brkdn)
355 
356 int total;
357 int *brkdn;
358 
359 {
360     register int i;
361 
362     /* write current number of processes and remember the value */
363     printf("%d processes:", total);
364     ltotal = total;
365 
366     /* put out enough spaces to get to column 15 */
367     i = digits(total);
368     while (i++ < 4)
369     {
370 	putchar(' ');
371     }
372 
373     /* format and print the process state summary */
374     summary_format(procstates_buffer, brkdn, procstate_names);
375     fputs(procstates_buffer, stdout);
376 
377     /* save the numbers for next time */
378     memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
379 }
380 
u_procstates(total,brkdn)381 u_procstates(total, brkdn)
382 
383 int total;
384 int *brkdn;
385 
386 {
387     static char new[MAX_COLS];
388     register int i;
389 
390     /* update number of processes only if it has changed */
391     if (ltotal != total)
392     {
393 	/* move and overwrite */
394 #if (x_procstate == 0)
395 	Move_to(x_procstate, y_procstate);
396 #else
397 	/* cursor is already there...no motion needed */
398 	/* assert(lastline == 1); */
399 #endif
400 	printf("%d", total);
401 
402 	/* if number of digits differs, rewrite the label */
403 	if (digits(total) != digits(ltotal))
404 	{
405 	    fputs(" processes:", stdout);
406 	    /* put out enough spaces to get to column 15 */
407 	    i = digits(total);
408 	    while (i++ < 4)
409 	    {
410 		putchar(' ');
411 	    }
412 	    /* cursor may end up right where we want it!!! */
413 	}
414 
415 	/* save new total */
416 	ltotal = total;
417     }
418 
419     /* see if any of the state numbers has changed */
420     if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
421     {
422 	/* format and update the line */
423 	summary_format(new, brkdn, procstate_names);
424 	line_update(procstates_buffer, new, x_brkdn, y_brkdn);
425 	memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
426     }
427 }
428 
429 #ifdef no_more
430 /*
431  *  *_cpustates(states, names) - print the cpu state percentages
432  *
433  *  Assumptions:  cursor is on the PREVIOUS line
434  */
435 
436 /* cpustates_tag() calculates the correct tag to use to label the line */
437 
cpustates_tag()438 char *cpustates_tag()
439 
440 {
441     register char *use;
442 
443     static char *short_tag = "CPU: ";
444     static char *long_tag = "CPU states: ";
445 
446     /* if length + strlen(long_tag) >= screen_width, then we have to
447        use the shorter tag (we subtract 2 to account for ": ") */
448     if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
449     {
450 	use = short_tag;
451     }
452     else
453     {
454 	use = long_tag;
455     }
456 
457     /* set cpustates_column accordingly then return result */
458     cpustates_column = strlen(use);
459     return(use);
460 }
461 #endif
462 
i_cpustates(states)463 i_cpustates(states)
464 
465 register int *states;
466 
467 {
468     register int i = 0;
469     register int value;
470     register char **names;
471     register char *thisname;
472     int cpu;
473 
474 for (cpu = 0; cpu < num_cpus; cpu++) {
475     names = cpustate_names;
476 
477     /* print tag and bump lastline */
478     if (num_cpus == 1)
479 	printf("\nCPU: ");
480     else {
481 	value = printf("\nCPU %d: ", cpu);
482 	while (value++ <= cpustates_column)
483 		printf(" ");
484     }
485     lastline++;
486 
487     /* now walk thru the names and print the line */
488     while ((thisname = *names++) != NULL)
489     {
490 	if (*thisname != '\0')
491 	{
492 	    /* retrieve the value and remember it */
493 	    value = *states++;
494 
495 	    /* if percentage is >= 1000, print it as 100% */
496 	    printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
497 		   (i++ % num_cpustates) == 0 ? "" : ", ",
498 		   ((float)value)/10.,
499 		   thisname);
500 	}
501     }
502 }
503 
504     /* copy over values into "last" array */
505     memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
506 }
507 
u_cpustates(states)508 u_cpustates(states)
509 
510 register int *states;
511 
512 {
513     register int value;
514     register char **names;
515     register char *thisname;
516     register int *lp;
517     register int *colp;
518     int cpu;
519 
520 for (cpu = 0; cpu < num_cpus; cpu++) {
521     names = cpustate_names;
522 
523     Move_to(cpustates_column, y_cpustates + cpu);
524     lastline = y_cpustates + cpu;
525     lp = lcpustates + (cpu * num_cpustates);
526     colp = cpustate_columns;
527 
528     /* we could be much more optimal about this */
529     while ((thisname = *names++) != NULL)
530     {
531 	if (*thisname != '\0')
532 	{
533 	    /* did the value change since last time? */
534 	    if (*lp != *states)
535 	    {
536 		/* yes, move and change */
537 		Move_to(cpustates_column + *colp, y_cpustates + cpu);
538 		lastline = y_cpustates + cpu;
539 
540 		/* retrieve value and remember it */
541 		value = *states;
542 
543 		/* if percentage is >= 1000, print it as 100% */
544 		printf((value >= 1000 ? "%4.0f" : "%4.1f"),
545 		       ((double)value)/10.);
546 
547 		/* remember it for next time */
548 		*lp = value;
549 	    }
550 	}
551 
552 	/* increment and move on */
553 	lp++;
554 	states++;
555 	colp++;
556     }
557 }
558 }
559 
z_cpustates()560 z_cpustates()
561 
562 {
563     register int i = 0;
564     register char **names;
565     register char *thisname;
566     register int *lp;
567     int cpu, value;
568 
569 for (cpu = 0; cpu < num_cpus; cpu++) {
570     names = cpustate_names;
571 
572     /* show tag and bump lastline */
573     if (num_cpus == 1)
574 	printf("\nCPU: ");
575     else {
576 	value = printf("\nCPU %d: ", cpu);
577 	while (value++ <= cpustates_column)
578 		printf(" ");
579     }
580     lastline++;
581 
582     while ((thisname = *names++) != NULL)
583     {
584 	if (*thisname != '\0')
585 	{
586 	    printf("%s    %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname);
587 	}
588     }
589 }
590 
591     /* fill the "last" array with all -1s, to insure correct updating */
592     lp = lcpustates;
593     i = num_cpustates * num_cpus;
594     while (--i >= 0)
595     {
596 	*lp++ = -1;
597     }
598 }
599 
600 /*
601  *  *_memory(stats) - print "Memory: " followed by the memory summary string
602  *
603  *  Assumptions:  cursor is on "lastline"
604  *                for i_memory ONLY: cursor is on the previous line
605  */
606 
607 char memory_buffer[MAX_COLS];
608 
i_memory(stats)609 i_memory(stats)
610 
611 int *stats;
612 
613 {
614     fputs("\nMem: ", stdout);
615     lastline++;
616 
617     /* format and print the memory summary */
618     summary_format(memory_buffer, stats, memory_names);
619     fputs(memory_buffer, stdout);
620 }
621 
u_memory(stats)622 u_memory(stats)
623 
624 int *stats;
625 
626 {
627     static char new[MAX_COLS];
628 
629     /* format the new line */
630     summary_format(new, stats, memory_names);
631     line_update(memory_buffer, new, x_mem, y_mem);
632 }
633 
634 /*
635  *  *_arc(stats) - print "ARC: " followed by the ARC summary string
636  *
637  *  Assumptions:  cursor is on "lastline"
638  *                for i_arc ONLY: cursor is on the previous line
639  */
640 char arc_buffer[MAX_COLS];
641 
i_arc(stats)642 i_arc(stats)
643 
644 int *stats;
645 
646 {
647     if (arc_names == NULL)
648 	return (0);
649 
650     fputs("\nARC: ", stdout);
651     lastline++;
652 
653     /* format and print the memory summary */
654     summary_format(arc_buffer, stats, arc_names);
655     fputs(arc_buffer, stdout);
656 }
657 
u_arc(stats)658 u_arc(stats)
659 
660 int *stats;
661 
662 {
663     static char new[MAX_COLS];
664 
665     if (arc_names == NULL)
666 	return (0);
667 
668     /* format the new line */
669     summary_format(new, stats, arc_names);
670     line_update(arc_buffer, new, x_arc, y_arc);
671 }
672 
673 
674 /*
675  *  *_swap(stats) - print "Swap: " followed by the swap summary string
676  *
677  *  Assumptions:  cursor is on "lastline"
678  *                for i_swap ONLY: cursor is on the previous line
679  */
680 
681 char swap_buffer[MAX_COLS];
682 
i_swap(stats)683 i_swap(stats)
684 
685 int *stats;
686 
687 {
688     fputs("\nSwap: ", stdout);
689     lastline++;
690 
691     /* format and print the swap summary */
692     summary_format(swap_buffer, stats, swap_names);
693     fputs(swap_buffer, stdout);
694 }
695 
u_swap(stats)696 u_swap(stats)
697 
698 int *stats;
699 
700 {
701     static char new[MAX_COLS];
702 
703     /* format the new line */
704     summary_format(new, stats, swap_names);
705     line_update(swap_buffer, new, x_swap, y_swap);
706 }
707 
708 /*
709  *  *_message() - print the next pending message line, or erase the one
710  *                that is there.
711  *
712  *  Note that u_message is (currently) the same as i_message.
713  *
714  *  Assumptions:  lastline is consistent
715  */
716 
717 /*
718  *  i_message is funny because it gets its message asynchronously (with
719  *	respect to screen updates).
720  */
721 
722 static char next_msg[MAX_COLS + 5];
723 static int msglen = 0;
724 /* Invariant: msglen is always the length of the message currently displayed
725    on the screen (even when next_msg doesn't contain that message). */
726 
i_message()727 i_message()
728 
729 {
730     while (lastline < y_message)
731     {
732 	fputc('\n', stdout);
733 	lastline++;
734     }
735     if (next_msg[0] != '\0')
736     {
737 	standout(next_msg);
738 	msglen = strlen(next_msg);
739 	next_msg[0] = '\0';
740     }
741     else if (msglen > 0)
742     {
743 	(void) clear_eol(msglen);
744 	msglen = 0;
745     }
746 }
747 
u_message()748 u_message()
749 
750 {
751     i_message();
752 }
753 
754 static int header_length;
755 
756 /*
757  * Trim a header string to the current display width and return a newly
758  * allocated area with the trimmed header.
759  */
760 
761 char *
trim_header(text)762 trim_header(text)
763 
764 char *text;
765 
766 {
767 	char *s;
768 	int width;
769 
770 	s = NULL;
771 	width = display_width;
772 	header_length = strlen(text);
773 	if (header_length >= width) {
774 		s = malloc((width + 1) * sizeof(char));
775 		if (s == NULL)
776 			return (NULL);
777 		strncpy(s, text, width);
778 		s[width] = '\0';
779 	}
780 	return (s);
781 }
782 
783 /*
784  *  *_header(text) - print the header for the process area
785  *
786  *  Assumptions:  cursor is on the previous line and lastline is consistent
787  */
788 
i_header(text)789 i_header(text)
790 
791 char *text;
792 
793 {
794     char *s;
795 
796     s = trim_header(text);
797     if (s != NULL)
798 	text = s;
799 
800     if (header_status == ON)
801     {
802 	putchar('\n');
803 	fputs(text, stdout);
804 	lastline++;
805     }
806     else if (header_status == ERASE)
807     {
808 	header_status = OFF;
809     }
810     free(s);
811 }
812 
813 /*ARGSUSED*/
u_header(text)814 u_header(text)
815 
816 char *text;		/* ignored */
817 
818 {
819 
820     if (header_status == ERASE)
821     {
822 	putchar('\n');
823 	lastline++;
824 	clear_eol(header_length);
825 	header_status = OFF;
826     }
827 }
828 
829 /*
830  *  *_process(line, thisline) - print one process line
831  *
832  *  Assumptions:  lastline is consistent
833  */
834 
i_process(line,thisline)835 i_process(line, thisline)
836 
837 int line;
838 char *thisline;
839 
840 {
841     register char *p;
842     register char *base;
843 
844     /* make sure we are on the correct line */
845     while (lastline < y_procs + line)
846     {
847 	putchar('\n');
848 	lastline++;
849     }
850 
851     /* truncate the line to conform to our current screen width */
852     thisline[display_width] = '\0';
853 
854     /* write the line out */
855     fputs(thisline, stdout);
856 
857     /* copy it in to our buffer */
858     base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
859     p = strecpy(base, thisline);
860 
861     /* zero fill the rest of it */
862     memzero(p, display_width - (p - base));
863 }
864 
u_process(line,newline)865 u_process(line, newline)
866 
867 int line;
868 char *newline;
869 
870 {
871     register char *optr;
872     register int screen_line = line + Header_lines;
873     register char *bufferline;
874 
875     /* remember a pointer to the current line in the screen buffer */
876     bufferline = &screenbuf[lineindex(line)];
877 
878     /* truncate the line to conform to our current screen width */
879     newline[display_width] = '\0';
880 
881     /* is line higher than we went on the last display? */
882     if (line >= last_hi)
883     {
884 	/* yes, just ignore screenbuf and write it out directly */
885 	/* get positioned on the correct line */
886 	if (screen_line - lastline == 1)
887 	{
888 	    putchar('\n');
889 	    lastline++;
890 	}
891 	else
892 	{
893 	    Move_to(0, screen_line);
894 	    lastline = screen_line;
895 	}
896 
897 	/* now write the line */
898 	fputs(newline, stdout);
899 
900 	/* copy it in to the buffer */
901 	optr = strecpy(bufferline, newline);
902 
903 	/* zero fill the rest of it */
904 	memzero(optr, display_width - (optr - bufferline));
905     }
906     else
907     {
908 	line_update(bufferline, newline, 0, line + Header_lines);
909     }
910 }
911 
u_endscreen(hi)912 u_endscreen(hi)
913 
914 register int hi;
915 
916 {
917     register int screen_line = hi + Header_lines;
918     register int i;
919 
920     if (smart_terminal)
921     {
922 	if (hi < last_hi)
923 	{
924 	    /* need to blank the remainder of the screen */
925 	    /* but only if there is any screen left below this line */
926 	    if (lastline + 1 < screen_length)
927 	    {
928 		/* efficiently move to the end of currently displayed info */
929 		if (screen_line - lastline < 5)
930 		{
931 		    while (lastline < screen_line)
932 		    {
933 			putchar('\n');
934 			lastline++;
935 		    }
936 		}
937 		else
938 		{
939 		    Move_to(0, screen_line);
940 		    lastline = screen_line;
941 		}
942 
943 		if (clear_to_end)
944 		{
945 		    /* we can do this the easy way */
946 		    putcap(clear_to_end);
947 		}
948 		else
949 		{
950 		    /* use clear_eol on each line */
951 		    i = hi;
952 		    while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
953 		    {
954 			putchar('\n');
955 		    }
956 		}
957 	    }
958 	}
959 	last_hi = hi;
960 
961 	/* move the cursor to a pleasant place */
962 	Move_to(x_idlecursor, y_idlecursor);
963 	lastline = y_idlecursor;
964     }
965     else
966     {
967 	/* separate this display from the next with some vertical room */
968 	fputs("\n\n", stdout);
969     }
970 }
971 
display_header(t)972 display_header(t)
973 
974 int t;
975 
976 {
977     if (t)
978     {
979 	header_status = ON;
980     }
981     else if (header_status == ON)
982     {
983 	header_status = ERASE;
984     }
985 }
986 
987 /*VARARGS2*/
new_message(type,msgfmt,a1,a2,a3)988 new_message(type, msgfmt, a1, a2, a3)
989 
990 int type;
991 char *msgfmt;
992 caddr_t a1, a2, a3;
993 
994 {
995     register int i;
996 
997     /* first, format the message */
998     (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3);
999 
1000     if (msglen > 0)
1001     {
1002 	/* message there already -- can we clear it? */
1003 	if (!overstrike)
1004 	{
1005 	    /* yes -- write it and clear to end */
1006 	    i = strlen(next_msg);
1007 	    if ((type & MT_delayed) == 0)
1008 	    {
1009 		type & MT_standout ? standout(next_msg) :
1010 		                     fputs(next_msg, stdout);
1011 		(void) clear_eol(msglen - i);
1012 		msglen = i;
1013 		next_msg[0] = '\0';
1014 	    }
1015 	}
1016     }
1017     else
1018     {
1019 	if ((type & MT_delayed) == 0)
1020 	{
1021 	    type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
1022 	    msglen = strlen(next_msg);
1023 	    next_msg[0] = '\0';
1024 	}
1025     }
1026 }
1027 
clear_message()1028 clear_message()
1029 
1030 {
1031     if (clear_eol(msglen) == 1)
1032     {
1033 	putchar('\r');
1034     }
1035 }
1036 
readline(buffer,size,numeric)1037 readline(buffer, size, numeric)
1038 
1039 char *buffer;
1040 int  size;
1041 int  numeric;
1042 
1043 {
1044     register char *ptr = buffer;
1045     register char ch;
1046     register char cnt = 0;
1047     register char maxcnt = 0;
1048 
1049     /* allow room for null terminator */
1050     size -= 1;
1051 
1052     /* read loop */
1053     while ((fflush(stdout), read(0, ptr, 1) > 0))
1054     {
1055 	/* newline means we are done */
1056 	if ((ch = *ptr) == '\n' || ch == '\r')
1057 	{
1058 	    break;
1059 	}
1060 
1061 	/* handle special editing characters */
1062 	if (ch == ch_kill)
1063 	{
1064 	    /* kill line -- account for overstriking */
1065 	    if (overstrike)
1066 	    {
1067 		msglen += maxcnt;
1068 	    }
1069 
1070 	    /* return null string */
1071 	    *buffer = '\0';
1072 	    putchar('\r');
1073 	    return(-1);
1074 	}
1075 	else if (ch == ch_erase)
1076 	{
1077 	    /* erase previous character */
1078 	    if (cnt <= 0)
1079 	    {
1080 		/* none to erase! */
1081 		putchar('\7');
1082 	    }
1083 	    else
1084 	    {
1085 		fputs("\b \b", stdout);
1086 		ptr--;
1087 		cnt--;
1088 	    }
1089 	}
1090 	/* check for character validity and buffer overflow */
1091 	else if (cnt == size || (numeric && !isdigit(ch)) ||
1092 		!isprint(ch))
1093 	{
1094 	    /* not legal */
1095 	    putchar('\7');
1096 	}
1097 	else
1098 	{
1099 	    /* echo it and store it in the buffer */
1100 	    putchar(ch);
1101 	    ptr++;
1102 	    cnt++;
1103 	    if (cnt > maxcnt)
1104 	    {
1105 		maxcnt = cnt;
1106 	    }
1107 	}
1108     }
1109 
1110     /* all done -- null terminate the string */
1111     *ptr = '\0';
1112 
1113     /* account for the extra characters in the message area */
1114     /* (if terminal overstrikes, remember the furthest they went) */
1115     msglen += overstrike ? maxcnt : cnt;
1116 
1117     /* return either inputted number or string length */
1118     putchar('\r');
1119     return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
1120 }
1121 
1122 /* internal support routines */
1123 
string_count(pp)1124 static int string_count(pp)
1125 
1126 register char **pp;
1127 
1128 {
1129     register int cnt;
1130 
1131     cnt = 0;
1132     while (*pp++ != NULL)
1133     {
1134 	cnt++;
1135     }
1136     return(cnt);
1137 }
1138 
summary_format(str,numbers,names)1139 static void summary_format(str, numbers, names)
1140 
1141 char *str;
1142 int *numbers;
1143 register char **names;
1144 
1145 {
1146     register char *p;
1147     register int num;
1148     register char *thisname;
1149     register int useM = No;
1150 
1151     /* format each number followed by its string */
1152     p = str;
1153     while ((thisname = *names++) != NULL)
1154     {
1155 	/* get the number to format */
1156 	num = *numbers++;
1157 
1158 	/* display only non-zero numbers */
1159 	if (num > 0)
1160 	{
1161 	    /* is this number in kilobytes? */
1162 	    if (thisname[0] == 'K')
1163 	    {
1164 		/* yes: format it as a memory value */
1165 		p = strecpy(p, format_k(num));
1166 
1167 		/* skip over the K, since it was included by format_k */
1168 		p = strecpy(p, thisname+1);
1169 	    }
1170 	    else
1171 	    {
1172 		p = strecpy(p, itoa(num));
1173 		p = strecpy(p, thisname);
1174 	    }
1175 	}
1176 
1177 	/* ignore negative numbers, but display corresponding string */
1178 	else if (num < 0)
1179 	{
1180 	    p = strecpy(p, thisname);
1181 	}
1182     }
1183 
1184     /* if the last two characters in the string are ", ", delete them */
1185     p -= 2;
1186     if (p >= str && p[0] == ',' && p[1] == ' ')
1187     {
1188 	*p = '\0';
1189     }
1190 }
1191 
line_update(old,new,start,line)1192 static void line_update(old, new, start, line)
1193 
1194 register char *old;
1195 register char *new;
1196 int start;
1197 int line;
1198 
1199 {
1200     register int ch;
1201     register int diff;
1202     register int newcol = start + 1;
1203     register int lastcol = start;
1204     char cursor_on_line = No;
1205     char *current;
1206 
1207     /* compare the two strings and only rewrite what has changed */
1208     current = old;
1209 #ifdef DEBUG
1210     fprintf(debug, "line_update, starting at %d\n", start);
1211     fputs(old, debug);
1212     fputc('\n', debug);
1213     fputs(new, debug);
1214     fputs("\n-\n", debug);
1215 #endif
1216 
1217     /* start things off on the right foot		    */
1218     /* this is to make sure the invariants get set up right */
1219     if ((ch = *new++) != *old)
1220     {
1221 	if (line - lastline == 1 && start == 0)
1222 	{
1223 	    putchar('\n');
1224 	}
1225 	else
1226 	{
1227 	    Move_to(start, line);
1228 	}
1229 	cursor_on_line = Yes;
1230 	putchar(ch);
1231 	*old = ch;
1232 	lastcol = 1;
1233     }
1234     old++;
1235 
1236     /*
1237      *  main loop -- check each character.  If the old and new aren't the
1238      *	same, then update the display.  When the distance from the
1239      *	current cursor position to the new change is small enough,
1240      *	the characters that belong there are written to move the
1241      *	cursor over.
1242      *
1243      *	Invariants:
1244      *	    lastcol is the column where the cursor currently is sitting
1245      *		(always one beyond the end of the last mismatch).
1246      */
1247     do		/* yes, a do...while */
1248     {
1249 	if ((ch = *new++) != *old)
1250 	{
1251 	    /* new character is different from old	  */
1252 	    /* make sure the cursor is on top of this character */
1253 	    diff = newcol - lastcol;
1254 	    if (diff > 0)
1255 	    {
1256 		/* some motion is required--figure out which is shorter */
1257 		if (diff < 6 && cursor_on_line)
1258 		{
1259 		    /* overwrite old stuff--get it out of the old buffer */
1260 		    printf("%.*s", diff, &current[lastcol-start]);
1261 		}
1262 		else
1263 		{
1264 		    /* use cursor addressing */
1265 		    Move_to(newcol, line);
1266 		    cursor_on_line = Yes;
1267 		}
1268 		/* remember where the cursor is */
1269 		lastcol = newcol + 1;
1270 	    }
1271 	    else
1272 	    {
1273 		/* already there, update position */
1274 		lastcol++;
1275 	    }
1276 
1277 	    /* write what we need to */
1278 	    if (ch == '\0')
1279 	    {
1280 		/* at the end--terminate with a clear-to-end-of-line */
1281 		(void) clear_eol(strlen(old));
1282 	    }
1283 	    else
1284 	    {
1285 		/* write the new character */
1286 		putchar(ch);
1287 	    }
1288 	    /* put the new character in the screen buffer */
1289 	    *old = ch;
1290 	}
1291 
1292 	/* update working column and screen buffer pointer */
1293 	newcol++;
1294 	old++;
1295 
1296     } while (ch != '\0');
1297 
1298     /* zero out the rest of the line buffer -- MUST BE DONE! */
1299     diff = display_width - newcol;
1300     if (diff > 0)
1301     {
1302 	memzero(old, diff);
1303     }
1304 
1305     /* remember where the current line is */
1306     if (cursor_on_line)
1307     {
1308 	lastline = line;
1309     }
1310 }
1311 
1312 /*
1313  *  printable(str) - make the string pointed to by "str" into one that is
1314  *	printable (i.e.: all ascii), by converting all non-printable
1315  *	characters into '?'.  Replacements are done in place and a pointer
1316  *	to the original buffer is returned.
1317  */
1318 
printable(str)1319 char *printable(str)
1320 
1321 char *str;
1322 
1323 {
1324     register char *ptr;
1325     register char ch;
1326 
1327     ptr = str;
1328     while ((ch = *ptr) != '\0')
1329     {
1330 	if (!isprint(ch))
1331 	{
1332 	    *ptr = '?';
1333 	}
1334 	ptr++;
1335     }
1336     return(str);
1337 }
1338 
1339 i_uptime(bt, tod)
1340 
1341 struct timeval* bt;
1342 time_t *tod;
1343 
1344 {
1345     time_t uptime;
1346     int days, hrs, mins, secs;
1347 
1348     if (bt->tv_sec != -1) {
1349 	uptime = *tod - bt->tv_sec;
1350 	days = uptime / 86400;
1351 	uptime %= 86400;
1352 	hrs = uptime / 3600;
1353 	uptime %= 3600;
1354 	mins = uptime / 60;
1355 	secs = uptime % 60;
1356 
1357 	/*
1358 	 *  Display the uptime.
1359 	 */
1360 
1361 	if (smart_terminal)
1362 	{
1363 	    Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
1364 	}
1365 	else
1366 	{
1367 	    fputs(" ", stdout);
1368 	}
1369 	printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
1370     }
1371 }
1372