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