| 1 | /* infokey.c -- compile ~/.infokey to ~/.info.
|
|---|
| 2 | $Id: infokey.c,v 1.9 2004/12/14 00:15:36 karl Exp $
|
|---|
| 3 |
|
|---|
| 4 | Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
|
|---|
| 5 |
|
|---|
| 6 | This program is free software; you can redistribute it and/or modify
|
|---|
| 7 | it under the terms of the GNU General Public License as published by
|
|---|
| 8 | the Free Software Foundation; either version 2, or (at your option)
|
|---|
| 9 | any later version.
|
|---|
| 10 |
|
|---|
| 11 | This program is distributed in the hope that it will be useful,
|
|---|
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 14 | GNU General Public License for more details.
|
|---|
| 15 |
|
|---|
| 16 | You should have received a copy of the GNU General Public License
|
|---|
| 17 | along with this program; if not, write to the Free Software
|
|---|
| 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|---|
| 19 |
|
|---|
| 20 | Written by Andrew Bettison <andrewb@zip.com.au>. */
|
|---|
| 21 |
|
|---|
| 22 | #include "info.h"
|
|---|
| 23 | #include "infomap.h"
|
|---|
| 24 | #include "infokey.h"
|
|---|
| 25 | #include "key.h"
|
|---|
| 26 | #include "getopt.h"
|
|---|
| 27 |
|
|---|
| 28 | static char *program_name = "infokey";
|
|---|
| 29 |
|
|---|
| 30 | /* Non-zero means print version info only. */
|
|---|
| 31 | static int print_version_p = 0;
|
|---|
| 32 |
|
|---|
| 33 | /* Non-zero means print a short description of the options. */
|
|---|
| 34 | static int print_help_p = 0;
|
|---|
| 35 |
|
|---|
| 36 | /* String specifying the source file. This is set by the user on the
|
|---|
| 37 | command line, or a default is used. */
|
|---|
| 38 | static char *input_filename = (char *) NULL;
|
|---|
| 39 |
|
|---|
| 40 | /* String specifying the name of the file to output to. This is
|
|---|
| 41 | set by the user on the command line, or a default is used. */
|
|---|
| 42 | static char *output_filename = (char *) NULL;
|
|---|
| 43 |
|
|---|
| 44 | /* Structure describing the options that Infokey accepts. We pass this
|
|---|
| 45 | structure to getopt_long (). If you add or otherwise change this
|
|---|
| 46 | structure, you must also change the string which follows it. */
|
|---|
| 47 | static struct option long_options[] =
|
|---|
| 48 | {
|
|---|
| 49 | {"output", 1, 0, 'o'},
|
|---|
| 50 | {"help", 0, &print_help_p, 1},
|
|---|
| 51 | {"version", 0, &print_version_p, 1},
|
|---|
| 52 | {NULL, 0, NULL, 0}
|
|---|
| 53 | };
|
|---|
| 54 |
|
|---|
| 55 | /* String describing the shorthand versions of the long options found above. */
|
|---|
| 56 | static char *short_options = "o:";
|
|---|
| 57 |
|
|---|
| 58 | /* Structure for holding the compiled sections. */
|
|---|
| 59 | enum sect_e
|
|---|
| 60 | {
|
|---|
| 61 | info = 0,
|
|---|
| 62 | ea = 1,
|
|---|
| 63 | var = 2
|
|---|
| 64 | };
|
|---|
| 65 | struct sect
|
|---|
| 66 | {
|
|---|
| 67 | unsigned int cur;
|
|---|
| 68 | unsigned char data[INFOKEY_MAX_SECTIONLEN];
|
|---|
| 69 | };
|
|---|
| 70 |
|
|---|
| 71 | /* Some "forward" declarations. */
|
|---|
| 72 | static char *mkpath (const char *dir, const char *file);
|
|---|
| 73 | static int compile (FILE *fp, const char *filename, struct sect *sections);
|
|---|
| 74 | static int write_infokey_file (FILE *fp, struct sect *sections);
|
|---|
| 75 | static void syntax_error (const char *filename,
|
|---|
| 76 | unsigned int linenum, const char *fmt,
|
|---|
| 77 | const void *a1, const void *a2, const void *a3, const void *a4);
|
|---|
| 78 | static void error_message (int error_code, const char *fmt,
|
|---|
| 79 | const void *a1, const void *a2, const void *a3, const void *a4);
|
|---|
| 80 | static void suggest_help (void);
|
|---|
| 81 | static void short_help (void);
|
|---|
| 82 | |
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 | /* **************************************************************** */
|
|---|
| 86 | /* */
|
|---|
| 87 | /* Main Entry Point to the Infokey Program */
|
|---|
| 88 | /* */
|
|---|
| 89 | /* **************************************************************** */
|
|---|
| 90 |
|
|---|
| 91 | int
|
|---|
| 92 | main (int argc, char **argv)
|
|---|
| 93 | {
|
|---|
| 94 | int getopt_long_index; /* Index returned by getopt_long (). */
|
|---|
| 95 |
|
|---|
| 96 | #ifdef HAVE_SETLOCALE
|
|---|
| 97 | /* Set locale via LC_ALL. */
|
|---|
| 98 | setlocale (LC_ALL, "");
|
|---|
| 99 | #endif
|
|---|
| 100 |
|
|---|
| 101 | #ifdef ENABLE_NLS
|
|---|
| 102 | /* Set the text message domain. */
|
|---|
| 103 | bindtextdomain (PACKAGE, LOCALEDIR);
|
|---|
| 104 | textdomain (PACKAGE);
|
|---|
| 105 | #endif
|
|---|
| 106 |
|
|---|
| 107 | while (1)
|
|---|
| 108 | {
|
|---|
| 109 | int option_character;
|
|---|
| 110 |
|
|---|
| 111 | option_character = getopt_long
|
|---|
| 112 | (argc, argv, short_options, long_options, &getopt_long_index);
|
|---|
| 113 |
|
|---|
| 114 | /* getopt_long () returns EOF when there are no more long options. */
|
|---|
| 115 | if (option_character == EOF)
|
|---|
| 116 | break;
|
|---|
| 117 |
|
|---|
| 118 | /* If this is a long option, then get the short version of it. */
|
|---|
| 119 | if (option_character == 0 && long_options[getopt_long_index].flag == 0)
|
|---|
| 120 | option_character = long_options[getopt_long_index].val;
|
|---|
| 121 |
|
|---|
| 122 | /* Case on the option that we have received. */
|
|---|
| 123 | switch (option_character)
|
|---|
| 124 | {
|
|---|
| 125 | case 0:
|
|---|
| 126 | break;
|
|---|
| 127 |
|
|---|
| 128 | /* User is specifying the name of a file to output to. */
|
|---|
| 129 | case 'o':
|
|---|
| 130 | if (output_filename)
|
|---|
| 131 | free (output_filename);
|
|---|
| 132 | output_filename = xstrdup (optarg);
|
|---|
| 133 | break;
|
|---|
| 134 |
|
|---|
| 135 | default:
|
|---|
| 136 | suggest_help ();
|
|---|
| 137 | xexit (1);
|
|---|
| 138 | }
|
|---|
| 139 | }
|
|---|
| 140 |
|
|---|
| 141 | /* If the user specified --version, then show the version and exit. */
|
|---|
| 142 | if (print_version_p)
|
|---|
| 143 | {
|
|---|
| 144 | printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION);
|
|---|
| 145 | puts ("");
|
|---|
| 146 | printf (_ ("Copyright (C) %s Free Software Foundation, Inc.\n\
|
|---|
| 147 | There is NO warranty. You may redistribute this software\n\
|
|---|
| 148 | under the terms of the GNU General Public License.\n\
|
|---|
| 149 | For more information about these matters, see the files named COPYING.\n"),
|
|---|
| 150 | "2003");
|
|---|
| 151 | xexit (0);
|
|---|
| 152 | }
|
|---|
| 153 |
|
|---|
| 154 | /* If the `--help' option was present, show the help and exit. */
|
|---|
| 155 | if (print_help_p)
|
|---|
| 156 | {
|
|---|
| 157 | short_help ();
|
|---|
| 158 | xexit (0);
|
|---|
| 159 | }
|
|---|
| 160 |
|
|---|
| 161 | /* If there is one argument remaining, it is the name of the input
|
|---|
| 162 | file. */
|
|---|
| 163 | if (optind == argc - 1)
|
|---|
| 164 | {
|
|---|
| 165 | if (input_filename)
|
|---|
| 166 | free (input_filename);
|
|---|
| 167 | input_filename = xstrdup (argv[optind]);
|
|---|
| 168 | }
|
|---|
| 169 | else if (optind != argc)
|
|---|
| 170 | {
|
|---|
| 171 | error_message (0, _("incorrect number of arguments"),
|
|---|
| 172 | NULL, NULL, NULL, NULL);
|
|---|
| 173 | suggest_help ();
|
|---|
| 174 | xexit (1);
|
|---|
| 175 | }
|
|---|
| 176 |
|
|---|
| 177 | /* Use default filenames where none given. */
|
|---|
| 178 | {
|
|---|
| 179 | char *homedir;
|
|---|
| 180 |
|
|---|
| 181 | homedir = getenv ("HOME");
|
|---|
| 182 | #ifdef __MSDOS__
|
|---|
| 183 | if (!homedir)
|
|---|
| 184 | homedir = ".";
|
|---|
| 185 | #endif
|
|---|
| 186 | if (!input_filename)
|
|---|
| 187 | input_filename = mkpath (homedir, INFOKEY_SRCFILE);
|
|---|
| 188 | if (!output_filename)
|
|---|
| 189 | output_filename = mkpath (homedir, INFOKEY_FILE);
|
|---|
| 190 | }
|
|---|
| 191 |
|
|---|
| 192 | {
|
|---|
| 193 | FILE *inf;
|
|---|
| 194 | FILE *outf;
|
|---|
| 195 | int write_error;
|
|---|
| 196 | static struct sect sections[3];
|
|---|
| 197 |
|
|---|
| 198 | /* Open the input file. */
|
|---|
| 199 | inf = fopen (input_filename, "r");
|
|---|
| 200 | if (!inf)
|
|---|
| 201 | {
|
|---|
| 202 | error_message (errno, _("cannot open input file `%s'"),
|
|---|
| 203 | input_filename, NULL, NULL, NULL);
|
|---|
| 204 | xexit (1);
|
|---|
| 205 | }
|
|---|
| 206 |
|
|---|
| 207 | /* Compile the input file to its verious sections, then write the
|
|---|
| 208 | section data to the output file. */
|
|---|
| 209 |
|
|---|
| 210 | if (compile (inf, input_filename, sections))
|
|---|
| 211 | {
|
|---|
| 212 | /* Open the output file. */
|
|---|
| 213 | outf = fopen (output_filename, FOPEN_WBIN);
|
|---|
| 214 | if (!outf)
|
|---|
| 215 | {
|
|---|
| 216 | error_message (errno, _("cannot create output file `%s'"),
|
|---|
| 217 | output_filename, NULL, NULL, NULL);
|
|---|
| 218 | xexit (1);
|
|---|
| 219 | }
|
|---|
| 220 |
|
|---|
| 221 | /* Write the contents of the output file and close it. If there is
|
|---|
| 222 | an error writing to the file, delete it and exit with a failure
|
|---|
| 223 | status. */
|
|---|
| 224 | write_error = 0;
|
|---|
| 225 | if (!write_infokey_file (outf, sections))
|
|---|
| 226 | {
|
|---|
| 227 | error_message (errno, _("error writing to `%s'"),
|
|---|
| 228 | output_filename, NULL, NULL, NULL);
|
|---|
| 229 | write_error = 1;
|
|---|
| 230 | }
|
|---|
| 231 | if (fclose (outf) == EOF)
|
|---|
| 232 | {
|
|---|
| 233 | error_message (errno, _("error closing output file `%s'"),
|
|---|
| 234 | output_filename, NULL, NULL, NULL);
|
|---|
| 235 | write_error = 1;
|
|---|
| 236 | }
|
|---|
| 237 | if (write_error)
|
|---|
| 238 | {
|
|---|
| 239 | unlink (output_filename);
|
|---|
| 240 | xexit (1);
|
|---|
| 241 | }
|
|---|
| 242 | }
|
|---|
| 243 |
|
|---|
| 244 | /* Close the input file. */
|
|---|
| 245 | fclose (inf);
|
|---|
| 246 | }
|
|---|
| 247 |
|
|---|
| 248 | return 0;
|
|---|
| 249 | }
|
|---|
| 250 |
|
|---|
| 251 | static char *
|
|---|
| 252 | mkpath (const char *dir, const char *file)
|
|---|
| 253 | {
|
|---|
| 254 | char *p;
|
|---|
| 255 |
|
|---|
| 256 | p = xmalloc (strlen (dir) + 1 + strlen (file) + 2);
|
|---|
| 257 | strcpy (p, dir);
|
|---|
| 258 | strcat (p, "/");
|
|---|
| 259 | strcat (p, file);
|
|---|
| 260 | return p;
|
|---|
| 261 | }
|
|---|
| 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
|
|---|
| 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 ? 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 *) (long) 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 *) (long) 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 (§ions[section], seq, slen)
|
|---|
| 680 | && add_to_section (§ions[section], "", 1)
|
|---|
| 681 | && add_to_section (§ions[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 (§ions[section], varn, varlen)
|
|---|
| 754 | && add_to_section (§ions[section], "", 1)
|
|---|
| 755 | && add_to_section (§ions[section], val, vallen)
|
|---|
| 756 | && add_to_section (§ions[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
|
|---|
| 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
|
|---|
| 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
|
|---|
| 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
|
|---|
| 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
|
|---|
| 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 (§ions[info], INFOKEY_SECTION_INFO, fp)
|
|---|
| 858 | && putsect (§ions[ea], INFOKEY_SECTION_EA, fp)
|
|---|
| 859 | && putsect (§ions[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 |
|
|---|
| 868 | /* Error handling. */
|
|---|
| 869 |
|
|---|
| 870 | /* Give the user a "syntax error" message in the form
|
|---|
| 871 | progname: "filename", line N: message
|
|---|
| 872 | */
|
|---|
| 873 | static void
|
|---|
| 874 | error_message (int error_code, const char *fmt,
|
|---|
| 875 | const void *a1, const void *a2, const void *a3, const void *a4)
|
|---|
| 876 | {
|
|---|
| 877 | fprintf (stderr, "%s: ", program_name);
|
|---|
| 878 | fprintf (stderr, fmt, a1, a2, a3, a4);
|
|---|
| 879 | if (error_code)
|
|---|
| 880 | fprintf (stderr, " - %s", strerror (error_code));
|
|---|
| 881 | fprintf (stderr, "\n");
|
|---|
| 882 | }
|
|---|
| 883 |
|
|---|
| 884 | /* Give the user a generic error message in the form
|
|---|
| 885 | progname: message
|
|---|
| 886 | */
|
|---|
| 887 | static void
|
|---|
| 888 | syntax_error (const char *filename,
|
|---|
| 889 | unsigned int linenum, const char *fmt,
|
|---|
| 890 | const void *a1, const void *a2, const void *a3, const void *a4)
|
|---|
| 891 | {
|
|---|
| 892 | fprintf (stderr, "%s: ", program_name);
|
|---|
| 893 | fprintf (stderr, _("\"%s\", line %u: "), filename, linenum);
|
|---|
| 894 | fprintf (stderr, fmt, a1, a2, a3, a4);
|
|---|
| 895 | fprintf (stderr, "\n");
|
|---|
| 896 | }
|
|---|
| 897 |
|
|---|
| 898 | /* Produce a gentle rtfm. */
|
|---|
| 899 | static void
|
|---|
| 900 | suggest_help (void)
|
|---|
| 901 | {
|
|---|
| 902 | fprintf (stderr, _("Try --help for more information.\n"));
|
|---|
| 903 | }
|
|---|
| 904 |
|
|---|
| 905 | /* Produce a scaled down description of the available options to Info. */
|
|---|
| 906 | static void
|
|---|
| 907 | short_help (void)
|
|---|
| 908 | {
|
|---|
| 909 | printf (_("\
|
|---|
| 910 | Usage: %s [OPTION]... [INPUT-FILE]\n\
|
|---|
| 911 | \n\
|
|---|
| 912 | Compile infokey source file to infokey file. Reads INPUT-FILE (default\n\
|
|---|
| 913 | $HOME/.infokey) and writes compiled key file to (by default) $HOME/.info.\n\
|
|---|
| 914 | \n\
|
|---|
| 915 | Options:\n\
|
|---|
| 916 | --output FILE output to FILE instead of $HOME/.info\n\
|
|---|
| 917 | --help display this help and exit.\n\
|
|---|
| 918 | --version display version information and exit.\n\
|
|---|
| 919 | "), program_name);
|
|---|
| 920 |
|
|---|
| 921 | puts (_("\n\
|
|---|
| 922 | Email bug reports to bug-texinfo@gnu.org,\n\
|
|---|
| 923 | general questions and discussion to help-texinfo@gnu.org.\n\
|
|---|
| 924 | Texinfo home page: http://www.gnu.org/software/texinfo/"));
|
|---|
| 925 |
|
|---|
| 926 | xexit (0);
|
|---|
| 927 | }
|
|---|