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 |
✓✓ |
2472 |
for (fm = table; fm < table + len; ) { |
246 |
|
|
/* |
247 |
|
|
* Rewrite each command in the table with any |
248 |
|
|
* special key abbreviations expanded. |
249 |
|
|
*/ |
250 |
✓✓ |
4160 |
for (to = fm; *fm != '\0'; ) { |
251 |
✓✓ |
1552 |
if (*fm != SK_SPECIAL_KEY) { |
252 |
|
1344 |
*to++ = *fm++; |
253 |
|
1344 |
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 |
|
208 |
repl = special_key_str(fm[1]); |
265 |
|
208 |
klen = fm[2] & 0377; |
266 |
|
208 |
fm += klen; |
267 |
✓✓✗✓
|
360 |
if (repl == NULL || strlen(repl) > klen) |
268 |
|
56 |
repl = "\377"; |
269 |
✓✓ |
1536 |
while (*repl != '\0') |
270 |
|
560 |
*to++ = *repl++; |
271 |
|
|
} |
272 |
|
1200 |
*to++ = '\0'; |
273 |
|
|
/* |
274 |
|
|
* Fill any unused bytes between end of command and |
275 |
|
|
* the action byte with A_SKIP. |
276 |
|
|
*/ |
277 |
✓✓ |
3776 |
while (to <= fm) |
278 |
|
688 |
*to++ = A_SKIP; |
279 |
|
1200 |
fm++; |
280 |
|
1200 |
a = *fm++ & 0377; |
281 |
✓✓ |
1200 |
if (a & A_EXTRA) { |
282 |
✓✓ |
288 |
while (*fm++ != '\0') |
283 |
|
128 |
continue; |
284 |
|
|
} |
285 |
|
|
} |
286 |
|
24 |
} |
287 |
|
|
|
288 |
|
|
/* |
289 |
|
|
* Initialize the command lists. |
290 |
|
|
*/ |
291 |
|
|
void |
292 |
|
|
init_cmds(void) |
293 |
|
|
{ |
294 |
|
|
/* |
295 |
|
|
* Add the default command tables. |
296 |
|
|
*/ |
297 |
|
16 |
add_fcmd_table((char *)cmdtable, sizeof (cmdtable)); |
298 |
|
8 |
add_ecmd_table((char *)edittable, sizeof (edittable)); |
299 |
✗✓ |
8 |
if (less_is_more) { |
300 |
|
|
add_fcmd_table((char *)moretable, sizeof (moretable)); |
301 |
|
|
return; |
302 |
|
|
} else { |
303 |
|
8 |
add_fcmd_table((char *)lesstable, sizeof (lesstable)); |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
/* |
307 |
|
|
* Try to add the tables in the system lesskey file. |
308 |
|
|
*/ |
309 |
|
8 |
add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1); |
310 |
|
|
/* |
311 |
|
|
* Try to add the tables in the standard lesskey file "$HOME/.less". |
312 |
|
|
*/ |
313 |
|
8 |
add_hometable("LESSKEY", LESSKEYFILE, 0); |
314 |
|
16 |
} |
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 |
✗✓ |
48 |
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 |
✗✓ |
24 |
if ((t = calloc(1, sizeof (struct tablelist))) == NULL) { |
331 |
|
|
return (-1); |
332 |
|
|
} |
333 |
|
24 |
expand_special_keys(buf, len); |
334 |
|
24 |
t->t_start = buf; |
335 |
|
24 |
t->t_end = buf + len; |
336 |
|
24 |
t->t_next = *tlist; |
337 |
|
24 |
*tlist = t; |
338 |
|
24 |
return (0); |
339 |
|
24 |
} |
340 |
|
|
|
341 |
|
|
/* |
342 |
|
|
* Add a command table. |
343 |
|
|
*/ |
344 |
|
|
void |
345 |
|
|
add_fcmd_table(char *buf, int len) |
346 |
|
|
{ |
347 |
✗✓ |
32 |
if (add_cmd_table(&list_fcmd_tables, buf, len) < 0) |
348 |
|
|
error("Warning: some commands disabled", NULL); |
349 |
|
16 |
} |
350 |
|
|
|
351 |
|
|
/* |
352 |
|
|
* Add an editing command table. |
353 |
|
|
*/ |
354 |
|
|
void |
355 |
|
|
add_ecmd_table(char *buf, int len) |
356 |
|
|
{ |
357 |
✗✓ |
16 |
if (add_cmd_table(&list_ecmd_tables, buf, len) < 0) |
358 |
|
|
error("Warning: some edit commands disabled", NULL); |
359 |
|
8 |
} |
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 |
|
584 |
*sp = NULL; |
383 |
✓✓ |
10800 |
for (p = table, q = cmd; p < endtable; p++, q++) { |
384 |
✓✓ |
5248 |
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 |
✓✓ |
628 |
if (*p == '\0') { |
393 |
|
44 |
a = *++p & 0377; |
394 |
✓✓ |
216 |
while (a == A_SKIP) |
395 |
|
64 |
a = *++p & 0377; |
396 |
✗✓ |
44 |
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 |
✗✓ |
44 |
if (a & A_EXTRA) { |
409 |
|
|
*sp = ++p; |
410 |
|
|
a &= ~A_EXTRA; |
411 |
|
|
} |
412 |
|
44 |
return (a); |
413 |
|
|
} |
414 |
✓✓ |
4620 |
} 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 |
|
96 |
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 |
✗✓✗✗
|
4524 |
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 |
✓✓ |
14758 |
while (*p++ != '\0') |
436 |
|
5710 |
continue; |
437 |
✓✓ |
13264 |
while (*p == A_SKIP) |
438 |
|
2108 |
p++; |
439 |
✓✓ |
4524 |
if (*p & A_EXTRA) |
440 |
✓✓ |
786 |
while (*++p != '\0') |
441 |
|
310 |
continue; |
442 |
|
4524 |
q = cmd-1; |
443 |
|
|
} |
444 |
|
|
} |
445 |
|
|
/* |
446 |
|
|
* No match found in the entire command table. |
447 |
|
|
*/ |
448 |
|
152 |
return (A_INVALID); |
449 |
|
292 |
} |
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 |
✓✓ |
1834 |
for (t = tlist; t != NULL; t = t->t_next) { |
466 |
|
292 |
action = cmd_search(cmd, t->t_start, t->t_end, sp); |
467 |
✓✓ |
292 |
if (action != A_INVALID) |
468 |
|
|
break; |
469 |
|
|
} |
470 |
✗✓ |
510 |
if (action == A_UINVALID) |
471 |
|
|
action = A_INVALID; |
472 |
|
510 |
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 |
|
280 |
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 |
|
24 |
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 |
|
374 |
char *s; |
502 |
|
|
|
503 |
|
|
/* |
504 |
|
|
* Ignore lookups of any LESS* setting when we are more, and ignore |
505 |
|
|
* the less key files |
506 |
|
|
*/ |
507 |
✗✓ |
187 |
if (less_is_more) { |
508 |
|
|
if (strncmp(var, "LESS", 4) == 0) { |
509 |
|
|
return (NULL); |
510 |
|
|
} |
511 |
|
|
return (getenv(var)); |
512 |
|
|
} |
513 |
|
187 |
a = cmd_decode(list_var_tables, var, &s); |
514 |
✗✓ |
187 |
if (a == EV_OK) |
515 |
|
|
return (s); |
516 |
|
187 |
s = getenv(var); |
517 |
✓✓✓✗
|
203 |
if (s != NULL && *s != '\0') |
518 |
|
16 |
return (s); |
519 |
|
171 |
a = cmd_decode(list_sysvar_tables, var, &s); |
520 |
✗✓ |
171 |
if (a == EV_OK) |
521 |
|
|
return (s); |
522 |
|
171 |
return (NULL); |
523 |
|
187 |
} |
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 |
✗✓ |
16 |
if (secure) |
620 |
|
|
return (1); |
621 |
|
|
/* |
622 |
|
|
* Try to open the lesskey file. |
623 |
|
|
*/ |
624 |
|
8 |
filename = shell_unquote(filename); |
625 |
|
8 |
f = open(filename, O_RDONLY); |
626 |
|
8 |
free(filename); |
627 |
✓✗ |
8 |
if (f < 0) |
628 |
|
8 |
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 |
|
8 |
} |
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 |
|
32 |
PARG parg; |
680 |
|
|
|
681 |
✓✗✗✓
|
32 |
if (envname != NULL && (filename = lgetenv(envname)) != NULL) |
682 |
|
|
filename = estrdup(filename); |
683 |
✓✓ |
16 |
else if (sysvar) |
684 |
|
8 |
filename = estrdup(def_filename); |
685 |
|
|
else |
686 |
|
8 |
filename = homefile(def_filename); |
687 |
✓✓ |
16 |
if (filename == NULL) |
688 |
|
8 |
return; |
689 |
✗✓ |
8 |
if (lesskey(filename, sysvar) < 0) { |
690 |
|
|
parg.p_string = filename; |
691 |
|
|
error("Cannot use lesskey file \"%s\"", &parg); |
692 |
|
|
} |
693 |
|
8 |
free(filename); |
694 |
|
24 |
} |
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 |
|
24 |
char *s; |
705 |
|
12 |
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 |
✓✗✗✓
|
24 |
if (c == erase_char || c == erase2_char) |
715 |
|
|
return (EC_BACKSPACE); |
716 |
✗✓ |
12 |
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 |
|
12 |
do { |
725 |
✗✓ |
12 |
if (nch > 0) |
726 |
|
|
c = getcc(); |
727 |
|
12 |
usercmd[nch] = (char)c; |
728 |
|
12 |
usercmd[nch+1] = '\0'; |
729 |
|
|
nch++; |
730 |
|
12 |
action = ecmd_decode(usercmd, &s); |
731 |
✗✓ |
12 |
} while (action == A_PREFIX); |
732 |
|
|
|
733 |
✗✓ |
12 |
if (flags & EC_NORIGHTLEFT) { |
734 |
|
|
switch (action) { |
735 |
|
|
case EC_RIGHT: |
736 |
|
|
case EC_LEFT: |
737 |
|
|
action = A_INVALID; |
738 |
|
|
break; |
739 |
|
|
} |
740 |
|
|
} |
741 |
✗✓ |
12 |
if (flags & EC_NOHISTORY) { |
742 |
|
|
/* |
743 |
|
|
* The caller says there is no history list. |
744 |
|
|
* Reject any history-manipulation action. |
745 |
|
|
*/ |
746 |
|
|
switch (action) { |
747 |
|
|
case EC_UP: |
748 |
|
|
case EC_DOWN: |
749 |
|
|
action = A_INVALID; |
750 |
|
|
break; |
751 |
|
|
} |
752 |
|
|
} |
753 |
✓✗ |
12 |
if (flags & EC_NOCOMPLETE) { |
754 |
|
|
/* |
755 |
|
|
* The caller says we don't want any filename completion cmds. |
756 |
|
|
* Reject them. |
757 |
|
|
*/ |
758 |
✗✗✗✓
|
12 |
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 |
✓✗ |
12 |
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 |
✗✓ |
24 |
while (nch > 1) { |
774 |
|
|
ungetcc(usercmd[--nch]); |
775 |
|
|
} |
776 |
|
|
} else { |
777 |
|
|
if (s != NULL) |
778 |
|
|
ungetsc(s); |
779 |
|
|
} |
780 |
|
12 |
return (action); |
781 |
|
12 |
} |