xref: /netbsd/external/gpl2/texinfo/dist/info/infokey.c (revision eda05004)
1 /*	$NetBSD: infokey.c,v 1.3 2016/10/08 20:34:59 joerg 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 
30 static char *program_name = "infokey";
31 
32 /* Non-zero means print version info only. */
33 static int print_version_p = 0;
34 
35 /* Non-zero means print a short description of the options. */
36 static 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. */
40 static 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. */
44 static 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. */
49 static 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. */
58 static char *short_options = "o:";
59 
60 /* Structure for holding the compiled sections. */
61 enum sect_e
62   {
63     info = 0,
64     ea = 1,
65     var = 2
66   };
67 struct sect
68   {
69     unsigned int cur;
70     unsigned char data[INFOKEY_MAX_SECTIONLEN];
71   };
72 
73 /* Some "forward" declarations. */
74 static char *mkpath (const char *dir, const char *file);
75 static int compile (FILE *fp, const char *filename, struct sect *sections);
76 static int write_infokey_file (FILE *fp, struct sect *sections);
77 static 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);
80 static void error_message (int error_code, const char *fmt,
81     const void *a1, const void *a2, const void *a3, const void *a4);
82 static void suggest_help (void);
83 static void short_help (void);
84 
85 
86 /* **************************************************************** */
87 /*                                                                  */
88 /*             Main Entry Point to the Infokey Program              */
89 /*                                                                  */
90 /* **************************************************************** */
91 
92 int
main(int argc,char ** argv)93 main (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\
148 There is NO warranty.  You may redistribute this software\n\
149 under the terms of the GNU General Public License.\n\
150 For 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 
252 static char *
mkpath(const char * dir,const char * file)253 mkpath (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 
380 static int add_to_section (struct sect *s, const char *str, unsigned int len);
381 static 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  */
386 static int
compile(FILE * fp,const char * filename,struct sect * sections)387 compile (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 ? (char)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  */
788 static int
add_to_section(struct sect * s,const char * str,unsigned int len)789 add_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  */
801 static int
lookup_action(const char * actname)802 lookup_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  */
818 static int
putint(int i,FILE * fp)819 putint (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  */
828 static int
putsect(struct sect * s,int code,FILE * fp)829 putsect (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  */
840 static int
write_infokey_file(FILE * fp,struct sect * sections)841 write_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  */
872 static void
error_message(int error_code,const char * fmt,const void * a1,const void * a2,const void * a3,const void * a4)873 error_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  */
886 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)887 syntax_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. */
898 static void
suggest_help(void)899 suggest_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. */
905 static void
short_help(void)906 short_help (void)
907 {
908   printf (_("\
909 Usage: %s [OPTION]... [INPUT-FILE]\n\
910 \n\
911 Compile 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\
914 Options:\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\
921 Email bug reports to bug-texinfo@gnu.org,\n\
922 general questions and discussion to help-texinfo@gnu.org.\n\
923 Texinfo home page: http://www.gnu.org/software/texinfo/"));
924 
925   xexit (0);
926 }
927