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

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