decode.c revision 63128
160786Sps/* 260786Sps * Copyright (C) 1984-2000 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 * 760786Sps * For more information about less, or for information on how to 860786Sps * contact the author, see the README file. 960786Sps */ 1060786Sps 1160786Sps 1260786Sps/* 1360786Sps * Routines to decode user commands. 1460786Sps * 1560786Sps * This is all table driven. 1660786Sps * A command table is a sequence of command descriptors. 1760786Sps * Each command descriptor is a sequence of bytes with the following format: 1860786Sps * <c1><c2>...<cN><0><action> 1960786Sps * The characters c1,c2,...,cN are the command string; that is, 2060786Sps * the characters which the user must type. 2160786Sps * It is terminated by a null <0> byte. 2260786Sps * The byte after the null byte is the action code associated 2360786Sps * with the command string. 2460786Sps * If an action byte is OR-ed with A_EXTRA, this indicates 2560786Sps * that the option byte is followed by an extra string. 2660786Sps * 2760786Sps * There may be many command tables. 2860786Sps * The first (default) table is built-in. 2960786Sps * Other tables are read in from "lesskey" files. 3060786Sps * All the tables are linked together and are searched in order. 3160786Sps */ 3260786Sps 3360786Sps#include "less.h" 3460786Sps#include "cmd.h" 3560786Sps#include "lesskey.h" 3660786Sps 3760786Spsextern int erase_char, kill_char; 3860786Spsextern int secure; 3960786Sps 4060786Sps#define SK(k) \ 4160786Sps SK_SPECIAL_KEY, (k), 6, 1, 1, 1 4260786Sps/* 4360786Sps * Command table is ordered roughly according to expected 4460786Sps * frequency of use, so the common commands are near the beginning. 4560786Sps */ 4660786Sps 4760786Spsstatic unsigned char cmdtable[] = 4860786Sps{ 4960786Sps '\r',0, A_F_LINE, 5060786Sps '\n',0, A_F_LINE, 5160786Sps 'e',0, A_F_LINE, 5260786Sps 'j',0, A_F_LINE, 5360786Sps SK(SK_DOWN_ARROW),0, A_F_LINE, 5460786Sps CONTROL('E'),0, A_F_LINE, 5560786Sps CONTROL('N'),0, A_F_LINE, 5660786Sps 'k',0, A_B_LINE, 5760786Sps 'y',0, A_B_LINE, 5860786Sps CONTROL('Y'),0, A_B_LINE, 5960786Sps SK(SK_CONTROL_K),0, A_B_LINE, 6060786Sps CONTROL('P'),0, A_B_LINE, 6160786Sps SK(SK_UP_ARROW),0, A_B_LINE, 6260786Sps 'J',0, A_FF_LINE, 6360786Sps 'K',0, A_BF_LINE, 6460786Sps 'Y',0, A_BF_LINE, 6560786Sps 'd',0, A_F_SCROLL, 6660786Sps CONTROL('D'),0, A_F_SCROLL, 6760786Sps 'u',0, A_B_SCROLL, 6860786Sps CONTROL('U'),0, A_B_SCROLL, 6960786Sps ' ',0, A_F_SCREEN, 7060786Sps 'f',0, A_F_SCREEN, 7160786Sps CONTROL('F'),0, A_F_SCREEN, 7260786Sps CONTROL('V'),0, A_F_SCREEN, 7360786Sps SK(SK_PAGE_DOWN),0, A_F_SCREEN, 7460786Sps 'b',0, A_B_SCREEN, 7560786Sps CONTROL('B'),0, A_B_SCREEN, 7660786Sps ESC,'v',0, A_B_SCREEN, 7760786Sps SK(SK_PAGE_UP),0, A_B_SCREEN, 7860786Sps 'z',0, A_F_WINDOW, 7960786Sps 'w',0, A_B_WINDOW, 8060786Sps ESC,' ',0, A_FF_SCREEN, 8160786Sps 'F',0, A_F_FOREVER, 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, 12360786Sps 12460786Sps '=',0, A_STAT, 12560786Sps CONTROL('G'),0, A_STAT, 12660786Sps ':','f',0, A_STAT, 12760786Sps '/',0, A_F_SEARCH, 12860786Sps '?',0, A_B_SEARCH, 12960786Sps ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0, 13060786Sps ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0, 13160786Sps 'n',0, A_AGAIN_SEARCH, 13260786Sps ESC,'n',0, A_T_AGAIN_SEARCH, 13360786Sps 'N',0, A_REVERSE_SEARCH, 13460786Sps ESC,'N',0, A_T_REVERSE_SEARCH, 13560786Sps 'm',0, A_SETMARK, 13660786Sps '\'',0, A_GOMARK, 13760786Sps CONTROL('X'),CONTROL('X'),0, A_GOMARK, 13860786Sps 'E',0, A_EXAMINE, 13960786Sps ':','e',0, A_EXAMINE, 14060786Sps CONTROL('X'),CONTROL('V'),0, A_EXAMINE, 14160786Sps ':','n',0, A_NEXT_FILE, 14260786Sps ':','p',0, A_PREV_FILE, 14360786Sps ':','x',0, A_INDEX_FILE, 14460786Sps ':','d',0, A_REMOVE_FILE, 14560786Sps '-',0, A_OPT_TOGGLE, 14660786Sps ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0, 14760786Sps 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0, 14860786Sps '_',0, A_DISP_OPTION, 14960786Sps '|',0, A_PIPE, 15060786Sps 'v',0, A_VISUAL, 15160786Sps '!',0, A_SHELL, 15260786Sps '+',0, A_FIRSTCMD, 15360786Sps 15460786Sps 'H',0, A_HELP, 15560786Sps 'h',0, A_HELP, 15660786Sps SK(SK_F1),0, A_HELP, 15760786Sps 'V',0, A_VERSION, 15860786Sps 'q',0, A_QUIT, 15960786Sps 'Q',0, A_QUIT, 16060786Sps ':','q',0, A_QUIT, 16160786Sps ':','Q',0, A_QUIT, 16260786Sps 'Z','Z',0, A_QUIT 16360786Sps}; 16460786Sps 16560786Spsstatic unsigned char edittable[] = 16660786Sps{ 16760786Sps '\t',0, EC_F_COMPLETE, /* TAB */ 16860786Sps '\17',0, EC_B_COMPLETE, /* BACKTAB */ 16960786Sps SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */ 17060786Sps ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */ 17160786Sps CONTROL('L'),0, EC_EXPAND, /* CTRL-L */ 17260786Sps CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */ 17360786Sps CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */ 17460786Sps ESC,'l',0, EC_RIGHT, /* ESC l */ 17560786Sps SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */ 17660786Sps ESC,'h',0, EC_LEFT, /* ESC h */ 17760786Sps SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */ 17860786Sps ESC,'b',0, EC_W_LEFT, /* ESC b */ 17960786Sps ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */ 18060786Sps SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */ 18160786Sps ESC,'w',0, EC_W_RIGHT, /* ESC w */ 18260786Sps ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */ 18360786Sps SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */ 18460786Sps ESC,'i',0, EC_INSERT, /* ESC i */ 18560786Sps SK(SK_INSERT),0, EC_INSERT, /* INSERT */ 18660786Sps ESC,'x',0, EC_DELETE, /* ESC x */ 18760786Sps SK(SK_DELETE),0, EC_DELETE, /* DELETE */ 18860786Sps ESC,'X',0, EC_W_DELETE, /* ESC X */ 18960786Sps ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */ 19060786Sps SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */ 19160786Sps SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */ 19260786Sps ESC,'\b',0, EC_W_BACKSPACE, /* ESC BACKSPACE */ 19360786Sps ESC,'0',0, EC_HOME, /* ESC 0 */ 19460786Sps SK(SK_HOME),0, EC_HOME, /* HOME */ 19560786Sps ESC,'$',0, EC_END, /* ESC $ */ 19660786Sps SK(SK_END),0, EC_END, /* END */ 19760786Sps ESC,'k',0, EC_UP, /* ESC k */ 19860786Sps SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */ 19960786Sps ESC,'j',0, EC_DOWN, /* ESC j */ 20060786Sps SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */ 20160786Sps}; 20260786Sps 20360786Sps/* 20460786Sps * Structure to support a list of command tables. 20560786Sps */ 20660786Spsstruct tablelist 20760786Sps{ 20860786Sps struct tablelist *t_next; 20960786Sps char *t_start; 21060786Sps char *t_end; 21160786Sps}; 21260786Sps 21360786Sps/* 21460786Sps * List of command tables and list of line-edit tables. 21560786Sps */ 21660786Spsstatic struct tablelist *list_fcmd_tables = NULL; 21760786Spsstatic struct tablelist *list_ecmd_tables = NULL; 21860786Spsstatic struct tablelist *list_var_tables = NULL; 21960786Spsstatic struct tablelist *list_sysvar_tables = NULL; 22060786Sps 22160786Sps 22260786Sps/* 22360786Sps * Expand special key abbreviations in a command table. 22460786Sps */ 22560786Sps static void 22660786Spsexpand_special_keys(table, len) 22760786Sps char *table; 22860786Sps int len; 22960786Sps{ 23060786Sps register char *fm; 23160786Sps register char *to; 23260786Sps register int a; 23360786Sps char *repl; 23460786Sps int klen; 23560786Sps 23660786Sps for (fm = table; fm < table + len; ) 23760786Sps { 23860786Sps /* 23960786Sps * Rewrite each command in the table with any 24060786Sps * special key abbreviations expanded. 24160786Sps */ 24260786Sps for (to = fm; *fm != '\0'; ) 24360786Sps { 24460786Sps if (*fm != SK_SPECIAL_KEY) 24560786Sps { 24660786Sps *to++ = *fm++; 24760786Sps continue; 24860786Sps } 24960786Sps /* 25060786Sps * After SK_SPECIAL_KEY, next byte is the type 25160786Sps * of special key (one of the SK_* contants), 25260786Sps * and the byte after that is the number of bytes, 25360786Sps * N, reserved by the abbreviation (including the 25460786Sps * SK_SPECIAL_KEY and key type bytes). 25560786Sps * Replace all N bytes with the actual bytes 25660786Sps * output by the special key on this terminal. 25760786Sps */ 25860786Sps repl = special_key_str(fm[1]); 25960786Sps klen = fm[2] & 0377; 26060786Sps fm += klen; 26160786Sps if (repl == NULL || strlen(repl) > klen) 26260786Sps repl = "\377"; 26360786Sps while (*repl != '\0') 26460786Sps *to++ = *repl++; 26560786Sps } 26660786Sps *to++ = '\0'; 26760786Sps /* 26860786Sps * Fill any unused bytes between end of command and 26960786Sps * the action byte with A_SKIP. 27060786Sps */ 27160786Sps while (to <= fm) 27260786Sps *to++ = A_SKIP; 27360786Sps fm++; 27460786Sps a = *fm++ & 0377; 27560786Sps if (a & A_EXTRA) 27660786Sps { 27760786Sps while (*fm++ != '\0') 27860786Sps continue; 27960786Sps } 28060786Sps } 28160786Sps} 28260786Sps 28360786Sps/* 28460786Sps * Initialize the command lists. 28560786Sps */ 28660786Sps public void 28760786Spsinit_cmds() 28860786Sps{ 28960786Sps /* 29060786Sps * Add the default command tables. 29160786Sps */ 29260786Sps add_fcmd_table((char*)cmdtable, sizeof(cmdtable)); 29360786Sps add_ecmd_table((char*)edittable, sizeof(edittable)); 29460786Sps#if USERFILE 29560786Sps /* 29660786Sps * Try to add the tables in the system lesskey file. 29760786Sps */ 29860786Sps add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1); 29960786Sps /* 30060786Sps * Try to add the tables in the standard lesskey file "$HOME/.less". 30160786Sps */ 30260786Sps add_hometable("LESSKEY", LESSKEYFILE, 0); 30360786Sps#endif 30460786Sps} 30560786Sps 30660786Sps/* 30760786Sps * Add a command table. 30860786Sps */ 30960786Sps static int 31060786Spsadd_cmd_table(tlist, buf, len) 31160786Sps struct tablelist **tlist; 31260786Sps char *buf; 31360786Sps int len; 31460786Sps{ 31560786Sps register struct tablelist *t; 31660786Sps 31760786Sps if (len == 0) 31860786Sps return (0); 31960786Sps /* 32060786Sps * Allocate a tablelist structure, initialize it, 32160786Sps * and link it into the list of tables. 32260786Sps */ 32360786Sps if ((t = (struct tablelist *) 32460786Sps calloc(1, sizeof(struct tablelist))) == NULL) 32560786Sps { 32660786Sps return (-1); 32760786Sps } 32860786Sps expand_special_keys(buf, len); 32960786Sps t->t_start = buf; 33060786Sps t->t_end = buf + len; 33160786Sps t->t_next = *tlist; 33260786Sps *tlist = t; 33360786Sps return (0); 33460786Sps} 33560786Sps 33660786Sps/* 33760786Sps * Add a command table. 33860786Sps */ 33960786Sps public void 34060786Spsadd_fcmd_table(buf, len) 34160786Sps char *buf; 34260786Sps int len; 34360786Sps{ 34460786Sps if (add_cmd_table(&list_fcmd_tables, buf, len) < 0) 34560786Sps error("Warning: some commands disabled", NULL_PARG); 34660786Sps} 34760786Sps 34860786Sps/* 34960786Sps * Add an editing command table. 35060786Sps */ 35160786Sps public void 35260786Spsadd_ecmd_table(buf, len) 35360786Sps char *buf; 35460786Sps int len; 35560786Sps{ 35660786Sps if (add_cmd_table(&list_ecmd_tables, buf, len) < 0) 35760786Sps error("Warning: some edit commands disabled", NULL_PARG); 35860786Sps} 35960786Sps 36060786Sps/* 36160786Sps * Add an environment variable table. 36260786Sps */ 36360786Sps static void 36460786Spsadd_var_table(tlist, buf, len) 36560786Sps struct tablelist **tlist; 36660786Sps char *buf; 36760786Sps int len; 36860786Sps{ 36960786Sps if (add_cmd_table(tlist, buf, len) < 0) 37060786Sps error("Warning: environment variables from lesskey file unavailable", NULL_PARG); 37160786Sps} 37260786Sps 37360786Sps/* 37460786Sps * Search a single command table for the command string in cmd. 37560786Sps */ 37663128Sps static int 37760786Spscmd_search(cmd, table, endtable, sp) 37860786Sps char *cmd; 37960786Sps char *table; 38060786Sps char *endtable; 38160786Sps char **sp; 38260786Sps{ 38360786Sps register char *p; 38460786Sps register char *q; 38560786Sps register int a; 38660786Sps 38763128Sps *sp = NULL; 38860786Sps for (p = table, q = cmd; p < endtable; p++, q++) 38960786Sps { 39060786Sps if (*p == *q) 39160786Sps { 39260786Sps /* 39360786Sps * Current characters match. 39460786Sps * If we're at the end of the string, we've found it. 39560786Sps * Return the action code, which is the character 39660786Sps * after the null at the end of the string 39760786Sps * in the command table. 39860786Sps */ 39960786Sps if (*p == '\0') 40060786Sps { 40160786Sps a = *++p & 0377; 40260786Sps while (a == A_SKIP) 40360786Sps a = *++p & 0377; 40460786Sps if (a == A_END_LIST) 40560786Sps { 40660786Sps /* 40760786Sps * We get here only if the original 40860786Sps * cmd string passed in was empty (""). 40960786Sps * I don't think that can happen, 41060786Sps * but just in case ... 41160786Sps */ 41260786Sps return (A_UINVALID); 41360786Sps } 41460786Sps /* 41560786Sps * Check for an "extra" string. 41660786Sps */ 41760786Sps if (a & A_EXTRA) 41860786Sps { 41960786Sps *sp = ++p; 42060786Sps a &= ~A_EXTRA; 42163128Sps } 42260786Sps return (a); 42360786Sps } 42460786Sps } else if (*q == '\0') 42560786Sps { 42660786Sps /* 42760786Sps * Hit the end of the user's command, 42860786Sps * but not the end of the string in the command table. 42960786Sps * The user's command is incomplete. 43060786Sps */ 43160786Sps return (A_PREFIX); 43260786Sps } else 43360786Sps { 43460786Sps /* 43560786Sps * Not a match. 43660786Sps * Skip ahead to the next command in the 43760786Sps * command table, and reset the pointer 43860786Sps * to the beginning of the user's command. 43960786Sps */ 44060786Sps if (*p == '\0' && p[1] == A_END_LIST) 44160786Sps { 44260786Sps /* 44360786Sps * A_END_LIST is a special marker that tells 44460786Sps * us to abort the cmd search. 44560786Sps */ 44660786Sps return (A_UINVALID); 44760786Sps } 44860786Sps while (*p++ != '\0') 44960786Sps continue; 45060786Sps while (*p == A_SKIP) 45160786Sps p++; 45260786Sps if (*p & A_EXTRA) 45360786Sps while (*++p != '\0') 45460786Sps continue; 45560786Sps q = cmd-1; 45660786Sps } 45760786Sps } 45860786Sps /* 45960786Sps * No match found in the entire command table. 46060786Sps */ 46160786Sps return (A_INVALID); 46260786Sps} 46360786Sps 46460786Sps/* 46560786Sps * Decode a command character and return the associated action. 46660786Sps * The "extra" string, if any, is returned in sp. 46760786Sps */ 46860786Sps static int 46960786Spscmd_decode(tlist, cmd, sp) 47060786Sps struct tablelist *tlist; 47160786Sps char *cmd; 47260786Sps char **sp; 47360786Sps{ 47460786Sps register struct tablelist *t; 47560786Sps register int action = A_INVALID; 47660786Sps 47760786Sps /* 47860786Sps * Search thru all the command tables. 47960786Sps * Stop when we find an action which is not A_INVALID. 48060786Sps */ 48160786Sps for (t = tlist; t != NULL; t = t->t_next) 48260786Sps { 48360786Sps action = cmd_search(cmd, t->t_start, t->t_end, sp); 48460786Sps if (action != A_INVALID) 48560786Sps break; 48660786Sps } 48763128Sps if (action == A_UINVALID) 48863128Sps action = A_INVALID; 48960786Sps return (action); 49060786Sps} 49160786Sps 49260786Sps/* 49360786Sps * Decode a command from the cmdtables list. 49460786Sps */ 49560786Sps public int 49660786Spsfcmd_decode(cmd, sp) 49760786Sps char *cmd; 49860786Sps char **sp; 49960786Sps{ 50060786Sps return (cmd_decode(list_fcmd_tables, cmd, sp)); 50160786Sps} 50260786Sps 50360786Sps/* 50460786Sps * Decode a command from the edittables list. 50560786Sps */ 50660786Sps public int 50760786Spsecmd_decode(cmd, sp) 50860786Sps char *cmd; 50960786Sps char **sp; 51060786Sps{ 51160786Sps return (cmd_decode(list_ecmd_tables, cmd, sp)); 51260786Sps} 51360786Sps 51460786Sps/* 51560786Sps * Get the value of an environment variable. 51660786Sps * Looks first in the lesskey file, then in the real environment. 51760786Sps */ 51860786Sps public char * 51960786Spslgetenv(var) 52060786Sps char *var; 52160786Sps{ 52260786Sps int a; 52360786Sps char *s; 52460786Sps 52560786Sps a = cmd_decode(list_var_tables, var, &s); 52660786Sps if (a == EV_OK) 52760786Sps return (s); 52860786Sps s = getenv(var); 52960786Sps if (s != NULL && *s != '\0') 53060786Sps return (s); 53160786Sps a = cmd_decode(list_sysvar_tables, var, &s); 53260786Sps if (a == EV_OK) 53360786Sps return (s); 53460786Sps return (NULL); 53560786Sps} 53660786Sps 53760786Sps#if USERFILE 53860786Sps/* 53960786Sps * Get an "integer" from a lesskey file. 54060786Sps * Integers are stored in a funny format: 54160786Sps * two bytes, low order first, in radix KRADIX. 54260786Sps */ 54360786Sps static int 54460786Spsgint(sp) 54560786Sps char **sp; 54660786Sps{ 54760786Sps int n; 54860786Sps 54960786Sps n = *(*sp)++; 55060786Sps n += *(*sp)++ * KRADIX; 55160786Sps return (n); 55260786Sps} 55360786Sps 55460786Sps/* 55560786Sps * Process an old (pre-v241) lesskey file. 55660786Sps */ 55760786Sps static int 55860786Spsold_lesskey(buf, len) 55960786Sps char *buf; 56060786Sps int len; 56160786Sps{ 56260786Sps /* 56360786Sps * Old-style lesskey file. 56460786Sps * The file must end with either 56560786Sps * ...,cmd,0,action 56660786Sps * or ...,cmd,0,action|A_EXTRA,string,0 56760786Sps * So the last byte or the second to last byte must be zero. 56860786Sps */ 56960786Sps if (buf[len-1] != '\0' && buf[len-2] != '\0') 57060786Sps return (-1); 57160786Sps add_fcmd_table(buf, len); 57260786Sps return (0); 57360786Sps} 57460786Sps 57560786Sps/* 57660786Sps * Process a new (post-v241) lesskey file. 57760786Sps */ 57860786Sps static int 57960786Spsnew_lesskey(buf, len, sysvar) 58060786Sps char *buf; 58160786Sps int len; 58260786Sps int sysvar; 58360786Sps{ 58460786Sps char *p; 58560786Sps register int c; 58660786Sps register int n; 58760786Sps 58860786Sps /* 58960786Sps * New-style lesskey file. 59060786Sps * Extract the pieces. 59160786Sps */ 59260786Sps if (buf[len-3] != C0_END_LESSKEY_MAGIC || 59360786Sps buf[len-2] != C1_END_LESSKEY_MAGIC || 59460786Sps buf[len-1] != C2_END_LESSKEY_MAGIC) 59560786Sps return (-1); 59660786Sps p = buf + 4; 59760786Sps for (;;) 59860786Sps { 59960786Sps c = *p++; 60060786Sps switch (c) 60160786Sps { 60260786Sps case CMD_SECTION: 60360786Sps n = gint(&p); 60460786Sps add_fcmd_table(p, n); 60560786Sps p += n; 60660786Sps break; 60760786Sps case EDIT_SECTION: 60860786Sps n = gint(&p); 60960786Sps add_ecmd_table(p, n); 61060786Sps p += n; 61160786Sps break; 61260786Sps case VAR_SECTION: 61360786Sps n = gint(&p); 61460786Sps add_var_table((sysvar) ? 61560786Sps &list_sysvar_tables : &list_var_tables, p, n); 61660786Sps p += n; 61760786Sps break; 61860786Sps case END_SECTION: 61960786Sps return (0); 62060786Sps default: 62160786Sps /* 62260786Sps * Unrecognized section type. 62360786Sps */ 62460786Sps return (-1); 62560786Sps } 62660786Sps } 62760786Sps} 62860786Sps 62960786Sps/* 63060786Sps * Set up a user command table, based on a "lesskey" file. 63160786Sps */ 63260786Sps public int 63360786Spslesskey(filename, sysvar) 63460786Sps char *filename; 63560786Sps int sysvar; 63660786Sps{ 63760786Sps register char *buf; 63860786Sps register POSITION len; 63960786Sps register long n; 64060786Sps register int f; 64160786Sps 64260786Sps if (secure) 64360786Sps return (1); 64460786Sps /* 64560786Sps * Try to open the lesskey file. 64660786Sps */ 64760786Sps filename = unquote_file(filename); 64860786Sps f = open(filename, OPEN_READ); 64960786Sps free(filename); 65060786Sps if (f < 0) 65160786Sps return (1); 65260786Sps 65360786Sps /* 65460786Sps * Read the file into a buffer. 65560786Sps * We first figure out the size of the file and allocate space for it. 65660786Sps * {{ Minimal error checking is done here. 65760786Sps * A garbage .less file will produce strange results. 65860786Sps * To avoid a large amount of error checking code here, we 65960786Sps * rely on the lesskey program to generate a good .less file. }} 66060786Sps */ 66160786Sps len = filesize(f); 66260786Sps if (len == NULL_POSITION || len < 3) 66360786Sps { 66460786Sps /* 66560786Sps * Bad file (valid file must have at least 3 chars). 66660786Sps */ 66760786Sps close(f); 66860786Sps return (-1); 66960786Sps } 67060786Sps if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL) 67160786Sps { 67260786Sps close(f); 67360786Sps return (-1); 67460786Sps } 67560786Sps if (lseek(f, (off_t)0, 0) == BAD_LSEEK) 67660786Sps { 67760786Sps free(buf); 67860786Sps close(f); 67960786Sps return (-1); 68060786Sps } 68160786Sps n = read(f, buf, (unsigned int) len); 68260786Sps close(f); 68360786Sps if (n != len) 68460786Sps { 68560786Sps free(buf); 68660786Sps return (-1); 68760786Sps } 68860786Sps 68960786Sps /* 69060786Sps * Figure out if this is an old-style (before version 241) 69160786Sps * or new-style lesskey file format. 69260786Sps */ 69360786Sps if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC || 69460786Sps buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC) 69560786Sps return (old_lesskey(buf, (int)len)); 69660786Sps return (new_lesskey(buf, (int)len, sysvar)); 69760786Sps} 69860786Sps 69960786Sps/* 70060786Sps * Add the standard lesskey file "$HOME/.less" 70160786Sps */ 70260786Sps public void 70360786Spsadd_hometable(envname, def_filename, sysvar) 70460786Sps char *envname; 70560786Sps char *def_filename; 70660786Sps int sysvar; 70760786Sps{ 70860786Sps char *filename; 70960786Sps PARG parg; 71060786Sps 71160786Sps if ((filename = lgetenv(envname)) != NULL) 71260786Sps filename = save(filename); 71360786Sps else if (sysvar) 71460786Sps filename = save(def_filename); 71560786Sps else 71660786Sps filename = homefile(def_filename); 71760786Sps if (filename == NULL) 71860786Sps return; 71960786Sps if (lesskey(filename, sysvar) < 0) 72060786Sps { 72160786Sps parg.p_string = filename; 72260786Sps error("Cannot use lesskey file \"%s\"", &parg); 72360786Sps } 72460786Sps free(filename); 72560786Sps} 72660786Sps#endif 72760786Sps 72860786Sps/* 72960786Sps * See if a char is a special line-editing command. 73060786Sps */ 73160786Sps public int 73260786Spseditchar(c, flags) 73360786Sps int c; 73460786Sps int flags; 73560786Sps{ 73660786Sps int action; 73760786Sps int nch; 73860786Sps char *s; 73960786Sps char usercmd[MAX_CMDLEN+1]; 74060786Sps 74160786Sps /* 74260786Sps * An editing character could actually be a sequence of characters; 74360786Sps * for example, an escape sequence sent by pressing the uparrow key. 74460786Sps * To match the editing string, we use the command decoder 74560786Sps * but give it the edit-commands command table 74660786Sps * This table is constructed to match the user's keyboard. 74760786Sps */ 74860786Sps if (c == erase_char) 74960786Sps return (EC_BACKSPACE); 75060786Sps if (c == kill_char) 75160786Sps return (EC_LINEKILL); 75260786Sps 75360786Sps /* 75460786Sps * Collect characters in a buffer. 75560786Sps * Start with the one we have, and get more if we need them. 75660786Sps */ 75760786Sps nch = 0; 75860786Sps do { 75960786Sps if (nch > 0) 76060786Sps c = getcc(); 76160786Sps usercmd[nch] = c; 76260786Sps usercmd[nch+1] = '\0'; 76360786Sps nch++; 76460786Sps action = ecmd_decode(usercmd, &s); 76560786Sps } while (action == A_PREFIX); 76660786Sps 76760786Sps#if CMD_HISTORY 76860786Sps if (flags & EC_NOHISTORY) 76960786Sps { 77060786Sps /* 77160786Sps * The caller says there is no history list. 77260786Sps * Reject any history-manipulation action. 77360786Sps */ 77460786Sps switch (action) 77560786Sps { 77660786Sps case EC_UP: 77760786Sps case EC_DOWN: 77860786Sps action = A_INVALID; 77960786Sps break; 78060786Sps } 78160786Sps } 78260786Sps#endif 78360786Sps#if TAB_COMPLETE_FILENAME 78460786Sps if (flags & EC_NOCOMPLETE) 78560786Sps { 78660786Sps /* 78760786Sps * The caller says we don't want any filename completion cmds. 78860786Sps * Reject them. 78960786Sps */ 79060786Sps switch (action) 79160786Sps { 79260786Sps case EC_F_COMPLETE: 79360786Sps case EC_B_COMPLETE: 79460786Sps case EC_EXPAND: 79560786Sps action = A_INVALID; 79660786Sps break; 79760786Sps } 79860786Sps } 79960786Sps#endif 80060786Sps if ((flags & EC_PEEK) || action == A_INVALID) 80160786Sps { 80260786Sps /* 80360786Sps * We're just peeking, or we didn't understand the command. 80460786Sps * Unget all the characters we read in the loop above. 80560786Sps * This does NOT include the original character that was 80660786Sps * passed in as a parameter. 80760786Sps */ 80860786Sps while (nch > 1) 80960786Sps { 81060786Sps ungetcc(usercmd[--nch]); 81160786Sps } 81260786Sps } else 81360786Sps { 81460786Sps if (s != NULL) 81560786Sps ungetsc(s); 81660786Sps } 81760786Sps return action; 81860786Sps} 81960786Sps 820