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

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