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