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