| 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 | } | 
|---|