"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/decode.c" of archive less-424.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Copyright (C) 1984-2008  Mark Nudelman
    3  *
    4  * You may distribute under the terms of either the GNU General Public
    5  * License or the Less License, as specified in the README file.
    6  *
    7  * For more information about less, or for information on how to
    8  * contact the author, see the README file.
    9  */
   10 
   11 
   12 /*
   13  * Routines to decode user commands.
   14  *
   15  * This is all table driven.
   16  * A command table is a sequence of command descriptors.
   17  * Each command descriptor is a sequence of bytes with the following format:
   18  *	<c1><c2>...<cN><0><action>
   19  * The characters c1,c2,...,cN are the command string; that is,
   20  * the characters which the user must type.
   21  * It is terminated by a null <0> byte.
   22  * The byte after the null byte is the action code associated
   23  * with the command string.
   24  * If an action byte is OR-ed with A_EXTRA, this indicates
   25  * that the option byte is followed by an extra string.
   26  *
   27  * There may be many command tables.
   28  * The first (default) table is built-in.
   29  * Other tables are read in from "lesskey" files.
   30  * All the tables are linked together and are searched in order.
   31  */
   32 
   33 #include "less.h"
   34 #include "cmd.h"
   35 #include "lesskey.h"
   36 
   37 extern int erase_char, erase2_char, kill_char;
   38 extern int secure;
   39 
   40 #define SK(k) \
   41 	SK_SPECIAL_KEY, (k), 6, 1, 1, 1
   42 /*
   43  * Command table is ordered roughly according to expected
   44  * frequency of use, so the common commands are near the beginning.
   45  */
   46 
   47 static unsigned char cmdtable[] =
   48 {
   49 	'\r',0,				A_F_LINE,
   50 	'\n',0,				A_F_LINE,
   51 	'e',0,				A_F_LINE,
   52 	'j',0,				A_F_LINE,
   53 	SK(SK_DOWN_ARROW),0,		A_F_LINE,
   54 	CONTROL('E'),0,			A_F_LINE,
   55 	CONTROL('N'),0,			A_F_LINE,
   56 	'k',0,				A_B_LINE,
   57 	'y',0,				A_B_LINE,
   58 	CONTROL('Y'),0,			A_B_LINE,
   59 	SK(SK_CONTROL_K),0,		A_B_LINE,
   60 	CONTROL('P'),0,			A_B_LINE,
   61 	SK(SK_UP_ARROW),0,		A_B_LINE,
   62 	'J',0,				A_FF_LINE,
   63 	'K',0,				A_BF_LINE,
   64 	'Y',0,				A_BF_LINE,
   65 	'd',0,				A_F_SCROLL,
   66 	CONTROL('D'),0,			A_F_SCROLL,
   67 	'u',0,				A_B_SCROLL,
   68 	CONTROL('U'),0,			A_B_SCROLL,
   69 	' ',0,				A_F_SCREEN,
   70 	'f',0,				A_F_SCREEN,
   71 	CONTROL('F'),0,			A_F_SCREEN,
   72 	CONTROL('V'),0,			A_F_SCREEN,
   73 	SK(SK_PAGE_DOWN),0,		A_F_SCREEN,
   74 	'b',0,				A_B_SCREEN,
   75 	CONTROL('B'),0,			A_B_SCREEN,
   76 	ESC,'v',0,			A_B_SCREEN,
   77 	SK(SK_PAGE_UP),0,		A_B_SCREEN,
   78 	'z',0,				A_F_WINDOW,
   79 	'w',0,				A_B_WINDOW,
   80 	ESC,' ',0,			A_FF_SCREEN,
   81 	'F',0,				A_F_FOREVER,
   82 	'R',0,				A_FREPAINT,
   83 	'r',0,				A_REPAINT,
   84 	CONTROL('R'),0,			A_REPAINT,
   85 	CONTROL('L'),0,			A_REPAINT,
   86 	ESC,'u',0,			A_UNDO_SEARCH,
   87 	'g',0,				A_GOLINE,
   88 	SK(SK_HOME),0,			A_GOLINE,
   89 	'<',0,				A_GOLINE,
   90 	ESC,'<',0,			A_GOLINE,
   91 	'p',0,				A_PERCENT,
   92 	'%',0,				A_PERCENT,
   93 	ESC,'[',0,			A_LSHIFT,
   94 	ESC,']',0,			A_RSHIFT,
   95 	ESC,'(',0,			A_LSHIFT,
   96 	ESC,')',0,			A_RSHIFT,
   97 	SK(SK_RIGHT_ARROW),0,		A_RSHIFT,
   98 	SK(SK_LEFT_ARROW),0,		A_LSHIFT,
   99 	'{',0,				A_F_BRACKET|A_EXTRA,	'{','}',0,
  100 	'}',0,				A_B_BRACKET|A_EXTRA,	'{','}',0,
  101 	'(',0,				A_F_BRACKET|A_EXTRA,	'(',')',0,
  102 	')',0,				A_B_BRACKET|A_EXTRA,	'(',')',0,
  103 	'[',0,				A_F_BRACKET|A_EXTRA,	'[',']',0,
  104 	']',0,				A_B_BRACKET|A_EXTRA,	'[',']',0,
  105 	ESC,CONTROL('F'),0,		A_F_BRACKET,
  106 	ESC,CONTROL('B'),0,		A_B_BRACKET,
  107 	'G',0,				A_GOEND,
  108 	ESC,'>',0,			A_GOEND,
  109 	'>',0,				A_GOEND,
  110 	SK(SK_END),0,			A_GOEND,
  111 	'P',0,				A_GOPOS,
  112 
  113 	'0',0,				A_DIGIT,
  114 	'1',0,				A_DIGIT,
  115 	'2',0,				A_DIGIT,
  116 	'3',0,				A_DIGIT,
  117 	'4',0,				A_DIGIT,
  118 	'5',0,				A_DIGIT,
  119 	'6',0,				A_DIGIT,
  120 	'7',0,				A_DIGIT,
  121 	'8',0,				A_DIGIT,
  122 	'9',0,				A_DIGIT,
  123 	'.',0,				A_DIGIT,
  124 
  125 	'=',0,				A_STAT,
  126 	CONTROL('G'),0,			A_STAT,
  127 	':','f',0,			A_STAT,
  128 	'/',0,				A_F_SEARCH,
  129 	'?',0,				A_B_SEARCH,
  130 	ESC,'/',0,			A_F_SEARCH|A_EXTRA,	'*',0,
  131 	ESC,'?',0,			A_B_SEARCH|A_EXTRA,	'*',0,
  132 	'n',0,				A_AGAIN_SEARCH,
  133 	ESC,'n',0,			A_T_AGAIN_SEARCH,
  134 	'N',0,				A_REVERSE_SEARCH,
  135 	ESC,'N',0,			A_T_REVERSE_SEARCH,
  136 	'&',0,				A_FILTER,
  137 	'm',0,				A_SETMARK,
  138 	'\'',0,				A_GOMARK,
  139 	CONTROL('X'),CONTROL('X'),0,	A_GOMARK,
  140 	'E',0,				A_EXAMINE,
  141 	':','e',0,			A_EXAMINE,
  142 	CONTROL('X'),CONTROL('V'),0,	A_EXAMINE,
  143 	':','n',0,			A_NEXT_FILE,
  144 	':','p',0,			A_PREV_FILE,
  145 	't',0,				A_NEXT_TAG,
  146 	'T',0,				A_PREV_TAG,
  147 	':','x',0,			A_INDEX_FILE,
  148 	':','d',0,			A_REMOVE_FILE,
  149 	'-',0,				A_OPT_TOGGLE,
  150 	':','t',0,			A_OPT_TOGGLE|A_EXTRA,	't',0,
  151 	's',0,				A_OPT_TOGGLE|A_EXTRA,	'o',0,
  152 	'_',0,				A_DISP_OPTION,
  153 	'|',0,				A_PIPE,
  154 	'v',0,				A_VISUAL,
  155 	'!',0,				A_SHELL,
  156 	'+',0,				A_FIRSTCMD,
  157 
  158 	'H',0,				A_HELP,
  159 	'h',0,				A_HELP,
  160 	SK(SK_F1),0,			A_HELP,
  161 	'V',0,				A_VERSION,
  162 	'q',0,				A_QUIT,
  163 	'Q',0,				A_QUIT,
  164 	':','q',0,			A_QUIT,
  165 	':','Q',0,			A_QUIT,
  166 	'Z','Z',0,			A_QUIT
  167 };
  168 
  169 static unsigned char edittable[] =
  170 {
  171 	'\t',0,	    			EC_F_COMPLETE,	/* TAB */
  172 	'\17',0,			EC_B_COMPLETE,	/* BACKTAB */
  173 	SK(SK_BACKTAB),0,		EC_B_COMPLETE,	/* BACKTAB */
  174 	ESC,'\t',0,			EC_B_COMPLETE,	/* ESC TAB */
  175 	CONTROL('L'),0,			EC_EXPAND,	/* CTRL-L */
  176 	CONTROL('V'),0,			EC_LITERAL,	/* BACKSLASH */
  177 	CONTROL('A'),0,			EC_LITERAL,	/* BACKSLASH */
  178    	ESC,'l',0,			EC_RIGHT,	/* ESC l */
  179 	SK(SK_RIGHT_ARROW),0,		EC_RIGHT,	/* RIGHTARROW */
  180 	ESC,'h',0,			EC_LEFT,	/* ESC h */
  181 	SK(SK_LEFT_ARROW),0,		EC_LEFT,	/* LEFTARROW */
  182 	ESC,'b',0,			EC_W_LEFT,	/* ESC b */
  183 	ESC,SK(SK_LEFT_ARROW),0,	EC_W_LEFT,	/* ESC LEFTARROW */
  184 	SK(SK_CTL_LEFT_ARROW),0,	EC_W_LEFT,	/* CTRL-LEFTARROW */
  185 	ESC,'w',0,			EC_W_RIGHT,	/* ESC w */
  186 	ESC,SK(SK_RIGHT_ARROW),0,	EC_W_RIGHT,	/* ESC RIGHTARROW */
  187 	SK(SK_CTL_RIGHT_ARROW),0,	EC_W_RIGHT,	/* CTRL-RIGHTARROW */
  188 	ESC,'i',0,			EC_INSERT,	/* ESC i */
  189 	SK(SK_INSERT),0,		EC_INSERT,	/* INSERT */
  190 	ESC,'x',0,			EC_DELETE,	/* ESC x */
  191 	SK(SK_DELETE),0,		EC_DELETE,	/* DELETE */
  192 	ESC,'X',0,			EC_W_DELETE,	/* ESC X */
  193 	ESC,SK(SK_DELETE),0,		EC_W_DELETE,	/* ESC DELETE */
  194 	SK(SK_CTL_DELETE),0,		EC_W_DELETE,	/* CTRL-DELETE */
  195 	SK(SK_CTL_BACKSPACE),0,		EC_W_BACKSPACE, /* CTRL-BACKSPACE */
  196 	ESC,'\b',0,			EC_W_BACKSPACE,	/* ESC BACKSPACE */
  197 	ESC,'0',0,			EC_HOME,	/* ESC 0 */
  198 	SK(SK_HOME),0,			EC_HOME,	/* HOME */
  199 	ESC,'$',0,			EC_END,		/* ESC $ */
  200 	SK(SK_END),0,			EC_END,		/* END */
  201 	ESC,'k',0,			EC_UP,		/* ESC k */
  202 	SK(SK_UP_ARROW),0,		EC_UP,		/* UPARROW */
  203 	ESC,'j',0,			EC_DOWN,	/* ESC j */
  204 	SK(SK_DOWN_ARROW),0,		EC_DOWN,	/* DOWNARROW */
  205 };
  206 
  207 /*
  208  * Structure to support a list of command tables.
  209  */
  210 struct tablelist
  211 {
  212 	struct tablelist *t_next;
  213 	char *t_start;
  214 	char *t_end;
  215 };
  216 
  217 /*
  218  * List of command tables and list of line-edit tables.
  219  */
  220 static struct tablelist *list_fcmd_tables = NULL;
  221 static struct tablelist *list_ecmd_tables = NULL;
  222 static struct tablelist *list_var_tables = NULL;
  223 static struct tablelist *list_sysvar_tables = NULL;
  224 
  225 
  226 /*
  227  * Expand special key abbreviations in a command table.
  228  */
  229 	static void
  230 expand_special_keys(table, len)
  231 	char *table;
  232 	int len;
  233 {
  234 	register char *fm;
  235 	register char *to;
  236 	register int a;
  237 	char *repl;
  238 	int klen;
  239 
  240 	for (fm = table;  fm < table + len; )
  241 	{
  242 		/*
  243 		 * Rewrite each command in the table with any
  244 		 * special key abbreviations expanded.
  245 		 */
  246 		for (to = fm;  *fm != '\0'; )
  247 		{
  248 			if (*fm != SK_SPECIAL_KEY)
  249 			{
  250 				*to++ = *fm++;
  251 				continue;
  252 			}
  253 			/*
  254 			 * After SK_SPECIAL_KEY, next byte is the type
  255 			 * of special key (one of the SK_* contants),
  256 			 * and the byte after that is the number of bytes,
  257 			 * N, reserved by the abbreviation (including the
  258 			 * SK_SPECIAL_KEY and key type bytes).
  259 			 * Replace all N bytes with the actual bytes
  260 			 * output by the special key on this terminal.
  261 			 */
  262 			repl = special_key_str(fm[1]);
  263 			klen = fm[2] & 0377;
  264 			fm += klen;
  265 			if (repl == NULL || (int) strlen(repl) > klen)
  266 				repl = "\377";
  267 			while (*repl != '\0')
  268 				*to++ = *repl++;
  269 		}
  270 		*to++ = '\0';
  271 		/*
  272 		 * Fill any unused bytes between end of command and
  273 		 * the action byte with A_SKIP.
  274 		 */
  275 		while (to <= fm)
  276 			*to++ = A_SKIP;
  277 		fm++;
  278 		a = *fm++ & 0377;
  279 		if (a & A_EXTRA)
  280 		{
  281 			while (*fm++ != '\0')
  282 				continue;
  283 		}
  284 	}
  285 }
  286 
  287 /*
  288  * Initialize the command lists.
  289  */
  290 	public void
  291 init_cmds()
  292 {
  293 	/*
  294 	 * Add the default command tables.
  295 	 */
  296 	add_fcmd_table((char*)cmdtable, sizeof(cmdtable));
  297 	add_ecmd_table((char*)edittable, sizeof(edittable));
  298 #if USERFILE
  299 	/*
  300 	 * For backwards compatibility,
  301 	 * try to add tables in the OLD system lesskey file.
  302 	 */
  303 #ifdef BINDIR
  304 	add_hometable(NULL, BINDIR "/.sysless", 1);
  305 #endif
  306 	/*
  307 	 * Try to add the tables in the system lesskey file.
  308 	 */
  309 	add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1);
  310 	/*
  311 	 * Try to add the tables in the standard lesskey file "$HOME/.less".
  312 	 */
  313 	add_hometable("LESSKEY", LESSKEYFILE, 0);
  314 #endif
  315 }
  316 
  317 /*
  318  * Add a command table.
  319  */
  320 	static int
  321 add_cmd_table(tlist, buf, len)
  322 	struct tablelist **tlist;
  323 	char *buf;
  324 	int len;
  325 {
  326 	register struct tablelist *t;
  327 
  328 	if (len == 0)
  329 		return (0);
  330 	/*
  331 	 * Allocate a tablelist structure, initialize it,
  332 	 * and link it into the list of tables.
  333 	 */
  334 	if ((t = (struct tablelist *)
  335 			calloc(1, sizeof(struct tablelist))) == NULL)
  336 	{
  337 		return (-1);
  338 	}
  339 	expand_special_keys(buf, len);
  340 	t->t_start = buf;
  341 	t->t_end = buf + len;
  342 	t->t_next = *tlist;
  343 	*tlist = t;
  344 	return (0);
  345 }
  346 
  347 /*
  348  * Add a command table.
  349  */
  350 	public void
  351 add_fcmd_table(buf, len)
  352 	char *buf;
  353 	int len;
  354 {
  355 	if (add_cmd_table(&list_fcmd_tables, buf, len) < 0)
  356 		error("Warning: some commands disabled", NULL_PARG);
  357 }
  358 
  359 /*
  360  * Add an editing command table.
  361  */
  362 	public void
  363 add_ecmd_table(buf, len)
  364 	char *buf;
  365 	int len;
  366 {
  367 	if (add_cmd_table(&list_ecmd_tables, buf, len) < 0)
  368 		error("Warning: some edit commands disabled", NULL_PARG);
  369 }
  370 
  371 /*
  372  * Add an environment variable table.
  373  */
  374 	static void
  375 add_var_table(tlist, buf, len)
  376 	struct tablelist **tlist;
  377 	char *buf;
  378 	int len;
  379 {
  380 	if (add_cmd_table(tlist, buf, len) < 0)
  381 		error("Warning: environment variables from lesskey file unavailable", NULL_PARG);
  382 }
  383 
  384 /*
  385  * Search a single command table for the command string in cmd.
  386  */
  387 	static int
  388 cmd_search(cmd, table, endtable, sp)
  389 	char *cmd;
  390 	char *table;
  391 	char *endtable;
  392 	char **sp;
  393 {
  394 	register char *p;
  395 	register char *q;
  396 	register int a;
  397 
  398 	*sp = NULL;
  399 	for (p = table, q = cmd;  p < endtable;  p++, q++)
  400 	{
  401 		if (*p == *q)
  402 		{
  403 			/*
  404 			 * Current characters match.
  405 			 * If we're at the end of the string, we've found it.
  406 			 * Return the action code, which is the character
  407 			 * after the null at the end of the string
  408 			 * in the command table.
  409 			 */
  410 			if (*p == '\0')
  411 			{
  412 				a = *++p & 0377;
  413 				while (a == A_SKIP)
  414 					a = *++p & 0377;
  415 				if (a == A_END_LIST)
  416 				{
  417 					/*
  418 					 * We get here only if the original
  419 					 * cmd string passed in was empty ("").
  420 					 * I don't think that can happen,
  421 					 * but just in case ...
  422 					 */
  423 					return (A_UINVALID);
  424 				}
  425 				/*
  426 				 * Check for an "extra" string.
  427 				 */
  428 				if (a & A_EXTRA)
  429 				{
  430 					*sp = ++p;
  431 					a &= ~A_EXTRA;
  432 				}
  433 				return (a);
  434 			}
  435 		} else if (*q == '\0')
  436 		{
  437 			/*
  438 			 * Hit the end of the user's command,
  439 			 * but not the end of the string in the command table.
  440 			 * The user's command is incomplete.
  441 			 */
  442 			return (A_PREFIX);
  443 		} else
  444 		{
  445 			/*
  446 			 * Not a match.
  447 			 * Skip ahead to the next command in the
  448 			 * command table, and reset the pointer
  449 			 * to the beginning of the user's command.
  450 			 */
  451 			if (*p == '\0' && p[1] == A_END_LIST)
  452 			{
  453 				/*
  454 				 * A_END_LIST is a special marker that tells
  455 				 * us to abort the cmd search.
  456 				 */
  457 				return (A_UINVALID);
  458 			}
  459 			while (*p++ != '\0')
  460 				continue;
  461 			while (*p == A_SKIP)
  462 				p++;
  463 			if (*p & A_EXTRA)
  464 				while (*++p != '\0')
  465 					continue;
  466 			q = cmd-1;
  467 		}
  468 	}
  469 	/*
  470 	 * No match found in the entire command table.
  471 	 */
  472 	return (A_INVALID);
  473 }
  474 
  475 /*
  476  * Decode a command character and return the associated action.
  477  * The "extra" string, if any, is returned in sp.
  478  */
  479 	static int
  480 cmd_decode(tlist, cmd, sp)
  481 	struct tablelist *tlist;
  482 	char *cmd;
  483 	char **sp;
  484 {
  485 	register struct tablelist *t;
  486 	register int action = A_INVALID;
  487 
  488 	/*
  489 	 * Search thru all the command tables.
  490 	 * Stop when we find an action which is not A_INVALID.
  491 	 */
  492 	for (t = tlist;  t != NULL;  t = t->t_next)
  493 	{
  494 		action = cmd_search(cmd, t->t_start, t->t_end, sp);
  495 		if (action != A_INVALID)
  496 			break;
  497 	}
  498 	if (action == A_UINVALID)
  499 		action = A_INVALID;
  500 	return (action);
  501 }
  502 
  503 /*
  504  * Decode a command from the cmdtables list.
  505  */
  506 	public int
  507 fcmd_decode(cmd, sp)
  508 	char *cmd;
  509 	char **sp;
  510 {
  511 	return (cmd_decode(list_fcmd_tables, cmd, sp));
  512 }
  513 
  514 /*
  515  * Decode a command from the edittables list.
  516  */
  517 	public int
  518 ecmd_decode(cmd, sp)
  519 	char *cmd;
  520 	char **sp;
  521 {
  522 	return (cmd_decode(list_ecmd_tables, cmd, sp));
  523 }
  524 
  525 /*
  526  * Get the value of an environment variable.
  527  * Looks first in the lesskey file, then in the real environment.
  528  */
  529 	public char *
  530 lgetenv(var)
  531 	char *var;
  532 {
  533 	int a;
  534 	char *s;
  535 
  536 	a = cmd_decode(list_var_tables, var, &s);
  537 	if (a == EV_OK)
  538 		return (s);
  539 	s = getenv(var);
  540 	if (s != NULL && *s != '\0')
  541 		return (s);
  542 	a = cmd_decode(list_sysvar_tables, var, &s);
  543 	if (a == EV_OK)
  544 		return (s);
  545 	return (NULL);
  546 }
  547 
  548 #if USERFILE
  549 /*
  550  * Get an "integer" from a lesskey file.
  551  * Integers are stored in a funny format:
  552  * two bytes, low order first, in radix KRADIX.
  553  */
  554 	static int
  555 gint(sp)
  556 	char **sp;
  557 {
  558 	int n;
  559 
  560 	n = *(*sp)++;
  561 	n += *(*sp)++ * KRADIX;
  562 	return (n);
  563 }
  564 
  565 /*
  566  * Process an old (pre-v241) lesskey file.
  567  */
  568 	static int
  569 old_lesskey(buf, len)
  570 	char *buf;
  571 	int len;
  572 {
  573 	/*
  574 	 * Old-style lesskey file.
  575 	 * The file must end with either
  576 	 *     ...,cmd,0,action
  577 	 * or  ...,cmd,0,action|A_EXTRA,string,0
  578 	 * So the last byte or the second to last byte must be zero.
  579 	 */
  580 	if (buf[len-1] != '\0' && buf[len-2] != '\0')
  581 		return (-1);
  582 	add_fcmd_table(buf, len);
  583 	return (0);
  584 }
  585 
  586 /*
  587  * Process a new (post-v241) lesskey file.
  588  */
  589 	static int
  590 new_lesskey(buf, len, sysvar)
  591 	char *buf;
  592 	int len;
  593 	int sysvar;
  594 {
  595 	char *p;
  596 	register int c;
  597 	register int n;
  598 
  599 	/*
  600 	 * New-style lesskey file.
  601 	 * Extract the pieces.
  602 	 */
  603 	if (buf[len-3] != C0_END_LESSKEY_MAGIC ||
  604 	    buf[len-2] != C1_END_LESSKEY_MAGIC ||
  605 	    buf[len-1] != C2_END_LESSKEY_MAGIC)
  606 		return (-1);
  607 	p = buf + 4;
  608 	for (;;)
  609 	{
  610 		c = *p++;
  611 		switch (c)
  612 		{
  613 		case CMD_SECTION:
  614 			n = gint(&p);
  615 			add_fcmd_table(p, n);
  616 			p += n;
  617 			break;
  618 		case EDIT_SECTION:
  619 			n = gint(&p);
  620 			add_ecmd_table(p, n);
  621 			p += n;
  622 			break;
  623 		case VAR_SECTION:
  624 			n = gint(&p);
  625 			add_var_table((sysvar) ?
  626 				&list_sysvar_tables : &list_var_tables, p, n);
  627 			p += n;
  628 			break;
  629 		case END_SECTION:
  630 			return (0);
  631 		default:
  632 			/*
  633 			 * Unrecognized section type.
  634 			 */
  635 			return (-1);
  636 		}
  637 	}
  638 }
  639 
  640 /*
  641  * Set up a user command table, based on a "lesskey" file.
  642  */
  643 	public int
  644 lesskey(filename, sysvar)
  645 	char *filename;
  646 	int sysvar;
  647 {
  648 	register char *buf;
  649 	register POSITION len;
  650 	register long n;
  651 	register int f;
  652 
  653 	if (secure)
  654 		return (1);
  655 	/*
  656 	 * Try to open the lesskey file.
  657 	 */
  658 	filename = shell_unquote(filename);
  659 	f = open(filename, OPEN_READ);
  660 	free(filename);
  661 	if (f < 0)
  662 		return (1);
  663 
  664 	/*
  665 	 * Read the file into a buffer.
  666 	 * We first figure out the size of the file and allocate space for it.
  667 	 * {{ Minimal error checking is done here.
  668 	 *    A garbage .less file will produce strange results.
  669 	 *    To avoid a large amount of error checking code here, we
  670 	 *    rely on the lesskey program to generate a good .less file. }}
  671 	 */
  672 	len = filesize(f);
  673 	if (len == NULL_POSITION || len < 3)
  674 	{
  675 		/*
  676 		 * Bad file (valid file must have at least 3 chars).
  677 		 */
  678 		close(f);
  679 		return (-1);
  680 	}
  681 	if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL)
  682 	{
  683 		close(f);
  684 		return (-1);
  685 	}
  686 	if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
  687 	{
  688 		free(buf);
  689 		close(f);
  690 		return (-1);
  691 	}
  692 	n = read(f, buf, (unsigned int) len);
  693 	close(f);
  694 	if (n != len)
  695 	{
  696 		free(buf);
  697 		return (-1);
  698 	}
  699 
  700 	/*
  701 	 * Figure out if this is an old-style (before version 241)
  702 	 * or new-style lesskey file format.
  703 	 */
  704 	if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC ||
  705 	    buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC)
  706 		return (old_lesskey(buf, (int)len));
  707 	return (new_lesskey(buf, (int)len, sysvar));
  708 }
  709 
  710 /*
  711  * Add the standard lesskey file "$HOME/.less"
  712  */
  713 	public void
  714 add_hometable(envname, def_filename, sysvar)
  715 	char *envname;
  716 	char *def_filename;
  717 	int sysvar;
  718 {
  719 	char *filename;
  720 	PARG parg;
  721 
  722 	if (envname != NULL && (filename = lgetenv(envname)) != NULL)
  723 		filename = save(filename);
  724 	else if (sysvar)
  725 		filename = save(def_filename);
  726 	else
  727 		filename = homefile(def_filename);
  728 	if (filename == NULL)
  729 		return;
  730 	if (lesskey(filename, sysvar) < 0)
  731 	{
  732 		parg.p_string = filename;
  733 		error("Cannot use lesskey file \"%s\"", &parg);
  734 	}
  735 	free(filename);
  736 }
  737 #endif
  738 
  739 /*
  740  * See if a char is a special line-editing command.
  741  */
  742 	public int
  743 editchar(c, flags)
  744 	int c;
  745 	int flags;
  746 {
  747 	int action;
  748 	int nch;
  749 	char *s;
  750 	char usercmd[MAX_CMDLEN+1];
  751 
  752 	/*
  753 	 * An editing character could actually be a sequence of characters;
  754 	 * for example, an escape sequence sent by pressing the uparrow key.
  755 	 * To match the editing string, we use the command decoder
  756 	 * but give it the edit-commands command table
  757 	 * This table is constructed to match the user's keyboard.
  758 	 */
  759 	if (c == erase_char || c == erase2_char)
  760 		return (EC_BACKSPACE);
  761 	if (c == kill_char)
  762 		return (EC_LINEKILL);
  763 
  764 	/*
  765 	 * Collect characters in a buffer.
  766 	 * Start with the one we have, and get more if we need them.
  767 	 */
  768 	nch = 0;
  769 	do {
  770 	  	if (nch > 0)
  771 			c = getcc();
  772 		usercmd[nch] = c;
  773 		usercmd[nch+1] = '\0';
  774 		nch++;
  775 		action = ecmd_decode(usercmd, &s);
  776 	} while (action == A_PREFIX);
  777 
  778 	if (flags & EC_NORIGHTLEFT)
  779 	{
  780 		switch (action)
  781 		{
  782 		case EC_RIGHT:
  783 		case EC_LEFT:
  784 			action = A_INVALID;
  785 			break;
  786 		}
  787 	}
  788 #if CMD_HISTORY
  789 	if (flags & EC_NOHISTORY)
  790 	{
  791 		/*
  792 		 * The caller says there is no history list.
  793 		 * Reject any history-manipulation action.
  794 		 */
  795 		switch (action)
  796 		{
  797 		case EC_UP:
  798 		case EC_DOWN:
  799 			action = A_INVALID;
  800 			break;
  801 		}
  802 	}
  803 #endif
  804 #if TAB_COMPLETE_FILENAME
  805 	if (flags & EC_NOCOMPLETE)
  806 	{
  807 		/*
  808 		 * The caller says we don't want any filename completion cmds.
  809 		 * Reject them.
  810 		 */
  811 		switch (action)
  812 		{
  813 		case EC_F_COMPLETE:
  814 		case EC_B_COMPLETE:
  815 		case EC_EXPAND:
  816 			action = A_INVALID;
  817 			break;
  818 		}
  819 	}
  820 #endif
  821 	if ((flags & EC_PEEK) || action == A_INVALID)
  822 	{
  823 		/*
  824 		 * We're just peeking, or we didn't understand the command.
  825 		 * Unget all the characters we read in the loop above.
  826 		 * This does NOT include the original character that was
  827 		 * passed in as a parameter.
  828 		 */
  829 		while (nch > 1)
  830 		{
  831 			ungetcc(usercmd[--nch]);
  832 		}
  833 	} else
  834 	{
  835 		if (s != NULL)
  836 			ungetsc(s);
  837 	}
  838 	return action;
  839 }
  840