1238730Sdelphij/* 2330571Sdelphij * Copyright (C) 1984-2017 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 960786Sps 1060786Sps 1160786Sps/* 1260786Sps * Routines to decode user commands. 1360786Sps * 1460786Sps * This is all table driven. 1560786Sps * A command table is a sequence of command descriptors. 1660786Sps * Each command descriptor is a sequence of bytes with the following format: 1760786Sps * <c1><c2>...<cN><0><action> 1860786Sps * The characters c1,c2,...,cN are the command string; that is, 1960786Sps * the characters which the user must type. 2060786Sps * It is terminated by a null <0> byte. 2160786Sps * The byte after the null byte is the action code associated 2260786Sps * with the command string. 2360786Sps * If an action byte is OR-ed with A_EXTRA, this indicates 2460786Sps * that the option byte is followed by an extra string. 2560786Sps * 2660786Sps * There may be many command tables. 2760786Sps * The first (default) table is built-in. 2860786Sps * Other tables are read in from "lesskey" files. 2960786Sps * All the tables are linked together and are searched in order. 3060786Sps */ 3160786Sps 3260786Sps#include "less.h" 3360786Sps#include "cmd.h" 3460786Sps#include "lesskey.h" 3560786Sps 36161475Sdelphijextern int erase_char, erase2_char, kill_char; 3760786Spsextern int secure; 3860786Sps 3960786Sps#define SK(k) \ 4060786Sps SK_SPECIAL_KEY, (k), 6, 1, 1, 1 4160786Sps/* 4260786Sps * Command table is ordered roughly according to expected 4360786Sps * frequency of use, so the common commands are near the beginning. 4460786Sps */ 4560786Sps 4660786Spsstatic unsigned char cmdtable[] = 4760786Sps{ 4860786Sps '\r',0, A_F_LINE, 4960786Sps '\n',0, A_F_LINE, 5060786Sps 'e',0, A_F_LINE, 5160786Sps 'j',0, A_F_LINE, 5260786Sps SK(SK_DOWN_ARROW),0, A_F_LINE, 5360786Sps CONTROL('E'),0, A_F_LINE, 5460786Sps CONTROL('N'),0, A_F_LINE, 5560786Sps 'k',0, A_B_LINE, 5660786Sps 'y',0, A_B_LINE, 5760786Sps CONTROL('Y'),0, A_B_LINE, 5860786Sps SK(SK_CONTROL_K),0, A_B_LINE, 5960786Sps CONTROL('P'),0, A_B_LINE, 6060786Sps SK(SK_UP_ARROW),0, A_B_LINE, 6160786Sps 'J',0, A_FF_LINE, 6260786Sps 'K',0, A_BF_LINE, 6360786Sps 'Y',0, A_BF_LINE, 6460786Sps 'd',0, A_F_SCROLL, 6560786Sps CONTROL('D'),0, A_F_SCROLL, 6660786Sps 'u',0, A_B_SCROLL, 6760786Sps CONTROL('U'),0, A_B_SCROLL, 6860786Sps ' ',0, A_F_SCREEN, 6960786Sps 'f',0, A_F_SCREEN, 7060786Sps CONTROL('F'),0, A_F_SCREEN, 7160786Sps CONTROL('V'),0, A_F_SCREEN, 7260786Sps SK(SK_PAGE_DOWN),0, A_F_SCREEN, 7360786Sps 'b',0, A_B_SCREEN, 7460786Sps CONTROL('B'),0, A_B_SCREEN, 7560786Sps ESC,'v',0, A_B_SCREEN, 7660786Sps SK(SK_PAGE_UP),0, A_B_SCREEN, 7760786Sps 'z',0, A_F_WINDOW, 7860786Sps 'w',0, A_B_WINDOW, 7960786Sps ESC,' ',0, A_FF_SCREEN, 8060786Sps 'F',0, A_F_FOREVER, 81237613Sdelphij ESC,'F',0, A_F_UNTIL_HILITE, 8260786Sps 'R',0, A_FREPAINT, 8360786Sps 'r',0, A_REPAINT, 8460786Sps CONTROL('R'),0, A_REPAINT, 8560786Sps CONTROL('L'),0, A_REPAINT, 8660786Sps ESC,'u',0, A_UNDO_SEARCH, 8760786Sps 'g',0, A_GOLINE, 8860786Sps SK(SK_HOME),0, A_GOLINE, 8960786Sps '<',0, A_GOLINE, 9060786Sps ESC,'<',0, A_GOLINE, 9160786Sps 'p',0, A_PERCENT, 9260786Sps '%',0, A_PERCENT, 9360786Sps ESC,'[',0, A_LSHIFT, 9460786Sps ESC,']',0, A_RSHIFT, 9560786Sps ESC,'(',0, A_LSHIFT, 9660786Sps ESC,')',0, A_RSHIFT, 97330571Sdelphij ESC,'{',0, A_LLSHIFT, 98330571Sdelphij ESC,'}',0, A_RRSHIFT, 9960786Sps SK(SK_RIGHT_ARROW),0, A_RSHIFT, 10060786Sps SK(SK_LEFT_ARROW),0, A_LSHIFT, 101330571Sdelphij SK(SK_CTL_RIGHT_ARROW),0, A_RRSHIFT, 102330571Sdelphij SK(SK_CTL_LEFT_ARROW),0, A_LLSHIFT, 10360786Sps '{',0, A_F_BRACKET|A_EXTRA, '{','}',0, 10460786Sps '}',0, A_B_BRACKET|A_EXTRA, '{','}',0, 10560786Sps '(',0, A_F_BRACKET|A_EXTRA, '(',')',0, 10660786Sps ')',0, A_B_BRACKET|A_EXTRA, '(',')',0, 10760786Sps '[',0, A_F_BRACKET|A_EXTRA, '[',']',0, 10860786Sps ']',0, A_B_BRACKET|A_EXTRA, '[',']',0, 10960786Sps ESC,CONTROL('F'),0, A_F_BRACKET, 11060786Sps ESC,CONTROL('B'),0, A_B_BRACKET, 11160786Sps 'G',0, A_GOEND, 112294286Sdelphij ESC,'G',0, A_GOEND_BUF, 11360786Sps ESC,'>',0, A_GOEND, 11460786Sps '>',0, A_GOEND, 11560786Sps SK(SK_END),0, A_GOEND, 11660786Sps 'P',0, A_GOPOS, 11760786Sps 11860786Sps '0',0, A_DIGIT, 11960786Sps '1',0, A_DIGIT, 12060786Sps '2',0, A_DIGIT, 12160786Sps '3',0, A_DIGIT, 12260786Sps '4',0, A_DIGIT, 12360786Sps '5',0, A_DIGIT, 12460786Sps '6',0, A_DIGIT, 12560786Sps '7',0, A_DIGIT, 12660786Sps '8',0, A_DIGIT, 12760786Sps '9',0, A_DIGIT, 128170256Sdelphij '.',0, A_DIGIT, 12960786Sps 13060786Sps '=',0, A_STAT, 13160786Sps CONTROL('G'),0, A_STAT, 13260786Sps ':','f',0, A_STAT, 13360786Sps '/',0, A_F_SEARCH, 13460786Sps '?',0, A_B_SEARCH, 13560786Sps ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0, 13660786Sps ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0, 13760786Sps 'n',0, A_AGAIN_SEARCH, 13860786Sps ESC,'n',0, A_T_AGAIN_SEARCH, 13960786Sps 'N',0, A_REVERSE_SEARCH, 14060786Sps ESC,'N',0, A_T_REVERSE_SEARCH, 141191930Sdelphij '&',0, A_FILTER, 14260786Sps 'm',0, A_SETMARK, 143330571Sdelphij 'M',0, A_SETMARKBOT, 144330571Sdelphij ESC,'m',0, A_CLRMARK, 14560786Sps '\'',0, A_GOMARK, 14660786Sps CONTROL('X'),CONTROL('X'),0, A_GOMARK, 14760786Sps 'E',0, A_EXAMINE, 14860786Sps ':','e',0, A_EXAMINE, 14960786Sps CONTROL('X'),CONTROL('V'),0, A_EXAMINE, 15060786Sps ':','n',0, A_NEXT_FILE, 15160786Sps ':','p',0, A_PREV_FILE, 15289019Sps 't',0, A_NEXT_TAG, 15389019Sps 'T',0, A_PREV_TAG, 15460786Sps ':','x',0, A_INDEX_FILE, 15560786Sps ':','d',0, A_REMOVE_FILE, 15660786Sps '-',0, A_OPT_TOGGLE, 15760786Sps ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0, 15860786Sps 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0, 15960786Sps '_',0, A_DISP_OPTION, 16060786Sps '|',0, A_PIPE, 16160786Sps 'v',0, A_VISUAL, 16260786Sps '!',0, A_SHELL, 16360786Sps '+',0, A_FIRSTCMD, 16460786Sps 16560786Sps 'H',0, A_HELP, 16660786Sps 'h',0, A_HELP, 16760786Sps SK(SK_F1),0, A_HELP, 16860786Sps 'V',0, A_VERSION, 16960786Sps 'q',0, A_QUIT, 17060786Sps 'Q',0, A_QUIT, 17160786Sps ':','q',0, A_QUIT, 17260786Sps ':','Q',0, A_QUIT, 17360786Sps 'Z','Z',0, A_QUIT 17460786Sps}; 17560786Sps 17660786Spsstatic unsigned char edittable[] = 17760786Sps{ 17860786Sps '\t',0, EC_F_COMPLETE, /* TAB */ 17960786Sps '\17',0, EC_B_COMPLETE, /* BACKTAB */ 18060786Sps SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */ 18160786Sps ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */ 18260786Sps CONTROL('L'),0, EC_EXPAND, /* CTRL-L */ 18360786Sps CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */ 18460786Sps CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */ 18560786Sps ESC,'l',0, EC_RIGHT, /* ESC l */ 18660786Sps SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */ 18760786Sps ESC,'h',0, EC_LEFT, /* ESC h */ 18860786Sps SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */ 18960786Sps ESC,'b',0, EC_W_LEFT, /* ESC b */ 19060786Sps ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */ 19160786Sps SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */ 19260786Sps ESC,'w',0, EC_W_RIGHT, /* ESC w */ 19360786Sps ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */ 19460786Sps SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */ 19560786Sps ESC,'i',0, EC_INSERT, /* ESC i */ 19660786Sps SK(SK_INSERT),0, EC_INSERT, /* INSERT */ 19760786Sps ESC,'x',0, EC_DELETE, /* ESC x */ 19860786Sps SK(SK_DELETE),0, EC_DELETE, /* DELETE */ 19960786Sps ESC,'X',0, EC_W_DELETE, /* ESC X */ 20060786Sps ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */ 20160786Sps SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */ 20260786Sps SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */ 20360786Sps ESC,'\b',0, EC_W_BACKSPACE, /* ESC BACKSPACE */ 20460786Sps ESC,'0',0, EC_HOME, /* ESC 0 */ 20560786Sps SK(SK_HOME),0, EC_HOME, /* HOME */ 20660786Sps ESC,'$',0, EC_END, /* ESC $ */ 20760786Sps SK(SK_END),0, EC_END, /* END */ 20860786Sps ESC,'k',0, EC_UP, /* ESC k */ 20960786Sps SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */ 21060786Sps ESC,'j',0, EC_DOWN, /* ESC j */ 21160786Sps SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */ 212221715Sdelphij CONTROL('G'),0, EC_ABORT, /* CTRL-G */ 21360786Sps}; 21460786Sps 21560786Sps/* 21660786Sps * Structure to support a list of command tables. 21760786Sps */ 21860786Spsstruct tablelist 21960786Sps{ 22060786Sps struct tablelist *t_next; 22160786Sps char *t_start; 22260786Sps char *t_end; 22360786Sps}; 22460786Sps 22560786Sps/* 22660786Sps * List of command tables and list of line-edit tables. 22760786Sps */ 22860786Spsstatic struct tablelist *list_fcmd_tables = NULL; 22960786Spsstatic struct tablelist *list_ecmd_tables = NULL; 23060786Spsstatic struct tablelist *list_var_tables = NULL; 23160786Spsstatic struct tablelist *list_sysvar_tables = NULL; 23260786Sps 23360786Sps 23460786Sps/* 23560786Sps * Expand special key abbreviations in a command table. 23660786Sps */ 23760786Sps static void 23860786Spsexpand_special_keys(table, len) 23960786Sps char *table; 24060786Sps int len; 24160786Sps{ 242330571Sdelphij char *fm; 243330571Sdelphij char *to; 244330571Sdelphij int a; 24560786Sps char *repl; 24660786Sps int klen; 24760786Sps 24860786Sps for (fm = table; fm < table + len; ) 24960786Sps { 25060786Sps /* 25160786Sps * Rewrite each command in the table with any 25260786Sps * special key abbreviations expanded. 25360786Sps */ 25460786Sps for (to = fm; *fm != '\0'; ) 25560786Sps { 25660786Sps if (*fm != SK_SPECIAL_KEY) 25760786Sps { 25860786Sps *to++ = *fm++; 25960786Sps continue; 26060786Sps } 26160786Sps /* 26260786Sps * After SK_SPECIAL_KEY, next byte is the type 26360786Sps * of special key (one of the SK_* contants), 26460786Sps * and the byte after that is the number of bytes, 26560786Sps * N, reserved by the abbreviation (including the 26660786Sps * SK_SPECIAL_KEY and key type bytes). 26760786Sps * Replace all N bytes with the actual bytes 26860786Sps * output by the special key on this terminal. 26960786Sps */ 27060786Sps repl = special_key_str(fm[1]); 27160786Sps klen = fm[2] & 0377; 27260786Sps fm += klen; 273128345Stjr if (repl == NULL || (int) strlen(repl) > klen) 27460786Sps repl = "\377"; 27560786Sps while (*repl != '\0') 27660786Sps *to++ = *repl++; 27760786Sps } 27860786Sps *to++ = '\0'; 27960786Sps /* 28060786Sps * Fill any unused bytes between end of command and 28160786Sps * the action byte with A_SKIP. 28260786Sps */ 28360786Sps while (to <= fm) 28460786Sps *to++ = A_SKIP; 28560786Sps fm++; 28660786Sps a = *fm++ & 0377; 28760786Sps if (a & A_EXTRA) 28860786Sps { 28960786Sps while (*fm++ != '\0') 29060786Sps continue; 29160786Sps } 29260786Sps } 29360786Sps} 29460786Sps 29560786Sps/* 296330571Sdelphij * Expand special key abbreviations in a list of command tables. 297330571Sdelphij */ 298330571Sdelphij static void 299330571Sdelphijexpand_cmd_table(tlist) 300330571Sdelphij struct tablelist *tlist; 301330571Sdelphij{ 302330571Sdelphij struct tablelist *t; 303330571Sdelphij for (t = tlist; t != NULL; t = t->t_next) 304330571Sdelphij { 305330571Sdelphij expand_special_keys(t->t_start, t->t_end - t->t_start); 306330571Sdelphij } 307330571Sdelphij} 308330571Sdelphij 309330571Sdelphij/* 310330571Sdelphij * Expand special key abbreviations in all command tables. 311330571Sdelphij */ 312330571Sdelphij public void 313330571Sdelphijexpand_cmd_tables() 314330571Sdelphij{ 315330571Sdelphij expand_cmd_table(list_fcmd_tables); 316330571Sdelphij expand_cmd_table(list_ecmd_tables); 317330571Sdelphij expand_cmd_table(list_var_tables); 318330571Sdelphij expand_cmd_table(list_sysvar_tables); 319330571Sdelphij} 320330571Sdelphij 321330571Sdelphij 322330571Sdelphij/* 32360786Sps * Initialize the command lists. 32460786Sps */ 32560786Sps public void 32660786Spsinit_cmds() 32760786Sps{ 32860786Sps /* 32960786Sps * Add the default command tables. 33060786Sps */ 33160786Sps add_fcmd_table((char*)cmdtable, sizeof(cmdtable)); 33260786Sps add_ecmd_table((char*)edittable, sizeof(edittable)); 33360786Sps#if USERFILE 33460786Sps /* 33589019Sps * For backwards compatibility, 33689019Sps * try to add tables in the OLD system lesskey file. 33789019Sps */ 33889019Sps#ifdef BINDIR 33989019Sps add_hometable(NULL, BINDIR "/.sysless", 1); 34089019Sps#endif 34189019Sps /* 34260786Sps * Try to add the tables in the system lesskey file. 34360786Sps */ 34460786Sps add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1); 34560786Sps /* 34660786Sps * Try to add the tables in the standard lesskey file "$HOME/.less". 34760786Sps */ 34860786Sps add_hometable("LESSKEY", LESSKEYFILE, 0); 34960786Sps#endif 35060786Sps} 35160786Sps 35260786Sps/* 35360786Sps * Add a command table. 35460786Sps */ 35560786Sps static int 35660786Spsadd_cmd_table(tlist, buf, len) 35760786Sps struct tablelist **tlist; 35860786Sps char *buf; 35960786Sps int len; 36060786Sps{ 361330571Sdelphij struct tablelist *t; 36260786Sps 36360786Sps if (len == 0) 36460786Sps return (0); 36560786Sps /* 36660786Sps * Allocate a tablelist structure, initialize it, 36760786Sps * and link it into the list of tables. 36860786Sps */ 36960786Sps if ((t = (struct tablelist *) 37060786Sps calloc(1, sizeof(struct tablelist))) == NULL) 37160786Sps { 37260786Sps return (-1); 37360786Sps } 37460786Sps t->t_start = buf; 37560786Sps t->t_end = buf + len; 37660786Sps t->t_next = *tlist; 37760786Sps *tlist = t; 37860786Sps return (0); 37960786Sps} 38060786Sps 38160786Sps/* 38260786Sps * Add a command table. 38360786Sps */ 38460786Sps public void 38560786Spsadd_fcmd_table(buf, len) 38660786Sps char *buf; 38760786Sps int len; 38860786Sps{ 38960786Sps if (add_cmd_table(&list_fcmd_tables, buf, len) < 0) 39060786Sps error("Warning: some commands disabled", NULL_PARG); 39160786Sps} 39260786Sps 39360786Sps/* 39460786Sps * Add an editing command table. 39560786Sps */ 39660786Sps public void 39760786Spsadd_ecmd_table(buf, len) 39860786Sps char *buf; 39960786Sps int len; 40060786Sps{ 40160786Sps if (add_cmd_table(&list_ecmd_tables, buf, len) < 0) 40260786Sps error("Warning: some edit commands disabled", NULL_PARG); 40360786Sps} 40460786Sps 40560786Sps/* 40660786Sps * Add an environment variable table. 40760786Sps */ 40860786Sps static void 40960786Spsadd_var_table(tlist, buf, len) 41060786Sps struct tablelist **tlist; 41160786Sps char *buf; 41260786Sps int len; 41360786Sps{ 41460786Sps if (add_cmd_table(tlist, buf, len) < 0) 41560786Sps error("Warning: environment variables from lesskey file unavailable", NULL_PARG); 41660786Sps} 41760786Sps 41860786Sps/* 41960786Sps * Search a single command table for the command string in cmd. 42060786Sps */ 42163128Sps static int 42260786Spscmd_search(cmd, table, endtable, sp) 42360786Sps char *cmd; 42460786Sps char *table; 42560786Sps char *endtable; 42660786Sps char **sp; 42760786Sps{ 428330571Sdelphij char *p; 429330571Sdelphij char *q; 430330571Sdelphij int a; 43160786Sps 43263128Sps *sp = NULL; 43360786Sps for (p = table, q = cmd; p < endtable; p++, q++) 43460786Sps { 43560786Sps if (*p == *q) 43660786Sps { 43760786Sps /* 43860786Sps * Current characters match. 43960786Sps * If we're at the end of the string, we've found it. 44060786Sps * Return the action code, which is the character 44160786Sps * after the null at the end of the string 44260786Sps * in the command table. 44360786Sps */ 44460786Sps if (*p == '\0') 44560786Sps { 44660786Sps a = *++p & 0377; 44760786Sps while (a == A_SKIP) 44860786Sps a = *++p & 0377; 44960786Sps if (a == A_END_LIST) 45060786Sps { 45160786Sps /* 45260786Sps * We get here only if the original 45360786Sps * cmd string passed in was empty (""). 45460786Sps * I don't think that can happen, 45560786Sps * but just in case ... 45660786Sps */ 45760786Sps return (A_UINVALID); 45860786Sps } 45960786Sps /* 46060786Sps * Check for an "extra" string. 46160786Sps */ 46260786Sps if (a & A_EXTRA) 46360786Sps { 46460786Sps *sp = ++p; 46560786Sps a &= ~A_EXTRA; 46663128Sps } 46760786Sps return (a); 46860786Sps } 46960786Sps } else if (*q == '\0') 47060786Sps { 47160786Sps /* 47260786Sps * Hit the end of the user's command, 47360786Sps * but not the end of the string in the command table. 47460786Sps * The user's command is incomplete. 47560786Sps */ 47660786Sps return (A_PREFIX); 47760786Sps } else 47860786Sps { 47960786Sps /* 48060786Sps * Not a match. 48160786Sps * Skip ahead to the next command in the 48260786Sps * command table, and reset the pointer 48360786Sps * to the beginning of the user's command. 48460786Sps */ 48560786Sps if (*p == '\0' && p[1] == A_END_LIST) 48660786Sps { 48760786Sps /* 48860786Sps * A_END_LIST is a special marker that tells 48960786Sps * us to abort the cmd search. 49060786Sps */ 49160786Sps return (A_UINVALID); 49260786Sps } 49360786Sps while (*p++ != '\0') 49460786Sps continue; 49560786Sps while (*p == A_SKIP) 49660786Sps p++; 49760786Sps if (*p & A_EXTRA) 49860786Sps while (*++p != '\0') 49960786Sps continue; 50060786Sps q = cmd-1; 50160786Sps } 50260786Sps } 50360786Sps /* 50460786Sps * No match found in the entire command table. 50560786Sps */ 50660786Sps return (A_INVALID); 50760786Sps} 50860786Sps 50960786Sps/* 51060786Sps * Decode a command character and return the associated action. 51160786Sps * The "extra" string, if any, is returned in sp. 51260786Sps */ 51360786Sps static int 51460786Spscmd_decode(tlist, cmd, sp) 51560786Sps struct tablelist *tlist; 51660786Sps char *cmd; 51760786Sps char **sp; 51860786Sps{ 519330571Sdelphij struct tablelist *t; 520330571Sdelphij int action = A_INVALID; 52160786Sps 52260786Sps /* 52360786Sps * Search thru all the command tables. 52460786Sps * Stop when we find an action which is not A_INVALID. 52560786Sps */ 52660786Sps for (t = tlist; t != NULL; t = t->t_next) 52760786Sps { 52860786Sps action = cmd_search(cmd, t->t_start, t->t_end, sp); 52960786Sps if (action != A_INVALID) 53060786Sps break; 53160786Sps } 53263128Sps if (action == A_UINVALID) 53363128Sps action = A_INVALID; 53460786Sps return (action); 53560786Sps} 53660786Sps 53760786Sps/* 53860786Sps * Decode a command from the cmdtables list. 53960786Sps */ 54060786Sps public int 54160786Spsfcmd_decode(cmd, sp) 54260786Sps char *cmd; 54360786Sps char **sp; 54460786Sps{ 54560786Sps return (cmd_decode(list_fcmd_tables, cmd, sp)); 54660786Sps} 54760786Sps 54860786Sps/* 54960786Sps * Decode a command from the edittables list. 55060786Sps */ 55160786Sps public int 55260786Spsecmd_decode(cmd, sp) 55360786Sps char *cmd; 55460786Sps char **sp; 55560786Sps{ 55660786Sps return (cmd_decode(list_ecmd_tables, cmd, sp)); 55760786Sps} 55860786Sps 55960786Sps/* 56060786Sps * Get the value of an environment variable. 56160786Sps * Looks first in the lesskey file, then in the real environment. 56260786Sps */ 56360786Sps public char * 56460786Spslgetenv(var) 56560786Sps char *var; 56660786Sps{ 56760786Sps int a; 56860786Sps char *s; 56960786Sps 57060786Sps a = cmd_decode(list_var_tables, var, &s); 57160786Sps if (a == EV_OK) 57260786Sps return (s); 57360786Sps s = getenv(var); 57460786Sps if (s != NULL && *s != '\0') 57560786Sps return (s); 57660786Sps a = cmd_decode(list_sysvar_tables, var, &s); 57760786Sps if (a == EV_OK) 57860786Sps return (s); 57960786Sps return (NULL); 58060786Sps} 58160786Sps 58260786Sps#if USERFILE 58360786Sps/* 58460786Sps * Get an "integer" from a lesskey file. 58560786Sps * Integers are stored in a funny format: 58660786Sps * two bytes, low order first, in radix KRADIX. 58760786Sps */ 58860786Sps static int 58960786Spsgint(sp) 59060786Sps char **sp; 59160786Sps{ 59260786Sps int n; 59360786Sps 59460786Sps n = *(*sp)++; 59560786Sps n += *(*sp)++ * KRADIX; 59660786Sps return (n); 59760786Sps} 59860786Sps 59960786Sps/* 60060786Sps * Process an old (pre-v241) lesskey file. 60160786Sps */ 60260786Sps static int 60360786Spsold_lesskey(buf, len) 60460786Sps char *buf; 60560786Sps int len; 60660786Sps{ 60760786Sps /* 60860786Sps * Old-style lesskey file. 60960786Sps * The file must end with either 61060786Sps * ...,cmd,0,action 61160786Sps * or ...,cmd,0,action|A_EXTRA,string,0 61260786Sps * So the last byte or the second to last byte must be zero. 61360786Sps */ 61460786Sps if (buf[len-1] != '\0' && buf[len-2] != '\0') 61560786Sps return (-1); 61660786Sps add_fcmd_table(buf, len); 61760786Sps return (0); 61860786Sps} 61960786Sps 62060786Sps/* 62160786Sps * Process a new (post-v241) lesskey file. 62260786Sps */ 62360786Sps static int 62460786Spsnew_lesskey(buf, len, sysvar) 62560786Sps char *buf; 62660786Sps int len; 62760786Sps int sysvar; 62860786Sps{ 62960786Sps char *p; 630330571Sdelphij int c; 631330571Sdelphij int n; 63260786Sps 63360786Sps /* 63460786Sps * New-style lesskey file. 63560786Sps * Extract the pieces. 63660786Sps */ 63760786Sps if (buf[len-3] != C0_END_LESSKEY_MAGIC || 63860786Sps buf[len-2] != C1_END_LESSKEY_MAGIC || 63960786Sps buf[len-1] != C2_END_LESSKEY_MAGIC) 64060786Sps return (-1); 64160786Sps p = buf + 4; 64260786Sps for (;;) 64360786Sps { 64460786Sps c = *p++; 64560786Sps switch (c) 64660786Sps { 64760786Sps case CMD_SECTION: 64860786Sps n = gint(&p); 64960786Sps add_fcmd_table(p, n); 65060786Sps p += n; 65160786Sps break; 65260786Sps case EDIT_SECTION: 65360786Sps n = gint(&p); 65460786Sps add_ecmd_table(p, n); 65560786Sps p += n; 65660786Sps break; 65760786Sps case VAR_SECTION: 65860786Sps n = gint(&p); 65960786Sps add_var_table((sysvar) ? 66060786Sps &list_sysvar_tables : &list_var_tables, p, n); 66160786Sps p += n; 66260786Sps break; 66360786Sps case END_SECTION: 66460786Sps return (0); 66560786Sps default: 66660786Sps /* 66760786Sps * Unrecognized section type. 66860786Sps */ 66960786Sps return (-1); 67060786Sps } 67160786Sps } 67260786Sps} 67360786Sps 67460786Sps/* 67560786Sps * Set up a user command table, based on a "lesskey" file. 67660786Sps */ 67760786Sps public int 67860786Spslesskey(filename, sysvar) 67960786Sps char *filename; 68060786Sps int sysvar; 68160786Sps{ 682330571Sdelphij char *buf; 683330571Sdelphij POSITION len; 684330571Sdelphij long n; 685330571Sdelphij int f; 68660786Sps 68760786Sps if (secure) 68860786Sps return (1); 68960786Sps /* 69060786Sps * Try to open the lesskey file. 69160786Sps */ 69260786Sps f = open(filename, OPEN_READ); 69360786Sps if (f < 0) 69460786Sps return (1); 69560786Sps 69660786Sps /* 69760786Sps * Read the file into a buffer. 69860786Sps * We first figure out the size of the file and allocate space for it. 69960786Sps * {{ Minimal error checking is done here. 70060786Sps * A garbage .less file will produce strange results. 70160786Sps * To avoid a large amount of error checking code here, we 70260786Sps * rely on the lesskey program to generate a good .less file. }} 70360786Sps */ 70460786Sps len = filesize(f); 70560786Sps if (len == NULL_POSITION || len < 3) 70660786Sps { 70760786Sps /* 70860786Sps * Bad file (valid file must have at least 3 chars). 70960786Sps */ 71060786Sps close(f); 71160786Sps return (-1); 71260786Sps } 71360786Sps if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL) 71460786Sps { 71560786Sps close(f); 71660786Sps return (-1); 71760786Sps } 718173682Sdelphij if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK) 71960786Sps { 72060786Sps free(buf); 72160786Sps close(f); 72260786Sps return (-1); 72360786Sps } 72460786Sps n = read(f, buf, (unsigned int) len); 72560786Sps close(f); 72660786Sps if (n != len) 72760786Sps { 72860786Sps free(buf); 72960786Sps return (-1); 73060786Sps } 73160786Sps 73260786Sps /* 73360786Sps * Figure out if this is an old-style (before version 241) 73460786Sps * or new-style lesskey file format. 73560786Sps */ 73660786Sps if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC || 73760786Sps buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC) 73860786Sps return (old_lesskey(buf, (int)len)); 73960786Sps return (new_lesskey(buf, (int)len, sysvar)); 74060786Sps} 74160786Sps 74260786Sps/* 74360786Sps * Add the standard lesskey file "$HOME/.less" 74460786Sps */ 74560786Sps public void 74660786Spsadd_hometable(envname, def_filename, sysvar) 74760786Sps char *envname; 74860786Sps char *def_filename; 74960786Sps int sysvar; 75060786Sps{ 75160786Sps char *filename; 75260786Sps PARG parg; 75360786Sps 75489019Sps if (envname != NULL && (filename = lgetenv(envname)) != NULL) 75560786Sps filename = save(filename); 75660786Sps else if (sysvar) 75760786Sps filename = save(def_filename); 75860786Sps else 75960786Sps filename = homefile(def_filename); 76060786Sps if (filename == NULL) 76160786Sps return; 76260786Sps if (lesskey(filename, sysvar) < 0) 76360786Sps { 76460786Sps parg.p_string = filename; 76560786Sps error("Cannot use lesskey file \"%s\"", &parg); 76660786Sps } 76760786Sps free(filename); 76860786Sps} 76960786Sps#endif 77060786Sps 77160786Sps/* 77260786Sps * See if a char is a special line-editing command. 77360786Sps */ 77460786Sps public int 77560786Spseditchar(c, flags) 77660786Sps int c; 77760786Sps int flags; 77860786Sps{ 77960786Sps int action; 78060786Sps int nch; 78160786Sps char *s; 78260786Sps char usercmd[MAX_CMDLEN+1]; 78360786Sps 78460786Sps /* 78560786Sps * An editing character could actually be a sequence of characters; 78660786Sps * for example, an escape sequence sent by pressing the uparrow key. 78760786Sps * To match the editing string, we use the command decoder 78860786Sps * but give it the edit-commands command table 78960786Sps * This table is constructed to match the user's keyboard. 79060786Sps */ 791161475Sdelphij if (c == erase_char || c == erase2_char) 79260786Sps return (EC_BACKSPACE); 79360786Sps if (c == kill_char) 79460786Sps return (EC_LINEKILL); 79560786Sps 79660786Sps /* 79760786Sps * Collect characters in a buffer. 79860786Sps * Start with the one we have, and get more if we need them. 79960786Sps */ 80060786Sps nch = 0; 80160786Sps do { 80260786Sps if (nch > 0) 80360786Sps c = getcc(); 80460786Sps usercmd[nch] = c; 80560786Sps usercmd[nch+1] = '\0'; 80660786Sps nch++; 80760786Sps action = ecmd_decode(usercmd, &s); 80860786Sps } while (action == A_PREFIX); 80960786Sps 81089019Sps if (flags & EC_NORIGHTLEFT) 81189019Sps { 81289019Sps switch (action) 81389019Sps { 81489019Sps case EC_RIGHT: 81589019Sps case EC_LEFT: 81689019Sps action = A_INVALID; 81789019Sps break; 81889019Sps } 81989019Sps } 82060786Sps#if CMD_HISTORY 82160786Sps if (flags & EC_NOHISTORY) 82260786Sps { 82360786Sps /* 82460786Sps * The caller says there is no history list. 82560786Sps * Reject any history-manipulation action. 82660786Sps */ 82760786Sps switch (action) 82860786Sps { 82960786Sps case EC_UP: 83060786Sps case EC_DOWN: 83160786Sps action = A_INVALID; 83260786Sps break; 83360786Sps } 83460786Sps } 83560786Sps#endif 83660786Sps#if TAB_COMPLETE_FILENAME 83760786Sps if (flags & EC_NOCOMPLETE) 83860786Sps { 83960786Sps /* 84060786Sps * The caller says we don't want any filename completion cmds. 84160786Sps * Reject them. 84260786Sps */ 84360786Sps switch (action) 84460786Sps { 84560786Sps case EC_F_COMPLETE: 84660786Sps case EC_B_COMPLETE: 84760786Sps case EC_EXPAND: 84860786Sps action = A_INVALID; 84960786Sps break; 85060786Sps } 85160786Sps } 85260786Sps#endif 85360786Sps if ((flags & EC_PEEK) || action == A_INVALID) 85460786Sps { 85560786Sps /* 85660786Sps * We're just peeking, or we didn't understand the command. 85760786Sps * Unget all the characters we read in the loop above. 85860786Sps * This does NOT include the original character that was 85960786Sps * passed in as a parameter. 86060786Sps */ 86160786Sps while (nch > 1) 86260786Sps { 86360786Sps ungetcc(usercmd[--nch]); 86460786Sps } 86560786Sps } else 86660786Sps { 86760786Sps if (s != NULL) 86860786Sps ungetsc(s); 86960786Sps } 87060786Sps return action; 87160786Sps} 87260786Sps 873