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