Back to index

tetex-bin  3.0
infokey.c
Go to the documentation of this file.
00001 /* infokey.c -- compile ~/.infokey to ~/.info.
00002    $Id: infokey.c,v 1.9 2004/12/14 00:15:36 karl Exp $
00003 
00004    Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 
00020    Written by Andrew Bettison <andrewb@zip.com.au>. */
00021 
00022 #include "info.h"
00023 #include "infomap.h"
00024 #include "infokey.h"
00025 #include "key.h"
00026 #include "getopt.h"
00027 
00028 static char *program_name = "infokey";
00029 
00030 /* Non-zero means print version info only. */
00031 static int print_version_p = 0;
00032 
00033 /* Non-zero means print a short description of the options. */
00034 static int print_help_p = 0;
00035 
00036 /* String specifying the source file.  This is set by the user on the
00037    command line, or a default is used. */
00038 static char *input_filename = (char *) NULL;
00039 
00040 /* String specifying the name of the file to output to.  This is
00041    set by the user on the command line, or a default is used. */
00042 static char *output_filename = (char *) NULL;
00043 
00044 /* Structure describing the options that Infokey accepts.  We pass this
00045    structure to getopt_long ().  If you add or otherwise change this
00046    structure, you must also change the string which follows it. */
00047 static struct option long_options[] =
00048 {
00049   {"output", 1, 0, 'o'},
00050   {"help", 0, &print_help_p, 1},
00051   {"version", 0, &print_version_p, 1},
00052   {NULL, 0, NULL, 0}
00053 };
00054 
00055 /* String describing the shorthand versions of the long options found above. */
00056 static char *short_options = "o:";
00057 
00058 /* Structure for holding the compiled sections. */
00059 enum sect_e
00060   {
00061     info = 0,
00062     ea = 1,
00063     var = 2
00064   };
00065 struct sect
00066   {
00067     unsigned int cur;
00068     unsigned char data[INFOKEY_MAX_SECTIONLEN];
00069   };
00070 
00071 /* Some "forward" declarations. */
00072 static char *mkpath (const char *dir, const char *file);
00073 static int compile (FILE *fp, const char *filename, struct sect *sections);
00074 static int write_infokey_file (FILE *fp, struct sect *sections);
00075 static void syntax_error (const char *filename,
00076     unsigned int linenum, const char *fmt,
00077     const void *a1, const void *a2, const void *a3, const void *a4);
00078 static void error_message (int error_code, const char *fmt,
00079     const void *a1, const void *a2, const void *a3, const void *a4);
00080 static void suggest_help (void);
00081 static void short_help (void);
00082 
00083 
00084 /* **************************************************************** */
00085 /*                                                                  */
00086 /*             Main Entry Point to the Infokey Program              */
00087 /*                                                                  */
00088 /* **************************************************************** */
00089 
00090 int
00091 main (int argc, char **argv)
00092 {
00093   int getopt_long_index;    /* Index returned by getopt_long (). */
00094 
00095 #ifdef HAVE_SETLOCALE
00096   /* Set locale via LC_ALL.  */
00097   setlocale (LC_ALL, "");
00098 #endif
00099 
00100 #ifdef ENABLE_NLS
00101   /* Set the text message domain.  */
00102   bindtextdomain (PACKAGE, LOCALEDIR);
00103   textdomain (PACKAGE);
00104 #endif
00105 
00106   while (1)
00107     {
00108       int option_character;
00109 
00110       option_character = getopt_long
00111        (argc, argv, short_options, long_options, &getopt_long_index);
00112 
00113       /* getopt_long () returns EOF when there are no more long options. */
00114       if (option_character == EOF)
00115        break;
00116 
00117       /* If this is a long option, then get the short version of it. */
00118       if (option_character == 0 && long_options[getopt_long_index].flag == 0)
00119        option_character = long_options[getopt_long_index].val;
00120 
00121       /* Case on the option that we have received. */
00122       switch (option_character)
00123        {
00124        case 0:
00125          break;
00126 
00127          /* User is specifying the name of a file to output to. */
00128        case 'o':
00129          if (output_filename)
00130            free (output_filename);
00131          output_filename = xstrdup (optarg);
00132          break;
00133 
00134        default:
00135          suggest_help ();
00136          xexit (1);
00137        }
00138     }
00139 
00140   /* If the user specified --version, then show the version and exit. */
00141   if (print_version_p)
00142     {
00143       printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION);
00144       puts ("");
00145       printf (_ ("Copyright (C) %s Free Software Foundation, Inc.\n\
00146 There is NO warranty.  You may redistribute this software\n\
00147 under the terms of the GNU General Public License.\n\
00148 For more information about these matters, see the files named COPYING.\n"),
00149              "2003");
00150       xexit (0);
00151     }
00152 
00153   /* If the `--help' option was present, show the help and exit. */
00154   if (print_help_p)
00155     {
00156       short_help ();
00157       xexit (0);
00158     }
00159 
00160   /* If there is one argument remaining, it is the name of the input
00161      file. */
00162   if (optind == argc - 1)
00163     {
00164       if (input_filename)
00165        free (input_filename);
00166       input_filename = xstrdup (argv[optind]);
00167     }
00168   else if (optind != argc)
00169     {
00170       error_message (0, _("incorrect number of arguments"),
00171           NULL, NULL, NULL, NULL);
00172       suggest_help ();
00173       xexit (1);
00174     }
00175 
00176   /* Use default filenames where none given. */
00177   {
00178     char *homedir;
00179 
00180     homedir = getenv ("HOME");
00181 #ifdef __MSDOS__
00182     if (!homedir)
00183       homedir = ".";
00184 #endif
00185     if (!input_filename)
00186       input_filename = mkpath (homedir, INFOKEY_SRCFILE);
00187     if (!output_filename)
00188       output_filename = mkpath (homedir, INFOKEY_FILE);
00189   }
00190 
00191   {
00192     FILE *inf;
00193     FILE *outf;
00194     int write_error;
00195     static struct sect sections[3];
00196 
00197     /* Open the input file. */
00198     inf = fopen (input_filename, "r");
00199     if (!inf)
00200       {
00201        error_message (errno, _("cannot open input file `%s'"),
00202             input_filename, NULL, NULL, NULL);
00203        xexit (1);
00204       }
00205 
00206     /* Compile the input file to its verious sections, then write the
00207        section data to the output file. */
00208 
00209     if (compile (inf, input_filename, sections))
00210       {
00211        /* Open the output file. */
00212        outf = fopen (output_filename, FOPEN_WBIN);
00213        if (!outf)
00214          {
00215            error_message (errno, _("cannot create output file `%s'"),
00216                 output_filename, NULL, NULL, NULL);
00217            xexit (1);
00218          }
00219 
00220        /* Write the contents of the output file and close it.  If there is
00221           an error writing to the file, delete it and exit with a failure
00222           status.  */
00223        write_error = 0;
00224        if (!write_infokey_file (outf, sections))
00225          {
00226            error_message (errno, _("error writing to `%s'"),
00227                 output_filename, NULL, NULL, NULL);
00228            write_error = 1;
00229          }
00230        if (fclose (outf) == EOF)
00231          {
00232            error_message (errno, _("error closing output file `%s'"),
00233                 output_filename, NULL, NULL, NULL);
00234            write_error = 1;
00235          }
00236        if (write_error)
00237          {
00238            unlink (output_filename);
00239            xexit (1);
00240          }
00241       }
00242 
00243     /* Close the input file. */
00244     fclose (inf);
00245   }
00246 
00247   return 0;
00248 }
00249 
00250 static char *
00251 mkpath (const char *dir, const char *file)
00252 {
00253   char *p;
00254 
00255   p = xmalloc (strlen (dir) + 1 + strlen (file) + 2);
00256   strcpy (p, dir);
00257   strcat (p, "/");
00258   strcat (p, file);
00259   return p;
00260 }
00261 
00262 
00263 /* Compilation - the real work.
00264 
00265        Source file syntax
00266        ------------------
00267        The source file is a line-based text file with the following
00268        structure:
00269 
00270               # comments
00271               # more comments
00272 
00273               #info
00274               u      prev-line
00275               d      next-line
00276               ^a     invalid              # just beep
00277               \ku    prev-line
00278               #stop
00279               \kd    next-line
00280               q      quit          # of course!
00281 
00282               #echo-area
00283               ^a     echo-area-beg-of-line
00284               ^e     echo-area-end-of-line
00285               \kr    echo-area-forward
00286               \kl    echo-area-backward
00287               \kh    echo-area-beg-of-line
00288               \ke    echo-area-end-of-line
00289 
00290               #var
00291               scroll-step=1
00292               ISO-Latin=Off
00293 
00294        Lines starting with '#' are comments, and are ignored.  Blank
00295        lines are ignored.  Each section is introduced by one of the
00296        following lines:
00297 
00298               #info
00299               #echo-area
00300               #var
00301 
00302        The sections may occur in any order.  Each section may be
00303        omitted completely.  If the 'info' section is the first in the
00304        file, its '#info' line may be omitted.
00305 
00306        The 'info' and 'echo-area' sections
00307        -----------------------------------
00308        Each line in the 'info' or 'echo-area' sections has the
00309        following syntax:
00310 
00311               key-sequence SPACE action-name [ SPACE [ # comment ] ] \n
00312 
00313        Where SPACE is one or more white space characters excluding
00314        newline, "action-name" is the name of a GNU Info command,
00315        "comment" is any sequence of characters excluding newline, and
00316        "key-sequence" is a concatenation of one or more key definitions
00317        using the following syntax:
00318 
00319           1.  A carat ^ followed by one character indicates a single
00320               control character;
00321 
00322           2.  A backslash \ followed by one, two, or three octal
00323               digits indicates a single character having that ASCII
00324               code;
00325 
00326           3.  \n indicates a single NEWLINE;
00327               \e indicates a single ESC;
00328               \r indicates a single CR;
00329               \t indicates a single TAB;
00330               \b indicates a single BACKSPACE;
00331 
00332           4.  \ku indicates the Up Arrow key;
00333               \kd indicates the Down Arrow key;
00334               \kl indicates the Left Arrow key;
00335               \kr indicates the Right Arrow key;
00336               \kP indicates the Page Up (PRIOR) key;
00337               \kN indicates the Page Down (NEXT) key;
00338               \kh indicates the Home key;
00339               \ke indicates the End key;
00340               \kx indicates the DEL key;
00341               \k followed by any other character indicates a single
00342               control-K, and the following character is interpreted
00343               as in rules 1, 2, 3, 5 and 6.
00344 
00345           5.  \m followed by any sequence defined in rules 1, 2, 3, 4
00346               or 6 indicates the "Meta" modification of that key.
00347 
00348           6.  A backslash \ followed by any character not described
00349               above indicates that character itself.  In particular:
00350               \\ indicates a single backslash \,
00351               \  (backslash-space) indicates a single space,
00352               \^ indicates a single caret ^,
00353 
00354        If the following line:
00355 
00356               #stop
00357 
00358        occurs anywhere in an 'info' or 'echo-area' section, that
00359        indicates to GNU Info to suppress all of its default key
00360        bindings in that context.
00361 
00362        The 'var' section
00363        -----------------
00364        Each line in the 'var' section has the following syntax:
00365 
00366               variable-name = value \n
00367 
00368        Where "variable-name" is the name of a GNU Info variable and
00369        "value" is the value that GNU Info will assign to that variable
00370        when commencing execution.  There must be no white space in the
00371        variable name, nor between the variable name and the '='.  All
00372        characters immediately following the '=', up to but not
00373        including the terminating newline, are considered to be the
00374        value that will be assigned.  In other words, white space
00375        following the '=' is not ignored.
00376  */
00377 
00378 static int add_to_section (struct sect *s, const char *str, unsigned int len);
00379 static int lookup_action (const char *actname);
00380 
00381 /* Compile the input file into its various sections.  Return true if no
00382    error was encountered.
00383  */
00384 static int
00385 compile (FILE *fp, const char *filename, struct sect *sections)
00386 {
00387   int error = 0;
00388   char rescan = 0;
00389   unsigned int lnum = 0;
00390   int c = 0;
00391 
00392   /* This parser is a true state machine, with no sneaky fetching
00393      of input characters inside the main loop.  In other words, all
00394      state is fully represented by the following variables:
00395    */
00396   enum
00397     {
00398       start_of_line,
00399       start_of_comment,
00400       in_line_comment,
00401       in_trailing_comment,
00402       get_keyseq,
00403       got_keyseq,
00404       get_action,
00405       got_action,
00406       get_varname,
00407       got_varname,
00408       get_equals,
00409       got_equals,
00410       get_value
00411     }
00412   state = start_of_line;
00413   enum sect_e section = info;
00414   enum
00415     {
00416       normal,
00417       slosh,
00418       control,
00419       octal,
00420       special_key
00421     }
00422   seqstate;          /* used if state == get_keyseq */
00423   char meta = 0;
00424   char ocnt = 0;     /* used if state == get_keyseq && seqstate == octal */
00425 
00426   /* Data is accumulated in the following variables.  The code
00427      avoids overflowing these strings, and throws an error
00428      where appropriate if a string limit is exceeded.  These string
00429      lengths are arbitrary (and should be large enough) and their
00430      lengths are not hard-coded anywhere else, so increasing them
00431      here will not break anything.  */
00432   char oval = 0;
00433   char comment[10];
00434   unsigned int clen = 0;
00435   char seq[20];
00436   unsigned int slen = 0;
00437   char act[80];
00438   unsigned int alen = 0;
00439   char varn[80];
00440   unsigned int varlen = 0;
00441   char val[80];
00442   unsigned int vallen = 0;
00443 
00444 #define       To_seq(c) \
00445                 do { \
00446                   if (slen < sizeof seq) \
00447                     seq[slen++] = meta ? Meta(c) : (c); \
00448                   else \
00449                     { \
00450                      syntax_error(filename, lnum, _("key sequence too long"), \
00451                             NULL, NULL, NULL, NULL); \
00452                      error = 1; \
00453                     } \
00454                   meta = 0; \
00455                 } while (0)
00456 
00457   sections[info].cur = 1;
00458   sections[info].data[0] = 0;
00459   sections[ea].cur = 1;
00460   sections[ea].data[0] = 0;
00461   sections[var].cur = 0;
00462 
00463   while (!error && (rescan || (c = fgetc (fp)) != EOF))
00464     {
00465       rescan = 0;
00466       switch (state)
00467        {
00468        case start_of_line:
00469          lnum++;
00470          if (c == '#')
00471            state = start_of_comment;
00472          else if (c != '\n')
00473            {
00474              switch (section)
00475               {
00476               case info:
00477               case ea:
00478                 state = get_keyseq;
00479                 seqstate = normal;
00480                 slen = 0;
00481                 break;
00482               case var:
00483                 state = get_varname;
00484                 varlen = 0;
00485                 break;
00486               }
00487              rescan = 1;
00488            }
00489          break;
00490 
00491        case start_of_comment:
00492          clen = 0;
00493          state = in_line_comment;
00494          /* fall through */
00495        case in_line_comment:
00496          if (c == '\n')
00497            {
00498              state = start_of_line;
00499              comment[clen] = '\0';
00500              if (strcmp (comment, "info") == 0)
00501               section = info;
00502              else if (strcmp (comment, "echo-area") == 0)
00503               section = ea;
00504              else if (strcmp (comment, "var") == 0)
00505               section = var;
00506              else if (strcmp (comment, "stop") == 0
00507                      && (section == info || section == ea))
00508               sections[section].data[0] = 1;
00509            }
00510          else if (clen < sizeof comment - 1)
00511            comment[clen++] = c;
00512          break;
00513 
00514        case in_trailing_comment:
00515          if (c == '\n')
00516            state = start_of_line;
00517          break;
00518 
00519        case get_keyseq:
00520          switch (seqstate)
00521            {
00522            case normal:
00523              if (c == '\n' || isspace (c))
00524               {
00525                 state = got_keyseq;
00526                 rescan = 1;
00527                 if (slen == 0)
00528                   {
00529                     syntax_error (filename, lnum, _("missing key sequence"),
00530                           NULL, NULL, NULL, NULL);
00531                     error = 1;
00532                   }
00533               }
00534              else if (c == '\\')
00535               seqstate = slosh;
00536              else if (c == '^')
00537               seqstate = control;
00538              else
00539               To_seq (c);
00540              break;
00541 
00542            case slosh:
00543              switch (c)
00544               {
00545               case '0': case '1': case '2': case '3':
00546               case '4': case '5': case '6': case '7':
00547                 seqstate = octal;
00548                 oval = c - '0';
00549                 ocnt = 1;
00550                 break;
00551               case 'b':
00552                 To_seq ('\b');
00553                 seqstate = normal;
00554                 break;
00555               case 'e':
00556                 To_seq ('\033');
00557                 seqstate = normal;
00558                 break;
00559               case 'n':
00560                 To_seq ('\n');
00561                 seqstate = normal;
00562                 break;
00563               case 'r':
00564                 To_seq ('\r');
00565                 seqstate = normal;
00566                 break;
00567               case 't':
00568                 To_seq ('\t');
00569                 seqstate = normal;
00570                 break;
00571               case 'm':
00572                 meta = 1;
00573                 seqstate = normal;
00574                 break;
00575               case 'k':
00576                 seqstate = special_key;
00577                 break;
00578               default:
00579                 /* Backslash followed by any other char
00580                    just means that char.  */
00581                 To_seq (c);
00582                 seqstate = normal;
00583                 break;
00584               }
00585              break;
00586 
00587            case octal:
00588              switch (c)
00589               {
00590               case '0': case '1': case '2': case '3':
00591               case '4': case '5': case '6': case '7':
00592                 if (++ocnt <= 3)
00593                   oval = oval * 8 + c - '0';
00594                 if (ocnt == 3)
00595                   seqstate = normal;
00596                 break;
00597               default:
00598                 ocnt = 4;
00599                 seqstate = normal;
00600                 rescan = 1;
00601                 break;
00602               }
00603              if (seqstate != octal)
00604               {
00605                 if (oval)
00606                   To_seq (oval);
00607                 else
00608                   {
00609                     syntax_error (filename, lnum,
00610                           _("NUL character (\\000) not permitted"),
00611                           NULL, NULL, NULL, NULL);
00612                     error = 1;
00613                   }
00614               }
00615              break;
00616 
00617            case special_key:
00618              To_seq (SK_ESCAPE);
00619              switch (c)
00620               {
00621               case 'u': To_seq (SK_UP_ARROW); break;
00622               case 'd': To_seq (SK_DOWN_ARROW); break;
00623               case 'r': To_seq (SK_RIGHT_ARROW); break;
00624               case 'l': To_seq (SK_LEFT_ARROW); break;
00625               case 'U': To_seq (SK_PAGE_UP); break;
00626               case 'D': To_seq (SK_PAGE_DOWN); break;
00627               case 'h': To_seq (SK_HOME); break;
00628               case 'e': To_seq (SK_END); break;
00629               case 'x': To_seq (SK_DELETE); break;
00630               default:  To_seq (SK_LITERAL); rescan = 1; break;
00631               }
00632              seqstate = normal;
00633              break;
00634 
00635            case control:
00636              if (CONTROL (c))
00637               To_seq (CONTROL (c));
00638              else
00639               {
00640                 syntax_error (filename, lnum,
00641                       (char *) _("NUL character (^%c) not permitted"),
00642                       (void *) (long) c, NULL, NULL, NULL);
00643                 error = 1;
00644               }
00645              seqstate = normal;
00646              break;
00647            }
00648          break;
00649 
00650        case got_keyseq:
00651          if (isspace (c) && c != '\n')
00652            break;
00653          state = get_action;
00654          alen = 0;
00655          /* fall through */
00656        case get_action:
00657          if (c == '\n' || isspace (c))
00658            {
00659              int a;
00660 
00661              state = got_action;
00662              rescan = 1;
00663              if (alen == 0)
00664               {
00665                 syntax_error (filename, lnum, (char *) _("missing action name"),
00666                             (void *) (long) c, NULL, NULL, NULL);
00667                 error = 1;
00668               }
00669              else
00670               {
00671                 act[alen] = '\0';
00672                 a = lookup_action (act);
00673                 if (a != -1)
00674                   {
00675                     char av = a;
00676 
00677                     if (!(add_to_section (&sections[section], seq, slen)
00678                          && add_to_section (&sections[section], "", 1)
00679                          && add_to_section (&sections[section], &av, 1)))
00680                      {
00681                        syntax_error (filename, lnum, _("section too long"),
00682                               NULL, NULL, NULL, NULL);
00683                        error = 1;
00684                      }
00685                   }
00686                 else
00687                   {
00688                     syntax_error (filename, lnum, _("unknown action `%s'"),
00689                           act, NULL, NULL, NULL);
00690                     error = 1;
00691                   }
00692               }
00693            }
00694          else if (alen < sizeof act - 1)
00695            act[alen++] = c;
00696          else
00697            {
00698              syntax_error (filename, lnum, _("action name too long"),
00699                   NULL, NULL, NULL, NULL);
00700              error = 1;
00701            }
00702          break;
00703 
00704        case got_action:
00705          if (c == '#')
00706            state = in_trailing_comment;
00707          else if (c == '\n')
00708            state = start_of_line;
00709          else if (!isspace (c))
00710            {
00711              syntax_error (filename, lnum,
00712                   _("extra characters following action `%s'"),
00713                   act, NULL, NULL, NULL);
00714              error = 1;
00715            }
00716          break;
00717 
00718        case get_varname:
00719          if (c == '=')
00720            {
00721              if (varlen == 0)
00722               {
00723                 syntax_error (filename, lnum, _("missing variable name"),
00724                       NULL, NULL, NULL, NULL);
00725                 error = 1;
00726               }
00727              state = get_value;
00728              vallen = 0;
00729            }
00730          else if (c == '\n' || isspace (c))
00731            {
00732              syntax_error (filename, lnum,
00733                   _("missing `=' immediately after variable name"),
00734                   NULL, NULL, NULL, NULL);
00735              error = 1;
00736            }
00737          else if (varlen < sizeof varn)
00738            varn[varlen++] = c;
00739          else
00740            {
00741              syntax_error (filename, lnum, _("variable name too long"),
00742                   NULL, NULL, NULL, NULL);
00743              error = 1;
00744            }
00745          break;
00746 
00747        case get_value:
00748          if (c == '\n')
00749            {
00750              state = start_of_line;
00751              if (!(add_to_section (&sections[section], varn, varlen)
00752                   && add_to_section (&sections[section], "", 1)
00753                   && add_to_section (&sections[section], val, vallen)
00754                   && add_to_section (&sections[section], "", 1)))
00755               {
00756                 syntax_error (filename, lnum, _("section too long"),
00757                       NULL, NULL, NULL, NULL);
00758                 error = 1;
00759               }
00760            }
00761          else if (vallen < sizeof val)
00762            val[vallen++] = c;
00763          else
00764            {
00765              syntax_error (filename, lnum, _("value too long"),
00766                   NULL, NULL, NULL, NULL);
00767              error = 1;
00768            }
00769          break;
00770 
00771         case get_equals:
00772         case got_equals:
00773         case got_varname:
00774           break;
00775        }
00776     }
00777 
00778 #undef To_seq
00779 
00780   return !error;
00781 }
00782 
00783 /* Add some characters to a section's data.  Return true if all the
00784    characters fit, or false if the section's size limit was exceeded.
00785  */
00786 static int
00787 add_to_section (struct sect *s, const char *str, unsigned int len)
00788 {
00789   if (s->cur + len > sizeof s->data)
00790     return 0;
00791   strncpy ((char *) s->data + s->cur, str, len);
00792   s->cur += len;
00793   return 1;
00794 }
00795 
00796 /* Translate from an action name to its numeric code.  This uses the
00797    auto-generated array in key.c.
00798  */
00799 static int
00800 lookup_action (const char *actname)
00801 {
00802   int i;
00803 
00804   if (strcmp ("invalid", actname) == 0)
00805     return A_INVALID;
00806   for (i = 0; function_key_array[i].name != NULL; i++)
00807     if (strcmp (function_key_array[i].name, actname) == 0)
00808       return function_key_array[i].code;
00809   return -1;
00810 }
00811 
00812 /* Put an integer to an infokey file.
00813    Integers are stored as two bytes, low order first,
00814    in radix INFOKEY_RADIX.
00815  */
00816 static int
00817 putint (int i, FILE *fp)
00818 {
00819   return fputc (i % INFOKEY_RADIX, fp) != EOF
00820     && fputc ((i / INFOKEY_RADIX) % INFOKEY_RADIX, fp) != EOF;
00821 }
00822 
00823 /* Write an entire section to an infokey file.  If the section is
00824    empty, simply omit it.
00825  */
00826 static int
00827 putsect (struct sect *s, int code, FILE *fp)
00828 {
00829   if (s->cur == 0)
00830     return 1;
00831   return fputc (code, fp) != EOF
00832     && putint (s->cur, fp)
00833     && fwrite (s->data, s->cur, 1, fp) == 1;
00834 }
00835 
00836 /* Write an entire infokey file, given an array containing its sections.
00837  */
00838 static int
00839 write_infokey_file (FILE *fp, struct sect *sections)
00840 {
00841   /* Get rid of sections with no effect. */
00842   if (sections[info].cur == 1 && sections[info].data[0] == 0)
00843     sections[info].cur = 0;
00844   if (sections[ea].cur == 1 && sections[ea].data[0] == 0)
00845     sections[ea].cur = 0;
00846 
00847   /* Write all parts of the file out in order (no lseeks),
00848      checking for errors all the way. */
00849   return fputc (INFOKEY_MAGIC_S0, fp) != EOF
00850     && fputc (INFOKEY_MAGIC_S1, fp) != EOF
00851     && fputc (INFOKEY_MAGIC_S2, fp) != EOF
00852     && fputc (INFOKEY_MAGIC_S3, fp) != EOF
00853     && fputs (VERSION, fp) != EOF
00854     && fputc ('\0', fp) != EOF
00855     && putsect (&sections[info], INFOKEY_SECTION_INFO, fp)
00856     && putsect (&sections[ea], INFOKEY_SECTION_EA, fp)
00857     && putsect (&sections[var], INFOKEY_SECTION_VAR, fp)
00858     && fputc (INFOKEY_MAGIC_E0, fp) != EOF
00859     && fputc (INFOKEY_MAGIC_E1, fp) != EOF
00860     && fputc (INFOKEY_MAGIC_E2, fp) != EOF
00861     && fputc (INFOKEY_MAGIC_E3, fp) != EOF;
00862 }
00863 
00864 
00865 /* Error handling. */
00866 
00867 /* Give the user a "syntax error" message in the form
00868        progname: "filename", line N: message
00869  */
00870 static void
00871 error_message (int error_code, const char *fmt,
00872     const void *a1, const void *a2, const void *a3, const void *a4)
00873 {
00874   fprintf (stderr, "%s: ", program_name);
00875   fprintf (stderr, fmt, a1, a2, a3, a4);
00876   if (error_code)
00877     fprintf (stderr, " - %s", strerror (error_code));
00878   fprintf (stderr, "\n");
00879 }
00880 
00881 /* Give the user a generic error message in the form
00882        progname: message
00883  */
00884 static void
00885 syntax_error (const char *filename,
00886     unsigned int linenum, const char *fmt,
00887     const void *a1, const void *a2, const void *a3, const void *a4)
00888 {
00889   fprintf (stderr, "%s: ", program_name);
00890   fprintf (stderr, _("\"%s\", line %u: "), filename, linenum);
00891   fprintf (stderr, fmt, a1, a2, a3, a4);
00892   fprintf (stderr, "\n");
00893 }
00894 
00895 /* Produce a gentle rtfm. */
00896 static void
00897 suggest_help (void)
00898 {
00899   fprintf (stderr, _("Try --help for more information.\n"));
00900 }
00901 
00902 /* Produce a scaled down description of the available options to Info. */
00903 static void
00904 short_help (void)
00905 {
00906   printf (_("\
00907 Usage: %s [OPTION]... [INPUT-FILE]\n\
00908 \n\
00909 Compile infokey source file to infokey file.  Reads INPUT-FILE (default\n\
00910 $HOME/.infokey) and writes compiled key file to (by default) $HOME/.info.\n\
00911 \n\
00912 Options:\n\
00913   --output FILE        output to FILE instead of $HOME/.info\n\
00914   --help               display this help and exit.\n\
00915   --version            display version information and exit.\n\
00916 "), program_name);
00917 
00918   puts (_("\n\
00919 Email bug reports to bug-texinfo@gnu.org,\n\
00920 general questions and discussion to help-texinfo@gnu.org.\n\
00921 Texinfo home page: http://www.gnu.org/software/texinfo/"));
00922 
00923   xexit (0);
00924 }