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