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