display.c revision 101692
150477Speter/*
235388Smjacob *  Top users/processes display for Unix
335388Smjacob *  Version 3
435388Smjacob *
573319Smjacob *  This program may be freely redistributed,
635388Smjacob *  but this entire comment MUST remain intact.
735388Smjacob *
835388Smjacob *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
935388Smjacob *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
1035388Smjacob *
1135388Smjacob * $FreeBSD: head/contrib/top/display.c 101692 2002-08-11 18:37:25Z dwmalone $
1235388Smjacob */
1366189Smjacob
1466189Smjacob/*
1535388Smjacob *  This file contains the routines that display information on the screen.
1635388Smjacob *  Each section of the screen has two routines:  one for initially writing
1735388Smjacob *  all constant and dynamic text, and one for only updating the text that
1835388Smjacob *  changes.  The prefix "i_" is used on all the "initial" routines and the
1935388Smjacob *  prefix "u_" is used for all the "updating" routines.
2035388Smjacob *
2135388Smjacob *  ASSUMPTIONS:
2235388Smjacob *        None of the "i_" routines use any of the termcap capabilities.
2335388Smjacob *        In this way, those routines can be safely used on terminals that
2435388Smjacob *        have minimal (or nonexistant) terminal capabilities.
2535388Smjacob *
2635388Smjacob *        The routines are called in this order:  *_loadave, i_timeofday,
2735388Smjacob *        *_procstates, *_cpustates, *_memory, *_message, *_header,
2835388Smjacob *        *_process, u_endscreen.
2977365Smjacob */
3077365Smjacob
3164176Smjacob#include "os.h"
3277365Smjacob#include <ctype.h>
33100679Smjacob#include <time.h>
3477365Smjacob#include <sys/time.h>
3577365Smjacob
3635388Smjacob#include "screen.h"		/* interface to screen package */
3775200Smjacob#include "layout.h"		/* defines for screen position layout */
38100679Smjacob#include "display.h"
3999756Smjacob#include "top.h"
4099756Smjacob#include "top.local.h"
4199756Smjacob#include "boolean.h"
4277365Smjacob#include "machine.h"		/* we should eliminate this!!! */
4362498Smjacob#include "utils.h"
4449915Smjacob
4549915Smjacob#ifdef DEBUG
4662173SmjacobFILE *debug;
4777365Smjacob#endif
4849915Smjacob
4939235Sgibbs/* imported from screen.c */
5060220Smjacobextern int overstrike;
5177365Smjacob
5277365Smjacobstatic int lmpid = 0;
5377365Smjacobstatic int last_hi = 0;		/* used in u_process and u_endscreen */
5477365Smjacobstatic int lastline = 0;
5577365Smjacobstatic int display_width = MAX_COLS;
5677365Smjacob
5777365Smjacob#define lineindex(l) ((l)*display_width)
5877365Smjacob
5977365Smjacobchar *printable();
6077365Smjacob
6177365Smjacob/* things initialized by display_init and used thruout */
6277365Smjacob
6377365Smjacob/* buffer of proc information lines for display updating */
6477365Smjacobchar *screenbuf = NULL;
6577365Smjacob
6677365Smjacobstatic char **procstate_names;
6777365Smjacobstatic char **cpustate_names;
6855371Smjacobstatic char **memory_names;
6949915Smjacobstatic char **swap_names;
7039235Sgibbs
7142131Smjacobstatic int num_procstates;
7239235Sgibbsstatic int num_cpustates;
7346972Smjacobstatic int num_memory;
7439235Sgibbsstatic int num_swap;
7539235Sgibbs
7646972Smjacobstatic int *lprocstates;
7746972Smjacobstatic int *lcpustates;
7839235Sgibbsstatic int *lmemory;
7939235Sgibbsstatic int *lswap;
8046972Smjacob
8139235Sgibbsstatic int *cpustate_columns;
8246972Smjacobstatic int cpustate_total_length;
8346972Smjacob
8446972Smjacobstatic enum { OFF, ON, ERASE } header_status = ON;
8546972Smjacob
8646972Smjacobstatic int string_count();
8746972Smjacobstatic void summary_format();
8846972Smjacobstatic void line_update();
8952349Smjacob
9039235Sgibbsint display_resize()
9139235Sgibbs
9239235Sgibbs{
9339235Sgibbs    register int lines;
9439235Sgibbs
9546972Smjacob    /* first, deallocate any previous buffer that may have been there */
9639235Sgibbs    if (screenbuf != NULL)
9779336Smjacob    {
9846972Smjacob	free(screenbuf);
9973245Smjacob    }
10046972Smjacob
10139235Sgibbs    /* calculate the current dimensions */
10279336Smjacob    /* if operating in "dumb" mode, we only need one line */
10339235Sgibbs    lines = smart_terminal ? screen_length - Header_lines : 1;
10439235Sgibbs
10579336Smjacob    if (lines < 0)
10662498Smjacob	lines = 0;
10762498Smjacob    /* we don't want more than MAX_COLS columns, since the machine-dependent
10862498Smjacob       modules make static allocations based on MAX_COLS and we don't want
10979336Smjacob       to run off the end of their buffers */
11062498Smjacob    display_width = screen_width;
11179336Smjacob    if (display_width >= MAX_COLS)
11279336Smjacob    {
11369597Smjacob	display_width = MAX_COLS - 1;
11469597Smjacob    }
11562498Smjacob
11662498Smjacob    /* now, allocate space for the screen buffer */
11762498Smjacob    screenbuf = (char *)malloc(lines * display_width);
11846972Smjacob    if (screenbuf == (char *)NULL)
11946972Smjacob    {
12079336Smjacob	/* oops! */
12139235Sgibbs	return(-1);
12239235Sgibbs    }
12339235Sgibbs
12446972Smjacob    /* return number of lines available */
12539235Sgibbs    /* for dumb terminals, pretend like we can show any amount */
12646972Smjacob    return(smart_terminal ? lines : Largest);
12746972Smjacob}
12877365Smjacob
12979336Smjacobint display_init(statics)
13039235Sgibbs
13139235Sgibbsstruct statics *statics;
13239235Sgibbs
13346972Smjacob{
13439235Sgibbs    register int lines;
13539235Sgibbs    register char **pp;
13643420Smjacob    register int *ip;
13746972Smjacob    register int i;
13839235Sgibbs
13979336Smjacob    /* call resize to do the dirty work */
14046972Smjacob    lines = display_resize();
14146972Smjacob
14277365Smjacob    /* only do the rest if we need to */
14377365Smjacob    if (lines > -1)
14477365Smjacob    {
14577365Smjacob	/* save pointers and allocate space for names */
14677365Smjacob	procstate_names = statics->procstate_names;
14779336Smjacob	num_procstates = string_count(procstate_names);
14879336Smjacob	lprocstates = (int *)malloc(num_procstates * sizeof(int));
14977365Smjacob
15077365Smjacob	cpustate_names = statics->cpustate_names;
15177365Smjacob
15277365Smjacob	swap_names = statics->swap_names;
15377365Smjacob	num_swap = string_count(swap_names);
15477365Smjacob	lswap = (int *)malloc(num_swap * sizeof(int));
15577365Smjacob	num_cpustates = string_count(cpustate_names);
15679336Smjacob	lcpustates = (int *)malloc(num_cpustates * sizeof(int));
15779336Smjacob	cpustate_columns = (int *)malloc(num_cpustates * sizeof(int));
15877365Smjacob
15977365Smjacob	memory_names = statics->memory_names;
16079338Smjacob	num_memory = string_count(memory_names);
16177365Smjacob	lmemory = (int *)malloc(num_memory * sizeof(int));
16239235Sgibbs
16377365Smjacob	/* calculate starting columns where needed */
16446972Smjacob	cpustate_total_length = 0;
16546972Smjacob	pp = cpustate_names;
16646972Smjacob	ip = cpustate_columns;
16754671Smjacob	while (*pp != NULL)
16879336Smjacob	{
16946972Smjacob	    *ip++ = cpustate_total_length;
17073245Smjacob	    if ((i = strlen(*pp++)) > 0)
17146972Smjacob	    {
17246972Smjacob		cpustate_total_length += i + 8;
17346972Smjacob	    }
17446972Smjacob	}
17577365Smjacob    }
17646972Smjacob
17746972Smjacob    /* return number of lines available */
17846972Smjacob    return(lines);
17946972Smjacob}
18046972Smjacob
18146972Smjacobi_loadave(mpid, avenrun)
18277365Smjacob
18379336Smjacobint mpid;
18446972Smjacobdouble *avenrun;
18546972Smjacob
18646972Smjacob{
18746972Smjacob    register int i;
18846972Smjacob
18946972Smjacob    /* i_loadave also clears the screen, since it is first */
19046972Smjacob    clear();
19146972Smjacob
19246972Smjacob    /* mpid == -1 implies this system doesn't have an _mpid */
19377365Smjacob    if (mpid != -1)
19479336Smjacob    {
19546972Smjacob	printf("last pid: %5d;  ", mpid);
19646972Smjacob    }
19746972Smjacob
19846972Smjacob    printf("load averages");
19946972Smjacob
20046972Smjacob    for (i = 0; i < 3; i++)
20146972Smjacob    {
20246972Smjacob	printf("%c %5.2f",
20346972Smjacob	    i == 0 ? ':' : ',',
20479336Smjacob	    avenrun[i]);
20546972Smjacob    }
20646972Smjacob    lmpid = mpid;
20746972Smjacob}
20877365Smjacob
20983028Smjacobu_loadave(mpid, avenrun)
21083028Smjacob
21183028Smjacobint mpid;
21283028Smjacobdouble *avenrun;
21383028Smjacob
21483028Smjacob{
21577365Smjacob    register int i;
21677365Smjacob
21777365Smjacob    if (mpid != -1)
21877365Smjacob    {
21977365Smjacob	/* change screen only when value has really changed */
22077365Smjacob	if (mpid != lmpid)
22172347Smjacob	{
22272347Smjacob	    Move_to(x_lastpid, y_lastpid);
22372347Smjacob	    printf("%5d", mpid);
22472347Smjacob	    lmpid = mpid;
22555371Smjacob	}
22655371Smjacob
22755371Smjacob	/* i remembers x coordinate to move to */
22855371Smjacob	i = x_loadave;
22955371Smjacob    }
23055371Smjacob    else
23155371Smjacob    {
23255371Smjacob	i = x_loadave_nompid;
23349915Smjacob    }
23477365Smjacob
23539235Sgibbs    /* move into position for load averages */
23639235Sgibbs    Move_to(i, y_loadave);
237102884Smjacob
23899598Smjacob    /* display new load averages */
23999598Smjacob    /* we should optimize this and only display changes */
24099598Smjacob    for (i = 0; i < 3; i++)
24199598Smjacob    {
24299598Smjacob	printf("%s%5.2f",
24399598Smjacob	    i == 0 ? "" : ", ",
24499598Smjacob	    avenrun[i]);
24599598Smjacob    }
24699598Smjacob}
24799598Smjacob
24899598Smjacobi_timeofday(tod)
24999598Smjacob
25099598Smjacobtime_t *tod;
25199598Smjacob
25277365Smjacob{
25383366Sjulian    /*
25477365Smjacob     *  Display the current time.
25577365Smjacob     *  "ctime" always returns a string that looks like this:
25677365Smjacob     *
25777365Smjacob     *	Sun Sep 16 01:03:52 1973
25877365Smjacob     *      012345678901234567890123
25977365Smjacob     *	          1         2
26077365Smjacob     *
26177365Smjacob     *  We want indices 11 thru 18 (length 8).
26277365Smjacob     */
26377365Smjacob
26477365Smjacob    if (smart_terminal)
26577365Smjacob    {
26677365Smjacob	Move_to(screen_width - 8, 0);
26777365Smjacob    }
26877365Smjacob    else
26990813Smjacob    {
27090813Smjacob	fputs("    ", stdout);
27190813Smjacob    }
27290813Smjacob#ifdef DEBUG
27390813Smjacob    {
27490813Smjacob	char *foo;
27590813Smjacob	foo = ctime(tod);
27690813Smjacob	fputs(foo, stdout);
27790813Smjacob    }
27890813Smjacob#endif
27990813Smjacob    printf("%-8.8s\n", &(ctime(tod)[11]));
28090813Smjacob    lastline = 1;
28190813Smjacob}
28290813Smjacob
28390813Smjacobstatic int ltotal = 0;
28490813Smjacobstatic char procstates_buffer[MAX_COLS];
28590813Smjacob
28690813Smjacob/*
28790813Smjacob *  *_procstates(total, brkdn, names) - print the process summary line
28890813Smjacob *
28990813Smjacob *  Assumptions:  cursor is at the beginning of the line on entry
29090813Smjacob *		  lastline is valid
29190813Smjacob */
29290813Smjacob
29390813Smjacobi_procstates(total, brkdn)
29490813Smjacob
29590813Smjacobint total;
29690813Smjacobint *brkdn;
29799598Smjacob
29890813Smjacob{
29990813Smjacob    register int i;
30090813Smjacob
30190813Smjacob    /* write current number of processes and remember the value */
30290813Smjacob    printf("%d processes:", total);
30390813Smjacob    ltotal = total;
30477365Smjacob
30577365Smjacob    /* put out enough spaces to get to column 15 */
30677365Smjacob    i = digits(total);
30777365Smjacob    while (i++ < 4)
30877365Smjacob    {
30977365Smjacob	putchar(' ');
31077365Smjacob    }
31177365Smjacob
31277365Smjacob    /* format and print the process state summary */
31377365Smjacob    summary_format(procstates_buffer, brkdn, procstate_names);
31477365Smjacob    fputs(procstates_buffer, stdout);
31577365Smjacob
31677365Smjacob    /* save the numbers for next time */
31777365Smjacob    memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
31891036Smjacob}
31977365Smjacob
32077365Smjacobu_procstates(total, brkdn)
32177365Smjacob
32277365Smjacobint total;
32377365Smjacobint *brkdn;
32477365Smjacob
32577365Smjacob{
32677365Smjacob    static char new[MAX_COLS];
32777365Smjacob    register int i;
32877365Smjacob
32977365Smjacob    /* update number of processes only if it has changed */
33077365Smjacob    if (ltotal != total)
33177365Smjacob    {
33277365Smjacob	/* move and overwrite */
33377365Smjacob#if (x_procstate == 0)
33477365Smjacob	Move_to(x_procstate, y_procstate);
33577365Smjacob#else
33677365Smjacob	/* cursor is already there...no motion needed */
33777365Smjacob	/* assert(lastline == 1); */
33877365Smjacob#endif
33977365Smjacob	printf("%d", total);
34077365Smjacob
34177365Smjacob	/* if number of digits differs, rewrite the label */
34277365Smjacob	if (digits(total) != digits(ltotal))
34377365Smjacob	{
34477365Smjacob	    fputs(" processes:", stdout);
34577365Smjacob	    /* put out enough spaces to get to column 15 */
34677365Smjacob	    i = digits(total);
34777365Smjacob	    while (i++ < 4)
34877365Smjacob	    {
34977365Smjacob		putchar(' ');
35077365Smjacob	    }
35177365Smjacob	    /* cursor may end up right where we want it!!! */
35277365Smjacob	}
35377365Smjacob
35477365Smjacob	/* save new total */
35577365Smjacob	ltotal = total;
35677365Smjacob    }
35777365Smjacob
35877365Smjacob    /* see if any of the state numbers has changed */
35977365Smjacob    if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
36077365Smjacob    {
36177365Smjacob	/* format and update the line */
36277365Smjacob	summary_format(new, brkdn, procstate_names);
36388855Smjacob	line_update(procstates_buffer, new, x_brkdn, y_brkdn);
36488855Smjacob	memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
36588855Smjacob    }
36688855Smjacob}
36788855Smjacob
36888855Smjacob/*
36988855Smjacob *  *_cpustates(states, names) - print the cpu state percentages
37088855Smjacob *
37188855Smjacob *  Assumptions:  cursor is on the PREVIOUS line
37288855Smjacob */
37388855Smjacob
37488855Smjacobstatic int cpustates_column;
37588855Smjacob
37688855Smjacob/* cpustates_tag() calculates the correct tag to use to label the line */
37788855Smjacob
37888855Smjacobchar *cpustates_tag()
37988855Smjacob
38088855Smjacob{
38188855Smjacob    register char *use;
38288855Smjacob
38388855Smjacob    static char *short_tag = "CPU: ";
38488855Smjacob    static char *long_tag = "CPU states: ";
38588855Smjacob
38688855Smjacob    /* if length + strlen(long_tag) >= screen_width, then we have to
38788855Smjacob       use the shorter tag (we subtract 2 to account for ": ") */
38888855Smjacob    if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
38988855Smjacob    {
39088855Smjacob	use = short_tag;
39188855Smjacob    }
39288855Smjacob    else
39388855Smjacob    {
39488855Smjacob	use = long_tag;
39588855Smjacob    }
39688855Smjacob
39798289Smjacob    /* set cpustates_column accordingly then return result */
39898289Smjacob    cpustates_column = strlen(use);
39998289Smjacob    return(use);
40098289Smjacob}
40198289Smjacob
40298289Smjacobi_cpustates(states)
40398289Smjacob
40498289Smjacobregister int *states;
40598289Smjacob
40698289Smjacob{
40798289Smjacob    register int i = 0;
40898289Smjacob    register int value;
40998289Smjacob    register char **names = cpustate_names;
41098289Smjacob    register char *thisname;
41198289Smjacob
41299598Smjacob    /* print tag and bump lastline */
41399598Smjacob    printf("\n%s", cpustates_tag());
41499598Smjacob    lastline++;
41599598Smjacob
41699598Smjacob    /* now walk thru the names and print the line */
41799598Smjacob    while ((thisname = *names++) != NULL)
41899598Smjacob    {
41999598Smjacob	if (*thisname != '\0')
42099598Smjacob	{
42199598Smjacob	    /* retrieve the value and remember it */
42299598Smjacob	    value = *states++;
42399598Smjacob
42499598Smjacob	    /* if percentage is >= 1000, print it as 100% */
42599598Smjacob	    printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
42699598Smjacob		   i++ == 0 ? "" : ", ",
42799598Smjacob		   ((float)value)/10.,
42899598Smjacob		   thisname);
42999598Smjacob	}
43099598Smjacob    }
43199598Smjacob
43299598Smjacob    /* copy over values into "last" array */
43399598Smjacob    memcpy(lcpustates, states, num_cpustates * sizeof(int));
43499598Smjacob}
43599598Smjacob
43699598Smjacobu_cpustates(states)
43799598Smjacob
43899598Smjacobregister int *states;
43999598Smjacob
44099598Smjacob{
44199598Smjacob    register int value;
44299598Smjacob    register char **names = cpustate_names;
44399598Smjacob    register char *thisname;
44499598Smjacob    register int *lp;
44599598Smjacob    register int *colp;
44699598Smjacob
44799598Smjacob    Move_to(cpustates_column, y_cpustates);
44899598Smjacob    lastline = y_cpustates;
44999598Smjacob    lp = lcpustates;
45099598Smjacob    colp = cpustate_columns;
45199598Smjacob
45299598Smjacob    /* we could be much more optimal about this */
45399598Smjacob    while ((thisname = *names++) != NULL)
45499598Smjacob    {
45599598Smjacob	if (*thisname != '\0')
45699598Smjacob	{
45799598Smjacob	    /* did the value change since last time? */
45899598Smjacob	    if (*lp != *states)
45999598Smjacob	    {
46099598Smjacob		/* yes, move and change */
46199598Smjacob		Move_to(cpustates_column + *colp, y_cpustates);
46299598Smjacob		lastline = y_cpustates;
46399598Smjacob
46499598Smjacob		/* retrieve value and remember it */
46599598Smjacob		value = *states;
46699598Smjacob
46799598Smjacob		/* if percentage is >= 1000, print it as 100% */
46899598Smjacob		printf((value >= 1000 ? "%4.0f" : "%4.1f"),
46999598Smjacob		       ((double)value)/10.);
47099598Smjacob
47199598Smjacob		/* remember it for next time */
47299598Smjacob		*lp = value;
47399598Smjacob	    }
47499598Smjacob	}
47599598Smjacob
47699598Smjacob	/* increment and move on */
47799598Smjacob	lp++;
47899598Smjacob	states++;
47999598Smjacob	colp++;
48099598Smjacob    }
48199598Smjacob}
48299598Smjacob
48399598Smjacobz_cpustates()
48499598Smjacob
48599598Smjacob{
48699598Smjacob    register int i = 0;
48799598Smjacob    register char **names = cpustate_names;
48899598Smjacob    register char *thisname;
48999598Smjacob    register int *lp;
49099598Smjacob
49199598Smjacob    /* show tag and bump lastline */
49299598Smjacob    printf("\n%s", cpustates_tag());
49399598Smjacob    lastline++;
49499598Smjacob
49599598Smjacob    while ((thisname = *names++) != NULL)
49699598Smjacob    {
49799598Smjacob	if (*thisname != '\0')
49899598Smjacob	{
49999598Smjacob	    printf("%s    %% %s", i++ == 0 ? "" : ", ", thisname);
50077365Smjacob	}
50177365Smjacob    }
50277365Smjacob
50377365Smjacob    /* fill the "last" array with all -1s, to insure correct updating */
50477365Smjacob    lp = lcpustates;
50577365Smjacob    i = num_cpustates;
50662498Smjacob    while (--i >= 0)
50762498Smjacob    {
50862498Smjacob	*lp++ = -1;
50962498Smjacob    }
51072347Smjacob}
51172347Smjacob
51272347Smjacob/*
51372347Smjacob *  *_memory(stats) - print "Memory: " followed by the memory summary string
51462498Smjacob *
51562498Smjacob *  Assumptions:  cursor is on "lastline"
51662498Smjacob *                for i_memory ONLY: cursor is on the previous line
51755371Smjacob */
51855371Smjacob
51955371Smjacobchar memory_buffer[MAX_COLS];
52055371Smjacob
52155371Smjacobi_memory(stats)
52255371Smjacob
52355371Smjacobint *stats;
524102884Smjacob
525102884Smjacob{
526102884Smjacob    fputs("\nMem: ", stdout);
527102884Smjacob    lastline++;
528102884Smjacob
529102884Smjacob    /* format and print the memory summary */
530102884Smjacob    summary_format(memory_buffer, stats, memory_names);
531102884Smjacob    fputs(memory_buffer, stdout);
532102884Smjacob}
53355371Smjacob
53475200Smjacobu_memory(stats)
53555371Smjacob
53655371Smjacobint *stats;
53755371Smjacob
53875200Smjacob{
53975200Smjacob    static char new[MAX_COLS];
54075200Smjacob
54155371Smjacob    /* format the new line */
54255371Smjacob    summary_format(new, stats, memory_names);
54355371Smjacob    line_update(memory_buffer, new, x_mem, y_mem);
54455371Smjacob}
54598289Smjacob
54698289Smjacob/*
54755371Smjacob *  *_swap(stats) - print "Swap: " followed by the swap summary string
548102884Smjacob *
54975200Smjacob *  Assumptions:  cursor is on "lastline"
55055371Smjacob *                for i_swap ONLY: cursor is on the previous line
55155371Smjacob */
55275200Smjacob
55375200Smjacobchar swap_buffer[MAX_COLS];
55455371Smjacob
55555371Smjacobi_swap(stats)
55655371Smjacob
55775200Smjacobint *stats;
55855371Smjacob
55955371Smjacob{
56055371Smjacob    fputs("\nSwap: ", stdout);
56155371Smjacob    lastline++;
56255371Smjacob
56355371Smjacob    /* format and print the swap summary */
564102884Smjacob    summary_format(swap_buffer, stats, swap_names);
56575200Smjacob    fputs(swap_buffer, stdout);
56655371Smjacob}
56775200Smjacob
56875200Smjacobu_swap(stats)
56975200Smjacob
57075200Smjacobint *stats;
57175200Smjacob
57275200Smjacob{
57375200Smjacob    static char new[MAX_COLS];
57475200Smjacob
57575200Smjacob    /* format the new line */
57675200Smjacob    summary_format(new, stats, swap_names);
57755371Smjacob    line_update(swap_buffer, new, x_swap, y_swap);
57855371Smjacob}
57955371Smjacob
58055371Smjacob/*
58155371Smjacob *  *_message() - print the next pending message line, or erase the one
58255371Smjacob *                that is there.
583102884Smjacob *
58475200Smjacob *  Note that u_message is (currently) the same as i_message.
58555371Smjacob *
58683028Smjacob *  Assumptions:  lastline is consistent
58755371Smjacob */
58855371Smjacob
58983028Smjacob/*
59082689Smjacob *  i_message is funny because it gets its message asynchronously (with
59182689Smjacob *	respect to screen updates).
59282689Smjacob */
59382689Smjacob
59455371Smjacobstatic char next_msg[MAX_COLS + 5];
59575200Smjacobstatic int msglen = 0;
59683028Smjacob/* Invariant: msglen is always the length of the message currently displayed
59783028Smjacob   on the screen (even when next_msg doesn't contain that message). */
59883028Smjacob
59955371Smjacobi_message()
60055371Smjacob
60155371Smjacob{
60275200Smjacob    while (lastline < y_message)
60355371Smjacob    {
60455371Smjacob	fputc('\n', stdout);
60555371Smjacob	lastline++;
60655371Smjacob    }
60755371Smjacob    if (next_msg[0] != '\0')
60855371Smjacob    {
60955371Smjacob	standout(next_msg);
610102884Smjacob	msglen = strlen(next_msg);
61155371Smjacob	next_msg[0] = '\0';
61255371Smjacob    }
61355371Smjacob    else if (msglen > 0)
61455371Smjacob    {
61555371Smjacob	(void) clear_eol(msglen);
61655371Smjacob	msglen = 0;
617102884Smjacob    }
61883028Smjacob}
61955371Smjacob
62083028Smjacobu_message()
62183028Smjacob
622102884Smjacob{
62383028Smjacob    i_message();
62455371Smjacob}
62555371Smjacob
626102884Smjacobstatic int header_length;
627102884Smjacob
628102884Smjacob/*
629102884Smjacob *  *_header(text) - print the header for the process area
630102884Smjacob *
63183028Smjacob *  Assumptions:  cursor is on the previous line and lastline is consistent
63255371Smjacob */
63355371Smjacob
63455371Smjacobi_header(text)
63555371Smjacob
636102884Smjacobchar *text;
63783028Smjacob
63855371Smjacob{
639102884Smjacob    header_length = strlen(text);
64083028Smjacob    if (header_status == ON)
64155371Smjacob    {
64255371Smjacob	putchar('\n');
643102884Smjacob	fputs(text, stdout);
644102884Smjacob	lastline++;
645102884Smjacob    }
646102884Smjacob    else if (header_status == ERASE)
647102884Smjacob    {
64855371Smjacob	header_status = OFF;
64955371Smjacob    }
65055371Smjacob}
651102884Smjacob
65283028Smjacob/*ARGSUSED*/
65355371Smjacobu_header(text)
65483028Smjacob
655102884Smjacobchar *text;		/* ignored */
65683028Smjacob
657102884Smjacob{
658102884Smjacob    if (header_status == ERASE)
659102884Smjacob    {
66055371Smjacob	putchar('\n');
66155371Smjacob	lastline++;
662102884Smjacob	clear_eol(header_length);
66383028Smjacob	header_status = OFF;
66455371Smjacob    }
66583028Smjacob}
66683028Smjacob
667102884Smjacob/*
66883028Smjacob *  *_process(line, thisline) - print one process line
669102884Smjacob *
670102884Smjacob *  Assumptions:  lastline is consistent
671102884Smjacob */
67255371Smjacob
67383028Smjacobi_process(line, thisline)
67455371Smjacob
67555371Smjacobint line;
676102884Smjacobchar *thisline;
67784242Smjacob
67884242Smjacob{
67984242Smjacob    register char *p;
68084242Smjacob    register char *base;
68184242Smjacob
68284242Smjacob    /* make sure we are on the correct line */
68384242Smjacob    while (lastline < y_procs + line)
68484242Smjacob    {
68584242Smjacob	putchar('\n');
68684242Smjacob	lastline++;
68784242Smjacob    }
68855371Smjacob
68975200Smjacob    /* truncate the line to conform to our current screen width */
69075200Smjacob    thisline[display_width] = '\0';
69155371Smjacob
69255371Smjacob    /* write the line out */
69355371Smjacob    fputs(thisline, stdout);
69475200Smjacob
69555371Smjacob    /* copy it in to our buffer */
69655371Smjacob    base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
69755371Smjacob    p = strecpy(base, thisline);
69855371Smjacob
69955371Smjacob    /* zero fill the rest of it */
70055371Smjacob    memzero(p, display_width - (p - base));
70175200Smjacob}
70255371Smjacob
70355371Smjacobu_process(line, newline)
70469557Sdwmalone
70555371Smjacobint line;
70655371Smjacobchar *newline;
70755371Smjacob
70855371Smjacob{
70955371Smjacob    register char *optr;
71055371Smjacob    register int screen_line = line + Header_lines;
71155371Smjacob    register char *bufferline;
71255371Smjacob
71355371Smjacob    /* remember a pointer to the current line in the screen buffer */
71455371Smjacob    bufferline = &screenbuf[lineindex(line)];
71575200Smjacob
71655371Smjacob    /* truncate the line to conform to our current screen width */
71755371Smjacob    newline[display_width] = '\0';
71855371Smjacob
71955371Smjacob    /* is line higher than we went on the last display? */
72055371Smjacob    if (line >= last_hi)
72175200Smjacob    {
72275200Smjacob	/* yes, just ignore screenbuf and write it out directly */
72375200Smjacob	/* get positioned on the correct line */
72475200Smjacob	if (screen_line - lastline == 1)
72555371Smjacob	{
72655371Smjacob	    putchar('\n');
72755371Smjacob	    lastline++;
72855371Smjacob	}
72955371Smjacob	else
73055371Smjacob	{
73155371Smjacob	    Move_to(0, screen_line);
73255371Smjacob	    lastline = screen_line;
73355371Smjacob	}
734102884Smjacob
73555371Smjacob	/* now write the line */
73655371Smjacob	fputs(newline, stdout);
73775200Smjacob
73855371Smjacob	/* copy it in to the buffer */
73955371Smjacob	optr = strecpy(bufferline, newline);
74075200Smjacob
74155371Smjacob	/* zero fill the rest of it */
74255371Smjacob	memzero(optr, display_width - (optr - bufferline));
74355371Smjacob    }
74475200Smjacob    else
74555371Smjacob    {
74655371Smjacob	line_update(bufferline, newline, 0, line + Header_lines);
74775200Smjacob    }
74875200Smjacob}
74955371Smjacob
75055371Smjacobu_endscreen(hi)
75155371Smjacob
75255371Smjacobregister int hi;
75375200Smjacob
75455371Smjacob{
75555371Smjacob    register int screen_line = hi + Header_lines;
75655371Smjacob    register int i;
75755371Smjacob
75855371Smjacob    if (smart_terminal)
75955371Smjacob    {
76055371Smjacob	if (hi < last_hi)
76155371Smjacob	{
76255371Smjacob	    /* need to blank the remainder of the screen */
76355371Smjacob	    /* but only if there is any screen left below this line */
76455371Smjacob	    if (lastline + 1 < screen_length)
76555371Smjacob	    {
76655371Smjacob		/* efficiently move to the end of currently displayed info */
76777365Smjacob		if (screen_line - lastline < 5)
76877365Smjacob		{
76977365Smjacob		    while (lastline < screen_line)
77039235Sgibbs		    {
77155371Smjacob			putchar('\n');
77255371Smjacob			lastline++;
77375200Smjacob		    }
77455371Smjacob		}
77555371Smjacob		else
77655371Smjacob		{
77783028Smjacob		    Move_to(0, screen_line);
77855371Smjacob		    lastline = screen_line;
77955371Smjacob		}
78055371Smjacob
78155371Smjacob		if (clear_to_end)
78275200Smjacob		{
78355371Smjacob		    /* we can do this the easy way */
78455371Smjacob		    putcap(clear_to_end);
78555371Smjacob		}
78655371Smjacob		else
78765140Smjacob		{
78865140Smjacob		    /* use clear_eol on each line */
78965140Smjacob		    i = hi;
79072347Smjacob		    while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
79172347Smjacob		    {
79265140Smjacob			putchar('\n');
79365140Smjacob		    }
79465140Smjacob		}
79583028Smjacob	    }
79665140Smjacob	}
79775200Smjacob	last_hi = hi;
79875200Smjacob
79965140Smjacob	/* move the cursor to a pleasant place */
80075200Smjacob	Move_to(x_idlecursor, y_idlecursor);
80165140Smjacob	lastline = y_idlecursor;
80265140Smjacob    }
80365140Smjacob    else
80465140Smjacob    {
80565140Smjacob	/* separate this display from the next with some vertical room */
80672347Smjacob	fputs("\n\n", stdout);
80765140Smjacob    }
80865140Smjacob}
80965140Smjacob
81083028Smjacobdisplay_header(t)
81183028Smjacob
81283028Smjacobint t;
81383028Smjacob
81483028Smjacob{
81583028Smjacob    if (t)
81683028Smjacob    {
81772347Smjacob	header_status = ON;
81872347Smjacob    }
81983028Smjacob    else if (header_status == ON)
82083028Smjacob    {
82183028Smjacob	header_status = ERASE;
82283028Smjacob    }
82383028Smjacob}
82483028Smjacob
82583028Smjacob/*VARARGS2*/
82665140Smjacobnew_message(type, msgfmt, a1, a2, a3)
82765140Smjacob
82865140Smjacobint type;
82983028Smjacobchar *msgfmt;
83083028Smjacobcaddr_t a1, a2, a3;
83183028Smjacob
83283028Smjacob{
83383028Smjacob    register int i;
83455371Smjacob
83555371Smjacob    /* first, format the message */
83682689Smjacob    (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3);
83782689Smjacob
83882689Smjacob    if (msglen > 0)
83955371Smjacob    {
84055371Smjacob	/* message there already -- can we clear it? */
84155371Smjacob	if (!overstrike)
84283028Smjacob	{
84383028Smjacob	    /* yes -- write it and clear to end */
84483028Smjacob	    i = strlen(next_msg);
84583028Smjacob	    if ((type & MT_delayed) == 0)
84655371Smjacob	    {
84782689Smjacob		type & MT_standout ? standout(next_msg) :
84882689Smjacob		                     fputs(next_msg, stdout);
84975200Smjacob		(void) clear_eol(msglen - i);
85055371Smjacob		msglen = i;
85183028Smjacob		next_msg[0] = '\0';
85283028Smjacob	    }
85355371Smjacob	}
85455371Smjacob    }
85555371Smjacob    else
85655371Smjacob    {
85755371Smjacob	if ((type & MT_delayed) == 0)
85855371Smjacob	{
85955371Smjacob	    type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
86055371Smjacob	    msglen = strlen(next_msg);
86155371Smjacob	    next_msg[0] = '\0';
86255371Smjacob	}
86355371Smjacob    }
86455371Smjacob}
86555371Smjacob
86683028Smjacobclear_message()
86755371Smjacob
86883028Smjacob{
86983028Smjacob    if (clear_eol(msglen) == 1)
87082689Smjacob    {
87155371Smjacob	putchar('\r');
87255371Smjacob    }
87382689Smjacob}
87455371Smjacob
87555371Smjacobreadline(buffer, size, numeric)
87655371Smjacob
87782689Smjacobchar *buffer;
87883028Smjacobint  size;
87982689Smjacobint  numeric;
88082689Smjacob
88182689Smjacob{
88282689Smjacob    register char *ptr = buffer;
88382689Smjacob    register char ch;
88482689Smjacob    register char cnt = 0;
88582689Smjacob    register char maxcnt = 0;
88682689Smjacob
88783028Smjacob    /* allow room for null terminator */
88882689Smjacob    size -= 1;
88982689Smjacob
89082689Smjacob    /* read loop */
89182689Smjacob    while ((fflush(stdout), read(0, ptr, 1) > 0))
89282689Smjacob    {
89383028Smjacob	/* newline means we are done */
89483028Smjacob	if ((ch = *ptr) == '\n' || ch == '\r')
89582689Smjacob	{
89655371Smjacob	    break;
89782689Smjacob	}
89855371Smjacob
89983028Smjacob	/* handle special editing characters */
90082689Smjacob	if (ch == ch_kill)
90182689Smjacob	{
90283028Smjacob	    /* kill line -- account for overstriking */
90383028Smjacob	    if (overstrike)
90482689Smjacob	    {
90582689Smjacob		msglen += maxcnt;
90682689Smjacob	    }
90782689Smjacob
90882689Smjacob	    /* return null string */
90982689Smjacob	    *buffer = '\0';
91082689Smjacob	    putchar('\r');
91182689Smjacob	    return(-1);
91282689Smjacob	}
91383028Smjacob	else if (ch == ch_erase)
91482689Smjacob	{
91582689Smjacob	    /* erase previous character */
91682689Smjacob	    if (cnt <= 0)
91782689Smjacob	    {
91882689Smjacob		/* none to erase! */
91983028Smjacob		putchar('\7');
92055371Smjacob	    }
92155371Smjacob	    else
92255371Smjacob	    {
92355371Smjacob		fputs("\b \b", stdout);
92455371Smjacob		ptr--;
92575200Smjacob		cnt--;
92655371Smjacob	    }
92755371Smjacob	}
92855371Smjacob	/* check for character validity and buffer overflow */
92955371Smjacob	else if (cnt == size || (numeric && !isdigit(ch)) ||
93075200Smjacob		!isprint(ch))
93155371Smjacob	{
93255371Smjacob	    /* not legal */
93355371Smjacob	    putchar('\7');
93455371Smjacob	}
93555371Smjacob	else
93655371Smjacob	{
93783028Smjacob	    /* echo it and store it in the buffer */
93855371Smjacob	    putchar(ch);
93955371Smjacob	    ptr++;
94055371Smjacob	    cnt++;
94155371Smjacob	    if (cnt > maxcnt)
94255371Smjacob	    {
94355371Smjacob		maxcnt = cnt;
94455371Smjacob	    }
94555371Smjacob	}
94655371Smjacob    }
94777365Smjacob
94877365Smjacob    /* all done -- null terminate the string */
94977365Smjacob    *ptr = '\0';
95077365Smjacob
95177365Smjacob    /* account for the extra characters in the message area */
95277365Smjacob    /* (if terminal overstrikes, remember the furthest they went) */
95377365Smjacob    msglen += overstrike ? maxcnt : cnt;
95477365Smjacob
95577365Smjacob    /* return either inputted number or string length */
95677365Smjacob    putchar('\r');
95777365Smjacob    return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
95877365Smjacob}
95977365Smjacob
96077365Smjacob/* internal support routines */
96155371Smjacob
96277365Smjacobstatic int string_count(pp)
96355371Smjacob
96473245Smjacobregister char **pp;
96555371Smjacob
96655371Smjacob{
96783028Smjacob    register int cnt;
96855371Smjacob
96973245Smjacob    cnt = 0;
97077365Smjacob    while (*pp++ != NULL)
97155371Smjacob    {
97255371Smjacob	cnt++;
97383028Smjacob    }
97455371Smjacob    return(cnt);
97555371Smjacob}
97673245Smjacob
97777365Smjacobstatic void summary_format(str, numbers, names)
97855371Smjacob
97955371Smjacobchar *str;
98055371Smjacobint *numbers;
98177365Smjacobregister char **names;
98255371Smjacob
98355371Smjacob{
98477365Smjacob    register char *p;
98555371Smjacob    register int num;
98677365Smjacob    register char *thisname;
98755371Smjacob    register int useM = No;
98877365Smjacob
98977365Smjacob    /* format each number followed by its string */
99077365Smjacob    p = str;
99177365Smjacob    while ((thisname = *names++) != NULL)
99277365Smjacob    {
99377365Smjacob	/* get the number to format */
99477365Smjacob	num = *numbers++;
99577365Smjacob
99677365Smjacob	/* display only non-zero numbers */
99777365Smjacob	if (num > 0)
99877365Smjacob	{
99955371Smjacob	    /* is this number in kilobytes? */
100073245Smjacob	    if (thisname[0] == 'K')
100155371Smjacob	    {
100255371Smjacob		/* yes: format it as a memory value */
100383028Smjacob		p = strecpy(p, format_k(num));
100455371Smjacob
100573245Smjacob		/* skip over the K, since it was included by format_k */
100673245Smjacob		p = strecpy(p, thisname+1);
100755371Smjacob	    }
100855371Smjacob	    else
100983028Smjacob	    {
101055371Smjacob		p = strecpy(p, itoa(num));
101155371Smjacob		p = strecpy(p, thisname);
101273245Smjacob	    }
101373245Smjacob	}
101455371Smjacob
101555371Smjacob	/* ignore negative numbers, but display corresponding string */
101677365Smjacob	else if (num < 0)
101777365Smjacob	{
101877365Smjacob	    p = strecpy(p, thisname);
101977365Smjacob	}
102055371Smjacob    }
102155371Smjacob
102277365Smjacob    /* if the last two characters in the string are ", ", delete them */
102377365Smjacob    p -= 2;
102477365Smjacob    if (p >= str && p[0] == ',' && p[1] == ' ')
102555371Smjacob    {
102673245Smjacob	*p = '\0';
102755371Smjacob    }
102855371Smjacob}
102983028Smjacob
103055371Smjacobstatic void line_update(old, new, start, line)
103173245Smjacob
103277365Smjacobregister char *old;
103355371Smjacobregister char *new;
103455371Smjacobint start;
103583028Smjacobint line;
103655371Smjacob
103755371Smjacob{
103873245Smjacob    register int ch;
103977365Smjacob    register int diff;
104055371Smjacob    register int newcol = start + 1;
104155371Smjacob    register int lastcol = start;
104282689Smjacob    char cursor_on_line = No;
104382689Smjacob    char *current;
104482689Smjacob
104582689Smjacob    /* compare the two strings and only rewrite what has changed */
104682689Smjacob    current = old;
104782689Smjacob#ifdef DEBUG
104882689Smjacob    fprintf(debug, "line_update, starting at %d\n", start);
104982689Smjacob    fputs(old, debug);
105083028Smjacob    fputc('\n', debug);
105182689Smjacob    fputs(new, debug);
105282689Smjacob    fputs("\n-\n", debug);
105382689Smjacob#endif
105482689Smjacob
105555371Smjacob    /* start things off on the right foot		    */
105682689Smjacob    /* this is to make sure the invariants get set up right */
105755371Smjacob    if ((ch = *new++) != *old)
105883028Smjacob    {
105955371Smjacob	if (line - lastline == 1 && start == 0)
106055371Smjacob	{
106155371Smjacob	    putchar('\n');
106273245Smjacob	}
106373245Smjacob	else
106455371Smjacob	{
106555371Smjacob	    Move_to(start, line);
106655371Smjacob	}
106755371Smjacob	cursor_on_line = Yes;
106855371Smjacob	putchar(ch);
106955371Smjacob	*old = ch;
107075200Smjacob	lastcol = 1;
107175200Smjacob    }
107255371Smjacob    old++;
107355371Smjacob
107455371Smjacob    /*
107555371Smjacob     *  main loop -- check each character.  If the old and new aren't the
107655371Smjacob     *	same, then update the display.  When the distance from the
107755371Smjacob     *	current cursor position to the new change is small enough,
107855371Smjacob     *	the characters that belong there are written to move the
107955371Smjacob     *	cursor over.
108055371Smjacob     *
108155371Smjacob     *	Invariants:
108255371Smjacob     *	    lastcol is the column where the cursor currently is sitting
108355371Smjacob     *		(always one beyond the end of the last mismatch).
108455371Smjacob     */
108555371Smjacob    do		/* yes, a do...while */
108655371Smjacob    {
108755371Smjacob	if ((ch = *new++) != *old)
108855371Smjacob	{
108955371Smjacob	    /* new character is different from old	  */
109055371Smjacob	    /* make sure the cursor is on top of this character */
109155371Smjacob	    diff = newcol - lastcol;
109255371Smjacob	    if (diff > 0)
109355371Smjacob	    {
109455371Smjacob		/* some motion is required--figure out which is shorter */
109555371Smjacob		if (diff < 6 && cursor_on_line)
109655371Smjacob		{
109755371Smjacob		    /* overwrite old stuff--get it out of the old buffer */
109875200Smjacob		    printf("%.*s", diff, &current[lastcol-start]);
109955371Smjacob		}
110055371Smjacob		else
110155371Smjacob		{
110255371Smjacob		    /* use cursor addressing */
110355371Smjacob		    Move_to(newcol, line);
110455371Smjacob		    cursor_on_line = Yes;
110555371Smjacob		}
110655371Smjacob		/* remember where the cursor is */
110755371Smjacob		lastcol = newcol + 1;
110855371Smjacob	    }
110955371Smjacob	    else
111055371Smjacob	    {
111155371Smjacob		/* already there, update position */
111255371Smjacob		lastcol++;
111355371Smjacob	    }
111455371Smjacob
111555371Smjacob	    /* write what we need to */
111655371Smjacob	    if (ch == '\0')
111755371Smjacob	    {
111855371Smjacob		/* at the end--terminate with a clear-to-end-of-line */
111955371Smjacob		(void) clear_eol(strlen(old));
112055371Smjacob	    }
112155371Smjacob	    else
112255371Smjacob	    {
112355371Smjacob		/* write the new character */
112455371Smjacob		putchar(ch);
112555371Smjacob	    }
112655371Smjacob	    /* put the new character in the screen buffer */
112755371Smjacob	    *old = ch;
112855371Smjacob	}
112955371Smjacob
113055371Smjacob	/* update working column and screen buffer pointer */
113155371Smjacob	newcol++;
113255371Smjacob	old++;
113355371Smjacob
113455371Smjacob    } while (ch != '\0');
113555371Smjacob
113655371Smjacob    /* zero out the rest of the line buffer -- MUST BE DONE! */
113755371Smjacob    diff = display_width - newcol;
113855371Smjacob    if (diff > 0)
113955371Smjacob    {
114055371Smjacob	memzero(old, diff);
114156007Smjacob    }
114273319Smjacob
114387635Smjacob    /* remember where the current line is */
114487635Smjacob    if (cursor_on_line)
114555371Smjacob    {
114663385Smjacob	lastline = line;
114787635Smjacob    }
114855387Smjacob}
114955387Smjacob
115055371Smjacob/*
115155371Smjacob *  printable(str) - make the string pointed to by "str" into one that is
115287635Smjacob *	printable (i.e.: all ascii), by converting all non-printable
115355371Smjacob *	characters into '?'.  Replacements are done in place and a pointer
115455371Smjacob *	to the original buffer is returned.
115555371Smjacob */
115655371Smjacob
115755371Smjacobchar *printable(str)
115855371Smjacob
115984242Smjacobchar *str;
116087635Smjacob
116156007Smjacob{
116255371Smjacob    register char *ptr;
116355371Smjacob    register char ch;
116456007Smjacob
116583028Smjacob    ptr = str;
116661775Smjacob    while ((ch = *ptr) != '\0')
116761775Smjacob    {
116863385Smjacob	if (!isprint(ch))
116984242Smjacob	{
117084242Smjacob	    *ptr = '?';
117198289Smjacob	}
117298289Smjacob	ptr++;
117384242Smjacob    }
117498289Smjacob    return(str);
117584242Smjacob}
117684242Smjacob
117756007Smjacobi_uptime(bt, tod)
117856007Smjacob
117956007Smjacobstruct timeval* bt;
118063385Smjacobtime_t *tod;
118163385Smjacob
118263385Smjacob{
118384242Smjacob    time_t uptime;
118484242Smjacob    int days, hrs, mins, secs;
118598289Smjacob
118698289Smjacob    if (bt->tv_sec != -1) {
118798289Smjacob	uptime = *tod - bt->tv_sec;
118898289Smjacob	uptime += 30;
118998289Smjacob	days = uptime / 86400;
119098289Smjacob	uptime %= 86400;
119198289Smjacob	hrs = uptime / 3600;
119263385Smjacob	uptime %= 3600;
119356007Smjacob	mins = uptime / 60;
119456007Smjacob	secs = uptime % 60;
119556007Smjacob
119656007Smjacob	/*
119756007Smjacob	 *  Display the uptime.
119855371Smjacob	 */
119956007Smjacob
120056007Smjacob	if (smart_terminal)
120156007Smjacob	{
120256007Smjacob	    Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
120356007Smjacob	}
120456007Smjacob	else
120555387Smjacob	{
120698289Smjacob	    fputs(" ", stdout);
120756007Smjacob	}
120856007Smjacob	printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
120956007Smjacob    }
121084242Smjacob}
121184242Smjacob