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