Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / miscellany / less-177 / decode.c
1 /*
2  * Routines to decode user commands.
3  *
4  * This is all table driven.
5  * A command table is a sequence of command descriptors.
6  * Each command descriptor is a sequence of bytes with the following format:
7  *      <c1><c2>...<cN><0><action>
8  * The characters c1,c2,...,cN are the command string; that is,
9  * the characters which the user must type.
10  * It is terminated by a null <0> byte.
11  * The byte after the null byte is the action code associated
12  * with the command string.
13  * If an action byte is OR-ed with A_EXTRA, this indicates
14  * that the option byte is followed by an extra string.
15  *
16  * There may be many command tables.
17  * The first (default) table is built-in.
18  * Other tables are read in from "lesskey" files.
19  * All the tables are linked together and are searched in order.
20  */
21
22 #include "less.h"
23 #include "cmd.h"
24 #if __MSDOS__
25 #include <io.h>
26 #include <stdlib.h>
27 #endif
28
29 /*
30  * Command table is ordered roughly according to expected
31  * frequency of use, so the common commands are near the beginning.
32  */
33 static char cmdtable[] =
34 {
35 #if __MSDOS__
36         /*
37          * PC function keys.
38          * Note that '\0' is converted to '\200' on input.
39          */
40         '\200','\120',0,                A_F_LINE,               /* down arrow */
41         '\200','\121',0,                A_F_SCREEN,             /* page down */
42         '\200','\110',0,                A_B_LINE,               /* up arrow */
43         '\200','\111',0,                A_B_SCREEN,             /* page up */
44         '\200','\107',0,                A_GOLINE,               /* home */
45         '\200','\117',0,                A_GOEND,                /* end */
46         '\200','\073',0,                A_HELP,                 /* F1 */
47         '\200','\104',0,                A_MODIFY_WINDOW,        /* F10 */
48         '\200','\103',0,                A_MODIFY_COLOURS,       /* F9 */
49 #endif
50         '\r',0,                         A_F_LINE,
51         '\n',0,                         A_F_LINE,
52         'e',0,                          A_F_LINE,
53         'j',0,                          A_F_LINE,
54         CONTROL('E'),0,                 A_F_LINE,
55         CONTROL('N'),0,                 A_F_LINE,
56         'k',0,                          A_B_LINE,
57         'y',0,                          A_B_LINE,
58         CONTROL('Y'),0,                 A_B_LINE,
59         CONTROL('K'),0,                 A_B_LINE,
60         CONTROL('P'),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         'b',0,                          A_B_SCREEN,
73         CONTROL('B'),0,                 A_B_SCREEN,
74         ESC,'v',0,                      A_B_SCREEN,
75         'z',0,                          A_F_WINDOW,
76         'w',0,                          A_B_WINDOW,
77         'F',0,                          A_F_FOREVER,
78         'R',0,                          A_FREPAINT,
79         'r',0,                          A_REPAINT,
80         CONTROL('R'),0,                 A_REPAINT,
81         CONTROL('L'),0,                 A_REPAINT,
82         'g',0,                          A_GOLINE,
83         '<',0,                          A_GOLINE,
84         ESC,'<',0,                      A_GOLINE,
85         'p',0,                          A_PERCENT,
86         '%',0,                          A_PERCENT,
87         '{',0,                          A_F_BRACKET|A_EXTRA,    '{','}',0,
88         '}',0,                          A_B_BRACKET|A_EXTRA,    '{','}',0,
89         '(',0,                          A_F_BRACKET|A_EXTRA,    '(',')',0,
90         ')',0,                          A_B_BRACKET|A_EXTRA,    '(',')',0,
91         '[',0,                          A_F_BRACKET|A_EXTRA,    '[',']',0,
92         ']',0,                          A_B_BRACKET|A_EXTRA,    '[',']',0,
93         ESC,CONTROL('F'),0,             A_F_BRACKET,
94         ESC,CONTROL('B'),0,             A_B_BRACKET,
95         'G',0,                          A_GOEND,
96         ESC,'>',0,                      A_GOEND,
97         '>',0,                          A_GOEND,
98         'P',0,                          A_GOPOS,
99
100         '0',0,                          A_DIGIT,
101         '1',0,                          A_DIGIT,
102         '2',0,                          A_DIGIT,
103         '3',0,                          A_DIGIT,
104         '4',0,                          A_DIGIT,
105         '5',0,                          A_DIGIT,
106         '6',0,                          A_DIGIT,
107         '7',0,                          A_DIGIT,
108         '8',0,                          A_DIGIT,
109         '9',0,                          A_DIGIT,
110
111         '=',0,                          A_STAT,
112         CONTROL('G'),0,                 A_STAT,
113         ':','f',0,                      A_STAT,
114         '/',0,                          A_F_SEARCH,
115         '?',0,                          A_B_SEARCH,
116         ESC,'/',0,                      A_F_SEARCH|A_EXTRA,     '*',0,
117         ESC,'?',0,                      A_B_SEARCH|A_EXTRA,     '*',0,
118         'n',0,                          A_AGAIN_SEARCH,
119         ESC,'n',0,                      A_T_AGAIN_SEARCH,
120         'N',0,                          A_REVERSE_SEARCH,
121         ESC,'N',0,                      A_T_REVERSE_SEARCH,
122         'm',0,                          A_SETMARK,
123         '\'',0,                         A_GOMARK,
124         CONTROL('X'),CONTROL('X'),0,    A_GOMARK,
125         'E',0,                          A_EXAMINE,
126         ':','e',0,                      A_EXAMINE,
127         CONTROL('X'),CONTROL('V'),0,    A_EXAMINE,
128         ':','n',0,                      A_NEXT_FILE,
129         ':','p',0,                      A_PREV_FILE,
130         ':','x',0,                      A_INDEX_FILE,
131         '-',0,                          A_OPT_TOGGLE,
132         ':','t',0,                      A_OPT_TOGGLE|A_EXTRA,   't',0,
133         's',0,                          A_OPT_TOGGLE|A_EXTRA,   'o',0,
134         '_',0,                          A_DISP_OPTION,
135         '|',0,                          A_PIPE,
136         'v',0,                          A_VISUAL,
137         '!',0,                          A_SHELL,
138         '+',0,                          A_FIRSTCMD,
139
140         'H',0,                          A_HELP,
141         'h',0,                          A_HELP,
142         'V',0,                          A_VERSION,
143         'q',0,                          A_QUIT,
144         ':','q',0,                      A_QUIT,
145         ':','Q',0,                      A_QUIT,
146         'Z','Z',0,                      A_QUIT,
147         ESC,ESC,0,                      A_QUIT,
148 };
149
150 /*
151  * Structure to support a list of command tables.
152  */
153 struct tablelist
154 {
155         struct tablelist *t_next;
156         char *t_start;
157         char *t_end;
158 };
159
160 /*
161  * Structure for the default command table.
162  */
163 static struct tablelist deftable = 
164         { NULL, cmdtable, cmdtable+sizeof(cmdtable) };
165
166 /*
167  * List of tables; initially contains only the default table.
168  */
169 static struct tablelist *tables = &deftable;
170
171 static int cmd_search();
172
173 extern int erase_char, kill_char;
174
175 /*
176  * Decode a command character and return the associated action.
177  * The "extra" string, if any, is returned in sp.
178  */
179         public int
180 cmd_decode(cmd, sp)
181         char *cmd;
182         char **sp;
183 {
184         register struct tablelist *t;
185         register int action;
186
187         /*
188          * Search thru all the command tables.
189          * Stop when we find an action which is not A_INVALID.
190          */
191         for (t = tables;  t != NULL;  t = t->t_next)
192         {
193                 action = cmd_search(cmd, t->t_start, t->t_end, sp);
194                 if (action != A_INVALID)
195                         break;
196         }
197         return (action);
198 }
199
200 /*
201  * Search a command table for the current command string (in cmd).
202  */
203         static int
204 cmd_search(cmd, table, endtable, sp)
205         char *cmd;
206         char *table;
207         char *endtable;
208         char **sp;
209 {
210         register char *p;
211         register char *q;
212         register int a;
213
214         for (p = table, q = cmd;  p < endtable;  p++, q++)
215         {
216                 if (*p == *q)
217                 {
218                         /*
219                          * Current characters match.
220                          * If we're at the end of the string, we've found it.
221                          * Return the action code, which is the character
222                          * after the null at the end of the string
223                          * in the command table.
224                          */
225                         if (*p == '\0')
226                         {
227                                 a = *++p & 0377;
228                                 /*
229                                  * Check for an "extra" string.
230                                  */
231                                 if (a & A_EXTRA)
232                                 {
233                                         *sp = ++p;
234                                         a &= ~A_EXTRA;
235                                 } else
236                                         *sp = NULL;
237                                 return (a);
238                         }
239                 } else if (*q == '\0')
240                 {
241                         /*
242                          * Hit the end of the user's command,
243                          * but not the end of the string in the command table.
244                          * The user's command is incomplete.
245                          */
246                         return (A_PREFIX);
247                 } else
248                 {
249                         /*
250                          * Not a match.
251                          * Skip ahead to the next command in the
252                          * command table, and reset the pointer
253                          * to the beginning of the user's command.
254                          */
255                         while (*p++ != '\0') ;
256                         if (*p & A_EXTRA)
257                                 while (*++p != '\0') ;
258                         q = cmd-1;
259                 }
260         }
261         /*
262          * No match found in the entire command table.
263          */
264         return (A_INVALID);
265 }
266
267 #if USERFILE
268 /*
269  * Set up a user command table, based on a "lesskey" file.
270  */
271         public int
272 add_cmdtable(filename)
273         char *filename;
274 {
275         register struct tablelist *t;
276         register POSITION len;
277         register long n;
278         register int f;
279
280         /*
281          * Try to open the lesskey file.
282          * If we can't, return an error.
283          */
284         f = open(filename, 0);
285         if (f < 0)
286                 return (-1);
287
288         /*
289          * Read the file into the user table.
290          * We first figure out the size of the file and allocate space for it.
291          * {{ Minimal error checking is done here.
292          *    A garbage .less file will produce strange results.
293          *    To avoid a large amount of error checking code here, we
294          *    rely on the lesskey program to generate a good .less file. }}
295          */
296         len = filesize(f);
297         if (len == NULL_POSITION || len < 3)
298         {
299                 /*
300                  * Bad file (valid file must have at least 3 chars).
301                  */
302                 close(f);
303                 return (-1);
304         }
305         if ((t = (struct tablelist *) 
306                         calloc(1, sizeof(struct tablelist))) == NULL)
307         {
308                 close(f);
309                 return (-1);
310         }
311         if ((t->t_start = (char *) calloc(len, sizeof(char))) == NULL)
312         {
313                 free((char *)t);
314                 close(f);
315                 return (-1);
316         }
317         if (lseek(f, (offset_t)0, 0) == BAD_LSEEK)
318         {
319                 free(t->t_start);
320                 free((char *)t);
321                 close(f);
322                 return (-1);
323         }
324         n = read(f, t->t_start, (unsigned int) len);
325         close(f);
326
327         /*
328          * In a valid lesskey file, the last byte or 
329          * the second to the last byte must be zero.
330          */
331         if (n != len || (t->t_start[n-1] != '\0' && t->t_start[n-2] != '\0'))
332         {
333                 free(t->t_start);
334                 free((char *)t);
335                 return (-1);
336         }
337         t->t_end = t->t_start + n;
338
339         /*
340          * Link it into the list of tables.
341          */
342         t->t_next = tables;
343         tables = t;
344         return (0);
345 }
346
347 /*
348  * Try to add the lesskey file "$HOME/.less"
349  */
350         public void
351 add_hometable()
352 {
353         char *filename;
354
355 #if __MSDOS__
356         filename = homefile("_less");
357 #else
358         filename = homefile(".less");
359 #endif
360         if (filename == NULL)
361                 return;
362         /*
363          * Ignore errors.
364          */
365         (void) add_cmdtable(filename);
366         free(filename);
367 }
368 #endif