[BACK]Return to decode.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/decode.c, Revision 1.4

1.1       etheisen    1: /*
1.4     ! millert     2:  * Copyright (C) 1984-2002  Mark Nudelman
1.1       etheisen    3:  *
1.4     ! 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.4     ! 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:  * Routines to decode user commands.
                     14:  *
                     15:  * This is all table driven.
                     16:  * A command table is a sequence of command descriptors.
                     17:  * Each command descriptor is a sequence of bytes with the following format:
                     18:  *     <c1><c2>...<cN><0><action>
                     19:  * The characters c1,c2,...,cN are the command string; that is,
                     20:  * the characters which the user must type.
                     21:  * It is terminated by a null <0> byte.
                     22:  * The byte after the null byte is the action code associated
                     23:  * with the command string.
                     24:  * If an action byte is OR-ed with A_EXTRA, this indicates
                     25:  * that the option byte is followed by an extra string.
                     26:  *
                     27:  * There may be many command tables.
                     28:  * The first (default) table is built-in.
                     29:  * Other tables are read in from "lesskey" files.
                     30:  * All the tables are linked together and are searched in order.
                     31:  */
                     32:
                     33: #include "less.h"
                     34: #include "cmd.h"
                     35: #include "lesskey.h"
                     36:
                     37: extern int erase_char, kill_char;
1.4     ! millert    38: extern int secure;
1.1       etheisen   39:
1.4     ! millert    40: #define SK(k) \
        !            41:        SK_SPECIAL_KEY, (k), 6, 1, 1, 1
1.1       etheisen   42: /*
                     43:  * Command table is ordered roughly according to expected
                     44:  * frequency of use, so the common commands are near the beginning.
                     45:  */
1.4     ! millert    46:
1.1       etheisen   47: static unsigned char cmdtable[] =
                     48: {
                     49:        '\r',0,                         A_F_LINE,
                     50:        '\n',0,                         A_F_LINE,
                     51:        'e',0,                          A_F_LINE,
                     52:        'j',0,                          A_F_LINE,
1.4     ! millert    53:        SK(SK_DOWN_ARROW),0,            A_F_LINE,
1.1       etheisen   54:        CONTROL('E'),0,                 A_F_LINE,
                     55:        CONTROL('N'),0,                 A_F_LINE,
                     56:        'k',0,                          A_B_LINE,
                     57:        'y',0,                          A_B_LINE,
                     58:        CONTROL('Y'),0,                 A_B_LINE,
1.4     ! millert    59:        SK(SK_CONTROL_K),0,             A_B_LINE,
1.1       etheisen   60:        CONTROL('P'),0,                 A_B_LINE,
1.4     ! millert    61:        SK(SK_UP_ARROW),0,              A_B_LINE,
1.1       etheisen   62:        'J',0,                          A_FF_LINE,
                     63:        'K',0,                          A_BF_LINE,
                     64:        'Y',0,                          A_BF_LINE,
                     65:        'd',0,                          A_F_SCROLL,
                     66:        CONTROL('D'),0,                 A_F_SCROLL,
                     67:        'u',0,                          A_B_SCROLL,
                     68:        CONTROL('U'),0,                 A_B_SCROLL,
                     69:        ' ',0,                          A_F_SCREEN,
                     70:        'f',0,                          A_F_SCREEN,
                     71:        CONTROL('F'),0,                 A_F_SCREEN,
                     72:        CONTROL('V'),0,                 A_F_SCREEN,
1.4     ! millert    73:        SK(SK_PAGE_DOWN),0,             A_F_SCREEN,
1.1       etheisen   74:        'b',0,                          A_B_SCREEN,
                     75:        CONTROL('B'),0,                 A_B_SCREEN,
                     76:        ESC,'v',0,                      A_B_SCREEN,
1.4     ! millert    77:        SK(SK_PAGE_UP),0,               A_B_SCREEN,
1.1       etheisen   78:        'z',0,                          A_F_WINDOW,
                     79:        'w',0,                          A_B_WINDOW,
1.4     ! millert    80:        ESC,' ',0,                      A_FF_SCREEN,
1.1       etheisen   81:        'F',0,                          A_F_FOREVER,
                     82:        'R',0,                          A_FREPAINT,
                     83:        'r',0,                          A_REPAINT,
                     84:        CONTROL('R'),0,                 A_REPAINT,
                     85:        CONTROL('L'),0,                 A_REPAINT,
                     86:        ESC,'u',0,                      A_UNDO_SEARCH,
                     87:        'g',0,                          A_GOLINE,
1.4     ! millert    88:        SK(SK_HOME),0,                  A_GOLINE,
1.1       etheisen   89:        '<',0,                          A_GOLINE,
                     90:        ESC,'<',0,                      A_GOLINE,
                     91:        'p',0,                          A_PERCENT,
                     92:        '%',0,                          A_PERCENT,
1.4     ! millert    93:        ESC,'[',0,                      A_LSHIFT,
        !            94:        ESC,']',0,                      A_RSHIFT,
        !            95:        ESC,'(',0,                      A_LSHIFT,
        !            96:        ESC,')',0,                      A_RSHIFT,
        !            97:        SK(SK_RIGHT_ARROW),0,           A_RSHIFT,
        !            98:        SK(SK_LEFT_ARROW),0,            A_LSHIFT,
1.1       etheisen   99:        '{',0,                          A_F_BRACKET|A_EXTRA,    '{','}',0,
                    100:        '}',0,                          A_B_BRACKET|A_EXTRA,    '{','}',0,
                    101:        '(',0,                          A_F_BRACKET|A_EXTRA,    '(',')',0,
                    102:        ')',0,                          A_B_BRACKET|A_EXTRA,    '(',')',0,
                    103:        '[',0,                          A_F_BRACKET|A_EXTRA,    '[',']',0,
                    104:        ']',0,                          A_B_BRACKET|A_EXTRA,    '[',']',0,
                    105:        ESC,CONTROL('F'),0,             A_F_BRACKET,
                    106:        ESC,CONTROL('B'),0,             A_B_BRACKET,
                    107:        'G',0,                          A_GOEND,
                    108:        ESC,'>',0,                      A_GOEND,
                    109:        '>',0,                          A_GOEND,
1.4     ! millert   110:        SK(SK_END),0,                   A_GOEND,
1.1       etheisen  111:        'P',0,                          A_GOPOS,
                    112:
                    113:        '0',0,                          A_DIGIT,
                    114:        '1',0,                          A_DIGIT,
                    115:        '2',0,                          A_DIGIT,
                    116:        '3',0,                          A_DIGIT,
                    117:        '4',0,                          A_DIGIT,
                    118:        '5',0,                          A_DIGIT,
                    119:        '6',0,                          A_DIGIT,
                    120:        '7',0,                          A_DIGIT,
                    121:        '8',0,                          A_DIGIT,
                    122:        '9',0,                          A_DIGIT,
                    123:
                    124:        '=',0,                          A_STAT,
                    125:        CONTROL('G'),0,                 A_STAT,
                    126:        ':','f',0,                      A_STAT,
                    127:        '/',0,                          A_F_SEARCH,
                    128:        '?',0,                          A_B_SEARCH,
                    129:        ESC,'/',0,                      A_F_SEARCH|A_EXTRA,     '*',0,
                    130:        ESC,'?',0,                      A_B_SEARCH|A_EXTRA,     '*',0,
                    131:        'n',0,                          A_AGAIN_SEARCH,
                    132:        ESC,'n',0,                      A_T_AGAIN_SEARCH,
                    133:        'N',0,                          A_REVERSE_SEARCH,
                    134:        ESC,'N',0,                      A_T_REVERSE_SEARCH,
                    135:        'm',0,                          A_SETMARK,
                    136:        '\'',0,                         A_GOMARK,
                    137:        CONTROL('X'),CONTROL('X'),0,    A_GOMARK,
                    138:        'E',0,                          A_EXAMINE,
                    139:        ':','e',0,                      A_EXAMINE,
                    140:        CONTROL('X'),CONTROL('V'),0,    A_EXAMINE,
                    141:        ':','n',0,                      A_NEXT_FILE,
                    142:        ':','p',0,                      A_PREV_FILE,
1.4     ! millert   143:        't',0,                          A_NEXT_TAG,
        !           144:        'T',0,                          A_PREV_TAG,
1.1       etheisen  145:        ':','x',0,                      A_INDEX_FILE,
1.4     ! millert   146:        ':','d',0,                      A_REMOVE_FILE,
1.1       etheisen  147:        '-',0,                          A_OPT_TOGGLE,
                    148:        ':','t',0,                      A_OPT_TOGGLE|A_EXTRA,   't',0,
                    149:        's',0,                          A_OPT_TOGGLE|A_EXTRA,   'o',0,
                    150:        '_',0,                          A_DISP_OPTION,
                    151:        '|',0,                          A_PIPE,
                    152:        'v',0,                          A_VISUAL,
                    153:        '!',0,                          A_SHELL,
                    154:        '+',0,                          A_FIRSTCMD,
                    155:
                    156:        'H',0,                          A_HELP,
                    157:        'h',0,                          A_HELP,
1.4     ! millert   158:        SK(SK_F1),0,                    A_HELP,
1.1       etheisen  159:        'V',0,                          A_VERSION,
                    160:        'q',0,                          A_QUIT,
1.4     ! millert   161:        'Q',0,                          A_QUIT,
1.1       etheisen  162:        ':','q',0,                      A_QUIT,
                    163:        ':','Q',0,                      A_QUIT,
                    164:        'Z','Z',0,                      A_QUIT
                    165: };
                    166:
                    167: static unsigned char edittable[] =
                    168: {
1.4     ! millert   169:        '\t',0,                         EC_F_COMPLETE,  /* TAB */
        !           170:        '\17',0,                        EC_B_COMPLETE,  /* BACKTAB */
        !           171:        SK(SK_BACKTAB),0,               EC_B_COMPLETE,  /* BACKTAB */
        !           172:        ESC,'\t',0,                     EC_B_COMPLETE,  /* ESC TAB */
        !           173:        CONTROL('L'),0,                 EC_EXPAND,      /* CTRL-L */
        !           174:        CONTROL('V'),0,                 EC_LITERAL,     /* BACKSLASH */
        !           175:        CONTROL('A'),0,                 EC_LITERAL,     /* BACKSLASH */
        !           176:        ESC,'l',0,                      EC_RIGHT,       /* ESC l */
        !           177:        SK(SK_RIGHT_ARROW),0,           EC_RIGHT,       /* RIGHTARROW */
        !           178:        ESC,'h',0,                      EC_LEFT,        /* ESC h */
        !           179:        SK(SK_LEFT_ARROW),0,            EC_LEFT,        /* LEFTARROW */
        !           180:        ESC,'b',0,                      EC_W_LEFT,      /* ESC b */
        !           181:        ESC,SK(SK_LEFT_ARROW),0,        EC_W_LEFT,      /* ESC LEFTARROW */
        !           182:        SK(SK_CTL_LEFT_ARROW),0,        EC_W_LEFT,      /* CTRL-LEFTARROW */
        !           183:        ESC,'w',0,                      EC_W_RIGHT,     /* ESC w */
        !           184:        ESC,SK(SK_RIGHT_ARROW),0,       EC_W_RIGHT,     /* ESC RIGHTARROW */
        !           185:        SK(SK_CTL_RIGHT_ARROW),0,       EC_W_RIGHT,     /* CTRL-RIGHTARROW */
        !           186:        ESC,'i',0,                      EC_INSERT,      /* ESC i */
        !           187:        SK(SK_INSERT),0,                EC_INSERT,      /* INSERT */
        !           188:        ESC,'x',0,                      EC_DELETE,      /* ESC x */
        !           189:        SK(SK_DELETE),0,                EC_DELETE,      /* DELETE */
        !           190:        ESC,'X',0,                      EC_W_DELETE,    /* ESC X */
        !           191:        ESC,SK(SK_DELETE),0,            EC_W_DELETE,    /* ESC DELETE */
        !           192:        SK(SK_CTL_DELETE),0,            EC_W_DELETE,    /* CTRL-DELETE */
        !           193:        SK(SK_CTL_BACKSPACE),0,         EC_W_BACKSPACE, /* CTRL-BACKSPACE */
        !           194:        ESC,'\b',0,                     EC_W_BACKSPACE, /* ESC BACKSPACE */
        !           195:        ESC,'0',0,                      EC_HOME,        /* ESC 0 */
        !           196:        SK(SK_HOME),0,                  EC_HOME,        /* HOME */
        !           197:        ESC,'$',0,                      EC_END,         /* ESC $ */
        !           198:        SK(SK_END),0,                   EC_END,         /* END */
        !           199:        ESC,'k',0,                      EC_UP,          /* ESC k */
        !           200:        SK(SK_UP_ARROW),0,              EC_UP,          /* UPARROW */
        !           201:        ESC,'j',0,                      EC_DOWN,        /* ESC j */
        !           202:        SK(SK_DOWN_ARROW),0,            EC_DOWN,        /* DOWNARROW */
1.1       etheisen  203: };
                    204:
                    205: /*
                    206:  * Structure to support a list of command tables.
                    207:  */
                    208: struct tablelist
                    209: {
                    210:        struct tablelist *t_next;
                    211:        char *t_start;
                    212:        char *t_end;
                    213: };
                    214:
                    215: /*
                    216:  * List of command tables and list of line-edit tables.
                    217:  */
                    218: static struct tablelist *list_fcmd_tables = NULL;
                    219: static struct tablelist *list_ecmd_tables = NULL;
1.4     ! millert   220: static struct tablelist *list_var_tables = NULL;
        !           221: static struct tablelist *list_sysvar_tables = NULL;
1.1       etheisen  222:
                    223:
                    224: /*
1.4     ! millert   225:  * Expand special key abbreviations in a command table.
        !           226:  */
        !           227:        static void
        !           228: expand_special_keys(table, len)
        !           229:        char *table;
        !           230:        int len;
        !           231: {
        !           232:        register char *fm;
        !           233:        register char *to;
        !           234:        register int a;
        !           235:        char *repl;
        !           236:        int klen;
        !           237:
        !           238:        for (fm = table;  fm < table + len; )
        !           239:        {
        !           240:                /*
        !           241:                 * Rewrite each command in the table with any
        !           242:                 * special key abbreviations expanded.
        !           243:                 */
        !           244:                for (to = fm;  *fm != '\0'; )
        !           245:                {
        !           246:                        if (*fm != SK_SPECIAL_KEY)
        !           247:                        {
        !           248:                                *to++ = *fm++;
        !           249:                                continue;
        !           250:                        }
        !           251:                        /*
        !           252:                         * After SK_SPECIAL_KEY, next byte is the type
        !           253:                         * of special key (one of the SK_* contants),
        !           254:                         * and the byte after that is the number of bytes,
        !           255:                         * N, reserved by the abbreviation (including the
        !           256:                         * SK_SPECIAL_KEY and key type bytes).
        !           257:                         * Replace all N bytes with the actual bytes
        !           258:                         * output by the special key on this terminal.
        !           259:                         */
        !           260:                        repl = special_key_str(fm[1]);
        !           261:                        klen = fm[2] & 0377;
        !           262:                        fm += klen;
        !           263:                        if (repl == NULL || (int) strlen(repl) > klen)
        !           264:                                repl = "\377";
        !           265:                        while (*repl != '\0')
        !           266:                                *to++ = *repl++;
        !           267:                }
        !           268:                *to++ = '\0';
        !           269:                /*
        !           270:                 * Fill any unused bytes between end of command and
        !           271:                 * the action byte with A_SKIP.
        !           272:                 */
        !           273:                while (to <= fm)
        !           274:                        *to++ = A_SKIP;
        !           275:                fm++;
        !           276:                a = *fm++ & 0377;
        !           277:                if (a & A_EXTRA)
        !           278:                {
        !           279:                        while (*fm++ != '\0')
        !           280:                                continue;
        !           281:                }
        !           282:        }
        !           283: }
        !           284:
        !           285: /*
1.1       etheisen  286:  * Initialize the command lists.
                    287:  */
                    288:        public void
                    289: init_cmds()
                    290: {
                    291:        /*
                    292:         * Add the default command tables.
                    293:         */
                    294:        add_fcmd_table((char*)cmdtable, sizeof(cmdtable));
                    295:        add_ecmd_table((char*)edittable, sizeof(edittable));
                    296: #if USERFILE
                    297:        /*
1.4     ! millert   298:         * For backwards compatibility,
        !           299:         * try to add tables in the OLD system lesskey file.
        !           300:         */
        !           301: #ifdef BINDIR
        !           302:        add_hometable(NULL, BINDIR "/.sysless", 1);
        !           303: #endif
        !           304:        /*
        !           305:         * Try to add the tables in the system lesskey file.
        !           306:         */
        !           307:        add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1);
        !           308:        /*
1.1       etheisen  309:         * Try to add the tables in the standard lesskey file "$HOME/.less".
                    310:         */
1.4     ! millert   311:        add_hometable("LESSKEY", LESSKEYFILE, 0);
1.1       etheisen  312: #endif
                    313: }
                    314:
                    315: /*
1.4     ! millert   316:  * Add a command table.
1.1       etheisen  317:  */
                    318:        static int
                    319: add_cmd_table(tlist, buf, len)
                    320:        struct tablelist **tlist;
                    321:        char *buf;
                    322:        int len;
                    323: {
1.4     ! millert   324:        register struct tablelist *t;
1.1       etheisen  325:
                    326:        if (len == 0)
                    327:                return (0);
                    328:        /*
                    329:         * Allocate a tablelist structure, initialize it,
                    330:         * and link it into the list of tables.
                    331:         */
                    332:        if ((t = (struct tablelist *)
                    333:                        calloc(1, sizeof(struct tablelist))) == NULL)
                    334:        {
                    335:                return (-1);
                    336:        }
1.4     ! millert   337:        expand_special_keys(buf, len);
1.1       etheisen  338:        t->t_start = buf;
                    339:        t->t_end = buf + len;
                    340:        t->t_next = *tlist;
                    341:        *tlist = t;
                    342:        return (0);
                    343: }
                    344:
                    345: /*
                    346:  * Add a command table.
                    347:  */
                    348:        public void
                    349: add_fcmd_table(buf, len)
                    350:        char *buf;
                    351:        int len;
                    352: {
                    353:        if (add_cmd_table(&list_fcmd_tables, buf, len) < 0)
                    354:                error("Warning: some commands disabled", NULL_PARG);
                    355: }
                    356:
                    357: /*
                    358:  * Add an editing command table.
                    359:  */
                    360:        public void
                    361: add_ecmd_table(buf, len)
                    362:        char *buf;
                    363:        int len;
                    364: {
                    365:        if (add_cmd_table(&list_ecmd_tables, buf, len) < 0)
                    366:                error("Warning: some edit commands disabled", NULL_PARG);
                    367: }
                    368:
                    369: /*
1.4     ! millert   370:  * Add an environment variable table.
        !           371:  */
        !           372:        static void
        !           373: add_var_table(tlist, buf, len)
        !           374:        struct tablelist **tlist;
        !           375:        char *buf;
        !           376:        int len;
        !           377: {
        !           378:        if (add_cmd_table(tlist, buf, len) < 0)
        !           379:                error("Warning: environment variables from lesskey file unavailable", NULL_PARG);
        !           380: }
        !           381:
        !           382: /*
1.1       etheisen  383:  * Search a single command table for the command string in cmd.
                    384:  */
1.4     ! millert   385:        static int
1.1       etheisen  386: cmd_search(cmd, table, endtable, sp)
                    387:        char *cmd;
                    388:        char *table;
                    389:        char *endtable;
                    390:        char **sp;
                    391: {
1.4     ! millert   392:        register char *p;
        !           393:        register char *q;
        !           394:        register int a;
1.1       etheisen  395:
1.4     ! millert   396:        *sp = NULL;
1.1       etheisen  397:        for (p = table, q = cmd;  p < endtable;  p++, q++)
                    398:        {
                    399:                if (*p == *q)
                    400:                {
                    401:                        /*
                    402:                         * Current characters match.
                    403:                         * If we're at the end of the string, we've found it.
                    404:                         * Return the action code, which is the character
                    405:                         * after the null at the end of the string
                    406:                         * in the command table.
                    407:                         */
                    408:                        if (*p == '\0')
                    409:                        {
                    410:                                a = *++p & 0377;
1.4     ! millert   411:                                while (a == A_SKIP)
        !           412:                                        a = *++p & 0377;
1.1       etheisen  413:                                if (a == A_END_LIST)
                    414:                                {
                    415:                                        /*
                    416:                                         * We get here only if the original
                    417:                                         * cmd string passed in was empty ("").
                    418:                                         * I don't think that can happen,
                    419:                                         * but just in case ...
                    420:                                         */
                    421:                                        return (A_UINVALID);
                    422:                                }
                    423:                                /*
                    424:                                 * Check for an "extra" string.
                    425:                                 */
                    426:                                if (a & A_EXTRA)
                    427:                                {
                    428:                                        *sp = ++p;
                    429:                                        a &= ~A_EXTRA;
1.4     ! millert   430:                                }
1.1       etheisen  431:                                return (a);
                    432:                        }
                    433:                } else if (*q == '\0')
                    434:                {
                    435:                        /*
                    436:                         * Hit the end of the user's command,
                    437:                         * but not the end of the string in the command table.
                    438:                         * The user's command is incomplete.
                    439:                         */
                    440:                        return (A_PREFIX);
                    441:                } else
                    442:                {
                    443:                        /*
                    444:                         * Not a match.
                    445:                         * Skip ahead to the next command in the
                    446:                         * command table, and reset the pointer
                    447:                         * to the beginning of the user's command.
                    448:                         */
                    449:                        if (*p == '\0' && p[1] == A_END_LIST)
                    450:                        {
                    451:                                /*
                    452:                                 * A_END_LIST is a special marker that tells
                    453:                                 * us to abort the cmd search.
                    454:                                 */
                    455:                                return (A_UINVALID);
                    456:                        }
1.4     ! millert   457:                        while (*p++ != '\0')
        !           458:                                continue;
        !           459:                        while (*p == A_SKIP)
        !           460:                                p++;
1.1       etheisen  461:                        if (*p & A_EXTRA)
1.4     ! millert   462:                                while (*++p != '\0')
        !           463:                                        continue;
1.1       etheisen  464:                        q = cmd-1;
                    465:                }
                    466:        }
                    467:        /*
                    468:         * No match found in the entire command table.
                    469:         */
                    470:        return (A_INVALID);
                    471: }
                    472:
                    473: /*
                    474:  * Decode a command character and return the associated action.
                    475:  * The "extra" string, if any, is returned in sp.
                    476:  */
                    477:        static int
                    478: cmd_decode(tlist, cmd, sp)
                    479:        struct tablelist *tlist;
                    480:        char *cmd;
                    481:        char **sp;
                    482: {
1.4     ! millert   483:        register struct tablelist *t;
        !           484:        register int action = A_INVALID;
1.1       etheisen  485:
                    486:        /*
                    487:         * Search thru all the command tables.
                    488:         * Stop when we find an action which is not A_INVALID.
                    489:         */
                    490:        for (t = tlist;  t != NULL;  t = t->t_next)
                    491:        {
                    492:                action = cmd_search(cmd, t->t_start, t->t_end, sp);
                    493:                if (action != A_INVALID)
                    494:                        break;
                    495:        }
1.4     ! millert   496:        if (action == A_UINVALID)
        !           497:                action = A_INVALID;
1.1       etheisen  498:        return (action);
                    499: }
                    500:
                    501: /*
                    502:  * Decode a command from the cmdtables list.
                    503:  */
                    504:        public int
                    505: fcmd_decode(cmd, sp)
                    506:        char *cmd;
                    507:        char **sp;
                    508: {
                    509:        return (cmd_decode(list_fcmd_tables, cmd, sp));
                    510: }
                    511:
                    512: /*
                    513:  * Decode a command from the edittables list.
                    514:  */
                    515:        public int
                    516: ecmd_decode(cmd, sp)
                    517:        char *cmd;
                    518:        char **sp;
                    519: {
                    520:        return (cmd_decode(list_ecmd_tables, cmd, sp));
                    521: }
                    522:
1.4     ! millert   523: /*
        !           524:  * Get the value of an environment variable.
        !           525:  * Looks first in the lesskey file, then in the real environment.
        !           526:  */
        !           527:        public char *
        !           528: lgetenv(var)
        !           529:        char *var;
        !           530: {
        !           531:        int a;
        !           532:        char *s;
        !           533:
        !           534:        a = cmd_decode(list_var_tables, var, &s);
        !           535:        if (a == EV_OK)
        !           536:                return (s);
        !           537:        s = getenv(var);
        !           538:        if (s != NULL && *s != '\0')
        !           539:                return (s);
        !           540:        a = cmd_decode(list_sysvar_tables, var, &s);
        !           541:        if (a == EV_OK)
        !           542:                return (s);
        !           543:        return (NULL);
        !           544: }
        !           545:
1.1       etheisen  546: #if USERFILE
1.4     ! millert   547: /*
        !           548:  * Get an "integer" from a lesskey file.
        !           549:  * Integers are stored in a funny format:
        !           550:  * two bytes, low order first, in radix KRADIX.
        !           551:  */
1.1       etheisen  552:        static int
                    553: gint(sp)
                    554:        char **sp;
                    555: {
                    556:        int n;
                    557:
                    558:        n = *(*sp)++;
                    559:        n += *(*sp)++ * KRADIX;
                    560:        return (n);
                    561: }
                    562:
1.4     ! millert   563: /*
        !           564:  * Process an old (pre-v241) lesskey file.
        !           565:  */
1.1       etheisen  566:        static int
                    567: old_lesskey(buf, len)
                    568:        char *buf;
                    569:        int len;
                    570: {
                    571:        /*
                    572:         * Old-style lesskey file.
                    573:         * The file must end with either
                    574:         *     ...,cmd,0,action
                    575:         * or  ...,cmd,0,action|A_EXTRA,string,0
                    576:         * So the last byte or the second to last byte must be zero.
                    577:         */
                    578:        if (buf[len-1] != '\0' && buf[len-2] != '\0')
                    579:                return (-1);
                    580:        add_fcmd_table(buf, len);
                    581:        return (0);
                    582: }
                    583:
1.4     ! millert   584: /*
        !           585:  * Process a new (post-v241) lesskey file.
        !           586:  */
1.1       etheisen  587:        static int
1.4     ! millert   588: new_lesskey(buf, len, sysvar)
1.1       etheisen  589:        char *buf;
                    590:        int len;
1.4     ! millert   591:        int sysvar;
1.1       etheisen  592: {
                    593:        char *p;
1.4     ! millert   594:        register int c;
        !           595:        register int n;
1.1       etheisen  596:
                    597:        /*
                    598:         * New-style lesskey file.
                    599:         * Extract the pieces.
                    600:         */
                    601:        if (buf[len-3] != C0_END_LESSKEY_MAGIC ||
                    602:            buf[len-2] != C1_END_LESSKEY_MAGIC ||
                    603:            buf[len-1] != C2_END_LESSKEY_MAGIC)
                    604:                return (-1);
                    605:        p = buf + 4;
1.4     ! millert   606:        for (;;)
1.1       etheisen  607:        {
                    608:                c = *p++;
                    609:                switch (c)
                    610:                {
                    611:                case CMD_SECTION:
                    612:                        n = gint(&p);
                    613:                        add_fcmd_table(p, n);
                    614:                        p += n;
                    615:                        break;
                    616:                case EDIT_SECTION:
                    617:                        n = gint(&p);
                    618:                        add_ecmd_table(p, n);
                    619:                        p += n;
                    620:                        break;
1.4     ! millert   621:                case VAR_SECTION:
        !           622:                        n = gint(&p);
        !           623:                        add_var_table((sysvar) ?
        !           624:                                &list_sysvar_tables : &list_var_tables, p, n);
        !           625:                        p += n;
        !           626:                        break;
1.1       etheisen  627:                case END_SECTION:
1.4     ! millert   628:                        return (0);
1.1       etheisen  629:                default:
1.4     ! millert   630:                        /*
        !           631:                         * Unrecognized section type.
        !           632:                         */
1.1       etheisen  633:                        return (-1);
                    634:                }
                    635:        }
                    636: }
                    637:
                    638: /*
                    639:  * Set up a user command table, based on a "lesskey" file.
                    640:  */
                    641:        public int
1.4     ! millert   642: lesskey(filename, sysvar)
1.1       etheisen  643:        char *filename;
1.4     ! millert   644:        int sysvar;
1.1       etheisen  645: {
1.4     ! millert   646:        register char *buf;
        !           647:        register POSITION len;
        !           648:        register long n;
        !           649:        register int f;
1.1       etheisen  650:
1.4     ! millert   651:        if (secure)
        !           652:                return (1);
1.1       etheisen  653:        /*
                    654:         * Try to open the lesskey file.
                    655:         */
1.4     ! millert   656:        filename = shell_unquote(filename);
1.1       etheisen  657:        f = open(filename, OPEN_READ);
1.4     ! millert   658:        free(filename);
1.1       etheisen  659:        if (f < 0)
                    660:                return (1);
                    661:
                    662:        /*
                    663:         * Read the file into a buffer.
                    664:         * We first figure out the size of the file and allocate space for it.
                    665:         * {{ Minimal error checking is done here.
                    666:         *    A garbage .less file will produce strange results.
                    667:         *    To avoid a large amount of error checking code here, we
                    668:         *    rely on the lesskey program to generate a good .less file. }}
                    669:         */
                    670:        len = filesize(f);
                    671:        if (len == NULL_POSITION || len < 3)
                    672:        {
                    673:                /*
                    674:                 * Bad file (valid file must have at least 3 chars).
                    675:                 */
                    676:                close(f);
                    677:                return (-1);
                    678:        }
                    679:        if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL)
                    680:        {
                    681:                close(f);
                    682:                return (-1);
                    683:        }
                    684:        if (lseek(f, (off_t)0, 0) == BAD_LSEEK)
                    685:        {
                    686:                free(buf);
                    687:                close(f);
                    688:                return (-1);
                    689:        }
                    690:        n = read(f, buf, (unsigned int) len);
                    691:        close(f);
                    692:        if (n != len)
                    693:        {
                    694:                free(buf);
                    695:                return (-1);
                    696:        }
                    697:
                    698:        /*
                    699:         * Figure out if this is an old-style (before version 241)
                    700:         * or new-style lesskey file format.
                    701:         */
                    702:        if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC ||
                    703:            buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC)
                    704:                return (old_lesskey(buf, (int)len));
1.4     ! millert   705:        return (new_lesskey(buf, (int)len, sysvar));
1.1       etheisen  706: }
                    707:
                    708: /*
                    709:  * Add the standard lesskey file "$HOME/.less"
                    710:  */
                    711:        public void
1.4     ! millert   712: add_hometable(envname, def_filename, sysvar)
        !           713:        char *envname;
        !           714:        char *def_filename;
        !           715:        int sysvar;
1.1       etheisen  716: {
                    717:        char *filename;
                    718:        PARG parg;
                    719:
1.4     ! millert   720:        if (envname != NULL && (filename = lgetenv(envname)) != NULL)
        !           721:                filename = save(filename);
        !           722:        else if (sysvar)
        !           723:                filename = save(def_filename);
        !           724:        else
        !           725:                filename = homefile(def_filename);
1.1       etheisen  726:        if (filename == NULL)
                    727:                return;
1.4     ! millert   728:        if (lesskey(filename, sysvar) < 0)
1.1       etheisen  729:        {
                    730:                parg.p_string = filename;
                    731:                error("Cannot use lesskey file \"%s\"", &parg);
                    732:        }
                    733:        free(filename);
                    734: }
                    735: #endif
                    736:
                    737: /*
                    738:  * See if a char is a special line-editing command.
                    739:  */
                    740:        public int
                    741: editchar(c, flags)
                    742:        int c;
                    743:        int flags;
                    744: {
                    745:        int action;
                    746:        int nch;
                    747:        char *s;
                    748:        char usercmd[MAX_CMDLEN+1];
                    749:
                    750:        /*
                    751:         * An editing character could actually be a sequence of characters;
                    752:         * for example, an escape sequence sent by pressing the uparrow key.
                    753:         * To match the editing string, we use the command decoder
                    754:         * but give it the edit-commands command table
                    755:         * This table is constructed to match the user's keyboard.
                    756:         */
                    757:        if (c == erase_char)
                    758:                return (EC_BACKSPACE);
                    759:        if (c == kill_char)
                    760:                return (EC_LINEKILL);
                    761:
                    762:        /*
                    763:         * Collect characters in a buffer.
                    764:         * Start with the one we have, and get more if we need them.
                    765:         */
                    766:        nch = 0;
                    767:        do {
                    768:                if (nch > 0)
                    769:                        c = getcc();
                    770:                usercmd[nch] = c;
                    771:                usercmd[nch+1] = '\0';
                    772:                nch++;
                    773:                action = ecmd_decode(usercmd, &s);
                    774:        } while (action == A_PREFIX);
                    775:
1.4     ! millert   776:        if (flags & EC_NORIGHTLEFT)
        !           777:        {
        !           778:                switch (action)
        !           779:                {
        !           780:                case EC_RIGHT:
        !           781:                case EC_LEFT:
        !           782:                        action = A_INVALID;
        !           783:                        break;
        !           784:                }
        !           785:        }
        !           786: #if CMD_HISTORY
1.1       etheisen  787:        if (flags & EC_NOHISTORY)
                    788:        {
                    789:                /*
                    790:                 * The caller says there is no history list.
                    791:                 * Reject any history-manipulation action.
                    792:                 */
                    793:                switch (action)
                    794:                {
                    795:                case EC_UP:
                    796:                case EC_DOWN:
                    797:                        action = A_INVALID;
                    798:                        break;
                    799:                }
                    800:        }
1.4     ! millert   801: #endif
        !           802: #if TAB_COMPLETE_FILENAME
1.1       etheisen  803:        if (flags & EC_NOCOMPLETE)
                    804:        {
                    805:                /*
                    806:                 * The caller says we don't want any filename completion cmds.
                    807:                 * Reject them.
                    808:                 */
                    809:                switch (action)
                    810:                {
                    811:                case EC_F_COMPLETE:
                    812:                case EC_B_COMPLETE:
                    813:                case EC_EXPAND:
                    814:                        action = A_INVALID;
                    815:                        break;
                    816:                }
                    817:        }
1.4     ! millert   818: #endif
1.1       etheisen  819:        if ((flags & EC_PEEK) || action == A_INVALID)
                    820:        {
                    821:                /*
                    822:                 * We're just peeking, or we didn't understand the command.
                    823:                 * Unget all the characters we read in the loop above.
                    824:                 * This does NOT include the original character that was
                    825:                 * passed in as a parameter.
                    826:                 */
1.4     ! millert   827:                while (nch > 1)
        !           828:                {
1.1       etheisen  829:                        ungetcc(usercmd[--nch]);
                    830:                }
                    831:        } else
                    832:        {
                    833:                if (s != NULL)
                    834:                        ungetsc(s);
                    835:        }
                    836:        return action;
                    837: }
                    838: