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