Annotation of src/usr.bin/top/display.c, Revision 1.56
1.55 cheloha 1: /* $OpenBSD: display.c,v 1.54 2018/01/04 17:44:20 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>
1.51 guenther 49: #include <sys/time.h>
1.20 millert 50: #include <sys/sched.h>
1.25 otto 51: #include <curses.h>
52: #include <errno.h>
1.2 downsj 53: #include <stdio.h>
1.1 downsj 54: #include <ctype.h>
1.20 millert 55: #include <err.h>
1.25 otto 56: #include <signal.h>
1.2 downsj 57: #include <stdlib.h>
58: #include <string.h>
59: #include <unistd.h>
1.1 downsj 60:
61: #include "screen.h" /* interface to screen package */
62: #include "layout.h" /* defines for screen position layout */
63: #include "display.h"
64: #include "top.h"
65: #include "boolean.h"
66: #include "machine.h" /* we should eliminate this!!! */
67: #include "utils.h"
68:
69: #ifdef DEBUG
1.14 deraadt 70: FILE *debug;
1.1 downsj 71: #endif
72:
1.14 deraadt 73: static int display_width = MAX_COLS;
74:
1.20 millert 75: static char *cpustates_tag(int);
1.14 deraadt 76: static int string_count(char **);
1.16 millert 77: static void summary_format(char *, size_t, int *, char **);
1.32 otto 78: static int readlinedumb(char *, int);
1.2 downsj 79:
1.1 downsj 80: #define lineindex(l) ((l)*display_width)
81:
1.18 otto 82: /* things initialized by display_init and used throughout */
1.1 downsj 83:
84: /* buffer of proc information lines for display updating */
1.14 deraadt 85: char *screenbuf = NULL;
1.1 downsj 86:
1.14 deraadt 87: static char **procstate_names;
88: static char **cpustate_names;
89: static char **memory_names;
90:
91: static int num_cpustates;
92:
93: static int *cpustate_columns;
94: static int cpustate_total_length;
95:
1.20 millert 96: /* display ips */
97: int y_mem;
98: int y_message;
99: int y_header;
100: int y_idlecursor;
101: int y_procs;
102: extern int ncpu;
1.34 tedu 103: extern int combine_cpus;
1.49 mpi 104: extern struct process_select ps;
1.20 millert 105:
1.33 otto 106: int header_status = Yes;
1.1 downsj 107:
1.25 otto 108: static int
109: empty(void)
110: {
111: return OK;
112: }
113:
114: static int
115: myfputs(const char *s)
116: {
117: return fputs(s, stdout);
118: }
119:
120: static int (*addstrp)(const char *);
121: static int (*printwp)(const char *, ...);
122: static int (*standoutp)(void);
123: static int (*standendp)(void);
124:
1.12 pvalchev 125: int
126: display_resize(void)
1.1 downsj 127: {
1.56 ! cheloha 128: int display_lines;
! 129: int cpu_lines = (combine_cpus ? 1 : ncpu);
1.34 tedu 130:
131: y_mem = 2 + cpu_lines;
132: y_header = 4 + cpu_lines;
133: y_procs = 5 + cpu_lines;
1.1 downsj 134:
1.14 deraadt 135: /* calculate the current dimensions */
136: /* if operating in "dumb" mode, we only need one line */
1.37 otto 137: display_lines = smart_terminal ? screen_length - y_procs : 1;
1.14 deraadt 138:
1.56 ! cheloha 139: y_idlecursor = y_message = 3 + (combine_cpus ? 1 : ncpu);
1.30 otto 140: if (screen_length <= y_message)
141: y_idlecursor = y_message = screen_length - 1;
142:
1.14 deraadt 143: /*
144: * we don't want more than MAX_COLS columns, since the
145: * machine-dependent modules make static allocations based on
146: * MAX_COLS and we don't want to run off the end of their buffers
147: */
148: display_width = screen_width;
149: if (display_width >= MAX_COLS)
150: display_width = MAX_COLS - 1;
151:
1.42 lum 152: if (display_lines < 0)
153: display_lines = 0;
154:
1.14 deraadt 155: /* return number of lines available */
156: /* for dumb terminals, pretend like we can show any amount */
157: return (smart_terminal ? display_lines : Largest);
1.1 downsj 158: }
159:
1.12 pvalchev 160: int
1.14 deraadt 161: display_init(struct statics * statics)
1.1 downsj 162: {
1.34 tedu 163: int display_lines, *ip, i;
1.14 deraadt 164: char **pp;
165:
1.25 otto 166: if (smart_terminal) {
167: addstrp = addstr;
1.45 guenther 168: printwp = printw;
1.25 otto 169: standoutp = standout;
170: standendp = standend;
171: } else {
172: addstrp = myfputs;
173: printwp = printf;
174: standoutp = empty;
175: standendp = empty;
176: }
177:
1.14 deraadt 178: /* call resize to do the dirty work */
179: display_lines = display_resize();
180:
181: /* only do the rest if we need to */
1.30 otto 182: /* save pointers and allocate space for names */
183: procstate_names = statics->procstate_names;
184:
185: cpustate_names = statics->cpustate_names;
186: num_cpustates = string_count(cpustate_names);
187:
188: cpustate_columns = calloc(num_cpustates, sizeof(int));
189: if (cpustate_columns == NULL)
190: err(1, NULL);
191:
192: memory_names = statics->memory_names;
193:
194: /* calculate starting columns where needed */
195: cpustate_total_length = 0;
196: pp = cpustate_names;
197: ip = cpustate_columns;
198: while (*pp != NULL) {
199: if ((i = strlen(*pp++)) > 0) {
200: *ip++ = cpustate_total_length;
201: cpustate_total_length += i + 8;
1.20 millert 202: }
1.30 otto 203: }
1.14 deraadt 204:
205: /* return number of lines available */
206: return (display_lines);
1.1 downsj 207: }
1.54 deraadt 208:
1.50 tedu 209: static void
210: format_uptime(char *buf, size_t buflen)
211: {
1.53 cheloha 212: time_t uptime;
1.50 tedu 213: int days, hrs, mins;
1.53 cheloha 214: struct timespec boottime;
1.50 tedu 215:
216: /*
217: * Print how long system has been up.
218: */
1.53 cheloha 219: if (clock_gettime(CLOCK_BOOTTIME, &boottime) != -1) {
220: uptime = boottime.tv_sec;
1.50 tedu 221: uptime += 30;
222: days = uptime / (3600 * 24);
223: uptime %= (3600 * 24);
224: hrs = uptime / 3600;
225: uptime %= 3600;
226: mins = uptime / 60;
227: if (days > 0)
228: snprintf(buf, buflen, "up %d day%s, %2d:%02d",
229: days, days > 1 ? "s" : "", hrs, mins);
230: else
231: snprintf(buf, buflen, "up %2d:%02d",
232: hrs, mins);
233: }
234: }
235:
1.1 downsj 236:
1.12 pvalchev 237: void
238: i_loadave(pid_t mpid, double *avenrun)
1.1 downsj 239: {
1.30 otto 240: if (screen_length > 1 || !smart_terminal) {
241: int i;
1.14 deraadt 242:
1.30 otto 243: move(0, 0);
244: clrtoeol();
1.1 downsj 245:
1.30 otto 246: addstrp("load averages");
247: /* mpid == -1 implies this system doesn't have an _mpid */
248: if (mpid != -1)
249: printwp("last pid: %5ld; ", (long) mpid);
1.1 downsj 250:
1.30 otto 251: for (i = 0; i < 3; i++)
252: printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]);
253: }
1.50 tedu 254:
1.1 downsj 255: }
256:
1.14 deraadt 257: /*
258: * Display the current time.
259: * "ctime" always returns a string that looks like this:
260: *
261: * Sun Sep 16 01:03:52 1973
262: * 012345678901234567890123
263: * 1 2
264: *
265: * We want indices 11 thru 18 (length 8).
266: */
267:
1.12 pvalchev 268: void
1.14 deraadt 269: i_timeofday(time_t * tod)
1.1 downsj 270: {
1.40 otto 271: static char buf[30];
272:
273: if (buf[0] == '\0')
274: gethostname(buf, sizeof(buf));
275:
1.30 otto 276: if (screen_length > 1 || !smart_terminal) {
277: if (smart_terminal) {
1.40 otto 278: move(0, screen_width - 8 - strlen(buf) - 1);
1.30 otto 279: } else {
280: if (fputs(" ", stdout) == EOF)
281: exit(1);
282: }
1.1 downsj 283: #ifdef DEBUG
1.30 otto 284: {
285: char *foo;
286: foo = ctime(tod);
287: addstrp(foo);
288: }
289: #endif
1.40 otto 290: printwp("%s %-8.8s", buf, &(ctime(tod)[11]));
1.30 otto 291: putn();
1.14 deraadt 292: }
1.1 downsj 293: }
294:
295: /*
1.44 guenther 296: * *_procstates(total, states, threads) - print the process/thread summary line
1.1 downsj 297: *
298: * Assumptions: cursor is at the beginning of the line on entry
299: */
1.12 pvalchev 300: void
1.44 guenther 301: i_procstates(int total, int *states, int threads)
1.1 downsj 302: {
1.30 otto 303: if (screen_length > 2 || !smart_terminal) {
304: char procstates_buffer[MAX_COLS];
1.50 tedu 305: char uptime[40];
1.30 otto 306:
307: move(1, 0);
308: clrtoeol();
1.44 guenther 309: /* write current number of procs and remember the value */
310: if (threads == Yes)
311: printwp("%d threads: ", total);
312: else
313: printwp("%d processes: ", total);
1.14 deraadt 314:
1.30 otto 315: /* format and print the process state summary */
1.44 guenther 316: summary_format(procstates_buffer, sizeof(procstates_buffer),
317: states, procstate_names);
1.1 downsj 318:
1.30 otto 319: addstrp(procstates_buffer);
1.50 tedu 320:
321: format_uptime(uptime, sizeof(uptime));
322: if (smart_terminal)
323: move(1, screen_width - strlen(uptime));
324: else
325: printwp(" ");
326: printwp("%s", uptime);
1.30 otto 327: putn();
328: }
1.1 downsj 329: }
330:
331: /*
1.44 guenther 332: * *_cpustates(states) - print the cpu state percentages
1.1 downsj 333: *
334: * Assumptions: cursor is on the PREVIOUS line
335: */
336:
337: /* cpustates_tag() calculates the correct tag to use to label the line */
338:
1.12 pvalchev 339: static char *
1.20 millert 340: cpustates_tag(int cpu)
1.1 downsj 341: {
1.30 otto 342: if (screen_length > 3 || !smart_terminal) {
343: static char *tag;
344: static int cpulen, old_width;
345: int i;
346:
347: if (cpulen == 0 && ncpu > 1) {
348: /* compute length of the cpu string */
349: for (i = ncpu; i > 0; cpulen++, i /= 10)
350: continue;
351: }
1.20 millert 352:
1.30 otto 353: if (old_width == screen_width) {
354: if (ncpu > 1) {
355: /* just store the cpu number in the tag */
356: i = tag[3 + cpulen];
357: snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu);
358: tag[3 + cpulen] = i;
359: }
360: } else {
361: /*
362: * use a long tag if it will fit, otherwise use short one.
363: */
364: free(tag);
365: if (cpustate_total_length + 10 + cpulen >= screen_width)
366: i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu);
367: else
368: i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu);
369: if (i == -1)
370: tag = NULL;
371: else
372: old_width = screen_width;
1.20 millert 373: }
1.30 otto 374: return (tag);
375: } else
1.34 tedu 376: return ("\0");
1.1 downsj 377: }
378:
1.12 pvalchev 379: void
1.56 ! cheloha 380: i_cpustates(int64_t *ostates)
1.1 downsj 381: {
1.56 ! cheloha 382: int i, first, cpu;
1.34 tedu 383: double value;
1.20 millert 384: int64_t *states;
1.34 tedu 385: char **names, *thisname;
386:
387: if (combine_cpus) {
388: static double *values;
389: if (!values) {
390: values = calloc(num_cpustates, sizeof(*values));
391: if (!values)
392: err(1, NULL);
393: }
394: memset(values, 0, num_cpustates * sizeof(*values));
395: for (cpu = 0; cpu < ncpu; cpu++) {
396: names = cpustate_names;
397: states = ostates + (CPUSTATES * cpu);
398: i = 0;
399: while ((thisname = *names++) != NULL) {
400: if (*thisname != '\0') {
401: /* retrieve the value and remember it */
402: values[i++] += *states++;
403: }
404: }
405: }
406: if (screen_length > 2 || !smart_terminal) {
407: names = cpustate_names;
408: i = 0;
409: first = 0;
410: move(2, 0);
411: clrtoeol();
1.56 ! cheloha 412: printwp("%-3d CPUs: ", ncpu);
1.14 deraadt 413:
1.34 tedu 414: while ((thisname = *names++) != NULL) {
415: if (*thisname != '\0') {
1.56 ! cheloha 416: value = values[i++] / ncpu;
1.34 tedu 417: /* if percentage is >= 1000, print it as 100% */
418: printwp((value >= 1000 ? "%s%4.0f%% %s" :
419: "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
420: value / 10., thisname);
421: }
422: }
423: putn();
424: }
425: return;
426: }
1.20 millert 427: for (cpu = 0; cpu < ncpu; cpu++) {
428: /* now walk thru the names and print the line */
429: names = cpustate_names;
1.34 tedu 430: first = 0;
1.20 millert 431: states = ostates + (CPUSTATES * cpu);
1.30 otto 432:
1.56 ! cheloha 433: if (screen_length > 2 + cpu || !smart_terminal) {
1.30 otto 434: move(2 + cpu, 0);
435: clrtoeol();
436: addstrp(cpustates_tag(cpu));
437:
438: while ((thisname = *names++) != NULL) {
439: if (*thisname != '\0') {
440: /* retrieve the value and remember it */
441: value = *states++;
442:
443: /* if percentage is >= 1000, print it as 100% */
444: printwp((value >= 1000 ? "%s%4.0f%% %s" :
1.34 tedu 445: "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
446: value / 10., thisname);
1.30 otto 447: }
1.20 millert 448: }
1.30 otto 449: putn();
1.14 deraadt 450: }
1.1 downsj 451: }
452: }
453:
454: /*
455: * *_memory(stats) - print "Memory: " followed by the memory summary string
456: */
1.12 pvalchev 457: void
458: i_memory(int *stats)
1.1 downsj 459: {
1.30 otto 460: if (screen_length > y_mem || !smart_terminal) {
461: char memory_buffer[MAX_COLS];
462:
463: move(y_mem, 0);
464: clrtoeol();
465: addstrp("Memory: ");
1.25 otto 466:
1.30 otto 467: /* format and print the memory summary */
468: summary_format(memory_buffer, sizeof(memory_buffer), stats,
469: memory_names);
470: addstrp(memory_buffer);
471: putn();
472: }
1.1 downsj 473: }
474:
475: /*
476: * *_message() - print the next pending message line, or erase the one
477: * that is there.
478: */
479:
480: /*
481: * i_message is funny because it gets its message asynchronously (with
482: * respect to screen updates).
483: */
484:
1.14 deraadt 485: static char next_msg[MAX_COLS + 5];
1.31 otto 486: static int msgon = 0;
1.1 downsj 487:
1.12 pvalchev 488: void
489: i_message(void)
1.1 downsj 490: {
1.25 otto 491: move(y_message, 0);
1.14 deraadt 492: if (next_msg[0] != '\0') {
1.25 otto 493: standoutp();
494: addstrp(next_msg);
495: standendp();
496: clrtoeol();
1.31 otto 497: msgon = TRUE;
1.14 deraadt 498: next_msg[0] = '\0';
1.31 otto 499: } else if (msgon) {
1.25 otto 500: clrtoeol();
1.31 otto 501: msgon = FALSE;
1.14 deraadt 502: }
1.1 downsj 503: }
504:
505: /*
506: * *_header(text) - print the header for the process area
507: */
508:
1.12 pvalchev 509: void
510: i_header(char *text)
1.1 downsj 511: {
1.33 otto 512: if (header_status == Yes && (screen_length > y_header
1.30 otto 513: || !smart_terminal)) {
1.25 otto 514: if (!smart_terminal) {
515: putn();
516: if (fputs(text, stdout) == EOF)
517: exit(1);
518: putn();
519: } else {
520: move(y_header, 0);
521: clrtoeol();
522: addstrp(text);
523: }
1.14 deraadt 524: }
1.1 downsj 525: }
526:
527: /*
528: * *_process(line, thisline) - print one process line
529: */
530:
1.12 pvalchev 531: void
1.25 otto 532: i_process(int line, char *thisline, int hl)
1.1 downsj 533: {
1.14 deraadt 534: /* make sure we are on the correct line */
1.25 otto 535: move(y_procs + line, 0);
1.1 downsj 536:
1.14 deraadt 537: /* truncate the line to conform to our current screen width */
538: thisline[display_width] = '\0';
1.1 downsj 539:
1.14 deraadt 540: /* write the line out */
1.25 otto 541: if (hl && smart_terminal)
542: standoutp();
543: addstrp(thisline);
544: if (hl && smart_terminal)
545: standendp();
546: putn();
547: clrtoeol();
1.1 downsj 548: }
549:
1.12 pvalchev 550: void
1.27 deraadt 551: u_endscreen(void)
1.1 downsj 552: {
1.14 deraadt 553: if (smart_terminal) {
1.25 otto 554: clrtobot();
1.14 deraadt 555: /* move the cursor to a pleasant place */
1.25 otto 556: move(y_idlecursor, x_idlecursor);
1.14 deraadt 557: } else {
558: /*
559: * separate this display from the next with some vertical
560: * room
561: */
562: if (fputs("\n\n", stdout) == EOF)
563: exit(1);
1.1 downsj 564: }
565: }
566:
1.12 pvalchev 567: void
1.33 otto 568: display_header(int status)
1.1 downsj 569: {
1.33 otto 570: header_status = status;
1.1 downsj 571: }
572:
1.12 pvalchev 573: void
1.14 deraadt 574: new_message(int type, const char *msgfmt,...)
575: {
576: va_list ap;
577:
578: va_start(ap, msgfmt);
579: /* first, format the message */
580: vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);
581: va_end(ap);
582:
1.31 otto 583: if (next_msg[0] != '\0') {
1.14 deraadt 584: /* message there already -- can we clear it? */
1.25 otto 585: /* yes -- write it and clear to end */
586: if ((type & MT_delayed) == 0) {
587: move(y_message, 0);
588: if (type & MT_standout)
589: standoutp();
590: addstrp(next_msg);
591: if (type & MT_standout)
592: standendp();
593: clrtoeol();
1.31 otto 594: msgon = TRUE;
1.14 deraadt 595: next_msg[0] = '\0';
1.31 otto 596: if (smart_terminal)
597: refresh();
1.5 deraadt 598: }
1.1 downsj 599: }
600: }
601:
1.12 pvalchev 602: void
603: clear_message(void)
1.1 downsj 604: {
1.25 otto 605: move(y_message, 0);
606: clrtoeol();
1.1 downsj 607: }
608:
1.25 otto 609:
610: static int
1.32 otto 611: readlinedumb(char *buffer, int size)
1.1 downsj 612: {
1.14 deraadt 613: char *ptr = buffer, ch, cnt = 0, maxcnt = 0;
1.17 deraadt 614: extern volatile sig_atomic_t leaveflag;
615: ssize_t len;
1.14 deraadt 616:
617: /* allow room for null terminator */
618: size -= 1;
619:
620: /* read loop */
1.19 deraadt 621: while ((fflush(stdout), (len = read(STDIN_FILENO, ptr, 1)) > 0)) {
1.17 deraadt 622:
623: if (len == 0 || leaveflag) {
624: end_screen();
625: exit(0);
626: }
627:
1.14 deraadt 628: /* newline means we are done */
629: if ((ch = *ptr) == '\n')
630: break;
631:
632: /* handle special editing characters */
633: if (ch == ch_kill) {
634: /* return null string */
635: *buffer = '\0';
1.25 otto 636: putr();
1.14 deraadt 637: return (-1);
638: } else if (ch == ch_erase) {
639: /* erase previous character */
640: if (cnt <= 0) {
641: /* none to erase! */
642: if (putchar('\7') == EOF)
643: exit(1);
644: } else {
645: if (fputs("\b \b", stdout) == EOF)
646: exit(1);
647: ptr--;
648: cnt--;
649: }
650: }
651: /* check for character validity and buffer overflow */
1.46 deraadt 652: else if (cnt == size || !isprint((unsigned char)ch)) {
1.14 deraadt 653: /* not legal */
654: if (putchar('\7') == EOF)
655: exit(1);
656: } else {
657: /* echo it and store it in the buffer */
658: if (putchar(ch) == EOF)
659: exit(1);
660: ptr++;
661: cnt++;
662: if (cnt > maxcnt)
663: maxcnt = cnt;
664: }
1.1 downsj 665: }
666:
1.14 deraadt 667: /* all done -- null terminate the string */
668: *ptr = '\0';
669:
670: /* return either inputted number or string length */
1.25 otto 671: putr();
1.32 otto 672: return (cnt == 0 ? -1 : cnt);
1.25 otto 673: }
674:
675: int
1.32 otto 676: readline(char *buffer, int size)
1.25 otto 677: {
678: size_t cnt;
679:
680: /* allow room for null terminator */
681: size -= 1;
682:
1.47 guenther 683: if (smart_terminal) {
684: int y, x;
685: getyx(stdscr, y, x);
686: while (getnstr(buffer, size) == KEY_RESIZE)
687: move(y, x);
688: } else
1.32 otto 689: return readlinedumb(buffer, size);
1.25 otto 690:
691: cnt = strlen(buffer);
692: if (cnt > 0 && buffer[cnt - 1] == '\n')
693: buffer[cnt - 1] = '\0';
1.32 otto 694: return (cnt == 0 ? -1 : cnt);
1.1 downsj 695: }
696:
697: /* internal support routines */
1.12 pvalchev 698: static int
699: string_count(char **pp)
1.1 downsj 700: {
1.14 deraadt 701: int cnt;
1.1 downsj 702:
1.14 deraadt 703: cnt = 0;
704: while (*pp++ != NULL)
705: cnt++;
706: return (cnt);
1.1 downsj 707: }
708:
1.16 millert 709: #define COPYLEFT(to, from) \
710: do { \
711: len = strlcpy((to), (from), left); \
712: if (len >= left) \
713: return; \
714: p += len; \
715: left -= len; \
716: } while (0)
717:
1.12 pvalchev 718: static void
1.16 millert 719: summary_format(char *buf, size_t left, int *numbers, char **names)
1.1 downsj 720: {
1.14 deraadt 721: char *p, *thisname;
1.16 millert 722: size_t len;
1.14 deraadt 723: int num;
1.1 downsj 724:
1.14 deraadt 725: /* format each number followed by its string */
1.16 millert 726: p = buf;
1.14 deraadt 727: while ((thisname = *names++) != NULL) {
728: /* get the number to format */
729: num = *numbers++;
730:
731: if (num >= 0) {
732: /* is this number in kilobytes? */
733: if (thisname[0] == 'K') {
734: /* yes: format it as a memory value */
1.16 millert 735: COPYLEFT(p, format_k(num));
1.14 deraadt 736:
737: /*
738: * skip over the K, since it was included by
739: * format_k
740: */
1.16 millert 741: COPYLEFT(p, thisname + 1);
1.14 deraadt 742: } else if (num > 0) {
1.16 millert 743: len = snprintf(p, left, "%d%s", num, thisname);
744: if (len == (size_t)-1 || len >= left)
745: return;
746: p += len;
747: left -= len;
1.14 deraadt 748: }
1.16 millert 749: } else {
750: /*
751: * Ignore negative numbers, but display corresponding
752: * string.
753: */
754: COPYLEFT(p, thisname);
1.14 deraadt 755: }
1.1 downsj 756: }
757:
1.14 deraadt 758: /* if the last two characters in the string are ", ", delete them */
759: p -= 2;
1.16 millert 760: if (p >= buf && p[0] == ',' && p[1] == ' ')
1.14 deraadt 761: *p = '\0';
1.1 downsj 762: }
763:
764: /*
765: * printable(str) - make the string pointed to by "str" into one that is
766: * printable (i.e.: all ascii), by converting all non-printable
767: * characters into '?'. Replacements are done in place and a pointer
768: * to the original buffer is returned.
769: */
1.12 pvalchev 770: char *
771: printable(char *str)
1.1 downsj 772: {
1.14 deraadt 773: char *ptr, ch;
1.1 downsj 774:
1.14 deraadt 775: ptr = str;
776: while ((ch = *ptr) != '\0') {
1.46 deraadt 777: if (!isprint((unsigned char)ch))
1.14 deraadt 778: *ptr = '?';
779: ptr++;
1.1 downsj 780: }
1.14 deraadt 781: return (str);
1.25 otto 782: }
783:
784:
785: /*
786: * show_help() - display the help screen; invoked in response to
787: * either 'h' or '?'.
788: */
789: void
790: show_help(void)
791: {
792: if (smart_terminal) {
793: clear();
794: nl();
795: }
796: printwp("These single-character commands are available:\n"
797: "\n"
798: "^L - redraw screen\n"
1.39 lum 799: "<space> - update screen\n"
1.25 otto 800: "+ - reset any g, p, or u filters\n"
1.35 jmc 801: "1 - display CPU statistics on a single line\n"
1.25 otto 802: "C - toggle the display of command line arguments\n"
803: "d count - show `count' displays, then exit\n"
804: "e - list errors generated by last \"kill\" or \"renice\" command\n"
1.41 jsing 805: "g string - filter on command name (g+ selects all commands)\n"
1.25 otto 806: "h | ? - help; show this text\n"
1.41 jsing 807: "H - toggle the display of threads\n"
1.25 otto 808: "I | i - toggle the display of idle processes\n"
809: "k [-sig] pid - send signal `-sig' to process `pid'\n"
810: "n|# count - show `count' processes\n"
1.36 tedu 811: "o field - specify sort order (size, res, cpu, time, pri, pid, command)\n"
1.25 otto 812: "P pid - highlight process `pid' (P+ switches highlighting off)\n"
813: "p pid - display process by `pid' (p+ selects all processes)\n"
814: "q - quit\n"
815: "r count pid - renice process `pid' to nice value `count'\n"
816: "S - toggle the display of system processes\n"
817: "s time - change delay between displays to `time' seconds\n"
1.43 brynet 818: "u [-]user - show processes for `user' (u+ shows all, u -user hides user)\n"
1.25 otto 819: "\n");
820:
821: if (smart_terminal) {
822: nonl();
823: refresh();
824: }
825: }
826:
827: /*
828: * show_errors() - display on stdout the current log of errors.
829: */
830: void
831: show_errors(void)
832: {
833: struct errs *errp = errs;
834: int cnt = 0;
835:
836: if (smart_terminal) {
837: clear();
838: nl();
839: }
840: printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
841: while (cnt++ < errcnt) {
1.38 otto 842: printwp("%5s: %s\n", errp->arg,
1.25 otto 843: errp->err == 0 ? "Not a number" : strerror(errp->err));
844: errp++;
845: }
1.38 otto 846: printwp("\n");
1.25 otto 847: if (smart_terminal) {
848: nonl();
849: refresh();
850: }
851: }
852:
853: void
854: anykey(void)
855: {
856: int ch;
1.28 deraadt 857: ssize_t len;
1.25 otto 858:
859: standoutp();
860: addstrp("Hit any key to continue: ");
861: standendp();
862: if (smart_terminal)
863: refresh();
1.52 deraadt 864: else
1.25 otto 865: fflush(stdout);
866: while (1) {
867: len = read(STDIN_FILENO, &ch, 1);
868: if (len == -1 && errno == EINTR)
869: continue;
870: if (len == 0)
871: exit(1);
872: break;
873: }
1.1 downsj 874: }