[BACK]Return to display.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / top

Annotation of src/usr.bin/top/display.c, Revision 1.41

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