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