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

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