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