display.c revision 223342
1151497Sru/*
2151497Sru *  Top users/processes display for Unix
3151497Sru *  Version 3
4151497Sru *
5151497Sru *  This program may be freely redistributed,
6151497Sru *  but this entire comment MUST remain intact.
7151497Sru *
8151497Sru *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
9151497Sru *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
10151497Sru *
11151497Sru * $FreeBSD: head/contrib/top/display.c 223342 2011-06-20 16:48:00Z delphij $
12151497Sru */
13151497Sru
14151497Sru/*
15151497Sru *  This file contains the routines that display information on the screen.
16151497Sru *  Each section of the screen has two routines:  one for initially writing
17151497Sru *  all constant and dynamic text, and one for only updating the text that
18151497Sru *  changes.  The prefix "i_" is used on all the "initial" routines and the
19151497Sru *  prefix "u_" is used for all the "updating" routines.
20151497Sru *
21151497Sru *  ASSUMPTIONS:
22151497Sru *        None of the "i_" routines use any of the termcap capabilities.
23151497Sru *        In this way, those routines can be safely used on terminals that
24151497Sru *        have minimal (or nonexistant) terminal capabilities.
25151497Sru *
26151497Sru *        The routines are called in this order:  *_loadave, i_timeofday,
27151497Sru *        *_procstates, *_cpustates, *_memory, *_message, *_header,
28151497Sru *        *_process, u_endscreen.
29151497Sru */
30151497Sru
31151497Sru#include "os.h"
32151497Sru#include <ctype.h>
33151497Sru#include <time.h>
34151497Sru#include <sys/time.h>
35151497Sru
36151497Sru#include "screen.h"		/* interface to screen package */
37151497Sru#include "layout.h"		/* defines for screen position layout */
38151497Sru#include "display.h"
39151497Sru#include "top.h"
40151497Sru#include "top.local.h"
41151497Sru#include "boolean.h"
42151497Sru#include "machine.h"		/* we should eliminate this!!! */
43151497Sru#include "utils.h"
44151497Sru
45151497Sru#ifdef DEBUG
46151497SruFILE *debug;
47151497Sru#endif
48151497Sru
49151497Sru/* imported from screen.c */
50151497Sruextern int overstrike;
51151497Sru
52151497Srustatic int lmpid = 0;
53151497Srustatic int last_hi = 0;		/* used in u_process and u_endscreen */
54151497Srustatic int lastline = 0;
55151497Srustatic int display_width = MAX_COLS;
56151497Sru
57151497Sru#define lineindex(l) ((l)*display_width)
58151497Sru
59151497Sruchar *printable();
60151497Sru
61151497Sru/* things initialized by display_init and used thruout */
62151497Sru
63151497Sru/* buffer of proc information lines for display updating */
64151497Sruchar *screenbuf = NULL;
65151497Sru
66151497Srustatic char **procstate_names;
67151497Srustatic char **cpustate_names;
68151497Srustatic char **memory_names;
69151497Srustatic char **swap_names;
70151497Sru
71151497Srustatic int num_procstates;
72151497Srustatic int num_cpustates;
73151497Srustatic int num_memory;
74151497Srustatic int num_swap;
75151497Sru
76151497Srustatic int *lprocstates;
77151497Srustatic int *lcpustates;
78151497Srustatic int *lmemory;
79151497Srustatic int *lswap;
80151497Sru
81151497Srustatic int num_cpus;
82151497Srustatic int *cpustate_columns;
83151497Srustatic int cpustate_total_length;
84151497Srustatic int cpustates_column;
85151497Sru
86151497Srustatic enum { OFF, ON, ERASE } header_status = ON;
87151497Sru
88151497Srustatic int string_count();
89151497Srustatic void summary_format();
90151497Srustatic void line_update();
91151497Sru
92151497Sruint  x_lastpid =	10;
93151497Sruint  y_lastpid =	0;
94151497Sruint  x_loadave =	33;
95151497Sruint  x_loadave_nompid =	15;
96151497Sruint  y_loadave =	0;
97151497Sruint  x_procstate =	0;
98151497Sruint  y_procstate =	1;
99151497Sruint  x_brkdn =		15;
100151497Sruint  y_brkdn =		1;
101151497Sruint  x_mem =		5;
102151497Sruint  y_mem =		3;
103151497Sruint  x_swap =		6;
104151497Sruint  y_swap =		4;
105151497Sruint  y_message =	5;
106151497Sruint  x_header =		0;
107151497Sruint  y_header =		6;
108151497Sruint  x_idlecursor =	0;
109151497Sruint  y_idlecursor =	5;
110151497Sruint  y_procs =		7;
111151497Sru
112151497Sruint  y_cpustates =	2;
113151497Sruint  Header_lines =	7;
114151497Sru
115151497Sruint display_resize()
116151497Sru
117151497Sru{
118151497Sru    register int lines;
119151497Sru
120151497Sru    /* first, deallocate any previous buffer that may have been there */
121151497Sru    if (screenbuf != NULL)
122151497Sru    {
123151497Sru	free(screenbuf);
124151497Sru    }
125151497Sru
126151497Sru    /* calculate the current dimensions */
127151497Sru    /* if operating in "dumb" mode, we only need one line */
128151497Sru    lines = smart_terminal ? screen_length - Header_lines : 1;
129151497Sru
130151497Sru    if (lines < 0)
131151497Sru	lines = 0;
132151497Sru    /* we don't want more than MAX_COLS columns, since the machine-dependent
133151497Sru       modules make static allocations based on MAX_COLS and we don't want
134151497Sru       to run off the end of their buffers */
135151497Sru    display_width = screen_width;
136151497Sru    if (display_width >= MAX_COLS)
137151497Sru    {
138151497Sru	display_width = MAX_COLS - 1;
139151497Sru    }
140151497Sru
141151497Sru    /* now, allocate space for the screen buffer */
142151497Sru    screenbuf = (char *)malloc(lines * display_width);
143151497Sru    if (screenbuf == (char *)NULL)
144151497Sru    {
145151497Sru	/* oops! */
146151497Sru	return(-1);
147151497Sru    }
148151497Sru
149151497Sru    /* return number of lines available */
150151497Sru    /* for dumb terminals, pretend like we can show any amount */
151151497Sru    return(smart_terminal ? lines : Largest);
152151497Sru}
153151497Sru
154151497Sruint display_init(statics)
155151497Sru
156151497Srustruct statics *statics;
157151497Sru
158151497Sru{
159151497Sru    register int lines;
160151497Sru    register char **pp;
161151497Sru    register int *ip;
162151497Sru    register int i;
163151497Sru
164151497Sru    /* call resize to do the dirty work */
165151497Sru    lines = display_resize();
166151497Sru    num_cpus = statics->ncpus;
167151497Sru    cpustates_column = 5;	/* CPU: */
168151497Sru    if (num_cpus != 1)
169151497Sru    cpustates_column += 2;	/* CPU 0: */
170151497Sru    for (i = num_cpus; i > 9; i /= 10)
171151497Sru	cpustates_column++;
172151497Sru
173151497Sru    /* only do the rest if we need to */
174151497Sru    if (lines > -1)
175151497Sru    {
176151497Sru	/* save pointers and allocate space for names */
177151497Sru	procstate_names = statics->procstate_names;
178151497Sru	num_procstates = string_count(procstate_names);
179151497Sru	lprocstates = (int *)malloc(num_procstates * sizeof(int));
180151497Sru
181151497Sru	cpustate_names = statics->cpustate_names;
182151497Sru
183151497Sru	swap_names = statics->swap_names;
184151497Sru	num_swap = string_count(swap_names);
185151497Sru	lswap = (int *)malloc(num_swap * sizeof(int));
186151497Sru	num_cpustates = string_count(cpustate_names);
187151497Sru	lcpustates = (int *)malloc(num_cpustates * sizeof(int) * num_cpus);
188151497Sru	cpustate_columns = (int *)malloc(num_cpustates * sizeof(int));
189151497Sru
190151497Sru	memory_names = statics->memory_names;
191151497Sru	num_memory = string_count(memory_names);
192151497Sru	lmemory = (int *)malloc(num_memory * sizeof(int));
193151497Sru
194151497Sru	/* calculate starting columns where needed */
195151497Sru	cpustate_total_length = 0;
196151497Sru	pp = cpustate_names;
197151497Sru	ip = cpustate_columns;
198151497Sru	while (*pp != NULL)
199151497Sru	{
200151497Sru	    *ip++ = cpustate_total_length;
201151497Sru	    if ((i = strlen(*pp++)) > 0)
202151497Sru	    {
203151497Sru		cpustate_total_length += i + 8;
204151497Sru	    }
205151497Sru	}
206151497Sru    }
207151497Sru
208151497Sru    /* return number of lines available */
209151497Sru    return(lines);
210151497Sru}
211151497Sru
212151497Srui_loadave(mpid, avenrun)
213151497Sru
214151497Sruint mpid;
215151497Srudouble *avenrun;
216151497Sru
217151497Sru{
218151497Sru    register int i;
219151497Sru
220151497Sru    /* i_loadave also clears the screen, since it is first */
221151497Sru    clear();
222151497Sru
223151497Sru    /* mpid == -1 implies this system doesn't have an _mpid */
224151497Sru    if (mpid != -1)
225151497Sru    {
226151497Sru	printf("last pid: %5d;  ", mpid);
227151497Sru    }
228151497Sru
229151497Sru    printf("load averages");
230151497Sru
231151497Sru    for (i = 0; i < 3; i++)
232151497Sru    {
233151497Sru	printf("%c %5.2f",
234151497Sru	    i == 0 ? ':' : ',',
235151497Sru	    avenrun[i]);
236151497Sru    }
237151497Sru    lmpid = mpid;
238151497Sru}
239151497Sru
240151497Sruu_loadave(mpid, avenrun)
241151497Sru
242151497Sruint mpid;
243151497Srudouble *avenrun;
244151497Sru
245151497Sru{
246151497Sru    register int i;
247151497Sru
248151497Sru    if (mpid != -1)
249151497Sru    {
250151497Sru	/* change screen only when value has really changed */
251151497Sru	if (mpid != lmpid)
252151497Sru	{
253151497Sru	    Move_to(x_lastpid, y_lastpid);
254151497Sru	    printf("%5d", mpid);
255151497Sru	    lmpid = mpid;
256151497Sru	}
257151497Sru
258151497Sru	/* i remembers x coordinate to move to */
259151497Sru	i = x_loadave;
260151497Sru    }
261151497Sru    else
262151497Sru    {
263151497Sru	i = x_loadave_nompid;
264151497Sru    }
265151497Sru
266151497Sru    /* move into position for load averages */
267151497Sru    Move_to(i, y_loadave);
268151497Sru
269151497Sru    /* display new load averages */
270151497Sru    /* we should optimize this and only display changes */
271151497Sru    for (i = 0; i < 3; i++)
272151497Sru    {
273151497Sru	printf("%s%5.2f",
274151497Sru	    i == 0 ? "" : ", ",
275151497Sru	    avenrun[i]);
276151497Sru    }
277151497Sru}
278151497Sru
279151497Srui_timeofday(tod)
280151497Sru
281151497Srutime_t *tod;
282151497Sru
283151497Sru{
284151497Sru    /*
285151497Sru     *  Display the current time.
286151497Sru     *  "ctime" always returns a string that looks like this:
287151497Sru     *
288151497Sru     *	Sun Sep 16 01:03:52 1973
289151497Sru     *      012345678901234567890123
290151497Sru     *	          1         2
291151497Sru     *
292151497Sru     *  We want indices 11 thru 18 (length 8).
293151497Sru     */
294151497Sru
295151497Sru    if (smart_terminal)
296151497Sru    {
297151497Sru	Move_to(screen_width - 8, 0);
298151497Sru    }
299151497Sru    else
300151497Sru    {
301151497Sru	fputs("    ", stdout);
302151497Sru    }
303151497Sru#ifdef DEBUG
304151497Sru    {
305151497Sru	char *foo;
306151497Sru	foo = ctime(tod);
307151497Sru	fputs(foo, stdout);
308151497Sru    }
309151497Sru#endif
310151497Sru    printf("%-8.8s\n", &(ctime(tod)[11]));
311151497Sru    lastline = 1;
312151497Sru}
313151497Sru
314151497Srustatic int ltotal = 0;
315151497Srustatic char procstates_buffer[MAX_COLS];
316151497Sru
317151497Sru/*
318151497Sru *  *_procstates(total, brkdn, names) - print the process summary line
319151497Sru *
320151497Sru *  Assumptions:  cursor is at the beginning of the line on entry
321151497Sru *		  lastline is valid
322151497Sru */
323151497Sru
324151497Srui_procstates(total, brkdn)
325151497Sru
326151497Sruint total;
327151497Sruint *brkdn;
328151497Sru
329151497Sru{
330151497Sru    register int i;
331151497Sru
332151497Sru    /* write current number of processes and remember the value */
333151497Sru    printf("%d processes:", total);
334151497Sru    ltotal = total;
335151497Sru
336151497Sru    /* put out enough spaces to get to column 15 */
337151497Sru    i = digits(total);
338151497Sru    while (i++ < 4)
339151497Sru    {
340151497Sru	putchar(' ');
341151497Sru    }
342151497Sru
343151497Sru    /* format and print the process state summary */
344151497Sru    summary_format(procstates_buffer, brkdn, procstate_names);
345151497Sru    fputs(procstates_buffer, stdout);
346151497Sru
347151497Sru    /* save the numbers for next time */
348151497Sru    memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
349151497Sru}
350151497Sru
351151497Sruu_procstates(total, brkdn)
352151497Sru
353151497Sruint total;
354151497Sruint *brkdn;
355151497Sru
356151497Sru{
357151497Sru    static char new[MAX_COLS];
358151497Sru    register int i;
359151497Sru
360151497Sru    /* update number of processes only if it has changed */
361151497Sru    if (ltotal != total)
362151497Sru    {
363151497Sru	/* move and overwrite */
364151497Sru#if (x_procstate == 0)
365151497Sru	Move_to(x_procstate, y_procstate);
366151497Sru#else
367151497Sru	/* cursor is already there...no motion needed */
368151497Sru	/* assert(lastline == 1); */
369151497Sru#endif
370151497Sru	printf("%d", total);
371151497Sru
372151497Sru	/* if number of digits differs, rewrite the label */
373151497Sru	if (digits(total) != digits(ltotal))
374151497Sru	{
375151497Sru	    fputs(" processes:", stdout);
376151497Sru	    /* put out enough spaces to get to column 15 */
377151497Sru	    i = digits(total);
378151497Sru	    while (i++ < 4)
379151497Sru	    {
380151497Sru		putchar(' ');
381151497Sru	    }
382151497Sru	    /* cursor may end up right where we want it!!! */
383151497Sru	}
384151497Sru
385151497Sru	/* save new total */
386151497Sru	ltotal = total;
387151497Sru    }
388151497Sru
389151497Sru    /* see if any of the state numbers has changed */
390151497Sru    if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
391151497Sru    {
392151497Sru	/* format and update the line */
393151497Sru	summary_format(new, brkdn, procstate_names);
394151497Sru	line_update(procstates_buffer, new, x_brkdn, y_brkdn);
395151497Sru	memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
396151497Sru    }
397151497Sru}
398151497Sru
399151497Sru#ifdef no_more
400151497Sru/*
401151497Sru *  *_cpustates(states, names) - print the cpu state percentages
402151497Sru *
403151497Sru *  Assumptions:  cursor is on the PREVIOUS line
404151497Sru */
405151497Sru
406151497Sru/* cpustates_tag() calculates the correct tag to use to label the line */
407151497Sru
408151497Sruchar *cpustates_tag()
409151497Sru
410151497Sru{
411151497Sru    register char *use;
412151497Sru
413151497Sru    static char *short_tag = "CPU: ";
414151497Sru    static char *long_tag = "CPU states: ";
415151497Sru
416151497Sru    /* if length + strlen(long_tag) >= screen_width, then we have to
417151497Sru       use the shorter tag (we subtract 2 to account for ": ") */
418151497Sru    if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
419151497Sru    {
420151497Sru	use = short_tag;
421151497Sru    }
422151497Sru    else
423151497Sru    {
424151497Sru	use = long_tag;
425151497Sru    }
426151497Sru
427151497Sru    /* set cpustates_column accordingly then return result */
428151497Sru    cpustates_column = strlen(use);
429151497Sru    return(use);
430151497Sru}
431151497Sru#endif
432151497Sru
433151497Srui_cpustates(states)
434151497Sru
435151497Sruregister int *states;
436151497Sru
437151497Sru{
438151497Sru    register int i = 0;
439151497Sru    register int value;
440151497Sru    register char **names;
441151497Sru    register char *thisname;
442151497Sru    int cpu;
443151497Sru
444151497Srufor (cpu = 0; cpu < num_cpus; cpu++) {
445151497Sru    names = cpustate_names;
446151497Sru
447151497Sru    /* print tag and bump lastline */
448151497Sru    if (num_cpus == 1)
449151497Sru	printf("\nCPU: ");
450151497Sru    else {
451151497Sru	value = printf("\nCPU %d: ", cpu);
452151497Sru	while (value++ <= cpustates_column)
453151497Sru		printf(" ");
454151497Sru    }
455151497Sru    lastline++;
456151497Sru
457151497Sru    /* now walk thru the names and print the line */
458151497Sru    while ((thisname = *names++) != NULL)
459151497Sru    {
460151497Sru	if (*thisname != '\0')
461151497Sru	{
462151497Sru	    /* retrieve the value and remember it */
463151497Sru	    value = *states++;
464151497Sru
465151497Sru	    /* if percentage is >= 1000, print it as 100% */
466151497Sru	    printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
467151497Sru		   (i++ % num_cpustates) == 0 ? "" : ", ",
468151497Sru		   ((float)value)/10.,
469151497Sru		   thisname);
470151497Sru	}
471151497Sru    }
472151497Sru}
473151497Sru
474151497Sru    /* copy over values into "last" array */
475151497Sru    memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
476151497Sru}
477151497Sru
478151497Sruu_cpustates(states)
479151497Sru
480151497Sruregister int *states;
481151497Sru
482151497Sru{
483151497Sru    register int value;
484151497Sru    register char **names;
485151497Sru    register char *thisname;
486151497Sru    register int *lp;
487151497Sru    register int *colp;
488151497Sru    int cpu;
489151497Sru
490151497Srufor (cpu = 0; cpu < num_cpus; cpu++) {
491151497Sru    names = cpustate_names;
492151497Sru
493151497Sru    Move_to(cpustates_column, y_cpustates + cpu);
494151497Sru    lastline = y_cpustates + cpu;
495151497Sru    lp = lcpustates + (cpu * num_cpustates);
496151497Sru    colp = cpustate_columns;
497151497Sru
498151497Sru    /* we could be much more optimal about this */
499151497Sru    while ((thisname = *names++) != NULL)
500151497Sru    {
501151497Sru	if (*thisname != '\0')
502151497Sru	{
503151497Sru	    /* did the value change since last time? */
504151497Sru	    if (*lp != *states)
505151497Sru	    {
506151497Sru		/* yes, move and change */
507151497Sru		Move_to(cpustates_column + *colp, y_cpustates + cpu);
508151497Sru		lastline = y_cpustates + cpu;
509151497Sru
510151497Sru		/* retrieve value and remember it */
511151497Sru		value = *states;
512151497Sru
513151497Sru		/* if percentage is >= 1000, print it as 100% */
514151497Sru		printf((value >= 1000 ? "%4.0f" : "%4.1f"),
515151497Sru		       ((double)value)/10.);
516151497Sru
517151497Sru		/* remember it for next time */
518151497Sru		*lp = value;
519151497Sru	    }
520151497Sru	}
521151497Sru
522151497Sru	/* increment and move on */
523151497Sru	lp++;
524151497Sru	states++;
525151497Sru	colp++;
526151497Sru    }
527151497Sru}
528151497Sru}
529151497Sru
530151497Sruz_cpustates()
531151497Sru
532151497Sru{
533151497Sru    register int i = 0;
534151497Sru    register char **names;
535151497Sru    register char *thisname;
536151497Sru    register int *lp;
537151497Sru    int cpu, value;
538151497Sru
539151497Srufor (cpu = 0; cpu < num_cpus; cpu++) {
540151497Sru    names = cpustate_names;
541151497Sru
542151497Sru    /* show tag and bump lastline */
543151497Sru    if (num_cpus == 1)
544151497Sru	printf("\nCPU: ");
545151497Sru    else {
546151497Sru	value = printf("\nCPU %d: ", cpu);
547151497Sru	while (value++ <= cpustates_column)
548151497Sru		printf(" ");
549151497Sru    }
550151497Sru    lastline++;
551151497Sru
552151497Sru    while ((thisname = *names++) != NULL)
553151497Sru    {
554151497Sru	if (*thisname != '\0')
555151497Sru	{
556151497Sru	    printf("%s    %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname);
557151497Sru	}
558151497Sru    }
559151497Sru}
560151497Sru
561151497Sru    /* fill the "last" array with all -1s, to insure correct updating */
562151497Sru    lp = lcpustates;
563151497Sru    i = num_cpustates * num_cpus;
564151497Sru    while (--i >= 0)
565151497Sru    {
566151497Sru	*lp++ = -1;
567151497Sru    }
568151497Sru}
569151497Sru
570151497Sru/*
571151497Sru *  *_memory(stats) - print "Memory: " followed by the memory summary string
572151497Sru *
573151497Sru *  Assumptions:  cursor is on "lastline"
574151497Sru *                for i_memory ONLY: cursor is on the previous line
575151497Sru */
576151497Sru
577151497Sruchar memory_buffer[MAX_COLS];
578151497Sru
579151497Srui_memory(stats)
580151497Sru
581151497Sruint *stats;
582151497Sru
583151497Sru{
584151497Sru    fputs("\nMem: ", stdout);
585151497Sru    lastline++;
586151497Sru
587151497Sru    /* format and print the memory summary */
588151497Sru    summary_format(memory_buffer, stats, memory_names);
589151497Sru    fputs(memory_buffer, stdout);
590151497Sru}
591151497Sru
592151497Sruu_memory(stats)
593151497Sru
594151497Sruint *stats;
595151497Sru
596151497Sru{
597151497Sru    static char new[MAX_COLS];
598151497Sru
599151497Sru    /* format the new line */
600151497Sru    summary_format(new, stats, memory_names);
601151497Sru    line_update(memory_buffer, new, x_mem, y_mem);
602151497Sru}
603151497Sru
604151497Sru/*
605151497Sru *  *_swap(stats) - print "Swap: " followed by the swap summary string
606151497Sru *
607151497Sru *  Assumptions:  cursor is on "lastline"
608151497Sru *                for i_swap ONLY: cursor is on the previous line
609151497Sru */
610151497Sru
611151497Sruchar swap_buffer[MAX_COLS];
612151497Sru
613151497Srui_swap(stats)
614151497Sru
615151497Sruint *stats;
616151497Sru
617151497Sru{
618151497Sru    fputs("\nSwap: ", stdout);
619151497Sru    lastline++;
620151497Sru
621151497Sru    /* format and print the swap summary */
622151497Sru    summary_format(swap_buffer, stats, swap_names);
623151497Sru    fputs(swap_buffer, stdout);
624151497Sru}
625151497Sru
626151497Sruu_swap(stats)
627151497Sru
628151497Sruint *stats;
629151497Sru
630151497Sru{
631151497Sru    static char new[MAX_COLS];
632151497Sru
633151497Sru    /* format the new line */
634151497Sru    summary_format(new, stats, swap_names);
635151497Sru    line_update(swap_buffer, new, x_swap, y_swap);
636151497Sru}
637151497Sru
638151497Sru/*
639151497Sru *  *_message() - print the next pending message line, or erase the one
640151497Sru *                that is there.
641151497Sru *
642151497Sru *  Note that u_message is (currently) the same as i_message.
643151497Sru *
644151497Sru *  Assumptions:  lastline is consistent
645151497Sru */
646151497Sru
647151497Sru/*
648151497Sru *  i_message is funny because it gets its message asynchronously (with
649151497Sru *	respect to screen updates).
650151497Sru */
651151497Sru
652151497Srustatic char next_msg[MAX_COLS + 5];
653151497Srustatic int msglen = 0;
654151497Sru/* Invariant: msglen is always the length of the message currently displayed
655151497Sru   on the screen (even when next_msg doesn't contain that message). */
656151497Sru
657151497Srui_message()
658151497Sru
659151497Sru{
660151497Sru    while (lastline < y_message)
661151497Sru    {
662151497Sru	fputc('\n', stdout);
663151497Sru	lastline++;
664151497Sru    }
665151497Sru    if (next_msg[0] != '\0')
666151497Sru    {
667151497Sru	standout(next_msg);
668151497Sru	msglen = strlen(next_msg);
669151497Sru	next_msg[0] = '\0';
670151497Sru    }
671151497Sru    else if (msglen > 0)
672151497Sru    {
673151497Sru	(void) clear_eol(msglen);
674151497Sru	msglen = 0;
675151497Sru    }
676151497Sru}
677151497Sru
678151497Sruu_message()
679151497Sru
680151497Sru{
681151497Sru    i_message();
682151497Sru}
683151497Sru
684151497Srustatic int header_length;
685151497Sru
686151497Sru/*
687151497Sru * Trim a header string to the current display width and return a newly
688151497Sru * allocated area with the trimmed header.
689151497Sru */
690151497Sru
691151497Sruchar *
692151497Srutrim_header(text)
693151497Sru
694151497Sruchar *text;
695151497Sru
696151497Sru{
697151497Sru	char *s;
698151497Sru	int width;
699151497Sru
700151497Sru	s = NULL;
701151497Sru	width = display_width;
702151497Sru	header_length = strlen(text);
703151497Sru	if (header_length >= width) {
704151497Sru		s = malloc((width + 1) * sizeof(char));
705151497Sru		if (s == NULL)
706151497Sru			return (NULL);
707151497Sru		strncpy(s, text, width);
708151497Sru		s[width] = '\0';
709151497Sru	}
710151497Sru	return (s);
711151497Sru}
712151497Sru
713151497Sru/*
714151497Sru *  *_header(text) - print the header for the process area
715151497Sru *
716151497Sru *  Assumptions:  cursor is on the previous line and lastline is consistent
717151497Sru */
718151497Sru
719151497Srui_header(text)
720151497Sru
721151497Sruchar *text;
722151497Sru
723151497Sru{
724151497Sru    char *s;
725151497Sru
726151497Sru    s = trim_header(text);
727151497Sru    if (s != NULL)
728151497Sru	text = s;
729151497Sru
730151497Sru    if (header_status == ON)
731151497Sru    {
732151497Sru	putchar('\n');
733151497Sru	fputs(text, stdout);
734151497Sru	lastline++;
735151497Sru    }
736151497Sru    else if (header_status == ERASE)
737151497Sru    {
738151497Sru	header_status = OFF;
739151497Sru    }
740151497Sru    free(s);
741151497Sru}
742151497Sru
743151497Sru/*ARGSUSED*/
744151497Sruu_header(text)
745151497Sru
746151497Sruchar *text;		/* ignored */
747151497Sru
748151497Sru{
749151497Sru
750151497Sru    if (header_status == ERASE)
751151497Sru    {
752151497Sru	putchar('\n');
753151497Sru	lastline++;
754151497Sru	clear_eol(header_length);
755151497Sru	header_status = OFF;
756151497Sru    }
757151497Sru}
758151497Sru
759151497Sru/*
760151497Sru *  *_process(line, thisline) - print one process line
761151497Sru *
762151497Sru *  Assumptions:  lastline is consistent
763151497Sru */
764151497Sru
765151497Srui_process(line, thisline)
766151497Sru
767151497Sruint line;
768151497Sruchar *thisline;
769151497Sru
770151497Sru{
771151497Sru    register char *p;
772151497Sru    register char *base;
773151497Sru
774151497Sru    /* make sure we are on the correct line */
775151497Sru    while (lastline < y_procs + line)
776151497Sru    {
777151497Sru	putchar('\n');
778151497Sru	lastline++;
779151497Sru    }
780151497Sru
781151497Sru    /* truncate the line to conform to our current screen width */
782151497Sru    thisline[display_width] = '\0';
783151497Sru
784151497Sru    /* write the line out */
785151497Sru    fputs(thisline, stdout);
786151497Sru
787151497Sru    /* copy it in to our buffer */
788151497Sru    base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
789151497Sru    p = strecpy(base, thisline);
790151497Sru
791151497Sru    /* zero fill the rest of it */
792151497Sru    memzero(p, display_width - (p - base));
793151497Sru}
794151497Sru
795151497Sruu_process(line, newline)
796151497Sru
797151497Sruint line;
798151497Sruchar *newline;
799151497Sru
800151497Sru{
801151497Sru    register char *optr;
802151497Sru    register int screen_line = line + Header_lines;
803151497Sru    register char *bufferline;
804151497Sru
805151497Sru    /* remember a pointer to the current line in the screen buffer */
806151497Sru    bufferline = &screenbuf[lineindex(line)];
807151497Sru
808151497Sru    /* truncate the line to conform to our current screen width */
809151497Sru    newline[display_width] = '\0';
810151497Sru
811151497Sru    /* is line higher than we went on the last display? */
812151497Sru    if (line >= last_hi)
813151497Sru    {
814151497Sru	/* yes, just ignore screenbuf and write it out directly */
815151497Sru	/* get positioned on the correct line */
816151497Sru	if (screen_line - lastline == 1)
817151497Sru	{
818151497Sru	    putchar('\n');
819151497Sru	    lastline++;
820151497Sru	}
821151497Sru	else
822151497Sru	{
823151497Sru	    Move_to(0, screen_line);
824151497Sru	    lastline = screen_line;
825151497Sru	}
826151497Sru
827151497Sru	/* now write the line */
828151497Sru	fputs(newline, stdout);
829151497Sru
830151497Sru	/* copy it in to the buffer */
831151497Sru	optr = strecpy(bufferline, newline);
832151497Sru
833151497Sru	/* zero fill the rest of it */
834151497Sru	memzero(optr, display_width - (optr - bufferline));
835151497Sru    }
836151497Sru    else
837151497Sru    {
838151497Sru	line_update(bufferline, newline, 0, line + Header_lines);
839151497Sru    }
840151497Sru}
841151497Sru
842151497Sruu_endscreen(hi)
843151497Sru
844151497Sruregister int hi;
845151497Sru
846151497Sru{
847151497Sru    register int screen_line = hi + Header_lines;
848151497Sru    register int i;
849151497Sru
850151497Sru    if (smart_terminal)
851151497Sru    {
852151497Sru	if (hi < last_hi)
853151497Sru	{
854151497Sru	    /* need to blank the remainder of the screen */
855151497Sru	    /* but only if there is any screen left below this line */
856151497Sru	    if (lastline + 1 < screen_length)
857151497Sru	    {
858151497Sru		/* efficiently move to the end of currently displayed info */
859151497Sru		if (screen_line - lastline < 5)
860151497Sru		{
861151497Sru		    while (lastline < screen_line)
862151497Sru		    {
863151497Sru			putchar('\n');
864151497Sru			lastline++;
865151497Sru		    }
866151497Sru		}
867151497Sru		else
868151497Sru		{
869151497Sru		    Move_to(0, screen_line);
870151497Sru		    lastline = screen_line;
871151497Sru		}
872151497Sru
873151497Sru		if (clear_to_end)
874151497Sru		{
875151497Sru		    /* we can do this the easy way */
876151497Sru		    putcap(clear_to_end);
877151497Sru		}
878151497Sru		else
879151497Sru		{
880151497Sru		    /* use clear_eol on each line */
881151497Sru		    i = hi;
882151497Sru		    while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
883151497Sru		    {
884151497Sru			putchar('\n');
885151497Sru		    }
886151497Sru		}
887151497Sru	    }
888151497Sru	}
889151497Sru	last_hi = hi;
890151497Sru
891151497Sru	/* move the cursor to a pleasant place */
892151497Sru	Move_to(x_idlecursor, y_idlecursor);
893151497Sru	lastline = y_idlecursor;
894151497Sru    }
895151497Sru    else
896151497Sru    {
897151497Sru	/* separate this display from the next with some vertical room */
898151497Sru	fputs("\n\n", stdout);
899151497Sru    }
900151497Sru}
901151497Sru
902151497Srudisplay_header(t)
903151497Sru
904151497Sruint t;
905151497Sru
906151497Sru{
907151497Sru    if (t)
908151497Sru    {
909151497Sru	header_status = ON;
910151497Sru    }
911151497Sru    else if (header_status == ON)
912151497Sru    {
913151497Sru	header_status = ERASE;
914151497Sru    }
915151497Sru}
916151497Sru
917151497Sru/*VARARGS2*/
918151497Srunew_message(type, msgfmt, a1, a2, a3)
919151497Sru
920151497Sruint type;
921151497Sruchar *msgfmt;
922151497Srucaddr_t a1, a2, a3;
923151497Sru
924151497Sru{
925151497Sru    register int i;
926151497Sru
927151497Sru    /* first, format the message */
928151497Sru    (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3);
929151497Sru
930151497Sru    if (msglen > 0)
931151497Sru    {
932151497Sru	/* message there already -- can we clear it? */
933151497Sru	if (!overstrike)
934151497Sru	{
935151497Sru	    /* yes -- write it and clear to end */
936151497Sru	    i = strlen(next_msg);
937151497Sru	    if ((type & MT_delayed) == 0)
938151497Sru	    {
939151497Sru		type & MT_standout ? standout(next_msg) :
940151497Sru		                     fputs(next_msg, stdout);
941151497Sru		(void) clear_eol(msglen - i);
942151497Sru		msglen = i;
943151497Sru		next_msg[0] = '\0';
944151497Sru	    }
945151497Sru	}
946151497Sru    }
947151497Sru    else
948151497Sru    {
949151497Sru	if ((type & MT_delayed) == 0)
950151497Sru	{
951151497Sru	    type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
952151497Sru	    msglen = strlen(next_msg);
953151497Sru	    next_msg[0] = '\0';
954151497Sru	}
955151497Sru    }
956151497Sru}
957151497Sru
958151497Sruclear_message()
959151497Sru
960151497Sru{
961151497Sru    if (clear_eol(msglen) == 1)
962151497Sru    {
963151497Sru	putchar('\r');
964151497Sru    }
965151497Sru}
966151497Sru
967151497Srureadline(buffer, size, numeric)
968151497Sru
969151497Sruchar *buffer;
970151497Sruint  size;
971151497Sruint  numeric;
972151497Sru
973151497Sru{
974151497Sru    register char *ptr = buffer;
975151497Sru    register char ch;
976151497Sru    register char cnt = 0;
977151497Sru    register char maxcnt = 0;
978151497Sru
979151497Sru    /* allow room for null terminator */
980151497Sru    size -= 1;
981151497Sru
982151497Sru    /* read loop */
983151497Sru    while ((fflush(stdout), read(0, ptr, 1) > 0))
984151497Sru    {
985151497Sru	/* newline means we are done */
986151497Sru	if ((ch = *ptr) == '\n' || ch == '\r')
987151497Sru	{
988151497Sru	    break;
989151497Sru	}
990151497Sru
991151497Sru	/* handle special editing characters */
992151497Sru	if (ch == ch_kill)
993151497Sru	{
994151497Sru	    /* kill line -- account for overstriking */
995151497Sru	    if (overstrike)
996151497Sru	    {
997151497Sru		msglen += maxcnt;
998151497Sru	    }
999151497Sru
1000151497Sru	    /* return null string */
1001151497Sru	    *buffer = '\0';
1002151497Sru	    putchar('\r');
1003151497Sru	    return(-1);
1004151497Sru	}
1005151497Sru	else if (ch == ch_erase)
1006151497Sru	{
1007151497Sru	    /* erase previous character */
1008151497Sru	    if (cnt <= 0)
1009151497Sru	    {
1010151497Sru		/* none to erase! */
1011151497Sru		putchar('\7');
1012151497Sru	    }
1013151497Sru	    else
1014151497Sru	    {
1015151497Sru		fputs("\b \b", stdout);
1016151497Sru		ptr--;
1017151497Sru		cnt--;
1018151497Sru	    }
1019151497Sru	}
1020151497Sru	/* check for character validity and buffer overflow */
1021151497Sru	else if (cnt == size || (numeric && !isdigit(ch)) ||
1022151497Sru		!isprint(ch))
1023151497Sru	{
1024151497Sru	    /* not legal */
1025151497Sru	    putchar('\7');
1026151497Sru	}
1027151497Sru	else
1028151497Sru	{
1029151497Sru	    /* echo it and store it in the buffer */
1030151497Sru	    putchar(ch);
1031151497Sru	    ptr++;
1032151497Sru	    cnt++;
1033151497Sru	    if (cnt > maxcnt)
1034151497Sru	    {
1035151497Sru		maxcnt = cnt;
1036151497Sru	    }
1037151497Sru	}
1038151497Sru    }
1039151497Sru
1040151497Sru    /* all done -- null terminate the string */
1041151497Sru    *ptr = '\0';
1042151497Sru
1043151497Sru    /* account for the extra characters in the message area */
1044151497Sru    /* (if terminal overstrikes, remember the furthest they went) */
1045151497Sru    msglen += overstrike ? maxcnt : cnt;
1046151497Sru
1047151497Sru    /* return either inputted number or string length */
1048151497Sru    putchar('\r');
1049151497Sru    return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
1050151497Sru}
1051151497Sru
1052151497Sru/* internal support routines */
1053151497Sru
1054151497Srustatic int string_count(pp)
1055151497Sru
1056151497Sruregister char **pp;
1057151497Sru
1058151497Sru{
1059151497Sru    register int cnt;
1060151497Sru
1061151497Sru    cnt = 0;
1062151497Sru    while (*pp++ != NULL)
1063151497Sru    {
1064151497Sru	cnt++;
1065151497Sru    }
1066151497Sru    return(cnt);
1067151497Sru}
1068151497Sru
1069151497Srustatic void summary_format(str, numbers, names)
1070151497Sru
1071151497Sruchar *str;
1072151497Sruint *numbers;
1073151497Sruregister char **names;
1074151497Sru
1075151497Sru{
1076151497Sru    register char *p;
1077151497Sru    register int num;
1078151497Sru    register char *thisname;
1079151497Sru    register int useM = No;
1080151497Sru
1081151497Sru    /* format each number followed by its string */
1082151497Sru    p = str;
1083151497Sru    while ((thisname = *names++) != NULL)
1084151497Sru    {
1085151497Sru	/* get the number to format */
1086151497Sru	num = *numbers++;
1087151497Sru
1088151497Sru	/* display only non-zero numbers */
1089151497Sru	if (num > 0)
1090151497Sru	{
1091151497Sru	    /* is this number in kilobytes? */
1092151497Sru	    if (thisname[0] == 'K')
1093151497Sru	    {
1094151497Sru		/* yes: format it as a memory value */
1095151497Sru		p = strecpy(p, format_k(num));
1096151497Sru
1097151497Sru		/* skip over the K, since it was included by format_k */
1098151497Sru		p = strecpy(p, thisname+1);
1099151497Sru	    }
1100151497Sru	    else
1101151497Sru	    {
1102151497Sru		p = strecpy(p, itoa(num));
1103151497Sru		p = strecpy(p, thisname);
1104151497Sru	    }
1105151497Sru	}
1106151497Sru
1107151497Sru	/* ignore negative numbers, but display corresponding string */
1108151497Sru	else if (num < 0)
1109151497Sru	{
1110151497Sru	    p = strecpy(p, thisname);
1111151497Sru	}
1112151497Sru    }
1113151497Sru
1114151497Sru    /* if the last two characters in the string are ", ", delete them */
1115151497Sru    p -= 2;
1116151497Sru    if (p >= str && p[0] == ',' && p[1] == ' ')
1117151497Sru    {
1118151497Sru	*p = '\0';
1119151497Sru    }
1120151497Sru}
1121151497Sru
1122151497Srustatic void line_update(old, new, start, line)
1123151497Sru
1124151497Sruregister char *old;
1125151497Sruregister char *new;
1126151497Sruint start;
1127151497Sruint line;
1128151497Sru
1129151497Sru{
1130151497Sru    register int ch;
1131151497Sru    register int diff;
1132151497Sru    register int newcol = start + 1;
1133151497Sru    register int lastcol = start;
1134151497Sru    char cursor_on_line = No;
1135151497Sru    char *current;
1136151497Sru
1137151497Sru    /* compare the two strings and only rewrite what has changed */
1138151497Sru    current = old;
1139151497Sru#ifdef DEBUG
1140151497Sru    fprintf(debug, "line_update, starting at %d\n", start);
1141151497Sru    fputs(old, debug);
1142151497Sru    fputc('\n', debug);
1143151497Sru    fputs(new, debug);
1144151497Sru    fputs("\n-\n", debug);
1145151497Sru#endif
1146151497Sru
1147151497Sru    /* start things off on the right foot		    */
1148151497Sru    /* this is to make sure the invariants get set up right */
1149151497Sru    if ((ch = *new++) != *old)
1150151497Sru    {
1151151497Sru	if (line - lastline == 1 && start == 0)
1152151497Sru	{
1153151497Sru	    putchar('\n');
1154151497Sru	}
1155151497Sru	else
1156151497Sru	{
1157151497Sru	    Move_to(start, line);
1158151497Sru	}
1159151497Sru	cursor_on_line = Yes;
1160151497Sru	putchar(ch);
1161151497Sru	*old = ch;
1162151497Sru	lastcol = 1;
1163151497Sru    }
1164151497Sru    old++;
1165151497Sru
1166151497Sru    /*
1167151497Sru     *  main loop -- check each character.  If the old and new aren't the
1168151497Sru     *	same, then update the display.  When the distance from the
1169151497Sru     *	current cursor position to the new change is small enough,
1170151497Sru     *	the characters that belong there are written to move the
1171151497Sru     *	cursor over.
1172151497Sru     *
1173151497Sru     *	Invariants:
1174151497Sru     *	    lastcol is the column where the cursor currently is sitting
1175151497Sru     *		(always one beyond the end of the last mismatch).
1176151497Sru     */
1177151497Sru    do		/* yes, a do...while */
1178151497Sru    {
1179151497Sru	if ((ch = *new++) != *old)
1180151497Sru	{
1181151497Sru	    /* new character is different from old	  */
1182151497Sru	    /* make sure the cursor is on top of this character */
1183151497Sru	    diff = newcol - lastcol;
1184151497Sru	    if (diff > 0)
1185151497Sru	    {
1186151497Sru		/* some motion is required--figure out which is shorter */
1187151497Sru		if (diff < 6 && cursor_on_line)
1188151497Sru		{
1189151497Sru		    /* overwrite old stuff--get it out of the old buffer */
1190151497Sru		    printf("%.*s", diff, &current[lastcol-start]);
1191151497Sru		}
1192151497Sru		else
1193151497Sru		{
1194151497Sru		    /* use cursor addressing */
1195151497Sru		    Move_to(newcol, line);
1196151497Sru		    cursor_on_line = Yes;
1197151497Sru		}
1198151497Sru		/* remember where the cursor is */
1199151497Sru		lastcol = newcol + 1;
1200151497Sru	    }
1201151497Sru	    else
1202151497Sru	    {
1203151497Sru		/* already there, update position */
1204151497Sru		lastcol++;
1205151497Sru	    }
1206151497Sru
1207151497Sru	    /* write what we need to */
1208151497Sru	    if (ch == '\0')
1209151497Sru	    {
1210151497Sru		/* at the end--terminate with a clear-to-end-of-line */
1211151497Sru		(void) clear_eol(strlen(old));
1212151497Sru	    }
1213151497Sru	    else
1214151497Sru	    {
1215151497Sru		/* write the new character */
1216151497Sru		putchar(ch);
1217151497Sru	    }
1218151497Sru	    /* put the new character in the screen buffer */
1219151497Sru	    *old = ch;
1220151497Sru	}
1221151497Sru
1222151497Sru	/* update working column and screen buffer pointer */
1223151497Sru	newcol++;
1224151497Sru	old++;
1225151497Sru
1226151497Sru    } while (ch != '\0');
1227151497Sru
1228151497Sru    /* zero out the rest of the line buffer -- MUST BE DONE! */
1229151497Sru    diff = display_width - newcol;
1230151497Sru    if (diff > 0)
1231151497Sru    {
1232151497Sru	memzero(old, diff);
1233151497Sru    }
1234151497Sru
1235151497Sru    /* remember where the current line is */
1236151497Sru    if (cursor_on_line)
1237151497Sru    {
1238151497Sru	lastline = line;
1239151497Sru    }
1240151497Sru}
1241151497Sru
1242151497Sru/*
1243151497Sru *  printable(str) - make the string pointed to by "str" into one that is
1244151497Sru *	printable (i.e.: all ascii), by converting all non-printable
1245151497Sru *	characters into '?'.  Replacements are done in place and a pointer
1246151497Sru *	to the original buffer is returned.
1247151497Sru */
1248151497Sru
1249151497Sruchar *printable(str)
1250151497Sru
1251151497Sruchar *str;
1252151497Sru
1253151497Sru{
1254151497Sru    register char *ptr;
1255151497Sru    register char ch;
1256151497Sru
1257151497Sru    ptr = str;
1258151497Sru    while ((ch = *ptr) != '\0')
1259151497Sru    {
1260151497Sru	if (!isprint(ch))
1261151497Sru	{
1262151497Sru	    *ptr = '?';
1263151497Sru	}
1264151497Sru	ptr++;
1265151497Sru    }
1266151497Sru    return(str);
1267151497Sru}
1268151497Sru
1269151497Srui_uptime(bt, tod)
1270151497Sru
1271151497Srustruct timeval* bt;
1272151497Srutime_t *tod;
1273151497Sru
1274151497Sru{
1275151497Sru    time_t uptime;
1276151497Sru    int days, hrs, mins, secs;
1277151497Sru
1278151497Sru    if (bt->tv_sec != -1) {
1279151497Sru	uptime = *tod - bt->tv_sec;
1280151497Sru	days = uptime / 86400;
1281151497Sru	uptime %= 86400;
1282151497Sru	hrs = uptime / 3600;
1283151497Sru	uptime %= 3600;
1284151497Sru	mins = uptime / 60;
1285151497Sru	secs = uptime % 60;
1286151497Sru
1287151497Sru	/*
1288151497Sru	 *  Display the uptime.
1289151497Sru	 */
1290151497Sru
1291151497Sru	if (smart_terminal)
1292151497Sru	{
1293151497Sru	    Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
1294151497Sru	}
1295151497Sru	else
1296151497Sru	{
1297151497Sru	    fputs(" ", stdout);
1298151497Sru	}
1299151497Sru	printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
1300151497Sru    }
1301151497Sru}
1302151497Sru