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