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