xref: /netbsd/external/gpl2/texinfo/dist/info/infokey.c (revision eda05004)
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 (&sections[section], seq, slen)
680dc174305Schristos 			    && add_to_section (&sections[section], "", 1)
681dc174305Schristos 			    && add_to_section (&sections[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 (&sections[section], varn, varlen)
754dc174305Schristos 		    && add_to_section (&sections[section], "", 1)
755dc174305Schristos 		    && add_to_section (&sections[section], val, vallen)
756dc174305Schristos 		    && add_to_section (&sections[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 (&sections[info], INFOKEY_SECTION_INFO, fp)
858dc174305Schristos     && putsect (&sections[ea], INFOKEY_SECTION_EA, fp)
859dc174305Schristos     && putsect (&sections[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