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