1 |
|
|
/* |
2 |
|
|
* Copyright (C) 1984-2012 Mark Nudelman |
3 |
|
|
* Modified for use with illumos by Garrett D'Amore. |
4 |
|
|
* Copyright 2014 Garrett D'Amore <garrett@damore.org> |
5 |
|
|
* |
6 |
|
|
* You may distribute under the terms of either the GNU General Public |
7 |
|
|
* License or the Less License, as specified in the README file. |
8 |
|
|
* |
9 |
|
|
* For more information, see the README file. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
/* |
13 |
|
|
* lesskey [-o output] [input] |
14 |
|
|
* |
15 |
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
16 |
|
|
* |
17 |
|
|
* Make a .less file. |
18 |
|
|
* If no input file is specified, standard input is used. |
19 |
|
|
* If no output file is specified, $HOME/.less is used. |
20 |
|
|
* |
21 |
|
|
* The .less file is used to specify (to "less") user-defined |
22 |
|
|
* key bindings. Basically any sequence of 1 to MAX_CMDLEN |
23 |
|
|
* keystrokes may be bound to an existing less function. |
24 |
|
|
* |
25 |
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
26 |
|
|
* |
27 |
|
|
* The input file is an ascii file consisting of a |
28 |
|
|
* sequence of lines of the form: |
29 |
|
|
* string <whitespace> action [chars] <newline> |
30 |
|
|
* |
31 |
|
|
* "string" is a sequence of command characters which form |
32 |
|
|
* the new user-defined command. The command |
33 |
|
|
* characters may be: |
34 |
|
|
* 1. The actual character itself. |
35 |
|
|
* 2. A character preceded by ^ to specify a |
36 |
|
|
* control character (e.g. ^X means control-X). |
37 |
|
|
* 3. A backslash followed by one to three octal digits |
38 |
|
|
* to specify a character by its octal value. |
39 |
|
|
* 4. A backslash followed by b, e, n, r or t |
40 |
|
|
* to specify \b, ESC, \n, \r or \t, respectively. |
41 |
|
|
* 5. Any character (other than those mentioned above) preceded |
42 |
|
|
* by a \ to specify the character itself (characters which |
43 |
|
|
* must be preceded by \ include ^, \, and whitespace. |
44 |
|
|
* "action" is the name of a "less" action, from the table below. |
45 |
|
|
* "chars" is an optional sequence of characters which is treated |
46 |
|
|
* as keyboard input after the command is executed. |
47 |
|
|
* |
48 |
|
|
* Blank lines and lines which start with # are ignored, |
49 |
|
|
* except for the special control lines: |
50 |
|
|
* #command Signals the beginning of the command |
51 |
|
|
* keys section. |
52 |
|
|
* #line-edit Signals the beginning of the line-editing |
53 |
|
|
* keys section. |
54 |
|
|
* #env Signals the beginning of the environment |
55 |
|
|
* variable section. |
56 |
|
|
* #stop Stops command parsing in less; |
57 |
|
|
* causes all default keys to be disabled. |
58 |
|
|
* |
59 |
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
60 |
|
|
* |
61 |
|
|
* The output file is a non-ascii file, consisting of a header, |
62 |
|
|
* one or more sections, and a trailer. |
63 |
|
|
* Each section begins with a section header, a section length word |
64 |
|
|
* and the section data. Normally there are three sections: |
65 |
|
|
* CMD_SECTION Definition of command keys. |
66 |
|
|
* EDIT_SECTION Definition of editing keys. |
67 |
|
|
* END_SECTION A special section header, with no |
68 |
|
|
* length word or section data. |
69 |
|
|
* |
70 |
|
|
* Section data consists of zero or more byte sequences of the form: |
71 |
|
|
* string <0> <action> |
72 |
|
|
* or |
73 |
|
|
* string <0> <action|A_EXTRA> chars <0> |
74 |
|
|
* |
75 |
|
|
* "string" is the command string. |
76 |
|
|
* "<0>" is one null byte. |
77 |
|
|
* "<action>" is one byte containing the action code (the A_xxx value). |
78 |
|
|
* If action is ORed with A_EXTRA, the action byte is followed |
79 |
|
|
* by the null-terminated "chars" string. |
80 |
|
|
* |
81 |
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
82 |
|
|
*/ |
83 |
|
|
|
84 |
|
|
#include <err.h> |
85 |
|
|
|
86 |
|
|
#include "cmd.h" |
87 |
|
|
#include "less.h" |
88 |
|
|
#include "lesskey.h" |
89 |
|
|
|
90 |
|
|
struct cmdname { |
91 |
|
|
char *cn_name; |
92 |
|
|
int cn_action; |
93 |
|
|
}; |
94 |
|
|
|
95 |
|
|
static void lkerr(char *); |
96 |
|
|
|
97 |
|
|
struct cmdname cmdnames[] = { |
98 |
|
|
{ "back-bracket", A_B_BRACKET }, |
99 |
|
|
{ "back-line", A_B_LINE }, |
100 |
|
|
{ "back-line-force", A_BF_LINE }, |
101 |
|
|
{ "back-screen", A_B_SCREEN }, |
102 |
|
|
{ "back-scroll", A_B_SCROLL }, |
103 |
|
|
{ "back-search", A_B_SEARCH }, |
104 |
|
|
{ "back-window", A_B_WINDOW }, |
105 |
|
|
{ "debug", A_DEBUG }, |
106 |
|
|
{ "digit", A_DIGIT }, |
107 |
|
|
{ "display-flag", A_DISP_OPTION }, |
108 |
|
|
{ "display-option", A_DISP_OPTION }, |
109 |
|
|
{ "end", A_GOEND }, |
110 |
|
|
{ "examine", A_EXAMINE }, |
111 |
|
|
{ "filter", A_FILTER }, |
112 |
|
|
{ "first-cmd", A_FIRSTCMD }, |
113 |
|
|
{ "firstcmd", A_FIRSTCMD }, |
114 |
|
|
{ "flush-repaint", A_FREPAINT }, |
115 |
|
|
{ "forw-bracket", A_F_BRACKET }, |
116 |
|
|
{ "forw-forever", A_F_FOREVER }, |
117 |
|
|
{ "forw-until-hilite", A_F_UNTIL_HILITE }, |
118 |
|
|
{ "forw-line", A_F_LINE }, |
119 |
|
|
{ "forw-line-force", A_FF_LINE }, |
120 |
|
|
{ "forw-screen", A_F_SCREEN }, |
121 |
|
|
{ "forw-screen-force", A_FF_SCREEN }, |
122 |
|
|
{ "forw-scroll", A_F_SCROLL }, |
123 |
|
|
{ "forw-search", A_F_SEARCH }, |
124 |
|
|
{ "forw-skip", A_F_SKIP }, |
125 |
|
|
{ "forw-window", A_F_WINDOW }, |
126 |
|
|
{ "goto-end", A_GOEND }, |
127 |
|
|
{ "goto-line", A_GOLINE }, |
128 |
|
|
{ "goto-mark", A_GOMARK }, |
129 |
|
|
{ "help", A_HELP }, |
130 |
|
|
{ "index-file", A_INDEX_FILE }, |
131 |
|
|
{ "invalid", A_UINVALID }, |
132 |
|
|
{ "left-scroll", A_LSHIFT }, |
133 |
|
|
{ "next-file", A_NEXT_FILE }, |
134 |
|
|
{ "next-tag", A_NEXT_TAG }, |
135 |
|
|
{ "noaction", A_NOACTION }, |
136 |
|
|
{ "percent", A_PERCENT }, |
137 |
|
|
{ "pipe", A_PIPE }, |
138 |
|
|
{ "prev-file", A_PREV_FILE }, |
139 |
|
|
{ "prev-tag", A_PREV_TAG }, |
140 |
|
|
{ "quit", A_QUIT }, |
141 |
|
|
{ "remove-file", A_REMOVE_FILE }, |
142 |
|
|
{ "repaint", A_REPAINT }, |
143 |
|
|
{ "repaint-flush", A_FREPAINT }, |
144 |
|
|
{ "repeat-search", A_AGAIN_SEARCH }, |
145 |
|
|
{ "repeat-search-all", A_T_AGAIN_SEARCH }, |
146 |
|
|
{ "reverse-search", A_REVERSE_SEARCH }, |
147 |
|
|
{ "reverse-search-all", A_T_REVERSE_SEARCH }, |
148 |
|
|
{ "right-scroll", A_RSHIFT }, |
149 |
|
|
{ "set-mark", A_SETMARK }, |
150 |
|
|
{ "status", A_STAT }, |
151 |
|
|
{ "toggle-flag", A_OPT_TOGGLE }, |
152 |
|
|
{ "toggle-option", A_OPT_TOGGLE }, |
153 |
|
|
{ "undo-hilite", A_UNDO_SEARCH }, |
154 |
|
|
{ "version", A_VERSION }, |
155 |
|
|
{ "visual", A_VISUAL }, |
156 |
|
|
{ NULL, 0 } |
157 |
|
|
}; |
158 |
|
|
|
159 |
|
|
struct cmdname editnames[] = { |
160 |
|
|
{ "back-complete", EC_B_COMPLETE }, |
161 |
|
|
{ "backspace", EC_BACKSPACE }, |
162 |
|
|
{ "delete", EC_DELETE }, |
163 |
|
|
{ "down", EC_DOWN }, |
164 |
|
|
{ "end", EC_END }, |
165 |
|
|
{ "expand", EC_EXPAND }, |
166 |
|
|
{ "forw-complete", EC_F_COMPLETE }, |
167 |
|
|
{ "home", EC_HOME }, |
168 |
|
|
{ "insert", EC_INSERT }, |
169 |
|
|
{ "invalid", EC_UINVALID }, |
170 |
|
|
{ "kill-line", EC_LINEKILL }, |
171 |
|
|
{ "abort", EC_ABORT }, |
172 |
|
|
{ "left", EC_LEFT }, |
173 |
|
|
{ "literal", EC_LITERAL }, |
174 |
|
|
{ "right", EC_RIGHT }, |
175 |
|
|
{ "up", EC_UP }, |
176 |
|
|
{ "word-backspace", EC_W_BACKSPACE }, |
177 |
|
|
{ "word-delete", EC_W_DELETE }, |
178 |
|
|
{ "word-left", EC_W_LEFT }, |
179 |
|
|
{ "word-right", EC_W_RIGHT }, |
180 |
|
|
{ NULL, 0 } |
181 |
|
|
}; |
182 |
|
|
|
183 |
|
|
struct table { |
184 |
|
|
struct cmdname *names; |
185 |
|
|
char *pbuffer; |
186 |
|
|
char buffer[MAX_USERCMD]; |
187 |
|
|
}; |
188 |
|
|
|
189 |
|
|
struct table cmdtable; |
190 |
|
|
struct table edittable; |
191 |
|
|
struct table vartable; |
192 |
|
|
struct table *currtable = &cmdtable; |
193 |
|
|
|
194 |
|
|
char fileheader[] = { |
195 |
|
|
C0_LESSKEY_MAGIC, |
196 |
|
|
C1_LESSKEY_MAGIC, |
197 |
|
|
C2_LESSKEY_MAGIC, |
198 |
|
|
C3_LESSKEY_MAGIC |
199 |
|
|
}; |
200 |
|
|
char filetrailer[] = { |
201 |
|
|
C0_END_LESSKEY_MAGIC, |
202 |
|
|
C1_END_LESSKEY_MAGIC, |
203 |
|
|
C2_END_LESSKEY_MAGIC |
204 |
|
|
}; |
205 |
|
|
char cmdsection[1] = { CMD_SECTION }; |
206 |
|
|
char editsection[1] = { EDIT_SECTION }; |
207 |
|
|
char varsection[1] = { VAR_SECTION }; |
208 |
|
|
char endsection[1] = { END_SECTION }; |
209 |
|
|
|
210 |
|
|
char *infile = NULL; |
211 |
|
|
char *outfile = NULL; |
212 |
|
|
|
213 |
|
|
int linenum; |
214 |
|
|
int errors; |
215 |
|
|
|
216 |
|
|
extern char version[]; |
217 |
|
|
|
218 |
|
|
static void |
219 |
|
|
usage(void) |
220 |
|
|
{ |
221 |
|
|
(void) fprintf(stderr, "usage: lesskey [-o output] [input]\n"); |
222 |
|
|
exit(1); |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
static char * |
226 |
|
|
mkpathname(char *dirname, char *filename) |
227 |
|
|
{ |
228 |
|
|
char *pathname; |
229 |
|
|
size_t len; |
230 |
|
|
|
231 |
|
|
len = strlen(dirname) + strlen(filename) + 2; |
232 |
|
|
pathname = calloc(1, len); |
233 |
|
|
if (pathname == NULL) { |
234 |
|
|
fprintf(stderr, "mkpathname: out of memory\n"); |
235 |
|
|
exit(1); |
236 |
|
|
} |
237 |
|
|
(void) snprintf(pathname, len, "%s/%s", dirname, filename); |
238 |
|
|
return (pathname); |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
/* |
242 |
|
|
* Figure out the name of a default file (in the user's HOME directory). |
243 |
|
|
*/ |
244 |
|
|
char * |
245 |
|
|
homefile(char *filename) |
246 |
|
|
{ |
247 |
|
|
char *p; |
248 |
|
|
char *pathname; |
249 |
|
|
|
250 |
|
|
if ((p = getenv("HOME")) != NULL && *p != '\0') { |
251 |
|
|
pathname = mkpathname(p, filename); |
252 |
|
|
} else { |
253 |
|
|
(void) fprintf(stderr, "cannot find $HOME - " |
254 |
|
|
"using current directory\n"); |
255 |
|
|
pathname = mkpathname(".", filename); |
256 |
|
|
} |
257 |
|
|
return (pathname); |
258 |
|
|
} |
259 |
|
|
|
260 |
|
|
/* |
261 |
|
|
* Parse command line arguments. |
262 |
|
|
*/ |
263 |
|
|
static void |
264 |
|
|
parse_args(int argc, char **argv) |
265 |
|
|
{ |
266 |
|
|
char *arg; |
267 |
|
|
|
268 |
|
|
outfile = NULL; |
269 |
|
|
while (--argc > 0) { |
270 |
|
|
arg = *++argv; |
271 |
|
|
if (arg[0] != '-') |
272 |
|
|
/* Arg does not start with "-"; it's not an option. */ |
273 |
|
|
break; |
274 |
|
|
if (arg[1] == '\0') |
275 |
|
|
/* "-" means standard input. */ |
276 |
|
|
break; |
277 |
|
|
if (arg[1] == '-' && arg[2] == '\0') { |
278 |
|
|
/* "--" means end of options. */ |
279 |
|
|
argc--; |
280 |
|
|
argv++; |
281 |
|
|
break; |
282 |
|
|
} |
283 |
|
|
switch (arg[1]) { |
284 |
|
|
case '-': |
285 |
|
|
if (strncmp(arg, "--output", 8) == 0) { |
286 |
|
|
if (arg[8] == '\0') |
287 |
|
|
outfile = &arg[8]; |
288 |
|
|
else if (arg[8] == '=') |
289 |
|
|
outfile = &arg[9]; |
290 |
|
|
else |
291 |
|
|
usage(); |
292 |
|
|
goto opt_o; |
293 |
|
|
} |
294 |
|
|
if (strcmp(arg, "--version") == 0) { |
295 |
|
|
goto opt_V; |
296 |
|
|
} |
297 |
|
|
usage(); |
298 |
|
|
break; |
299 |
|
|
case 'o': |
300 |
|
|
outfile = &argv[0][2]; |
301 |
|
|
opt_o: |
302 |
|
|
if (*outfile == '\0') { |
303 |
|
|
if (--argc <= 0) |
304 |
|
|
usage(); |
305 |
|
|
outfile = *(++argv); |
306 |
|
|
} |
307 |
|
|
break; |
308 |
|
|
case 'V': |
309 |
|
|
opt_V: |
310 |
|
|
(void) printf("lesskey version %s\n", version); |
311 |
|
|
exit(0); |
312 |
|
|
default: |
313 |
|
|
usage(); |
314 |
|
|
} |
315 |
|
|
} |
316 |
|
|
if (argc > 1) |
317 |
|
|
usage(); |
318 |
|
|
/* |
319 |
|
|
* Open the input file, or use DEF_LESSKEYINFILE if none specified. |
320 |
|
|
*/ |
321 |
|
|
if (argc > 0) |
322 |
|
|
infile = *argv; |
323 |
|
|
else |
324 |
|
|
infile = homefile(DEF_LESSKEYINFILE); |
325 |
|
|
} |
326 |
|
|
|
327 |
|
|
/* |
328 |
|
|
* Initialize data structures. |
329 |
|
|
*/ |
330 |
|
|
static void |
331 |
|
|
init_tables(void) |
332 |
|
|
{ |
333 |
|
|
cmdtable.names = cmdnames; |
334 |
|
|
cmdtable.pbuffer = cmdtable.buffer; |
335 |
|
|
|
336 |
|
|
edittable.names = editnames; |
337 |
|
|
edittable.pbuffer = edittable.buffer; |
338 |
|
|
|
339 |
|
|
vartable.names = NULL; |
340 |
|
|
vartable.pbuffer = vartable.buffer; |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
/* |
344 |
|
|
* Parse one character of a string. |
345 |
|
|
*/ |
346 |
|
|
static char * |
347 |
|
|
tstr(char **pp, int xlate) |
348 |
|
|
{ |
349 |
|
|
char *p; |
350 |
|
|
char ch; |
351 |
|
|
int i; |
352 |
|
|
static char buf[10]; |
353 |
|
|
static char tstr_control_k[] = |
354 |
|
|
{ SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' }; |
355 |
|
|
|
356 |
|
|
p = *pp; |
357 |
|
|
switch (*p) { |
358 |
|
|
case '\\': |
359 |
|
|
++p; |
360 |
|
|
switch (*p) { |
361 |
|
|
case '0': case '1': case '2': case '3': |
362 |
|
|
case '4': case '5': case '6': case '7': |
363 |
|
|
/* |
364 |
|
|
* Parse an octal number. |
365 |
|
|
*/ |
366 |
|
|
ch = 0; |
367 |
|
|
i = 0; |
368 |
|
|
do |
369 |
|
|
ch = 8*ch + (*p - '0'); |
370 |
|
|
while (*++p >= '0' && *p <= '7' && ++i < 3) |
371 |
|
|
; |
372 |
|
|
*pp = p; |
373 |
|
|
if (xlate && ch == CONTROL('K')) |
374 |
|
|
return (tstr_control_k); |
375 |
|
|
buf[0] = ch; |
376 |
|
|
buf[1] = '\0'; |
377 |
|
|
return (buf); |
378 |
|
|
case 'b': |
379 |
|
|
*pp = p+1; |
380 |
|
|
return ("\b"); |
381 |
|
|
case 'e': |
382 |
|
|
*pp = p+1; |
383 |
|
|
buf[0] = ESC; |
384 |
|
|
buf[1] = '\0'; |
385 |
|
|
return (buf); |
386 |
|
|
case 'n': |
387 |
|
|
*pp = p+1; |
388 |
|
|
return ("\n"); |
389 |
|
|
case 'r': |
390 |
|
|
*pp = p+1; |
391 |
|
|
return ("\r"); |
392 |
|
|
case 't': |
393 |
|
|
*pp = p+1; |
394 |
|
|
return ("\t"); |
395 |
|
|
case 'k': |
396 |
|
|
if (xlate) { |
397 |
|
|
switch (*++p) { |
398 |
|
|
case 'u': ch = SK_UP_ARROW; break; |
399 |
|
|
case 'd': ch = SK_DOWN_ARROW; break; |
400 |
|
|
case 'r': ch = SK_RIGHT_ARROW; break; |
401 |
|
|
case 'l': ch = SK_LEFT_ARROW; break; |
402 |
|
|
case 'U': ch = SK_PAGE_UP; break; |
403 |
|
|
case 'D': ch = SK_PAGE_DOWN; break; |
404 |
|
|
case 'h': ch = SK_HOME; break; |
405 |
|
|
case 'e': ch = SK_END; break; |
406 |
|
|
case 'x': ch = SK_DELETE; break; |
407 |
|
|
default: |
408 |
|
|
lkerr("illegal char after \\k"); |
409 |
|
|
*pp = p+1; |
410 |
|
|
return (""); |
411 |
|
|
} |
412 |
|
|
*pp = p+1; |
413 |
|
|
buf[0] = SK_SPECIAL_KEY; |
414 |
|
|
buf[1] = ch; |
415 |
|
|
buf[2] = 6; |
416 |
|
|
buf[3] = 1; |
417 |
|
|
buf[4] = 1; |
418 |
|
|
buf[5] = 1; |
419 |
|
|
buf[6] = '\0'; |
420 |
|
|
return (buf); |
421 |
|
|
} |
422 |
|
|
/* FALLTHRU */ |
423 |
|
|
default: |
424 |
|
|
/* |
425 |
|
|
* Backslash followed by any other char |
426 |
|
|
* just means that char. |
427 |
|
|
*/ |
428 |
|
|
*pp = p+1; |
429 |
|
|
buf[0] = *p; |
430 |
|
|
buf[1] = '\0'; |
431 |
|
|
if (xlate && buf[0] == CONTROL('K')) |
432 |
|
|
return (tstr_control_k); |
433 |
|
|
return (buf); |
434 |
|
|
} |
435 |
|
|
case '^': |
436 |
|
|
/* |
437 |
|
|
* Caret means CONTROL. |
438 |
|
|
*/ |
439 |
|
|
*pp = p+2; |
440 |
|
|
buf[0] = CONTROL(p[1]); |
441 |
|
|
buf[1] = '\0'; |
442 |
|
|
if (buf[0] == CONTROL('K')) |
443 |
|
|
return (tstr_control_k); |
444 |
|
|
return (buf); |
445 |
|
|
} |
446 |
|
|
*pp = p+1; |
447 |
|
|
buf[0] = *p; |
448 |
|
|
buf[1] = '\0'; |
449 |
|
|
if (xlate && buf[0] == CONTROL('K')) |
450 |
|
|
return (tstr_control_k); |
451 |
|
|
return (buf); |
452 |
|
|
} |
453 |
|
|
|
454 |
|
|
/* |
455 |
|
|
* Skip leading spaces in a string. |
456 |
|
|
*/ |
457 |
|
|
char * |
458 |
|
|
skipsp(char *s) |
459 |
|
|
{ |
460 |
|
|
while (*s == ' ' || *s == '\t') |
461 |
|
|
s++; |
462 |
|
|
return (s); |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
/* |
466 |
|
|
* Skip non-space characters in a string. |
467 |
|
|
*/ |
468 |
|
|
static char * |
469 |
|
|
skipnsp(char *s) |
470 |
|
|
{ |
471 |
|
|
while (*s != '\0' && *s != ' ' && *s != '\t') |
472 |
|
|
s++; |
473 |
|
|
return (s); |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
/* |
477 |
|
|
* Clean up an input line: |
478 |
|
|
* strip off the trailing newline & any trailing # comment. |
479 |
|
|
*/ |
480 |
|
|
static char * |
481 |
|
|
clean_line(char *s) |
482 |
|
|
{ |
483 |
|
|
int i; |
484 |
|
|
|
485 |
|
|
s = skipsp(s); |
486 |
|
|
for (i = 0; s[i] != '\n' && s[i] != '\r' && s[i] != '\0'; i++) |
487 |
|
|
if (s[i] == '#' && (i == 0 || s[i-1] != '\\')) |
488 |
|
|
break; |
489 |
|
|
s[i] = '\0'; |
490 |
|
|
return (s); |
491 |
|
|
} |
492 |
|
|
|
493 |
|
|
/* |
494 |
|
|
* Add a byte to the output command table. |
495 |
|
|
*/ |
496 |
|
|
static void |
497 |
|
|
add_cmd_char(int c) |
498 |
|
|
{ |
499 |
|
|
if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD) { |
500 |
|
|
lkerr("too many commands"); |
501 |
|
|
exit(1); |
502 |
|
|
} |
503 |
|
|
*(currtable->pbuffer)++ = (char)c; |
504 |
|
|
} |
505 |
|
|
|
506 |
|
|
/* |
507 |
|
|
* Add a string to the output command table. |
508 |
|
|
*/ |
509 |
|
|
static void |
510 |
|
|
add_cmd_str(char *s) |
511 |
|
|
{ |
512 |
|
|
for (; *s != '\0'; s++) |
513 |
|
|
add_cmd_char(*s); |
514 |
|
|
} |
515 |
|
|
|
516 |
|
|
/* |
517 |
|
|
* See if we have a special "control" line. |
518 |
|
|
*/ |
519 |
|
|
static int |
520 |
|
|
control_line(char *s) |
521 |
|
|
{ |
522 |
|
|
#define PREFIX(str, pat) (strncmp(str, pat, strlen(pat)) == 0) |
523 |
|
|
|
524 |
|
|
if (PREFIX(s, "#line-edit")) { |
525 |
|
|
currtable = &edittable; |
526 |
|
|
return (1); |
527 |
|
|
} |
528 |
|
|
if (PREFIX(s, "#command")) { |
529 |
|
|
currtable = &cmdtable; |
530 |
|
|
return (1); |
531 |
|
|
} |
532 |
|
|
if (PREFIX(s, "#env")) { |
533 |
|
|
currtable = &vartable; |
534 |
|
|
return (1); |
535 |
|
|
} |
536 |
|
|
if (PREFIX(s, "#stop")) { |
537 |
|
|
add_cmd_char('\0'); |
538 |
|
|
add_cmd_char(A_END_LIST); |
539 |
|
|
return (1); |
540 |
|
|
} |
541 |
|
|
return (0); |
542 |
|
|
} |
543 |
|
|
|
544 |
|
|
/* |
545 |
|
|
* Output some bytes. |
546 |
|
|
*/ |
547 |
|
|
static void |
548 |
|
|
fputbytes(FILE *fd, char *buf, int len) |
549 |
|
|
{ |
550 |
|
|
while (len-- > 0) { |
551 |
|
|
(void) fwrite(buf, sizeof (char), 1, fd); |
552 |
|
|
buf++; |
553 |
|
|
} |
554 |
|
|
} |
555 |
|
|
|
556 |
|
|
/* |
557 |
|
|
* Output an integer, in special KRADIX form. |
558 |
|
|
*/ |
559 |
|
|
static void |
560 |
|
|
fputint(FILE *fd, unsigned int val) |
561 |
|
|
{ |
562 |
|
|
char c; |
563 |
|
|
|
564 |
|
|
if (val >= KRADIX*KRADIX) { |
565 |
|
|
(void) fprintf(stderr, "error: integer too big (%d > %d)\n", |
566 |
|
|
val, KRADIX*KRADIX); |
567 |
|
|
exit(1); |
568 |
|
|
} |
569 |
|
|
c = val % KRADIX; |
570 |
|
|
(void) fwrite(&c, sizeof (char), 1, fd); |
571 |
|
|
c = val / KRADIX; |
572 |
|
|
(void) fwrite(&c, sizeof (char), 1, fd); |
573 |
|
|
} |
574 |
|
|
|
575 |
|
|
/* |
576 |
|
|
* Find an action, given the name of the action. |
577 |
|
|
*/ |
578 |
|
|
static int |
579 |
|
|
findaction(char *actname) |
580 |
|
|
{ |
581 |
|
|
int i; |
582 |
|
|
|
583 |
|
|
for (i = 0; currtable->names[i].cn_name != NULL; i++) |
584 |
|
|
if (strcmp(currtable->names[i].cn_name, actname) == 0) |
585 |
|
|
return (currtable->names[i].cn_action); |
586 |
|
|
lkerr("unknown action"); |
587 |
|
|
return (A_INVALID); |
588 |
|
|
} |
589 |
|
|
|
590 |
|
|
static void |
591 |
|
|
lkerr(char *s) |
592 |
|
|
{ |
593 |
|
|
(void) fprintf(stderr, "line %d: %s\n", linenum, s); |
594 |
|
|
errors++; |
595 |
|
|
} |
596 |
|
|
|
597 |
|
|
|
598 |
|
|
static void |
599 |
|
|
parse_cmdline(char *p) |
600 |
|
|
{ |
601 |
|
|
int cmdlen; |
602 |
|
|
char *actname; |
603 |
|
|
int action; |
604 |
|
|
char *s; |
605 |
|
|
char c; |
606 |
|
|
|
607 |
|
|
/* |
608 |
|
|
* Parse the command string and store it in the current table. |
609 |
|
|
*/ |
610 |
|
|
cmdlen = 0; |
611 |
|
|
do { |
612 |
|
|
s = tstr(&p, 1); |
613 |
|
|
cmdlen += strlen(s); |
614 |
|
|
if (cmdlen > MAX_CMDLEN) |
615 |
|
|
lkerr("command too long"); |
616 |
|
|
else |
617 |
|
|
add_cmd_str(s); |
618 |
|
|
} while (*p != ' ' && *p != '\t' && *p != '\0'); |
619 |
|
|
/* |
620 |
|
|
* Terminate the command string with a null byte. |
621 |
|
|
*/ |
622 |
|
|
add_cmd_char('\0'); |
623 |
|
|
|
624 |
|
|
/* |
625 |
|
|
* Skip white space between the command string |
626 |
|
|
* and the action name. |
627 |
|
|
* Terminate the action name with a null byte. |
628 |
|
|
*/ |
629 |
|
|
p = skipsp(p); |
630 |
|
|
if (*p == '\0') { |
631 |
|
|
lkerr("missing action"); |
632 |
|
|
return; |
633 |
|
|
} |
634 |
|
|
actname = p; |
635 |
|
|
p = skipnsp(p); |
636 |
|
|
c = *p; |
637 |
|
|
*p = '\0'; |
638 |
|
|
|
639 |
|
|
/* |
640 |
|
|
* Parse the action name and store it in the current table. |
641 |
|
|
*/ |
642 |
|
|
action = findaction(actname); |
643 |
|
|
|
644 |
|
|
/* |
645 |
|
|
* See if an extra string follows the action name. |
646 |
|
|
*/ |
647 |
|
|
*p = c; |
648 |
|
|
p = skipsp(p); |
649 |
|
|
if (*p == '\0') { |
650 |
|
|
add_cmd_char(action); |
651 |
|
|
} else { |
652 |
|
|
/* |
653 |
|
|
* OR the special value A_EXTRA into the action byte. |
654 |
|
|
* Put the extra string after the action byte. |
655 |
|
|
*/ |
656 |
|
|
add_cmd_char(action | A_EXTRA); |
657 |
|
|
while (*p != '\0') |
658 |
|
|
add_cmd_str(tstr(&p, 0)); |
659 |
|
|
add_cmd_char('\0'); |
660 |
|
|
} |
661 |
|
|
} |
662 |
|
|
|
663 |
|
|
static void |
664 |
|
|
parse_varline(char *p) |
665 |
|
|
{ |
666 |
|
|
char *s; |
667 |
|
|
|
668 |
|
|
do { |
669 |
|
|
s = tstr(&p, 0); |
670 |
|
|
add_cmd_str(s); |
671 |
|
|
} while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0'); |
672 |
|
|
/* |
673 |
|
|
* Terminate the variable name with a null byte. |
674 |
|
|
*/ |
675 |
|
|
add_cmd_char('\0'); |
676 |
|
|
|
677 |
|
|
p = skipsp(p); |
678 |
|
|
if (*p++ != '=') { |
679 |
|
|
lkerr("missing ="); |
680 |
|
|
return; |
681 |
|
|
} |
682 |
|
|
|
683 |
|
|
add_cmd_char(EV_OK|A_EXTRA); |
684 |
|
|
|
685 |
|
|
p = skipsp(p); |
686 |
|
|
while (*p != '\0') { |
687 |
|
|
s = tstr(&p, 0); |
688 |
|
|
add_cmd_str(s); |
689 |
|
|
} |
690 |
|
|
add_cmd_char('\0'); |
691 |
|
|
} |
692 |
|
|
|
693 |
|
|
/* |
694 |
|
|
* Parse a line from the lesskey file. |
695 |
|
|
*/ |
696 |
|
|
static void |
697 |
|
|
parse_line(char *line) |
698 |
|
|
{ |
699 |
|
|
char *p; |
700 |
|
|
|
701 |
|
|
/* |
702 |
|
|
* See if it is a control line. |
703 |
|
|
*/ |
704 |
|
|
if (control_line(line)) |
705 |
|
|
return; |
706 |
|
|
/* |
707 |
|
|
* Skip leading white space. |
708 |
|
|
* Replace the final newline with a null byte. |
709 |
|
|
* Ignore blank lines and comments. |
710 |
|
|
*/ |
711 |
|
|
p = clean_line(line); |
712 |
|
|
if (*p == '\0') |
713 |
|
|
return; |
714 |
|
|
|
715 |
|
|
if (currtable == &vartable) |
716 |
|
|
parse_varline(p); |
717 |
|
|
else |
718 |
|
|
parse_cmdline(p); |
719 |
|
|
} |
720 |
|
|
|
721 |
|
|
int |
722 |
|
|
main(int argc, char **argv) |
723 |
|
|
{ |
724 |
|
|
FILE *desc; |
725 |
|
|
FILE *out; |
726 |
|
|
char line[1024]; |
727 |
|
|
|
728 |
|
|
if (pledge("stdio rpath wpath cpath flock", NULL) == -1) |
729 |
|
|
err(1, "pledge"); |
730 |
|
|
|
731 |
|
|
/* |
732 |
|
|
* Process command line arguments. |
733 |
|
|
*/ |
734 |
|
|
parse_args(argc, argv); |
735 |
|
|
init_tables(); |
736 |
|
|
|
737 |
|
|
/* |
738 |
|
|
* Open the input file. |
739 |
|
|
*/ |
740 |
|
|
if (strcmp(infile, "-") == 0) |
741 |
|
|
desc = stdin; |
742 |
|
|
else if ((desc = fopen(infile, "r")) == NULL) { |
743 |
|
|
perror(infile); |
744 |
|
|
usage(); |
745 |
|
|
} |
746 |
|
|
|
747 |
|
|
/* |
748 |
|
|
* Read and parse the input file, one line at a time. |
749 |
|
|
*/ |
750 |
|
|
errors = 0; |
751 |
|
|
linenum = 0; |
752 |
|
|
while (fgets(line, sizeof (line), desc) != NULL) { |
753 |
|
|
++linenum; |
754 |
|
|
parse_line(line); |
755 |
|
|
} |
756 |
|
|
fclose(desc); |
757 |
|
|
|
758 |
|
|
/* |
759 |
|
|
* Write the output file. |
760 |
|
|
* If no output file was specified, use "$HOME/.less" |
761 |
|
|
*/ |
762 |
|
|
if (errors > 0) { |
763 |
|
|
(void) fprintf(stderr, "%d errors; no output produced\n", |
764 |
|
|
errors); |
765 |
|
|
exit(1); |
766 |
|
|
} |
767 |
|
|
|
768 |
|
|
if (outfile == NULL) |
769 |
|
|
outfile = getenv("LESSKEY"); |
770 |
|
|
if (outfile == NULL) |
771 |
|
|
outfile = homefile(LESSKEYFILE); |
772 |
|
|
if ((out = fopen(outfile, "wb")) == NULL) { |
773 |
|
|
perror(outfile); |
774 |
|
|
exit(1); |
775 |
|
|
} |
776 |
|
|
|
777 |
|
|
/* File header */ |
778 |
|
|
fputbytes(out, fileheader, sizeof (fileheader)); |
779 |
|
|
|
780 |
|
|
/* Command key section */ |
781 |
|
|
fputbytes(out, cmdsection, sizeof (cmdsection)); |
782 |
|
|
fputint(out, cmdtable.pbuffer - cmdtable.buffer); |
783 |
|
|
fputbytes(out, (char *)cmdtable.buffer, |
784 |
|
|
cmdtable.pbuffer-cmdtable.buffer); |
785 |
|
|
|
786 |
|
|
/* Edit key section */ |
787 |
|
|
fputbytes(out, editsection, sizeof (editsection)); |
788 |
|
|
fputint(out, edittable.pbuffer - edittable.buffer); |
789 |
|
|
fputbytes(out, (char *)edittable.buffer, |
790 |
|
|
edittable.pbuffer-edittable.buffer); |
791 |
|
|
|
792 |
|
|
/* Environment variable section */ |
793 |
|
|
fputbytes(out, varsection, sizeof (varsection)); |
794 |
|
|
fputint(out, vartable.pbuffer - vartable.buffer); |
795 |
|
|
fputbytes(out, (char *)vartable.buffer, |
796 |
|
|
vartable.pbuffer-vartable.buffer); |
797 |
|
|
|
798 |
|
|
/* File trailer */ |
799 |
|
|
fputbytes(out, endsection, sizeof (endsection)); |
800 |
|
|
fputbytes(out, filetrailer, sizeof (filetrailer)); |
801 |
|
|
fclose(out); |
802 |
|
|
return (0); |
803 |
|
|
} |