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