Annotation of src/usr.bin/top/display.c, Revision 1.67
1.67 ! cheloha 1: /* $OpenBSD: display.c,v 1.66 2022/08/08 16:54:09 cheloha 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.60 kn 60: #include <stdbool.h>
1.1 downsj 61:
62: #include "screen.h" /* interface to screen package */
63: #include "layout.h" /* defines for screen position layout */
64: #include "display.h"
65: #include "top.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.57 cheloha 103: extern int ncpuonline;
1.34 tedu 104: extern int combine_cpus;
1.49 mpi 105: extern struct process_select ps;
1.20 millert 106:
1.60 kn 107: int header_status = true;
1.1 downsj 108:
1.25 otto 109: static int
110: empty(void)
111: {
112: return OK;
113: }
114:
115: static int
116: myfputs(const char *s)
117: {
118: return fputs(s, stdout);
119: }
120:
121: static int (*addstrp)(const char *);
122: static int (*printwp)(const char *, ...);
123: static int (*standoutp)(void);
124: static int (*standendp)(void);
125:
1.12 pvalchev 126: int
127: display_resize(void)
1.1 downsj 128: {
1.57 cheloha 129: int cpu_lines, display_lines;
1.34 tedu 130:
1.57 cheloha 131: ncpuonline = getncpuonline();
132: cpu_lines = (combine_cpus ? 1 : ncpuonline);
1.34 tedu 133: y_mem = 2 + cpu_lines;
134: y_header = 4 + cpu_lines;
135: y_procs = 5 + cpu_lines;
1.1 downsj 136:
1.14 deraadt 137: /* calculate the current dimensions */
138: /* if operating in "dumb" mode, we only need one line */
1.37 otto 139: display_lines = smart_terminal ? screen_length - y_procs : 1;
1.14 deraadt 140:
1.57 cheloha 141: y_idlecursor = y_message = 3 + (combine_cpus ? 1 : ncpuonline);
1.30 otto 142: if (screen_length <= y_message)
143: y_idlecursor = y_message = screen_length - 1;
144:
1.14 deraadt 145: /*
146: * we don't want more than MAX_COLS columns, since the
147: * machine-dependent modules make static allocations based on
148: * MAX_COLS and we don't want to run off the end of their buffers
149: */
150: display_width = screen_width;
151: if (display_width >= MAX_COLS)
152: display_width = MAX_COLS - 1;
153:
1.42 lum 154: if (display_lines < 0)
155: display_lines = 0;
156:
1.14 deraadt 157: /* return number of lines available */
158: /* for dumb terminals, pretend like we can show any amount */
159: return (smart_terminal ? display_lines : Largest);
1.1 downsj 160: }
161:
1.12 pvalchev 162: int
1.14 deraadt 163: display_init(struct statics * statics)
1.1 downsj 164: {
1.34 tedu 165: int display_lines, *ip, i;
1.14 deraadt 166: char **pp;
167:
1.25 otto 168: if (smart_terminal) {
169: addstrp = addstr;
1.45 guenther 170: printwp = printw;
1.25 otto 171: standoutp = standout;
172: standendp = standend;
173: } else {
174: addstrp = myfputs;
175: printwp = printf;
176: standoutp = empty;
177: standendp = empty;
178: }
179:
1.14 deraadt 180: /* call resize to do the dirty work */
181: display_lines = display_resize();
182:
183: /* only do the rest if we need to */
1.30 otto 184: /* save pointers and allocate space for names */
185: procstate_names = statics->procstate_names;
186:
187: cpustate_names = statics->cpustate_names;
188: num_cpustates = string_count(cpustate_names);
189:
190: cpustate_columns = calloc(num_cpustates, sizeof(int));
191: if (cpustate_columns == NULL)
192: err(1, NULL);
193:
194: memory_names = statics->memory_names;
195:
196: /* calculate starting columns where needed */
197: cpustate_total_length = 0;
198: pp = cpustate_names;
199: ip = cpustate_columns;
200: while (*pp != NULL) {
201: if ((i = strlen(*pp++)) > 0) {
202: *ip++ = cpustate_total_length;
203: cpustate_total_length += i + 8;
1.20 millert 204: }
1.30 otto 205: }
1.14 deraadt 206:
207: /* return number of lines available */
208: return (display_lines);
1.1 downsj 209: }
1.54 deraadt 210:
1.66 cheloha 211: /*
212: * Print the time elapsed since the system booted.
213: */
1.50 tedu 214: static void
215: format_uptime(char *buf, size_t buflen)
216: {
1.66 cheloha 217: struct timespec boottime;
1.53 cheloha 218: time_t uptime;
1.66 cheloha 219: unsigned int days, hrs, mins, secs;
220:
221: if (clock_gettime(CLOCK_BOOTTIME, &boottime) == -1)
222: err(1, "clock_gettime");
1.50 tedu 223:
1.66 cheloha 224: uptime = boottime.tv_sec;
225: days = uptime / (3600 * 24);
226: uptime %= (3600 * 24);
227: hrs = uptime / 3600;
228: uptime %= 3600;
229: mins = uptime / 60;
230: secs = uptime % 60;
231: snprintf(buf, buflen, "up %u days %02u:%02u:%02u",
232: days, hrs, mins, secs);
1.50 tedu 233: }
234:
1.1 downsj 235:
1.12 pvalchev 236: void
1.67 ! cheloha 237: i_loadave(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: for (i = 0; i < 3; i++)
247: printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]);
248: }
1.50 tedu 249:
1.1 downsj 250: }
251:
1.14 deraadt 252: /*
253: * Display the current time.
254: * "ctime" always returns a string that looks like this:
255: *
256: * Sun Sep 16 01:03:52 1973
257: * 012345678901234567890123
258: * 1 2
259: *
260: * We want indices 11 thru 18 (length 8).
261: */
262:
1.12 pvalchev 263: void
1.14 deraadt 264: i_timeofday(time_t * tod)
1.1 downsj 265: {
1.40 otto 266: static char buf[30];
267:
268: if (buf[0] == '\0')
269: gethostname(buf, sizeof(buf));
270:
1.30 otto 271: if (screen_length > 1 || !smart_terminal) {
272: if (smart_terminal) {
1.40 otto 273: move(0, screen_width - 8 - strlen(buf) - 1);
1.30 otto 274: } else {
275: if (fputs(" ", stdout) == EOF)
276: exit(1);
277: }
1.1 downsj 278: #ifdef DEBUG
1.30 otto 279: {
280: char *foo;
281: foo = ctime(tod);
282: addstrp(foo);
283: }
284: #endif
1.40 otto 285: printwp("%s %-8.8s", buf, &(ctime(tod)[11]));
1.30 otto 286: putn();
1.14 deraadt 287: }
1.1 downsj 288: }
289:
290: /*
1.44 guenther 291: * *_procstates(total, states, threads) - print the process/thread summary line
1.1 downsj 292: *
293: * Assumptions: cursor is at the beginning of the line on entry
294: */
1.12 pvalchev 295: void
1.44 guenther 296: i_procstates(int total, int *states, int threads)
1.1 downsj 297: {
1.30 otto 298: if (screen_length > 2 || !smart_terminal) {
299: char procstates_buffer[MAX_COLS];
1.50 tedu 300: char uptime[40];
1.30 otto 301:
302: move(1, 0);
303: clrtoeol();
1.44 guenther 304: /* write current number of procs and remember the value */
1.60 kn 305: printwp("%d %s: ", total, threads ? "threads" : "processes");
1.14 deraadt 306:
1.30 otto 307: /* format and print the process state summary */
1.44 guenther 308: summary_format(procstates_buffer, sizeof(procstates_buffer),
309: states, procstate_names);
1.1 downsj 310:
1.30 otto 311: addstrp(procstates_buffer);
1.50 tedu 312:
313: format_uptime(uptime, sizeof(uptime));
314: if (smart_terminal)
315: move(1, screen_width - strlen(uptime));
316: else
317: printwp(" ");
318: printwp("%s", uptime);
1.30 otto 319: putn();
320: }
1.1 downsj 321: }
322:
323: /*
1.44 guenther 324: * *_cpustates(states) - print the cpu state percentages
1.1 downsj 325: *
326: * Assumptions: cursor is on the PREVIOUS line
327: */
328:
329: /* cpustates_tag() calculates the correct tag to use to label the line */
330:
1.12 pvalchev 331: static char *
1.20 millert 332: cpustates_tag(int cpu)
1.1 downsj 333: {
1.30 otto 334: if (screen_length > 3 || !smart_terminal) {
335: static char *tag;
336: static int cpulen, old_width;
337: int i;
338:
339: if (cpulen == 0 && ncpu > 1) {
340: /* compute length of the cpu string */
341: for (i = ncpu; i > 0; cpulen++, i /= 10)
342: continue;
343: }
1.20 millert 344:
1.30 otto 345: if (old_width == screen_width) {
346: if (ncpu > 1) {
347: /* just store the cpu number in the tag */
348: i = tag[3 + cpulen];
349: snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu);
350: tag[3 + cpulen] = i;
351: }
352: } else {
353: /*
354: * use a long tag if it will fit, otherwise use short one.
355: */
356: free(tag);
357: if (cpustate_total_length + 10 + cpulen >= screen_width)
358: i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu);
359: else
360: i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu);
361: if (i == -1)
362: tag = NULL;
363: else
364: old_width = screen_width;
1.20 millert 365: }
1.30 otto 366: return (tag);
367: } else
1.34 tedu 368: return ("\0");
1.1 downsj 369: }
370:
1.12 pvalchev 371: void
1.57 cheloha 372: i_cpustates(int64_t *ostates, int *online)
1.1 downsj 373: {
1.57 cheloha 374: int i, first, cpu, cpu_line;
1.34 tedu 375: double value;
1.20 millert 376: int64_t *states;
1.34 tedu 377: char **names, *thisname;
378:
379: if (combine_cpus) {
380: static double *values;
381: if (!values) {
382: values = calloc(num_cpustates, sizeof(*values));
383: if (!values)
384: err(1, NULL);
385: }
386: memset(values, 0, num_cpustates * sizeof(*values));
387: for (cpu = 0; cpu < ncpu; cpu++) {
1.57 cheloha 388: if (!online[cpu])
389: continue;
1.34 tedu 390: names = cpustate_names;
391: states = ostates + (CPUSTATES * cpu);
392: i = 0;
393: while ((thisname = *names++) != NULL) {
394: if (*thisname != '\0') {
395: /* retrieve the value and remember it */
396: values[i++] += *states++;
397: }
398: }
399: }
400: if (screen_length > 2 || !smart_terminal) {
401: names = cpustate_names;
402: i = 0;
403: first = 0;
404: move(2, 0);
405: clrtoeol();
1.57 cheloha 406: printwp("%-3d CPUs: ", ncpuonline);
1.14 deraadt 407:
1.34 tedu 408: while ((thisname = *names++) != NULL) {
409: if (*thisname != '\0') {
1.57 cheloha 410: value = values[i++] / ncpuonline;
1.34 tedu 411: /* if percentage is >= 1000, print it as 100% */
412: printwp((value >= 1000 ? "%s%4.0f%% %s" :
413: "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
414: value / 10., thisname);
415: }
416: }
417: putn();
418: }
419: return;
420: }
1.57 cheloha 421: for (cpu = cpu_line = 0; cpu < ncpu; cpu++) {
422: /* skip if offline */
423: if (!online[cpu])
424: continue;
425:
1.20 millert 426: /* now walk thru the names and print the line */
427: names = cpustate_names;
1.34 tedu 428: first = 0;
1.20 millert 429: states = ostates + (CPUSTATES * cpu);
1.30 otto 430:
1.57 cheloha 431: if (screen_length > 2 + cpu_line || !smart_terminal) {
432: move(2 + cpu_line, 0);
1.30 otto 433: clrtoeol();
434: addstrp(cpustates_tag(cpu));
435:
436: while ((thisname = *names++) != NULL) {
437: if (*thisname != '\0') {
438: /* retrieve the value and remember it */
439: value = *states++;
440:
441: /* if percentage is >= 1000, print it as 100% */
442: printwp((value >= 1000 ? "%s%4.0f%% %s" :
1.34 tedu 443: "%s%4.1f%% %s"), first++ == 0 ? "" : ", ",
444: value / 10., thisname);
1.30 otto 445: }
1.20 millert 446: }
1.30 otto 447: putn();
1.57 cheloha 448: cpu_line++;
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.60 kn 511: if (header_status && (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.59 deraadt 721: int 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);
1.59 deraadt 743: if (len < 0 || len >= left)
1.16 millert 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.61 kn 799: "+ - reset any P highlight, g, p, or u filters\n"
1.35 jmc 800: "1 - display CPU statistics on a single line\n"
1.62 kn 801: "9 | 0 - scroll up/down the process list by one line\n"
802: "( | ) - scroll up/down the process list by screen half\n"
1.25 otto 803: "C - toggle the display of command line arguments\n"
804: "d count - show `count' displays, then exit\n"
805: "e - list errors generated by last \"kill\" or \"renice\" command\n"
1.63 kn 806: "g|/ string - filter on command name (g+ or /+ selects all commands)\n"
1.25 otto 807: "h | ? - help; show this text\n"
1.41 jsing 808: "H - toggle the display of threads\n"
1.25 otto 809: "I | i - toggle the display of idle processes\n"
1.62 kn 810: "k [-sig] pid - send signal `-sig' (TERM by default) to process `pid'\n"
1.25 otto 811: "n|# count - show `count' processes\n"
1.58 kn 812: "o [-]field - specify sort order (size, res, cpu, time, pri, pid, command)\n"
813: " (o -field sorts in reverse)\n"
1.25 otto 814: "P pid - highlight process `pid' (P+ switches highlighting off)\n"
815: "p pid - display process by `pid' (p+ selects all processes)\n"
816: "q - quit\n"
817: "r count pid - renice process `pid' to nice value `count'\n"
818: "S - toggle the display of system processes\n"
819: "s time - change delay between displays to `time' seconds\n"
1.64 kn 820: "T [-]rtable - show processes associated with routing table `rtable'\n"
821: " (T+ shows all, T -rtable hides rtable)\n"
1.65 kn 822: "t - toggle the display of routing tables\n"
1.43 brynet 823: "u [-]user - show processes for `user' (u+ shows all, u -user hides user)\n"
1.25 otto 824: "\n");
825:
826: if (smart_terminal) {
827: nonl();
828: refresh();
829: }
830: }
831:
832: /*
833: * show_errors() - display on stdout the current log of errors.
834: */
835: void
836: show_errors(void)
837: {
838: struct errs *errp = errs;
839: int cnt = 0;
840:
841: if (smart_terminal) {
842: clear();
843: nl();
844: }
845: printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
846: while (cnt++ < errcnt) {
1.38 otto 847: printwp("%5s: %s\n", errp->arg,
1.25 otto 848: errp->err == 0 ? "Not a number" : strerror(errp->err));
849: errp++;
850: }
1.38 otto 851: printwp("\n");
1.25 otto 852: if (smart_terminal) {
853: nonl();
854: refresh();
855: }
856: }
857:
858: void
859: anykey(void)
860: {
861: int ch;
1.28 deraadt 862: ssize_t len;
1.25 otto 863:
864: standoutp();
865: addstrp("Hit any key to continue: ");
866: standendp();
867: if (smart_terminal)
868: refresh();
1.52 deraadt 869: else
1.25 otto 870: fflush(stdout);
871: while (1) {
872: len = read(STDIN_FILENO, &ch, 1);
873: if (len == -1 && errno == EINTR)
874: continue;
875: if (len == 0)
876: exit(1);
877: break;
878: }
1.1 downsj 879: }