[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.20

1.20    ! millert     1: /* $OpenBSD: display.c,v 1.19 2005/04/13 02:33:09 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/time.h>
        !            50: #include <sys/sched.h>
1.2       downsj     51: #include <stdio.h>
1.1       downsj     52: #include <ctype.h>
1.20    ! millert    53: #include <err.h>
1.2       downsj     54: #include <stdlib.h>
                     55: #include <string.h>
1.17      deraadt    56: #include <signal.h>
1.2       downsj     57: #include <term.h>
1.1       downsj     58: #include <time.h>
1.2       downsj     59: #include <unistd.h>
1.13      pvalchev   60: #include <stdarg.h>
1.1       downsj     61:
                     62: #include "screen.h"            /* interface to screen package */
                     63: #include "layout.h"            /* defines for screen position layout */
                     64: #include "display.h"
                     65: #include "top.h"
                     66: #include "top.local.h"
                     67: #include "boolean.h"
                     68: #include "machine.h"           /* we should eliminate this!!! */
                     69: #include "utils.h"
                     70:
                     71: #ifdef DEBUG
1.14      deraadt    72: FILE           *debug;
1.1       downsj     73: #endif
                     74:
1.14      deraadt    75: static pid_t    lmpid = 0;
                     76: static int      last_hi = 0;   /* used in u_process and u_endscreen */
                     77: static int      lastline = 0;
                     78: static int      display_width = MAX_COLS;
                     79:
1.20    ! millert    80: static char    *cpustates_tag(int);
1.14      deraadt    81: static int      string_count(char **);
1.16      millert    82: static void     summary_format(char *, size_t, int *, char **);
1.14      deraadt    83: static void     line_update(char *, char *, int, int);
1.2       downsj     84:
1.1       downsj     85: #define lineindex(l) ((l)*display_width)
                     86:
1.18      otto       87: /* things initialized by display_init and used throughout */
1.1       downsj     88:
                     89: /* buffer of proc information lines for display updating */
1.14      deraadt    90: char           *screenbuf = NULL;
1.1       downsj     91:
1.14      deraadt    92: static char   **procstate_names;
                     93: static char   **cpustate_names;
                     94: static char   **memory_names;
                     95:
                     96: static int      num_procstates;
                     97: static int      num_cpustates;
                     98:
                     99: static int     *lprocstates;
1.20    ! millert   100: static int64_t **lcpustates;
1.14      deraadt   101:
                    102: static int     *cpustate_columns;
                    103: static int      cpustate_total_length;
                    104:
1.20    ! millert   105: /* display ips */
        !           106: int y_mem;
        !           107: int y_message;
        !           108: int y_header;
        !           109: int y_idlecursor;
        !           110: int y_procs;
        !           111: extern int ncpu;
        !           112: int Header_lines;
        !           113:
1.14      deraadt   114: static enum {
                    115:        OFF, ON, ERASE
                    116: } header_status = ON;
1.1       downsj    117:
1.12      pvalchev  118: int
                    119: display_resize(void)
1.1       downsj    120: {
1.14      deraadt   121:        int display_lines;
1.1       downsj    122:
1.14      deraadt   123:        /* first, deallocate any previous buffer that may have been there */
                    124:        if (screenbuf != NULL)
                    125:                free(screenbuf);
                    126:
                    127:        /* calculate the current dimensions */
                    128:        /* if operating in "dumb" mode, we only need one line */
                    129:        display_lines = smart_terminal ? screen_length - Header_lines : 1;
                    130:
                    131:        /*
                    132:         * we don't want more than MAX_COLS columns, since the
                    133:         * machine-dependent modules make static allocations based on
                    134:         * MAX_COLS and we don't want to run off the end of their buffers
                    135:         */
                    136:        display_width = screen_width;
                    137:        if (display_width >= MAX_COLS)
                    138:                display_width = MAX_COLS - 1;
                    139:
                    140:        /* now, allocate space for the screen buffer */
1.20    ! millert   141:        screenbuf = malloc(display_lines * display_width);
        !           142:        if (screenbuf == NULL)
1.14      deraadt   143:                return (-1);
                    144:
                    145:        /* return number of lines available */
                    146:        /* for dumb terminals, pretend like we can show any amount */
                    147:        return (smart_terminal ? display_lines : Largest);
1.1       downsj    148: }
                    149:
1.12      pvalchev  150: int
1.14      deraadt   151: display_init(struct statics * statics)
1.1       downsj    152: {
1.20    ! millert   153:        int display_lines, *ip, i, cpu;
1.14      deraadt   154:        char **pp;
                    155:
1.20    ! millert   156:        y_mem = 2 + ncpu;
        !           157:        y_message = 3 + ncpu;
        !           158:        y_header = 4 + ncpu;
        !           159:        y_idlecursor = 3 + ncpu;
        !           160:        y_procs = 5 + ncpu;
        !           161:        Header_lines = 5 + ncpu;
        !           162:
1.14      deraadt   163:        /* call resize to do the dirty work */
                    164:        display_lines = display_resize();
                    165:
                    166:        /* only do the rest if we need to */
                    167:        if (display_lines > -1) {
                    168:                /* save pointers and allocate space for names */
                    169:                procstate_names = statics->procstate_names;
                    170:                num_procstates = string_count(procstate_names);
1.20    ! millert   171:                lprocstates = malloc(num_procstates * sizeof(int));
        !           172:                if (lprocstates == NULL)
        !           173:                        err(1, NULL);
1.14      deraadt   174:
                    175:                cpustate_names = statics->cpustate_names;
                    176:                num_cpustates = string_count(cpustate_names);
1.20    ! millert   177:                lcpustates = malloc(ncpu * sizeof(int64_t *));
        !           178:                if (lcpustates == NULL)
        !           179:                        err(1, NULL);
        !           180:                for (cpu = 0; cpu < ncpu; cpu++) {
        !           181:                        lcpustates[cpu] = malloc(num_cpustates * sizeof(int64_t));
        !           182:                        if (lcpustates[cpu] == NULL)
        !           183:                                err(1, NULL);
        !           184:                }
        !           185:
        !           186:                cpustate_columns = malloc(num_cpustates * sizeof(int));
        !           187:                if (cpustate_columns == NULL)
        !           188:                        err(1, NULL);
1.14      deraadt   189:
                    190:                memory_names = statics->memory_names;
                    191:
                    192:                /* calculate starting columns where needed */
                    193:                cpustate_total_length = 0;
                    194:                pp = cpustate_names;
                    195:                ip = cpustate_columns;
                    196:                while (*pp != NULL) {
                    197:                        if ((i = strlen(*pp++)) > 0) {
                    198:                                *ip++ = cpustate_total_length;
                    199:                                cpustate_total_length += i + 8;
                    200:                        }
                    201:                }
1.1       downsj    202:        }
1.14      deraadt   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.14      deraadt   210:        int i;
                    211:
                    212:        /* i_loadave also clears the screen, since it is first */
                    213:        clear();
1.1       downsj    214:
1.14      deraadt   215:        /* mpid == -1 implies this system doesn't have an _mpid */
                    216:        if (mpid != -1)
                    217:                printf("last pid: %5ld;  ", (long) mpid);
1.1       downsj    218:
1.14      deraadt   219:        printf("load averages");
                    220:
                    221:        for (i = 0; i < 3; i++)
                    222:                printf("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]);
                    223:
                    224:        lmpid = mpid;
1.1       downsj    225: }
                    226:
1.12      pvalchev  227: void
                    228: u_loadave(pid_t mpid, double *avenrun)
1.1       downsj    229: {
1.14      deraadt   230:        int i;
1.1       downsj    231:
1.14      deraadt   232:        if (mpid != -1) {
                    233:                /* change screen only when value has really changed */
                    234:                if (mpid != lmpid) {
                    235:                        Move_to(x_lastpid, y_lastpid);
                    236:                        printf("%5ld", (long) mpid);
                    237:                        lmpid = mpid;
                    238:                }
                    239:                /* i remembers x coordinate to move to */
                    240:                i = x_loadave;
                    241:        } else
                    242:                i = x_loadave_nompid;
                    243:
                    244:        /* move into position for load averages */
                    245:        Move_to(i, y_loadave);
                    246:
                    247:        /* display new load averages */
                    248:        /* we should optimize this and only display changes */
                    249:        for (i = 0; i < 3; i++)
                    250:                printf("%s%5.2f", i == 0 ? "" : ", ", avenrun[i]);
1.1       downsj    251: }
                    252:
1.14      deraadt   253: /*
                    254:  *  Display the current time.
                    255:  *  "ctime" always returns a string that looks like this:
                    256:  *
                    257:  *     Sun Sep 16 01:03:52 1973
                    258:  *      012345678901234567890123
                    259:  *               1         2
                    260:  *
                    261:  *  We want indices 11 thru 18 (length 8).
                    262:  */
                    263:
1.12      pvalchev  264: void
1.14      deraadt   265: i_timeofday(time_t * tod)
1.1       downsj    266: {
1.14      deraadt   267:
                    268:        if (smart_terminal) {
                    269:                Move_to(screen_width - 8, 0);
                    270:        } else {
                    271:                if (fputs("    ", stdout) == EOF)
                    272:                        exit(1);
                    273:        }
1.1       downsj    274: #ifdef DEBUG
1.14      deraadt   275:        {
                    276:                char *foo;
                    277:                foo = ctime(tod);
                    278:                if (fputs(foo, stdout) == EOF)
                    279:                        exit(1);
                    280:        }
1.1       downsj    281: #endif
1.14      deraadt   282:        printf("%-8.8s\n", &(ctime(tod)[11]));
                    283:        lastline = 1;
1.1       downsj    284: }
                    285:
1.14      deraadt   286: static int      ltotal = 0;
                    287: static char     procstates_buffer[128];
1.1       downsj    288:
                    289: /*
                    290:  *  *_procstates(total, brkdn, names) - print the process summary line
                    291:  *
                    292:  *  Assumptions:  cursor is at the beginning of the line on entry
                    293:  *               lastline is valid
                    294:  */
1.12      pvalchev  295: void
                    296: i_procstates(int total, int *brkdn)
1.1       downsj    297: {
1.14      deraadt   298:        int i;
1.1       downsj    299:
1.14      deraadt   300:        /* write current number of processes and remember the value */
                    301:        printf("%d processes:", total);
                    302:        ltotal = total;
                    303:
                    304:        /* put out enough spaces to get to column 15 */
                    305:        i = digits(total);
                    306:        while (i++ < 4) {
                    307:                if (putchar(' ') == EOF)
                    308:                        exit(1);
                    309:        }
                    310:
                    311:        /* format and print the process state summary */
1.16      millert   312:        summary_format(procstates_buffer, sizeof(procstates_buffer), brkdn,
                    313:            procstate_names);
1.14      deraadt   314:        if (fputs(procstates_buffer, stdout) == EOF)
1.5       deraadt   315:                exit(1);
1.1       downsj    316:
1.14      deraadt   317:        /* save the numbers for next time */
                    318:        memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
1.1       downsj    319: }
                    320:
1.12      pvalchev  321: void
                    322: u_procstates(int total, int *brkdn)
1.1       downsj    323: {
1.14      deraadt   324:        static char new[128];
                    325:        int i;
1.1       downsj    326:
1.14      deraadt   327:        /* update number of processes only if it has changed */
                    328:        if (ltotal != total) {
                    329:                /* move and overwrite */
1.1       downsj    330: #if (x_procstate == 0)
1.14      deraadt   331:                Move_to(x_procstate, y_procstate);
1.1       downsj    332: #else
1.14      deraadt   333:                /* cursor is already there...no motion needed */
                    334:                /* assert(lastline == 1); */
1.1       downsj    335: #endif
1.14      deraadt   336:                printf("%d", total);
1.1       downsj    337:
1.14      deraadt   338:                /* if number of digits differs, rewrite the label */
                    339:                if (digits(total) != digits(ltotal)) {
                    340:                        if (fputs(" processes:", stdout) == EOF)
                    341:                                exit(1);
                    342:                        /* put out enough spaces to get to column 15 */
                    343:                        i = digits(total);
                    344:                        while (i++ < 4) {
                    345:                                if (putchar(' ') == EOF)
                    346:                                        exit(1);
                    347:                        }
                    348:                        /* cursor may end up right where we want it!!! */
                    349:                }
                    350:                /* save new total */
                    351:                ltotal = total;
                    352:        }
                    353:        /* see if any of the state numbers has changed */
                    354:        if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) {
                    355:                /* format and update the line */
1.16      millert   356:                summary_format(new, sizeof(new), brkdn, procstate_names);
1.14      deraadt   357:                line_update(procstates_buffer, new, x_brkdn, y_brkdn);
                    358:                memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
1.1       downsj    359:        }
                    360: }
                    361:
                    362: /*
                    363:  *  *_cpustates(states, names) - print the cpu state percentages
                    364:  *
                    365:  *  Assumptions:  cursor is on the PREVIOUS line
                    366:  */
                    367:
1.14      deraadt   368: static int      cpustates_column;
1.1       downsj    369:
                    370: /* cpustates_tag() calculates the correct tag to use to label the line */
                    371:
1.12      pvalchev  372: static char *
1.20    ! millert   373: cpustates_tag(int cpu)
1.1       downsj    374: {
1.20    ! millert   375:        static char *tag;
        !           376:        static int cpulen, old_width;
        !           377:        int i;
1.14      deraadt   378:
1.20    ! millert   379:        if (cpulen == 0 && ncpu > 1) {
        !           380:                /* compute length of the cpu string */
        !           381:                for (i = ncpu; i > 0; cpulen++, i /= 10)
        !           382:                        continue;
        !           383:        }
        !           384:
        !           385:        if (old_width == screen_width) {
        !           386:                if (ncpu > 1) {
        !           387:                        /* just store the cpu number in the tag */
        !           388:                        i = tag[3 + cpulen];
        !           389:                        snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu);
        !           390:                        tag[3 + cpulen] = i;
        !           391:                }
        !           392:        } else {
        !           393:                /*
        !           394:                 * use a long tag if it will fit, otherwise use short one.
        !           395:                 */
        !           396:                free(tag);
        !           397:                if (cpustate_total_length + 10 + cpulen >= screen_width)
        !           398:                        i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu);
        !           399:                else
        !           400:                        i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu);
        !           401:                if (i == -1)
        !           402:                        tag = NULL;
        !           403:                else {
        !           404:                        cpustates_column = strlen(tag);
        !           405:                        old_width = screen_width;
        !           406:                }
        !           407:        }
        !           408:        return (tag);
1.1       downsj    409: }
                    410:
1.12      pvalchev  411: void
1.20    ! millert   412: i_cpustates(int64_t *ostates)
1.1       downsj    413: {
1.20    ! millert   414:        int i, cpu, value;
        !           415:        int64_t *states;
1.14      deraadt   416:        char **names = cpustate_names, *thisname;
                    417:
1.20    ! millert   418:        for (cpu = 0; cpu < ncpu; cpu++) {
        !           419:                /* print tag and bump lastline */
        !           420:                printf("\n%s", cpustates_tag(cpu));
        !           421:                lastline++;
        !           422:
        !           423:                /* now walk thru the names and print the line */
        !           424:                names = cpustate_names;
        !           425:                i = 0;
        !           426:                states = ostates + (CPUSTATES * cpu);
        !           427:                while ((thisname = *names++) != NULL) {
        !           428:                        if (*thisname != '\0') {
        !           429:                                /* retrieve the value and remember it */
        !           430:                                value = *states++;
        !           431:
        !           432:                                /* if percentage is >= 1000, print it as 100% */
        !           433:                                printf((value >= 1000 ? "%s%4.0f%% %s" :
        !           434:                                    "%s%4.1f%% %s"), i++ == 0 ? "" : ", ",
        !           435:                                    ((float) value) / 10., thisname);
        !           436:                        }
1.14      deraadt   437:                }
1.20    ! millert   438:
        !           439:                /* copy over values into "last" array */
        !           440:                memcpy(lcpustates[cpu], ostates, num_cpustates * sizeof(int64_t));
1.1       downsj    441:        }
                    442: }
                    443:
1.12      pvalchev  444: void
1.20    ! millert   445: u_cpustates(int64_t *ostates)
1.1       downsj    446: {
1.20    ! millert   447:        char **names, *thisname;
        !           448:        int cpu, value, *colp;
        !           449:        int64_t *lp, *states;
        !           450:
        !           451:        for (cpu = 0; cpu < ncpu; cpu++) {
        !           452:                lastline = y_cpustates + cpu;
        !           453:                states = ostates + (CPUSTATES * cpu);
        !           454:                Move_to(cpustates_column, lastline);
        !           455:                lp = lcpustates[cpu];
        !           456:                colp = cpustate_columns;
        !           457:
        !           458:                /* we could be much more optimal about this */
        !           459:                names = cpustate_names;
        !           460:                while ((thisname = *names++) != NULL) {
        !           461:                        if (*thisname != '\0') {
        !           462:                                /* did the value change since last time? */
        !           463:                                if (*lp != *states) {
        !           464:                                        /* yes, move and change */
        !           465:                                        lastline = y_cpustates + cpu;
        !           466:                                        Move_to(cpustates_column + *colp,
        !           467:                                            lastline);
        !           468:
        !           469:                                        /* retrieve value and remember it */
        !           470:                                        value = *states;
        !           471:
        !           472:                                        /* if percentage is >= 1000,
        !           473:                                         * print it as 100%
        !           474:                                         */
        !           475:                                        printf((value >= 1000 ? "%4.0f" :
        !           476:                                            "%4.1f"), ((double) value) / 10.);
        !           477:
        !           478:                                        /* remember it for next time */
        !           479:                                        *lp = *states;
        !           480:                                }
1.14      deraadt   481:                        }
1.20    ! millert   482:                        /* increment and move on */
        !           483:                        lp++;
        !           484:                        states++;
        !           485:                        colp++;
1.14      deraadt   486:                }
                    487:        }
1.1       downsj    488: }
                    489:
1.12      pvalchev  490: void
                    491: z_cpustates(void)
1.1       downsj    492: {
1.20    ! millert   493:        char **names, *thisname;
        !           494:        int cpu, i;
        !           495:        int64_t *lp;
        !           496:
        !           497:        for (cpu = 0; cpu < ncpu; cpu++) {
        !           498:                /* show tag and bump lastline */
        !           499:                printf("\n%s", cpustates_tag(cpu));
        !           500:                lastline++;
        !           501:
        !           502:                names = cpustate_names;
        !           503:                i = 0;
        !           504:                while ((thisname = *names++) != NULL) {
        !           505:                        if (*thisname != '\0')
        !           506:                                printf("%s    %% %s", i++ == 0 ? "" : ", ",
        !           507:                                    thisname);
        !           508:                }
1.14      deraadt   509:
1.20    ! millert   510:                /* fill the "last" array with all -1s, to ensure correct updating */
        !           511:                lp = lcpustates[cpu];
        !           512:                i = num_cpustates;
        !           513:                while (--i >= 0)
        !           514:                        *lp++ = -1;
        !           515:        }
1.1       downsj    516: }
                    517:
1.14      deraadt   518: static char     memory_buffer[MAX_COLS];
                    519:
1.1       downsj    520: /*
                    521:  *  *_memory(stats) - print "Memory: " followed by the memory summary string
                    522:  *
                    523:  *  Assumptions:  cursor is on "lastline"
                    524:  *                for i_memory ONLY: cursor is on the previous line
                    525:  */
1.12      pvalchev  526: void
                    527: i_memory(int *stats)
1.1       downsj    528: {
1.14      deraadt   529:        if (fputs("\nMemory: ", stdout) == EOF)
                    530:                exit(1);
                    531:        lastline++;
                    532:
                    533:        /* format and print the memory summary */
1.16      millert   534:        summary_format(memory_buffer, sizeof(memory_buffer), stats,
                    535:            memory_names);
1.14      deraadt   536:        if (fputs(memory_buffer, stdout) == EOF)
                    537:                exit(1);
1.1       downsj    538: }
                    539:
1.12      pvalchev  540: void
                    541: u_memory(int *stats)
1.1       downsj    542: {
1.14      deraadt   543:        static char new[MAX_COLS];
1.1       downsj    544:
1.14      deraadt   545:        /* format the new line */
1.16      millert   546:        summary_format(new, sizeof(new), stats, memory_names);
1.14      deraadt   547:        line_update(memory_buffer, new, x_mem, y_mem);
1.1       downsj    548: }
                    549:
                    550: /*
                    551:  *  *_message() - print the next pending message line, or erase the one
                    552:  *                that is there.
                    553:  *
                    554:  *  Note that u_message is (currently) the same as i_message.
                    555:  *
                    556:  *  Assumptions:  lastline is consistent
                    557:  */
                    558:
                    559: /*
                    560:  *  i_message is funny because it gets its message asynchronously (with
                    561:  *     respect to screen updates).
                    562:  */
                    563:
1.14      deraadt   564: static char     next_msg[MAX_COLS + 5];
                    565: static int      msglen = 0;
                    566: /*
                    567:  * Invariant: msglen is always the length of the message currently displayed
                    568:  * on the screen (even when next_msg doesn't contain that message).
                    569:  */
1.1       downsj    570:
1.12      pvalchev  571: void
                    572: i_message(void)
1.1       downsj    573: {
1.14      deraadt   574:        while (lastline < y_message) {
                    575:                if (fputc('\n', stdout) == EOF)
                    576:                        exit(1);
                    577:                lastline++;
                    578:        }
                    579:        if (next_msg[0] != '\0') {
                    580:                standout(next_msg);
                    581:                msglen = strlen(next_msg);
                    582:                next_msg[0] = '\0';
                    583:        } else if (msglen > 0) {
                    584:                (void) clear_eol(msglen);
                    585:                msglen = 0;
                    586:        }
1.1       downsj    587: }
                    588:
1.12      pvalchev  589: void
                    590: u_message(void)
1.1       downsj    591: {
1.14      deraadt   592:        i_message();
1.1       downsj    593: }
                    594:
1.14      deraadt   595: static int      header_length;
1.1       downsj    596:
                    597: /*
                    598:  *  *_header(text) - print the header for the process area
                    599:  *
                    600:  *  Assumptions:  cursor is on the previous line and lastline is consistent
                    601:  */
                    602:
1.12      pvalchev  603: void
                    604: i_header(char *text)
1.1       downsj    605: {
1.14      deraadt   606:        header_length = strlen(text);
                    607:        if (header_status == ON) {
                    608:                if (putchar('\n') == EOF)
                    609:                        exit(1);
                    610:                if (fputs(text, stdout) == EOF)
                    611:                        exit(1);
                    612:                lastline++;
                    613:        } else if (header_status == ERASE) {
                    614:                header_status = OFF;
                    615:        }
1.1       downsj    616: }
                    617:
1.14      deraadt   618: /* ARGSUSED */
1.12      pvalchev  619: void
                    620: u_header(char *text)
1.1       downsj    621: {
1.14      deraadt   622:        if (header_status == ERASE) {
                    623:                if (putchar('\n') == EOF)
                    624:                        exit(1);
                    625:                lastline++;
                    626:                clear_eol(header_length);
                    627:                header_status = OFF;
                    628:        }
1.1       downsj    629: }
                    630:
                    631: /*
                    632:  *  *_process(line, thisline) - print one process line
                    633:  *
                    634:  *  Assumptions:  lastline is consistent
                    635:  */
                    636:
1.12      pvalchev  637: void
                    638: i_process(int line, char *thisline)
1.1       downsj    639: {
1.16      millert   640:        char *base;
                    641:        size_t len;
1.1       downsj    642:
1.14      deraadt   643:        /* make sure we are on the correct line */
                    644:        while (lastline < y_procs + line) {
                    645:                if (putchar('\n') == EOF)
                    646:                        exit(1);
                    647:                lastline++;
                    648:        }
1.1       downsj    649:
1.14      deraadt   650:        /* truncate the line to conform to our current screen width */
                    651:        thisline[display_width] = '\0';
1.1       downsj    652:
1.14      deraadt   653:        /* write the line out */
                    654:        if (fputs(thisline, stdout) == EOF)
                    655:                exit(1);
1.1       downsj    656:
1.14      deraadt   657:        /* copy it in to our buffer */
                    658:        base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
1.16      millert   659:        len = strlcpy(base, thisline, display_width);
                    660:        if (len < (size_t)display_width) {
                    661:                /* zero fill the rest of it */
                    662:                memset(base + len, 0, display_width - len);
                    663:        }
1.1       downsj    664: }
                    665:
1.12      pvalchev  666: void
                    667: u_process(int linenum, char *linebuf)
1.1       downsj    668: {
1.14      deraadt   669:        int screen_line = linenum + Header_lines;
1.16      millert   670:        char *bufferline;
                    671:        size_t len;
1.14      deraadt   672:
                    673:        /* remember a pointer to the current line in the screen buffer */
                    674:        bufferline = &screenbuf[lineindex(linenum)];
                    675:
                    676:        /* truncate the line to conform to our current screen width */
                    677:        linebuf[display_width] = '\0';
                    678:
                    679:        /* is line higher than we went on the last display? */
                    680:        if (linenum >= last_hi) {
                    681:                /* yes, just ignore screenbuf and write it out directly */
                    682:                /* get positioned on the correct line */
                    683:                if (screen_line - lastline == 1) {
                    684:                        if (putchar('\n') == EOF)
                    685:                                exit(1);
                    686:                        lastline++;
                    687:                } else {
                    688:                        Move_to(0, screen_line);
                    689:                        lastline = screen_line;
                    690:                }
1.1       downsj    691:
1.14      deraadt   692:                /* now write the line */
                    693:                if (fputs(linebuf, stdout) == EOF)
                    694:                        exit(1);
1.1       downsj    695:
1.14      deraadt   696:                /* copy it in to the buffer */
1.16      millert   697:                len = strlcpy(bufferline, linebuf, display_width);
                    698:                if (len < (size_t)display_width) {
                    699:                        /* zero fill the rest of it */
                    700:                        memset(bufferline + len, 0, display_width - len);
                    701:                }
1.14      deraadt   702:        } else {
                    703:                line_update(bufferline, linebuf, 0, linenum + Header_lines);
                    704:        }
1.1       downsj    705: }
                    706:
1.12      pvalchev  707: void
                    708: u_endscreen(int hi)
1.1       downsj    709: {
1.14      deraadt   710:        int screen_line = hi + Header_lines, i;
1.1       downsj    711:
1.14      deraadt   712:        if (smart_terminal) {
                    713:                if (hi < last_hi) {
                    714:                        /* need to blank the remainder of the screen */
                    715:                        /*
                    716:                         * but only if there is any screen left below this
                    717:                         * line
                    718:                         */
                    719:                        if (lastline + 1 < screen_length) {
                    720:                                /*
                    721:                                 * efficiently move to the end of currently
                    722:                                 * displayed info
                    723:                                 */
                    724:                                if (screen_line - lastline < 5) {
                    725:                                        while (lastline < screen_line) {
                    726:                                                if (putchar('\n') == EOF)
                    727:                                                        exit(1);
                    728:                                                lastline++;
                    729:                                        }
                    730:                                } else {
                    731:                                        Move_to(0, screen_line);
                    732:                                        lastline = screen_line;
                    733:                                }
                    734:
                    735:                                if (clear_to_end) {
                    736:                                        /* we can do this the easy way */
                    737:                                        putcap(clear_to_end);
                    738:                                } else {
                    739:                                        /* use clear_eol on each line */
                    740:                                        i = hi;
                    741:                                        while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) {
                    742:                                                if (putchar('\n') == EOF)
                    743:                                                        exit(1);
                    744:                                        }
                    745:                                }
                    746:                        }
1.1       downsj    747:                }
1.14      deraadt   748:                last_hi = hi;
1.1       downsj    749:
1.14      deraadt   750:                /* move the cursor to a pleasant place */
                    751:                Move_to(x_idlecursor, y_idlecursor);
                    752:                lastline = y_idlecursor;
                    753:        } else {
                    754:                /*
                    755:                 * separate this display from the next with some vertical
                    756:                 * room
                    757:                 */
                    758:                if (fputs("\n\n", stdout) == EOF)
                    759:                        exit(1);
1.1       downsj    760:        }
                    761: }
                    762:
1.12      pvalchev  763: void
                    764: display_header(int t)
1.1       downsj    765: {
1.14      deraadt   766:        if (t) {
                    767:                header_status = ON;
                    768:        } else if (header_status == ON) {
                    769:                header_status = ERASE;
                    770:        }
1.1       downsj    771: }
                    772:
1.12      pvalchev  773: void
1.14      deraadt   774: new_message(int type, const char *msgfmt,...)
                    775: {
                    776:        va_list ap;
                    777:        int i;
                    778:
                    779:        va_start(ap, msgfmt);
                    780:        /* first, format the message */
                    781:        vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);
                    782:        va_end(ap);
                    783:
                    784:        if (msglen > 0) {
                    785:                /* message there already -- can we clear it? */
                    786:                if (!overstrike) {
                    787:                        /* yes -- write it and clear to end */
                    788:                        i = strlen(next_msg);
                    789:                        if ((type & MT_delayed) == 0) {
                    790:                                if (type & MT_standout)
                    791:                                        standout(next_msg);
                    792:                                else {
                    793:                                        if (fputs(next_msg, stdout) == EOF)
                    794:                                                exit(1);
                    795:                                }
                    796:                                (void) clear_eol(msglen - i);
                    797:                                msglen = i;
                    798:                                next_msg[0] = '\0';
                    799:                        }
                    800:                }
                    801:        } else {
                    802:                if ((type & MT_delayed) == 0) {
                    803:                        if (type & MT_standout)
                    804:                                standout(next_msg);
                    805:                        else {
                    806:                                if (fputs(next_msg, stdout) == EOF)
                    807:                                        exit(1);
                    808:                        }
                    809:                        msglen = strlen(next_msg);
                    810:                        next_msg[0] = '\0';
1.5       deraadt   811:                }
1.1       downsj    812:        }
                    813: }
                    814:
1.12      pvalchev  815: void
                    816: clear_message(void)
1.1       downsj    817: {
1.14      deraadt   818:        if (clear_eol(msglen) == 1) {
                    819:                if (putchar('\r') == EOF)
                    820:                        exit(1);
                    821:        }
1.1       downsj    822: }
                    823:
1.12      pvalchev  824: int
                    825: readline(char *buffer, int size, int numeric)
1.1       downsj    826: {
1.14      deraadt   827:        char *ptr = buffer, ch, cnt = 0, maxcnt = 0;
1.17      deraadt   828:        extern volatile sig_atomic_t leaveflag;
                    829:        ssize_t len;
1.14      deraadt   830:
                    831:        /* allow room for null terminator */
                    832:        size -= 1;
                    833:
                    834:        /* read loop */
1.19      deraadt   835:        while ((fflush(stdout), (len = read(STDIN_FILENO, ptr, 1)) > 0)) {
1.17      deraadt   836:
                    837:                if (len == 0 || leaveflag) {
                    838:                        end_screen();
                    839:                        exit(0);
                    840:                }
                    841:
1.14      deraadt   842:                /* newline means we are done */
                    843:                if ((ch = *ptr) == '\n')
                    844:                        break;
                    845:
                    846:                /* handle special editing characters */
                    847:                if (ch == ch_kill) {
                    848:                        /* kill line -- account for overstriking */
                    849:                        if (overstrike)
                    850:                                msglen += maxcnt;
                    851:
                    852:                        /* return null string */
                    853:                        *buffer = '\0';
                    854:                        if (putchar('\r') == EOF)
                    855:                                exit(1);
                    856:                        return (-1);
                    857:                } else if (ch == ch_erase) {
                    858:                        /* erase previous character */
                    859:                        if (cnt <= 0) {
                    860:                                /* none to erase! */
                    861:                                if (putchar('\7') == EOF)
                    862:                                        exit(1);
                    863:                        } else {
                    864:                                if (fputs("\b \b", stdout) == EOF)
                    865:                                        exit(1);
                    866:                                ptr--;
                    867:                                cnt--;
                    868:                        }
                    869:                }
                    870:                /* check for character validity and buffer overflow */
                    871:                else if (cnt == size || (numeric && !isdigit(ch)) ||
                    872:                    !isprint(ch)) {
                    873:                        /* not legal */
                    874:                        if (putchar('\7') == EOF)
                    875:                                exit(1);
                    876:                } else {
                    877:                        /* echo it and store it in the buffer */
                    878:                        if (putchar(ch) == EOF)
                    879:                                exit(1);
                    880:                        ptr++;
                    881:                        cnt++;
                    882:                        if (cnt > maxcnt)
                    883:                                maxcnt = cnt;
                    884:                }
1.1       downsj    885:        }
                    886:
1.14      deraadt   887:        /* all done -- null terminate the string */
                    888:        *ptr = '\0';
                    889:
                    890:        /* account for the extra characters in the message area */
                    891:        /* (if terminal overstrikes, remember the furthest they went) */
                    892:        msglen += overstrike ? maxcnt : cnt;
                    893:
                    894:        /* return either inputted number or string length */
                    895:        if (putchar('\r') == EOF)
1.5       deraadt   896:                exit(1);
1.14      deraadt   897:        return (cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
1.1       downsj    898: }
                    899:
                    900: /* internal support routines */
1.12      pvalchev  901: static int
                    902: string_count(char **pp)
1.1       downsj    903: {
1.14      deraadt   904:        int cnt;
1.1       downsj    905:
1.14      deraadt   906:        cnt = 0;
                    907:        while (*pp++ != NULL)
                    908:                cnt++;
                    909:        return (cnt);
1.1       downsj    910: }
                    911:
1.16      millert   912: #define        COPYLEFT(to, from)                              \
                    913:        do {                                            \
                    914:                len = strlcpy((to), (from), left);      \
                    915:                if (len >= left)                        \
                    916:                        return;                         \
                    917:                p += len;                               \
                    918:                left -= len;                            \
                    919:        } while (0)
                    920:
1.12      pvalchev  921: static void
1.16      millert   922: summary_format(char *buf, size_t left, int *numbers, char **names)
1.1       downsj    923: {
1.14      deraadt   924:        char *p, *thisname;
1.16      millert   925:        size_t len;
1.14      deraadt   926:        int num;
1.1       downsj    927:
1.14      deraadt   928:        /* format each number followed by its string */
1.16      millert   929:        p = buf;
1.14      deraadt   930:        while ((thisname = *names++) != NULL) {
                    931:                /* get the number to format */
                    932:                num = *numbers++;
                    933:
                    934:                if (num >= 0) {
                    935:                        /* is this number in kilobytes? */
                    936:                        if (thisname[0] == 'K') {
                    937:                                /* yes: format it as a memory value */
1.16      millert   938:                                COPYLEFT(p, format_k(num));
1.14      deraadt   939:
                    940:                                /*
                    941:                                 * skip over the K, since it was included by
                    942:                                 * format_k
                    943:                                 */
1.16      millert   944:                                COPYLEFT(p, thisname + 1);
1.14      deraadt   945:                        } else if (num > 0) {
1.16      millert   946:                                len = snprintf(p, left, "%d%s", num, thisname);
                    947:                                if (len == (size_t)-1 || len >= left)
                    948:                                        return;
                    949:                                p += len;
                    950:                                left -= len;
1.14      deraadt   951:                        }
1.16      millert   952:                } else {
                    953:                        /*
                    954:                         * Ignore negative numbers, but display corresponding
                    955:                         * string.
                    956:                         */
                    957:                        COPYLEFT(p, thisname);
1.14      deraadt   958:                }
1.1       downsj    959:        }
                    960:
1.14      deraadt   961:        /* if the last two characters in the string are ", ", delete them */
                    962:        p -= 2;
1.16      millert   963:        if (p >= buf && p[0] == ',' && p[1] == ' ')
1.14      deraadt   964:                *p = '\0';
1.1       downsj    965: }
                    966:
1.12      pvalchev  967: static void
                    968: line_update(char *old, char *new, int start, int line)
1.1       downsj    969: {
1.14      deraadt   970:        int ch, diff, newcol = start + 1, lastcol = start;
                    971:        char cursor_on_line = No, *current;
1.1       downsj    972:
1.14      deraadt   973:        /* compare the two strings and only rewrite what has changed */
                    974:        current = old;
1.1       downsj    975: #ifdef DEBUG
1.14      deraadt   976:        fprintf(debug, "line_update, starting at %d\n", start);
                    977:        fputs(old, debug);
                    978:        fputc('\n', debug);
                    979:        fputs(new, debug);
                    980:        fputs("\n-\n", debug);
1.1       downsj    981: #endif
                    982:
1.14      deraadt   983:        /* start things off on the right foot               */
                    984:        /* this is to make sure the invariants get set up right */
                    985:        if ((ch = *new++) != *old) {
                    986:                if (line - lastline == 1 && start == 0) {
                    987:                        if (putchar('\n') == EOF)
                    988:                                exit(1);
                    989:                } else
                    990:                        Move_to(start, line);
                    991:
                    992:                cursor_on_line = Yes;
                    993:                if (putchar(ch) == EOF)
                    994:                        exit(1);
                    995:                *old = ch;
                    996:                lastcol = 1;
1.1       downsj    997:        }
                    998:        old++;
                    999:
1.14      deraadt  1000:        /*
                   1001:         *  main loop -- check each character.  If the old and new aren't the
                   1002:         *      same, then update the display.  When the distance from the
                   1003:         *      current cursor position to the new change is small enough,
                   1004:         *      the characters that belong there are written to move the
                   1005:         *      cursor over.
                   1006:         *
                   1007:         *      Invariants:
                   1008:         *          lastcol is the column where the cursor currently is sitting
                   1009:         *              (always one beyond the end of the last mismatch).
                   1010:         */
                   1011:        do {
                   1012:                if ((ch = *new++) != *old) {
                   1013:                        /* new character is different from old    */
                   1014:                        /* make sure the cursor is on top of this character */
                   1015:                        diff = newcol - lastcol;
                   1016:                        if (diff > 0) {
                   1017:                                /*
                   1018:                                 * some motion is required--figure out which
                   1019:                                 * is shorter
                   1020:                                 */
                   1021:                                if (diff < 6 && cursor_on_line) {
                   1022:                                        /*
                   1023:                                         * overwrite old stuff--get it out of
                   1024:                                         * the old buffer
                   1025:                                         */
                   1026:                                        printf("%.*s", diff, &current[lastcol - start]);
                   1027:                                } else {
                   1028:                                        /* use cursor addressing */
                   1029:                                        Move_to(newcol, line);
                   1030:                                        cursor_on_line = Yes;
                   1031:                                }
                   1032:                                /* remember where the cursor is */
                   1033:                                lastcol = newcol + 1;
                   1034:                        } else {
                   1035:                                /* already there, update position */
                   1036:                                lastcol++;
                   1037:                        }
                   1038:
                   1039:                        /* write what we need to */
                   1040:                        if (ch == '\0') {
                   1041:                                /*
                   1042:                                 * at the end--terminate with a
                   1043:                                 * clear-to-end-of-line
                   1044:                                 */
                   1045:                                (void) clear_eol(strlen(old));
                   1046:                        } else {
                   1047:                                /* write the new character */
                   1048:                                if (putchar(ch) == EOF)
                   1049:                                        exit(1);
                   1050:                        }
                   1051:                        /* put the new character in the screen buffer */
                   1052:                        *old = ch;
                   1053:                }
                   1054:                /* update working column and screen buffer pointer */
                   1055:                newcol++;
                   1056:                old++;
                   1057:        } while (ch != '\0');
                   1058:
                   1059:        /* zero out the rest of the line buffer -- MUST BE DONE! */
                   1060:        diff = display_width - newcol;
                   1061:        if (diff > 0)
                   1062:                memset(old, 0, diff);
                   1063:
                   1064:        /* remember where the current line is */
                   1065:        if (cursor_on_line)
                   1066:                lastline = line;
1.1       downsj   1067: }
                   1068:
                   1069: /*
                   1070:  *  printable(str) - make the string pointed to by "str" into one that is
                   1071:  *     printable (i.e.: all ascii), by converting all non-printable
                   1072:  *     characters into '?'.  Replacements are done in place and a pointer
                   1073:  *     to the original buffer is returned.
                   1074:  */
1.12      pvalchev 1075: char *
                   1076: printable(char *str)
1.1       downsj   1077: {
1.14      deraadt  1078:        char *ptr, ch;
1.1       downsj   1079:
1.14      deraadt  1080:        ptr = str;
                   1081:        while ((ch = *ptr) != '\0') {
                   1082:                if (!isprint(ch))
                   1083:                        *ptr = '?';
                   1084:                ptr++;
1.1       downsj   1085:        }
1.14      deraadt  1086:        return (str);
1.1       downsj   1087: }