GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/less/less/../decode.c Lines: 125 199 62.8 %
Date: 2017-11-07 Branches: 85 149 57.0 %

Line Branch Exec Source
1
/*
2
 * Copyright (C) 1984-2012  Mark Nudelman
3
 * Modified for use with illumos by Garrett D'Amore.
4
 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
5
 *
6
 * You may distribute under the terms of either the GNU General Public
7
 * License or the Less License, as specified in the README file.
8
 *
9
 * For more information, see the README file.
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 "cmd.h"
34
#include "less.h"
35
#include "lesskey.h"
36
37
extern int erase_char, erase2_char, kill_char;
38
extern int secure, less_is_more;
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
	ESC, 'F', 0,			A_F_UNTIL_HILITE,
83
	'R', 0,				A_FREPAINT,
84
	'r', 0,				A_REPAINT,
85
	CONTROL('R'), 0,		A_REPAINT,
86
	CONTROL('L'), 0,		A_REPAINT,
87
	ESC, 'u', 0,			A_UNDO_SEARCH,
88
	'g', 0,				A_GOLINE,
89
	SK(SK_HOME), 0,			A_GOLINE,
90
	'<', 0,				A_GOLINE,
91
	ESC, '<', 0,			A_GOLINE,
92
	'p', 0,				A_PERCENT,
93
	'%', 0,				A_PERCENT,
94
	ESC, '[', 0,			A_LSHIFT,
95
	ESC, ']', 0,			A_RSHIFT,
96
	ESC, '(', 0,			A_LSHIFT,
97
	ESC, ')', 0,			A_RSHIFT,
98
	SK(SK_RIGHT_ARROW), 0,		A_RSHIFT,
99
	SK(SK_LEFT_ARROW), 0,		A_LSHIFT,
100
	'{', 0,				A_F_BRACKET|A_EXTRA,	'{', '}', 0,
101
	'}', 0,				A_B_BRACKET|A_EXTRA,	'{', '}', 0,
102
	'(', 0,				A_F_BRACKET|A_EXTRA,	'(', ')', 0,
103
	')', 0,				A_B_BRACKET|A_EXTRA,	'(', ')', 0,
104
	'[', 0,				A_F_BRACKET|A_EXTRA,	'[', ']', 0,
105
	']', 0,				A_B_BRACKET|A_EXTRA,	'[', ']', 0,
106
	ESC, CONTROL('F'), 0,		A_F_BRACKET,
107
	ESC, CONTROL('B'), 0,		A_B_BRACKET,
108
	'G', 0,				A_GOEND,
109
	ESC, '>', 0,			A_GOEND,
110
	'>', 0,				A_GOEND,
111
	SK(SK_END), 0,			A_GOEND,
112
	'P', 0,				A_GOPOS,
113
114
	'0', 0,				A_DIGIT,
115
	'1', 0,				A_DIGIT,
116
	'2', 0,				A_DIGIT,
117
	'3', 0,				A_DIGIT,
118
	'4', 0,				A_DIGIT,
119
	'5', 0,				A_DIGIT,
120
	'6', 0,				A_DIGIT,
121
	'7', 0,				A_DIGIT,
122
	'8', 0,				A_DIGIT,
123
	'9', 0,				A_DIGIT,
124
	'.', 0,				A_DIGIT,
125
126
	'=', 0,				A_STAT,
127
	CONTROL('G'), 0,		A_STAT,
128
	':', 'f', 0,			A_STAT,
129
	'/', 0,				A_F_SEARCH,
130
	'?', 0,				A_B_SEARCH,
131
	ESC, '/', 0,			A_F_SEARCH|A_EXTRA,	'*', 0,
132
	ESC, '?', 0,			A_B_SEARCH|A_EXTRA,	'*', 0,
133
	'n', 0,				A_AGAIN_SEARCH,
134
	ESC, 'n', 0,			A_T_AGAIN_SEARCH,
135
	'N', 0,				A_REVERSE_SEARCH,
136
	ESC, 'N', 0,			A_T_REVERSE_SEARCH,
137
	'&', 0,				A_FILTER,
138
	'm', 0,				A_SETMARK,
139
	'\'', 0,			A_GOMARK,
140
	CONTROL('X'), CONTROL('X'), 0,	A_GOMARK,
141
	'E', 0,				A_EXAMINE,
142
	':', 'e', 0,			A_EXAMINE,
143
	CONTROL('X'), CONTROL('V'), 0,	A_EXAMINE,
144
	':', 'n', 0,			A_NEXT_FILE,
145
	':', 'p', 0,			A_PREV_FILE,
146
	't', 0,				A_NEXT_TAG,
147
	'T', 0,				A_PREV_TAG,
148
	':', 'x', 0,			A_INDEX_FILE,
149
	':', 'd', 0,			A_REMOVE_FILE,
150
	':', 't', 0,			A_OPT_TOGGLE|A_EXTRA,	't', 0,
151
	'|', 0,				A_PIPE,
152
	'v', 0,				A_VISUAL,
153
	'+', 0,				A_FIRSTCMD,
154
155
	'H', 0,				A_HELP,
156
	'h', 0,				A_HELP,
157
	SK(SK_F1), 0,			A_HELP,
158
	'V', 0,				A_VERSION,
159
	'q', 0,				A_QUIT,
160
	'Q', 0,				A_QUIT,
161
	':', 'q', 0,			A_QUIT,
162
	':', 'Q', 0,			A_QUIT,
163
	'Z', 'Z', 0,			A_QUIT
164
};
165
166
static unsigned char lesstable[] = {
167
	'-', 0,				A_OPT_TOGGLE,
168
	's', 0,				A_OPT_TOGGLE|A_EXTRA,	'o', 0,
169
	'_', 0,				A_DISP_OPTION
170
};
171
172
static unsigned char moretable[] = {
173
	's', 0,				A_F_SKIP
174
};
175
176
static unsigned char edittable[] =
177
{
178
	'\t', 0,			EC_F_COMPLETE,	/* TAB */
179
	'\17', 0,			EC_B_COMPLETE,	/* BACKTAB */
180
	SK(SK_BACKTAB), 0,		EC_B_COMPLETE,	/* BACKTAB */
181
	ESC, '\t', 0,			EC_B_COMPLETE,	/* ESC TAB */
182
	CONTROL('L'), 0,		EC_EXPAND,	/* CTRL-L */
183
	CONTROL('V'), 0,		EC_LITERAL,	/* BACKSLASH */
184
	CONTROL('A'), 0,		EC_LITERAL,	/* BACKSLASH */
185
	ESC, 'l', 0,			EC_RIGHT,	/* ESC l */
186
	SK(SK_RIGHT_ARROW), 0,		EC_RIGHT,	/* RIGHTARROW */
187
	ESC, 'h', 0,			EC_LEFT,	/* ESC h */
188
	SK(SK_LEFT_ARROW), 0,		EC_LEFT,	/* LEFTARROW */
189
	ESC, 'b', 0,			EC_W_LEFT,	/* ESC b */
190
	ESC, SK(SK_LEFT_ARROW), 0,	EC_W_LEFT,	/* ESC LEFTARROW */
191
	SK(SK_CTL_LEFT_ARROW), 0,	EC_W_LEFT,	/* CTRL-LEFTARROW */
192
	ESC, 'w', 0,			EC_W_RIGHT,	/* ESC w */
193
	ESC, SK(SK_RIGHT_ARROW), 0,	EC_W_RIGHT,	/* ESC RIGHTARROW */
194
	SK(SK_CTL_RIGHT_ARROW), 0,	EC_W_RIGHT,	/* CTRL-RIGHTARROW */
195
	ESC, 'i', 0,			EC_INSERT,	/* ESC i */
196
	SK(SK_INSERT), 0,		EC_INSERT,	/* INSERT */
197
	ESC, 'x', 0,			EC_DELETE,	/* ESC x */
198
	SK(SK_DELETE), 0,		EC_DELETE,	/* DELETE */
199
	ESC, 'X', 0,			EC_W_DELETE,	/* ESC X */
200
	ESC, SK(SK_DELETE), 0,		EC_W_DELETE,	/* ESC DELETE */
201
	SK(SK_CTL_DELETE), 0,		EC_W_DELETE,	/* CTRL-DELETE */
202
	SK(SK_CTL_BACKSPACE), 0,	EC_W_BACKSPACE, /* CTRL-BACKSPACE */
203
	ESC, '\b', 0,			EC_W_BACKSPACE,	/* ESC BACKSPACE */
204
	ESC, '0', 0,			EC_HOME,	/* ESC 0 */
205
	SK(SK_HOME), 0,			EC_HOME,	/* HOME */
206
	ESC, '$', 0,			EC_END,		/* ESC $ */
207
	SK(SK_END), 0,			EC_END,		/* END */
208
	ESC, 'k', 0,			EC_UP,		/* ESC k */
209
	SK(SK_UP_ARROW), 0,		EC_UP,		/* UPARROW */
210
	ESC, 'j', 0,			EC_DOWN,	/* ESC j */
211
	SK(SK_DOWN_ARROW), 0,		EC_DOWN,	/* DOWNARROW */
212
	CONTROL('G'), 0,		EC_ABORT,	/* CTRL-G */
213
};
214
215
/*
216
 * Structure to support a list of command tables.
217
 */
218
struct tablelist {
219
	struct tablelist *t_next;
220
	char *t_start;
221
	char *t_end;
222
};
223
224
/*
225
 * List of command tables and list of line-edit tables.
226
 */
227
static struct tablelist *list_fcmd_tables = NULL;
228
static struct tablelist *list_ecmd_tables = NULL;
229
static struct tablelist *list_var_tables = NULL;
230
static struct tablelist *list_sysvar_tables = NULL;
231
232
233
/*
234
 * Expand special key abbreviations in a command table.
235
 */
236
static void
237
expand_special_keys(char *table, int len)
238
{
239
	char *fm;
240
	char *to;
241
	int a;
242
	char *repl;
243
	int klen;
244
245
6191
	for (fm = table; fm < table + len; ) {
246
		/*
247
		 * Rewrite each command in the table with any
248
		 * special key abbreviations expanded.
249
		 */
250
19236
		for (to = fm; *fm != '\0'; ) {
251
7556
			if (*fm != SK_SPECIAL_KEY) {
252
6542
				*to++ = *fm++;
253
6542
				continue;
254
			}
255
			/*
256
			 * After SK_SPECIAL_KEY, next byte is the type
257
			 * of special key (one of the SK_* contants),
258
			 * and the byte after that is the number of bytes,
259
			 * N, reserved by the abbreviation (including the
260
			 * SK_SPECIAL_KEY and key type bytes).
261
			 * Replace all N bytes with the actual bytes
262
			 * output by the special key on this terminal.
263
			 */
264
1014
			repl = special_key_str(fm[1]);
265
1014
			klen = fm[2] & 0377;
266
1014
			fm += klen;
267

1755
			if (repl == NULL || strlen(repl) > klen)
268
273
				repl = "\377";
269
6474
			while (*repl != '\0')
270
2730
				*to++ = *repl++;
271
		}
272
5840
		*to++ = '\0';
273
		/*
274
		 * Fill any unused bytes between end of command and
275
		 * the action byte with A_SKIP.
276
		 */
277
18388
		while (to <= fm)
278
3354
			*to++ = A_SKIP;
279
5840
		fm++;
280
5840
		a = *fm++ & 0377;
281
5840
		if (a & A_EXTRA) {
282
1004
			while (*fm++ != '\0')
283
				continue;
284
		}
285
	}
286
117
}
287
288
/*
289
 * Initialize the command lists.
290
 */
291
void
292
init_cmds(void)
293
{
294
	/*
295
	 * Add the default command tables.
296
	 */
297
78
	add_fcmd_table((char *)cmdtable, sizeof (cmdtable));
298
39
	add_ecmd_table((char *)edittable, sizeof (edittable));
299
39
	if (less_is_more) {
300
5
		add_fcmd_table((char *)moretable, sizeof (moretable));
301
5
		return;
302
	} else {
303
34
		add_fcmd_table((char *)lesstable, sizeof (lesstable));
304
	}
305
306
	/*
307
	 * Try to add the tables in the system lesskey file.
308
	 */
309
34
	add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1);
310
	/*
311
	 * Try to add the tables in the standard lesskey file "$HOME/.less".
312
	 */
313
34
	add_hometable("LESSKEY", LESSKEYFILE, 0);
314
73
}
315
316
/*
317
 * Add a command table.
318
 */
319
static int
320
add_cmd_table(struct tablelist **tlist, char *buf, int len)
321
{
322
	struct tablelist *t;
323
324
234
	if (len == 0)
325
		return (0);
326
	/*
327
	 * Allocate a tablelist structure, initialize it,
328
	 * and link it into the list of tables.
329
	 */
330
117
	if ((t = calloc(1, sizeof (struct tablelist))) == NULL) {
331
		return (-1);
332
	}
333
117
	expand_special_keys(buf, len);
334
117
	t->t_start = buf;
335
117
	t->t_end = buf + len;
336
117
	t->t_next = *tlist;
337
117
	*tlist = t;
338
117
	return (0);
339
117
}
340
341
/*
342
 * Add a command table.
343
 */
344
void
345
add_fcmd_table(char *buf, int len)
346
{
347
156
	if (add_cmd_table(&list_fcmd_tables, buf, len) < 0)
348
		error("Warning: some commands disabled", NULL);
349
78
}
350
351
/*
352
 * Add an editing command table.
353
 */
354
void
355
add_ecmd_table(char *buf, int len)
356
{
357
78
	if (add_cmd_table(&list_ecmd_tables, buf, len) < 0)
358
		error("Warning: some edit commands disabled", NULL);
359
39
}
360
361
/*
362
 * Add an environment variable table.
363
 */
364
static void
365
add_var_table(struct tablelist **tlist, char *buf, int len)
366
{
367
	if (add_cmd_table(tlist, buf, len) < 0)
368
		error("Warning: environment variables from "
369
		    "lesskey file unavailable", NULL);
370
}
371
372
/*
373
 * Search a single command table for the command string in cmd.
374
 */
375
static int
376
cmd_search(const char *cmd, char *table, char *endtable, char **sp)
377
{
378
	char *p;
379
	const char *q;
380
	int a;
381
382
13574
	*sp = NULL;
383
191886
	for (p = table, q = cmd; p < endtable; p++, q++) {
384
92540
		if (*p == *q) {
385
			/*
386
			 * Current characters match.
387
			 * If we're at the end of the string, we've found it.
388
			 * Return the action code, which is the character
389
			 * after the null at the end of the string
390
			 * in the command table.
391
			 */
392
14386
			if (*p == '\0') {
393
917
				a = *++p & 0377;
394
5440
				while (a == A_SKIP)
395
1803
					a = *++p & 0377;
396
917
				if (a == A_END_LIST) {
397
					/*
398
					 * We get here only if the original
399
					 * cmd string passed in was empty ("").
400
					 * I don't think that can happen,
401
					 * but just in case ...
402
					 */
403
					return (A_UINVALID);
404
				}
405
				/*
406
				 * Check for an "extra" string.
407
				 */
408
917
				if (a & A_EXTRA) {
409
1
					*sp = ++p;
410
1
					a &= ~A_EXTRA;
411
1
				}
412
917
				return (a);
413
			}
414
78154
		} else if (*q == '\0') {
415
			/*
416
			 * Hit the end of the user's command,
417
			 * but not the end of the string in the command table.
418
			 * The user's command is incomplete.
419
			 */
420
2467
			return (A_PREFIX);
421
		} else {
422
			/*
423
			 * Not a match.
424
			 * Skip ahead to the next command in the
425
			 * command table, and reset the pointer
426
			 * to the beginning of the user's command.
427
			 */
428

75687
			if (*p == '\0' && p[1] == A_END_LIST) {
429
				/*
430
				 * A_END_LIST is a special marker that tells
431
				 * us to abort the cmd search.
432
				 */
433
				return (A_UINVALID);
434
			}
435
159272
			while (*p++ != '\0')
436
				continue;
437
132579
			while (*p == A_SKIP)
438
28446
				p++;
439
75687
			if (*p & A_EXTRA)
440
7546
				while (*++p != '\0')
441
					continue;
442
75687
			q = cmd-1;
443
		}
444
	}
445
	/*
446
	 * No match found in the entire command table.
447
	 */
448
3403
	return (A_INVALID);
449
6787
}
450
451
/*
452
 * Decode a command character and return the associated action.
453
 * The "extra" string, if any, is returned in sp.
454
 */
455
static int
456
cmd_decode(struct tablelist *tlist, const char *cmd, char **sp)
457
{
458
	struct tablelist *t;
459
	int action = A_INVALID;
460
461
	/*
462
	 * Search thru all the command tables.
463
	 * Stop when we find an action which is not A_INVALID.
464
	 */
465
21239
	for (t = tlist; t != NULL; t = t->t_next) {
466
6787
		action = cmd_search(cmd, t->t_start, t->t_end, sp);
467
6787
		if (action != A_INVALID)
468
			break;
469
	}
470
4811
	if (action == A_UINVALID)
471
		action = A_INVALID;
472
4811
	return (action);
473
}
474
475
/*
476
 * Decode a command from the cmdtables list.
477
 */
478
int
479
fcmd_decode(const char *cmd, char **sp)
480
{
481
6766
	return (cmd_decode(list_fcmd_tables, cmd, sp));
482
}
483
484
/*
485
 * Decode a command from the edittables list.
486
 */
487
int
488
ecmd_decode(const char *cmd, char **sp)
489
{
490
44
	return (cmd_decode(list_ecmd_tables, cmd, sp));
491
}
492
493
/*
494
 * Get the value of an environment variable.
495
 * Looks first in the lesskey file, then in the real environment.
496
 */
497
char *
498
lgetenv(char *var)
499
{
500
	int a;
501
1638
	char *s;
502
503
	/*
504
	 * Ignore lookups of any LESS* setting when we are more, and ignore
505
	 * the less key files
506
	 */
507
819
	if (less_is_more) {
508
82
		if (strncmp(var, "LESS", 4) == 0) {
509
62
			return (NULL);
510
		}
511
20
		return (getenv(var));
512
	}
513
737
	a = cmd_decode(list_var_tables, var, &s);
514
737
	if (a == EV_OK)
515
		return (s);
516
737
	s = getenv(var);
517

805
	if (s != NULL && *s != '\0')
518
68
		return (s);
519
669
	a = cmd_decode(list_sysvar_tables, var, &s);
520
669
	if (a == EV_OK)
521
		return (s);
522
669
	return (NULL);
523
819
}
524
525
/*
526
 * Get an "integer" from a lesskey file.
527
 * Integers are stored in a funny format:
528
 * two bytes, low order first, in radix KRADIX.
529
 */
530
static int
531
gint(char **sp)
532
{
533
	int n;
534
535
	n = *(*sp)++;
536
	n += *(*sp)++ * KRADIX;
537
	return (n);
538
}
539
540
/*
541
 * Process an old (pre-v241) lesskey file.
542
 */
543
static int
544
old_lesskey(char *buf, int len)
545
{
546
	/*
547
	 * Old-style lesskey file.
548
	 * The file must end with either
549
	 *	..,cmd,0,action
550
	 * or	...,cmd,0,action|A_EXTRA,string,0
551
	 * So the last byte or the second to last byte must be zero.
552
	 */
553
	if (buf[len-1] != '\0' && buf[len-2] != '\0')
554
		return (-1);
555
	add_fcmd_table(buf, len);
556
	return (0);
557
}
558
559
/*
560
 * Process a new (post-v241) lesskey file.
561
 */
562
static int
563
new_lesskey(char *buf, int len, int sysvar)
564
{
565
	char *p;
566
	int c;
567
	int n;
568
569
	/*
570
	 * New-style lesskey file.
571
	 * Extract the pieces.
572
	 */
573
	if (buf[len-3] != C0_END_LESSKEY_MAGIC ||
574
	    buf[len-2] != C1_END_LESSKEY_MAGIC ||
575
	    buf[len-1] != C2_END_LESSKEY_MAGIC)
576
		return (-1);
577
	p = buf + 4;
578
	for (;;) {
579
		c = *p++;
580
		switch (c) {
581
		case CMD_SECTION:
582
			n = gint(&p);
583
			add_fcmd_table(p, n);
584
			p += n;
585
			break;
586
		case EDIT_SECTION:
587
			n = gint(&p);
588
			add_ecmd_table(p, n);
589
			p += n;
590
			break;
591
		case VAR_SECTION:
592
			n = gint(&p);
593
			add_var_table((sysvar) ?
594
			    &list_sysvar_tables : &list_var_tables, p, n);
595
			p += n;
596
			break;
597
		case END_SECTION:
598
			return (0);
599
		default:
600
			/*
601
			 * Unrecognized section type.
602
			 */
603
			return (-1);
604
		}
605
	}
606
}
607
608
/*
609
 * Set up a user command table, based on a "lesskey" file.
610
 */
611
int
612
lesskey(char *filename, int sysvar)
613
{
614
	char *buf;
615
	off_t len;
616
	long n;
617
	int f;
618
619
68
	if (secure)
620
		return (1);
621
	/*
622
	 * Try to open the lesskey file.
623
	 */
624
34
	filename = shell_unquote(filename);
625
34
	f = open(filename, O_RDONLY);
626
34
	free(filename);
627
34
	if (f < 0)
628
34
		return (1);
629
630
	/*
631
	 * Read the file into a buffer.
632
	 * We first figure out the size of the file and allocate space for it.
633
	 * {{ Minimal error checking is done here.
634
	 *    A garbage .less file will produce strange results.
635
	 *    To avoid a large amount of error checking code here, we
636
	 *    rely on the lesskey program to generate a good .less file. }}
637
	 */
638
	len = filesize(f);
639
	if (len == -1 || len < 3) {
640
		/*
641
		 * Bad file (valid file must have at least 3 chars).
642
		 */
643
		(void) close(f);
644
		return (-1);
645
	}
646
	if ((buf = calloc((int)len, sizeof (char))) == NULL) {
647
		(void) close(f);
648
		return (-1);
649
	}
650
	if (lseek(f, (off_t)0, SEEK_SET) == (off_t)-1) {
651
		free(buf);
652
		(void) close(f);
653
		return (-1);
654
	}
655
	n = read(f, buf, (unsigned int) len);
656
	close(f);
657
	if (n != len) {
658
		free(buf);
659
		return (-1);
660
	}
661
662
	/*
663
	 * Figure out if this is an old-style (before version 241)
664
	 * or new-style lesskey file format.
665
	 */
666
	if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC ||
667
	    buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC)
668
		return (old_lesskey(buf, (int)len));
669
	return (new_lesskey(buf, (int)len, sysvar));
670
34
}
671
672
/*
673
 * Add the standard lesskey file "$HOME/.less"
674
 */
675
void
676
add_hometable(char *envname, char *def_filename, int sysvar)
677
{
678
	char *filename;
679
136
	PARG parg;
680
681

136
	if (envname != NULL && (filename = lgetenv(envname)) != NULL)
682
		filename = estrdup(filename);
683
68
	else if (sysvar)
684
34
		filename = estrdup(def_filename);
685
	else
686
34
		filename = homefile(def_filename);
687
68
	if (filename == NULL)
688
34
		return;
689
34
	if (lesskey(filename, sysvar) < 0) {
690
		parg.p_string = filename;
691
		error("Cannot use lesskey file \"%s\"", &parg);
692
	}
693
34
	free(filename);
694
102
}
695
696
/*
697
 * See if a char is a special line-editing command.
698
 */
699
int
700
editchar(int c, int flags)
701
{
702
	int action;
703
	int nch;
704
50
	char *s;
705
25
	char usercmd[MAX_CMDLEN+1];
706
707
	/*
708
	 * An editing character could actually be a sequence of characters;
709
	 * for example, an escape sequence sent by pressing the uparrow key.
710
	 * To match the editing string, we use the command decoder
711
	 * but give it the edit-commands command table
712
	 * This table is constructed to match the user's keyboard.
713
	 */
714

47
	if (c == erase_char || c == erase2_char)
715
3
		return (EC_BACKSPACE);
716
22
	if (c == kill_char)
717
		return (EC_LINEKILL);
718
719
	/*
720
	 * Collect characters in a buffer.
721
	 * Start with the one we have, and get more if we need them.
722
	 */
723
	nch = 0;
724
22
	do {
725
22
		if (nch > 0)
726
			c = getcc();
727
22
		usercmd[nch] = (char)c;
728
22
		usercmd[nch+1] = '\0';
729
		nch++;
730
22
		action = ecmd_decode(usercmd, &s);
731
22
	} while (action == A_PREFIX);
732
733
22
	if (flags & EC_NORIGHTLEFT) {
734
		switch (action) {
735
		case EC_RIGHT:
736
		case EC_LEFT:
737
			action = A_INVALID;
738
			break;
739
		}
740
	}
741
22
	if (flags & EC_NOHISTORY) {
742
		/*
743
		 * The caller says there is no history list.
744
		 * Reject any history-manipulation action.
745
		 */
746
2
		switch (action) {
747
		case EC_UP:
748
		case EC_DOWN:
749
			action = A_INVALID;
750
			break;
751
		}
752
	}
753
22
	if (flags & EC_NOCOMPLETE) {
754
		/*
755
		 * The caller says we don't want any filename completion cmds.
756
		 * Reject them.
757
		 */
758

20
		switch (action) {
759
		case EC_F_COMPLETE:
760
		case EC_B_COMPLETE:
761
		case EC_EXPAND:
762
			action = A_INVALID;
763
			break;
764
		}
765
	}
766
22
	if ((flags & EC_PEEK) || action == A_INVALID) {
767
		/*
768
		 * We're just peeking, or we didn't understand the command.
769
		 * Unget all the characters we read in the loop above.
770
		 * This does NOT include the original character that was
771
		 * passed in as a parameter.
772
		 */
773
21
		while (nch > 1) {
774
			ungetcc(usercmd[--nch]);
775
		}
776
	} else {
777
1
		if (s != NULL)
778
			ungetsc(s);
779
	}
780
22
	return (action);
781
25
}