160786Sps/* 2240121Sdelphij * Copyright (C) 1984-2012 Mark Nudelman 360786Sps * 460786Sps * You may distribute under the terms of either the GNU General Public 560786Sps * License or the Less License, as specified in the README file. 660786Sps * 7240121Sdelphij * For more information, see the README file. 860786Sps */ 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, 81240121Sdelphij 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, 9760786Sps SK(SK_RIGHT_ARROW),0, A_RSHIFT, 9860786Sps SK(SK_LEFT_ARROW),0, A_LSHIFT, 9960786Sps '{',0, A_F_BRACKET|A_EXTRA, '{','}',0, 10060786Sps '}',0, A_B_BRACKET|A_EXTRA, '{','}',0, 10160786Sps '(',0, A_F_BRACKET|A_EXTRA, '(',')',0, 10260786Sps ')',0, A_B_BRACKET|A_EXTRA, '(',')',0, 10360786Sps '[',0, A_F_BRACKET|A_EXTRA, '[',']',0, 10460786Sps ']',0, A_B_BRACKET|A_EXTRA, '[',']',0, 10560786Sps ESC,CONTROL('F'),0, A_F_BRACKET, 10660786Sps ESC,CONTROL('B'),0, A_B_BRACKET, 10760786Sps 'G',0, A_GOEND, 10860786Sps ESC,'>',0, A_GOEND, 10960786Sps '>',0, A_GOEND, 11060786Sps SK(SK_END),0, A_GOEND, 11160786Sps 'P',0, A_GOPOS, 11260786Sps 11360786Sps '0',0, A_DIGIT, 11460786Sps '1',0, A_DIGIT, 11560786Sps '2',0, A_DIGIT, 11660786Sps '3',0, A_DIGIT, 11760786Sps '4',0, A_DIGIT, 11860786Sps '5',0, A_DIGIT, 11960786Sps '6',0, A_DIGIT, 12060786Sps '7',0, A_DIGIT, 12160786Sps '8',0, A_DIGIT, 12260786Sps '9',0, A_DIGIT, 123170256Sdelphij '.',0, A_DIGIT, 12460786Sps 12560786Sps '=',0, A_STAT, 12660786Sps CONTROL('G'),0, A_STAT, 12760786Sps ':','f',0, A_STAT, 12860786Sps '/',0, A_F_SEARCH, 12960786Sps '?',0, A_B_SEARCH, 13060786Sps ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0, 13160786Sps ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0, 13260786Sps 'n',0, A_AGAIN_SEARCH, 13360786Sps ESC,'n',0, A_T_AGAIN_SEARCH, 13460786Sps 'N',0, A_REVERSE_SEARCH, 13560786Sps ESC,'N',0, A_T_REVERSE_SEARCH, 136191930Sdelphij '&',0, A_FILTER, 13760786Sps 'm',0, A_SETMARK, 13860786Sps '\'',0, A_GOMARK, 13960786Sps CONTROL('X'),CONTROL('X'),0, A_GOMARK, 14060786Sps 'E',0, A_EXAMINE, 14160786Sps ':','e',0, A_EXAMINE, 14260786Sps CONTROL('X'),CONTROL('V'),0, A_EXAMINE, 14360786Sps ':','n',0, A_NEXT_FILE, 14460786Sps ':','p',0, A_PREV_FILE, 14589019Sps 't',0, A_NEXT_TAG, 14689019Sps 'T',0, A_PREV_TAG, 14760786Sps ':','x',0, A_INDEX_FILE, 14860786Sps ':','d',0, A_REMOVE_FILE, 14960786Sps '-',0, A_OPT_TOGGLE, 15060786Sps ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0, 15160786Sps 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0, 15260786Sps '_',0, A_DISP_OPTION, 15360786Sps '|',0, A_PIPE, 15460786Sps 'v',0, A_VISUAL, 15560786Sps '!',0, A_SHELL, 15660786Sps '+',0, A_FIRSTCMD, 15760786Sps 15860786Sps 'H',0, A_HELP, 15960786Sps 'h',0, A_HELP, 16060786Sps SK(SK_F1),0, A_HELP, 16160786Sps 'V',0, A_VERSION, 16260786Sps 'q',0, A_QUIT, 16360786Sps 'Q',0, A_QUIT, 16460786Sps ':','q',0, A_QUIT, 16560786Sps ':','Q',0, A_QUIT, 16660786Sps 'Z','Z',0, A_QUIT 16760786Sps}; 16860786Sps 16960786Spsstatic unsigned char edittable[] = 17060786Sps{ 17160786Sps '\t',0, EC_F_COMPLETE, /* TAB */ 17260786Sps '\17',0, EC_B_COMPLETE, /* BACKTAB */ 17360786Sps SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */ 17460786Sps ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */ 17560786Sps CONTROL('L'),0, EC_EXPAND, /* CTRL-L */ 17660786Sps CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */ 17760786Sps CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */ 17860786Sps ESC,'l',0, EC_RIGHT, /* ESC l */ 17960786Sps SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */ 18060786Sps ESC,'h',0, EC_LEFT, /* ESC h */ 18160786Sps SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */ 18260786Sps ESC,'b',0, EC_W_LEFT, /* ESC b */ 18360786Sps ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */ 18460786Sps SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */ 18560786Sps ESC,'w',0, EC_W_RIGHT, /* ESC w */ 18660786Sps ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */ 18760786Sps SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */ 18860786Sps ESC,'i',0, EC_INSERT, /* ESC i */ 18960786Sps SK(SK_INSERT),0, EC_INSERT, /* INSERT */ 19060786Sps ESC,'x',0, EC_DELETE, /* ESC x */ 19160786Sps SK(SK_DELETE),0, EC_DELETE, /* DELETE */ 19260786Sps ESC,'X',0, EC_W_DELETE, /* ESC X */ 19360786Sps ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */ 19460786Sps SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */ 19560786Sps SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */ 19660786Sps ESC,'\b',0, EC_W_BACKSPACE, /* ESC BACKSPACE */ 19760786Sps ESC,'0',0, EC_HOME, /* ESC 0 */ 19860786Sps SK(SK_HOME),0, EC_HOME, /* HOME */ 19960786Sps ESC,'$',0, EC_END, /* ESC $ */ 20060786Sps SK(SK_END),0, EC_END, /* END */ 20160786Sps ESC,'k',0, EC_UP, /* ESC k */ 20260786Sps SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */ 20360786Sps ESC,'j',0, EC_DOWN, /* ESC j */ 20460786Sps SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */ 205221715Sdelphij CONTROL('G'),0, EC_ABORT, /* CTRL-G */ 20660786Sps}; 20760786Sps 20860786Sps/* 20960786Sps * Structure to support a list of command tables. 21060786Sps */ 21160786Spsstruct tablelist 21260786Sps{ 21360786Sps struct tablelist *t_next; 21460786Sps char *t_start; 21560786Sps char *t_end; 21660786Sps}; 21760786Sps 21860786Sps/* 21960786Sps * List of command tables and list of line-edit tables. 22060786Sps */ 22160786Spsstatic struct tablelist *list_fcmd_tables = NULL; 22260786Spsstatic struct tablelist *list_ecmd_tables = NULL; 22360786Spsstatic struct tablelist *list_var_tables = NULL; 22460786Spsstatic struct tablelist *list_sysvar_tables = NULL; 22560786Sps 22660786Sps 22760786Sps/* 22860786Sps * Expand special key abbreviations in a command table. 22960786Sps */ 23060786Sps static void 23160786Spsexpand_special_keys(table, len) 23260786Sps char *table; 23360786Sps int len; 23460786Sps{ 23560786Sps register char *fm; 23660786Sps register char *to; 23760786Sps register int a; 23860786Sps char *repl; 23960786Sps int klen; 24060786Sps 24160786Sps for (fm = table; fm < table + len; ) 24260786Sps { 24360786Sps /* 24460786Sps * Rewrite each command in the table with any 24560786Sps * special key abbreviations expanded. 24660786Sps */ 24760786Sps for (to = fm; *fm != '\0'; ) 24860786Sps { 24960786Sps if (*fm != SK_SPECIAL_KEY) 25060786Sps { 25160786Sps *to++ = *fm++; 25260786Sps continue; 25360786Sps } 25460786Sps /* 25560786Sps * After SK_SPECIAL_KEY, next byte is the type 25660786Sps * of special key (one of the SK_* contants), 25760786Sps * and the byte after that is the number of bytes, 25860786Sps * N, reserved by the abbreviation (including the 25960786Sps * SK_SPECIAL_KEY and key type bytes). 26060786Sps * Replace all N bytes with the actual bytes 26160786Sps * output by the special key on this terminal. 26260786Sps */ 26360786Sps repl = special_key_str(fm[1]); 26460786Sps klen = fm[2] & 0377; 26560786Sps fm += klen; 266128345Stjr if (repl == NULL || (int) strlen(repl) > klen) 26760786Sps repl = "\377"; 26860786Sps while (*repl != '\0') 26960786Sps *to++ = *repl++; 27060786Sps } 27160786Sps *to++ = '\0'; 27260786Sps /* 27360786Sps * Fill any unused bytes between end of command and 27460786Sps * the action byte with A_SKIP. 27560786Sps */ 27660786Sps while (to <= fm) 27760786Sps *to++ = A_SKIP; 27860786Sps fm++; 27960786Sps a = *fm++ & 0377; 28060786Sps if (a & A_EXTRA) 28160786Sps { 28260786Sps while (*fm++ != '\0') 28360786Sps continue; 28460786Sps } 28560786Sps } 28660786Sps} 28760786Sps 28860786Sps/* 28960786Sps * Initialize the command lists. 29060786Sps */ 29160786Sps public void 29260786Spsinit_cmds() 29360786Sps{ 29460786Sps /* 29560786Sps * Add the default command tables. 29660786Sps */ 29760786Sps add_fcmd_table((char*)cmdtable, sizeof(cmdtable)); 29860786Sps add_ecmd_table((char*)edittable, sizeof(edittable)); 29960786Sps#if USERFILE 30060786Sps /* 30189019Sps * For backwards compatibility, 30289019Sps * try to add tables in the OLD system lesskey file. 30389019Sps */ 30489019Sps#ifdef BINDIR 30589019Sps add_hometable(NULL, BINDIR "/.sysless", 1); 30689019Sps#endif 30789019Sps /* 30860786Sps * Try to add the tables in the system lesskey file. 30960786Sps */ 31060786Sps add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1); 31160786Sps /* 31260786Sps * Try to add the tables in the standard lesskey file "$HOME/.less". 31360786Sps */ 31460786Sps add_hometable("LESSKEY", LESSKEYFILE, 0); 31560786Sps#endif 31660786Sps} 31760786Sps 31860786Sps/* 31960786Sps * Add a command table. 32060786Sps */ 32160786Sps static int 32260786Spsadd_cmd_table(tlist, buf, len) 32360786Sps struct tablelist **tlist; 32460786Sps char *buf; 32560786Sps int len; 32660786Sps{ 32760786Sps register struct tablelist *t; 32860786Sps 32960786Sps if (len == 0) 33060786Sps return (0); 33160786Sps /* 33260786Sps * Allocate a tablelist structure, initialize it, 33360786Sps * and link it into the list of tables. 33460786Sps */ 33560786Sps if ((t = (struct tablelist *) 33660786Sps calloc(1, sizeof(struct tablelist))) == NULL) 33760786Sps { 33860786Sps return (-1); 33960786Sps } 34060786Sps expand_special_keys(buf, len); 34160786Sps t->t_start = buf; 34260786Sps t->t_end = buf + len; 34360786Sps t->t_next = *tlist; 34460786Sps *tlist = t; 34560786Sps return (0); 34660786Sps} 34760786Sps 34860786Sps/* 34960786Sps * Add a command table. 35060786Sps */ 35160786Sps public void 35260786Spsadd_fcmd_table(buf, len) 35360786Sps char *buf; 35460786Sps int len; 35560786Sps{ 35660786Sps if (add_cmd_table(&list_fcmd_tables, buf, len) < 0) 35760786Sps error("Warning: some commands disabled", NULL_PARG); 35860786Sps} 35960786Sps 36060786Sps/* 36160786Sps * Add an editing command table. 36260786Sps */ 36360786Sps public void 36460786Spsadd_ecmd_table(buf, len) 36560786Sps char *buf; 36660786Sps int len; 36760786Sps{ 36860786Sps if (add_cmd_table(&list_ecmd_tables, buf, len) < 0) 36960786Sps error("Warning: some edit commands disabled", NULL_PARG); 37060786Sps} 37160786Sps 37260786Sps/* 37360786Sps * Add an environment variable table. 37460786Sps */ 37560786Sps static void 37660786Spsadd_var_table(tlist, buf, len) 37760786Sps struct tablelist **tlist; 37860786Sps char *buf; 37960786Sps int len; 38060786Sps{ 38160786Sps if (add_cmd_table(tlist, buf, len) < 0) 38260786Sps error("Warning: environment variables from lesskey file unavailable", NULL_PARG); 38360786Sps} 38460786Sps 38560786Sps/* 38660786Sps * Search a single command table for the command string in cmd. 38760786Sps */ 38863128Sps static int 38960786Spscmd_search(cmd, table, endtable, sp) 39060786Sps char *cmd; 39160786Sps char *table; 39260786Sps char *endtable; 39360786Sps char **sp; 39460786Sps{ 39560786Sps register char *p; 39660786Sps register char *q; 39760786Sps register int a; 39860786Sps 39963128Sps *sp = NULL; 40060786Sps for (p = table, q = cmd; p < endtable; p++, q++) 40160786Sps { 40260786Sps if (*p == *q) 40360786Sps { 40460786Sps /* 40560786Sps * Current characters match. 40660786Sps * If we're at the end of the string, we've found it. 40760786Sps * Return the action code, which is the character 40860786Sps * after the null at the end of the string 40960786Sps * in the command table. 41060786Sps */ 41160786Sps if (*p == '\0') 41260786Sps { 41360786Sps a = *++p & 0377; 41460786Sps while (a == A_SKIP) 41560786Sps a = *++p & 0377; 41660786Sps if (a == A_END_LIST) 41760786Sps { 41860786Sps /* 41960786Sps * We get here only if the original 42060786Sps * cmd string passed in was empty (""). 42160786Sps * I don't think that can happen, 42260786Sps * but just in case ... 42360786Sps */ 42460786Sps return (A_UINVALID); 42560786Sps } 42660786Sps /* 42760786Sps * Check for an "extra" string. 42860786Sps */ 42960786Sps if (a & A_EXTRA) 43060786Sps { 43160786Sps *sp = ++p; 43260786Sps a &= ~A_EXTRA; 43363128Sps } 43460786Sps return (a); 43560786Sps } 43660786Sps } else if (*q == '\0') 43760786Sps { 43860786Sps /* 43960786Sps * Hit the end of the user's command, 44060786Sps * but not the end of the string in the command table. 44160786Sps * The user's command is incomplete. 44260786Sps */ 44360786Sps return (A_PREFIX); 44460786Sps } else 44560786Sps { 44660786Sps /* 44760786Sps * Not a match. 44860786Sps * Skip ahead to the next command in the 44960786Sps * command table, and reset the pointer 45060786Sps * to the beginning of the user's command. 45160786Sps */ 45260786Sps if (*p == '\0' && p[1] == A_END_LIST) 45360786Sps { 45460786Sps /* 45560786Sps * A_END_LIST is a special marker that tells 45660786Sps * us to abort the cmd search. 45760786Sps */ 45860786Sps return (A_UINVALID); 45960786Sps } 46060786Sps while (*p++ != '\0') 46160786Sps continue; 46260786Sps while (*p == A_SKIP) 46360786Sps p++; 46460786Sps if (*p & A_EXTRA) 46560786Sps while (*++p != '\0') 46660786Sps continue; 46760786Sps q = cmd-1; 46860786Sps } 46960786Sps } 47060786Sps /* 47160786Sps * No match found in the entire command table. 47260786Sps */ 47360786Sps return (A_INVALID); 47460786Sps} 47560786Sps 47660786Sps/* 47760786Sps * Decode a command character and return the associated action. 47860786Sps * The "extra" string, if any, is returned in sp. 47960786Sps */ 48060786Sps static int 48160786Spscmd_decode(tlist, cmd, sp) 48260786Sps struct tablelist *tlist; 48360786Sps char *cmd; 48460786Sps char **sp; 48560786Sps{ 48660786Sps register struct tablelist *t; 48760786Sps register int action = A_INVALID; 48860786Sps 48960786Sps /* 49060786Sps * Search thru all the command tables. 49160786Sps * Stop when we find an action which is not A_INVALID. 49260786Sps */ 49360786Sps for (t = tlist; t != NULL; t = t->t_next) 49460786Sps { 49560786Sps action = cmd_search(cmd, t->t_start, t->t_end, sp); 49660786Sps if (action != A_INVALID) 49760786Sps break; 49860786Sps } 49963128Sps if (action == A_UINVALID) 50063128Sps action = A_INVALID; 50160786Sps return (action); 50260786Sps} 50360786Sps 50460786Sps/* 50560786Sps * Decode a command from the cmdtables list. 50660786Sps */ 50760786Sps public int 50860786Spsfcmd_decode(cmd, sp) 50960786Sps char *cmd; 51060786Sps char **sp; 51160786Sps{ 51260786Sps return (cmd_decode(list_fcmd_tables, cmd, sp)); 51360786Sps} 51460786Sps 51560786Sps/* 51660786Sps * Decode a command from the edittables list. 51760786Sps */ 51860786Sps public int 51960786Spsecmd_decode(cmd, sp) 52060786Sps char *cmd; 52160786Sps char **sp; 52260786Sps{ 52360786Sps return (cmd_decode(list_ecmd_tables, cmd, sp)); 52460786Sps} 52560786Sps 52660786Sps/* 52760786Sps * Get the value of an environment variable. 52860786Sps * Looks first in the lesskey file, then in the real environment. 52960786Sps */ 53060786Sps public char * 53160786Spslgetenv(var) 53260786Sps char *var; 53360786Sps{ 53460786Sps int a; 53560786Sps char *s; 53660786Sps 53760786Sps a = cmd_decode(list_var_tables, var, &s); 53860786Sps if (a == EV_OK) 53960786Sps return (s); 54060786Sps s = getenv(var); 54160786Sps if (s != NULL && *s != '\0') 54260786Sps return (s); 54360786Sps a = cmd_decode(list_sysvar_tables, var, &s); 54460786Sps if (a == EV_OK) 54560786Sps return (s); 54660786Sps return (NULL); 54760786Sps} 54860786Sps 54960786Sps#if USERFILE 55060786Sps/* 55160786Sps * Get an "integer" from a lesskey file. 55260786Sps * Integers are stored in a funny format: 55360786Sps * two bytes, low order first, in radix KRADIX. 55460786Sps */ 55560786Sps static int 55660786Spsgint(sp) 55760786Sps char **sp; 55860786Sps{ 55960786Sps int n; 56060786Sps 56160786Sps n = *(*sp)++; 56260786Sps n += *(*sp)++ * KRADIX; 56360786Sps return (n); 56460786Sps} 56560786Sps 56660786Sps/* 56760786Sps * Process an old (pre-v241) lesskey file. 56860786Sps */ 56960786Sps static int 57060786Spsold_lesskey(buf, len) 57160786Sps char *buf; 57260786Sps int len; 57360786Sps{ 57460786Sps /* 57560786Sps * Old-style lesskey file. 57660786Sps * The file must end with either 57760786Sps * ...,cmd,0,action 57860786Sps * or ...,cmd,0,action|A_EXTRA,string,0 57960786Sps * So the last byte or the second to last byte must be zero. 58060786Sps */ 58160786Sps if (buf[len-1] != '\0' && buf[len-2] != '\0') 58260786Sps return (-1); 58360786Sps add_fcmd_table(buf, len); 58460786Sps return (0); 58560786Sps} 58660786Sps 58760786Sps/* 58860786Sps * Process a new (post-v241) lesskey file. 58960786Sps */ 59060786Sps static int 59160786Spsnew_lesskey(buf, len, sysvar) 59260786Sps char *buf; 59360786Sps int len; 59460786Sps int sysvar; 59560786Sps{ 59660786Sps char *p; 59760786Sps register int c; 59860786Sps register int n; 59960786Sps 60060786Sps /* 60160786Sps * New-style lesskey file. 60260786Sps * Extract the pieces. 60360786Sps */ 60460786Sps if (buf[len-3] != C0_END_LESSKEY_MAGIC || 60560786Sps buf[len-2] != C1_END_LESSKEY_MAGIC || 60660786Sps buf[len-1] != C2_END_LESSKEY_MAGIC) 60760786Sps return (-1); 60860786Sps p = buf + 4; 60960786Sps for (;;) 61060786Sps { 61160786Sps c = *p++; 61260786Sps switch (c) 61360786Sps { 61460786Sps case CMD_SECTION: 61560786Sps n = gint(&p); 61660786Sps add_fcmd_table(p, n); 61760786Sps p += n; 61860786Sps break; 61960786Sps case EDIT_SECTION: 62060786Sps n = gint(&p); 62160786Sps add_ecmd_table(p, n); 62260786Sps p += n; 62360786Sps break; 62460786Sps case VAR_SECTION: 62560786Sps n = gint(&p); 62660786Sps add_var_table((sysvar) ? 62760786Sps &list_sysvar_tables : &list_var_tables, p, n); 62860786Sps p += n; 62960786Sps break; 63060786Sps case END_SECTION: 63160786Sps return (0); 63260786Sps default: 63360786Sps /* 63460786Sps * Unrecognized section type. 63560786Sps */ 63660786Sps return (-1); 63760786Sps } 63860786Sps } 63960786Sps} 64060786Sps 64160786Sps/* 64260786Sps * Set up a user command table, based on a "lesskey" file. 64360786Sps */ 64460786Sps public int 64560786Spslesskey(filename, sysvar) 64660786Sps char *filename; 64760786Sps int sysvar; 64860786Sps{ 64960786Sps register char *buf; 65060786Sps register POSITION len; 65160786Sps register long n; 65260786Sps register int f; 65360786Sps 65460786Sps if (secure) 65560786Sps return (1); 65660786Sps /* 65760786Sps * Try to open the lesskey file. 65860786Sps */ 659128345Stjr filename = shell_unquote(filename); 66060786Sps f = open(filename, OPEN_READ); 66160786Sps free(filename); 66260786Sps if (f < 0) 66360786Sps return (1); 66460786Sps 66560786Sps /* 66660786Sps * Read the file into a buffer. 66760786Sps * We first figure out the size of the file and allocate space for it. 66860786Sps * {{ Minimal error checking is done here. 66960786Sps * A garbage .less file will produce strange results. 67060786Sps * To avoid a large amount of error checking code here, we 67160786Sps * rely on the lesskey program to generate a good .less file. }} 67260786Sps */ 67360786Sps len = filesize(f); 67460786Sps if (len == NULL_POSITION || len < 3) 67560786Sps { 67660786Sps /* 67760786Sps * Bad file (valid file must have at least 3 chars). 67860786Sps */ 67960786Sps close(f); 68060786Sps return (-1); 68160786Sps } 68260786Sps if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL) 68360786Sps { 68460786Sps close(f); 68560786Sps return (-1); 68660786Sps } 687173682Sdelphij if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK) 68860786Sps { 68960786Sps free(buf); 69060786Sps close(f); 69160786Sps return (-1); 69260786Sps } 69360786Sps n = read(f, buf, (unsigned int) len); 69460786Sps close(f); 69560786Sps if (n != len) 69660786Sps { 69760786Sps free(buf); 69860786Sps return (-1); 69960786Sps } 70060786Sps 70160786Sps /* 70260786Sps * Figure out if this is an old-style (before version 241) 70360786Sps * or new-style lesskey file format. 70460786Sps */ 70560786Sps if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC || 70660786Sps buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC) 70760786Sps return (old_lesskey(buf, (int)len)); 70860786Sps return (new_lesskey(buf, (int)len, sysvar)); 70960786Sps} 71060786Sps 71160786Sps/* 71260786Sps * Add the standard lesskey file "$HOME/.less" 71360786Sps */ 71460786Sps public void 71560786Spsadd_hometable(envname, def_filename, sysvar) 71660786Sps char *envname; 71760786Sps char *def_filename; 71860786Sps int sysvar; 71960786Sps{ 72060786Sps char *filename; 72160786Sps PARG parg; 72260786Sps 72389019Sps if (envname != NULL && (filename = lgetenv(envname)) != NULL) 72460786Sps filename = save(filename); 72560786Sps else if (sysvar) 72660786Sps filename = save(def_filename); 72760786Sps else 72860786Sps filename = homefile(def_filename); 72960786Sps if (filename == NULL) 73060786Sps return; 73160786Sps if (lesskey(filename, sysvar) < 0) 73260786Sps { 73360786Sps parg.p_string = filename; 73460786Sps error("Cannot use lesskey file \"%s\"", &parg); 73560786Sps } 73660786Sps free(filename); 73760786Sps} 73860786Sps#endif 73960786Sps 74060786Sps/* 74160786Sps * See if a char is a special line-editing command. 74260786Sps */ 74360786Sps public int 74460786Spseditchar(c, flags) 74560786Sps int c; 74660786Sps int flags; 74760786Sps{ 74860786Sps int action; 74960786Sps int nch; 75060786Sps char *s; 75160786Sps char usercmd[MAX_CMDLEN+1]; 75260786Sps 75360786Sps /* 75460786Sps * An editing character could actually be a sequence of characters; 75560786Sps * for example, an escape sequence sent by pressing the uparrow key. 75660786Sps * To match the editing string, we use the command decoder 75760786Sps * but give it the edit-commands command table 75860786Sps * This table is constructed to match the user's keyboard. 75960786Sps */ 760161475Sdelphij if (c == erase_char || c == erase2_char) 76160786Sps return (EC_BACKSPACE); 76260786Sps if (c == kill_char) 76360786Sps return (EC_LINEKILL); 76460786Sps 76560786Sps /* 76660786Sps * Collect characters in a buffer. 76760786Sps * Start with the one we have, and get more if we need them. 76860786Sps */ 76960786Sps nch = 0; 77060786Sps do { 77160786Sps if (nch > 0) 77260786Sps c = getcc(); 77360786Sps usercmd[nch] = c; 77460786Sps usercmd[nch+1] = '\0'; 77560786Sps nch++; 77660786Sps action = ecmd_decode(usercmd, &s); 77760786Sps } while (action == A_PREFIX); 77860786Sps 77989019Sps if (flags & EC_NORIGHTLEFT) 78089019Sps { 78189019Sps switch (action) 78289019Sps { 78389019Sps case EC_RIGHT: 78489019Sps case EC_LEFT: 78589019Sps action = A_INVALID; 78689019Sps break; 78789019Sps } 78889019Sps } 78960786Sps#if CMD_HISTORY 79060786Sps if (flags & EC_NOHISTORY) 79160786Sps { 79260786Sps /* 79360786Sps * The caller says there is no history list. 79460786Sps * Reject any history-manipulation action. 79560786Sps */ 79660786Sps switch (action) 79760786Sps { 79860786Sps case EC_UP: 79960786Sps case EC_DOWN: 80060786Sps action = A_INVALID; 80160786Sps break; 80260786Sps } 80360786Sps } 80460786Sps#endif 80560786Sps#if TAB_COMPLETE_FILENAME 80660786Sps if (flags & EC_NOCOMPLETE) 80760786Sps { 80860786Sps /* 80960786Sps * The caller says we don't want any filename completion cmds. 81060786Sps * Reject them. 81160786Sps */ 81260786Sps switch (action) 81360786Sps { 81460786Sps case EC_F_COMPLETE: 81560786Sps case EC_B_COMPLETE: 81660786Sps case EC_EXPAND: 81760786Sps action = A_INVALID; 81860786Sps break; 81960786Sps } 82060786Sps } 82160786Sps#endif 82260786Sps if ((flags & EC_PEEK) || action == A_INVALID) 82360786Sps { 82460786Sps /* 82560786Sps * We're just peeking, or we didn't understand the command. 82660786Sps * Unget all the characters we read in the loop above. 82760786Sps * This does NOT include the original character that was 82860786Sps * passed in as a parameter. 82960786Sps */ 83060786Sps while (nch > 1) 83160786Sps { 83260786Sps ungetcc(usercmd[--nch]); 83360786Sps } 83460786Sps } else 83560786Sps { 83660786Sps if (s != NULL) 83760786Sps ungetsc(s); 83860786Sps } 83960786Sps return action; 84060786Sps} 84160786Sps 842