]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/top/display.c
Merge ACPICA 20110211.
[FreeBSD/FreeBSD.git] / contrib / top / display.c
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  * $FreeBSD$
12  */
13
14 /*
15  *  This file contains the routines that display information on the screen.
16  *  Each section of the screen has two routines:  one for initially writing
17  *  all constant and dynamic text, and one for only updating the text that
18  *  changes.  The prefix "i_" is used on all the "initial" routines and the
19  *  prefix "u_" is used for all the "updating" routines.
20  *
21  *  ASSUMPTIONS:
22  *        None of the "i_" routines use any of the termcap capabilities.
23  *        In this way, those routines can be safely used on terminals that
24  *        have minimal (or nonexistant) terminal capabilities.
25  *
26  *        The routines are called in this order:  *_loadave, i_timeofday,
27  *        *_procstates, *_cpustates, *_memory, *_message, *_header,
28  *        *_process, u_endscreen.
29  */
30
31 #include "os.h"
32 #include <ctype.h>
33 #include <time.h>
34 #include <sys/time.h>
35
36 #include "screen.h"             /* interface to screen package */
37 #include "layout.h"             /* defines for screen position layout */
38 #include "display.h"
39 #include "top.h"
40 #include "top.local.h"
41 #include "boolean.h"
42 #include "machine.h"            /* we should eliminate this!!! */
43 #include "utils.h"
44
45 #ifdef DEBUG
46 FILE *debug;
47 #endif
48
49 /* imported from screen.c */
50 extern int overstrike;
51
52 static int lmpid = 0;
53 static int last_hi = 0;         /* used in u_process and u_endscreen */
54 static int lastline = 0;
55 static int display_width = MAX_COLS;
56
57 #define lineindex(l) ((l)*display_width)
58
59 char *printable();
60
61 /* things initialized by display_init and used thruout */
62
63 /* buffer of proc information lines for display updating */
64 char *screenbuf = NULL;
65
66 static char **procstate_names;
67 static char **cpustate_names;
68 static char **memory_names;
69 static char **swap_names;
70
71 static int num_procstates;
72 static int num_cpustates;
73 static int num_memory;
74 static int num_swap;
75
76 static int *lprocstates;
77 static int *lcpustates;
78 static int *lmemory;
79 static int *lswap;
80
81 static int num_cpus;
82 static int *cpustate_columns;
83 static int cpustate_total_length;
84 static int cpustates_column;
85
86 static enum { OFF, ON, ERASE } header_status = ON;
87
88 static int string_count();
89 static void summary_format();
90 static void line_update();
91
92 int  x_lastpid =        10;
93 int  y_lastpid =        0;
94 int  x_loadave =        33;
95 int  x_loadave_nompid = 15;
96 int  y_loadave =        0;
97 int  x_procstate =      0;
98 int  y_procstate =      1;
99 int  x_brkdn =          15;
100 int  y_brkdn =          1;
101 int  x_mem =            5;
102 int  y_mem =            3;
103 int  x_swap =           6;
104 int  y_swap =           4;
105 int  y_message =        5;
106 int  x_header =         0;
107 int  y_header =         6;
108 int  x_idlecursor =     0;
109 int  y_idlecursor =     5;
110 int  y_procs =          7;
111
112 int  y_cpustates =      2;
113 int  Header_lines =     7;
114
115 int 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
154 int display_init(statics)
155
156 struct 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
212 i_loadave(mpid, avenrun)
213
214 int mpid;
215 double *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
240 u_loadave(mpid, avenrun)
241
242 int mpid;
243 double *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
279 i_timeofday(tod)
280
281 time_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
314 static int ltotal = 0;
315 static 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
324 i_procstates(total, brkdn)
325
326 int total;
327 int *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
351 u_procstates(total, brkdn)
352
353 int total;
354 int *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
408 char *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
433 i_cpustates(states)
434
435 register 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
444 for (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         value = printf("\nCPU %d: ", cpu);
452         while (value++ <= cpustates_column)
453                 printf(" ");
454     }
455     lastline++;
456
457     /* now walk thru the names and print the line */
458     while ((thisname = *names++) != NULL)
459     {
460         if (*thisname != '\0')
461         {
462             /* retrieve the value and remember it */
463             value = *states++;
464
465             /* if percentage is >= 1000, print it as 100% */
466             printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
467                    (i++ % num_cpustates) == 0 ? "" : ", ",
468                    ((float)value)/10.,
469                    thisname);
470         }
471     }
472 }
473
474     /* copy over values into "last" array */
475     memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
476 }
477
478 u_cpustates(states)
479
480 register int *states;
481
482 {
483     register int value;
484     register char **names;
485     register char *thisname;
486     register int *lp;
487     register int *colp;
488     int cpu;
489
490 for (cpu = 0; cpu < num_cpus; cpu++) {
491     names = cpustate_names;
492
493     Move_to(cpustates_column, y_cpustates + cpu);
494     lastline = y_cpustates + cpu;
495     lp = lcpustates + (cpu * num_cpustates);
496     colp = cpustate_columns;
497
498     /* we could be much more optimal about this */
499     while ((thisname = *names++) != NULL)
500     {
501         if (*thisname != '\0')
502         {
503             /* did the value change since last time? */
504             if (*lp != *states)
505             {
506                 /* yes, move and change */
507                 Move_to(cpustates_column + *colp, y_cpustates + cpu);
508                 lastline = y_cpustates + cpu;
509
510                 /* retrieve value and remember it */
511                 value = *states;
512
513                 /* if percentage is >= 1000, print it as 100% */
514                 printf((value >= 1000 ? "%4.0f" : "%4.1f"),
515                        ((double)value)/10.);
516
517                 /* remember it for next time */
518                 *lp = value;
519             }
520         }
521
522         /* increment and move on */
523         lp++;
524         states++;
525         colp++;
526     }
527 }
528 }
529
530 z_cpustates()
531
532 {
533     register int i = 0;
534     register char **names;
535     register char *thisname;
536     register int *lp;
537     int cpu, value;
538
539 for (cpu = 0; cpu < num_cpus; cpu++) {
540     names = cpustate_names;
541
542     /* show tag and bump lastline */
543     if (num_cpus == 1)
544         printf("\nCPU: ");
545     else {
546         value = printf("\nCPU %d: ", cpu);
547         while (value++ <= cpustates_column)
548                 printf(" ");
549     }
550     lastline++;
551
552     while ((thisname = *names++) != NULL)
553     {
554         if (*thisname != '\0')
555         {
556             printf("%s    %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname);
557         }
558     }
559 }
560
561     /* fill the "last" array with all -1s, to insure correct updating */
562     lp = lcpustates;
563     i = num_cpustates * num_cpus;
564     while (--i >= 0)
565     {
566         *lp++ = -1;
567     }
568 }
569
570 /*
571  *  *_memory(stats) - print "Memory: " followed by the memory summary string
572  *
573  *  Assumptions:  cursor is on "lastline"
574  *                for i_memory ONLY: cursor is on the previous line
575  */
576
577 char memory_buffer[MAX_COLS];
578
579 i_memory(stats)
580
581 int *stats;
582
583 {
584     fputs("\nMem: ", stdout);
585     lastline++;
586
587     /* format and print the memory summary */
588     summary_format(memory_buffer, stats, memory_names);
589     fputs(memory_buffer, stdout);
590 }
591
592 u_memory(stats)
593
594 int *stats;
595
596 {
597     static char new[MAX_COLS];
598
599     /* format the new line */
600     summary_format(new, stats, memory_names);
601     line_update(memory_buffer, new, x_mem, y_mem);
602 }
603
604 /*
605  *  *_swap(stats) - print "Swap: " followed by the swap summary string
606  *
607  *  Assumptions:  cursor is on "lastline"
608  *                for i_swap ONLY: cursor is on the previous line
609  */
610
611 char swap_buffer[MAX_COLS];
612
613 i_swap(stats)
614
615 int *stats;
616
617 {
618     fputs("\nSwap: ", stdout);
619     lastline++;
620
621     /* format and print the swap summary */
622     summary_format(swap_buffer, stats, swap_names);
623     fputs(swap_buffer, stdout);
624 }
625
626 u_swap(stats)
627
628 int *stats;
629
630 {
631     static char new[MAX_COLS];
632
633     /* format the new line */
634     summary_format(new, stats, swap_names);
635     line_update(swap_buffer, new, x_swap, y_swap);
636 }
637
638 /*
639  *  *_message() - print the next pending message line, or erase the one
640  *                that is there.
641  *
642  *  Note that u_message is (currently) the same as i_message.
643  *
644  *  Assumptions:  lastline is consistent
645  */
646
647 /*
648  *  i_message is funny because it gets its message asynchronously (with
649  *      respect to screen updates).
650  */
651
652 static char next_msg[MAX_COLS + 5];
653 static int msglen = 0;
654 /* Invariant: msglen is always the length of the message currently displayed
655    on the screen (even when next_msg doesn't contain that message). */
656
657 i_message()
658
659 {
660     while (lastline < y_message)
661     {
662         fputc('\n', stdout);
663         lastline++;
664     }
665     if (next_msg[0] != '\0')
666     {
667         standout(next_msg);
668         msglen = strlen(next_msg);
669         next_msg[0] = '\0';
670     }
671     else if (msglen > 0)
672     {
673         (void) clear_eol(msglen);
674         msglen = 0;
675     }
676 }
677
678 u_message()
679
680 {
681     i_message();
682 }
683
684 static int header_length;
685
686 /*
687  * Trim a header string to the current display width and return a newly
688  * allocated area with the trimmed header.
689  */
690
691 char *
692 trim_header(text)
693
694 char *text;
695
696 {
697         char *s;
698         int width;
699
700         s = NULL;
701         width = screen_width;
702         header_length = strlen(text);
703         if (header_length >= width) {
704                 s = malloc((width + 1) * sizeof(char));
705                 if (s == NULL)
706                         return (NULL);
707                 strncpy(s, text, width);
708                 s[width] = '\0';
709         } else {
710                 s = malloc((width + 1) * sizeof(char));
711                 if (s == NULL)
712                         return (NULL);
713                 strncpy(s, text, width);
714                 while (screen_width > header_length)
715                         s[header_length++] = ' ';
716                 s[width] = '\0';
717         }
718         return (s);
719 }
720
721 /*
722  *  *_header(text) - print the header for the process area
723  *
724  *  Assumptions:  cursor is on the previous line and lastline is consistent
725  */
726
727 i_header(text)
728
729 char *text;
730
731 {
732     char *s;
733
734     s = trim_header(text);
735     if (s != NULL)
736         text = s;
737
738     if (header_status == ON)
739     {
740         putchar('\n');
741         standout(text, stdout);
742         lastline++;
743     }
744     else if (header_status == ERASE)
745     {
746         header_status = OFF;
747     }
748     free(s);
749 }
750
751 /*ARGSUSED*/
752 u_header(text)
753
754 char *text;             /* ignored */
755
756 {
757
758     if (header_status == ERASE)
759     {
760         putchar('\n');
761         lastline++;
762         clear_eol(header_length);
763         header_status = OFF;
764     }
765 }
766
767 /*
768  *  *_process(line, thisline) - print one process line
769  *
770  *  Assumptions:  lastline is consistent
771  */
772
773 i_process(line, thisline)
774
775 int line;
776 char *thisline;
777
778 {
779     register char *p;
780     register char *base;
781
782     /* make sure we are on the correct line */
783     while (lastline < y_procs + line)
784     {
785         putchar('\n');
786         lastline++;
787     }
788
789     /* truncate the line to conform to our current screen width */
790     thisline[display_width] = '\0';
791
792     /* write the line out */
793     fputs(thisline, stdout);
794
795     /* copy it in to our buffer */
796     base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
797     p = strecpy(base, thisline);
798
799     /* zero fill the rest of it */
800     memzero(p, display_width - (p - base));
801 }
802
803 u_process(line, newline)
804
805 int line;
806 char *newline;
807
808 {
809     register char *optr;
810     register int screen_line = line + Header_lines;
811     register char *bufferline;
812
813     /* remember a pointer to the current line in the screen buffer */
814     bufferline = &screenbuf[lineindex(line)];
815
816     /* truncate the line to conform to our current screen width */
817     newline[display_width] = '\0';
818
819     /* is line higher than we went on the last display? */
820     if (line >= last_hi)
821     {
822         /* yes, just ignore screenbuf and write it out directly */
823         /* get positioned on the correct line */
824         if (screen_line - lastline == 1)
825         {
826             putchar('\n');
827             lastline++;
828         }
829         else
830         {
831             Move_to(0, screen_line);
832             lastline = screen_line;
833         }
834
835         /* now write the line */
836         fputs(newline, stdout);
837
838         /* copy it in to the buffer */
839         optr = strecpy(bufferline, newline);
840
841         /* zero fill the rest of it */
842         memzero(optr, display_width - (optr - bufferline));
843     }
844     else
845     {
846         line_update(bufferline, newline, 0, line + Header_lines);
847     }
848 }
849
850 u_endscreen(hi)
851
852 register int hi;
853
854 {
855     register int screen_line = hi + Header_lines;
856     register int i;
857
858     if (smart_terminal)
859     {
860         if (hi < last_hi)
861         {
862             /* need to blank the remainder of the screen */
863             /* but only if there is any screen left below this line */
864             if (lastline + 1 < screen_length)
865             {
866                 /* efficiently move to the end of currently displayed info */
867                 if (screen_line - lastline < 5)
868                 {
869                     while (lastline < screen_line)
870                     {
871                         putchar('\n');
872                         lastline++;
873                     }
874                 }
875                 else
876                 {
877                     Move_to(0, screen_line);
878                     lastline = screen_line;
879                 }
880
881                 if (clear_to_end)
882                 {
883                     /* we can do this the easy way */
884                     putcap(clear_to_end);
885                 }
886                 else
887                 {
888                     /* use clear_eol on each line */
889                     i = hi;
890                     while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
891                     {
892                         putchar('\n');
893                     }
894                 }
895             }
896         }
897         last_hi = hi;
898
899         /* move the cursor to a pleasant place */
900         Move_to(x_idlecursor, y_idlecursor);
901         lastline = y_idlecursor;
902     }
903     else
904     {
905         /* separate this display from the next with some vertical room */
906         fputs("\n\n", stdout);
907     }
908 }
909
910 display_header(t)
911
912 int t;
913
914 {
915     if (t)
916     {
917         header_status = ON;
918     }
919     else if (header_status == ON)
920     {
921         header_status = ERASE;
922     }
923 }
924
925 /*VARARGS2*/
926 new_message(type, msgfmt, a1, a2, a3)
927
928 int type;
929 char *msgfmt;
930 caddr_t a1, a2, a3;
931
932 {
933     register int i;
934
935     /* first, format the message */
936     (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3);
937
938     if (msglen > 0)
939     {
940         /* message there already -- can we clear it? */
941         if (!overstrike)
942         {
943             /* yes -- write it and clear to end */
944             i = strlen(next_msg);
945             if ((type & MT_delayed) == 0)
946             {
947                 type & MT_standout ? standout(next_msg) :
948                                      fputs(next_msg, stdout);
949                 (void) clear_eol(msglen - i);
950                 msglen = i;
951                 next_msg[0] = '\0';
952             }
953         }
954     }
955     else
956     {
957         if ((type & MT_delayed) == 0)
958         {
959             type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
960             msglen = strlen(next_msg);
961             next_msg[0] = '\0';
962         }
963     }
964 }
965
966 clear_message()
967
968 {
969     if (clear_eol(msglen) == 1)
970     {
971         putchar('\r');
972     }
973 }
974
975 readline(buffer, size, numeric)
976
977 char *buffer;
978 int  size;
979 int  numeric;
980
981 {
982     register char *ptr = buffer;
983     register char ch;
984     register char cnt = 0;
985     register char maxcnt = 0;
986
987     /* allow room for null terminator */
988     size -= 1;
989
990     /* read loop */
991     while ((fflush(stdout), read(0, ptr, 1) > 0))
992     {
993         /* newline means we are done */
994         if ((ch = *ptr) == '\n' || ch == '\r')
995         {
996             break;
997         }
998
999         /* handle special editing characters */
1000         if (ch == ch_kill)
1001         {
1002             /* kill line -- account for overstriking */
1003             if (overstrike)
1004             {
1005                 msglen += maxcnt;
1006             }
1007
1008             /* return null string */
1009             *buffer = '\0';
1010             putchar('\r');
1011             return(-1);
1012         }
1013         else if (ch == ch_erase)
1014         {
1015             /* erase previous character */
1016             if (cnt <= 0)
1017             {
1018                 /* none to erase! */
1019                 putchar('\7');
1020             }
1021             else
1022             {
1023                 fputs("\b \b", stdout);
1024                 ptr--;
1025                 cnt--;
1026             }
1027         }
1028         /* check for character validity and buffer overflow */
1029         else if (cnt == size || (numeric && !isdigit(ch)) ||
1030                 !isprint(ch))
1031         {
1032             /* not legal */
1033             putchar('\7');
1034         }
1035         else
1036         {
1037             /* echo it and store it in the buffer */
1038             putchar(ch);
1039             ptr++;
1040             cnt++;
1041             if (cnt > maxcnt)
1042             {
1043                 maxcnt = cnt;
1044             }
1045         }
1046     }
1047
1048     /* all done -- null terminate the string */
1049     *ptr = '\0';
1050
1051     /* account for the extra characters in the message area */
1052     /* (if terminal overstrikes, remember the furthest they went) */
1053     msglen += overstrike ? maxcnt : cnt;
1054
1055     /* return either inputted number or string length */
1056     putchar('\r');
1057     return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
1058 }
1059
1060 /* internal support routines */
1061
1062 static int string_count(pp)
1063
1064 register char **pp;
1065
1066 {
1067     register int cnt;
1068
1069     cnt = 0;
1070     while (*pp++ != NULL)
1071     {
1072         cnt++;
1073     }
1074     return(cnt);
1075 }
1076
1077 static void summary_format(str, numbers, names)
1078
1079 char *str;
1080 int *numbers;
1081 register char **names;
1082
1083 {
1084     register char *p;
1085     register int num;
1086     register char *thisname;
1087     register int useM = No;
1088
1089     /* format each number followed by its string */
1090     p = str;
1091     while ((thisname = *names++) != NULL)
1092     {
1093         /* get the number to format */
1094         num = *numbers++;
1095
1096         /* display only non-zero numbers */
1097         if (num > 0)
1098         {
1099             /* is this number in kilobytes? */
1100             if (thisname[0] == 'K')
1101             {
1102                 /* yes: format it as a memory value */
1103                 p = strecpy(p, format_k(num));
1104
1105                 /* skip over the K, since it was included by format_k */
1106                 p = strecpy(p, thisname+1);
1107             }
1108             else
1109             {
1110                 p = strecpy(p, itoa(num));
1111                 p = strecpy(p, thisname);
1112             }
1113         }
1114
1115         /* ignore negative numbers, but display corresponding string */
1116         else if (num < 0)
1117         {
1118             p = strecpy(p, thisname);
1119         }
1120     }
1121
1122     /* if the last two characters in the string are ", ", delete them */
1123     p -= 2;
1124     if (p >= str && p[0] == ',' && p[1] == ' ')
1125     {
1126         *p = '\0';
1127     }
1128 }
1129
1130 static void line_update(old, new, start, line)
1131
1132 register char *old;
1133 register char *new;
1134 int start;
1135 int line;
1136
1137 {
1138     register int ch;
1139     register int diff;
1140     register int newcol = start + 1;
1141     register int lastcol = start;
1142     char cursor_on_line = No;
1143     char *current;
1144
1145     /* compare the two strings and only rewrite what has changed */
1146     current = old;
1147 #ifdef DEBUG
1148     fprintf(debug, "line_update, starting at %d\n", start);
1149     fputs(old, debug);
1150     fputc('\n', debug);
1151     fputs(new, debug);
1152     fputs("\n-\n", debug);
1153 #endif
1154
1155     /* start things off on the right foot                   */
1156     /* this is to make sure the invariants get set up right */
1157     if ((ch = *new++) != *old)
1158     {
1159         if (line - lastline == 1 && start == 0)
1160         {
1161             putchar('\n');
1162         }
1163         else
1164         {
1165             Move_to(start, line);
1166         }
1167         cursor_on_line = Yes;
1168         putchar(ch);
1169         *old = ch;
1170         lastcol = 1;
1171     }
1172     old++;
1173         
1174     /*
1175      *  main loop -- check each character.  If the old and new aren't the
1176      *  same, then update the display.  When the distance from the
1177      *  current cursor position to the new change is small enough,
1178      *  the characters that belong there are written to move the
1179      *  cursor over.
1180      *
1181      *  Invariants:
1182      *      lastcol is the column where the cursor currently is sitting
1183      *          (always one beyond the end of the last mismatch).
1184      */
1185     do          /* yes, a do...while */
1186     {
1187         if ((ch = *new++) != *old)
1188         {
1189             /* new character is different from old        */
1190             /* make sure the cursor is on top of this character */
1191             diff = newcol - lastcol;
1192             if (diff > 0)
1193             {
1194                 /* some motion is required--figure out which is shorter */
1195                 if (diff < 6 && cursor_on_line)
1196                 {
1197                     /* overwrite old stuff--get it out of the old buffer */
1198                     printf("%.*s", diff, &current[lastcol-start]);
1199                 }
1200                 else
1201                 {
1202                     /* use cursor addressing */
1203                     Move_to(newcol, line);
1204                     cursor_on_line = Yes;
1205                 }
1206                 /* remember where the cursor is */
1207                 lastcol = newcol + 1;
1208             }
1209             else
1210             {
1211                 /* already there, update position */
1212                 lastcol++;
1213             }
1214                 
1215             /* write what we need to */
1216             if (ch == '\0')
1217             {
1218                 /* at the end--terminate with a clear-to-end-of-line */
1219                 (void) clear_eol(strlen(old));
1220             }
1221             else
1222             {
1223                 /* write the new character */
1224                 putchar(ch);
1225             }
1226             /* put the new character in the screen buffer */
1227             *old = ch;
1228         }
1229             
1230         /* update working column and screen buffer pointer */
1231         newcol++;
1232         old++;
1233             
1234     } while (ch != '\0');
1235
1236     /* zero out the rest of the line buffer -- MUST BE DONE! */
1237     diff = display_width - newcol;
1238     if (diff > 0)
1239     {
1240         memzero(old, diff);
1241     }
1242
1243     /* remember where the current line is */
1244     if (cursor_on_line)
1245     {
1246         lastline = line;
1247     }
1248 }
1249
1250 /*
1251  *  printable(str) - make the string pointed to by "str" into one that is
1252  *      printable (i.e.: all ascii), by converting all non-printable
1253  *      characters into '?'.  Replacements are done in place and a pointer
1254  *      to the original buffer is returned.
1255  */
1256
1257 char *printable(str)
1258
1259 char *str;
1260
1261 {
1262     register char *ptr;
1263     register char ch;
1264
1265     ptr = str;
1266     while ((ch = *ptr) != '\0')
1267     {
1268         if (!isprint(ch))
1269         {
1270             *ptr = '?';
1271         }
1272         ptr++;
1273     }
1274     return(str);
1275 }
1276
1277 i_uptime(bt, tod)
1278
1279 struct timeval* bt;
1280 time_t *tod;
1281
1282 {
1283     time_t uptime;
1284     int days, hrs, mins, secs;
1285
1286     if (bt->tv_sec != -1) {
1287         uptime = *tod - bt->tv_sec;
1288         days = uptime / 86400;
1289         uptime %= 86400;
1290         hrs = uptime / 3600;
1291         uptime %= 3600;
1292         mins = uptime / 60;
1293         secs = uptime % 60;
1294
1295         /*
1296          *  Display the uptime.
1297          */
1298
1299         if (smart_terminal)
1300         {
1301             Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
1302         }
1303         else
1304         {
1305             fputs(" ", stdout);
1306         }
1307         printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
1308     }
1309 }