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