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

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