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