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