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