display.c revision 223936
1326949Sdim/*
2326949Sdim *  Top users/processes display for Unix
3353358Sdim *  Version 3
4353358Sdim *
5353358Sdim *  This program may be freely redistributed,
6326949Sdim *  but this entire comment MUST remain intact.
7326949Sdim *
8326949Sdim *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
9326949Sdim *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
10326949Sdim *
11326949Sdim * $FreeBSD: head/contrib/top/display.c 223936 2011-07-11 16:48:52Z jhb $
12326949Sdim */
13326949Sdim
14326949Sdim/*
15326949Sdim *  This file contains the routines that display information on the screen.
16326949Sdim *  Each section of the screen has two routines:  one for initially writing
17326949Sdim *  all constant and dynamic text, and one for only updating the text that
18326949Sdim *  changes.  The prefix "i_" is used on all the "initial" routines and the
19326949Sdim *  prefix "u_" is used for all the "updating" routines.
20326949Sdim *
21326949Sdim *  ASSUMPTIONS:
22326949Sdim *        None of the "i_" routines use any of the termcap capabilities.
23344779Sdim *        In this way, those routines can be safely used on terminals that
24326949Sdim *        have minimal (or nonexistant) terminal capabilities.
25326949Sdim *
26326949Sdim *        The routines are called in this order:  *_loadave, i_timeofday,
27326949Sdim *        *_procstates, *_cpustates, *_memory, *_message, *_header,
28326949Sdim *        *_process, u_endscreen.
29326949Sdim */
30326949Sdim
31326949Sdim#include "os.h"
32326949Sdim#include <ctype.h>
33326949Sdim#include <time.h>
34326949Sdim#include <sys/time.h>
35326949Sdim
36326949Sdim#include "screen.h"		/* interface to screen package */
37326949Sdim#include "layout.h"		/* defines for screen position layout */
38326949Sdim#include "display.h"
39326949Sdim#include "top.h"
40326949Sdim#include "top.local.h"
41326949Sdim#include "boolean.h"
42326949Sdim#include "machine.h"		/* we should eliminate this!!! */
43326949Sdim#include "utils.h"
44326949Sdim
45326949Sdim#ifdef DEBUG
46326949SdimFILE *debug;
47326949Sdim#endif
48326949Sdim
49326949Sdim/* imported from screen.c */
50326949Sdimextern int overstrike;
51326949Sdim
52326949Sdimstatic int lmpid = 0;
53326949Sdimstatic int last_hi = 0;		/* used in u_process and u_endscreen */
54326949Sdimstatic int lastline = 0;
55326949Sdimstatic int display_width = MAX_COLS;
56326949Sdim
57326949Sdim#define lineindex(l) ((l)*display_width)
58326949Sdim
59326949Sdimchar *printable();
60326949Sdim
61326949Sdim/* things initialized by display_init and used thruout */
62326949Sdim
63326949Sdim/* buffer of proc information lines for display updating */
64326949Sdimchar *screenbuf = NULL;
65326949Sdim
66326949Sdimstatic char **procstate_names;
67326949Sdimstatic char **cpustate_names;
68326949Sdimstatic char **memory_names;
69326949Sdimstatic char **swap_names;
70326949Sdim
71326949Sdimstatic int num_procstates;
72326949Sdimstatic int num_cpustates;
73326949Sdimstatic int num_memory;
74326949Sdimstatic int num_swap;
75326949Sdim
76326949Sdimstatic int *lprocstates;
77326949Sdimstatic int *lcpustates;
78341825Sdimstatic int *lmemory;
79341825Sdimstatic int *lswap;
80326949Sdim
81326949Sdimstatic int num_cpus;
82326949Sdimstatic int *cpustate_columns;
83326949Sdimstatic int cpustate_total_length;
84326949Sdimstatic int cpustates_column;
85326949Sdim
86326949Sdimstatic enum { OFF, ON, ERASE } header_status = ON;
87326949Sdim
88326949Sdimstatic int string_count();
89326949Sdimstatic void summary_format();
90326949Sdimstatic void line_update();
91326949Sdim
92326949Sdimint  x_lastpid =	10;
93326949Sdimint  y_lastpid =	0;
94326949Sdimint  x_loadave =	33;
95326949Sdimint  x_loadave_nompid =	15;
96326949Sdimint  y_loadave =	0;
97326949Sdimint  x_procstate =	0;
98326949Sdimint  y_procstate =	1;
99326949Sdimint  x_brkdn =		15;
100326949Sdimint  y_brkdn =		1;
101326949Sdimint  x_mem =		5;
102326949Sdimint  y_mem =		3;
103326949Sdimint  x_swap =		6;
104326949Sdimint  y_swap =		4;
105326949Sdimint  y_message =	5;
106326949Sdimint  x_header =		0;
107326949Sdimint  y_header =		6;
108326949Sdimint  x_idlecursor =	0;
109326949Sdimint  y_idlecursor =	5;
110326949Sdimint  y_procs =		7;
111326949Sdim
112326949Sdimint  y_cpustates =	2;
113326949Sdimint  Header_lines =	7;
114326949Sdim
115326949Sdimint display_resize()
116326949Sdim
117326949Sdim{
118326949Sdim    register int lines;
119326949Sdim
120326949Sdim    /* first, deallocate any previous buffer that may have been there */
121326949Sdim    if (screenbuf != NULL)
122326949Sdim    {
123326949Sdim	free(screenbuf);
124326949Sdim    }
125326949Sdim
126326949Sdim    /* calculate the current dimensions */
127326949Sdim    /* if operating in "dumb" mode, we only need one line */
128326949Sdim    lines = smart_terminal ? screen_length - Header_lines : 1;
129326949Sdim
130326949Sdim    if (lines < 0)
131326949Sdim	lines = 0;
132326949Sdim    /* we don't want more than MAX_COLS columns, since the machine-dependent
133326949Sdim       modules make static allocations based on MAX_COLS and we don't want
134326949Sdim       to run off the end of their buffers */
135326949Sdim    display_width = screen_width;
136326949Sdim    if (display_width >= MAX_COLS)
137326949Sdim    {
138326949Sdim	display_width = MAX_COLS - 1;
139353358Sdim    }
140326949Sdim
141326949Sdim    /* now, allocate space for the screen buffer */
142326949Sdim    screenbuf = (char *)malloc(lines * display_width);
143326949Sdim    if (screenbuf == (char *)NULL)
144326949Sdim    {
145353358Sdim	/* oops! */
146326949Sdim	return(-1);
147326949Sdim    }
148326949Sdim
149326949Sdim    /* return number of lines available */
150326949Sdim    /* for dumb terminals, pretend like we can show any amount */
151326949Sdim    return(smart_terminal ? lines : Largest);
152326949Sdim}
153326949Sdim
154326949Sdimint display_updatecpus(statics)
155326949Sdim
156326949Sdimstruct statics *statics;
157326949Sdim
158326949Sdim{
159353358Sdim    register int lines;
160326949Sdim    register int i;
161326949Sdim
162353358Sdim    /* call resize to do the dirty work */
163326949Sdim    lines = display_resize();
164326949Sdim    num_cpus = statics->ncpus;
165326949Sdim    cpustates_column = 5;	/* CPU: */
166326949Sdim    if (num_cpus != 1)
167326949Sdim    cpustates_column += 2;	/* CPU 0: */
168326949Sdim    for (i = num_cpus; i > 9; i /= 10)
169326949Sdim	cpustates_column++;
170326949Sdim
171326949Sdim    return(lines);
172326949Sdim}
173326949Sdim
174326949Sdimint display_init(statics)
175326949Sdim
176326949Sdimstruct statics *statics;
177326949Sdim
178326949Sdim{
179326949Sdim    register int lines;
180326949Sdim    register char **pp;
181326949Sdim    register int *ip;
182326949Sdim    register int i;
183326949Sdim
184326949Sdim    lines = display_updatecpus(statics);
185326949Sdim
186326949Sdim    /* only do the rest if we need to */
187326949Sdim    if (lines > -1)
188326949Sdim    {
189326949Sdim	/* save pointers and allocate space for names */
190326949Sdim	procstate_names = statics->procstate_names;
191326949Sdim	num_procstates = string_count(procstate_names);
192326949Sdim	lprocstates = (int *)malloc(num_procstates * sizeof(int));
193326949Sdim
194326949Sdim	cpustate_names = statics->cpustate_names;
195326949Sdim
196326949Sdim	swap_names = statics->swap_names;
197326949Sdim	num_swap = string_count(swap_names);
198326949Sdim	lswap = (int *)malloc(num_swap * sizeof(int));
199326949Sdim	num_cpustates = string_count(cpustate_names);
200326949Sdim	lcpustates = (int *)malloc(num_cpustates * sizeof(int) * num_cpus);
201326949Sdim	cpustate_columns = (int *)malloc(num_cpustates * sizeof(int));
202326949Sdim
203326949Sdim	memory_names = statics->memory_names;
204326949Sdim	num_memory = string_count(memory_names);
205326949Sdim	lmemory = (int *)malloc(num_memory * sizeof(int));
206326949Sdim
207326949Sdim	/* calculate starting columns where needed */
208326949Sdim	cpustate_total_length = 0;
209	pp = cpustate_names;
210	ip = cpustate_columns;
211	while (*pp != NULL)
212	{
213	    *ip++ = cpustate_total_length;
214	    if ((i = strlen(*pp++)) > 0)
215	    {
216		cpustate_total_length += i + 8;
217	    }
218	}
219    }
220
221    /* return number of lines available */
222    return(lines);
223}
224
225i_loadave(mpid, avenrun)
226
227int mpid;
228double *avenrun;
229
230{
231    register int i;
232
233    /* i_loadave also clears the screen, since it is first */
234    clear();
235
236    /* mpid == -1 implies this system doesn't have an _mpid */
237    if (mpid != -1)
238    {
239	printf("last pid: %5d;  ", mpid);
240    }
241
242    printf("load averages");
243
244    for (i = 0; i < 3; i++)
245    {
246	printf("%c %5.2f",
247	    i == 0 ? ':' : ',',
248	    avenrun[i]);
249    }
250    lmpid = mpid;
251}
252
253u_loadave(mpid, avenrun)
254
255int mpid;
256double *avenrun;
257
258{
259    register int i;
260
261    if (mpid != -1)
262    {
263	/* change screen only when value has really changed */
264	if (mpid != lmpid)
265	{
266	    Move_to(x_lastpid, y_lastpid);
267	    printf("%5d", mpid);
268	    lmpid = mpid;
269	}
270
271	/* i remembers x coordinate to move to */
272	i = x_loadave;
273    }
274    else
275    {
276	i = x_loadave_nompid;
277    }
278
279    /* move into position for load averages */
280    Move_to(i, y_loadave);
281
282    /* display new load averages */
283    /* we should optimize this and only display changes */
284    for (i = 0; i < 3; i++)
285    {
286	printf("%s%5.2f",
287	    i == 0 ? "" : ", ",
288	    avenrun[i]);
289    }
290}
291
292i_timeofday(tod)
293
294time_t *tod;
295
296{
297    /*
298     *  Display the current time.
299     *  "ctime" always returns a string that looks like this:
300     *
301     *	Sun Sep 16 01:03:52 1973
302     *      012345678901234567890123
303     *	          1         2
304     *
305     *  We want indices 11 thru 18 (length 8).
306     */
307
308    if (smart_terminal)
309    {
310	Move_to(screen_width - 8, 0);
311    }
312    else
313    {
314	fputs("    ", stdout);
315    }
316#ifdef DEBUG
317    {
318	char *foo;
319	foo = ctime(tod);
320	fputs(foo, stdout);
321    }
322#endif
323    printf("%-8.8s\n", &(ctime(tod)[11]));
324    lastline = 1;
325}
326
327static int ltotal = 0;
328static char procstates_buffer[MAX_COLS];
329
330/*
331 *  *_procstates(total, brkdn, names) - print the process summary line
332 *
333 *  Assumptions:  cursor is at the beginning of the line on entry
334 *		  lastline is valid
335 */
336
337i_procstates(total, brkdn)
338
339int total;
340int *brkdn;
341
342{
343    register int i;
344
345    /* write current number of processes and remember the value */
346    printf("%d processes:", total);
347    ltotal = total;
348
349    /* put out enough spaces to get to column 15 */
350    i = digits(total);
351    while (i++ < 4)
352    {
353	putchar(' ');
354    }
355
356    /* format and print the process state summary */
357    summary_format(procstates_buffer, brkdn, procstate_names);
358    fputs(procstates_buffer, stdout);
359
360    /* save the numbers for next time */
361    memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
362}
363
364u_procstates(total, brkdn)
365
366int total;
367int *brkdn;
368
369{
370    static char new[MAX_COLS];
371    register int i;
372
373    /* update number of processes only if it has changed */
374    if (ltotal != total)
375    {
376	/* move and overwrite */
377#if (x_procstate == 0)
378	Move_to(x_procstate, y_procstate);
379#else
380	/* cursor is already there...no motion needed */
381	/* assert(lastline == 1); */
382#endif
383	printf("%d", total);
384
385	/* if number of digits differs, rewrite the label */
386	if (digits(total) != digits(ltotal))
387	{
388	    fputs(" processes:", stdout);
389	    /* put out enough spaces to get to column 15 */
390	    i = digits(total);
391	    while (i++ < 4)
392	    {
393		putchar(' ');
394	    }
395	    /* cursor may end up right where we want it!!! */
396	}
397
398	/* save new total */
399	ltotal = total;
400    }
401
402    /* see if any of the state numbers has changed */
403    if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
404    {
405	/* format and update the line */
406	summary_format(new, brkdn, procstate_names);
407	line_update(procstates_buffer, new, x_brkdn, y_brkdn);
408	memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
409    }
410}
411
412#ifdef no_more
413/*
414 *  *_cpustates(states, names) - print the cpu state percentages
415 *
416 *  Assumptions:  cursor is on the PREVIOUS line
417 */
418
419/* cpustates_tag() calculates the correct tag to use to label the line */
420
421char *cpustates_tag()
422
423{
424    register char *use;
425
426    static char *short_tag = "CPU: ";
427    static char *long_tag = "CPU states: ";
428
429    /* if length + strlen(long_tag) >= screen_width, then we have to
430       use the shorter tag (we subtract 2 to account for ": ") */
431    if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
432    {
433	use = short_tag;
434    }
435    else
436    {
437	use = long_tag;
438    }
439
440    /* set cpustates_column accordingly then return result */
441    cpustates_column = strlen(use);
442    return(use);
443}
444#endif
445
446i_cpustates(states)
447
448register int *states;
449
450{
451    register int i = 0;
452    register int value;
453    register char **names;
454    register char *thisname;
455    int cpu;
456
457for (cpu = 0; cpu < num_cpus; cpu++) {
458    names = cpustate_names;
459
460    /* print tag and bump lastline */
461    if (num_cpus == 1)
462	printf("\nCPU: ");
463    else {
464	value = printf("\nCPU %d: ", cpu);
465	while (value++ <= cpustates_column)
466		printf(" ");
467    }
468    lastline++;
469
470    /* now walk thru the names and print the line */
471    while ((thisname = *names++) != NULL)
472    {
473	if (*thisname != '\0')
474	{
475	    /* retrieve the value and remember it */
476	    value = *states++;
477
478	    /* if percentage is >= 1000, print it as 100% */
479	    printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
480		   (i++ % num_cpustates) == 0 ? "" : ", ",
481		   ((float)value)/10.,
482		   thisname);
483	}
484    }
485}
486
487    /* copy over values into "last" array */
488    memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
489}
490
491u_cpustates(states)
492
493register int *states;
494
495{
496    register int value;
497    register char **names;
498    register char *thisname;
499    register int *lp;
500    register int *colp;
501    int cpu;
502
503for (cpu = 0; cpu < num_cpus; cpu++) {
504    names = cpustate_names;
505
506    Move_to(cpustates_column, y_cpustates + cpu);
507    lastline = y_cpustates + cpu;
508    lp = lcpustates + (cpu * num_cpustates);
509    colp = cpustate_columns;
510
511    /* we could be much more optimal about this */
512    while ((thisname = *names++) != NULL)
513    {
514	if (*thisname != '\0')
515	{
516	    /* did the value change since last time? */
517	    if (*lp != *states)
518	    {
519		/* yes, move and change */
520		Move_to(cpustates_column + *colp, y_cpustates + cpu);
521		lastline = y_cpustates + cpu;
522
523		/* retrieve value and remember it */
524		value = *states;
525
526		/* if percentage is >= 1000, print it as 100% */
527		printf((value >= 1000 ? "%4.0f" : "%4.1f"),
528		       ((double)value)/10.);
529
530		/* remember it for next time */
531		*lp = value;
532	    }
533	}
534
535	/* increment and move on */
536	lp++;
537	states++;
538	colp++;
539    }
540}
541}
542
543z_cpustates()
544
545{
546    register int i = 0;
547    register char **names;
548    register char *thisname;
549    register int *lp;
550    int cpu, value;
551
552for (cpu = 0; cpu < num_cpus; cpu++) {
553    names = cpustate_names;
554
555    /* show tag and bump lastline */
556    if (num_cpus == 1)
557	printf("\nCPU: ");
558    else {
559	value = printf("\nCPU %d: ", cpu);
560	while (value++ <= cpustates_column)
561		printf(" ");
562    }
563    lastline++;
564
565    while ((thisname = *names++) != NULL)
566    {
567	if (*thisname != '\0')
568	{
569	    printf("%s    %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname);
570	}
571    }
572}
573
574    /* fill the "last" array with all -1s, to insure correct updating */
575    lp = lcpustates;
576    i = num_cpustates * num_cpus;
577    while (--i >= 0)
578    {
579	*lp++ = -1;
580    }
581}
582
583/*
584 *  *_memory(stats) - print "Memory: " followed by the memory summary string
585 *
586 *  Assumptions:  cursor is on "lastline"
587 *                for i_memory ONLY: cursor is on the previous line
588 */
589
590char memory_buffer[MAX_COLS];
591
592i_memory(stats)
593
594int *stats;
595
596{
597    fputs("\nMem: ", stdout);
598    lastline++;
599
600    /* format and print the memory summary */
601    summary_format(memory_buffer, stats, memory_names);
602    fputs(memory_buffer, stdout);
603}
604
605u_memory(stats)
606
607int *stats;
608
609{
610    static char new[MAX_COLS];
611
612    /* format the new line */
613    summary_format(new, stats, memory_names);
614    line_update(memory_buffer, new, x_mem, y_mem);
615}
616
617/*
618 *  *_swap(stats) - print "Swap: " followed by the swap summary string
619 *
620 *  Assumptions:  cursor is on "lastline"
621 *                for i_swap ONLY: cursor is on the previous line
622 */
623
624char swap_buffer[MAX_COLS];
625
626i_swap(stats)
627
628int *stats;
629
630{
631    fputs("\nSwap: ", stdout);
632    lastline++;
633
634    /* format and print the swap summary */
635    summary_format(swap_buffer, stats, swap_names);
636    fputs(swap_buffer, stdout);
637}
638
639u_swap(stats)
640
641int *stats;
642
643{
644    static char new[MAX_COLS];
645
646    /* format the new line */
647    summary_format(new, stats, swap_names);
648    line_update(swap_buffer, new, x_swap, y_swap);
649}
650
651/*
652 *  *_message() - print the next pending message line, or erase the one
653 *                that is there.
654 *
655 *  Note that u_message is (currently) the same as i_message.
656 *
657 *  Assumptions:  lastline is consistent
658 */
659
660/*
661 *  i_message is funny because it gets its message asynchronously (with
662 *	respect to screen updates).
663 */
664
665static char next_msg[MAX_COLS + 5];
666static int msglen = 0;
667/* Invariant: msglen is always the length of the message currently displayed
668   on the screen (even when next_msg doesn't contain that message). */
669
670i_message()
671
672{
673    while (lastline < y_message)
674    {
675	fputc('\n', stdout);
676	lastline++;
677    }
678    if (next_msg[0] != '\0')
679    {
680	standout(next_msg);
681	msglen = strlen(next_msg);
682	next_msg[0] = '\0';
683    }
684    else if (msglen > 0)
685    {
686	(void) clear_eol(msglen);
687	msglen = 0;
688    }
689}
690
691u_message()
692
693{
694    i_message();
695}
696
697static int header_length;
698
699/*
700 * Trim a header string to the current display width and return a newly
701 * allocated area with the trimmed header.
702 */
703
704char *
705trim_header(text)
706
707char *text;
708
709{
710	char *s;
711	int width;
712
713	s = NULL;
714	width = display_width;
715	header_length = strlen(text);
716	if (header_length >= width) {
717		s = malloc((width + 1) * sizeof(char));
718		if (s == NULL)
719			return (NULL);
720		strncpy(s, text, width);
721		s[width] = '\0';
722	}
723	return (s);
724}
725
726/*
727 *  *_header(text) - print the header for the process area
728 *
729 *  Assumptions:  cursor is on the previous line and lastline is consistent
730 */
731
732i_header(text)
733
734char *text;
735
736{
737    char *s;
738
739    s = trim_header(text);
740    if (s != NULL)
741	text = s;
742
743    if (header_status == ON)
744    {
745	putchar('\n');
746	fputs(text, stdout);
747	lastline++;
748    }
749    else if (header_status == ERASE)
750    {
751	header_status = OFF;
752    }
753    free(s);
754}
755
756/*ARGSUSED*/
757u_header(text)
758
759char *text;		/* ignored */
760
761{
762
763    if (header_status == ERASE)
764    {
765	putchar('\n');
766	lastline++;
767	clear_eol(header_length);
768	header_status = OFF;
769    }
770}
771
772/*
773 *  *_process(line, thisline) - print one process line
774 *
775 *  Assumptions:  lastline is consistent
776 */
777
778i_process(line, thisline)
779
780int line;
781char *thisline;
782
783{
784    register char *p;
785    register char *base;
786
787    /* make sure we are on the correct line */
788    while (lastline < y_procs + line)
789    {
790	putchar('\n');
791	lastline++;
792    }
793
794    /* truncate the line to conform to our current screen width */
795    thisline[display_width] = '\0';
796
797    /* write the line out */
798    fputs(thisline, stdout);
799
800    /* copy it in to our buffer */
801    base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
802    p = strecpy(base, thisline);
803
804    /* zero fill the rest of it */
805    memzero(p, display_width - (p - base));
806}
807
808u_process(line, newline)
809
810int line;
811char *newline;
812
813{
814    register char *optr;
815    register int screen_line = line + Header_lines;
816    register char *bufferline;
817
818    /* remember a pointer to the current line in the screen buffer */
819    bufferline = &screenbuf[lineindex(line)];
820
821    /* truncate the line to conform to our current screen width */
822    newline[display_width] = '\0';
823
824    /* is line higher than we went on the last display? */
825    if (line >= last_hi)
826    {
827	/* yes, just ignore screenbuf and write it out directly */
828	/* get positioned on the correct line */
829	if (screen_line - lastline == 1)
830	{
831	    putchar('\n');
832	    lastline++;
833	}
834	else
835	{
836	    Move_to(0, screen_line);
837	    lastline = screen_line;
838	}
839
840	/* now write the line */
841	fputs(newline, stdout);
842
843	/* copy it in to the buffer */
844	optr = strecpy(bufferline, newline);
845
846	/* zero fill the rest of it */
847	memzero(optr, display_width - (optr - bufferline));
848    }
849    else
850    {
851	line_update(bufferline, newline, 0, line + Header_lines);
852    }
853}
854
855u_endscreen(hi)
856
857register int hi;
858
859{
860    register int screen_line = hi + Header_lines;
861    register int i;
862
863    if (smart_terminal)
864    {
865	if (hi < last_hi)
866	{
867	    /* need to blank the remainder of the screen */
868	    /* but only if there is any screen left below this line */
869	    if (lastline + 1 < screen_length)
870	    {
871		/* efficiently move to the end of currently displayed info */
872		if (screen_line - lastline < 5)
873		{
874		    while (lastline < screen_line)
875		    {
876			putchar('\n');
877			lastline++;
878		    }
879		}
880		else
881		{
882		    Move_to(0, screen_line);
883		    lastline = screen_line;
884		}
885
886		if (clear_to_end)
887		{
888		    /* we can do this the easy way */
889		    putcap(clear_to_end);
890		}
891		else
892		{
893		    /* use clear_eol on each line */
894		    i = hi;
895		    while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
896		    {
897			putchar('\n');
898		    }
899		}
900	    }
901	}
902	last_hi = hi;
903
904	/* move the cursor to a pleasant place */
905	Move_to(x_idlecursor, y_idlecursor);
906	lastline = y_idlecursor;
907    }
908    else
909    {
910	/* separate this display from the next with some vertical room */
911	fputs("\n\n", stdout);
912    }
913}
914
915display_header(t)
916
917int t;
918
919{
920    if (t)
921    {
922	header_status = ON;
923    }
924    else if (header_status == ON)
925    {
926	header_status = ERASE;
927    }
928}
929
930/*VARARGS2*/
931new_message(type, msgfmt, a1, a2, a3)
932
933int type;
934char *msgfmt;
935caddr_t a1, a2, a3;
936
937{
938    register int i;
939
940    /* first, format the message */
941    (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3);
942
943    if (msglen > 0)
944    {
945	/* message there already -- can we clear it? */
946	if (!overstrike)
947	{
948	    /* yes -- write it and clear to end */
949	    i = strlen(next_msg);
950	    if ((type & MT_delayed) == 0)
951	    {
952		type & MT_standout ? standout(next_msg) :
953		                     fputs(next_msg, stdout);
954		(void) clear_eol(msglen - i);
955		msglen = i;
956		next_msg[0] = '\0';
957	    }
958	}
959    }
960    else
961    {
962	if ((type & MT_delayed) == 0)
963	{
964	    type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
965	    msglen = strlen(next_msg);
966	    next_msg[0] = '\0';
967	}
968    }
969}
970
971clear_message()
972
973{
974    if (clear_eol(msglen) == 1)
975    {
976	putchar('\r');
977    }
978}
979
980readline(buffer, size, numeric)
981
982char *buffer;
983int  size;
984int  numeric;
985
986{
987    register char *ptr = buffer;
988    register char ch;
989    register char cnt = 0;
990    register char maxcnt = 0;
991
992    /* allow room for null terminator */
993    size -= 1;
994
995    /* read loop */
996    while ((fflush(stdout), read(0, ptr, 1) > 0))
997    {
998	/* newline means we are done */
999	if ((ch = *ptr) == '\n' || ch == '\r')
1000	{
1001	    break;
1002	}
1003
1004	/* handle special editing characters */
1005	if (ch == ch_kill)
1006	{
1007	    /* kill line -- account for overstriking */
1008	    if (overstrike)
1009	    {
1010		msglen += maxcnt;
1011	    }
1012
1013	    /* return null string */
1014	    *buffer = '\0';
1015	    putchar('\r');
1016	    return(-1);
1017	}
1018	else if (ch == ch_erase)
1019	{
1020	    /* erase previous character */
1021	    if (cnt <= 0)
1022	    {
1023		/* none to erase! */
1024		putchar('\7');
1025	    }
1026	    else
1027	    {
1028		fputs("\b \b", stdout);
1029		ptr--;
1030		cnt--;
1031	    }
1032	}
1033	/* check for character validity and buffer overflow */
1034	else if (cnt == size || (numeric && !isdigit(ch)) ||
1035		!isprint(ch))
1036	{
1037	    /* not legal */
1038	    putchar('\7');
1039	}
1040	else
1041	{
1042	    /* echo it and store it in the buffer */
1043	    putchar(ch);
1044	    ptr++;
1045	    cnt++;
1046	    if (cnt > maxcnt)
1047	    {
1048		maxcnt = cnt;
1049	    }
1050	}
1051    }
1052
1053    /* all done -- null terminate the string */
1054    *ptr = '\0';
1055
1056    /* account for the extra characters in the message area */
1057    /* (if terminal overstrikes, remember the furthest they went) */
1058    msglen += overstrike ? maxcnt : cnt;
1059
1060    /* return either inputted number or string length */
1061    putchar('\r');
1062    return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
1063}
1064
1065/* internal support routines */
1066
1067static int string_count(pp)
1068
1069register char **pp;
1070
1071{
1072    register int cnt;
1073
1074    cnt = 0;
1075    while (*pp++ != NULL)
1076    {
1077	cnt++;
1078    }
1079    return(cnt);
1080}
1081
1082static void summary_format(str, numbers, names)
1083
1084char *str;
1085int *numbers;
1086register char **names;
1087
1088{
1089    register char *p;
1090    register int num;
1091    register char *thisname;
1092    register int useM = No;
1093
1094    /* format each number followed by its string */
1095    p = str;
1096    while ((thisname = *names++) != NULL)
1097    {
1098	/* get the number to format */
1099	num = *numbers++;
1100
1101	/* display only non-zero numbers */
1102	if (num > 0)
1103	{
1104	    /* is this number in kilobytes? */
1105	    if (thisname[0] == 'K')
1106	    {
1107		/* yes: format it as a memory value */
1108		p = strecpy(p, format_k(num));
1109
1110		/* skip over the K, since it was included by format_k */
1111		p = strecpy(p, thisname+1);
1112	    }
1113	    else
1114	    {
1115		p = strecpy(p, itoa(num));
1116		p = strecpy(p, thisname);
1117	    }
1118	}
1119
1120	/* ignore negative numbers, but display corresponding string */
1121	else if (num < 0)
1122	{
1123	    p = strecpy(p, thisname);
1124	}
1125    }
1126
1127    /* if the last two characters in the string are ", ", delete them */
1128    p -= 2;
1129    if (p >= str && p[0] == ',' && p[1] == ' ')
1130    {
1131	*p = '\0';
1132    }
1133}
1134
1135static void line_update(old, new, start, line)
1136
1137register char *old;
1138register char *new;
1139int start;
1140int line;
1141
1142{
1143    register int ch;
1144    register int diff;
1145    register int newcol = start + 1;
1146    register int lastcol = start;
1147    char cursor_on_line = No;
1148    char *current;
1149
1150    /* compare the two strings and only rewrite what has changed */
1151    current = old;
1152#ifdef DEBUG
1153    fprintf(debug, "line_update, starting at %d\n", start);
1154    fputs(old, debug);
1155    fputc('\n', debug);
1156    fputs(new, debug);
1157    fputs("\n-\n", debug);
1158#endif
1159
1160    /* start things off on the right foot		    */
1161    /* this is to make sure the invariants get set up right */
1162    if ((ch = *new++) != *old)
1163    {
1164	if (line - lastline == 1 && start == 0)
1165	{
1166	    putchar('\n');
1167	}
1168	else
1169	{
1170	    Move_to(start, line);
1171	}
1172	cursor_on_line = Yes;
1173	putchar(ch);
1174	*old = ch;
1175	lastcol = 1;
1176    }
1177    old++;
1178
1179    /*
1180     *  main loop -- check each character.  If the old and new aren't the
1181     *	same, then update the display.  When the distance from the
1182     *	current cursor position to the new change is small enough,
1183     *	the characters that belong there are written to move the
1184     *	cursor over.
1185     *
1186     *	Invariants:
1187     *	    lastcol is the column where the cursor currently is sitting
1188     *		(always one beyond the end of the last mismatch).
1189     */
1190    do		/* yes, a do...while */
1191    {
1192	if ((ch = *new++) != *old)
1193	{
1194	    /* new character is different from old	  */
1195	    /* make sure the cursor is on top of this character */
1196	    diff = newcol - lastcol;
1197	    if (diff > 0)
1198	    {
1199		/* some motion is required--figure out which is shorter */
1200		if (diff < 6 && cursor_on_line)
1201		{
1202		    /* overwrite old stuff--get it out of the old buffer */
1203		    printf("%.*s", diff, &current[lastcol-start]);
1204		}
1205		else
1206		{
1207		    /* use cursor addressing */
1208		    Move_to(newcol, line);
1209		    cursor_on_line = Yes;
1210		}
1211		/* remember where the cursor is */
1212		lastcol = newcol + 1;
1213	    }
1214	    else
1215	    {
1216		/* already there, update position */
1217		lastcol++;
1218	    }
1219
1220	    /* write what we need to */
1221	    if (ch == '\0')
1222	    {
1223		/* at the end--terminate with a clear-to-end-of-line */
1224		(void) clear_eol(strlen(old));
1225	    }
1226	    else
1227	    {
1228		/* write the new character */
1229		putchar(ch);
1230	    }
1231	    /* put the new character in the screen buffer */
1232	    *old = ch;
1233	}
1234
1235	/* update working column and screen buffer pointer */
1236	newcol++;
1237	old++;
1238
1239    } while (ch != '\0');
1240
1241    /* zero out the rest of the line buffer -- MUST BE DONE! */
1242    diff = display_width - newcol;
1243    if (diff > 0)
1244    {
1245	memzero(old, diff);
1246    }
1247
1248    /* remember where the current line is */
1249    if (cursor_on_line)
1250    {
1251	lastline = line;
1252    }
1253}
1254
1255/*
1256 *  printable(str) - make the string pointed to by "str" into one that is
1257 *	printable (i.e.: all ascii), by converting all non-printable
1258 *	characters into '?'.  Replacements are done in place and a pointer
1259 *	to the original buffer is returned.
1260 */
1261
1262char *printable(str)
1263
1264char *str;
1265
1266{
1267    register char *ptr;
1268    register char ch;
1269
1270    ptr = str;
1271    while ((ch = *ptr) != '\0')
1272    {
1273	if (!isprint(ch))
1274	{
1275	    *ptr = '?';
1276	}
1277	ptr++;
1278    }
1279    return(str);
1280}
1281
1282i_uptime(bt, tod)
1283
1284struct timeval* bt;
1285time_t *tod;
1286
1287{
1288    time_t uptime;
1289    int days, hrs, mins, secs;
1290
1291    if (bt->tv_sec != -1) {
1292	uptime = *tod - bt->tv_sec;
1293	days = uptime / 86400;
1294	uptime %= 86400;
1295	hrs = uptime / 3600;
1296	uptime %= 3600;
1297	mins = uptime / 60;
1298	secs = uptime % 60;
1299
1300	/*
1301	 *  Display the uptime.
1302	 */
1303
1304	if (smart_terminal)
1305	{
1306	    Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
1307	}
1308	else
1309	{
1310	    fputs(" ", stdout);
1311	}
1312	printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
1313    }
1314}
1315