1 /* infokey.c -- compile ~/.infokey to ~/.info.
2 $Id: infokey.c,v 1.10 2002/03/19 14:36:49 karl Exp $
4 Copyright (C) 1999, 2001, 02 Free Software Foundation, Inc.
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)
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.
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.
20 Written by Andrew Bettison <andrewb@zip.com.au>. */
28 static char *program_name = "infokey";
30 /* Non-zero means print version info only. */
31 static int print_version_p = 0;
33 /* Non-zero means print a short description of the options. */
34 static int print_help_p = 0;
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;
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;
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[] =
49 {"output", 1, 0, 'o'},
50 {"help", 0, &print_help_p, 1},
51 {"version", 0, &print_version_p, 1},
55 /* String describing the shorthand versions of the long options found above. */
56 static char *short_options = "o:";
58 /* Structure for holding the compiled sections. */
68 unsigned char data[INFOKEY_MAX_SECTIONLEN];
71 /* Some "forward" declarations. */
72 static char *mkpath ();
73 static int compile (), write_infokey_file ();
74 static void syntax_error (), error_message (), suggest_help (), short_help ();
77 /* **************************************************************** */
79 /* Main Entry Point to the Infokey Program */
81 /* **************************************************************** */
88 int getopt_long_index; /* Index returned by getopt_long (). */
89 NODE *initial_node; /* First node loaded by Info. */
92 /* Set locale via LC_ALL. */
93 setlocale (LC_ALL, "");
96 /* Set the text message domain. */
97 bindtextdomain (PACKAGE, LOCALEDIR);
102 int option_character;
104 option_character = getopt_long
105 (argc, argv, short_options, long_options, &getopt_long_index);
107 /* getopt_long () returns EOF when there are no more long options. */
108 if (option_character == EOF)
111 /* If this is a long option, then get the short version of it. */
112 if (option_character == 0 && long_options[getopt_long_index].flag == 0)
113 option_character = long_options[getopt_long_index].val;
115 /* Case on the option that we have received. */
116 switch (option_character)
121 /* User is specifying the name of a file to output to. */
124 free (output_filename);
125 output_filename = xstrdup (optarg);
134 /* If the user specified --version, then show the version and exit. */
137 printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION);
139 printf (_ ("Copyright (C) %s Free Software Foundation, Inc.\n\
140 There is NO warranty. You may redistribute this software\n\
141 under the terms of the GNU General Public License.\n\
142 For more information about these matters, see the files named COPYING.\n"),
147 /* If the `--help' option was present, show the help and exit. */
154 /* If there is one argument remaining, it is the name of the input
156 if (optind == argc - 1)
159 free (input_filename);
160 input_filename = xstrdup (argv[optind]);
162 else if (optind != argc)
164 error_message (0, _("incorrect number of arguments"));
169 /* Use default filenames where none given. */
173 homedir = getenv ("HOME");
179 input_filename = mkpath (homedir, INFOKEY_SRCFILE);
180 if (!output_filename)
181 output_filename = mkpath (homedir, INFOKEY_FILE);
188 static struct sect sections[3];
190 /* Open the input file. */
191 inf = fopen (input_filename, "r");
194 error_message (errno, _("cannot open input file `%s'"), input_filename);
198 /* Compile the input file to its verious sections, then write the
199 section data to the output file. */
201 if (compile (inf, input_filename, sections))
203 /* Open the output file. */
204 outf = fopen (output_filename, FOPEN_WBIN);
207 error_message (errno, _("cannot create output file `%s'"), output_filename);
211 /* Write the contents of the output file and close it. If there is
212 an error writing to the file, delete it and exit with a failure
215 if (!write_infokey_file (outf, sections))
217 error_message (errno, _("error writing to `%s'"), output_filename);
220 if (fclose (outf) == EOF)
222 error_message (errno, _("error closing output file `%s'"), output_filename);
227 unlink (output_filename);
232 /* Close the input file. */
246 p = xmalloc (strlen (dir) + 1 + strlen (file) + 2);
254 /* Compilation - the real work.
258 The source file is a line-based text file with the following
267 ^a invalid # just beep
274 ^a echo-area-beg-of-line
275 ^e echo-area-end-of-line
276 \kr echo-area-forward
277 \kl echo-area-backward
278 \kh echo-area-beg-of-line
279 \ke echo-area-end-of-line
285 Lines starting with '#' are comments, and are ignored. Blank
286 lines are ignored. Each section is introduced by one of the
293 The sections may occur in any order. Each section may be
294 omitted completely. If the 'info' section is the first in the
295 file, its '#info' line may be omitted.
297 The 'info' and 'echo-area' sections
298 -----------------------------------
299 Each line in the 'info' or 'echo-area' sections has the
302 key-sequence SPACE action-name [ SPACE [ # comment ] ] \n
304 Where SPACE is one or more white space characters excluding
305 newline, "action-name" is the name of a GNU Info command,
306 "comment" is any sequence of characters excluding newline, and
307 "key-sequence" is a concatenation of one or more key definitions
308 using the following syntax:
310 1. A carat ^ followed by one character indicates a single
313 2. A backslash \ followed by one, two, or three octal
314 digits indicates a single character having that ASCII
317 3. \n indicates a single NEWLINE;
318 \e indicates a single ESC;
319 \r indicates a single CR;
320 \t indicates a single TAB;
321 \b indicates a single BACKSPACE;
323 4. \ku indicates the Up Arrow key;
324 \kd indicates the Down Arrow key;
325 \kl indicates the Left Arrow key;
326 \kr indicates the Right Arrow key;
327 \kP indicates the Page Up (PRIOR) key;
328 \kN indicates the Page Down (NEXT) key;
329 \kh indicates the Home key;
330 \ke indicates the End key;
331 \kx indicates the DEL key;
332 \k followed by any other character indicates a single
333 control-K, and the following character is interpreted
334 as in rules 1, 2, 3, 5 and 6.
336 5. \m followed by any sequence defined in rules 1, 2, 3, 4
337 or 6 indicates the "Meta" modification of that key.
339 6. A backslash \ followed by any character not described
340 above indicates that character itself. In particular:
341 \\ indicates a single backslash \,
342 \ (backslash-space) indicates a single space,
343 \^ indicates a single caret ^,
345 If the following line:
349 occurs anywhere in an 'info' or 'echo-area' section, that
350 indicates to GNU Info to suppress all of its default key
351 bindings in that context.
355 Each line in the 'var' section has the following syntax:
357 variable-name = value \n
359 Where "variable-name" is the name of a GNU Info variable and
360 "value" is the value that GNU Info will assign to that variable
361 when commencing execution. There must be no white space in the
362 variable name, nor between the variable name and the '='. All
363 characters immediately following the '=', up to but not
364 including the terminating newline, are considered to be the
365 value that will be assigned. In other words, white space
366 following the '=' is not ignored.
369 static int add_to_section (), lookup_action ();
371 /* Compile the input file into its various sections. Return true if no
372 error was encountered.
375 compile (fp, filename, sections)
377 const char *filename;
378 struct sect sections[];
382 unsigned int lnum = 0;
385 /* This parser is a true state machine, with no sneaky fetching
386 of input characters inside the main loop. In other words, all
387 state is fully represented by the following variables:
405 state = start_of_line;
406 enum sect_e section = info;
415 seqstate; /* used if state == get_keyseq */
417 char ocnt; /* used if state == get_keyseq && seqstate == octal */
419 /* Data is accumulated in the following variables. The code
420 avoids overflowing these strings, and throws an error
421 where appropriate if a string limit is exceeded. These string
422 lengths are arbitrary (and should be large enough) and their
423 lengths are not hard-coded anywhere else, so increasing them
424 here will not break anything. */
439 if (slen < sizeof seq) \
440 seq[slen++] = meta ? Meta(c) : (c); \
443 syntax_error(filename, lnum, _("key sequence too long")); \
449 sections[info].cur = 1;
450 sections[info].data[0] = 0;
451 sections[ea].cur = 1;
452 sections[ea].data[0] = 0;
453 sections[var].cur = 0;
455 while (!error && (rescan || (c = fgetc (fp)) != EOF))
463 state = start_of_comment;
483 case start_of_comment:
485 state = in_line_comment;
487 case in_line_comment:
490 state = start_of_line;
491 comment[clen] = '\0';
492 if (strcmp (comment, "info") == 0)
494 else if (strcmp (comment, "echo-area") == 0)
496 else if (strcmp (comment, "var") == 0)
498 else if (strcmp (comment, "stop") == 0
499 && (section == info || section == ea))
500 sections[section].data[0] = 1;
502 else if (clen < sizeof comment - 1)
506 case in_trailing_comment:
508 state = start_of_line;
515 if (c == '\n' || isspace (c))
521 syntax_error (filename, lnum, _("missing key sequence"));
536 case '0': case '1': case '2': case '3':
537 case '4': case '5': case '6': case '7':
567 seqstate = special_key;
570 /* Backslash followed by any other char
571 just means that char. */
581 case '0': case '1': case '2': case '3':
582 case '4': case '5': case '6': case '7':
584 oval = oval * 8 + c - '0';
594 if (seqstate != octal)
600 syntax_error (filename, lnum, _("NUL character (\\000) not permitted"));
610 case 'u': To_seq (SK_UP_ARROW); break;
611 case 'd': To_seq (SK_DOWN_ARROW); break;
612 case 'r': To_seq (SK_RIGHT_ARROW); break;
613 case 'l': To_seq (SK_LEFT_ARROW); break;
614 case 'U': To_seq (SK_PAGE_UP); break;
615 case 'D': To_seq (SK_PAGE_DOWN); break;
616 case 'h': To_seq (SK_HOME); break;
617 case 'e': To_seq (SK_END); break;
618 case 'x': To_seq (SK_DELETE); break;
619 default: To_seq (SK_LITERAL); rescan = 1; break;
626 To_seq (CONTROL (c));
629 syntax_error (filename, lnum, _("NUL character (^%c) not permitted"), c);
638 if (isspace (c) && c != '\n')
644 if (c == '\n' || isspace (c))
652 syntax_error (filename, lnum, _("missing action name"), c);
658 a = lookup_action (act);
663 if (!(add_to_section (§ions[section], seq, slen)
664 && add_to_section (§ions[section], "", 1)
665 && add_to_section (§ions[section], &av, 1)))
667 syntax_error (filename, lnum, _("section too long"));
673 syntax_error (filename, lnum, _("unknown action `%s'"), act);
678 else if (alen < sizeof act - 1)
682 syntax_error (filename, lnum, _("action name too long"));
689 state = in_trailing_comment;
691 state = start_of_line;
692 else if (!isspace (c))
694 syntax_error (filename, lnum, _("extra characters following action `%s'"), act);
704 syntax_error (filename, lnum, _("missing variable name"));
710 else if (c == '\n' || isspace (c))
712 syntax_error (filename, lnum, _("missing `=' immediately after variable name"));
715 else if (varlen < sizeof varn)
719 syntax_error (filename, lnum, _("variable name too long"));
727 state = start_of_line;
728 if (!(add_to_section (§ions[section], varn, varlen)
729 && add_to_section (§ions[section], "", 1)
730 && add_to_section (§ions[section], val, vallen)
731 && add_to_section (§ions[section], "", 1)))
733 syntax_error (filename, lnum, _("section too long"));
737 else if (vallen < sizeof val)
741 syntax_error (filename, lnum, _("value too long"));
753 /* Add some characters to a section's data. Return true if all the
754 characters fit, or false if the section's size limit was exceeded.
757 add_to_section (s, str, len)
762 if (s->cur + len > sizeof s->data)
764 strncpy (s->data + s->cur, str, len);
769 /* Translate from an action name to its numeric code. This uses the
770 auto-generated array in key.c.
773 lookup_action (actname)
778 if (strcmp ("invalid", actname) == 0)
780 for (i = 0; function_key_array[i].name != NULL; i++)
781 if (strcmp (function_key_array[i].name, actname) == 0)
782 return function_key_array[i].code;
786 /* Put an integer to an infokey file.
787 Integers are stored as two bytes, low order first,
788 in radix INFOKEY_RADIX.
795 return fputc (i % INFOKEY_RADIX, fp) != EOF
796 && fputc ((i / INFOKEY_RADIX) % INFOKEY_RADIX, fp) != EOF;
799 /* Write an entire section to an infokey file. If the section is
800 empty, simply omit it.
803 putsect (s, code, fp)
810 return fputc (code, fp) != EOF
811 && putint (s->cur, fp)
812 && fwrite (s->data, s->cur, 1, fp) == 1;
815 /* Write an entire infokey file, given an array containing its sections.
818 write_infokey_file (fp, sections)
820 struct sect sections[];
822 /* Get rid of sections with no effect. */
823 if (sections[info].cur == 1 && sections[info].data[0] == 0)
824 sections[info].cur = 0;
825 if (sections[ea].cur == 1 && sections[ea].data[0] == 0)
826 sections[ea].cur = 0;
828 /* Write all parts of the file out in order (no lseeks),
829 checking for errors all the way. */
830 return fputc (INFOKEY_MAGIC_S0, fp) != EOF
831 && fputc (INFOKEY_MAGIC_S1, fp) != EOF
832 && fputc (INFOKEY_MAGIC_S2, fp) != EOF
833 && fputc (INFOKEY_MAGIC_S3, fp) != EOF
834 && fputs (VERSION, fp) != EOF
835 && fputc ('\0', fp) != EOF
836 && putsect (§ions[info], INFOKEY_SECTION_INFO, fp)
837 && putsect (§ions[ea], INFOKEY_SECTION_EA, fp)
838 && putsect (§ions[var], INFOKEY_SECTION_VAR, fp)
839 && fputc (INFOKEY_MAGIC_E0, fp) != EOF
840 && fputc (INFOKEY_MAGIC_E1, fp) != EOF
841 && fputc (INFOKEY_MAGIC_E2, fp) != EOF
842 && fputc (INFOKEY_MAGIC_E3, fp) != EOF;
846 /* Error handling. */
848 /* Give the user a "syntax error" message in the form
849 progname: "filename", line N: message
852 error_message (error_code, fmt, a1, a2, a3, a4)
855 const void *a1, *a2, *a3, *a4;
857 fprintf (stderr, "%s: ", program_name);
858 fprintf (stderr, fmt, a1, a2, a3, a4);
860 fprintf (stderr, " - %s", strerror (error_code));
861 fprintf (stderr, "\n");
864 /* Give the user a generic error message in the form
868 syntax_error (filename, linenum, fmt, a1, a2, a3, a4)
869 const char *filename;
870 unsigned int linenum;
872 const void *a1, *a2, *a3, *a4;
874 fprintf (stderr, "%s: ", program_name);
875 fprintf (stderr, _("\"%s\", line %u: "), filename, linenum);
876 fprintf (stderr, fmt, a1, a2, a3, a4);
877 fprintf (stderr, "\n");
880 /* Produce a gentle rtfm. */
884 fprintf (stderr, _("Try --help for more information.\n"));
887 /* Produce a scaled down description of the available options to Info. */
892 Usage: %s [OPTION]... [INPUT-FILE]\n\
894 Compile infokey source file to infokey file. Reads INPUT-FILE (default\n\
895 $HOME/.infokey) and writes compiled key file to (by default) $HOME/.info.\n\
898 --output FILE output to FILE instead of $HOME/.info\n\
899 --help display this help and exit.\n\
900 --version display version information and exit.\n\
904 Email bug reports to bug-texinfo@gnu.org,\n\
905 general questions and discussion to help-texinfo@gnu.org.\n\
906 Texinfo home page: http://www.gnu.org/software/texinfo/"));