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