xref: /openbsd/gnu/usr.bin/texinfo/info/infokey.c (revision 404b540a)
1 /* infokey.c -- compile ~/.infokey to ~/.info.
2    $Id: infokey.c,v 1.1.1.2 2006/07/17 16:03:45 espie 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 
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 
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 
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 }
925