display.c revision 146344
118334Speter/*
2169689Skan *  Top users/processes display for Unix
3169689Skan *  Version 3
4169689Skan *
518334Speter *  This program may be freely redistributed,
690075Sobrien *  but this entire comment MUST remain intact.
718334Speter *
890075Sobrien *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
990075Sobrien *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
1090075Sobrien *
1190075Sobrien * $FreeBSD: head/contrib/top/display.c 146344 2005-05-18 13:48:33Z keramida $
1218334Speter */
1390075Sobrien
1490075Sobrien/*
1590075Sobrien *  This file contains the routines that display information on the screen.
1690075Sobrien *  Each section of the screen has two routines:  one for initially writing
1718334Speter *  all constant and dynamic text, and one for only updating the text that
1818334Speter *  changes.  The prefix "i_" is used on all the "initial" routines and the
1990075Sobrien *  prefix "u_" is used for all the "updating" routines.
20169689Skan *
21169689Skan *  ASSUMPTIONS:
2218334Speter *        None of the "i_" routines use any of the termcap capabilities.
2318334Speter *        In this way, those routines can be safely used on terminals that
2418334Speter *        have minimal (or nonexistant) terminal capabilities.
2518334Speter *
2618334Speter *        The routines are called in this order:  *_loadave, i_timeofday,
27169689Skan *        *_procstates, *_cpustates, *_memory, *_message, *_header,
2818334Speter *        *_process, u_endscreen.
2918334Speter */
3050397Sobrien
31132718Skan#include "os.h"
32132718Skan#include <ctype.h>
3390075Sobrien#include <time.h>
3418334Speter#include <sys/time.h>
35169689Skan
3618334Speter#include "screen.h"		/* interface to screen package */
3790075Sobrien#include "layout.h"		/* defines for screen position layout */
3818334Speter#include "display.h"
3918334Speter#include "top.h"
4050397Sobrien#include "top.local.h"
4150397Sobrien#include "boolean.h"
4252284Sobrien#include "machine.h"		/* we should eliminate this!!! */
4390075Sobrien#include "utils.h"
4490075Sobrien
45169689Skan#ifdef DEBUG
46169689SkanFILE *debug;
47169689Skan#endif
4818334Speter
49169689Skan/* imported from screen.c */
50169689Skanextern int overstrike;
51169689Skan
52169689Skanstatic int lmpid = 0;
53169689Skanstatic int last_hi = 0;		/* used in u_process and u_endscreen */
54169689Skanstatic int lastline = 0;
55169689Skanstatic int display_width = MAX_COLS;
56169689Skan
57169689Skan#define lineindex(l) ((l)*display_width)
58169689Skan
59169689Skanchar *printable();
60169689Skan
61169689Skan/* things initialized by display_init and used thruout */
62169689Skan
63169689Skan/* buffer of proc information lines for display updating */
64169689Skanchar *screenbuf = NULL;
65169689Skan
66169689Skanstatic char **procstate_names;
67169689Skanstatic char **cpustate_names;
68169689Skanstatic char **memory_names;
69169689Skanstatic char **swap_names;
70169689Skan
7118334Speterstatic int num_procstates;
7218334Speterstatic int num_cpustates;
7318334Speterstatic int num_memory;
7418334Speterstatic int num_swap;
75132718Skan
76132718Skanstatic int *lprocstates;
7790075Sobrienstatic int *lcpustates;
78169689Skanstatic int *lmemory;
79132718Skanstatic int *lswap;
80169689Skan
81169689Skanstatic int *cpustate_columns;
82169689Skanstatic int cpustate_total_length;
83169689Skan
84132718Skanstatic enum { OFF, ON, ERASE } header_status = ON;
85132718Skan
86132718Skanstatic int string_count();
87132718Skanstatic void summary_format();
88169689Skanstatic void line_update();
89132718Skan
90132718Skanint display_resize()
91132718Skan
92132718Skan{
93132718Skan    register int lines;
94132718Skan
95132718Skan    /* first, deallocate any previous buffer that may have been there */
96169689Skan    if (screenbuf != NULL)
97169689Skan    {
98132718Skan	free(screenbuf);
99132718Skan    }
100132718Skan
101132718Skan    /* calculate the current dimensions */
102132718Skan    /* if operating in "dumb" mode, we only need one line */
103132718Skan    lines = smart_terminal ? screen_length - Header_lines : 1;
104132718Skan
105169689Skan    if (lines < 0)
106169689Skan	lines = 0;
107169689Skan    /* we don't want more than MAX_COLS columns, since the machine-dependent
108169689Skan       modules make static allocations based on MAX_COLS and we don't want
109169689Skan       to run off the end of their buffers */
11018334Speter    display_width = screen_width;
111169689Skan    if (display_width >= MAX_COLS)
112169689Skan    {
113169689Skan	display_width = MAX_COLS - 1;
114169689Skan    }
115169689Skan
116169689Skan    /* now, allocate space for the screen buffer */
117169689Skan    screenbuf = (char *)malloc(lines * display_width);
118169689Skan    if (screenbuf == (char *)NULL)
119169689Skan    {
120169689Skan	/* oops! */
121169689Skan	return(-1);
122169689Skan    }
123169689Skan
124169689Skan    /* return number of lines available */
125169689Skan    /* for dumb terminals, pretend like we can show any amount */
126169689Skan    return(smart_terminal ? lines : Largest);
127169689Skan}
128169689Skan
129169689Skanint display_init(statics)
130169689Skan
131169689Skanstruct statics *statics;
132169689Skan
133169689Skan{
134169689Skan    register int lines;
135169689Skan    register char **pp;
136169689Skan    register int *ip;
137169689Skan    register int i;
138169689Skan
139169689Skan    /* call resize to do the dirty work */
140169689Skan    lines = display_resize();
14118334Speter
14218334Speter    /* only do the rest if we need to */
14318334Speter    if (lines > -1)
14418334Speter    {
145132718Skan	/* save pointers and allocate space for names */
14618334Speter	procstate_names = statics->procstate_names;
14718334Speter	num_procstates = string_count(procstate_names);
14818334Speter	lprocstates = (int *)malloc(num_procstates * sizeof(int));
14990075Sobrien
15052284Sobrien	cpustate_names = statics->cpustate_names;
15152284Sobrien
15218334Speter	swap_names = statics->swap_names;
15390075Sobrien	num_swap = string_count(swap_names);
15418334Speter	lswap = (int *)malloc(num_swap * sizeof(int));
15518334Speter	num_cpustates = string_count(cpustate_names);
156117395Skan	lcpustates = (int *)malloc(num_cpustates * sizeof(int));
15718334Speter	cpustate_columns = (int *)malloc(num_cpustates * sizeof(int));
15818334Speter
15918334Speter	memory_names = statics->memory_names;
16018334Speter	num_memory = string_count(memory_names);
16118334Speter	lmemory = (int *)malloc(num_memory * sizeof(int));
16218334Speter
16318334Speter	/* calculate starting columns where needed */
16418334Speter	cpustate_total_length = 0;
165132718Skan	pp = cpustate_names;
16618334Speter	ip = cpustate_columns;
16752284Sobrien	while (*pp != NULL)
16818334Speter	{
16918334Speter	    *ip++ = cpustate_total_length;
17018334Speter	    if ((i = strlen(*pp++)) > 0)
17118334Speter	    {
17218334Speter		cpustate_total_length += i + 8;
17318334Speter	    }
17418334Speter	}
175169689Skan    }
17618334Speter
17718334Speter    /* return number of lines available */
17818334Speter    return(lines);
17918334Speter}
18018334Speter
18118334Speteri_loadave(mpid, avenrun)
18218334Speter
18318334Speterint mpid;
18452284Sobriendouble *avenrun;
18518334Speter
18618334Speter{
18718334Speter    register int i;
18852284Sobrien
18918334Speter    /* i_loadave also clears the screen, since it is first */
19018334Speter    clear();
19118334Speter
19252284Sobrien    /* mpid == -1 implies this system doesn't have an _mpid */
19318334Speter    if (mpid != -1)
19418334Speter    {
19518334Speter	printf("last pid: %5d;  ", mpid);
19618334Speter    }
19718334Speter
19818334Speter    printf("load averages");
19918334Speter
20018334Speter    for (i = 0; i < 3; i++)
20118334Speter    {
20296263Sobrien	printf("%c %5.2f",
20396263Sobrien	    i == 0 ? ':' : ',',
20496263Sobrien	    avenrun[i]);
20596263Sobrien    }
20696263Sobrien    lmpid = mpid;
20718334Speter}
20818334Speter
20918334Speteru_loadave(mpid, avenrun)
21018334Speter
21118334Speterint mpid;
21218334Speterdouble *avenrun;
21318334Speter
214169689Skan{
21518334Speter    register int i;
21618334Speter
21718334Speter    if (mpid != -1)
218169689Skan    {
219169689Skan	/* change screen only when value has really changed */
22018334Speter	if (mpid != lmpid)
22118334Speter	{
222169689Skan	    Move_to(x_lastpid, y_lastpid);
22318334Speter	    printf("%5d", mpid);
22418334Speter	    lmpid = mpid;
22518334Speter	}
226117395Skan
227117395Skan	/* i remembers x coordinate to move to */
228117395Skan	i = x_loadave;
229117395Skan    }
230132718Skan    else
231117395Skan    {
232117395Skan	i = x_loadave_nompid;
233117395Skan    }
234117395Skan
235117395Skan    /* move into position for load averages */
236117395Skan    Move_to(i, y_loadave);
237117395Skan
238169689Skan    /* display new load averages */
239169689Skan    /* we should optimize this and only display changes */
240169689Skan    for (i = 0; i < 3; i++)
241117395Skan    {
242117395Skan	printf("%s%5.2f",
243117395Skan	    i == 0 ? "" : ", ",
244117395Skan	    avenrun[i]);
245117395Skan    }
246117395Skan}
24718334Speter
24818334Speteri_timeofday(tod)
24918334Speter
25018334Spetertime_t *tod;
251132718Skan
25218334Speter{
253132718Skan    /*
25452750Sobrien     *  Display the current time.
25518334Speter     *  "ctime" always returns a string that looks like this:
256169689Skan     *
257169689Skan     *	Sun Sep 16 01:03:52 1973
258169689Skan     *      012345678901234567890123
259169689Skan     *	          1         2
260169689Skan     *
261169689Skan     *  We want indices 11 thru 18 (length 8).
262169689Skan     */
263169689Skan
264169689Skan    if (smart_terminal)
265169689Skan    {
266169689Skan	Move_to(screen_width - 8, 0);
26718334Speter    }
268169689Skan    else
26918334Speter    {
270169689Skan	fputs("    ", stdout);
271169689Skan    }
272169689Skan#ifdef DEBUG
27318334Speter    {
27418334Speter	char *foo;
275169689Skan	foo = ctime(tod);
27618334Speter	fputs(foo, stdout);
27790075Sobrien    }
27890075Sobrien#endif
27918334Speter    printf("%-8.8s\n", &(ctime(tod)[11]));
28018334Speter    lastline = 1;
28118334Speter}
28218334Speter
28318334Speterstatic int ltotal = 0;
28418334Speterstatic char procstates_buffer[MAX_COLS];
28518334Speter
28618334Speter/*
28718334Speter *  *_procstates(total, brkdn, names) - print the process summary line
28818334Speter *
28918334Speter *  Assumptions:  cursor is at the beginning of the line on entry
29018334Speter *		  lastline is valid
291169689Skan */
292169689Skan
293169689Skani_procstates(total, brkdn)
29450397Sobrien
295169689Skanint total;
29618334Speterint *brkdn;
297169689Skan
298169689Skan{
299169689Skan    register int i;
300169689Skan
30118334Speter    /* write current number of processes and remember the value */
302169689Skan    printf("%d processes:", total);
303169689Skan    ltotal = total;
304169689Skan
305169689Skan    /* put out enough spaces to get to column 15 */
30618334Speter    i = digits(total);
307169689Skan    while (i++ < 4)
30818334Speter    {
30918334Speter	putchar(' ');
31018334Speter    }
31118334Speter
312169689Skan    /* format and print the process state summary */
31318334Speter    summary_format(procstates_buffer, brkdn, procstate_names);
31452284Sobrien    fputs(procstates_buffer, stdout);
31552284Sobrien
316169689Skan    /* save the numbers for next time */
317169689Skan    memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
318169689Skan}
319169689Skan
32018334Speteru_procstates(total, brkdn)
32118334Speter
32218334Speterint total;
32318334Speterint *brkdn;
324169689Skan
325169689Skan{
326169689Skan    static char new[MAX_COLS];
327169689Skan    register int i;
328169689Skan
329169689Skan    /* update number of processes only if it has changed */
330169689Skan    if (ltotal != total)
331169689Skan    {
332169689Skan	/* move and overwrite */
333169689Skan#if (x_procstate == 0)
334169689Skan	Move_to(x_procstate, y_procstate);
335169689Skan#else
336169689Skan	/* cursor is already there...no motion needed */
337169689Skan	/* assert(lastline == 1); */
338169689Skan#endif
339169689Skan	printf("%d", total);
340169689Skan
341169689Skan	/* if number of digits differs, rewrite the label */
342169689Skan	if (digits(total) != digits(ltotal))
343169689Skan	{
344169689Skan	    fputs(" processes:", stdout);
345169689Skan	    /* put out enough spaces to get to column 15 */
346169689Skan	    i = digits(total);
34718334Speter	    while (i++ < 4)
348169689Skan	    {
349169689Skan		putchar(' ');
35018334Speter	    }
351169689Skan	    /* cursor may end up right where we want it!!! */
352169689Skan	}
35318334Speter
354169689Skan	/* save new total */
355169689Skan	ltotal = total;
356169689Skan    }
357169689Skan
358169689Skan    /* see if any of the state numbers has changed */
359169689Skan    if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
360169689Skan    {
361169689Skan	/* format and update the line */
362169689Skan	summary_format(new, brkdn, procstate_names);
363169689Skan	line_update(procstates_buffer, new, x_brkdn, y_brkdn);
364169689Skan	memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
365169689Skan    }
366169689Skan}
367169689Skan
368169689Skan/*
369169689Skan *  *_cpustates(states, names) - print the cpu state percentages
370169689Skan *
371169689Skan *  Assumptions:  cursor is on the PREVIOUS line
372169689Skan */
373169689Skan
374169689Skanstatic int cpustates_column;
37518334Speter
37618334Speter/* cpustates_tag() calculates the correct tag to use to label the line */
37718334Speter
378169689Skanchar *cpustates_tag()
379169689Skan
380169689Skan{
381169689Skan    register char *use;
382169689Skan
383169689Skan    static char *short_tag = "CPU: ";
384169689Skan    static char *long_tag = "CPU states: ";
385169689Skan
386169689Skan    /* if length + strlen(long_tag) >= screen_width, then we have to
387169689Skan       use the shorter tag (we subtract 2 to account for ": ") */
388169689Skan    if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
389169689Skan    {
390169689Skan	use = short_tag;
39118334Speter    }
39218334Speter    else
39318334Speter    {
39418334Speter	use = long_tag;
395169689Skan    }
39618334Speter
39718334Speter    /* set cpustates_column accordingly then return result */
39818334Speter    cpustates_column = strlen(use);
39918334Speter    return(use);
40018334Speter}
40118334Speter
40218334Speteri_cpustates(states)
403169689Skan
40418334Speterregister int *states;
405169689Skan
40618334Speter{
40718334Speter    register int i = 0;
40818334Speter    register int value;
40918334Speter    register char **names = cpustate_names;
41018334Speter    register char *thisname;
411169689Skan
412169689Skan    /* print tag and bump lastline */
413169689Skan    printf("\n%s", cpustates_tag());
41418334Speter    lastline++;
41518334Speter
41618334Speter    /* now walk thru the names and print the line */
41718334Speter    while ((thisname = *names++) != NULL)
418169689Skan    {
419169689Skan	if (*thisname != '\0')
42018334Speter	{
42118334Speter	    /* retrieve the value and remember it */
42218334Speter	    value = *states++;
42318334Speter
424169689Skan	    /* if percentage is >= 1000, print it as 100% */
425169689Skan	    printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
426169689Skan		   i++ == 0 ? "" : ", ",
42718334Speter		   ((float)value)/10.,
42818334Speter		   thisname);
42918334Speter	}
43018334Speter    }
43118334Speter
43218334Speter    /* copy over values into "last" array */
43318334Speter    memcpy(lcpustates, states, num_cpustates * sizeof(int));
43418334Speter}
43518334Speter
43618334Speteru_cpustates(states)
43718334Speter
43818334Speterregister int *states;
43918334Speter
44018334Speter{
44118334Speter    register int value;
44218334Speter    register char **names = cpustate_names;
44318334Speter    register char *thisname;
44418334Speter    register int *lp;
44518334Speter    register int *colp;
44618334Speter
44718334Speter    Move_to(cpustates_column, y_cpustates);
44818334Speter    lastline = y_cpustates;
44918334Speter    lp = lcpustates;
45018334Speter    colp = cpustate_columns;
451132718Skan
45218334Speter    /* we could be much more optimal about this */
45318334Speter    while ((thisname = *names++) != NULL)
45418334Speter    {
45518334Speter	if (*thisname != '\0')
45618334Speter	{
45718334Speter	    /* did the value change since last time? */
45818334Speter	    if (*lp != *states)
459169689Skan	    {
460169689Skan		/* yes, move and change */
461169689Skan		Move_to(cpustates_column + *colp, y_cpustates);
462169689Skan		lastline = y_cpustates;
46318334Speter
46418334Speter		/* retrieve value and remember it */
465169689Skan		value = *states;
466169689Skan
467169689Skan		/* if percentage is >= 1000, print it as 100% */
468169689Skan		printf((value >= 1000 ? "%4.0f" : "%4.1f"),
469169689Skan		       ((double)value)/10.);
470169689Skan
471169689Skan		/* remember it for next time */
472169689Skan		*lp = value;
473169689Skan	    }
474169689Skan	}
475169689Skan
476169689Skan	/* increment and move on */
477169689Skan	lp++;
478169689Skan	states++;
47918334Speter	colp++;
48018334Speter    }
48118334Speter}
48218334Speter
48318334Speterz_cpustates()
484169689Skan
485169689Skan{
486169689Skan    register int i = 0;
487169689Skan    register char **names = cpustate_names;
48818334Speter    register char *thisname;
48918334Speter    register int *lp;
490169689Skan
491169689Skan    /* show tag and bump lastline */
492169689Skan    printf("\n%s", cpustates_tag());
493169689Skan    lastline++;
494169689Skan
495169689Skan    while ((thisname = *names++) != NULL)
496169689Skan    {
497169689Skan	if (*thisname != '\0')
498169689Skan	{
499169689Skan	    printf("%s    %% %s", i++ == 0 ? "" : ", ", thisname);
500169689Skan	}
501169689Skan    }
502169689Skan
503169689Skan    /* fill the "last" array with all -1s, to insure correct updating */
50418334Speter    lp = lcpustates;
505169689Skan    i = num_cpustates;
50618334Speter    while (--i >= 0)
50718334Speter    {
50818334Speter	*lp++ = -1;
509169689Skan    }
51018334Speter}
511169689Skan
51250397Sobrien/*
51318334Speter *  *_memory(stats) - print "Memory: " followed by the memory summary string
51418334Speter *
51518334Speter *  Assumptions:  cursor is on "lastline"
51618334Speter *                for i_memory ONLY: cursor is on the previous line
51718334Speter */
51818334Speter
51918334Speterchar memory_buffer[MAX_COLS];
520169689Skan
521169689Skani_memory(stats)
522169689Skan
523169689Skanint *stats;
524169689Skan
525169689Skan{
526169689Skan    fputs("\nMem: ", stdout);
527169689Skan    lastline++;
528169689Skan
529169689Skan    /* format and print the memory summary */
530169689Skan    summary_format(memory_buffer, stats, memory_names);
531169689Skan    fputs(memory_buffer, stdout);
532169689Skan}
533169689Skan
534169689Skanu_memory(stats)
535169689Skan
536169689Skanint *stats;
537169689Skan
538169689Skan{
539169689Skan    static char new[MAX_COLS];
540169689Skan
541169689Skan    /* format the new line */
542169689Skan    summary_format(new, stats, memory_names);
543169689Skan    line_update(memory_buffer, new, x_mem, y_mem);
544169689Skan}
545169689Skan
546169689Skan/*
547169689Skan *  *_swap(stats) - print "Swap: " followed by the swap summary string
548169689Skan *
549169689Skan *  Assumptions:  cursor is on "lastline"
550169689Skan *                for i_swap ONLY: cursor is on the previous line
551169689Skan */
552169689Skan
553169689Skanchar swap_buffer[MAX_COLS];
554169689Skan
555169689Skani_swap(stats)
556169689Skan
557169689Skanint *stats;
558169689Skan
559169689Skan{
560169689Skan    fputs("\nSwap: ", stdout);
561169689Skan    lastline++;
562169689Skan
563169689Skan    /* format and print the swap summary */
564169689Skan    summary_format(swap_buffer, stats, swap_names);
565169689Skan    fputs(swap_buffer, stdout);
566169689Skan}
567169689Skan
568169689Skanu_swap(stats)
569169689Skan
570169689Skanint *stats;
571169689Skan
572169689Skan{
573169689Skan    static char new[MAX_COLS];
574169689Skan
575169689Skan    /* format the new line */
576169689Skan    summary_format(new, stats, swap_names);
577169689Skan    line_update(swap_buffer, new, x_swap, y_swap);
578169689Skan}
579169689Skan
580169689Skan/*
581169689Skan *  *_message() - print the next pending message line, or erase the one
582169689Skan *                that is there.
583169689Skan *
584169689Skan *  Note that u_message is (currently) the same as i_message.
585169689Skan *
586169689Skan *  Assumptions:  lastline is consistent
587169689Skan */
588169689Skan
589169689Skan/*
590169689Skan *  i_message is funny because it gets its message asynchronously (with
591169689Skan *	respect to screen updates).
592169689Skan */
593169689Skan
594169689Skanstatic char next_msg[MAX_COLS + 5];
595169689Skanstatic int msglen = 0;
596169689Skan/* Invariant: msglen is always the length of the message currently displayed
597169689Skan   on the screen (even when next_msg doesn't contain that message). */
598169689Skan
599169689Skani_message()
600169689Skan
601169689Skan{
602169689Skan    while (lastline < y_message)
603169689Skan    {
604169689Skan	fputc('\n', stdout);
605169689Skan	lastline++;
606169689Skan    }
607169689Skan    if (next_msg[0] != '\0')
608169689Skan    {
609169689Skan	standout(next_msg);
610169689Skan	msglen = strlen(next_msg);
611169689Skan	next_msg[0] = '\0';
612169689Skan    }
613169689Skan    else if (msglen > 0)
614169689Skan    {
615169689Skan	(void) clear_eol(msglen);
616169689Skan	msglen = 0;
617169689Skan    }
618169689Skan}
619169689Skan
620169689Skanu_message()
621169689Skan
622169689Skan{
623169689Skan    i_message();
624169689Skan}
625169689Skan
626169689Skanstatic int header_length;
627169689Skan
628169689Skan/*
629169689Skan * Trim a header string to the current display width and return a newly
630169689Skan * allocated area with the trimmed header.
631169689Skan */
632169689Skan
633169689Skanchar *
634169689Skantrim_header(text)
635169689Skan
636169689Skanchar *text;
637169689Skan
638169689Skan{
639169689Skan	char *s;
640169689Skan	int width;
641169689Skan
642169689Skan	s = NULL;
643169689Skan	width = display_width;
644169689Skan	header_length = strlen(text);
645169689Skan	if (header_length >= width) {
646169689Skan		s = malloc((width + 1) * sizeof(char));
647169689Skan		if (s == NULL)
648169689Skan			return (NULL);
649169689Skan		strncpy(s, text, width);
650169689Skan		s[width] = '\0';
651169689Skan	}
652169689Skan	return (s);
653169689Skan}
654169689Skan
655169689Skan/*
656169689Skan *  *_header(text) - print the header for the process area
657169689Skan *
658169689Skan *  Assumptions:  cursor is on the previous line and lastline is consistent
659169689Skan */
660169689Skan
661169689Skani_header(text)
662169689Skan
663169689Skanchar *text;
664169689Skan
665169689Skan{
666169689Skan    char *s;
667169689Skan
668169689Skan    s = trim_header(text);
669169689Skan    if (s != NULL)
670169689Skan	text = s;
671169689Skan
672169689Skan    if (header_status == ON)
673169689Skan    {
674169689Skan	putchar('\n');
675169689Skan	fputs(text, stdout);
676169689Skan	lastline++;
677169689Skan    }
678169689Skan    else if (header_status == ERASE)
679169689Skan    {
680169689Skan	header_status = OFF;
681169689Skan    }
682169689Skan    free(s);
683169689Skan}
684169689Skan
685169689Skan/*ARGSUSED*/
686169689Skanu_header(text)
687169689Skan
688169689Skanchar *text;		/* ignored */
689169689Skan
690169689Skan{
691169689Skan    char *s;
692169689Skan
693169689Skan    s = trim_header(text);
694169689Skan    if (s != NULL)
695169689Skan	text = s;
696169689Skan
697169689Skan    if (header_status == ERASE)
698169689Skan    {
699169689Skan	putchar('\n');
700169689Skan	lastline++;
701169689Skan	clear_eol(header_length);
702169689Skan	header_status = OFF;
703169689Skan    }
704169689Skan    free(s);
705169689Skan}
706169689Skan
707169689Skan/*
708169689Skan *  *_process(line, thisline) - print one process line
709169689Skan *
710169689Skan *  Assumptions:  lastline is consistent
711169689Skan */
712169689Skan
713169689Skani_process(line, thisline)
714169689Skan
715169689Skanint line;
716169689Skanchar *thisline;
717169689Skan
718169689Skan{
719169689Skan    register char *p;
720169689Skan    register char *base;
721169689Skan
722169689Skan    /* make sure we are on the correct line */
723169689Skan    while (lastline < y_procs + line)
724169689Skan    {
725169689Skan	putchar('\n');
726169689Skan	lastline++;
727169689Skan    }
728169689Skan
729169689Skan    /* truncate the line to conform to our current screen width */
730169689Skan    thisline[display_width] = '\0';
731169689Skan
732169689Skan    /* write the line out */
733169689Skan    fputs(thisline, stdout);
734169689Skan
735169689Skan    /* copy it in to our buffer */
73618334Speter    base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
737169689Skan    p = strecpy(base, thisline);
738169689Skan
739169689Skan    /* zero fill the rest of it */
740169689Skan    memzero(p, display_width - (p - base));
741169689Skan}
742169689Skan
743169689Skanu_process(line, newline)
744169689Skan
745169689Skanint line;
746169689Skanchar *newline;
747169689Skan
748169689Skan{
749169689Skan    register char *optr;
750169689Skan    register int screen_line = line + Header_lines;
751169689Skan    register char *bufferline;
752169689Skan
753169689Skan    /* remember a pointer to the current line in the screen buffer */
754169689Skan    bufferline = &screenbuf[lineindex(line)];
755169689Skan
756169689Skan    /* truncate the line to conform to our current screen width */
757169689Skan    newline[display_width] = '\0';
758169689Skan
759169689Skan    /* is line higher than we went on the last display? */
760169689Skan    if (line >= last_hi)
761169689Skan    {
762169689Skan	/* yes, just ignore screenbuf and write it out directly */
763169689Skan	/* get positioned on the correct line */
76418334Speter	if (screen_line - lastline == 1)
76518334Speter	{
76618334Speter	    putchar('\n');
76718334Speter	    lastline++;
76818334Speter	}
769169689Skan	else
77018334Speter	{
771169689Skan	    Move_to(0, screen_line);
772169689Skan	    lastline = screen_line;
773169689Skan	}
774169689Skan
775169689Skan	/* now write the line */
776169689Skan	fputs(newline, stdout);
777169689Skan
778169689Skan	/* copy it in to the buffer */
779169689Skan	optr = strecpy(bufferline, newline);
780169689Skan
781169689Skan	/* zero fill the rest of it */
782169689Skan	memzero(optr, display_width - (optr - bufferline));
783169689Skan    }
784169689Skan    else
785169689Skan    {
786169689Skan	line_update(bufferline, newline, 0, line + Header_lines);
787169689Skan    }
78890075Sobrien}
78990075Sobrien
79018334Speteru_endscreen(hi)
79118334Speter
79218334Speterregister int hi;
79318334Speter
79450397Sobrien{
79550397Sobrien    register int screen_line = hi + Header_lines;
79618334Speter    register int i;
79718334Speter
79890075Sobrien    if (smart_terminal)
79990075Sobrien    {
80090075Sobrien	if (hi < last_hi)
801169689Skan	{
802169689Skan	    /* need to blank the remainder of the screen */
80390075Sobrien	    /* but only if there is any screen left below this line */
80490075Sobrien	    if (lastline + 1 < screen_length)
805169689Skan	    {
806169689Skan		/* efficiently move to the end of currently displayed info */
80790075Sobrien		if (screen_line - lastline < 5)
808169689Skan		{
809132718Skan		    while (lastline < screen_line)
810132718Skan		    {
811132718Skan			putchar('\n');
81218334Speter			lastline++;
813132718Skan		    }
814169689Skan		}
815132718Skan		else
816169689Skan		{
81718334Speter		    Move_to(0, screen_line);
81818334Speter		    lastline = screen_line;
81918334Speter		}
82018334Speter
82118334Speter		if (clear_to_end)
82218334Speter		{
823132718Skan		    /* we can do this the easy way */
824132718Skan		    putcap(clear_to_end);
82518334Speter		}
826169689Skan		else
82718334Speter		{
82852284Sobrien		    /* use clear_eol on each line */
82918334Speter		    i = hi;
83018334Speter		    while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
83118334Speter		    {
83218334Speter			putchar('\n');
83350397Sobrien		    }
83418334Speter		}
835169689Skan	    }
836169689Skan	}
83718334Speter	last_hi = hi;
83818334Speter
83918334Speter	/* move the cursor to a pleasant place */
840169689Skan	Move_to(x_idlecursor, y_idlecursor);
84118334Speter	lastline = y_idlecursor;
84218334Speter    }
84318334Speter    else
84418334Speter    {
84518334Speter	/* separate this display from the next with some vertical room */
84618334Speter	fputs("\n\n", stdout);
84718334Speter    }
84818334Speter}
849169689Skan
850169689Skandisplay_header(t)
851169689Skan
852132718Skanint t;
85318334Speter
854169689Skan{
85518334Speter    if (t)
85618334Speter    {
85718334Speter	header_status = ON;
858169689Skan    }
85918334Speter    else if (header_status == ON)
86018334Speter    {
86118334Speter	header_status = ERASE;
86218334Speter    }
86318334Speter}
86418334Speter
86590075Sobrien/*VARARGS2*/
86690075Sobriennew_message(type, msgfmt, a1, a2, a3)
86718334Speter
86818334Speterint type;
86918334Speterchar *msgfmt;
87018334Spetercaddr_t a1, a2, a3;
871169689Skan
87218334Speter{
87318334Speter    register int i;
87418334Speter
87590075Sobrien    /* first, format the message */
87618334Speter    (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3);
87718334Speter
878169689Skan    if (msglen > 0)
879169689Skan    {
88090075Sobrien	/* message there already -- can we clear it? */
881169689Skan	if (!overstrike)
88290075Sobrien	{
88390075Sobrien	    /* yes -- write it and clear to end */
884169689Skan	    i = strlen(next_msg);
88590075Sobrien	    if ((type & MT_delayed) == 0)
88690075Sobrien	    {
887169689Skan		type & MT_standout ? standout(next_msg) :
888169689Skan		                     fputs(next_msg, stdout);
88990075Sobrien		(void) clear_eol(msglen - i);
89090075Sobrien		msglen = i;
89190075Sobrien		next_msg[0] = '\0';
89290075Sobrien	    }
89390075Sobrien	}
89490075Sobrien    }
895169689Skan    else
896169689Skan    {
89790075Sobrien	if ((type & MT_delayed) == 0)
89890075Sobrien	{
899169689Skan	    type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
90018334Speter	    msglen = strlen(next_msg);
90118334Speter	    next_msg[0] = '\0';
902169689Skan	}
90318334Speter    }
904132718Skan}
905132718Skan
906169689Skanclear_message()
907169689Skan
908169689Skan{
909169689Skan    if (clear_eol(msglen) == 1)
910169689Skan    {
911169689Skan	putchar('\r');
912169689Skan    }
913169689Skan}
914169689Skan
915169689Skanreadline(buffer, size, numeric)
916169689Skan
917169689Skanchar *buffer;
91818334Speterint  size;
91950397Sobrienint  numeric;
920132718Skan
921169689Skan{
922169689Skan    register char *ptr = buffer;
923132718Skan    register char ch;
924132718Skan    register char cnt = 0;
92550397Sobrien    register char maxcnt = 0;
92650397Sobrien
92718334Speter    /* allow room for null terminator */
92818334Speter    size -= 1;
92918334Speter
93018334Speter    /* read loop */
93118334Speter    while ((fflush(stdout), read(0, ptr, 1) > 0))
932169689Skan    {
93318334Speter	/* newline means we are done */
93418334Speter	if ((ch = *ptr) == '\n' || ch == '\r')
935169689Skan	{
93618334Speter	    break;
93718334Speter	}
938169689Skan
93918334Speter	/* handle special editing characters */
940169689Skan	if (ch == ch_kill)
941169689Skan	{
942169689Skan	    /* kill line -- account for overstriking */
943169689Skan	    if (overstrike)
944169689Skan	    {
945169689Skan		msglen += maxcnt;
946169689Skan	    }
947169689Skan
948169689Skan	    /* return null string */
94918334Speter	    *buffer = '\0';
95018334Speter	    putchar('\r');
95118334Speter	    return(-1);
95218334Speter	}
95318334Speter	else if (ch == ch_erase)
95418334Speter	{
95518334Speter	    /* erase previous character */
95618334Speter	    if (cnt <= 0)
957169689Skan	    {
958169689Skan		/* none to erase! */
959169689Skan		putchar('\7');
960132718Skan	    }
961169689Skan	    else
962132718Skan	    {
963132718Skan		fputs("\b \b", stdout);
964132718Skan		ptr--;
965132718Skan		cnt--;
966132718Skan	    }
967169689Skan	}
968169689Skan	/* check for character validity and buffer overflow */
969169689Skan	else if (cnt == size || (numeric && !isdigit(ch)) ||
970169689Skan		!isprint(ch))
971169689Skan	{
972169689Skan	    /* not legal */
973169689Skan	    putchar('\7');
974132718Skan	}
975132718Skan	else
976132718Skan	{
977132718Skan	    /* echo it and store it in the buffer */
978132718Skan	    putchar(ch);
979169689Skan	    ptr++;
980169689Skan	    cnt++;
981169689Skan	    if (cnt > maxcnt)
982169689Skan	    {
983169689Skan		maxcnt = cnt;
984169689Skan	    }
985169689Skan	}
986132718Skan    }
987132718Skan
988132718Skan    /* all done -- null terminate the string */
989132718Skan    *ptr = '\0';
990132718Skan
991169689Skan    /* account for the extra characters in the message area */
992132718Skan    /* (if terminal overstrikes, remember the furthest they went) */
993169689Skan    msglen += overstrike ? maxcnt : cnt;
994169689Skan
995169689Skan    /* return either inputted number or string length */
996169689Skan    putchar('\r');
997169689Skan    return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
998169689Skan}
999169689Skan
1000132718Skan/* internal support routines */
1001169689Skan
1002132718Skanstatic int string_count(pp)
1003169689Skan
1004169689Skanregister char **pp;
1005169689Skan
1006169689Skan{
1007169689Skan    register int cnt;
1008169689Skan
1009169689Skan    cnt = 0;
1010169689Skan    while (*pp++ != NULL)
1011169689Skan    {
1012169689Skan	cnt++;
1013169689Skan    }
1014169689Skan    return(cnt);
1015169689Skan}
1016169689Skan
1017169689Skanstatic void summary_format(str, numbers, names)
1018132718Skan
1019169689Skanchar *str;
1020169689Skanint *numbers;
1021169689Skanregister char **names;
1022169689Skan
1023169689Skan{
1024169689Skan    register char *p;
1025169689Skan    register int num;
1026169689Skan    register char *thisname;
1027169689Skan    register int useM = No;
1028169689Skan
1029169689Skan    /* format each number followed by its string */
1030169689Skan    p = str;
1031169689Skan    while ((thisname = *names++) != NULL)
1032169689Skan    {
1033169689Skan	/* get the number to format */
1034132718Skan	num = *numbers++;
1035132718Skan
1036132718Skan	/* display only non-zero numbers */
1037132718Skan	if (num > 0)
1038132718Skan	{
1039132718Skan	    /* is this number in kilobytes? */
1040132718Skan	    if (thisname[0] == 'K')
1041169689Skan	    {
1042132718Skan		/* yes: format it as a memory value */
1043132718Skan		p = strecpy(p, format_k(num));
1044132718Skan
1045132718Skan		/* skip over the K, since it was included by format_k */
1046132718Skan		p = strecpy(p, thisname+1);
1047132718Skan	    }
1048132718Skan	    else
1049169689Skan	    {
1050169689Skan		p = strecpy(p, itoa(num));
1051169689Skan		p = strecpy(p, thisname);
1052132718Skan	    }
1053132718Skan	}
1054132718Skan
1055132718Skan	/* ignore negative numbers, but display corresponding string */
1056132718Skan	else if (num < 0)
1057132718Skan	{
1058132718Skan	    p = strecpy(p, thisname);
1059132718Skan	}
1060132718Skan    }
1061132718Skan
1062132718Skan    /* if the last two characters in the string are ", ", delete them */
1063132718Skan    p -= 2;
1064132718Skan    if (p >= str && p[0] == ',' && p[1] == ' ')
1065132718Skan    {
1066132718Skan	*p = '\0';
1067132718Skan    }
1068132718Skan}
1069132718Skan
1070132718Skanstatic void line_update(old, new, start, line)
1071132718Skan
1072132718Skanregister char *old;
1073132718Skanregister char *new;
1074169689Skanint start;
1075132718Skanint line;
1076132718Skan
1077169689Skan{
1078132718Skan    register int ch;
1079132718Skan    register int diff;
1080132718Skan    register int newcol = start + 1;
1081132718Skan    register int lastcol = start;
1082132718Skan    char cursor_on_line = No;
1083132718Skan    char *current;
1084169689Skan
1085169689Skan    /* compare the two strings and only rewrite what has changed */
1086169689Skan    current = old;
1087169689Skan#ifdef DEBUG
1088169689Skan    fprintf(debug, "line_update, starting at %d\n", start);
1089169689Skan    fputs(old, debug);
1090169689Skan    fputc('\n', debug);
1091169689Skan    fputs(new, debug);
1092169689Skan    fputs("\n-\n", debug);
1093169689Skan#endif
1094169689Skan
1095169689Skan    /* start things off on the right foot		    */
1096169689Skan    /* this is to make sure the invariants get set up right */
1097169689Skan    if ((ch = *new++) != *old)
1098169689Skan    {
1099169689Skan	if (line - lastline == 1 && start == 0)
1100169689Skan	{
1101169689Skan	    putchar('\n');
1102169689Skan	}
1103169689Skan	else
1104169689Skan	{
1105169689Skan	    Move_to(start, line);
1106169689Skan	}
1107169689Skan	cursor_on_line = Yes;
1108169689Skan	putchar(ch);
1109169689Skan	*old = ch;
1110169689Skan	lastcol = 1;
1111169689Skan    }
1112169689Skan    old++;
1113169689Skan
1114169689Skan    /*
1115132718Skan     *  main loop -- check each character.  If the old and new aren't the
1116169689Skan     *	same, then update the display.  When the distance from the
1117169689Skan     *	current cursor position to the new change is small enough,
1118169689Skan     *	the characters that belong there are written to move the
1119169689Skan     *	cursor over.
1120132718Skan     *
1121132718Skan     *	Invariants:
1122132718Skan     *	    lastcol is the column where the cursor currently is sitting
1123132718Skan     *		(always one beyond the end of the last mismatch).
1124132718Skan     */
1125132718Skan    do		/* yes, a do...while */
1126169689Skan    {
1127169689Skan	if ((ch = *new++) != *old)
1128169689Skan	{
1129169689Skan	    /* new character is different from old	  */
1130132718Skan	    /* make sure the cursor is on top of this character */
1131132718Skan	    diff = newcol - lastcol;
1132132718Skan	    if (diff > 0)
1133132718Skan	    {
1134132718Skan		/* some motion is required--figure out which is shorter */
1135132718Skan		if (diff < 6 && cursor_on_line)
1136169689Skan		{
1137132718Skan		    /* overwrite old stuff--get it out of the old buffer */
1138169689Skan		    printf("%.*s", diff, &current[lastcol-start]);
1139169689Skan		}
1140169689Skan		else
1141169689Skan		{
1142132718Skan		    /* use cursor addressing */
1143169689Skan		    Move_to(newcol, line);
1144169689Skan		    cursor_on_line = Yes;
1145169689Skan		}
1146169689Skan		/* remember where the cursor is */
1147169689Skan		lastcol = newcol + 1;
1148169689Skan	    }
1149169689Skan	    else
1150169689Skan	    {
1151169689Skan		/* already there, update position */
1152169689Skan		lastcol++;
1153169689Skan	    }
1154169689Skan
1155169689Skan	    /* write what we need to */
1156169689Skan	    if (ch == '\0')
1157169689Skan	    {
1158169689Skan		/* at the end--terminate with a clear-to-end-of-line */
1159169689Skan		(void) clear_eol(strlen(old));
1160169689Skan	    }
1161169689Skan	    else
1162169689Skan	    {
1163169689Skan		/* write the new character */
1164169689Skan		putchar(ch);
1165169689Skan	    }
1166169689Skan	    /* put the new character in the screen buffer */
1167169689Skan	    *old = ch;
1168169689Skan	}
1169169689Skan
1170169689Skan	/* update working column and screen buffer pointer */
1171169689Skan	newcol++;
1172169689Skan	old++;
1173169689Skan
1174169689Skan    } while (ch != '\0');
1175169689Skan
1176132718Skan    /* zero out the rest of the line buffer -- MUST BE DONE! */
1177132718Skan    diff = display_width - newcol;
1178132718Skan    if (diff > 0)
1179132718Skan    {
1180132718Skan	memzero(old, diff);
1181169689Skan    }
1182132718Skan
1183132718Skan    /* remember where the current line is */
1184132718Skan    if (cursor_on_line)
1185169689Skan    {
1186132718Skan	lastline = line;
1187169689Skan    }
1188169689Skan}
1189169689Skan
1190169689Skan/*
1191132718Skan *  printable(str) - make the string pointed to by "str" into one that is
1192132718Skan *	printable (i.e.: all ascii), by converting all non-printable
1193132718Skan *	characters into '?'.  Replacements are done in place and a pointer
1194132718Skan *	to the original buffer is returned.
1195132718Skan */
1196132718Skan
1197132718Skanchar *printable(str)
1198132718Skan
1199132718Skanchar *str;
1200132718Skan
1201132718Skan{
1202169689Skan    register char *ptr;
1203169689Skan    register char ch;
1204169689Skan
1205169689Skan    ptr = str;
1206169689Skan    while ((ch = *ptr) != '\0')
1207132718Skan    {
1208169689Skan	if (!isprint(ch))
1209169689Skan	{
1210132718Skan	    *ptr = '?';
1211132718Skan	}
1212132718Skan	ptr++;
1213132718Skan    }
1214169689Skan    return(str);
1215132718Skan}
1216132718Skan
1217132718Skani_uptime(bt, tod)
1218132718Skan
1219132718Skanstruct timeval* bt;
1220132718Skantime_t *tod;
1221132718Skan
1222132718Skan{
1223132718Skan    time_t uptime;
1224169689Skan    int days, hrs, mins, secs;
1225132718Skan
1226132718Skan    if (bt->tv_sec != -1) {
1227132718Skan	uptime = *tod - bt->tv_sec;
1228132718Skan	uptime += 30;
1229132718Skan	days = uptime / 86400;
1230132718Skan	uptime %= 86400;
1231132718Skan	hrs = uptime / 3600;
1232132718Skan	uptime %= 3600;
1233132718Skan	mins = uptime / 60;
1234132718Skan	secs = uptime % 60;
1235132718Skan
1236169689Skan	/*
1237169689Skan	 *  Display the uptime.
1238169689Skan	 */
1239169689Skan
1240132718Skan	if (smart_terminal)
1241132718Skan	{
1242132718Skan	    Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
1243169689Skan	}
1244132718Skan	else
1245132718Skan	{
1246132718Skan	    fputs(" ", stdout);
124718334Speter	}
124818334Speter	printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
124918334Speter    }
1250132718Skan}
125118334Speter