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

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.4     ! millert    60: static void summary_format __P((char *, 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.4     ! millert   308:     summary_format(procstates_buffer, brkdn, procstate_names);
1.1       downsj    309:     fputs(procstates_buffer, stdout);
                    310:
                    311:     /* save the numbers for next time */
                    312:     memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
                    313: }
                    314:
1.2       downsj    315: void u_procstates(total, brkdn)
1.1       downsj    316:
                    317: int total;
                    318: int *brkdn;
                    319:
                    320: {
                    321:     static char new[128];
                    322:     register int i;
                    323:
                    324:     /* update number of processes only if it has changed */
                    325:     if (ltotal != total)
                    326:     {
                    327:        /* move and overwrite */
                    328: #if (x_procstate == 0)
                    329:        Move_to(x_procstate, y_procstate);
                    330: #else
                    331:        /* cursor is already there...no motion needed */
                    332:        /* assert(lastline == 1); */
                    333: #endif
                    334:        printf("%d", total);
                    335:
                    336:        /* if number of digits differs, rewrite the label */
                    337:        if (digits(total) != digits(ltotal))
                    338:        {
                    339:            fputs(" processes:", stdout);
                    340:            /* put out enough spaces to get to column 15 */
                    341:            i = digits(total);
                    342:            while (i++ < 4)
                    343:            {
                    344:                putchar(' ');
                    345:            }
                    346:            /* cursor may end up right where we want it!!! */
                    347:        }
                    348:
                    349:        /* save new total */
                    350:        ltotal = total;
                    351:     }
                    352:
                    353:     /* see if any of the state numbers has changed */
                    354:     if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
                    355:     {
                    356:        /* format and update the line */
1.4     ! millert   357:        summary_format(new, brkdn, procstate_names);
1.1       downsj    358:        line_update(procstates_buffer, new, x_brkdn, y_brkdn);
                    359:        memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
                    360:     }
                    361: }
                    362:
                    363: /*
                    364:  *  *_cpustates(states, names) - print the cpu state percentages
                    365:  *
                    366:  *  Assumptions:  cursor is on the PREVIOUS line
                    367:  */
                    368:
                    369: static int cpustates_column;
                    370:
                    371: /* cpustates_tag() calculates the correct tag to use to label the line */
                    372:
1.2       downsj    373: static char *cpustates_tag()
1.1       downsj    374:
                    375: {
                    376:     register char *use;
                    377:
                    378:     static char *short_tag = "CPU: ";
                    379:     static char *long_tag = "CPU states: ";
                    380:
                    381:     /* if length + strlen(long_tag) >= screen_width, then we have to
                    382:        use the shorter tag (we subtract 2 to account for ": ") */
                    383:     if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
                    384:     {
                    385:        use = short_tag;
                    386:     }
                    387:     else
                    388:     {
                    389:        use = long_tag;
                    390:     }
                    391:
                    392:     /* set cpustates_column accordingly then return result */
                    393:     cpustates_column = strlen(use);
                    394:     return(use);
                    395: }
                    396:
1.2       downsj    397: void i_cpustates(states)
1.1       downsj    398:
                    399: register int *states;
                    400:
                    401: {
                    402:     register int i = 0;
                    403:     register int value;
                    404:     register char **names = cpustate_names;
                    405:     register char *thisname;
                    406:
                    407:     /* print tag and bump lastline */
                    408:     printf("\n%s", cpustates_tag());
                    409:     lastline++;
                    410:
                    411:     /* now walk thru the names and print the line */
                    412:     while ((thisname = *names++) != NULL)
                    413:     {
                    414:        if (*thisname != '\0')
                    415:        {
                    416:            /* retrieve the value and remember it */
                    417:            value = *states++;
                    418:
                    419:            /* if percentage is >= 1000, print it as 100% */
                    420:            printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
                    421:                   i++ == 0 ? "" : ", ",
                    422:                   ((float)value)/10.,
                    423:                   thisname);
                    424:        }
                    425:     }
                    426:
                    427:     /* copy over values into "last" array */
                    428:     memcpy(lcpustates, states, num_cpustates * sizeof(int));
                    429: }
                    430:
1.2       downsj    431: void u_cpustates(states)
1.1       downsj    432:
                    433: register int *states;
                    434:
                    435: {
                    436:     register int value;
                    437:     register char **names = cpustate_names;
                    438:     register char *thisname;
                    439:     register int *lp;
                    440:     register int *colp;
                    441:
                    442:     Move_to(cpustates_column, y_cpustates);
                    443:     lastline = y_cpustates;
                    444:     lp = lcpustates;
                    445:     colp = cpustate_columns;
                    446:
                    447:     /* we could be much more optimal about this */
                    448:     while ((thisname = *names++) != NULL)
                    449:     {
                    450:        if (*thisname != '\0')
                    451:        {
                    452:            /* did the value change since last time? */
                    453:            if (*lp != *states)
                    454:            {
                    455:                /* yes, move and change */
                    456:                Move_to(cpustates_column + *colp, y_cpustates);
                    457:                lastline = y_cpustates;
                    458:
                    459:                /* retrieve value and remember it */
                    460:                value = *states;
                    461:
                    462:                /* if percentage is >= 1000, print it as 100% */
                    463:                printf((value >= 1000 ? "%4.0f" : "%4.1f"),
                    464:                       ((double)value)/10.);
                    465:
                    466:                /* remember it for next time */
                    467:                *lp = *states;
                    468:            }
                    469:        }
                    470:
                    471:        /* increment and move on */
                    472:        lp++;
                    473:        states++;
                    474:        colp++;
                    475:     }
                    476: }
                    477:
1.2       downsj    478: void z_cpustates()
1.1       downsj    479:
                    480: {
                    481:     register int i = 0;
                    482:     register char **names = cpustate_names;
                    483:     register char *thisname;
                    484:     register int *lp;
                    485:
                    486:     /* show tag and bump lastline */
                    487:     printf("\n%s", cpustates_tag());
                    488:     lastline++;
                    489:
                    490:     while ((thisname = *names++) != NULL)
                    491:     {
                    492:        if (*thisname != '\0')
                    493:        {
                    494:            printf("%s    %% %s", i++ == 0 ? "" : ", ", thisname);
                    495:        }
                    496:     }
                    497:
                    498:     /* fill the "last" array with all -1s, to insure correct updating */
                    499:     lp = lcpustates;
                    500:     i = num_cpustates;
                    501:     while (--i >= 0)
                    502:     {
                    503:        *lp++ = -1;
                    504:     }
                    505: }
                    506:
                    507: /*
                    508:  *  *_memory(stats) - print "Memory: " followed by the memory summary string
                    509:  *
                    510:  *  Assumptions:  cursor is on "lastline"
                    511:  *                for i_memory ONLY: cursor is on the previous line
                    512:  */
                    513:
1.2       downsj    514: static char memory_buffer[MAX_COLS];
1.1       downsj    515:
1.2       downsj    516: void i_memory(stats)
1.1       downsj    517:
                    518: int *stats;
                    519:
                    520: {
                    521:     fputs("\nMemory: ", stdout);
                    522:     lastline++;
                    523:
                    524:     /* format and print the memory summary */
1.4     ! millert   525:     summary_format(memory_buffer, stats, memory_names);
1.1       downsj    526:     fputs(memory_buffer, stdout);
                    527: }
                    528:
1.2       downsj    529: void u_memory(stats)
1.1       downsj    530:
                    531: int *stats;
                    532:
                    533: {
                    534:     static char new[MAX_COLS];
                    535:
                    536:     /* format the new line */
1.4     ! millert   537:     summary_format(new, stats, memory_names);
1.1       downsj    538:     line_update(memory_buffer, new, x_mem, y_mem);
                    539: }
                    540:
                    541: /*
                    542:  *  *_message() - print the next pending message line, or erase the one
                    543:  *                that is there.
                    544:  *
                    545:  *  Note that u_message is (currently) the same as i_message.
                    546:  *
                    547:  *  Assumptions:  lastline is consistent
                    548:  */
                    549:
                    550: /*
                    551:  *  i_message is funny because it gets its message asynchronously (with
                    552:  *     respect to screen updates).
                    553:  */
                    554:
                    555: static char next_msg[MAX_COLS + 5];
                    556: static int msglen = 0;
                    557: /* Invariant: msglen is always the length of the message currently displayed
                    558:    on the screen (even when next_msg doesn't contain that message). */
                    559:
1.2       downsj    560: void i_message()
1.1       downsj    561:
                    562: {
                    563:     while (lastline < y_message)
                    564:     {
                    565:        fputc('\n', stdout);
                    566:        lastline++;
                    567:     }
                    568:     if (next_msg[0] != '\0')
                    569:     {
                    570:        standout(next_msg);
                    571:        msglen = strlen(next_msg);
                    572:        next_msg[0] = '\0';
                    573:     }
                    574:     else if (msglen > 0)
                    575:     {
                    576:        (void) clear_eol(msglen);
                    577:        msglen = 0;
                    578:     }
                    579: }
                    580:
1.2       downsj    581: void u_message()
1.1       downsj    582:
                    583: {
                    584:     i_message();
                    585: }
                    586:
                    587: static int header_length;
                    588:
                    589: /*
                    590:  *  *_header(text) - print the header for the process area
                    591:  *
                    592:  *  Assumptions:  cursor is on the previous line and lastline is consistent
                    593:  */
                    594:
1.2       downsj    595: void i_header(text)
1.1       downsj    596:
                    597: char *text;
                    598:
                    599: {
                    600:     header_length = strlen(text);
                    601:     if (header_status == ON)
                    602:     {
                    603:        putchar('\n');
                    604:        fputs(text, stdout);
                    605:        lastline++;
                    606:     }
                    607:     else if (header_status == ERASE)
                    608:     {
                    609:        header_status = OFF;
                    610:     }
                    611: }
                    612:
                    613: /*ARGSUSED*/
1.2       downsj    614: void u_header(text)
1.1       downsj    615:
                    616: char *text;            /* ignored */
                    617:
                    618: {
                    619:     if (header_status == ERASE)
                    620:     {
                    621:        putchar('\n');
                    622:        lastline++;
                    623:        clear_eol(header_length);
                    624:        header_status = OFF;
                    625:     }
                    626: }
                    627:
                    628: /*
                    629:  *  *_process(line, thisline) - print one process line
                    630:  *
                    631:  *  Assumptions:  lastline is consistent
                    632:  */
                    633:
1.2       downsj    634: void i_process(line, thisline)
1.1       downsj    635:
                    636: int line;
                    637: char *thisline;
                    638:
                    639: {
                    640:     register char *p;
                    641:     register char *base;
                    642:
                    643:     /* make sure we are on the correct line */
                    644:     while (lastline < y_procs + line)
                    645:     {
                    646:        putchar('\n');
                    647:        lastline++;
                    648:     }
                    649:
                    650:     /* truncate the line to conform to our current screen width */
                    651:     thisline[display_width] = '\0';
                    652:
                    653:     /* write the line out */
                    654:     fputs(thisline, stdout);
                    655:
                    656:     /* copy it in to our buffer */
                    657:     base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
                    658:     p = strecpy(base, thisline);
                    659:
                    660:     /* zero fill the rest of it */
1.2       downsj    661:     memset(p, 0, display_width - (p - base));
1.1       downsj    662: }
                    663:
1.2       downsj    664: void u_process(linenum, linebuf)
1.1       downsj    665:
1.2       downsj    666: int linenum;
                    667: char *linebuf;
1.1       downsj    668:
                    669: {
                    670:     register char *optr;
1.2       downsj    671:     register int screen_line = linenum + Header_lines;
1.1       downsj    672:     register char *bufferline;
                    673:
                    674:     /* remember a pointer to the current line in the screen buffer */
1.2       downsj    675:     bufferline = &screenbuf[lineindex(linenum)];
1.1       downsj    676:
                    677:     /* truncate the line to conform to our current screen width */
1.2       downsj    678:     linebuf[display_width] = '\0';
1.1       downsj    679:
                    680:     /* is line higher than we went on the last display? */
1.2       downsj    681:     if (linenum >= last_hi)
1.1       downsj    682:     {
                    683:        /* yes, just ignore screenbuf and write it out directly */
                    684:        /* get positioned on the correct line */
                    685:        if (screen_line - lastline == 1)
                    686:        {
                    687:            putchar('\n');
                    688:            lastline++;
                    689:        }
                    690:        else
                    691:        {
                    692:            Move_to(0, screen_line);
                    693:            lastline = screen_line;
                    694:        }
                    695:
                    696:        /* now write the line */
1.2       downsj    697:        fputs(linebuf, stdout);
1.1       downsj    698:
                    699:        /* copy it in to the buffer */
1.2       downsj    700:        optr = strecpy(bufferline, linebuf);
1.1       downsj    701:
                    702:        /* zero fill the rest of it */
1.2       downsj    703:        memset(optr, 0, display_width - (optr - bufferline));
1.1       downsj    704:     }
                    705:     else
                    706:     {
1.2       downsj    707:        line_update(bufferline, linebuf, 0, linenum + Header_lines);
1.1       downsj    708:     }
                    709: }
                    710:
1.2       downsj    711: void u_endscreen(hi)
1.1       downsj    712:
                    713: register int hi;
                    714:
                    715: {
                    716:     register int screen_line = hi + Header_lines;
                    717:     register int i;
                    718:
                    719:     if (smart_terminal)
                    720:     {
                    721:        if (hi < last_hi)
                    722:        {
                    723:            /* need to blank the remainder of the screen */
                    724:            /* but only if there is any screen left below this line */
                    725:            if (lastline + 1 < screen_length)
                    726:            {
                    727:                /* efficiently move to the end of currently displayed info */
                    728:                if (screen_line - lastline < 5)
                    729:                {
                    730:                    while (lastline < screen_line)
                    731:                    {
                    732:                        putchar('\n');
                    733:                        lastline++;
                    734:                    }
                    735:                }
                    736:                else
                    737:                {
                    738:                    Move_to(0, screen_line);
                    739:                    lastline = screen_line;
                    740:                }
                    741:
                    742:                if (clear_to_end)
                    743:                {
                    744:                    /* we can do this the easy way */
                    745:                    putcap(clear_to_end);
                    746:                }
                    747:                else
                    748:                {
                    749:                    /* use clear_eol on each line */
                    750:                    i = hi;
                    751:                    while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
                    752:                    {
                    753:                        putchar('\n');
                    754:                    }
                    755:                }
                    756:            }
                    757:        }
                    758:        last_hi = hi;
                    759:
                    760:        /* move the cursor to a pleasant place */
                    761:        Move_to(x_idlecursor, y_idlecursor);
                    762:        lastline = y_idlecursor;
                    763:     }
                    764:     else
                    765:     {
                    766:        /* separate this display from the next with some vertical room */
                    767:        fputs("\n\n", stdout);
                    768:     }
                    769: }
                    770:
1.2       downsj    771: void display_header(t)
1.1       downsj    772:
                    773: int t;
                    774:
                    775: {
                    776:     if (t)
                    777:     {
                    778:        header_status = ON;
                    779:     }
                    780:     else if (header_status == ON)
                    781:     {
                    782:        header_status = ERASE;
                    783:     }
                    784: }
                    785:
                    786: /*VARARGS2*/
1.2       downsj    787: void new_message(type, msgfmt, a1, a2, a3)
1.1       downsj    788:
                    789: int type;
                    790: char *msgfmt;
                    791: caddr_t a1, a2, a3;
                    792:
                    793: {
                    794:     register int i;
                    795:
                    796:     /* first, format the message */
                    797:     (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3);
                    798:
                    799:     if (msglen > 0)
                    800:     {
                    801:        /* message there already -- can we clear it? */
                    802:        if (!overstrike)
                    803:        {
                    804:            /* yes -- write it and clear to end */
                    805:            i = strlen(next_msg);
                    806:            if ((type & MT_delayed) == 0)
                    807:            {
                    808:                type & MT_standout ? standout(next_msg) :
                    809:                                     fputs(next_msg, stdout);
                    810:                (void) clear_eol(msglen - i);
                    811:                msglen = i;
                    812:                next_msg[0] = '\0';
                    813:            }
                    814:        }
                    815:     }
                    816:     else
                    817:     {
                    818:        if ((type & MT_delayed) == 0)
                    819:        {
                    820:            type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
                    821:            msglen = strlen(next_msg);
                    822:            next_msg[0] = '\0';
                    823:        }
                    824:     }
                    825: }
                    826:
1.2       downsj    827: void clear_message()
1.1       downsj    828:
                    829: {
                    830:     if (clear_eol(msglen) == 1)
                    831:     {
                    832:        putchar('\r');
                    833:     }
                    834: }
                    835:
1.2       downsj    836: int readline(buffer, size, numeric)
1.1       downsj    837:
                    838: char *buffer;
                    839: int  size;
                    840: int  numeric;
                    841:
                    842: {
                    843:     register char *ptr = buffer;
                    844:     register char ch;
                    845:     register char cnt = 0;
                    846:     register char maxcnt = 0;
                    847:
                    848:     /* allow room for null terminator */
                    849:     size -= 1;
                    850:
                    851:     /* read loop */
                    852:     while ((fflush(stdout), read(0, ptr, 1) > 0))
                    853:     {
                    854:        /* newline means we are done */
                    855:        if ((ch = *ptr) == '\n')
                    856:        {
                    857:            break;
                    858:        }
                    859:
                    860:        /* handle special editing characters */
                    861:        if (ch == ch_kill)
                    862:        {
                    863:            /* kill line -- account for overstriking */
                    864:            if (overstrike)
                    865:            {
                    866:                msglen += maxcnt;
                    867:            }
                    868:
                    869:            /* return null string */
                    870:            *buffer = '\0';
                    871:            putchar('\r');
                    872:            return(-1);
                    873:        }
                    874:        else if (ch == ch_erase)
                    875:        {
                    876:            /* erase previous character */
                    877:            if (cnt <= 0)
                    878:            {
                    879:                /* none to erase! */
                    880:                putchar('\7');
                    881:            }
                    882:            else
                    883:            {
                    884:                fputs("\b \b", stdout);
                    885:                ptr--;
                    886:                cnt--;
                    887:            }
                    888:        }
                    889:        /* check for character validity and buffer overflow */
                    890:        else if (cnt == size || (numeric && !isdigit(ch)) ||
                    891:                !isprint(ch))
                    892:        {
                    893:            /* not legal */
                    894:            putchar('\7');
                    895:        }
                    896:        else
                    897:        {
                    898:            /* echo it and store it in the buffer */
                    899:            putchar(ch);
                    900:            ptr++;
                    901:            cnt++;
                    902:            if (cnt > maxcnt)
                    903:            {
                    904:                maxcnt = cnt;
                    905:            }
                    906:        }
                    907:     }
                    908:
                    909:     /* all done -- null terminate the string */
                    910:     *ptr = '\0';
                    911:
                    912:     /* account for the extra characters in the message area */
                    913:     /* (if terminal overstrikes, remember the furthest they went) */
                    914:     msglen += overstrike ? maxcnt : cnt;
                    915:
                    916:     /* return either inputted number or string length */
                    917:     putchar('\r');
                    918:     return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
                    919: }
                    920:
                    921: /* internal support routines */
                    922:
                    923: static int string_count(pp)
                    924:
                    925: register char **pp;
                    926:
                    927: {
                    928:     register int cnt;
                    929:
                    930:     cnt = 0;
                    931:     while (*pp++ != NULL)
                    932:     {
                    933:        cnt++;
                    934:     }
                    935:     return(cnt);
                    936: }
                    937:
1.4     ! millert   938: static void summary_format(str, numbers, names)
1.1       downsj    939:
                    940: char *str;
                    941: int *numbers;
                    942: register char **names;
                    943:
                    944: {
1.4     ! millert   945:     register char *p;
1.1       downsj    946:     register int num;
                    947:     register char *thisname;
                    948:
                    949:     /* format each number followed by its string */
1.4     ! millert   950:     p = str;
1.1       downsj    951:     while ((thisname = *names++) != NULL)
                    952:     {
                    953:        /* get the number to format */
                    954:        num = *numbers++;
                    955:
                    956:        /* display only non-zero numbers */
                    957:        if (num > 0)
                    958:        {
                    959:            /* is this number in kilobytes? */
                    960:            if (thisname[0] == 'K')
                    961:            {
                    962:                /* yes: format it as a memory value */
1.4     ! millert   963:                p = strecpy(p, format_k(num));
1.1       downsj    964:
                    965:                /* skip over the K, since it was included by format_k */
1.4     ! millert   966:                p = strecpy(p, thisname+1);
1.1       downsj    967:            }
                    968:            else
                    969:            {
1.4     ! millert   970:                p = strecpy(p, itoa(num));
        !           971:                p = strecpy(p, thisname);
1.1       downsj    972:            }
                    973:        }
                    974:
                    975:        /* ignore negative numbers, but display corresponding string */
                    976:        else if (num < 0)
                    977:        {
1.4     ! millert   978:            p = strecpy(p, thisname);
1.1       downsj    979:        }
                    980:     }
                    981:
                    982:     /* if the last two characters in the string are ", ", delete them */
1.4     ! millert   983:     p -= 2;
        !           984:     if (p >= str && p[0] == ',' && p[1] == ' ')
1.1       downsj    985:     {
1.4     ! millert   986:        *p = '\0';
1.1       downsj    987:     }
                    988: }
                    989:
                    990: static void line_update(old, new, start, line)
                    991:
                    992: register char *old;
                    993: register char *new;
                    994: int start;
                    995: int line;
                    996:
                    997: {
                    998:     register int ch;
                    999:     register int diff;
                   1000:     register int newcol = start + 1;
                   1001:     register int lastcol = start;
                   1002:     char cursor_on_line = No;
                   1003:     char *current;
                   1004:
                   1005:     /* compare the two strings and only rewrite what has changed */
                   1006:     current = old;
                   1007: #ifdef DEBUG
                   1008:     fprintf(debug, "line_update, starting at %d\n", start);
                   1009:     fputs(old, debug);
                   1010:     fputc('\n', debug);
                   1011:     fputs(new, debug);
                   1012:     fputs("\n-\n", debug);
                   1013: #endif
                   1014:
                   1015:     /* start things off on the right foot                  */
                   1016:     /* this is to make sure the invariants get set up right */
                   1017:     if ((ch = *new++) != *old)
                   1018:     {
                   1019:        if (line - lastline == 1 && start == 0)
                   1020:        {
                   1021:            putchar('\n');
                   1022:        }
                   1023:        else
                   1024:        {
                   1025:            Move_to(start, line);
                   1026:        }
                   1027:        cursor_on_line = Yes;
                   1028:        putchar(ch);
                   1029:        *old = ch;
                   1030:        lastcol = 1;
                   1031:     }
                   1032:     old++;
                   1033:
                   1034:     /*
                   1035:      *  main loop -- check each character.  If the old and new aren't the
                   1036:      * same, then update the display.  When the distance from the
                   1037:      * current cursor position to the new change is small enough,
                   1038:      * the characters that belong there are written to move the
                   1039:      * cursor over.
                   1040:      *
                   1041:      * Invariants:
                   1042:      *     lastcol is the column where the cursor currently is sitting
                   1043:      *         (always one beyond the end of the last mismatch).
                   1044:      */
                   1045:     do         /* yes, a do...while */
                   1046:     {
                   1047:        if ((ch = *new++) != *old)
                   1048:        {
                   1049:            /* new character is different from old        */
                   1050:            /* make sure the cursor is on top of this character */
                   1051:            diff = newcol - lastcol;
                   1052:            if (diff > 0)
                   1053:            {
                   1054:                /* some motion is required--figure out which is shorter */
                   1055:                if (diff < 6 && cursor_on_line)
                   1056:                {
                   1057:                    /* overwrite old stuff--get it out of the old buffer */
                   1058:                    printf("%.*s", diff, &current[lastcol-start]);
                   1059:                }
                   1060:                else
                   1061:                {
                   1062:                    /* use cursor addressing */
                   1063:                    Move_to(newcol, line);
                   1064:                    cursor_on_line = Yes;
                   1065:                }
                   1066:                /* remember where the cursor is */
                   1067:                lastcol = newcol + 1;
                   1068:            }
                   1069:            else
                   1070:            {
                   1071:                /* already there, update position */
                   1072:                lastcol++;
                   1073:            }
                   1074:
                   1075:            /* write what we need to */
                   1076:            if (ch == '\0')
                   1077:            {
                   1078:                /* at the end--terminate with a clear-to-end-of-line */
                   1079:                (void) clear_eol(strlen(old));
                   1080:            }
                   1081:            else
                   1082:            {
                   1083:                /* write the new character */
                   1084:                putchar(ch);
                   1085:            }
                   1086:            /* put the new character in the screen buffer */
                   1087:            *old = ch;
                   1088:        }
                   1089:
                   1090:        /* update working column and screen buffer pointer */
                   1091:        newcol++;
                   1092:        old++;
                   1093:
                   1094:     } while (ch != '\0');
                   1095:
                   1096:     /* zero out the rest of the line buffer -- MUST BE DONE! */
                   1097:     diff = display_width - newcol;
                   1098:     if (diff > 0)
                   1099:     {
1.2       downsj   1100:        memset(old, 0, diff);
1.1       downsj   1101:     }
                   1102:
                   1103:     /* remember where the current line is */
                   1104:     if (cursor_on_line)
                   1105:     {
                   1106:        lastline = line;
                   1107:     }
                   1108: }
                   1109:
                   1110: /*
                   1111:  *  printable(str) - make the string pointed to by "str" into one that is
                   1112:  *     printable (i.e.: all ascii), by converting all non-printable
                   1113:  *     characters into '?'.  Replacements are done in place and a pointer
                   1114:  *     to the original buffer is returned.
                   1115:  */
                   1116:
                   1117: char *printable(str)
                   1118:
                   1119: char *str;
                   1120:
                   1121: {
                   1122:     register char *ptr;
                   1123:     register char ch;
                   1124:
                   1125:     ptr = str;
                   1126:     while ((ch = *ptr) != '\0')
                   1127:     {
                   1128:        if (!isprint(ch))
                   1129:        {
                   1130:            *ptr = '?';
                   1131:        }
                   1132:        ptr++;
                   1133:     }
                   1134:     return(str);
                   1135: }