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

Annotation of src/usr.bin/less/prompt.c, Revision 1.15

1.1       etheisen    1: /*
1.13      shadchin    2:  * Copyright (C) 1984-2012  Mark Nudelman
1.15    ! nicm        3:  * Modified for use with illumos by Garrett D'Amore.
        !             4:  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
1.1       etheisen    5:  *
1.7       millert     6:  * You may distribute under the terms of either the GNU General Public
                      7:  * License or the Less License, as specified in the README file.
1.1       etheisen    8:  *
1.13      shadchin    9:  * For more information, see the README file.
1.14      nicm       10:  */
1.1       etheisen   11:
                     12: /*
                     13:  * Prompting and other messages.
                     14:  * There are three flavors of prompts, SHORT, MEDIUM and LONG,
                     15:  * selected by the -m/-M options.
                     16:  * There is also the "equals message", printed by the = command.
1.14      nicm       17:  * A prompt is a message composed of various pieces, such as the
1.1       etheisen   18:  * name of the file being viewed, the percentage into the file, etc.
                     19:  */
                     20:
                     21: #include "less.h"
                     22: #include "position.h"
                     23:
                     24: extern int pr_type;
                     25: extern int new_file;
                     26: extern int sc_width;
                     27: extern int so_s_width, so_e_width;
                     28: extern int linenums;
1.7       millert    29: extern int hshift;
1.1       etheisen   30: extern int sc_height;
                     31: extern int jump_sline;
1.12      shadchin   32: extern int less_is_more;
1.1       etheisen   33: extern IFILE curr_ifile;
                     34: extern char *editor;
1.7       millert    35: extern char *editproto;
1.1       etheisen   36:
                     37: /*
                     38:  * Prototypes for the three flavors of prompts.
                     39:  * These strings are expanded by pr_expand().
                     40:  */
1.14      nicm       41: static const char s_proto[] =
                     42:        "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t";
                     43: static const char m_proto[] =
                     44:        "?n?f%f .?m(%T %i of %m) ..?e(END) "
                     45:        "?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
                     46: static const char M_proto[] =
                     47:        "?f%f .?n?m(%T %i of %m) ..?"
                     48:        "ltlines %lt-%lb?L/%L. :byte %bB?s/%s. .?e(END)"
                     49:        " ?x- Next\\: %x.:?pB%pB\\%..%t";
                     50: static const char e_proto[] =
                     51:        "?f%f .?m(%T %i of %m) .?ltlines "
                     52:        "%lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
                     53: static const char h_proto[] =
                     54:        "HELP -- ?eEND -- Press g to see it again:"
                     55:        "Press RETURN for more., or q when done";
                     56: static const char w_proto[] =
                     57:        "Waiting for data";
                     58: static const char more_proto[] =
                     59:        "%f (?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)";
                     60: static const char more_M_proto[] =
                     61:        "%f (?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)"
                     62:        "[Press space to continue, q to quit, h for help]";
                     63:
                     64: char *prproto[3];
                     65: char const *eqproto = e_proto;
                     66: char const *hproto = h_proto;
                     67: char const *wproto = w_proto;
1.1       etheisen   68:
1.7       millert    69: static char message[PROMPT_SIZE];
1.1       etheisen   70: static char *mp;
                     71:
                     72: /*
                     73:  * Initialize the prompt prototype strings.
                     74:  */
1.14      nicm       75: void
                     76: init_prompt(void)
1.1       etheisen   77: {
1.9       millert    78:        prproto[0] = save(s_proto);
1.14      nicm       79:        prproto[1] = save(less_is_more ? more_proto : m_proto);
                     80:        prproto[2] = save(less_is_more ? more_M_proto : M_proto);
1.1       etheisen   81:        eqproto = save(e_proto);
1.7       millert    82:        hproto = save(h_proto);
                     83:        wproto = save(w_proto);
1.1       etheisen   84: }
                     85:
                     86: /*
1.7       millert    87:  * Append a string to the end of the message.
1.1       etheisen   88:  */
1.14      nicm       89: static void
                     90: ap_str(char *s)
1.1       etheisen   91: {
1.7       millert    92:        int len;
                     93:
                     94:        len = strlen(s);
                     95:        if (mp + len >= message + PROMPT_SIZE)
                     96:                len = message + PROMPT_SIZE - mp - 1;
1.14      nicm       97:        (void) strncpy(mp, s, len);
1.7       millert    98:        mp += len;
                     99:        *mp = '\0';
                    100: }
                    101:
                    102: /*
                    103:  * Append a character to the end of the message.
                    104:  */
1.14      nicm      105: static void
                    106: ap_char(char c)
1.7       millert   107: {
                    108:        char buf[2];
                    109:
                    110:        buf[0] = c;
                    111:        buf[1] = '\0';
                    112:        ap_str(buf);
1.1       etheisen  113: }
                    114:
                    115: /*
1.14      nicm      116:  * Append a off_t (as a decimal integer) to the end of the message.
1.1       etheisen  117:  */
1.14      nicm      118: static void
                    119: ap_pos(off_t pos)
1.1       etheisen  120: {
1.7       millert   121:        char buf[INT_STRLEN_BOUND(pos) + 2];
                    122:
1.14      nicm      123:        postoa(pos, buf, sizeof buf);
1.7       millert   124:        ap_str(buf);
1.1       etheisen  125: }
                    126:
                    127: /*
1.7       millert   128:  * Append a line number to the end of the message.
1.1       etheisen  129:  */
1.14      nicm      130: static void
                    131: ap_linenum(LINENUM linenum)
1.1       etheisen  132: {
1.7       millert   133:        char buf[INT_STRLEN_BOUND(linenum) + 2];
                    134:
1.14      nicm      135:        linenumtoa(linenum, buf, sizeof buf);
1.7       millert   136:        ap_str(buf);
1.1       etheisen  137: }
                    138:
                    139: /*
1.7       millert   140:  * Append an integer to the end of the message.
1.1       etheisen  141:  */
1.14      nicm      142: static void
                    143: ap_int(int num)
1.1       etheisen  144: {
1.7       millert   145:        char buf[INT_STRLEN_BOUND(num) + 2];
                    146:
1.14      nicm      147:        inttoa(num, buf, sizeof buf);
1.7       millert   148:        ap_str(buf);
1.1       etheisen  149: }
                    150:
                    151: /*
                    152:  * Append a question mark to the end of the message.
                    153:  */
1.14      nicm      154: static void
                    155: ap_quest(void)
1.1       etheisen  156: {
1.7       millert   157:        ap_str("?");
1.1       etheisen  158: }
                    159:
                    160: /*
                    161:  * Return the "current" byte offset in the file.
                    162:  */
1.14      nicm      163: static off_t
                    164: curr_byte(int where)
1.1       etheisen  165: {
1.14      nicm      166:        off_t pos;
1.1       etheisen  167:
                    168:        pos = position(where);
1.14      nicm      169:        while (pos == -1 && where >= 0 && where < sc_height-1)
1.1       etheisen  170:                pos = position(++where);
1.14      nicm      171:        if (pos == -1)
1.1       etheisen  172:                pos = ch_length();
                    173:        return (pos);
                    174: }
                    175:
                    176: /*
                    177:  * Return the value of a prototype conditional.
1.14      nicm      178:  * A prototype string may include conditionals which consist of a
1.1       etheisen  179:  * question mark followed by a single letter.
                    180:  * Here we decode that letter and return the appropriate boolean value.
                    181:  */
1.14      nicm      182: static int
                    183: cond(char c, int where)
1.1       etheisen  184: {
1.14      nicm      185:        off_t len;
1.7       millert   186:
1.14      nicm      187:        switch (c) {
1.1       etheisen  188:        case 'a':       /* Anything in the message yet? */
                    189:                return (mp > message);
                    190:        case 'b':       /* Current byte offset known? */
1.14      nicm      191:                return (curr_byte(where) != -1);
1.7       millert   192:        case 'c':
                    193:                return (hshift != 0);
1.1       etheisen  194:        case 'e':       /* At end of file? */
1.12      shadchin  195:                return (eof_displayed());
1.1       etheisen  196:        case 'f':       /* Filename known? */
                    197:                return (strcmp(get_filename(curr_ifile), "-") != 0);
                    198:        case 'l':       /* Line number known? */
1.7       millert   199:        case 'd':       /* Same as l */
1.1       etheisen  200:                return (linenums);
                    201:        case 'L':       /* Final line number known? */
1.12      shadchin  202:        case 'D':       /* Final page number known? */
1.14      nicm      203:                return (linenums && ch_length() != -1);
1.1       etheisen  204:        case 'm':       /* More than one file? */
1.7       millert   205:                return (ntags() ? (ntags() > 1) : (nifile() > 1));
1.1       etheisen  206:        case 'n':       /* First prompt in a new file? */
1.7       millert   207:                return (ntags() ? 1 : new_file);
                    208:        case 'p':       /* Percent into file (bytes) known? */
1.14      nicm      209:                return (curr_byte(where) != -1 && ch_length() > 0);
1.7       millert   210:        case 'P':       /* Percent into file (lines) known? */
                    211:                return (currline(where) != 0 &&
1.14      nicm      212:                    (len = ch_length()) > 0 && find_linenum(len) != 0);
1.1       etheisen  213:        case 's':       /* Size of file known? */
                    214:        case 'B':
1.14      nicm      215:                return (ch_length() != -1);
1.1       etheisen  216:        case 'x':       /* Is there a "next" file? */
1.7       millert   217:                if (ntags())
                    218:                        return (0);
1.1       etheisen  219:                return (next_ifile(curr_ifile) != NULL_IFILE);
                    220:        }
                    221:        return (0);
                    222: }
                    223:
                    224: /*
                    225:  * Decode a "percent" prototype character.
                    226:  * A prototype string may include various "percent" escapes;
                    227:  * that is, a percent sign followed by a single letter.
                    228:  * Here we decode that letter and take the appropriate action,
                    229:  * usually by appending something to the message being built.
                    230:  */
1.14      nicm      231: static void
                    232: protochar(int c, int where)
1.1       etheisen  233: {
1.14      nicm      234:        off_t pos;
                    235:        off_t len;
1.1       etheisen  236:        int n;
1.7       millert   237:        LINENUM linenum;
                    238:        LINENUM last_linenum;
1.1       etheisen  239:        IFILE h;
                    240:
1.14      nicm      241: #undef PAGE_NUM
                    242: #define        PAGE_NUM(linenum)  ((((linenum) - 1) / (sc_height - 1)) + 1)
1.12      shadchin  243:
1.14      nicm      244:        switch (c) {
1.1       etheisen  245:        case 'b':       /* Current byte offset */
                    246:                pos = curr_byte(where);
1.14      nicm      247:                if (pos != -1)
1.1       etheisen  248:                        ap_pos(pos);
                    249:                else
                    250:                        ap_quest();
                    251:                break;
1.7       millert   252:        case 'c':
                    253:                ap_int(hshift);
                    254:                break;
                    255:        case 'd':       /* Current page number */
                    256:                linenum = currline(where);
                    257:                if (linenum > 0 && sc_height > 1)
1.12      shadchin  258:                        ap_linenum(PAGE_NUM(linenum));
1.7       millert   259:                else
                    260:                        ap_quest();
                    261:                break;
1.12      shadchin  262:        case 'D':       /* Final page number */
                    263:                /* Find the page number of the last byte in the file (len-1). */
1.7       millert   264:                len = ch_length();
1.14      nicm      265:                if (len == -1) {
1.7       millert   266:                        ap_quest();
1.14      nicm      267:                } else if (len == 0) {
1.12      shadchin  268:                        /* An empty file has no pages. */
                    269:                        ap_linenum(0);
1.14      nicm      270:                } else {
1.12      shadchin  271:                        linenum = find_linenum(len - 1);
                    272:                        if (linenum <= 0)
                    273:                                ap_quest();
1.14      nicm      274:                        else
1.12      shadchin  275:                                ap_linenum(PAGE_NUM(linenum));
                    276:                }
1.7       millert   277:                break;
1.1       etheisen  278:        case 'E':       /* Editor name */
                    279:                ap_str(editor);
                    280:                break;
                    281:        case 'f':       /* File name */
                    282:                ap_str(get_filename(curr_ifile));
                    283:                break;
1.12      shadchin  284:        case 'F':       /* Last component of file name */
                    285:                ap_str(last_component(get_filename(curr_ifile)));
                    286:                break;
1.1       etheisen  287:        case 'i':       /* Index into list of files */
1.7       millert   288:                if (ntags())
                    289:                        ap_int(curr_tag());
                    290:                else
                    291:                        ap_int(get_index(curr_ifile));
1.1       etheisen  292:                break;
                    293:        case 'l':       /* Current line number */
1.7       millert   294:                linenum = currline(where);
                    295:                if (linenum != 0)
                    296:                        ap_linenum(linenum);
1.1       etheisen  297:                else
                    298:                        ap_quest();
                    299:                break;
                    300:        case 'L':       /* Final line number */
                    301:                len = ch_length();
1.14      nicm      302:                if (len == -1 || len == ch_zero() ||
1.7       millert   303:                    (linenum = find_linenum(len)) <= 0)
1.1       etheisen  304:                        ap_quest();
                    305:                else
1.7       millert   306:                        ap_linenum(linenum-1);
1.1       etheisen  307:                break;
                    308:        case 'm':       /* Number of files */
1.7       millert   309:                n = ntags();
                    310:                if (n)
                    311:                        ap_int(n);
                    312:                else
                    313:                        ap_int(nifile());
1.1       etheisen  314:                break;
1.7       millert   315:        case 'p':       /* Percent into file (bytes) */
1.1       etheisen  316:                pos = curr_byte(where);
                    317:                len = ch_length();
1.14      nicm      318:                if (pos != -1 && len > 0)
                    319:                        ap_int(percentage(pos, len));
1.1       etheisen  320:                else
                    321:                        ap_quest();
                    322:                break;
1.7       millert   323:        case 'P':       /* Percent into file (lines) */
                    324:                linenum = currline(where);
                    325:                if (linenum == 0 ||
1.14      nicm      326:                    (len = ch_length()) == -1 || len == ch_zero() ||
1.7       millert   327:                    (last_linenum = find_linenum(len)) <= 0)
                    328:                        ap_quest();
                    329:                else
                    330:                        ap_int(percentage(linenum, last_linenum));
                    331:                break;
1.1       etheisen  332:        case 's':       /* Size of file */
                    333:        case 'B':
                    334:                len = ch_length();
1.14      nicm      335:                if (len != -1)
1.1       etheisen  336:                        ap_pos(len);
                    337:                else
                    338:                        ap_quest();
                    339:                break;
                    340:        case 't':       /* Truncate trailing spaces in the message */
                    341:                while (mp > message && mp[-1] == ' ')
                    342:                        mp--;
1.10      jasper    343:                *mp = '\0';
1.1       etheisen  344:                break;
1.7       millert   345:        case 'T':       /* Type of list */
                    346:                if (ntags())
                    347:                        ap_str("tag");
                    348:                else
                    349:                        ap_str("file");
                    350:                break;
1.1       etheisen  351:        case 'x':       /* Name of next file */
                    352:                h = next_ifile(curr_ifile);
                    353:                if (h != NULL_IFILE)
                    354:                        ap_str(get_filename(h));
                    355:                else
                    356:                        ap_quest();
                    357:                break;
                    358:        }
                    359: }
                    360:
                    361: /*
                    362:  * Skip a false conditional.
1.14      nicm      363:  * When a false condition is found (either a false IF or the ELSE part
1.1       etheisen  364:  * of a true IF), this routine scans the prototype string to decide
                    365:  * where to resume parsing the string.
                    366:  * We must keep track of nested IFs and skip them properly.
                    367:  */
1.14      nicm      368: static const char *
                    369: skipcond(const char *p)
1.1       etheisen  370: {
1.14      nicm      371:        int iflevel;
1.1       etheisen  372:
                    373:        /*
                    374:         * We came in here after processing a ? or :,
                    375:         * so we start nested one level deep.
                    376:         */
                    377:        iflevel = 1;
                    378:
1.14      nicm      379:        for (;;) {
                    380:                switch (*++p) {
                    381:                case '?':
                    382:                        /*
                    383:                         * Start of a nested IF.
                    384:                         */
                    385:                        iflevel++;
                    386:                        break;
                    387:                case ':':
                    388:                        /*
                    389:                         * Else.
                    390:                         * If this matches the IF we came in here with,
                    391:                         * then we're done.
                    392:                         */
                    393:                        if (iflevel == 1)
                    394:                                return (p);
                    395:                        break;
                    396:                case '.':
                    397:                        /*
                    398:                         * Endif.
                    399:                         * If this matches the IF we came in here with,
                    400:                         * then we're done.
                    401:                         */
                    402:                        if (--iflevel == 0)
                    403:                                return (p);
                    404:                        break;
                    405:                case '\\':
                    406:                        /*
                    407:                         * Backslash escapes the next character.
                    408:                         */
                    409:                        ++p;
                    410:                        break;
                    411:                case '\0':
                    412:                        /*
                    413:                         * Whoops.  Hit end of string.
                    414:                         * This is a malformed conditional, but just treat it
                    415:                         * as if all active conditionals ends here.
                    416:                         */
                    417:                        return (p-1);
                    418:                }
1.1       etheisen  419:        }
                    420:        /*NOTREACHED*/
                    421: }
                    422:
1.7       millert   423: /*
                    424:  * Decode a char that represents a position on the screen.
                    425:  */
1.14      nicm      426: static const char *
                    427: wherechar(const char *p, int *wp)
1.1       etheisen  428: {
1.14      nicm      429:        switch (*p) {
1.7       millert   430:        case 'b': case 'd': case 'l': case 'p': case 'P':
1.14      nicm      431:                switch (*++p) {
1.1       etheisen  432:                case 't':   *wp = TOP;                  break;
                    433:                case 'm':   *wp = MIDDLE;               break;
                    434:                case 'b':   *wp = BOTTOM;               break;
                    435:                case 'B':   *wp = BOTTOM_PLUS_ONE;      break;
                    436:                case 'j':   *wp = adjsline(jump_sline); break;
                    437:                default:    *wp = TOP;  p--;            break;
                    438:                }
                    439:        }
                    440:        return (p);
                    441: }
                    442:
                    443: /*
                    444:  * Construct a message based on a prototype string.
                    445:  */
1.14      nicm      446: char *
                    447: pr_expand(const char *proto, int maxwidth)
1.1       etheisen  448: {
1.14      nicm      449:        const char *p;
                    450:        int c;
1.1       etheisen  451:        int where;
                    452:
                    453:        mp = message;
                    454:
                    455:        if (*proto == '\0')
                    456:                return ("");
                    457:
1.14      nicm      458:        for (p = proto; *p != '\0'; p++) {
                    459:                switch (*p) {
1.1       etheisen  460:                default:        /* Just put the character in the message */
1.7       millert   461:                        ap_char(*p);
1.1       etheisen  462:                        break;
                    463:                case '\\':      /* Backslash escapes the next character */
                    464:                        p++;
1.7       millert   465:                        ap_char(*p);
1.1       etheisen  466:                        break;
                    467:                case '?':       /* Conditional (IF) */
1.14      nicm      468:                        if ((c = *++p) == '\0') {
1.1       etheisen  469:                                --p;
1.14      nicm      470:                        } else {
1.7       millert   471:                                where = 0;
1.1       etheisen  472:                                p = wherechar(p, &where);
                    473:                                if (!cond(c, where))
                    474:                                        p = skipcond(p);
                    475:                        }
                    476:                        break;
                    477:                case ':':       /* ELSE */
                    478:                        p = skipcond(p);
                    479:                        break;
                    480:                case '.':       /* ENDIF */
                    481:                        break;
                    482:                case '%':       /* Percent escape */
1.14      nicm      483:                        if ((c = *++p) == '\0') {
1.1       etheisen  484:                                --p;
1.14      nicm      485:                        } else {
1.7       millert   486:                                where = 0;
1.1       etheisen  487:                                p = wherechar(p, &where);
1.14      nicm      488:                                protochar(c, where);
1.1       etheisen  489:                        }
                    490:                        break;
                    491:                }
                    492:        }
                    493:
                    494:        if (mp == message)
1.12      shadchin  495:                return ("");
1.14      nicm      496:        if (maxwidth > 0 && mp >= message + maxwidth) {
1.1       etheisen  497:                /*
                    498:                 * Message is too long.
                    499:                 * Return just the final portion of it.
                    500:                 */
                    501:                return (mp - maxwidth);
                    502:        }
                    503:        return (message);
                    504: }
                    505:
                    506: /*
                    507:  * Return a message suitable for printing by the "=" command.
                    508:  */
1.14      nicm      509: char *
                    510: eq_message(void)
1.1       etheisen  511: {
                    512:        return (pr_expand(eqproto, 0));
                    513: }
                    514:
                    515: /*
                    516:  * Return a prompt.
                    517:  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
                    518:  * If we can't come up with an appropriate prompt, return NULL
                    519:  * and the caller will prompt with a colon.
                    520:  */
1.14      nicm      521: char *
                    522: prompt_string(void)
1.1       etheisen  523: {
1.7       millert   524:        char *prompt;
1.12      shadchin  525:        int type;
1.7       millert   526:
1.14      nicm      527:        type = pr_type;
1.12      shadchin  528:        prompt = pr_expand((ch_getflags() & CH_HELPFILE) ?
1.14      nicm      529:            hproto : prproto[type], sc_width-so_s_width-so_e_width-2);
1.7       millert   530:        new_file = 0;
                    531:        return (prompt);
                    532: }
                    533:
                    534: /*
                    535:  * Return a message suitable for printing while waiting in the F command.
                    536:  */
1.14      nicm      537: char *
                    538: wait_message(void)
1.7       millert   539: {
                    540:        return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2));
1.1       etheisen  541: }