1 | /* infokey.c -- compile ~/.infokey to ~/.info.
|
---|
2 | $Id: infokey.c,v 1.9 2004/12/14 00:15:36 karl Exp $
|
---|
3 |
|
---|
4 | Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
|
---|
5 |
|
---|
6 | This program is free software; you can redistribute it and/or modify
|
---|
7 | it under the terms of the GNU General Public License as published by
|
---|
8 | the Free Software Foundation; either version 2, or (at your option)
|
---|
9 | any later version.
|
---|
10 |
|
---|
11 | This program is distributed in the hope that it will be useful,
|
---|
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
14 | GNU General Public License for more details.
|
---|
15 |
|
---|
16 | You should have received a copy of the GNU General Public License
|
---|
17 | along with this program; if not, write to the Free Software
|
---|
18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
---|
19 |
|
---|
20 | Written by Andrew Bettison <andrewb@zip.com.au>. */
|
---|
21 |
|
---|
22 | #include "info.h"
|
---|
23 | #include "infomap.h"
|
---|
24 | #include "infokey.h"
|
---|
25 | #include "key.h"
|
---|
26 | #include "getopt.h"
|
---|
27 |
|
---|
28 | static char *program_name = "infokey";
|
---|
29 |
|
---|
30 | /* Non-zero means print version info only. */
|
---|
31 | static int print_version_p = 0;
|
---|
32 |
|
---|
33 | /* Non-zero means print a short description of the options. */
|
---|
34 | static int print_help_p = 0;
|
---|
35 |
|
---|
36 | /* String specifying the source file. This is set by the user on the
|
---|
37 | command line, or a default is used. */
|
---|
38 | static char *input_filename = (char *) NULL;
|
---|
39 |
|
---|
40 | /* String specifying the name of the file to output to. This is
|
---|
41 | set by the user on the command line, or a default is used. */
|
---|
42 | static char *output_filename = (char *) NULL;
|
---|
43 |
|
---|
44 | /* Structure describing the options that Infokey accepts. We pass this
|
---|
45 | structure to getopt_long (). If you add or otherwise change this
|
---|
46 | structure, you must also change the string which follows it. */
|
---|
47 | static struct option long_options[] =
|
---|
48 | {
|
---|
49 | {"output", 1, 0, 'o'},
|
---|
50 | {"help", 0, &print_help_p, 1},
|
---|
51 | {"version", 0, &print_version_p, 1},
|
---|
52 | {NULL, 0, NULL, 0}
|
---|
53 | };
|
---|
54 |
|
---|
55 | /* String describing the shorthand versions of the long options found above. */
|
---|
56 | static char *short_options = "o:";
|
---|
57 |
|
---|
58 | /* Structure for holding the compiled sections. */
|
---|
59 | enum sect_e
|
---|
60 | {
|
---|
61 | info = 0,
|
---|
62 | ea = 1,
|
---|
63 | var = 2
|
---|
64 | };
|
---|
65 | struct sect
|
---|
66 | {
|
---|
67 | unsigned int cur;
|
---|
68 | unsigned char data[INFOKEY_MAX_SECTIONLEN];
|
---|
69 | };
|
---|
70 |
|
---|
71 | /* Some "forward" declarations. */
|
---|
72 | static char *mkpath (const char *dir, const char *file);
|
---|
73 | static int compile (FILE *fp, const char *filename, struct sect *sections);
|
---|
74 | static int write_infokey_file (FILE *fp, struct sect *sections);
|
---|
75 | static void syntax_error (const char *filename,
|
---|
76 | unsigned int linenum, const char *fmt,
|
---|
77 | const void *a1, const void *a2, const void *a3, const void *a4);
|
---|
78 | static void error_message (int error_code, const char *fmt,
|
---|
79 | const void *a1, const void *a2, const void *a3, const void *a4);
|
---|
80 | static void suggest_help (void);
|
---|
81 | static void short_help (void);
|
---|
82 | |
---|
83 |
|
---|
84 |
|
---|
85 | /* **************************************************************** */
|
---|
86 | /* */
|
---|
87 | /* Main Entry Point to the Infokey Program */
|
---|
88 | /* */
|
---|
89 | /* **************************************************************** */
|
---|
90 |
|
---|
91 | int
|
---|
92 | main (int argc, char **argv)
|
---|
93 | {
|
---|
94 | int getopt_long_index; /* Index returned by getopt_long (). */
|
---|
95 |
|
---|
96 | #ifdef HAVE_SETLOCALE
|
---|
97 | /* Set locale via LC_ALL. */
|
---|
98 | setlocale (LC_ALL, "");
|
---|
99 | #endif
|
---|
100 |
|
---|
101 | #ifdef ENABLE_NLS
|
---|
102 | /* Set the text message domain. */
|
---|
103 | bindtextdomain (PACKAGE, LOCALEDIR);
|
---|
104 | textdomain (PACKAGE);
|
---|
105 | #endif
|
---|
106 |
|
---|
107 | while (1)
|
---|
108 | {
|
---|
109 | int option_character;
|
---|
110 |
|
---|
111 | option_character = getopt_long
|
---|
112 | (argc, argv, short_options, long_options, &getopt_long_index);
|
---|
113 |
|
---|
114 | /* getopt_long () returns EOF when there are no more long options. */
|
---|
115 | if (option_character == EOF)
|
---|
116 | break;
|
---|
117 |
|
---|
118 | /* If this is a long option, then get the short version of it. */
|
---|
119 | if (option_character == 0 && long_options[getopt_long_index].flag == 0)
|
---|
120 | option_character = long_options[getopt_long_index].val;
|
---|
121 |
|
---|
122 | /* Case on the option that we have received. */
|
---|
123 | switch (option_character)
|
---|
124 | {
|
---|
125 | case 0:
|
---|
126 | break;
|
---|
127 |
|
---|
128 | /* User is specifying the name of a file to output to. */
|
---|
129 | case 'o':
|
---|
130 | if (output_filename)
|
---|
131 | free (output_filename);
|
---|
132 | output_filename = xstrdup (optarg);
|
---|
133 | break;
|
---|
134 |
|
---|
135 | default:
|
---|
136 | suggest_help ();
|
---|
137 | xexit (1);
|
---|
138 | }
|
---|
139 | }
|
---|
140 |
|
---|
141 | /* If the user specified --version, then show the version and exit. */
|
---|
142 | if (print_version_p)
|
---|
143 | {
|
---|
144 | printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION);
|
---|
145 | puts ("");
|
---|
146 | printf (_ ("Copyright (C) %s Free Software Foundation, Inc.\n\
|
---|
147 | There is NO warranty. You may redistribute this software\n\
|
---|
148 | under the terms of the GNU General Public License.\n\
|
---|
149 | For more information about these matters, see the files named COPYING.\n"),
|
---|
150 | "2003");
|
---|
151 | xexit (0);
|
---|
152 | }
|
---|
153 |
|
---|
154 | /* If the `--help' option was present, show the help and exit. */
|
---|
155 | if (print_help_p)
|
---|
156 | {
|
---|
157 | short_help ();
|
---|
158 | xexit (0);
|
---|
159 | }
|
---|
160 |
|
---|
161 | /* If there is one argument remaining, it is the name of the input
|
---|
162 | file. */
|
---|
163 | if (optind == argc - 1)
|
---|
164 | {
|
---|
165 | if (input_filename)
|
---|
166 | free (input_filename);
|
---|
167 | input_filename = xstrdup (argv[optind]);
|
---|
168 | }
|
---|
169 | else if (optind != argc)
|
---|
170 | {
|
---|
171 | error_message (0, _("incorrect number of arguments"),
|
---|
172 | NULL, NULL, NULL, NULL);
|
---|
173 | suggest_help ();
|
---|
174 | xexit (1);
|
---|
175 | }
|
---|
176 |
|
---|
177 | /* Use default filenames where none given. */
|
---|
178 | {
|
---|
179 | char *homedir;
|
---|
180 |
|
---|
181 | homedir = getenv ("HOME");
|
---|
182 | #ifdef __MSDOS__
|
---|
183 | if (!homedir)
|
---|
184 | homedir = ".";
|
---|
185 | #endif
|
---|
186 | if (!input_filename)
|
---|
187 | input_filename = mkpath (homedir, INFOKEY_SRCFILE);
|
---|
188 | if (!output_filename)
|
---|
189 | output_filename = mkpath (homedir, INFOKEY_FILE);
|
---|
190 | }
|
---|
191 |
|
---|
192 | {
|
---|
193 | FILE *inf;
|
---|
194 | FILE *outf;
|
---|
195 | int write_error;
|
---|
196 | static struct sect sections[3];
|
---|
197 |
|
---|
198 | /* Open the input file. */
|
---|
199 | inf = fopen (input_filename, "r");
|
---|
200 | if (!inf)
|
---|
201 | {
|
---|
202 | error_message (errno, _("cannot open input file `%s'"),
|
---|
203 | input_filename, NULL, NULL, NULL);
|
---|
204 | xexit (1);
|
---|
205 | }
|
---|
206 |
|
---|
207 | /* Compile the input file to its verious sections, then write the
|
---|
208 | section data to the output file. */
|
---|
209 |
|
---|
210 | if (compile (inf, input_filename, sections))
|
---|
211 | {
|
---|
212 | /* Open the output file. */
|
---|
213 | outf = fopen (output_filename, FOPEN_WBIN);
|
---|
214 | if (!outf)
|
---|
215 | {
|
---|
216 | error_message (errno, _("cannot create output file `%s'"),
|
---|
217 | output_filename, NULL, NULL, NULL);
|
---|
218 | xexit (1);
|
---|
219 | }
|
---|
220 |
|
---|
221 | /* Write the contents of the output file and close it. If there is
|
---|
222 | an error writing to the file, delete it and exit with a failure
|
---|
223 | status. */
|
---|
224 | write_error = 0;
|
---|
225 | if (!write_infokey_file (outf, sections))
|
---|
226 | {
|
---|
227 | error_message (errno, _("error writing to `%s'"),
|
---|
228 | output_filename, NULL, NULL, NULL);
|
---|
229 | write_error = 1;
|
---|
230 | }
|
---|
231 | if (fclose (outf) == EOF)
|
---|
232 | {
|
---|
233 | error_message (errno, _("error closing output file `%s'"),
|
---|
234 | output_filename, NULL, NULL, NULL);
|
---|
235 | write_error = 1;
|
---|
236 | }
|
---|
237 | if (write_error)
|
---|
238 | {
|
---|
239 | unlink (output_filename);
|
---|
240 | xexit (1);
|
---|
241 | }
|
---|
242 | }
|
---|
243 |
|
---|
244 | /* Close the input file. */
|
---|
245 | fclose (inf);
|
---|
246 | }
|
---|
247 |
|
---|
248 | return 0;
|
---|
249 | }
|
---|
250 |
|
---|
251 | static char *
|
---|
252 | mkpath (const char *dir, const char *file)
|
---|
253 | {
|
---|
254 | char *p;
|
---|
255 |
|
---|
256 | p = xmalloc (strlen (dir) + 1 + strlen (file) + 2);
|
---|
257 | strcpy (p, dir);
|
---|
258 | strcat (p, "/");
|
---|
259 | strcat (p, file);
|
---|
260 | return p;
|
---|
261 | }
|
---|
262 | |
---|
263 |
|
---|
264 |
|
---|
265 | /* Compilation - the real work.
|
---|
266 |
|
---|
267 | Source file syntax
|
---|
268 | ------------------
|
---|
269 | The source file is a line-based text file with the following
|
---|
270 | structure:
|
---|
271 |
|
---|
272 | # comments
|
---|
273 | # more comments
|
---|
274 |
|
---|
275 | #info
|
---|
276 | u prev-line
|
---|
277 | d next-line
|
---|
278 | ^a invalid # just beep
|
---|
279 | \ku prev-line
|
---|
280 | #stop
|
---|
281 | \kd next-line
|
---|
282 | q quit # of course!
|
---|
283 |
|
---|
284 | #echo-area
|
---|
285 | ^a echo-area-beg-of-line
|
---|
286 | ^e echo-area-end-of-line
|
---|
287 | \kr echo-area-forward
|
---|
288 | \kl echo-area-backward
|
---|
289 | \kh echo-area-beg-of-line
|
---|
290 | \ke echo-area-end-of-line
|
---|
291 |
|
---|
292 | #var
|
---|
293 | scroll-step=1
|
---|
294 | ISO-Latin=Off
|
---|
295 |
|
---|
296 | Lines starting with '#' are comments, and are ignored. Blank
|
---|
297 | lines are ignored. Each section is introduced by one of the
|
---|
298 | following lines:
|
---|
299 |
|
---|
300 | #info
|
---|
301 | #echo-area
|
---|
302 | #var
|
---|
303 |
|
---|
304 | The sections may occur in any order. Each section may be
|
---|
305 | omitted completely. If the 'info' section is the first in the
|
---|
306 | file, its '#info' line may be omitted.
|
---|
307 |
|
---|
308 | The 'info' and 'echo-area' sections
|
---|
309 | -----------------------------------
|
---|
310 | Each line in the 'info' or 'echo-area' sections has the
|
---|
311 | following syntax:
|
---|
312 |
|
---|
313 | key-sequence SPACE action-name [ SPACE [ # comment ] ] \n
|
---|
314 |
|
---|
315 | Where SPACE is one or more white space characters excluding
|
---|
316 | newline, "action-name" is the name of a GNU Info command,
|
---|
317 | "comment" is any sequence of characters excluding newline, and
|
---|
318 | "key-sequence" is a concatenation of one or more key definitions
|
---|
319 | using the following syntax:
|
---|
320 |
|
---|
321 | 1. A carat ^ followed by one character indicates a single
|
---|
322 | control character;
|
---|
323 |
|
---|
324 | 2. A backslash \ followed by one, two, or three octal
|
---|
325 | digits indicates a single character having that ASCII
|
---|
326 | code;
|
---|
327 |
|
---|
328 | 3. \n indicates a single NEWLINE;
|
---|
329 | \e indicates a single ESC;
|
---|
330 | \r indicates a single CR;
|
---|
331 | \t indicates a single TAB;
|
---|
332 | \b indicates a single BACKSPACE;
|
---|
333 |
|
---|
334 | 4. \ku indicates the Up Arrow key;
|
---|
335 | \kd indicates the Down Arrow key;
|
---|
336 | \kl indicates the Left Arrow key;
|
---|
337 | \kr indicates the Right Arrow key;
|
---|
338 | \kP indicates the Page Up (PRIOR) key;
|
---|
339 | \kN indicates the Page Down (NEXT) key;
|
---|
340 | \kh indicates the Home key;
|
---|
341 | \ke indicates the End key;
|
---|
342 | \kx indicates the DEL key;
|
---|
343 | \k followed by any other character indicates a single
|
---|
344 | control-K, and the following character is interpreted
|
---|
345 | as in rules 1, 2, 3, 5 and 6.
|
---|
346 |
|
---|
347 | 5. \m followed by any sequence defined in rules 1, 2, 3, 4
|
---|
348 | or 6 indicates the "Meta" modification of that key.
|
---|
349 |
|
---|
350 | 6. A backslash \ followed by any character not described
|
---|
351 | above indicates that character itself. In particular:
|
---|
352 | \\ indicates a single backslash \,
|
---|
353 | \ (backslash-space) indicates a single space,
|
---|
354 | \^ indicates a single caret ^,
|
---|
355 |
|
---|
356 | If the following line:
|
---|
357 |
|
---|
358 | #stop
|
---|
359 |
|
---|
360 | occurs anywhere in an 'info' or 'echo-area' section, that
|
---|
361 | indicates to GNU Info to suppress all of its default key
|
---|
362 | bindings in that context.
|
---|
363 |
|
---|
364 | The 'var' section
|
---|
365 | -----------------
|
---|
366 | Each line in the 'var' section has the following syntax:
|
---|
367 |
|
---|
368 | variable-name = value \n
|
---|
369 |
|
---|
370 | Where "variable-name" is the name of a GNU Info variable and
|
---|
371 | "value" is the value that GNU Info will assign to that variable
|
---|
372 | when commencing execution. There must be no white space in the
|
---|
373 | variable name, nor between the variable name and the '='. All
|
---|
374 | characters immediately following the '=', up to but not
|
---|
375 | including the terminating newline, are considered to be the
|
---|
376 | value that will be assigned. In other words, white space
|
---|
377 | following the '=' is not ignored.
|
---|
378 | */
|
---|
379 |
|
---|
380 | static int add_to_section (struct sect *s, const char *str, unsigned int len);
|
---|
381 | static int lookup_action (const char *actname);
|
---|
382 |
|
---|
383 | /* Compile the input file into its various sections. Return true if no
|
---|
384 | error was encountered.
|
---|
385 | */
|
---|
386 | static int
|
---|
387 | compile (FILE *fp, const char *filename, struct sect *sections)
|
---|
388 | {
|
---|
389 | int error = 0;
|
---|
390 | char rescan = 0;
|
---|
391 | unsigned int lnum = 0;
|
---|
392 | int c = 0;
|
---|
393 |
|
---|
394 | /* This parser is a true state machine, with no sneaky fetching
|
---|
395 | of input characters inside the main loop. In other words, all
|
---|
396 | state is fully represented by the following variables:
|
---|
397 | */
|
---|
398 | enum
|
---|
399 | {
|
---|
400 | start_of_line,
|
---|
401 | start_of_comment,
|
---|
402 | in_line_comment,
|
---|
403 | in_trailing_comment,
|
---|
404 | get_keyseq,
|
---|
405 | got_keyseq,
|
---|
406 | get_action,
|
---|
407 | got_action,
|
---|
408 | get_varname,
|
---|
409 | got_varname,
|
---|
410 | get_equals,
|
---|
411 | got_equals,
|
---|
412 | get_value
|
---|
413 | }
|
---|
414 | state = start_of_line;
|
---|
415 | enum sect_e section = info;
|
---|
416 | enum
|
---|
417 | {
|
---|
418 | normal,
|
---|
419 | slosh,
|
---|
420 | control,
|
---|
421 | octal,
|
---|
422 | special_key
|
---|
423 | }
|
---|
424 | seqstate; /* used if state == get_keyseq */
|
---|
425 | char meta = 0;
|
---|
426 | char ocnt = 0; /* used if state == get_keyseq && seqstate == octal */
|
---|
427 |
|
---|
428 | /* Data is accumulated in the following variables. The code
|
---|
429 | avoids overflowing these strings, and throws an error
|
---|
430 | where appropriate if a string limit is exceeded. These string
|
---|
431 | lengths are arbitrary (and should be large enough) and their
|
---|
432 | lengths are not hard-coded anywhere else, so increasing them
|
---|
433 | here will not break anything. */
|
---|
434 | char oval = 0;
|
---|
435 | char comment[10];
|
---|
436 | unsigned int clen = 0;
|
---|
437 | char seq[20];
|
---|
438 | unsigned int slen = 0;
|
---|
439 | char act[80];
|
---|
440 | unsigned int alen = 0;
|
---|
441 | char varn[80];
|
---|
442 | unsigned int varlen = 0;
|
---|
443 | char val[80];
|
---|
444 | unsigned int vallen = 0;
|
---|
445 |
|
---|
446 | #define To_seq(c) \
|
---|
447 | do { \
|
---|
448 | if (slen < sizeof seq) \
|
---|
449 | seq[slen++] = meta ? Meta(c) : (c); \
|
---|
450 | else \
|
---|
451 | { \
|
---|
452 | syntax_error(filename, lnum, _("key sequence too long"), \
|
---|
453 | NULL, NULL, NULL, NULL); \
|
---|
454 | error = 1; \
|
---|
455 | } \
|
---|
456 | meta = 0; \
|
---|
457 | } while (0)
|
---|
458 |
|
---|
459 | sections[info].cur = 1;
|
---|
460 | sections[info].data[0] = 0;
|
---|
461 | sections[ea].cur = 1;
|
---|
462 | sections[ea].data[0] = 0;
|
---|
463 | sections[var].cur = 0;
|
---|
464 |
|
---|
465 | while (!error && (rescan || (c = fgetc (fp)) != EOF))
|
---|
466 | {
|
---|
467 | rescan = 0;
|
---|
468 | switch (state)
|
---|
469 | {
|
---|
470 | case start_of_line:
|
---|
471 | lnum++;
|
---|
472 | if (c == '#')
|
---|
473 | state = start_of_comment;
|
---|
474 | else if (c != '\n')
|
---|
475 | {
|
---|
476 | switch (section)
|
---|
477 | {
|
---|
478 | case info:
|
---|
479 | case ea:
|
---|
480 | state = get_keyseq;
|
---|
481 | seqstate = normal;
|
---|
482 | slen = 0;
|
---|
483 | break;
|
---|
484 | case var:
|
---|
485 | state = get_varname;
|
---|
486 | varlen = 0;
|
---|
487 | break;
|
---|
488 | }
|
---|
489 | rescan = 1;
|
---|
490 | }
|
---|
491 | break;
|
---|
492 |
|
---|
493 | case start_of_comment:
|
---|
494 | clen = 0;
|
---|
495 | state = in_line_comment;
|
---|
496 | /* fall through */
|
---|
497 | case in_line_comment:
|
---|
498 | if (c == '\n')
|
---|
499 | {
|
---|
500 | state = start_of_line;
|
---|
501 | comment[clen] = '\0';
|
---|
502 | if (strcmp (comment, "info") == 0)
|
---|
503 | section = info;
|
---|
504 | else if (strcmp (comment, "echo-area") == 0)
|
---|
505 | section = ea;
|
---|
506 | else if (strcmp (comment, "var") == 0)
|
---|
507 | section = var;
|
---|
508 | else if (strcmp (comment, "stop") == 0
|
---|
509 | && (section == info || section == ea))
|
---|
510 | sections[section].data[0] = 1;
|
---|
511 | }
|
---|
512 | else if (clen < sizeof comment - 1)
|
---|
513 | comment[clen++] = c;
|
---|
514 | break;
|
---|
515 |
|
---|
516 | case in_trailing_comment:
|
---|
517 | if (c == '\n')
|
---|
518 | state = start_of_line;
|
---|
519 | break;
|
---|
520 |
|
---|
521 | case get_keyseq:
|
---|
522 | switch (seqstate)
|
---|
523 | {
|
---|
524 | case normal:
|
---|
525 | if (c == '\n' || isspace (c))
|
---|
526 | {
|
---|
527 | state = got_keyseq;
|
---|
528 | rescan = 1;
|
---|
529 | if (slen == 0)
|
---|
530 | {
|
---|
531 | syntax_error (filename, lnum, _("missing key sequence"),
|
---|
532 | NULL, NULL, NULL, NULL);
|
---|
533 | error = 1;
|
---|
534 | }
|
---|
535 | }
|
---|
536 | else if (c == '\\')
|
---|
537 | seqstate = slosh;
|
---|
538 | else if (c == '^')
|
---|
539 | seqstate = control;
|
---|
540 | else
|
---|
541 | To_seq (c);
|
---|
542 | break;
|
---|
543 |
|
---|
544 | case slosh:
|
---|
545 | switch (c)
|
---|
546 | {
|
---|
547 | case '0': case '1': case '2': case '3':
|
---|
548 | case '4': case '5': case '6': case '7':
|
---|
549 | seqstate = octal;
|
---|
550 | oval = c - '0';
|
---|
551 | ocnt = 1;
|
---|
552 | break;
|
---|
553 | case 'b':
|
---|
554 | To_seq ('\b');
|
---|
555 | seqstate = normal;
|
---|
556 | break;
|
---|
557 | case 'e':
|
---|
558 | To_seq ('\033');
|
---|
559 | seqstate = normal;
|
---|
560 | break;
|
---|
561 | case 'n':
|
---|
562 | To_seq ('\n');
|
---|
563 | seqstate = normal;
|
---|
564 | break;
|
---|
565 | case 'r':
|
---|
566 | To_seq ('\r');
|
---|
567 | seqstate = normal;
|
---|
568 | break;
|
---|
569 | case 't':
|
---|
570 | To_seq ('\t');
|
---|
571 | seqstate = normal;
|
---|
572 | break;
|
---|
573 | case 'm':
|
---|
574 | meta = 1;
|
---|
575 | seqstate = normal;
|
---|
576 | break;
|
---|
577 | case 'k':
|
---|
578 | seqstate = special_key;
|
---|
579 | break;
|
---|
580 | default:
|
---|
581 | /* Backslash followed by any other char
|
---|
582 | just means that char. */
|
---|
583 | To_seq (c);
|
---|
584 | seqstate = normal;
|
---|
585 | break;
|
---|
586 | }
|
---|
587 | break;
|
---|
588 |
|
---|
589 | case octal:
|
---|
590 | switch (c)
|
---|
591 | {
|
---|
592 | case '0': case '1': case '2': case '3':
|
---|
593 | case '4': case '5': case '6': case '7':
|
---|
594 | if (++ocnt <= 3)
|
---|
595 | oval = oval * 8 + c - '0';
|
---|
596 | if (ocnt == 3)
|
---|
597 | seqstate = normal;
|
---|
598 | break;
|
---|
599 | default:
|
---|
600 | ocnt = 4;
|
---|
601 | seqstate = normal;
|
---|
602 | rescan = 1;
|
---|
603 | break;
|
---|
604 | }
|
---|
605 | if (seqstate != octal)
|
---|
606 | {
|
---|
607 | if (oval)
|
---|
608 | To_seq (oval);
|
---|
609 | else
|
---|
610 | {
|
---|
611 | syntax_error (filename, lnum,
|
---|
612 | _("NUL character (\\000) not permitted"),
|
---|
613 | NULL, NULL, NULL, NULL);
|
---|
614 | error = 1;
|
---|
615 | }
|
---|
616 | }
|
---|
617 | break;
|
---|
618 |
|
---|
619 | case special_key:
|
---|
620 | To_seq (SK_ESCAPE);
|
---|
621 | switch (c)
|
---|
622 | {
|
---|
623 | case 'u': To_seq (SK_UP_ARROW); break;
|
---|
624 | case 'd': To_seq (SK_DOWN_ARROW); break;
|
---|
625 | case 'r': To_seq (SK_RIGHT_ARROW); break;
|
---|
626 | case 'l': To_seq (SK_LEFT_ARROW); break;
|
---|
627 | case 'U': To_seq (SK_PAGE_UP); break;
|
---|
628 | case 'D': To_seq (SK_PAGE_DOWN); break;
|
---|
629 | case 'h': To_seq (SK_HOME); break;
|
---|
630 | case 'e': To_seq (SK_END); break;
|
---|
631 | case 'x': To_seq (SK_DELETE); break;
|
---|
632 | default: To_seq (SK_LITERAL); rescan = 1; break;
|
---|
633 | }
|
---|
634 | seqstate = normal;
|
---|
635 | break;
|
---|
636 |
|
---|
637 | case control:
|
---|
638 | if (CONTROL (c))
|
---|
639 | To_seq (CONTROL (c));
|
---|
640 | else
|
---|
641 | {
|
---|
642 | syntax_error (filename, lnum,
|
---|
643 | (char *) _("NUL character (^%c) not permitted"),
|
---|
644 | (void *) (long) c, NULL, NULL, NULL);
|
---|
645 | error = 1;
|
---|
646 | }
|
---|
647 | seqstate = normal;
|
---|
648 | break;
|
---|
649 | }
|
---|
650 | break;
|
---|
651 |
|
---|
652 | case got_keyseq:
|
---|
653 | if (isspace (c) && c != '\n')
|
---|
654 | break;
|
---|
655 | state = get_action;
|
---|
656 | alen = 0;
|
---|
657 | /* fall through */
|
---|
658 | case get_action:
|
---|
659 | if (c == '\n' || isspace (c))
|
---|
660 | {
|
---|
661 | int a;
|
---|
662 |
|
---|
663 | state = got_action;
|
---|
664 | rescan = 1;
|
---|
665 | if (alen == 0)
|
---|
666 | {
|
---|
667 | syntax_error (filename, lnum, (char *) _("missing action name"),
|
---|
668 | (void *) (long) c, NULL, NULL, NULL);
|
---|
669 | error = 1;
|
---|
670 | }
|
---|
671 | else
|
---|
672 | {
|
---|
673 | act[alen] = '\0';
|
---|
674 | a = lookup_action (act);
|
---|
675 | if (a != -1)
|
---|
676 | {
|
---|
677 | char av = a;
|
---|
678 |
|
---|
679 | if (!(add_to_section (§ions[section], seq, slen)
|
---|
680 | && add_to_section (§ions[section], "", 1)
|
---|
681 | && add_to_section (§ions[section], &av, 1)))
|
---|
682 | {
|
---|
683 | syntax_error (filename, lnum, _("section too long"),
|
---|
684 | NULL, NULL, NULL, NULL);
|
---|
685 | error = 1;
|
---|
686 | }
|
---|
687 | }
|
---|
688 | else
|
---|
689 | {
|
---|
690 | syntax_error (filename, lnum, _("unknown action `%s'"),
|
---|
691 | act, NULL, NULL, NULL);
|
---|
692 | error = 1;
|
---|
693 | }
|
---|
694 | }
|
---|
695 | }
|
---|
696 | else if (alen < sizeof act - 1)
|
---|
697 | act[alen++] = c;
|
---|
698 | else
|
---|
699 | {
|
---|
700 | syntax_error (filename, lnum, _("action name too long"),
|
---|
701 | NULL, NULL, NULL, NULL);
|
---|
702 | error = 1;
|
---|
703 | }
|
---|
704 | break;
|
---|
705 |
|
---|
706 | case got_action:
|
---|
707 | if (c == '#')
|
---|
708 | state = in_trailing_comment;
|
---|
709 | else if (c == '\n')
|
---|
710 | state = start_of_line;
|
---|
711 | else if (!isspace (c))
|
---|
712 | {
|
---|
713 | syntax_error (filename, lnum,
|
---|
714 | _("extra characters following action `%s'"),
|
---|
715 | act, NULL, NULL, NULL);
|
---|
716 | error = 1;
|
---|
717 | }
|
---|
718 | break;
|
---|
719 |
|
---|
720 | case get_varname:
|
---|
721 | if (c == '=')
|
---|
722 | {
|
---|
723 | if (varlen == 0)
|
---|
724 | {
|
---|
725 | syntax_error (filename, lnum, _("missing variable name"),
|
---|
726 | NULL, NULL, NULL, NULL);
|
---|
727 | error = 1;
|
---|
728 | }
|
---|
729 | state = get_value;
|
---|
730 | vallen = 0;
|
---|
731 | }
|
---|
732 | else if (c == '\n' || isspace (c))
|
---|
733 | {
|
---|
734 | syntax_error (filename, lnum,
|
---|
735 | _("missing `=' immediately after variable name"),
|
---|
736 | NULL, NULL, NULL, NULL);
|
---|
737 | error = 1;
|
---|
738 | }
|
---|
739 | else if (varlen < sizeof varn)
|
---|
740 | varn[varlen++] = c;
|
---|
741 | else
|
---|
742 | {
|
---|
743 | syntax_error (filename, lnum, _("variable name too long"),
|
---|
744 | NULL, NULL, NULL, NULL);
|
---|
745 | error = 1;
|
---|
746 | }
|
---|
747 | break;
|
---|
748 |
|
---|
749 | case get_value:
|
---|
750 | if (c == '\n')
|
---|
751 | {
|
---|
752 | state = start_of_line;
|
---|
753 | if (!(add_to_section (§ions[section], varn, varlen)
|
---|
754 | && add_to_section (§ions[section], "", 1)
|
---|
755 | && add_to_section (§ions[section], val, vallen)
|
---|
756 | && add_to_section (§ions[section], "", 1)))
|
---|
757 | {
|
---|
758 | syntax_error (filename, lnum, _("section too long"),
|
---|
759 | NULL, NULL, NULL, NULL);
|
---|
760 | error = 1;
|
---|
761 | }
|
---|
762 | }
|
---|
763 | else if (vallen < sizeof val)
|
---|
764 | val[vallen++] = c;
|
---|
765 | else
|
---|
766 | {
|
---|
767 | syntax_error (filename, lnum, _("value too long"),
|
---|
768 | NULL, NULL, NULL, NULL);
|
---|
769 | error = 1;
|
---|
770 | }
|
---|
771 | break;
|
---|
772 |
|
---|
773 | case get_equals:
|
---|
774 | case got_equals:
|
---|
775 | case got_varname:
|
---|
776 | break;
|
---|
777 | }
|
---|
778 | }
|
---|
779 |
|
---|
780 | #undef To_seq
|
---|
781 |
|
---|
782 | return !error;
|
---|
783 | }
|
---|
784 |
|
---|
785 | /* Add some characters to a section's data. Return true if all the
|
---|
786 | characters fit, or false if the section's size limit was exceeded.
|
---|
787 | */
|
---|
788 | static int
|
---|
789 | add_to_section (struct sect *s, const char *str, unsigned int len)
|
---|
790 | {
|
---|
791 | if (s->cur + len > sizeof s->data)
|
---|
792 | return 0;
|
---|
793 | strncpy ((char *) s->data + s->cur, str, len);
|
---|
794 | s->cur += len;
|
---|
795 | return 1;
|
---|
796 | }
|
---|
797 |
|
---|
798 | /* Translate from an action name to its numeric code. This uses the
|
---|
799 | auto-generated array in key.c.
|
---|
800 | */
|
---|
801 | static int
|
---|
802 | lookup_action (const char *actname)
|
---|
803 | {
|
---|
804 | int i;
|
---|
805 |
|
---|
806 | if (strcmp ("invalid", actname) == 0)
|
---|
807 | return A_INVALID;
|
---|
808 | for (i = 0; function_key_array[i].name != NULL; i++)
|
---|
809 | if (strcmp (function_key_array[i].name, actname) == 0)
|
---|
810 | return function_key_array[i].code;
|
---|
811 | return -1;
|
---|
812 | }
|
---|
813 |
|
---|
814 | /* Put an integer to an infokey file.
|
---|
815 | Integers are stored as two bytes, low order first,
|
---|
816 | in radix INFOKEY_RADIX.
|
---|
817 | */
|
---|
818 | static int
|
---|
819 | putint (int i, FILE *fp)
|
---|
820 | {
|
---|
821 | return fputc (i % INFOKEY_RADIX, fp) != EOF
|
---|
822 | && fputc ((i / INFOKEY_RADIX) % INFOKEY_RADIX, fp) != EOF;
|
---|
823 | }
|
---|
824 |
|
---|
825 | /* Write an entire section to an infokey file. If the section is
|
---|
826 | empty, simply omit it.
|
---|
827 | */
|
---|
828 | static int
|
---|
829 | putsect (struct sect *s, int code, FILE *fp)
|
---|
830 | {
|
---|
831 | if (s->cur == 0)
|
---|
832 | return 1;
|
---|
833 | return fputc (code, fp) != EOF
|
---|
834 | && putint (s->cur, fp)
|
---|
835 | && fwrite (s->data, s->cur, 1, fp) == 1;
|
---|
836 | }
|
---|
837 |
|
---|
838 | /* Write an entire infokey file, given an array containing its sections.
|
---|
839 | */
|
---|
840 | static int
|
---|
841 | write_infokey_file (FILE *fp, struct sect *sections)
|
---|
842 | {
|
---|
843 | /* Get rid of sections with no effect. */
|
---|
844 | if (sections[info].cur == 1 && sections[info].data[0] == 0)
|
---|
845 | sections[info].cur = 0;
|
---|
846 | if (sections[ea].cur == 1 && sections[ea].data[0] == 0)
|
---|
847 | sections[ea].cur = 0;
|
---|
848 |
|
---|
849 | /* Write all parts of the file out in order (no lseeks),
|
---|
850 | checking for errors all the way. */
|
---|
851 | return fputc (INFOKEY_MAGIC_S0, fp) != EOF
|
---|
852 | && fputc (INFOKEY_MAGIC_S1, fp) != EOF
|
---|
853 | && fputc (INFOKEY_MAGIC_S2, fp) != EOF
|
---|
854 | && fputc (INFOKEY_MAGIC_S3, fp) != EOF
|
---|
855 | && fputs (VERSION, fp) != EOF
|
---|
856 | && fputc ('\0', fp) != EOF
|
---|
857 | && putsect (§ions[info], INFOKEY_SECTION_INFO, fp)
|
---|
858 | && putsect (§ions[ea], INFOKEY_SECTION_EA, fp)
|
---|
859 | && putsect (§ions[var], INFOKEY_SECTION_VAR, fp)
|
---|
860 | && fputc (INFOKEY_MAGIC_E0, fp) != EOF
|
---|
861 | && fputc (INFOKEY_MAGIC_E1, fp) != EOF
|
---|
862 | && fputc (INFOKEY_MAGIC_E2, fp) != EOF
|
---|
863 | && fputc (INFOKEY_MAGIC_E3, fp) != EOF;
|
---|
864 | }
|
---|
865 | |
---|
866 |
|
---|
867 |
|
---|
868 | /* Error handling. */
|
---|
869 |
|
---|
870 | /* Give the user a "syntax error" message in the form
|
---|
871 | progname: "filename", line N: message
|
---|
872 | */
|
---|
873 | static void
|
---|
874 | error_message (int error_code, const char *fmt,
|
---|
875 | const void *a1, const void *a2, const void *a3, const void *a4)
|
---|
876 | {
|
---|
877 | fprintf (stderr, "%s: ", program_name);
|
---|
878 | fprintf (stderr, fmt, a1, a2, a3, a4);
|
---|
879 | if (error_code)
|
---|
880 | fprintf (stderr, " - %s", strerror (error_code));
|
---|
881 | fprintf (stderr, "\n");
|
---|
882 | }
|
---|
883 |
|
---|
884 | /* Give the user a generic error message in the form
|
---|
885 | progname: message
|
---|
886 | */
|
---|
887 | static void
|
---|
888 | syntax_error (const char *filename,
|
---|
889 | unsigned int linenum, const char *fmt,
|
---|
890 | const void *a1, const void *a2, const void *a3, const void *a4)
|
---|
891 | {
|
---|
892 | fprintf (stderr, "%s: ", program_name);
|
---|
893 | fprintf (stderr, _("\"%s\", line %u: "), filename, linenum);
|
---|
894 | fprintf (stderr, fmt, a1, a2, a3, a4);
|
---|
895 | fprintf (stderr, "\n");
|
---|
896 | }
|
---|
897 |
|
---|
898 | /* Produce a gentle rtfm. */
|
---|
899 | static void
|
---|
900 | suggest_help (void)
|
---|
901 | {
|
---|
902 | fprintf (stderr, _("Try --help for more information.\n"));
|
---|
903 | }
|
---|
904 |
|
---|
905 | /* Produce a scaled down description of the available options to Info. */
|
---|
906 | static void
|
---|
907 | short_help (void)
|
---|
908 | {
|
---|
909 | printf (_("\
|
---|
910 | Usage: %s [OPTION]... [INPUT-FILE]\n\
|
---|
911 | \n\
|
---|
912 | Compile infokey source file to infokey file. Reads INPUT-FILE (default\n\
|
---|
913 | $HOME/.infokey) and writes compiled key file to (by default) $HOME/.info.\n\
|
---|
914 | \n\
|
---|
915 | Options:\n\
|
---|
916 | --output FILE output to FILE instead of $HOME/.info\n\
|
---|
917 | --help display this help and exit.\n\
|
---|
918 | --version display version information and exit.\n\
|
---|
919 | "), program_name);
|
---|
920 |
|
---|
921 | puts (_("\n\
|
---|
922 | Email bug reports to bug-texinfo@gnu.org,\n\
|
---|
923 | general questions and discussion to help-texinfo@gnu.org.\n\
|
---|
924 | Texinfo home page: http://www.gnu.org/software/texinfo/"));
|
---|
925 |
|
---|
926 | xexit (0);
|
---|
927 | }
|
---|