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