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