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