1 /*
2 * Copyright (C) 1984-2026 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, see the README file.
8 */
9
10
11 /*
12 * Routines to decode user commands.
13 *
14 * This is all table driven.
15 * A command table is a sequence of command descriptors.
16 * Each command descriptor is a sequence of bytes with the following format:
17 * <c1><c2>...<cN><0><action>
18 * The characters c1,c2,...,cN are the command string; that is,
19 * the characters which the user must type.
20 * It is terminated by a null <0> byte.
21 * The byte after the null byte is the action code associated
22 * with the command string.
23 * If an action byte is OR-ed with A_EXTRA, this indicates
24 * that the option byte is followed by an extra string.
25 *
26 * There may be many command tables.
27 * The first (default) table is built-in.
28 * Other tables are read in from "lesskey" files.
29 * All the tables are linked together and are searched in order.
30 */
31
32 #include "less.h"
33 #include "cmd.h"
34 #include "lesskey.h"
35
36 extern int erase_char, erase2_char, kill_char;
37 extern int emouse;
38 extern int mouse_reverse;
39 extern int hshift;
40 extern int sc_height;
41 extern constant char *no_config;
42
43 #if USERFILE
44 /* "content" is lesskey source, never binary. */
45 static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar);
46 static int add_hometable(int (*call_lesskey)(constant char *, lbool), constant char *envname, constant char *def_filename, lbool sysvar);
47 #endif /* USERFILE */
48
49 #define SK(k) \
50 SK_SPECIAL_KEY, (k), 6, 1, 1, 1
51 /*
52 * Command table is ordered roughly according to expected
53 * frequency of use, so the common commands are near the beginning.
54 */
55
56 static unsigned char cmdtable[] =
57 {
58 '\r',0, A_F_LINE,
59 '\n',0, A_F_LINE,
60 'e',0, A_F_LINE,
61 'j',0, A_F_LINE,
62 SK(SK_DOWN_ARROW),0, A_F_LINE,
63 CONTROL('E'),0, A_F_LINE,
64 CONTROL('N'),0, A_F_LINE,
65 'k',0, A_B_LINE,
66 'y',0, A_B_LINE,
67 CONTROL('Y'),0, A_B_LINE,
68 SK(SK_CONTROL_K),0, A_B_LINE,
69 CONTROL('P'),0, A_B_LINE,
70 SK(SK_UP_ARROW),0, A_B_LINE,
71 'J',0, A_FF_LINE,
72 'K',0, A_BF_LINE,
73 'Y',0, A_BF_LINE,
74 'd',0, A_F_SCROLL,
75 CONTROL('D'),0, A_F_SCROLL,
76 'u',0, A_B_SCROLL,
77 CONTROL('U'),0, A_B_SCROLL,
78 ESC,'[','M',0, A_X11MOUSE_IN,
79 ESC,'[','<',0, A_X116MOUSE_IN,
80 ' ',0, A_F_SCREEN,
81 'f',0, A_F_SCREEN,
82 CONTROL('F'),0, A_F_SCREEN,
83 CONTROL('V'),0, A_F_SCREEN,
84 SK(SK_PAGE_DOWN),0, A_F_SCREEN,
85 'b',0, A_B_SCREEN,
86 CONTROL('B'),0, A_B_SCREEN,
87 ESC,'v',0, A_B_SCREEN,
88 SK(SK_PAGE_UP),0, A_B_SCREEN,
89 'z',0, A_F_WINDOW,
90 'w',0, A_B_WINDOW,
91 ESC,' ',0, A_FF_SCREEN,
92 ESC,'b',0, A_BF_SCREEN,
93 ESC,'j',0, A_F_NEWLINE,
94 ESC,'k',0, A_B_NEWLINE,
95 'F',0, A_F_FOREVER,
96 ESC,'f',0, A_F_FOREVER_BELL,
97 ESC,'F',0, A_F_UNTIL_HILITE,
98 'R',0, A_FREPAINT,
99 'r',0, A_REPAINT,
100 CONTROL('R'),0, A_REPAINT,
101 CONTROL('L'),0, A_REPAINT,
102 ESC,'u',0, A_UNDO_SEARCH,
103 ESC,'U',0, A_CLR_SEARCH,
104 'g',0, A_GOLINE,
105 SK(SK_HOME),0, A_GOLINE,
106 SK(SK_SHIFT_HOME),0, A_GOLINE|A_EXTRA, ESC,'{',0,
107 SK(SK_CTL_HOME),0, A_GOLINE|A_EXTRA, ESC,'{',0,
108 SK(SK_END),0, A_GOEND,
109 SK(SK_SHIFT_END),0, A_GOEND|A_EXTRA, ESC,'}',0,
110 SK(SK_CTL_END),0, A_GOEND|A_EXTRA, ESC,'}',0,
111 '<',0, A_GOLINE,
112 ESC,'<',0, A_GOLINE,
113 'p',0, A_PERCENT,
114 '%',0, A_PERCENT,
115 ESC,'(',0, A_LSHIFT,
116 ESC,')',0, A_RSHIFT,
117 ESC,'{',0, A_LLSHIFT,
118 ESC,'}',0, A_RRSHIFT,
119 SK(SK_RIGHT_ARROW),0, A_RSHIFT,
120 SK(SK_LEFT_ARROW),0, A_LSHIFT,
121 SK(SK_CTL_RIGHT_ARROW),0, A_RRSHIFT,
122 SK(SK_CTL_LEFT_ARROW),0, A_LLSHIFT,
123 SK(SK_SHIFT_RIGHT_ARROW),0, A_RRSHIFT,
124 SK(SK_SHIFT_LEFT_ARROW),0, A_LLSHIFT,
125 ESC,SK(SK_RIGHT_ARROW),0, A_RSHIFT_LIMIT,
126 ESC,']',0, A_RSHIFT_LIMIT,
127 '{',0, A_F_BRACKET|A_EXTRA, '{','}',0,
128 '}',0, A_B_BRACKET|A_EXTRA, '{','}',0,
129 '(',0, A_F_BRACKET|A_EXTRA, '(',')',0,
130 ')',0, A_B_BRACKET|A_EXTRA, '(',')',0,
131 '[',0, A_F_BRACKET|A_EXTRA, '[',']',0,
132 ']',0, A_B_BRACKET|A_EXTRA, '[',']',0,
133 ESC,CONTROL('F'),0, A_F_BRACKET,
134 ESC,CONTROL('B'),0, A_B_BRACKET,
135 'G',0, A_GOEND,
136 ESC,'G',0, A_GOEND_BUF,
137 ESC,'>',0, A_GOEND,
138 '>',0, A_GOEND,
139 'P',0, A_GOPOS,
140
141 '0',0, A_DIGIT,
142 '1',0, A_DIGIT,
143 '2',0, A_DIGIT,
144 '3',0, A_DIGIT,
145 '4',0, A_DIGIT,
146 '5',0, A_DIGIT,
147 '6',0, A_DIGIT,
148 '7',0, A_DIGIT,
149 '8',0, A_DIGIT,
150 '9',0, A_DIGIT,
151 '.',0, A_DIGIT,
152
153 '=',0, A_STAT,
154 CONTROL('G'),0, A_STAT,
155 ':','f',0, A_STAT,
156 '/',0, A_F_SEARCH,
157 '?',0, A_B_SEARCH,
158 ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0,
159 ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0,
160 'n',0, A_AGAIN_SEARCH,
161 ESC,'n',0, A_T_AGAIN_SEARCH,
162 'N',0, A_REVERSE_SEARCH,
163 ESC,'N',0, A_T_REVERSE_SEARCH,
164 '&',0, A_FILTER,
165 'm',0, A_SETMARK,
166 'M',0, A_SETMARKBOT,
167 ESC,'m',0, A_CLRMARK,
168 '\'',0, A_GOMARK,
169 CONTROL('X'),CONTROL('X'),0, A_GOMARK,
170 'E',0, A_EXAMINE,
171 ':','e',0, A_EXAMINE,
172 CONTROL('X'),CONTROL('V'),0, A_EXAMINE,
173 ':','n',0, A_NEXT_FILE,
174 ':','p',0, A_PREV_FILE,
175 CONTROL('O'),CONTROL('N'),0, A_OSC8_F_SEARCH,
176 CONTROL('O'),'n',0, A_OSC8_F_SEARCH,
177 CONTROL('O'),CONTROL('P'),0, A_OSC8_B_SEARCH,
178 CONTROL('O'),'p',0, A_OSC8_B_SEARCH,
179 CONTROL('O'),CONTROL('O'),0, A_OSC8_OPEN,
180 CONTROL('O'),'o',0, A_OSC8_OPEN,
181 CONTROL('O'),CONTROL('L'),0, A_OSC8_JUMP,
182 CONTROL('O'),'l',0, A_OSC8_JUMP,
183 't',0, A_NEXT_TAG,
184 'T',0, A_PREV_TAG,
185 ':','x',0, A_INDEX_FILE,
186 ':','d',0, A_REMOVE_FILE,
187 '-',0, A_OPT_TOGGLE,
188 ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0,
189 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0,
190 '_',0, A_DISP_OPTION,
191 '|',0, A_PIPE,
192 'v',0, A_VISUAL,
193 '!',0, A_SHELL,
194 '#',0, A_PSHELL,
195 '+',0, A_FIRSTCMD,
196
197 SK(SK_PAD_U),0, A_B_LINE,
198 SK(SK_PAD_D),0, A_F_LINE,
199 SK(SK_PAD_R),0, A_RSHIFT,
200 SK(SK_PAD_L),0, A_LSHIFT,
201 SK(SK_PAD_UR),0, A_B_SCREEN,
202 SK(SK_PAD_UL),0, A_GOLINE,
203 SK(SK_PAD_DR),0, A_F_SCREEN,
204 SK(SK_PAD_DL),0, A_GOEND,
205 SK(SK_PAD_STAR),0, A_NOACTION|A_EXTRA, '*',0,
206 SK(SK_PAD_SLASH),0, A_NOACTION|A_EXTRA, '/',0,
207 SK(SK_PAD_DASH),0, A_NOACTION|A_EXTRA, '-',0,
208 SK(SK_PAD_PLUS),0, A_NOACTION|A_EXTRA, '+',0,
209 SK(SK_PAD_DOT),0, A_NOACTION|A_EXTRA, '.',0,
210 SK(SK_PAD_COMMA),0, A_NOACTION,
211 SK(SK_PAD_ZERO),0, A_NOACTION|A_EXTRA, '0',0,
212 SK(SK_PAD_CENTER),0, A_NOACTION,
213 SK(SK_CTL_UP_ARROW),0, A_NOACTION,
214 SK(SK_CTL_DOWN_ARROW),0, A_NOACTION,
215 SK(SK_SHIFT_UP_ARROW),0, A_NOACTION,
216 SK(SK_SHIFT_DOWN_ARROW),0, A_NOACTION,
217 SK(SK_DELETE),0, A_NOACTION,
218 SK(SK_CTL_DELETE),0, A_NOACTION,
219 SK(SK_SHIFT_DELETE),0, A_NOACTION,
220 SK(SK_INSERT),0, A_NOACTION,
221 SK(SK_BACKTAB),0, A_NOACTION,
222 SK(SK_BACKSPACE),0, A_NOACTION,
223 SK(SK_CTL_BACKSPACE),0, A_NOACTION,
224 SK(SK_SHIFT_PAGE_UP),0, A_NOACTION,
225 SK(SK_SHIFT_PAGE_DOWN),0, A_NOACTION,
226 SK(SK_CTL_PAGE_UP),0, A_NOACTION,
227 SK(SK_CTL_PAGE_DOWN),0, A_NOACTION,
228 '\b',0, A_NOACTION,
229 '\177',0, A_NOACTION,
230
231 ESC,'[','2','0','0','~',0, A_START_PASTE,
232 ESC,'[','2','0','1','~',0, A_END_PASTE,
233
234 'H',0, A_HELP,
235 'h',0, A_HELP,
236 SK(SK_F1),0, A_HELP,
237 'V',0, A_VERSION,
238 'q',0, A_QUIT,
239 'Q',0, A_QUIT,
240 ':','q',0, A_QUIT,
241 ':','Q',0, A_QUIT,
242 'Z','Z',0, A_QUIT
243 };
244
245 static unsigned char edittable[] =
246 {
247 '\t',0, EC_F_COMPLETE, /* TAB */
248 '\17',0, EC_B_COMPLETE, /* BACKTAB */
249 SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */
250 ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */
251 CONTROL('L'),0, EC_EXPAND, /* CTRL-L */
252 CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */
253 CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */
254 ESC,'l',0, EC_RIGHT, /* ESC l */
255 SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */
256 ESC,'h',0, EC_LEFT, /* ESC h */
257 SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */
258 ESC,'b',0, EC_W_LEFT, /* ESC b */
259 ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */
260 SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */
261 ESC,'w',0, EC_W_RIGHT, /* ESC w */
262 ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */
263 SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */
264 ESC,'i',0, EC_INSERT, /* ESC i */
265 SK(SK_INSERT),0, EC_INSERT, /* INSERT */
266 ESC,'x',0, EC_DELETE, /* ESC x */
267 SK(SK_DELETE),0, EC_DELETE, /* DELETE */
268 ESC,'X',0, EC_W_DELETE, /* ESC X */
269 ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */
270 SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */
271 SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */
272 ESC,SK(SK_BACKSPACE),0, EC_W_BACKSPACE, /* ESC BACKSPACE */
273 ESC,'0',0, EC_HOME, /* ESC 0 */
274 SK(SK_HOME),0, EC_HOME, /* HOME */
275 SK(SK_SHIFT_HOME),0, EC_HOME, /* SHIFT-HOME */
276 SK(SK_CTL_HOME),0, EC_HOME, /* CTRL-HOME */
277 ESC,'$',0, EC_END, /* ESC $ */
278 SK(SK_END),0, EC_END, /* END */
279 SK(SK_SHIFT_END),0, EC_END, /* SHIFT-END */
280 SK(SK_CTL_END),0, EC_END, /* CTRL-END */
281 ESC,'k',0, EC_UP, /* ESC k */
282 SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */
283 ESC,'j',0, EC_DOWN, /* ESC j */
284 SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */
285 CONTROL('G'),0, EC_ABORT, /* CTRL-G */
286 SK(SK_PAD_U),0, EC_UP,
287 SK(SK_PAD_D),0, EC_DOWN,
288 SK(SK_PAD_R),0, EC_RIGHT,
289 SK(SK_PAD_L),0, EC_LEFT,
290 SK(SK_PAD_UR),0, A_NOACTION,
291 SK(SK_PAD_UL),0, EC_HOME,
292 SK(SK_PAD_DR),0, A_NOACTION,
293 SK(SK_PAD_DL),0, EC_END,
294 SK(SK_PAD_STAR),0, A_NOACTION|A_EXTRA, '*',0,
295 SK(SK_PAD_SLASH),0, A_NOACTION|A_EXTRA, '/',0,
296 SK(SK_PAD_DASH),0, A_NOACTION|A_EXTRA, '-',0,
297 SK(SK_PAD_PLUS),0, A_NOACTION|A_EXTRA, '+',0,
298 SK(SK_PAD_DOT),0, A_NOACTION|A_EXTRA, '.',0,
299 SK(SK_PAD_COMMA),0, A_NOACTION|A_EXTRA, ',',0,
300 SK(SK_PAD_ZERO),0, A_NOACTION|A_EXTRA, '0',0,
301 SK(SK_PAD_CENTER),0, A_NOACTION,
302 SK(SK_CTL_UP_ARROW),0, A_NOACTION,
303 SK(SK_CTL_DOWN_ARROW),0, A_NOACTION,
304 SK(SK_SHIFT_UP_ARROW),0, A_NOACTION,
305 SK(SK_SHIFT_DOWN_ARROW),0, A_NOACTION,
306 SK(SK_SHIFT_DELETE),0, A_NOACTION,
307 SK(SK_PAGE_UP),0, A_NOACTION,
308 SK(SK_PAGE_DOWN),0, A_NOACTION,
309 SK(SK_SHIFT_LEFT_ARROW),0, A_NOACTION,
310 SK(SK_SHIFT_RIGHT_ARROW),0, A_NOACTION,
311 SK(SK_SHIFT_PAGE_UP),0, A_NOACTION,
312 SK(SK_SHIFT_PAGE_DOWN),0, A_NOACTION,
313 SK(SK_CTL_PAGE_UP),0, A_NOACTION,
314 SK(SK_CTL_PAGE_DOWN),0, A_NOACTION,
315 ESC,'[','M',0, EC_X11MOUSE, /* X11 mouse report */
316 ESC,'[','<',0, EC_X116MOUSE, /* X11 1006 mouse report */
317 ESC,'[','2','0','0','~',0, A_START_PASTE, /* open paste bracket */
318 ESC,'[','2','0','1','~',0, A_END_PASTE, /* close paste bracket */
319 };
320
321 /*
322 * Default environment variables.
323 * Note: "\201" is (A_EXTRA|EV_OK).
324 */
325 static char dflt_vartable[] =
326 {
327 "LESS_OSC8_OPEN_ANY\0\201"
328 #ifdef LIBEXECDIR
329 "-" LIBEXECDIR "/less-osc8-open"
330 #else
331 "-less-osc8-open"
332 #endif
333 "\0"
334 };
335
336 /*
337 * Structure to support a list of command tables.
338 */
339 struct tablelist
340 {
341 struct tablelist *t_next;
342 unsigned char *t_start;
343 unsigned char *t_end;
344 };
345
346 /*
347 * List of command tables and list of line-edit tables.
348 */
349 static struct tablelist *list_fcmd_tables = NULL;
350 static struct tablelist *list_ecmd_tables = NULL;
351 static struct tablelist *list_var_tables = NULL;
352 static struct tablelist *list_sysvar_tables = NULL;
353
354 #if USERFILE
355 static struct tablelist * find_stop_table(struct tablelist *t);
356 #endif
357
358 /*
359 * Expand special key abbreviations in a command table.
360 */
expand_special_keys(unsigned char * table,size_t len)361 static void expand_special_keys(unsigned char *table, size_t len)
362 {
363 unsigned char *fm;
364 unsigned char *to;
365 int a;
366 constant char *repl;
367 size_t klen;
368
369 for (fm = table; fm < table + len; )
370 {
371 /*
372 * Rewrite each command in the table with any
373 * special key abbreviations expanded.
374 */
375 for (to = fm; fm < table + len && *fm != '\0'; )
376 {
377 if (*fm != SK_SPECIAL_KEY)
378 {
379 *to++ = *fm++;
380 continue;
381 }
382 /*
383 * After SK_SPECIAL_KEY, next byte is the type
384 * of special key (one of the SK_* constants),
385 * and the byte after that is the number of bytes,
386 * N, reserved by the abbreviation (including the
387 * SK_SPECIAL_KEY and key type bytes).
388 * Replace all N bytes with the actual bytes
389 * output by the special key on this terminal.
390 */
391 repl = special_key_str(fm[1]);
392 klen = fm[2];
393 fm += klen;
394 if (klen < 3)
395 {
396 /* Malformed entry; N cannot be less than 3.
397 * Skip this entry and (for simplicity) the rest of the table. */
398 return;
399 }
400 if (repl == NULL || strlen(repl) > klen)
401 repl = "\377";
402 if (to + strlen(repl) + 1 > table + len)
403 {
404 /* Replacement string won't fit in table.
405 * This cannot happen unless klen is wrong. */
406 return;
407 }
408 while (*repl != '\0')
409 *to++ = (unsigned char) *repl++; /*{{type-issue}}*/
410 }
411 if (fm + 2 > table + len)
412 return; /* last entry is truncated */
413 *to++ = '\0';
414 /*
415 * Fill any unused bytes between end of command and
416 * the action byte with A_SKIP.
417 */
418 while (to <= fm)
419 *to++ = A_SKIP;
420 fm++;
421 a = *fm++;
422 if (a & A_EXTRA)
423 {
424 while (fm < table + len && *fm++ != '\0')
425 continue;
426 }
427 }
428 }
429
430 /*
431 * Expand special key abbreviations in a list of command tables.
432 */
expand_cmd_table(struct tablelist * tlist)433 static void expand_cmd_table(struct tablelist *tlist)
434 {
435 struct tablelist *t;
436 for (t = tlist; t != NULL; t = t->t_next)
437 {
438 expand_special_keys(t->t_start, ptr_diff(t->t_end, t->t_start));
439 }
440 }
441
442 /*
443 * Expand special key abbreviations in all command tables.
444 */
expand_cmd_tables(void)445 public void expand_cmd_tables(void)
446 {
447 expand_cmd_table(list_fcmd_tables);
448 expand_cmd_table(list_ecmd_tables);
449 expand_cmd_table(list_var_tables);
450 expand_cmd_table(list_sysvar_tables);
451 }
452
453 /*
454 * Add a command table.
455 */
add_cmd_table(struct tablelist ** tlist,unsigned char * buf,size_t len)456 static int add_cmd_table(struct tablelist **tlist, unsigned char *buf, size_t len)
457 {
458 struct tablelist *t;
459
460 if (len == 0)
461 return (0);
462 /*
463 * Allocate a tablelist structure, initialize it,
464 * and link it into the list of tables.
465 */
466 if ((t = (struct tablelist *)
467 calloc(1, sizeof(struct tablelist))) == NULL)
468 {
469 return (-1);
470 }
471 t->t_start = buf;
472 t->t_end = buf + len;
473 t->t_next = NULL;
474 if (*tlist == NULL)
475 *tlist = t;
476 else
477 {
478 struct tablelist *e;
479 for (e = *tlist; e->t_next != NULL; e = e->t_next)
480 continue;
481 e->t_next = t;
482 }
483 return (0);
484 }
485
486 /*
487 * Add a command table.
488 */
add_fcmd_table(unsigned char * buf,size_t len)489 static void add_fcmd_table(unsigned char *buf, size_t len)
490 {
491 if (add_cmd_table(&list_fcmd_tables, buf, len) < 0)
492 error(LM(some_commands_disabled), NULL_PARG);
493 }
494
495 /*
496 * Add an editing command table.
497 */
add_ecmd_table(unsigned char * buf,size_t len)498 static void add_ecmd_table(unsigned char *buf, size_t len)
499 {
500 if (add_cmd_table(&list_ecmd_tables, buf, len) < 0)
501 error(LM(some_edit_commands_disabled), NULL_PARG);
502 }
503
504 /*
505 * Add an environment variable table.
506 */
add_var_table(struct tablelist ** tlist,mutable unsigned char * buf,size_t len)507 static void add_var_table(struct tablelist **tlist, mutable unsigned char *buf, size_t len)
508 {
509 struct xbuffer xbuf;
510
511 xbuf_init(&xbuf);
512 expand_evars((mutable char*)buf, len, &xbuf); /*{{unsigned-issue}}*/
513 /* {{ We leak the table in buf. expand_evars scribbled in it so it's useless anyway. }} */
514 if (add_cmd_table(tlist, xbuf.data, xbuf.end) < 0)
515 error(LM(environment_variables_from_lesskey_file_unavailable), NULL_PARG);
516 }
517
add_uvar_table(unsigned char * buf,size_t len)518 static void add_uvar_table(unsigned char *buf, size_t len)
519 {
520 add_var_table(&list_var_tables, buf, len);
521 }
522
add_sysvar_table(unsigned char * buf,size_t len)523 static void add_sysvar_table(unsigned char *buf, size_t len)
524 {
525 add_var_table(&list_sysvar_tables, buf, len);
526 }
527
528 /*
529 * Initialize the command lists.
530 */
init_cmds(void)531 public void init_cmds(void)
532 {
533 unsigned char *udflt_vartable = (unsigned char *) dflt_vartable;
534
535 /*
536 * Add the default command tables.
537 */
538 add_fcmd_table(cmdtable, sizeof(cmdtable));
539 add_ecmd_table(edittable, sizeof(edittable));
540 add_sysvar_table(udflt_vartable, sizeof(dflt_vartable));
541 #if USERFILE
542 #ifdef BINDIR /* For backwards compatibility */
543 /* Try to add tables in the OLD system lesskey file. */
544 add_hometable(lesskey, NULL, BINDIR "/.sysless", TRUE);
545 #endif
546 /*
547 * Try to load lesskey source file or binary file.
548 * If the source file succeeds, don't load binary file.
549 * The binary file is likely to have been generated from
550 * a (possibly out of date) copy of the src file,
551 * so loading it is at best redundant.
552 */
553 /*
554 * Try to add tables in system lesskey src file.
555 */
556 #if HAVE_LESSKEYSRC
557 if (add_hometable(lesskey_src, "LESSKEYIN_SYSTEM", LESSKEYINFILE_SYS, TRUE) != 0)
558 #endif
559 {
560 /*
561 * Try to add the tables in the system lesskey binary file.
562 */
563 add_hometable(lesskey, "LESSKEY_SYSTEM", LESSKEYFILE_SYS, TRUE);
564 }
565 /*
566 * Try to add tables in the lesskey src file "$HOME/.lesskey".
567 */
568 #if HAVE_LESSKEYSRC
569 if (add_hometable(lesskey_src, "LESSKEYIN", DEF_LESSKEYINFILE, FALSE) != 0)
570 #endif
571 {
572 /*
573 * Try to add the tables in the standard lesskey binary file "$HOME/.less".
574 */
575 add_hometable(lesskey, "LESSKEY", LESSKEYFILE, FALSE);
576 }
577
578 add_content_table(lesskey_content, "LESSKEY_CONTENT_SYSTEM", TRUE);
579 add_content_table(lesskey_content, "LESSKEY_CONTENT", FALSE);
580
581 /*
582 * If any command table contains a #stop directive, discard all other tables.
583 */
584 {
585 struct tablelist *t = find_stop_table(list_fcmd_tables);
586 if (t != NULL)
587 {
588 t->t_next = NULL;
589 list_fcmd_tables = t;
590 }
591 }
592 #endif /* USERFILE */
593 }
594
595 /*
596 * Remove the last command table in a list.
597 */
pop_cmd_table(struct tablelist ** tlist)598 static void pop_cmd_table(struct tablelist **tlist)
599 {
600 struct tablelist *t;
601 if (*tlist == NULL)
602 return;
603 if ((*tlist)->t_next == NULL)
604 {
605 t = *tlist;
606 *tlist = NULL;
607 } else
608 {
609 struct tablelist *e;
610 for (e = *tlist; e->t_next->t_next != NULL; e = e->t_next)
611 continue;
612 t = e->t_next;
613 e->t_next = NULL;
614 }
615 free(t);
616 }
617
618 /*
619 * Return action for a mouse wheel down event.
620 */
mouse_wheel_down(void)621 static int mouse_wheel_down(void)
622 {
623 if (!(emouse & EMOUSE_VSCROLL))
624 return A_NOACTION;
625 return mouse_reverse ? A_B_MOUSE : A_F_MOUSE;
626 }
627
628 /*
629 * Return action for a mouse wheel up event.
630 */
mouse_wheel_up(void)631 static int mouse_wheel_up(void)
632 {
633 if (!(emouse & EMOUSE_VSCROLL))
634 return A_NOACTION;
635 return mouse_reverse ? A_F_MOUSE : A_B_MOUSE;
636 }
637
638 /*
639 * Return action for a mouse wheel left event.
640 */
mouse_wheel_left(void)641 static int mouse_wheel_left(void)
642 {
643 if (!(emouse & EMOUSE_HSCROLL))
644 return A_NOACTION;
645 return mouse_reverse ? A_R_MOUSE : A_L_MOUSE;
646 }
647
648 /*
649 * Return action for a mouse wheel right event.
650 */
mouse_wheel_right(void)651 static int mouse_wheel_right(void)
652 {
653 if (!(emouse & EMOUSE_HSCROLL))
654 return A_NOACTION;
655 return mouse_reverse ? A_L_MOUSE : A_R_MOUSE;
656 }
657
658 /*
659 * Return action for the left mouse button trigger.
660 */
mouse_button_left(int x,int y,lbool down,lbool drag)661 static int mouse_button_left(int x, int y, lbool down, lbool drag)
662 {
663 static int last_drag_x = -1;
664 static int last_drag_y = -1;
665 static int last_click_y = -1;
666
667 if (down && !drag)
668 {
669 last_drag_x = x;
670 last_drag_y = last_click_y = y;
671 }
672 if (drag)
673 {
674 if ((emouse & EMOUSE_HDRAG) && last_drag_x >= 0 && x != last_drag_x) {
675 /* Drag text left/right */
676 pos_rehead(FALSE);
677 if (hshift < x - last_drag_x)
678 hshift = 0;
679 else
680 hshift -= x - last_drag_x;
681 screen_trashed();
682 cmd_exec();
683 last_drag_x = x;
684 }
685 if ((emouse & EMOUSE_VDRAG) && last_drag_y >= 0) {
686 /* Drag text up/down */
687 if (y > last_drag_y)
688 {
689 cmd_exec();
690 backward(y - last_drag_y, FALSE, FALSE, FALSE);
691 last_drag_y = y;
692 } else if (y < last_drag_y)
693 {
694 cmd_exec();
695 forward(last_drag_y - y, FALSE, FALSE, FALSE);
696 last_drag_y = y;
697 }
698 }
699 } else if ((emouse & EMOUSE_LCLICK) && !down)
700 {
701 #if OSC8_LINK
702 if (secure_allow(SF_OSC8_OPEN))
703 {
704 cmd_exec();
705 if (osc8_click(y, x))
706 return (A_NOACTION);
707 }
708 #else
709 (void) x;
710 #endif /* OSC8_LINK */
711 if (y < sc_height-1 && y == last_click_y)
712 {
713 cmd_exec();
714 setmark('#', y, 0);
715 screen_trashed();
716 }
717 }
718 return (A_NOACTION);
719 }
720
721 /*
722 * Return action for the right mouse button trigger.
723 */
mouse_button_right(int x,int y,lbool down,lbool drag)724 static int mouse_button_right(int x, int y, lbool down, lbool drag)
725 {
726 (void) x; (void) drag;
727 /*
728 * {{ unlike mouse_button_left, we could return an action,
729 * but keep it near mouse_button_left for readability. }}
730 */
731 if (!(emouse & EMOUSE_RCLICK))
732 return (A_NOACTION);
733 if (!down && y < sc_height-1)
734 {
735 gomark('#', 0);
736 screen_trashed();
737 }
738 return (A_NOACTION);
739 }
740
741 /*
742 * Read a decimal integer. Return the integer and set *pterm to the terminating char.
743 */
getcc_int(char * pterm)744 static int getcc_int(char *pterm)
745 {
746 int num = 0;
747 int digits = 0;
748 for (;;)
749 {
750 char ch = getcc();
751 if (ch < '0' || ch > '9')
752 {
753 if (pterm != NULL) *pterm = ch;
754 if (digits == 0)
755 return (-1);
756 return (num);
757 }
758 if (ckd_mul(&num, num, 10) || ckd_add(&num, num, ch - '0'))
759 return -1;
760 ++digits;
761 }
762 }
763
x11mouse_button(int btn,int x,int y,lbool down,lbool drag)764 static int x11mouse_button(int btn, int x, int y, lbool down, lbool drag)
765 {
766 switch (btn) {
767 case X11MOUSE_BUTTON1:
768 return mouse_button_left(x, y, down, drag);
769 /* is BUTTON2 the rightmost with 2-buttons mouse? */
770 case X11MOUSE_BUTTON2:
771 case X11MOUSE_BUTTON3:
772 return mouse_button_right(x, y, down, drag);
773 }
774 return (A_NOACTION);
775 }
776
777 /*
778 * Read suffix of mouse input and return the action to take.
779 * The prefix ("\e[M") has already been read.
780 */
x11mouse_action(lbool skip)781 static int x11mouse_action(lbool skip)
782 {
783 static int prev_b = X11MOUSE_BUTTON_REL;
784 int x, y;
785 int b = getcc() - X11MOUSE_OFFSET;
786 lbool drag = ((b & X11MOUSE_DRAG) != 0);
787 b &= ~X11MOUSE_DRAG;
788 x = getcc() - X11MOUSE_OFFSET-1;
789 y = getcc() - X11MOUSE_OFFSET-1;
790 if (skip)
791 return (A_NOACTION);
792 switch (b) {
793 case X11MOUSE_WHEEL_DOWN:
794 return mouse_wheel_down();
795 case X11MOUSE_WHEEL_UP:
796 return mouse_wheel_up();
797 case X11MOUSE_WHEEL_LEFT:
798 return mouse_wheel_left();
799 case X11MOUSE_WHEEL_RIGHT:
800 return mouse_wheel_right();
801 case X11MOUSE_BUTTON1:
802 case X11MOUSE_BUTTON2:
803 case X11MOUSE_BUTTON3:
804 prev_b = b;
805 return x11mouse_button(b, x, y, TRUE, drag);
806 case X11MOUSE_BUTTON_REL: /* button up */
807 return x11mouse_button(prev_b, x, y, FALSE, drag);
808 }
809 return (A_NOACTION);
810 }
811
812 /*
813 * Read suffix of mouse input and return the action to take.
814 * The prefix ("\e[<") has already been read.
815 */
x116mouse_action(lbool skip)816 static int x116mouse_action(lbool skip)
817 {
818 char ch;
819 int x, y;
820 int b = getcc_int(&ch);
821 lbool drag = ((b & X11MOUSE_DRAG) != 0);
822 b &= ~X11MOUSE_DRAG;
823 if (b < 0 || ch != ';') return (A_NOACTION);
824 x = getcc_int(&ch) - 1;
825 if (x < 0 || ch != ';') return (A_NOACTION);
826 y = getcc_int(&ch) - 1;
827 if (y < 0) return (A_NOACTION);
828 if (skip)
829 return (A_NOACTION);
830 switch (b) {
831 case X11MOUSE_WHEEL_DOWN:
832 return mouse_wheel_down();
833 case X11MOUSE_WHEEL_UP:
834 return mouse_wheel_up();
835 case X11MOUSE_WHEEL_LEFT:
836 return mouse_wheel_left();
837 case X11MOUSE_WHEEL_RIGHT:
838 return mouse_wheel_right();
839 case X11MOUSE_BUTTON1:
840 case X11MOUSE_BUTTON2:
841 case X11MOUSE_BUTTON3: {
842 lbool down = (ch == 'M');
843 lbool up = (ch == 'm');
844 if (up || down)
845 return x11mouse_button(b, x, y, down, drag);
846 break; }
847 }
848 return (A_NOACTION);
849 }
850
851 /*
852 * Return the largest N such that the first N chars of goal
853 * are equal to the last N chars of str.
854 */
cmd_match(constant char * goal,constant char * str)855 static size_t cmd_match(constant char *goal, constant char *str)
856 {
857 size_t slen = strlen(str);
858 size_t len;
859 for (len = slen; len > 0; len--)
860 if (strncmp(str + slen - len, goal, len) == 0)
861 break;
862 return len;
863 }
864
865 /*
866 * Skip to next null byte in entry, and return pointer to it.
867 * Return NULL if no null byte is found before end.
868 */
skip_to_null(constant unsigned char * entry,constant unsigned char * end)869 static constant unsigned char * skip_to_null(constant unsigned char *entry, constant unsigned char *end)
870 {
871 for (;;)
872 {
873 if (entry >= end)
874 return NULL;
875 if (*entry == '\0')
876 return entry;
877 ++entry;
878 }
879 }
880
881 /*
882 * Return pointer to next command table entry.
883 * Also return the command length, the action code and the extra string
884 * from the current entry.
885 * Return NULL if the entry is truncated.
886 */
cmd_next_entry(constant unsigned char * entry,constant unsigned char * end,mutable int * action,mutable constant unsigned char ** extra,mutable size_t * cmdlen)887 static constant unsigned char * cmd_next_entry(constant unsigned char *entry, constant unsigned char *end, mutable int *action, mutable constant unsigned char **extra, mutable size_t *cmdlen)
888 {
889 int a;
890 constant unsigned char *oentry = entry;
891 entry = skip_to_null(entry, end); /* skip to end of cmd */
892 if (entry == NULL)
893 return NULL;
894 if (cmdlen != NULL)
895 *cmdlen = ptr_diff(entry, oentry);
896 do { /* skip any A_SKIP bytes between cmd and action */
897 if (++entry >= end)
898 return NULL;
899 a = *entry;
900 } while (a == A_SKIP);
901 ++entry; /* skip action */
902 if (extra != NULL)
903 *extra = (a & A_EXTRA) ? entry : NULL;
904 if (a & A_EXTRA)
905 {
906 entry = skip_to_null(entry, end); /* skip to end of extra string */
907 if (entry == NULL)
908 return NULL;
909 ++entry; /* skip null at end of extra string */
910 a &= ~A_EXTRA;
911 }
912 if (action != NULL)
913 *action = a;
914 return entry;
915 }
916
917 #if USERFILE
918 /*
919 * Does a command table contain a #stop directive?
920 */
table_has_stop(struct tablelist * t)921 static lbool table_has_stop(struct tablelist *t)
922 {
923 constant unsigned char *entry = t->t_start;
924
925 while (entry < t->t_end)
926 {
927 int action;
928 entry = cmd_next_entry(entry, t->t_end, &action, NULL, NULL);
929 if (entry == NULL)
930 break;
931 if (action == A_END_LIST)
932 return TRUE;
933 }
934 return FALSE;
935 }
936
937 /*
938 * Find the first command table with a #stop directive, if any.
939 */
find_stop_table(struct tablelist * t)940 static struct tablelist * find_stop_table(struct tablelist *t)
941 {
942 for (; t != NULL; t = t->t_next)
943 {
944 if (table_has_stop(t))
945 return t;
946 }
947 return NULL;
948 }
949 #endif /* USERFILE */
950
951 /*
952 * Search a list of command tables for the command string in cmd.
953 */
cmd_decode(struct tablelist * tlist,constant char * cmd,lbool anchored,constant char ** extra)954 static int cmd_decode(struct tablelist *tlist, constant char *cmd, lbool anchored, constant char **extra)
955 {
956 int action = A_INVALID;
957 size_t match_len = 0;
958 constant unsigned char *table = NULL;
959 constant unsigned char *endtable;
960
961 if (extra != NULL)
962 *extra = NULL;
963 while (tlist != NULL)
964 {
965 int taction;
966 constant unsigned char *textra;
967 size_t tcmdlen;
968 size_t tmatch;
969 if (table == NULL)
970 {
971 /* Beginning of table: set start/end pointers. */
972 table = tlist->t_start;
973 endtable = tlist->t_end;
974 }
975 if (anchored)
976 tmatch = (strcmp((constant char *) table, cmd) == 0) ? strlen(cmd) : 0;
977 else
978 tmatch = cmd_match((constant char *) table, cmd);
979 table = cmd_next_entry(table, tlist->t_end, &taction, &textra, &tcmdlen);
980 if (table == NULL)
981 {
982 /* Truncated/malformed entry; ignore this entry and move to next table. */
983 tlist = tlist->t_next;
984 continue;
985 }
986 if (table >= endtable)
987 {
988 /* Last entry in table; process this entry then move to next table. */
989 tlist = tlist->t_next;
990 table = NULL;
991 }
992 if (taction == A_END_LIST)
993 break;
994 if (tmatch >= match_len)
995 {
996 if (tmatch == tcmdlen)
997 {
998 /* (Last chars of) cmd matches this table entry. */
999 action = taction;
1000 if (extra != NULL)
1001 *extra = (constant char *) textra;
1002 } else if (tmatch > 0 && (tmatch > match_len || !anchored))
1003 {
1004 /* cmd is a prefix of this table entry */
1005 action = A_PREFIX;
1006 if (extra != NULL)
1007 *extra = NULL;
1008 }
1009 match_len = tmatch;
1010 }
1011 }
1012 if (action == A_X11MOUSE_IN)
1013 action = x11mouse_action(FALSE);
1014 else if (action == A_X116MOUSE_IN)
1015 action = x116mouse_action(FALSE);
1016 return action;
1017 }
1018
1019 /*
1020 * Decode a command from the cmdtables list.
1021 */
fcmd_decode(constant char * cmd,constant char ** sp)1022 public int fcmd_decode(constant char *cmd, constant char **sp)
1023 {
1024 return (cmd_decode(list_fcmd_tables, cmd, FALSE, sp));
1025 }
1026
1027 /*
1028 * Decode a command from the edittables list.
1029 */
ecmd_decode(constant char * cmd,constant char ** sp)1030 static int ecmd_decode(constant char *cmd, constant char **sp)
1031 {
1032 return (cmd_decode(list_ecmd_tables, cmd, FALSE, sp));
1033 }
1034
1035 /*
1036 * Parse a comma-separated list.
1037 * Call func repeatedly, passing each item in the list.
1038 * Stop and return FALSE if func ever returns FALSE,
1039 * otherwise parse the entire list and return TRUE.
1040 */
parse_csl(lbool (* func)(constant char * word,size_t wlen,void * arg),constant char * str,void * arg)1041 public lbool parse_csl(lbool (*func)(constant char *word, size_t wlen, void *arg), constant char *str, void *arg)
1042 {
1043 for (;;)
1044 {
1045 constant char *estr;
1046 while (*str == ' ' || *str == ',') ++str; /* skip leading spaces/commas */
1047 if (*str == '\0') break;
1048 estr = strchr(str, ',');
1049 if (estr == NULL) estr = str + strlen(str);
1050 while (estr > str && estr[-1] == ' ') --estr; /* trim trailing spaces */
1051 if (!(*func)(str, ptr_diff(estr, str), arg))
1052 return FALSE;
1053 str = estr;
1054 }
1055 return TRUE;
1056 }
1057
1058 /*
1059 * Display error message for parse_csl_bitmap.
1060 */
csl_bitmap_error(constant char * pfx,constant char * type,size_t len,constant char * name)1061 static int csl_bitmap_error(constant char *pfx, constant char *type, size_t len, constant char *name)
1062 {
1063 PARG parg;
1064 size_t msglen = len + strlen(pfx) + strlen(type) + 32;
1065 char *msg = ecalloc(msglen, sizeof(char));
1066 SNPRINTF4(msg, msglen, "%s: %s name \"%.*s\"", pfx, type, (int) len, name);
1067 parg.p_string = msg;
1068 error("%s", &parg);
1069 free(msg);
1070 return 0;
1071 }
1072
1073 /*
1074 * Return the bit value of a csl_bitmap name.
1075 */
csl_bitmap_bit(constant char * name,size_t len,struct csl_bitmap_def * defs,int num_defs,constant char * pfx)1076 static int csl_bitmap_bit(constant char *name, size_t len, struct csl_bitmap_def *defs, int num_defs, constant char *pfx)
1077 {
1078 int i;
1079 int match = -1;
1080
1081 for (i = 0; i < num_defs; i++)
1082 {
1083 if (strncmp(defs[i].bit_name, name, len) == 0)
1084 {
1085 if (match >= 0) /* name is ambiguous */
1086 return csl_bitmap_error(pfx, LM(ambiguous), len, name);
1087 match = i;
1088 }
1089 }
1090 if (match < 0)
1091 return csl_bitmap_error(pfx, LM(invalid), len, name);
1092 return defs[match].bit_value;
1093 }
1094
1095 /* State for parse_csl_bitmap. */
1096 struct csl_bitmap_info
1097 {
1098 int bitmap; /* bitmap being constructed */
1099 struct csl_bitmap_def *def; /* array of name/value pairs */
1100 int num_defs; /* length of def array */
1101 constant char *pfx; /* prefix for error messages */
1102 };
1103
1104 /*
1105 * Set a bit in a csl_bitmap_info based on a csl_bitmap name.
1106 */
csl_bitmap_set(constant char * word,size_t wlen,void * arg)1107 static lbool csl_bitmap_set(constant char *word, size_t wlen, void *arg)
1108 {
1109 struct csl_bitmap_info *info = (struct csl_bitmap_info *) arg;
1110 info->bitmap |= csl_bitmap_bit(word, wlen, info->def, info->num_defs, info->pfx);
1111 return TRUE;
1112 }
1113
1114 /*
1115 * Parse a comma-separated list of csl_bitmap names and return
1116 * the combined bitmap value.
1117 */
parse_csl_bitmap(constant char * str,struct csl_bitmap_def * defs,int num_defs,constant char * pfx)1118 public int parse_csl_bitmap(constant char *str, struct csl_bitmap_def *defs, int num_defs, constant char *pfx)
1119 {
1120 struct csl_bitmap_info info;
1121 info.bitmap = 0;
1122 info.def = defs;
1123 info.num_defs = num_defs;
1124 info.pfx = pfx;
1125 if (!parse_csl(csl_bitmap_set, str, &info))
1126 return -1;
1127 return info.bitmap;
1128 }
1129
1130 /*
1131 * Should we ignore the setting of an environment variable?
1132 */
word_no_match(constant char * word,size_t wlen,void * arg)1133 static lbool word_no_match(constant char *word, size_t wlen, void *arg)
1134 {
1135 constant char *var = (constant char *) arg;
1136 return !(wlen == strlen(var) && strncmp(var, word, wlen) == 0);
1137 }
ignore_env(constant char * var)1138 static lbool ignore_env(constant char *var)
1139 {
1140 if (isnullenv(no_config))
1141 return FALSE; /* no_config is not set; don't ignore anything */
1142 /* no_config is set; ignore any var that does not appear in no_config */
1143 return parse_csl(word_no_match, no_config, (void*) var);
1144 }
1145
1146 #if !GETENV_NONVOLATILE
1147
1148 /*
1149 * Cache for environment variables.
1150 * We keep them in a cache to ensure that lgetenv always returns
1151 * a string with indefinite lifetime. POSIX specifies that a string
1152 * returned from getenv may be overwritten by a subsequent getenv.
1153 */
1154 struct env_cache {
1155 struct env_cache *ec_next;
1156 char *ec_var;
1157 char *ec_value;
1158 };
1159
1160 static struct env_cache *env_cache_list = NULL;
1161
1162 /*
1163 * Add an environment variable to the cache.
1164 */
addenv_cache(constant char * var,constant char * value)1165 static void addenv_cache(constant char *var, constant char *value)
1166 {
1167 struct env_cache *ec = ecalloc(1, sizeof(struct env_cache));
1168
1169 ec->ec_var = save(var);
1170 ec->ec_value = save(value);
1171 ec->ec_next = env_cache_list;
1172 env_cache_list = ec;
1173 }
1174
1175 /*
1176 * Find an environment variable in the cache.
1177 */
getenv_cache(constant char * var)1178 static constant char * getenv_cache(constant char *var)
1179 {
1180 struct env_cache *ec;
1181
1182 for (ec = env_cache_list; ec != NULL; ec = ec->ec_next)
1183 {
1184 if (strcmp(var, ec->ec_var) == 0)
1185 return ec->ec_value;
1186 }
1187 return NULL;
1188 }
1189
1190 #endif /* GETENV_NONVOLATILE */
1191
1192 /*
1193 * Get the value of an environment variable.
1194 * Looks first in the lesskey file, then in the real environment.
1195 */
lgetenv(constant char * var)1196 public constant char * lgetenv(constant char *var)
1197 {
1198 int a;
1199 constant char *s;
1200
1201 if (ignore_env(var))
1202 return (NULL);
1203 a = cmd_decode(list_var_tables, var, TRUE, &s);
1204 if (a == EV_OK)
1205 return (s);
1206 #if !GETENV_NONVOLATILE
1207 s = getenv_cache(var);
1208 if (!isnullenv(s))
1209 return (s);
1210 #endif
1211 s = getenv(var);
1212 if (!isnullenv(s))
1213 {
1214 #if !GETENV_NONVOLATILE
1215 addenv_cache(var, s);
1216 #endif
1217 return (s);
1218 }
1219 a = cmd_decode(list_sysvar_tables, var, TRUE, &s);
1220 if (a == EV_OK)
1221 return (s);
1222 return (NULL);
1223 }
1224
1225 /*
1226 * Like lgetenv, but also uses a buffer partially filled with an env table.
1227 */
lgetenv_ext(constant char * var,unsigned char * env_buf,size_t env_buf_len)1228 public constant char * lgetenv_ext(constant char *var, unsigned char *env_buf, size_t env_buf_len)
1229 {
1230 constant char *r;
1231 size_t e;
1232 size_t env_end = 0;
1233
1234 for (e = 0;;)
1235 {
1236 for (; e < env_buf_len; e++)
1237 if (env_buf[e] == '\0')
1238 break;
1239 if (e+1 >= env_buf_len) break;
1240 if (env_buf[++e] & A_EXTRA)
1241 {
1242 for (e = e+1; e < env_buf_len; e++)
1243 if (env_buf[e] == '\0')
1244 break;
1245 }
1246 e++;
1247 if (e >= env_buf_len) break;
1248 env_end = e;
1249 }
1250 /* Temporarily add env_buf to var_tables, do the lookup, then remove it. */
1251 add_cmd_table(&list_var_tables, env_buf, env_end);
1252 r = lgetenv(var);
1253 pop_cmd_table(&list_var_tables);
1254 return r;
1255 }
1256
1257 /*
1258 * Is a string null or empty?
1259 */
isnullenv(constant char * s)1260 public lbool isnullenv(constant char *s)
1261 {
1262 return (s == NULL || *s == '\0');
1263 }
1264
1265 #if USERFILE
1266 /*
1267 * Get an "integer" from a lesskey file.
1268 * Integers are stored in a funny format:
1269 * two bytes, low order first, in radix KRADIX.
1270 */
gint(unsigned char ** sp)1271 static size_t gint(unsigned char **sp)
1272 {
1273 size_t n;
1274
1275 n = *(*sp)++;
1276 n += *(*sp)++ * KRADIX;
1277 return (n);
1278 }
1279
1280 /*
1281 * Process a new (post-v241) lesskey file.
1282 */
new_lesskey(unsigned char * buf,size_t len,lbool sysvar)1283 static int new_lesskey(unsigned char *buf, size_t len, lbool sysvar)
1284 {
1285 unsigned char *p;
1286 unsigned char *end;
1287 int c;
1288 size_t n;
1289
1290 /*
1291 * New-style lesskey file.
1292 * Extract the pieces.
1293 */
1294 if (buf[len-3] != C0_END_LESSKEY_MAGIC ||
1295 buf[len-2] != C1_END_LESSKEY_MAGIC ||
1296 buf[len-1] != C2_END_LESSKEY_MAGIC)
1297 return (-1);
1298 p = buf + 4;
1299 end = buf + len;
1300 for (;;)
1301 {
1302 c = *p++;
1303 switch (c)
1304 {
1305 case CMD_SECTION:
1306 n = gint(&p);
1307 if (p+n >= end)
1308 return (-1);
1309 add_fcmd_table(p, n);
1310 p += n;
1311 break;
1312 case EDIT_SECTION:
1313 n = gint(&p);
1314 if (p+n >= end)
1315 return (-1);
1316 add_ecmd_table(p, n);
1317 p += n;
1318 break;
1319 case VAR_SECTION:
1320 n = gint(&p);
1321 if (p+n >= end)
1322 return (-1);
1323 if (sysvar)
1324 add_sysvar_table(p, n);
1325 else
1326 add_uvar_table(p, n);
1327 p += n;
1328 break;
1329 case END_SECTION:
1330 return (0);
1331 default:
1332 /*
1333 * Unrecognized section type.
1334 */
1335 return (-1);
1336 }
1337 }
1338 }
1339
1340 /*
1341 * Set up a user command table, based on a "lesskey" file.
1342 */
lesskey(constant char * filename,lbool sysvar)1343 public int lesskey(constant char *filename, lbool sysvar)
1344 {
1345 unsigned char *buf;
1346 POSITION len;
1347 ssize_t n;
1348 int f;
1349
1350 if (!secure_allow(SF_LESSKEY) || !isnullenv(no_config))
1351 return (1);
1352 /*
1353 * Try to open the lesskey file.
1354 */
1355 f = open(filename, OPEN_READ);
1356 if (f < 0)
1357 return (1);
1358
1359 /*
1360 * Read the file into a buffer.
1361 * We first figure out the size of the file and allocate space for it.
1362 * {{ Minimal error checking is done here.
1363 * A garbage .less file will produce strange results.
1364 * To avoid a large amount of error checking code here, we
1365 * rely on the lesskey program to generate a good .less file. }}
1366 */
1367 len = filesize(f);
1368 if (len == NULL_POSITION || len < 3)
1369 {
1370 /*
1371 * Bad file (valid file must have at least 3 chars).
1372 */
1373 close(f);
1374 return (-1);
1375 }
1376 if ((buf = (unsigned char *) calloc((size_t)len, sizeof(char))) == NULL)
1377 {
1378 close(f);
1379 return (-1);
1380 }
1381 if (less_lseek(f, (less_off_t)0, SEEK_SET) == BAD_LSEEK)
1382 {
1383 free(buf);
1384 close(f);
1385 return (-1);
1386 }
1387 n = read(f, buf, (size_t) len);
1388 close(f);
1389 if (n != len)
1390 {
1391 free(buf);
1392 return (-1);
1393 }
1394
1395 /*
1396 * Verify lesskey file header.
1397 */
1398 if (len < 4 ||
1399 buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC ||
1400 buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC)
1401 return (-1);
1402 return (new_lesskey(buf, (size_t) len, sysvar));
1403 }
1404
1405 #if HAVE_LESSKEYSRC
lesskey_text(constant char * filename,lbool sysvar,lbool content)1406 static int lesskey_text(constant char *filename, lbool sysvar, lbool content)
1407 {
1408 int r;
1409 static struct lesskey_tables tables;
1410
1411 if (!secure_allow(SF_LESSKEY) || !isnullenv(no_config))
1412 return (1);
1413 r = content ? parse_lesskey_content(filename, &tables) : parse_lesskey(filename, &tables);
1414 if (r != 0)
1415 return (r);
1416 add_fcmd_table(tables.cmdtable.buf.data, tables.cmdtable.buf.end);
1417 add_ecmd_table(tables.edittable.buf.data, tables.edittable.buf.end);
1418 if (sysvar)
1419 add_sysvar_table(tables.vartable.buf.data, tables.vartable.buf.end);
1420 else
1421 add_uvar_table(tables.vartable.buf.data, tables.vartable.buf.end);
1422 return (0);
1423 }
1424
lesskey_src(constant char * filename,lbool sysvar)1425 public int lesskey_src(constant char *filename, lbool sysvar)
1426 {
1427 return lesskey_text(filename, sysvar, FALSE);
1428 }
1429
lesskey_content(constant char * content,lbool sysvar)1430 public int lesskey_content(constant char *content, lbool sysvar)
1431 {
1432 return lesskey_text(content, sysvar, TRUE);
1433 }
1434
lesskey_parse_error(constant char * s)1435 public void lesskey_parse_error(constant char *s)
1436 {
1437 PARG parg;
1438 parg.p_string = s;
1439 error("%s", &parg);
1440 }
1441 #endif /* HAVE_LESSKEYSRC */
1442
1443 /*
1444 * Add a lesskey file.
1445 */
add_hometable(int (* call_lesskey)(constant char *,lbool),constant char * envname,constant char * def_filename,lbool sysvar)1446 static int add_hometable(int (*call_lesskey)(constant char *, lbool), constant char *envname, constant char *def_filename, lbool sysvar)
1447 {
1448 char *filename = NULL;
1449 constant char *efilename;
1450 int r;
1451
1452 #if LESSTEST
1453 if (is_lesstest()) /* Don't use lesskey files in lesstest */
1454 return -1;
1455 #endif
1456 if (envname != NULL && (efilename = lgetenv(envname)) != NULL)
1457 filename = save(efilename);
1458 else if (sysvar) /* def_filename is full path */
1459 filename = save(def_filename);
1460 else /* def_filename is just basename */
1461 {
1462 /* Remove first char (normally a dot) unless stored in $HOME. */
1463 constant char *xdg = lgetenv("XDG_CONFIG_HOME");
1464 if (!isnullenv(xdg))
1465 filename = dirfile(xdg, &def_filename[1], TRUE);
1466 if (filename == NULL)
1467 {
1468 constant char *home = lgetenv("HOME");
1469 if (!isnullenv(home))
1470 {
1471 char *cfg_dir = dirfile(home, ".config", FALSE);
1472 filename = dirfile(cfg_dir, &def_filename[1], TRUE);
1473 free(cfg_dir);
1474 }
1475 }
1476 if (filename == NULL)
1477 filename = homefile(def_filename);
1478 }
1479 if (filename == NULL)
1480 return -1;
1481 r = (*call_lesskey)(filename, sysvar);
1482 free(filename);
1483 return (r);
1484 }
1485
1486 /*
1487 * Add the content of a lesskey source file.
1488 */
add_content_table(int (* call_lesskey)(constant char *,lbool),constant char * envname,lbool sysvar)1489 static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar)
1490 {
1491 constant char *content;
1492
1493 (void) call_lesskey; /* not used */
1494 content = lgetenv(envname);
1495 if (isnullenv(content))
1496 return;
1497 lesskey_content(content, sysvar);
1498 }
1499 #endif /* USERFILE */
1500
1501 /*
1502 * See if a char is a special line-editing command.
1503 */
editchar(char c,int flags)1504 public int editchar(char c, int flags)
1505 {
1506 int action;
1507 int nch;
1508 constant char *s;
1509 char usercmd[MAX_CMDLEN+1];
1510
1511 /*
1512 * An editing character could actually be a sequence of characters;
1513 * for example, an escape sequence sent by pressing the uparrow key.
1514 * To match the editing string, we use the command decoder
1515 * but give it the edit-commands command table
1516 * This table is constructed to match the user's keyboard.
1517 */
1518 if (c == erase_char || c == erase2_char)
1519 return (EC_BACKSPACE);
1520 if (c == kill_char)
1521 {
1522 #if MSDOS_COMPILER==WIN32C
1523 if (!win32_kbhit())
1524 #endif
1525 return (EC_LINEKILL);
1526 }
1527
1528 /*
1529 * Collect characters in a buffer.
1530 * Start with the one we have, and get more if we need them.
1531 */
1532 nch = 0;
1533 do {
1534 if (nch > 0)
1535 c = getcc();
1536 usercmd[nch] = c;
1537 usercmd[nch+1] = '\0';
1538 nch++;
1539 action = ecmd_decode(usercmd, &s);
1540 } while (action == A_PREFIX && nch < MAX_CMDLEN);
1541
1542 if (action == EC_X11MOUSE)
1543 return (x11mouse_action(TRUE));
1544 if (action == EC_X116MOUSE)
1545 return (x116mouse_action(TRUE));
1546
1547 if (flags & ECF_NORIGHTLEFT)
1548 {
1549 switch (action)
1550 {
1551 case EC_RIGHT:
1552 case EC_LEFT:
1553 action = A_INVALID;
1554 break;
1555 }
1556 }
1557 #if CMD_HISTORY
1558 if (flags & ECF_NOHISTORY)
1559 {
1560 /*
1561 * The caller says there is no history list.
1562 * Reject any history-manipulation action.
1563 */
1564 switch (action)
1565 {
1566 case EC_UP:
1567 case EC_DOWN:
1568 action = A_INVALID;
1569 break;
1570 }
1571 }
1572 #endif
1573 if (flags & ECF_NOCOMPLETE)
1574 {
1575 /*
1576 * The caller says we don't want any filename completion cmds.
1577 * Reject them.
1578 */
1579 switch (action)
1580 {
1581 case EC_F_COMPLETE:
1582 case EC_B_COMPLETE:
1583 case EC_EXPAND:
1584 action = A_INVALID;
1585 break;
1586 }
1587 }
1588 if ((flags & ECF_PEEK) || action == A_INVALID)
1589 {
1590 /*
1591 * We're just peeking, or we didn't understand the command.
1592 * Unget all the characters we read in the loop above.
1593 * This does NOT include the original character that was
1594 * passed in as a parameter.
1595 */
1596 while (nch > 1)
1597 {
1598 ungetcc(usercmd[--nch]);
1599 }
1600 } else
1601 {
1602 if (s != NULL)
1603 ungetsc(s);
1604 }
1605 return action;
1606 }
1607
1608