Add manpage for stge(4)
[dragonfly.git] / contrib / texinfo-4 / info / infokey.c
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
28 static char *program_name = "infokey";
29
30 /* Non-zero means print version info only. */
31 static int print_version_p = 0;
32
33 /* Non-zero means print a short description of the options. */
34 static 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. */
38 static 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. */
42 static 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. */
47 static 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. */
56 static char *short_options = "o:";
57
58 /* Structure for holding the compiled sections. */
59 enum sect_e
60   {
61     info = 0,
62     ea = 1,
63     var = 2
64   };
65 struct sect
66   {
67     unsigned int cur;
68     unsigned char data[INFOKEY_MAX_SECTIONLEN];
69   };
70
71 /* Some "forward" declarations. */
72 static char *mkpath (const char *dir, const char *file);
73 static int compile (FILE *fp, const char *filename, struct sect *sections);
74 static int write_infokey_file (FILE *fp, struct sect *sections);
75 static 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);
78 static void error_message (int error_code, const char *fmt,
79     const void *a1, const void *a2, const void *a3, const void *a4);
80 static void suggest_help (void);
81 static void short_help (void);
82 \f
83
84 /* **************************************************************** */
85 /*                                                                  */
86 /*             Main Entry Point to the Infokey Program              */
87 /*                                                                  */
88 /* **************************************************************** */
89
90 int
91 main (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\
146 There is NO warranty.  You may redistribute this software\n\
147 under the terms of the GNU General Public License.\n\
148 For 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
250 static char *
251 mkpath (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 \f
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
378 static int add_to_section (struct sect *s, const char *str, unsigned int len);
379 static 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  */
384 static int
385 compile (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  */
786 static int
787 add_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  */
799 static int
800 lookup_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  */
816 static int
817 putint (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  */
826 static int
827 putsect (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  */
838 static int
839 write_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 \f
864
865 /* Error handling. */
866
867 /* Give the user a "syntax error" message in the form
868         progname: "filename", line N: message
869  */
870 static void
871 error_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  */
884 static void
885 syntax_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. */
896 static void
897 suggest_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. */
903 static void
904 short_help (void)
905 {
906   printf (_("\
907 Usage: %s [OPTION]... [INPUT-FILE]\n\
908 \n\
909 Compile 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\
912 Options:\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\
919 Email bug reports to bug-texinfo@gnu.org,\n\
920 general questions and discussion to help-texinfo@gnu.org.\n\
921 Texinfo home page: http://www.gnu.org/software/texinfo/"));
922
923   xexit (0);
924 }