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

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