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