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