1 /* infokey.c -- compile ~/.infokey to ~/.info.
2 $Id: infokey.c,v 1.18 2008/06/11 09:55:42 gray Exp $
4 Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 2007, 2008
5 Free Software Foundation, Inc.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Written by Andrew Bettison <andrewb@zip.com.au>. */
28 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
= 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
= 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 (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);
84 /* **************************************************************** */
86 /* Main Entry Point to the Infokey Program */
88 /* **************************************************************** */
91 main (int argc
, char **argv
)
93 int getopt_long_index
; /* Index returned by getopt_long (). */
96 /* Set locale via LC_ALL. */
97 setlocale (LC_ALL
, "");
101 /* Set the text message domain. */
102 bindtextdomain (PACKAGE
, LOCALEDIR
);
103 textdomain (PACKAGE
);
108 int option_character
;
110 option_character
= getopt_long
111 (argc
, argv
, short_options
, long_options
, &getopt_long_index
);
113 /* getopt_long () returns EOF when there are no more long options. */
114 if (option_character
== EOF
)
117 /* If this is a long option, then get the short version of it. */
118 if (option_character
== 0 && long_options
[getopt_long_index
].flag
== 0)
119 option_character
= long_options
[getopt_long_index
].val
;
121 /* Case on the option that we have received. */
122 switch (option_character
)
127 /* User is specifying the name of a file to output to. */
130 free (output_filename
);
131 output_filename
= xstrdup (optarg
);
140 /* If the user specified --version, then show the version and exit. */
143 printf ("%s (GNU %s) %s\n", program_name
, PACKAGE
, VERSION
);
145 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
146 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
147 This is free software: you are free to change and redistribute it.\n\
148 There is NO WARRANTY, to the extent permitted by law.\n"),
153 /* If the `--help' option was present, show the help and exit. */
160 /* If there is one argument remaining, it is the name of the input
162 if (optind
== argc
- 1)
165 free (input_filename
);
166 input_filename
= xstrdup (argv
[optind
]);
168 else if (optind
!= argc
)
170 error_message (0, _("incorrect number of arguments"),
171 NULL
, NULL
, NULL
, NULL
);
176 /* Use default filenames where none given. */
180 homedir
= getenv ("HOME");
186 input_filename
= mkpath (homedir
, INFOKEY_SRCFILE
);
187 if (!output_filename
)
188 output_filename
= mkpath (homedir
, INFOKEY_FILE
);
195 static struct sect sections
[3];
197 /* Open the input file. */
198 inf
= fopen (input_filename
, "r");
201 error_message (errno
, _("cannot open input file `%s'"),
202 input_filename
, NULL
, NULL
, NULL
);
206 /* Compile the input file to its verious sections, then write the
207 section data to the output file. */
209 if (compile (inf
, input_filename
, sections
))
211 /* Open the output file. */
212 outf
= fopen (output_filename
, FOPEN_WBIN
);
215 error_message (errno
, _("cannot create output file `%s'"),
216 output_filename
, NULL
, NULL
, NULL
);
220 /* Write the contents of the output file and close it. If there is
221 an error writing to the file, delete it and exit with a failure
224 if (!write_infokey_file (outf
, sections
))
226 error_message (errno
, _("error writing to `%s'"),
227 output_filename
, NULL
, NULL
, NULL
);
230 if (fclose (outf
) == EOF
)
232 error_message (errno
, _("error closing output file `%s'"),
233 output_filename
, NULL
, NULL
, NULL
);
238 unlink (output_filename
);
243 /* Close the input file. */
251 mkpath (const char *dir
, const char *file
)
255 p
= xmalloc (strlen (dir
) + 1 + strlen (file
) + 2);
263 /* Compilation - the real work.
267 The source file is a line-based text file with the following
276 ^a invalid # just beep
283 ^a echo-area-beg-of-line
284 ^e echo-area-end-of-line
285 \kr echo-area-forward
286 \kl echo-area-backward
287 \kh echo-area-beg-of-line
288 \ke echo-area-end-of-line
294 Lines starting with '#' are comments, and are ignored. Blank
295 lines are ignored. Each section is introduced by one of the
302 The sections may occur in any order. Each section may be
303 omitted completely. If the 'info' section is the first in the
304 file, its '#info' line may be omitted.
306 The 'info' and 'echo-area' sections
307 -----------------------------------
308 Each line in the 'info' or 'echo-area' sections has the
311 key-sequence SPACE action-name [ SPACE [ # comment ] ] \n
313 Where SPACE is one or more white space characters excluding
314 newline, "action-name" is the name of a GNU Info command,
315 "comment" is any sequence of characters excluding newline, and
316 "key-sequence" is a concatenation of one or more key definitions
317 using the following syntax:
319 1. A carat ^ followed by one character indicates a single
322 2. A backslash \ followed by one, two, or three octal
323 digits indicates a single character having that ASCII
326 3. \n indicates a single NEWLINE;
327 \e indicates a single ESC;
328 \r indicates a single CR;
329 \t indicates a single TAB;
330 \b indicates a single BACKSPACE;
332 4. \ku indicates the Up Arrow key;
333 \kd indicates the Down Arrow key;
334 \kl indicates the Left Arrow key;
335 \kr indicates the Right Arrow key;
336 \kP indicates the Page Up (PRIOR) key;
337 \kN indicates the Page Down (NEXT) key;
338 \kh indicates the Home key;
339 \ke indicates the End key;
340 \kx indicates the DEL key;
341 \k followed by any other character indicates a single
342 control-K, and the following character is interpreted
343 as in rules 1, 2, 3, 5 and 6.
345 5. \m followed by any sequence defined in rules 1, 2, 3, 4
346 or 6 indicates the "Meta" modification of that key.
348 6. A backslash \ followed by any character not described
349 above indicates that character itself. In particular:
350 \\ indicates a single backslash \,
351 \ (backslash-space) indicates a single space,
352 \^ indicates a single caret ^,
354 If the following line:
358 occurs anywhere in an 'info' or 'echo-area' section, that
359 indicates to GNU Info to suppress all of its default key
360 bindings in that context.
364 Each line in the 'var' section has the following syntax:
366 variable-name = value \n
368 Where "variable-name" is the name of a GNU Info variable and
369 "value" is the value that GNU Info will assign to that variable
370 when commencing execution. There must be no white space in the
371 variable name, nor between the variable name and the '='. All
372 characters immediately following the '=', up to but not
373 including the terminating newline, are considered to be the
374 value that will be assigned. In other words, white space
375 following the '=' is not ignored.
378 static int add_to_section (struct sect
*s
, const char *str
, unsigned int len
);
379 static int lookup_action (const char *actname
);
381 /* Compile the input file into its various sections. Return true if no
382 error was encountered.
385 compile (FILE *fp
, const char *filename
, struct sect
*sections
)
389 unsigned int lnum
= 0;
392 /* This parser is a true state machine, with no sneaky fetching
393 of input characters inside the main loop. In other words, all
394 state is fully represented by the following variables:
412 state
= start_of_line
;
413 enum sect_e section
= info
;
422 seqstate
; /* used if state == get_keyseq */
424 char ocnt
= 0; /* used if state == get_keyseq && seqstate == octal */
426 /* Data is accumulated in the following variables. The code
427 avoids overflowing these strings, and throws an error
428 where appropriate if a string limit is exceeded. These string
429 lengths are arbitrary (and should be large enough) and their
430 lengths are not hard-coded anywhere else, so increasing them
431 here will not break anything. */
434 unsigned int clen
= 0;
436 unsigned int slen
= 0;
438 unsigned int alen
= 0;
440 unsigned int varlen
= 0;
442 unsigned int vallen
= 0;
446 if (slen < sizeof seq) \
447 seq[slen++] = meta ? Meta(c) : (c); \
450 syntax_error(filename, lnum, _("key sequence too long"), \
451 NULL, NULL, NULL, NULL); \
457 sections
[info
].cur
= 1;
458 sections
[info
].data
[0] = 0;
459 sections
[ea
].cur
= 1;
460 sections
[ea
].data
[0] = 0;
461 sections
[var
].cur
= 0;
463 while (!error
&& (rescan
|| (c
= fgetc (fp
)) != EOF
))
471 state
= start_of_comment
;
491 case start_of_comment
:
493 state
= in_line_comment
;
495 case in_line_comment
:
498 state
= start_of_line
;
499 comment
[clen
] = '\0';
500 if (strcmp (comment
, "info") == 0)
502 else if (strcmp (comment
, "echo-area") == 0)
504 else if (strcmp (comment
, "var") == 0)
506 else if (strcmp (comment
, "stop") == 0
507 && (section
== info
|| section
== ea
))
508 sections
[section
].data
[0] = 1;
510 else if (clen
< sizeof comment
- 1)
514 case in_trailing_comment
:
516 state
= start_of_line
;
523 if (c
== '\n' || isspace (c
))
529 syntax_error (filename
, lnum
, _("missing key sequence"),
530 NULL
, NULL
, NULL
, NULL
);
545 case '0': case '1': case '2': case '3':
546 case '4': case '5': case '6': case '7':
576 seqstate
= special_key
;
579 /* Backslash followed by any other char
580 just means that char. */
590 case '0': case '1': case '2': case '3':
591 case '4': case '5': case '6': case '7':
593 oval
= oval
* 8 + c
- '0';
603 if (seqstate
!= octal
)
609 syntax_error (filename
, lnum
,
610 _("NUL character (\\000) not permitted"),
611 NULL
, NULL
, NULL
, NULL
);
621 case 'u': To_seq (SK_UP_ARROW
); break;
622 case 'd': To_seq (SK_DOWN_ARROW
); break;
623 case 'r': To_seq (SK_RIGHT_ARROW
); break;
624 case 'l': To_seq (SK_LEFT_ARROW
); break;
625 case 'U': To_seq (SK_PAGE_UP
); break;
626 case 'D': To_seq (SK_PAGE_DOWN
); break;
627 case 'h': To_seq (SK_HOME
); break;
628 case 'e': To_seq (SK_END
); break;
629 case 'x': To_seq (SK_DELETE
); break;
630 default: To_seq (SK_LITERAL
); rescan
= 1; break;
637 To_seq (CONTROL (c
));
640 syntax_error (filename
, lnum
,
641 _("NUL character (^%c) not permitted"),
642 (void *) (long) c
, NULL
, NULL
, NULL
);
651 if (isspace (c
) && c
!= '\n')
657 if (c
== '\n' || isspace (c
))
665 syntax_error (filename
, lnum
, _("missing action name"),
666 (void *) (long) c
, NULL
, NULL
, NULL
);
672 a
= lookup_action (act
);
677 if (!(add_to_section (§ions
[section
], seq
, slen
)
678 && add_to_section (§ions
[section
], "", 1)
679 && add_to_section (§ions
[section
], &av
, 1)))
681 syntax_error (filename
, lnum
, _("section too long"),
682 NULL
, NULL
, NULL
, NULL
);
688 syntax_error (filename
, lnum
, _("unknown action `%s'"),
689 act
, NULL
, NULL
, NULL
);
694 else if (alen
< sizeof act
- 1)
698 syntax_error (filename
, lnum
, _("action name too long"),
699 NULL
, NULL
, NULL
, NULL
);
706 state
= in_trailing_comment
;
708 state
= start_of_line
;
709 else if (!isspace (c
))
711 syntax_error (filename
, lnum
,
712 _("extra characters following action `%s'"),
713 act
, NULL
, NULL
, NULL
);
723 syntax_error (filename
, lnum
, _("missing variable name"),
724 NULL
, NULL
, NULL
, NULL
);
730 else if (c
== '\n' || isspace (c
))
732 syntax_error (filename
, lnum
,
733 _("missing `=' immediately after variable name"),
734 NULL
, NULL
, NULL
, NULL
);
737 else if (varlen
< sizeof varn
)
741 syntax_error (filename
, lnum
, _("variable name too long"),
742 NULL
, NULL
, NULL
, NULL
);
750 state
= start_of_line
;
751 if (!(add_to_section (§ions
[section
], varn
, varlen
)
752 && add_to_section (§ions
[section
], "", 1)
753 && add_to_section (§ions
[section
], val
, vallen
)
754 && add_to_section (§ions
[section
], "", 1)))
756 syntax_error (filename
, lnum
, _("section too long"),
757 NULL
, NULL
, NULL
, NULL
);
761 else if (vallen
< sizeof val
)
765 syntax_error (filename
, lnum
, _("value too long"),
766 NULL
, NULL
, NULL
, NULL
);
783 /* Add some characters to a section's data. Return true if all the
784 characters fit, or false if the section's size limit was exceeded.
787 add_to_section (struct sect
*s
, const char *str
, unsigned int len
)
789 if (s
->cur
+ len
> sizeof s
->data
)
791 strncpy ((char *) s
->data
+ s
->cur
, str
, len
);
796 /* Translate from an action name to its numeric code. This uses the
797 auto-generated array in key.c.
800 lookup_action (const char *actname
)
804 if (strcmp ("invalid", actname
) == 0)
806 for (i
= 0; function_key_array
[i
].name
!= NULL
; i
++)
807 if (strcmp (function_key_array
[i
].name
, actname
) == 0)
808 return function_key_array
[i
].code
;
812 /* Put an integer to an infokey file.
813 Integers are stored as two bytes, low order first,
814 in radix INFOKEY_RADIX.
817 putint (int i
, FILE *fp
)
819 return fputc (i
% INFOKEY_RADIX
, fp
) != EOF
820 && fputc ((i
/ INFOKEY_RADIX
) % INFOKEY_RADIX
, fp
) != EOF
;
823 /* Write an entire section to an infokey file. If the section is
824 empty, simply omit it.
827 putsect (struct sect
*s
, int code
, FILE *fp
)
831 return fputc (code
, fp
) != EOF
832 && putint (s
->cur
, fp
)
833 && fwrite (s
->data
, s
->cur
, 1, fp
) == 1;
836 /* Write an entire infokey file, given an array containing its sections.
839 write_infokey_file (FILE *fp
, struct sect
*sections
)
841 /* Get rid of sections with no effect. */
842 if (sections
[info
].cur
== 1 && sections
[info
].data
[0] == 0)
843 sections
[info
].cur
= 0;
844 if (sections
[ea
].cur
== 1 && sections
[ea
].data
[0] == 0)
845 sections
[ea
].cur
= 0;
847 /* Write all parts of the file out in order (no lseeks),
848 checking for errors all the way. */
849 return fputc (INFOKEY_MAGIC_S0
, fp
) != EOF
850 && fputc (INFOKEY_MAGIC_S1
, fp
) != EOF
851 && fputc (INFOKEY_MAGIC_S2
, fp
) != EOF
852 && fputc (INFOKEY_MAGIC_S3
, fp
) != EOF
853 && fputs (VERSION
, fp
) != EOF
854 && fputc ('\0', fp
) != EOF
855 && putsect (§ions
[info
], INFOKEY_SECTION_INFO
, fp
)
856 && putsect (§ions
[ea
], INFOKEY_SECTION_EA
, fp
)
857 && putsect (§ions
[var
], INFOKEY_SECTION_VAR
, fp
)
858 && fputc (INFOKEY_MAGIC_E0
, fp
) != EOF
859 && fputc (INFOKEY_MAGIC_E1
, fp
) != EOF
860 && fputc (INFOKEY_MAGIC_E2
, fp
) != EOF
861 && fputc (INFOKEY_MAGIC_E3
, fp
) != EOF
;
865 /* Error handling. */
867 /* Give the user a "syntax error" message in the form
868 progname: "filename", line N: message
871 error_message (int error_code
, const char *fmt
,
872 const void *a1
, const void *a2
, const void *a3
, const void *a4
)
874 fprintf (stderr
, "%s: ", program_name
);
875 fprintf (stderr
, fmt
, a1
, a2
, a3
, a4
);
877 fprintf (stderr
, " - %s", strerror (error_code
));
878 fprintf (stderr
, "\n");
881 /* Give the user a generic error message in the form
885 syntax_error (const char *filename
,
886 unsigned int linenum
, const char *fmt
,
887 const void *a1
, const void *a2
, const void *a3
, const void *a4
)
889 fprintf (stderr
, "%s: ", program_name
);
890 fprintf (stderr
, _("\"%s\", line %u: "), filename
, linenum
);
891 fprintf (stderr
, fmt
, a1
, a2
, a3
, a4
);
892 fprintf (stderr
, "\n");
895 /* Produce a gentle rtfm. */
899 fprintf (stderr
, _("Try --help for more information.\n"));
902 /* Produce a scaled down description of the available options to Info. */
907 Usage: %s [OPTION]... [INPUT-FILE]\n\
909 Compile infokey source file to infokey file. Reads INPUT-FILE (default\n\
910 $HOME/.infokey) and writes compiled key file to (by default) $HOME/.info.\n\
913 --output FILE output to FILE instead of $HOME/.info\n\
914 --help display this help and exit.\n\
915 --version display version information and exit.\n\
919 Email bug reports to bug-texinfo@gnu.org,\n\
920 general questions and discussion to help-texinfo@gnu.org.\n\
921 Texinfo home page: http://www.gnu.org/software/texinfo/"));