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

1.5     ! mpech       1: /*     $OpenBSD: prompt.c,v 1.4 2001/01/29 01:58:03 niklas Exp $       */
1.4       niklas      2:
1.1       etheisen    3: /*
                      4:  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice in the documentation and/or other materials provided with
                     14:  *    the distribution.
                     15:  *
                     16:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
                     17:  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     18:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     19:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
                     20:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     21:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
                     22:  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
                     23:  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     24:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
                     25:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
                     26:  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     27:  */
                     28:
                     29:
                     30: /*
                     31:  * Prompting and other messages.
                     32:  * There are three flavors of prompts, SHORT, MEDIUM and LONG,
                     33:  * selected by the -m/-M options.
                     34:  * There is also the "equals message", printed by the = command.
                     35:  * A prompt is a message composed of various pieces, such as the
                     36:  * name of the file being viewed, the percentage into the file, etc.
                     37:  */
                     38:
                     39: #include "less.h"
                     40: #include "position.h"
                     41:
                     42: extern int pr_type;
                     43: extern int hit_eof;
                     44: extern int new_file;
                     45: extern int sc_width;
                     46: extern int so_s_width, so_e_width;
                     47: extern int linenums;
                     48: extern int sc_height;
                     49: extern int jump_sline;
                     50: extern IFILE curr_ifile;
                     51: #if EDITOR
                     52: extern char *editor;
                     53: #endif
                     54:
                     55: /*
                     56:  * Prototypes for the three flavors of prompts.
                     57:  * These strings are expanded by pr_expand().
                     58:  */
                     59: static char s_proto[] =
                     60:   "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
                     61: static char m_proto[] =
1.2       etheisen   62:   "?f%f .?m(file %i of %m) .?e(END) ?x- Next\\: %x.:(?pB%pB\\%:byte %bB?s/%s..).%t";
1.1       etheisen   63: static char M_proto[] =
                     64:   "?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
                     65: static char e_proto[] =
                     66:   "?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
                     67:
                     68: public char *prproto[3];
                     69: public char *eqproto = e_proto;
                     70:
                     71: static char message[250];
                     72: static char *mp;
                     73:
                     74: /*
                     75:  * Initialize the prompt prototype strings.
                     76:  */
                     77:        public void
                     78: init_prompt()
                     79: {
                     80:        prproto[0] = save(s_proto);
                     81:        prproto[1] = save(m_proto);
                     82:        prproto[2] = save(M_proto);
                     83:        eqproto = save(e_proto);
                     84: }
                     85:
                     86: /*
                     87:  * Set the message pointer to the end of the message string.
                     88:  */
                     89:        static void
                     90: setmp()
                     91: {
                     92:        while (*mp != '\0')
                     93:                mp++;
                     94: }
                     95:
                     96: /*
                     97:  * Append a POSITION (as a decimal integer) to the end of the message.
                     98:  */
                     99:        static void
                    100: ap_pos(pos)
                    101:        POSITION pos;
                    102: {
1.3       millert   103:        sprintf(mp, "%qd", pos);
1.1       etheisen  104:        setmp();
                    105: }
                    106:
                    107: /*
                    108:  * Append an integer to the end of the message.
                    109:  */
                    110:        static void
                    111: ap_int(n)
                    112:        int n;
                    113: {
                    114:        sprintf(mp, "%d", n);
                    115:        setmp();
                    116: }
                    117:
                    118: /*
                    119:  * Append a string to the end of the message.
                    120:  */
                    121:        static void
                    122: ap_str(s)
                    123:        char *s;
                    124: {
                    125:        strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp));
                    126:        setmp();
                    127: }
                    128:
                    129: /*
                    130:  * Append a question mark to the end of the message.
                    131:  */
                    132:        static void
                    133: ap_quest()
                    134: {
                    135:        *mp++ = '?';
                    136: }
                    137:
                    138: /*
                    139:  * Return the "current" byte offset in the file.
                    140:  */
                    141:        static POSITION
                    142: curr_byte(where)
                    143:        int where;
                    144: {
                    145:        POSITION pos;
                    146:
                    147:        pos = position(where);
                    148:        while (pos == NULL_POSITION && where >= 0 && where < sc_height)
                    149:                pos = position(++where);
                    150:        if (pos == NULL_POSITION)
                    151:                pos = ch_length();
                    152:        return (pos);
                    153: }
                    154:
                    155: /*
                    156:  * Return the value of a prototype conditional.
                    157:  * A prototype string may include conditionals which consist of a
                    158:  * question mark followed by a single letter.
                    159:  * Here we decode that letter and return the appropriate boolean value.
                    160:  */
                    161:        static int
                    162: cond(c, where)
                    163:        char c;
                    164:        int where;
                    165: {
                    166:        switch (c)
                    167:        {
                    168:        case 'a':       /* Anything in the message yet? */
                    169:                return (mp > message);
                    170:        case 'b':       /* Current byte offset known? */
                    171:                return (curr_byte(where) != NULL_POSITION);
                    172:        case 'e':       /* At end of file? */
                    173:                return (hit_eof);
                    174:        case 'f':       /* Filename known? */
                    175:                return (strcmp(get_filename(curr_ifile), "-") != 0);
                    176:        case 'l':       /* Line number known? */
                    177:                return (linenums);
                    178:        case 'L':       /* Final line number known? */
                    179:                return (linenums && ch_length() != NULL_POSITION);
                    180:        case 'm':       /* More than one file? */
                    181:                return (nifile() > 1);
                    182:        case 'n':       /* First prompt in a new file? */
                    183:                return (new_file);
                    184:        case 'p':       /* Percent into file known? */
                    185:                return (curr_byte(where) != NULL_POSITION &&
                    186:                                ch_length() > 0);
                    187:        case 's':       /* Size of file known? */
                    188:        case 'B':
                    189:                return (ch_length() != NULL_POSITION);
                    190:        case 'x':       /* Is there a "next" file? */
                    191:                return (next_ifile(curr_ifile) != NULL_IFILE);
                    192:        }
                    193:        return (0);
                    194: }
                    195:
                    196: /*
                    197:  * Decode a "percent" prototype character.
                    198:  * A prototype string may include various "percent" escapes;
                    199:  * that is, a percent sign followed by a single letter.
                    200:  * Here we decode that letter and take the appropriate action,
                    201:  * usually by appending something to the message being built.
                    202:  */
                    203:        static void
                    204: protochar(c, where)
                    205:        int c;
                    206:        int where;
                    207: {
                    208:        POSITION pos;
                    209:        POSITION len;
                    210:        int n;
                    211:        IFILE h;
                    212:
                    213:        switch (c)
                    214:        {
                    215:        case 'b':       /* Current byte offset */
                    216:                pos = curr_byte(where);
                    217:                if (pos != NULL_POSITION)
                    218:                        ap_pos(pos);
                    219:                else
                    220:                        ap_quest();
                    221:                break;
                    222: #if EDITOR
                    223:        case 'E':       /* Editor name */
                    224:                ap_str(editor);
                    225:                break;
                    226: #endif
                    227:        case 'f':       /* File name */
                    228:                ap_str(get_filename(curr_ifile));
                    229:                break;
                    230:        case 'i':       /* Index into list of files */
                    231:                ap_int(get_index(curr_ifile));
                    232:                break;
                    233:        case 'l':       /* Current line number */
                    234:                n = currline(where);
                    235:                if (n != 0)
                    236:                        ap_int(n);
                    237:                else
                    238:                        ap_quest();
                    239:                break;
                    240:        case 'L':       /* Final line number */
                    241:                len = ch_length();
                    242:                if (len == NULL_POSITION || len == ch_zero() ||
                    243:                    (n = find_linenum(len)) <= 0)
                    244:                        ap_quest();
                    245:                else
                    246:                        ap_int(n-1);
                    247:                break;
                    248:        case 'm':       /* Number of files */
                    249:                ap_int(nifile());
                    250:                break;
                    251:        case 'p':       /* Percent into file */
                    252:                pos = curr_byte(where);
                    253:                len = ch_length();
                    254:                if (pos != NULL_POSITION && len > 0)
                    255:                        ap_int(percentage(pos,len));
                    256:                else
                    257:                        ap_quest();
                    258:                break;
                    259:        case 's':       /* Size of file */
                    260:        case 'B':
                    261:                len = ch_length();
                    262:                if (len != NULL_POSITION)
                    263:                        ap_pos(len);
                    264:                else
                    265:                        ap_quest();
                    266:                break;
                    267:        case 't':       /* Truncate trailing spaces in the message */
                    268:                while (mp > message && mp[-1] == ' ')
                    269:                        mp--;
                    270:                break;
                    271:        case 'x':       /* Name of next file */
                    272:                h = next_ifile(curr_ifile);
                    273:                if (h != NULL_IFILE)
                    274:                        ap_str(get_filename(h));
                    275:                else
                    276:                        ap_quest();
                    277:                break;
                    278:        }
                    279: }
                    280:
                    281: /*
                    282:  * Skip a false conditional.
                    283:  * When a false condition is found (either a false IF or the ELSE part
                    284:  * of a true IF), this routine scans the prototype string to decide
                    285:  * where to resume parsing the string.
                    286:  * We must keep track of nested IFs and skip them properly.
                    287:  */
                    288:        static char *
                    289: skipcond(p)
1.5     ! mpech     290:        char *p;
1.1       etheisen  291: {
1.5     ! mpech     292:        int iflevel;
1.1       etheisen  293:
                    294:        /*
                    295:         * We came in here after processing a ? or :,
                    296:         * so we start nested one level deep.
                    297:         */
                    298:        iflevel = 1;
                    299:
                    300:        for (;;) switch (*++p)
                    301:        {
                    302:        case '?':
                    303:                /*
                    304:                 * Start of a nested IF.
                    305:                 */
                    306:                iflevel++;
                    307:                break;
                    308:        case ':':
                    309:                /*
                    310:                 * Else.
                    311:                 * If this matches the IF we came in here with,
                    312:                 * then we're done.
                    313:                 */
                    314:                if (iflevel == 1)
                    315:                        return (p);
                    316:                break;
                    317:        case '.':
                    318:                /*
                    319:                 * Endif.
                    320:                 * If this matches the IF we came in here with,
                    321:                 * then we're done.
                    322:                 */
                    323:                if (--iflevel == 0)
                    324:                        return (p);
                    325:                break;
                    326:        case '\\':
                    327:                /*
                    328:                 * Backslash escapes the next character.
                    329:                 */
                    330:                ++p;
                    331:                break;
                    332:        case '\0':
                    333:                /*
                    334:                 * Whoops.  Hit end of string.
                    335:                 * This is a malformed conditional, but just treat it
                    336:                 * as if all active conditionals ends here.
                    337:                 */
                    338:                return (p-1);
                    339:        }
                    340:        /*NOTREACHED*/
                    341: }
                    342:
                    343:        static char *
                    344: wherechar(p, wp)
                    345:        char *p;
                    346:        int *wp;
                    347: {
                    348:        switch (*p)
                    349:        {
                    350:        case 'b': case 'l': case 'p':
                    351:                switch (*++p)
                    352:                {
                    353:                case 't':   *wp = TOP;                  break;
                    354:                case 'm':   *wp = MIDDLE;               break;
                    355:                case 'b':   *wp = BOTTOM;               break;
                    356:                case 'B':   *wp = BOTTOM_PLUS_ONE;      break;
                    357:                case 'j':   *wp = adjsline(jump_sline); break;
                    358:                default:    *wp = TOP;  p--;            break;
                    359:                }
                    360:        }
                    361:        return (p);
                    362: }
                    363:
                    364: /*
                    365:  * Construct a message based on a prototype string.
                    366:  */
                    367:        public char *
                    368: pr_expand(proto, maxwidth)
                    369:        char *proto;
                    370:        int maxwidth;
                    371: {
1.5     ! mpech     372:        char *p;
        !           373:        int c;
1.1       etheisen  374:        int where;
                    375:
                    376:        mp = message;
                    377:
                    378:        if (*proto == '\0')
                    379:                return ("");
                    380:
                    381:        for (p = proto;  *p != '\0';  p++)
                    382:        {
                    383:                switch (*p)
                    384:                {
                    385:                default:        /* Just put the character in the message */
                    386:                        *mp++ = *p;
                    387:                        break;
                    388:                case '\\':      /* Backslash escapes the next character */
                    389:                        p++;
                    390:                        *mp++ = *p;
                    391:                        break;
                    392:                case '?':       /* Conditional (IF) */
                    393:                        if ((c = *++p) == '\0')
                    394:                                --p;
                    395:                        else
                    396:                        {
                    397:                                p = wherechar(p, &where);
                    398:                                if (!cond(c, where))
                    399:                                        p = skipcond(p);
                    400:                        }
                    401:                        break;
                    402:                case ':':       /* ELSE */
                    403:                        p = skipcond(p);
                    404:                        break;
                    405:                case '.':       /* ENDIF */
                    406:                        break;
                    407:                case '%':       /* Percent escape */
                    408:                        if ((c = *++p) == '\0')
                    409:                                --p;
                    410:                        else
                    411:                        {
                    412:                                p = wherechar(p, &where);
                    413:                                protochar(c, where);
                    414:                        }
                    415:                        break;
                    416:                }
                    417:        }
                    418:
                    419:        new_file = 0;
                    420:        if (mp == message)
                    421:                return (NULL);
                    422:        *mp = '\0';
                    423:        if (maxwidth > 0 && mp >= message + maxwidth)
                    424:        {
                    425:                /*
                    426:                 * Message is too long.
                    427:                 * Return just the final portion of it.
                    428:                 */
                    429:                return (mp - maxwidth);
                    430:        }
                    431:        return (message);
                    432: }
                    433:
                    434: /*
                    435:  * Return a message suitable for printing by the "=" command.
                    436:  */
                    437:        public char *
                    438: eq_message()
                    439: {
                    440:        return (pr_expand(eqproto, 0));
                    441: }
                    442:
                    443: /*
                    444:  * Return a prompt.
                    445:  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
                    446:  * If we can't come up with an appropriate prompt, return NULL
                    447:  * and the caller will prompt with a colon.
                    448:  */
                    449:        public char *
                    450: pr_string()
                    451: {
                    452:        return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2));
                    453: }