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