]> diplodocus.org Git - nmh/blob - docs/historical/mh-6.8.5/miscellany/less-177/decode.c
sbr/mts.c: Delete mmdlm2; use same-valued mmdlm1 instead.
[nmh] / 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