193139Sru/* infokey.c -- compile ~/.infokey to ~/.info. 2146515Sru $Id: infokey.c,v 1.9 2004/12/14 00:15:36 karl Exp $ 393139Sru 4146515Sru Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. 593139Sru 693139Sru This program is free software; you can redistribute it and/or modify 793139Sru it under the terms of the GNU General Public License as published by 893139Sru the Free Software Foundation; either version 2, or (at your option) 993139Sru any later version. 1093139Sru 1193139Sru This program is distributed in the hope that it will be useful, 1293139Sru but WITHOUT ANY WARRANTY; without even the implied warranty of 1393139Sru MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1493139Sru GNU General Public License for more details. 1593139Sru 1693139Sru You should have received a copy of the GNU General Public License 1793139Sru along with this program; if not, write to the Free Software 1893139Sru Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1993139Sru 2093139Sru Written by Andrew Bettison <andrewb@zip.com.au>. */ 2193139Sru 2293139Sru#include "info.h" 2393139Sru#include "infomap.h" 2493139Sru#include "infokey.h" 2593139Sru#include "key.h" 2693139Sru#include "getopt.h" 2793139Sru 2893139Srustatic char *program_name = "infokey"; 2993139Sru 3093139Sru/* Non-zero means print version info only. */ 3193139Srustatic int print_version_p = 0; 3293139Sru 3393139Sru/* Non-zero means print a short description of the options. */ 3493139Srustatic int print_help_p = 0; 3593139Sru 3693139Sru/* String specifying the source file. This is set by the user on the 3793139Sru command line, or a default is used. */ 3893139Srustatic char *input_filename = (char *) NULL; 3993139Sru 4093139Sru/* String specifying the name of the file to output to. This is 4193139Sru set by the user on the command line, or a default is used. */ 4293139Srustatic char *output_filename = (char *) NULL; 4393139Sru 4493139Sru/* Structure describing the options that Infokey accepts. We pass this 4593139Sru structure to getopt_long (). If you add or otherwise change this 4693139Sru structure, you must also change the string which follows it. */ 4793139Srustatic struct option long_options[] = 4893139Sru{ 4993139Sru {"output", 1, 0, 'o'}, 5093139Sru {"help", 0, &print_help_p, 1}, 5193139Sru {"version", 0, &print_version_p, 1}, 5293139Sru {NULL, 0, NULL, 0} 5393139Sru}; 5493139Sru 5593139Sru/* String describing the shorthand versions of the long options found above. */ 5693139Srustatic char *short_options = "o:"; 5793139Sru 5893139Sru/* Structure for holding the compiled sections. */ 5993139Sruenum sect_e 6093139Sru { 6193139Sru info = 0, 6293139Sru ea = 1, 63100513Sru var = 2 6493139Sru }; 6593139Srustruct sect 6693139Sru { 6793139Sru unsigned int cur; 6893139Sru unsigned char data[INFOKEY_MAX_SECTIONLEN]; 6993139Sru }; 7093139Sru 7193139Sru/* Some "forward" declarations. */ 72146515Srustatic char *mkpath (const char *dir, const char *file); 73146515Srustatic int compile (FILE *fp, const char *filename, struct sect *sections); 74146515Srustatic int write_infokey_file (FILE *fp, struct sect *sections); 75146515Srustatic void syntax_error (const char *filename, 76146515Sru unsigned int linenum, const char *fmt, 77146515Sru const void *a1, const void *a2, const void *a3, const void *a4); 78146515Srustatic void error_message (int error_code, const char *fmt, 79146515Sru const void *a1, const void *a2, const void *a3, const void *a4); 80146515Srustatic void suggest_help (void); 81146515Srustatic void short_help (void); 8293139Sru 8393139Sru 8493139Sru/* **************************************************************** */ 8593139Sru/* */ 8693139Sru/* Main Entry Point to the Infokey Program */ 8793139Sru/* */ 8893139Sru/* **************************************************************** */ 8993139Sru 9093139Sruint 91146515Srumain (int argc, char **argv) 9293139Sru{ 9393139Sru int getopt_long_index; /* Index returned by getopt_long (). */ 9493139Sru 9593139Sru#ifdef HAVE_SETLOCALE 9693139Sru /* Set locale via LC_ALL. */ 9793139Sru setlocale (LC_ALL, ""); 9893139Sru#endif 9993139Sru 100146515Sru#ifdef ENABLE_NLS 10193139Sru /* Set the text message domain. */ 10293139Sru bindtextdomain (PACKAGE, LOCALEDIR); 10393139Sru textdomain (PACKAGE); 104146515Sru#endif 10593139Sru 10693139Sru while (1) 10793139Sru { 10893139Sru int option_character; 10993139Sru 11093139Sru option_character = getopt_long 11193139Sru (argc, argv, short_options, long_options, &getopt_long_index); 11293139Sru 11393139Sru /* getopt_long () returns EOF when there are no more long options. */ 11493139Sru if (option_character == EOF) 11593139Sru break; 11693139Sru 11793139Sru /* If this is a long option, then get the short version of it. */ 11893139Sru if (option_character == 0 && long_options[getopt_long_index].flag == 0) 11993139Sru option_character = long_options[getopt_long_index].val; 12093139Sru 12193139Sru /* Case on the option that we have received. */ 12293139Sru switch (option_character) 12393139Sru { 12493139Sru case 0: 12593139Sru break; 12693139Sru 12793139Sru /* User is specifying the name of a file to output to. */ 12893139Sru case 'o': 12993139Sru if (output_filename) 13093139Sru free (output_filename); 13193139Sru output_filename = xstrdup (optarg); 13293139Sru break; 13393139Sru 13493139Sru default: 13593139Sru suggest_help (); 13693139Sru xexit (1); 13793139Sru } 13893139Sru } 13993139Sru 14093139Sru /* If the user specified --version, then show the version and exit. */ 14193139Sru if (print_version_p) 14293139Sru { 14393139Sru printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION); 14493139Sru puts (""); 14593139Sru printf (_ ("Copyright (C) %s Free Software Foundation, Inc.\n\ 14693139SruThere is NO warranty. You may redistribute this software\n\ 14793139Sruunder the terms of the GNU General Public License.\n\ 14893139SruFor more information about these matters, see the files named COPYING.\n"), 149114472Sru "2003"); 15093139Sru xexit (0); 15193139Sru } 15293139Sru 15393139Sru /* If the `--help' option was present, show the help and exit. */ 15493139Sru if (print_help_p) 15593139Sru { 15693139Sru short_help (); 15793139Sru xexit (0); 15893139Sru } 15993139Sru 16093139Sru /* If there is one argument remaining, it is the name of the input 16193139Sru file. */ 16293139Sru if (optind == argc - 1) 16393139Sru { 16493139Sru if (input_filename) 16593139Sru free (input_filename); 16693139Sru input_filename = xstrdup (argv[optind]); 16793139Sru } 16893139Sru else if (optind != argc) 16993139Sru { 170146515Sru error_message (0, _("incorrect number of arguments"), 171146515Sru NULL, NULL, NULL, NULL); 17293139Sru suggest_help (); 17393139Sru xexit (1); 17493139Sru } 17593139Sru 17693139Sru /* Use default filenames where none given. */ 17793139Sru { 17893139Sru char *homedir; 17993139Sru 18093139Sru homedir = getenv ("HOME"); 18193139Sru#ifdef __MSDOS__ 18293139Sru if (!homedir) 18393139Sru homedir = "."; 18493139Sru#endif 18593139Sru if (!input_filename) 18693139Sru input_filename = mkpath (homedir, INFOKEY_SRCFILE); 18793139Sru if (!output_filename) 18893139Sru output_filename = mkpath (homedir, INFOKEY_FILE); 18993139Sru } 19093139Sru 19193139Sru { 19293139Sru FILE *inf; 19393139Sru FILE *outf; 19493139Sru int write_error; 19593139Sru static struct sect sections[3]; 19693139Sru 19793139Sru /* Open the input file. */ 19893139Sru inf = fopen (input_filename, "r"); 19993139Sru if (!inf) 20093139Sru { 201146515Sru error_message (errno, _("cannot open input file `%s'"), 202146515Sru input_filename, NULL, NULL, NULL); 20393139Sru xexit (1); 20493139Sru } 20593139Sru 20693139Sru /* Compile the input file to its verious sections, then write the 20793139Sru section data to the output file. */ 20893139Sru 20993139Sru if (compile (inf, input_filename, sections)) 21093139Sru { 21193139Sru /* Open the output file. */ 21293139Sru outf = fopen (output_filename, FOPEN_WBIN); 21393139Sru if (!outf) 21493139Sru { 215146515Sru error_message (errno, _("cannot create output file `%s'"), 216146515Sru output_filename, NULL, NULL, NULL); 21793139Sru xexit (1); 21893139Sru } 21993139Sru 22093139Sru /* Write the contents of the output file and close it. If there is 22193139Sru an error writing to the file, delete it and exit with a failure 22293139Sru status. */ 22393139Sru write_error = 0; 22493139Sru if (!write_infokey_file (outf, sections)) 22593139Sru { 226146515Sru error_message (errno, _("error writing to `%s'"), 227146515Sru output_filename, NULL, NULL, NULL); 22893139Sru write_error = 1; 22993139Sru } 23093139Sru if (fclose (outf) == EOF) 23193139Sru { 232146515Sru error_message (errno, _("error closing output file `%s'"), 233146515Sru output_filename, NULL, NULL, NULL); 23493139Sru write_error = 1; 23593139Sru } 23693139Sru if (write_error) 23793139Sru { 23893139Sru unlink (output_filename); 23993139Sru xexit (1); 24093139Sru } 24193139Sru } 24293139Sru 24393139Sru /* Close the input file. */ 24493139Sru fclose (inf); 24593139Sru } 24693139Sru 247116525Sru return 0; 24893139Sru} 24993139Sru 25093139Srustatic char * 251146515Srumkpath (const char *dir, const char *file) 25293139Sru{ 25393139Sru char *p; 25493139Sru 25593139Sru p = xmalloc (strlen (dir) + 1 + strlen (file) + 2); 25693139Sru strcpy (p, dir); 25793139Sru strcat (p, "/"); 25893139Sru strcat (p, file); 25993139Sru return p; 26093139Sru} 26193139Sru 26293139Sru 26393139Sru/* Compilation - the real work. 26493139Sru 26593139Sru Source file syntax 26693139Sru ------------------ 26793139Sru The source file is a line-based text file with the following 26893139Sru structure: 26993139Sru 27093139Sru # comments 27193139Sru # more comments 27293139Sru 27393139Sru #info 27493139Sru u prev-line 27593139Sru d next-line 27693139Sru ^a invalid # just beep 27793139Sru \ku prev-line 27893139Sru #stop 27993139Sru \kd next-line 28093139Sru q quit # of course! 28193139Sru 28293139Sru #echo-area 28393139Sru ^a echo-area-beg-of-line 28493139Sru ^e echo-area-end-of-line 28593139Sru \kr echo-area-forward 28693139Sru \kl echo-area-backward 28793139Sru \kh echo-area-beg-of-line 28893139Sru \ke echo-area-end-of-line 28993139Sru 29093139Sru #var 29193139Sru scroll-step=1 29293139Sru ISO-Latin=Off 29393139Sru 29493139Sru Lines starting with '#' are comments, and are ignored. Blank 29593139Sru lines are ignored. Each section is introduced by one of the 29693139Sru following lines: 29793139Sru 29893139Sru #info 29993139Sru #echo-area 30093139Sru #var 301116525Sru 30293139Sru The sections may occur in any order. Each section may be 30393139Sru omitted completely. If the 'info' section is the first in the 30493139Sru file, its '#info' line may be omitted. 305116525Sru 30693139Sru The 'info' and 'echo-area' sections 30793139Sru ----------------------------------- 30893139Sru Each line in the 'info' or 'echo-area' sections has the 30993139Sru following syntax: 31093139Sru 31193139Sru key-sequence SPACE action-name [ SPACE [ # comment ] ] \n 312116525Sru 31393139Sru Where SPACE is one or more white space characters excluding 31493139Sru newline, "action-name" is the name of a GNU Info command, 31593139Sru "comment" is any sequence of characters excluding newline, and 31693139Sru "key-sequence" is a concatenation of one or more key definitions 31793139Sru using the following syntax: 31893139Sru 31993139Sru 1. A carat ^ followed by one character indicates a single 32093139Sru control character; 32193139Sru 32293139Sru 2. A backslash \ followed by one, two, or three octal 32393139Sru digits indicates a single character having that ASCII 32493139Sru code; 32593139Sru 32693139Sru 3. \n indicates a single NEWLINE; 32793139Sru \e indicates a single ESC; 32893139Sru \r indicates a single CR; 32993139Sru \t indicates a single TAB; 33093139Sru \b indicates a single BACKSPACE; 331116525Sru 33293139Sru 4. \ku indicates the Up Arrow key; 33393139Sru \kd indicates the Down Arrow key; 33493139Sru \kl indicates the Left Arrow key; 33593139Sru \kr indicates the Right Arrow key; 33693139Sru \kP indicates the Page Up (PRIOR) key; 33793139Sru \kN indicates the Page Down (NEXT) key; 33893139Sru \kh indicates the Home key; 33993139Sru \ke indicates the End key; 34093139Sru \kx indicates the DEL key; 34193139Sru \k followed by any other character indicates a single 34293139Sru control-K, and the following character is interpreted 34393139Sru as in rules 1, 2, 3, 5 and 6. 34493139Sru 34593139Sru 5. \m followed by any sequence defined in rules 1, 2, 3, 4 34693139Sru or 6 indicates the "Meta" modification of that key. 34793139Sru 34893139Sru 6. A backslash \ followed by any character not described 34993139Sru above indicates that character itself. In particular: 35093139Sru \\ indicates a single backslash \, 35193139Sru \ (backslash-space) indicates a single space, 35293139Sru \^ indicates a single caret ^, 35393139Sru 35493139Sru If the following line: 35593139Sru 35693139Sru #stop 357116525Sru 35893139Sru occurs anywhere in an 'info' or 'echo-area' section, that 35993139Sru indicates to GNU Info to suppress all of its default key 36093139Sru bindings in that context. 361116525Sru 36293139Sru The 'var' section 36393139Sru ----------------- 36493139Sru Each line in the 'var' section has the following syntax: 36593139Sru 36693139Sru variable-name = value \n 367116525Sru 36893139Sru Where "variable-name" is the name of a GNU Info variable and 36993139Sru "value" is the value that GNU Info will assign to that variable 37093139Sru when commencing execution. There must be no white space in the 37193139Sru variable name, nor between the variable name and the '='. All 37293139Sru characters immediately following the '=', up to but not 37393139Sru including the terminating newline, are considered to be the 37493139Sru value that will be assigned. In other words, white space 37593139Sru following the '=' is not ignored. 37693139Sru */ 37793139Sru 378146515Srustatic int add_to_section (struct sect *s, const char *str, unsigned int len); 379146515Srustatic int lookup_action (const char *actname); 38093139Sru 38193139Sru/* Compile the input file into its various sections. Return true if no 38293139Sru error was encountered. 38393139Sru */ 38493139Srustatic int 385146515Srucompile (FILE *fp, const char *filename, struct sect *sections) 38693139Sru{ 38793139Sru int error = 0; 38893139Sru char rescan = 0; 38993139Sru unsigned int lnum = 0; 390146515Sru int c = 0; 39193139Sru 39293139Sru /* This parser is a true state machine, with no sneaky fetching 39393139Sru of input characters inside the main loop. In other words, all 39493139Sru state is fully represented by the following variables: 39593139Sru */ 39693139Sru enum 39793139Sru { 39893139Sru start_of_line, 39993139Sru start_of_comment, 40093139Sru in_line_comment, 40193139Sru in_trailing_comment, 40293139Sru get_keyseq, 40393139Sru got_keyseq, 40493139Sru get_action, 40593139Sru got_action, 40693139Sru get_varname, 40793139Sru got_varname, 40893139Sru get_equals, 40993139Sru got_equals, 410100513Sru get_value 41193139Sru } 41293139Sru state = start_of_line; 41393139Sru enum sect_e section = info; 41493139Sru enum 41593139Sru { 41693139Sru normal, 41793139Sru slosh, 41893139Sru control, 41993139Sru octal, 420100513Sru special_key 42193139Sru } 422146515Sru seqstate; /* used if state == get_keyseq */ 42393139Sru char meta = 0; 424146515Sru char ocnt = 0; /* used if state == get_keyseq && seqstate == octal */ 42593139Sru 42693139Sru /* Data is accumulated in the following variables. The code 42793139Sru avoids overflowing these strings, and throws an error 42893139Sru where appropriate if a string limit is exceeded. These string 42993139Sru lengths are arbitrary (and should be large enough) and their 43093139Sru lengths are not hard-coded anywhere else, so increasing them 43193139Sru here will not break anything. */ 432146515Sru char oval = 0; 43393139Sru char comment[10]; 434146515Sru unsigned int clen = 0; 43593139Sru char seq[20]; 436146515Sru unsigned int slen = 0; 43793139Sru char act[80]; 438146515Sru unsigned int alen = 0; 43993139Sru char varn[80]; 440146515Sru unsigned int varlen = 0; 44193139Sru char val[80]; 442146515Sru unsigned int vallen = 0; 44393139Sru 44493139Sru#define To_seq(c) \ 44593139Sru do { \ 44693139Sru if (slen < sizeof seq) \ 44793139Sru seq[slen++] = meta ? Meta(c) : (c); \ 44893139Sru else \ 44993139Sru { \ 450146515Sru syntax_error(filename, lnum, _("key sequence too long"), \ 451146515Sru NULL, NULL, NULL, NULL); \ 45293139Sru error = 1; \ 45393139Sru } \ 45493139Sru meta = 0; \ 45593139Sru } while (0) 45693139Sru 45793139Sru sections[info].cur = 1; 45893139Sru sections[info].data[0] = 0; 45993139Sru sections[ea].cur = 1; 46093139Sru sections[ea].data[0] = 0; 46193139Sru sections[var].cur = 0; 46293139Sru 46393139Sru while (!error && (rescan || (c = fgetc (fp)) != EOF)) 46493139Sru { 46593139Sru rescan = 0; 46693139Sru switch (state) 46793139Sru { 46893139Sru case start_of_line: 46993139Sru lnum++; 47093139Sru if (c == '#') 47193139Sru state = start_of_comment; 47293139Sru else if (c != '\n') 47393139Sru { 47493139Sru switch (section) 47593139Sru { 47693139Sru case info: 47793139Sru case ea: 47893139Sru state = get_keyseq; 47993139Sru seqstate = normal; 48093139Sru slen = 0; 48193139Sru break; 48293139Sru case var: 48393139Sru state = get_varname; 48493139Sru varlen = 0; 48593139Sru break; 48693139Sru } 48793139Sru rescan = 1; 48893139Sru } 48993139Sru break; 49093139Sru 49193139Sru case start_of_comment: 49293139Sru clen = 0; 49393139Sru state = in_line_comment; 49493139Sru /* fall through */ 49593139Sru case in_line_comment: 49693139Sru if (c == '\n') 49793139Sru { 49893139Sru state = start_of_line; 49993139Sru comment[clen] = '\0'; 50093139Sru if (strcmp (comment, "info") == 0) 50193139Sru section = info; 50293139Sru else if (strcmp (comment, "echo-area") == 0) 50393139Sru section = ea; 50493139Sru else if (strcmp (comment, "var") == 0) 50593139Sru section = var; 50693139Sru else if (strcmp (comment, "stop") == 0 50793139Sru && (section == info || section == ea)) 50893139Sru sections[section].data[0] = 1; 50993139Sru } 51093139Sru else if (clen < sizeof comment - 1) 51193139Sru comment[clen++] = c; 51293139Sru break; 51393139Sru 51493139Sru case in_trailing_comment: 51593139Sru if (c == '\n') 51693139Sru state = start_of_line; 51793139Sru break; 51893139Sru 51993139Sru case get_keyseq: 52093139Sru switch (seqstate) 52193139Sru { 52293139Sru case normal: 52393139Sru if (c == '\n' || isspace (c)) 52493139Sru { 52593139Sru state = got_keyseq; 52693139Sru rescan = 1; 52793139Sru if (slen == 0) 52893139Sru { 529146515Sru syntax_error (filename, lnum, _("missing key sequence"), 530146515Sru NULL, NULL, NULL, NULL); 53193139Sru error = 1; 53293139Sru } 53393139Sru } 53493139Sru else if (c == '\\') 53593139Sru seqstate = slosh; 53693139Sru else if (c == '^') 53793139Sru seqstate = control; 53893139Sru else 53993139Sru To_seq (c); 54093139Sru break; 54193139Sru 54293139Sru case slosh: 54393139Sru switch (c) 54493139Sru { 54593139Sru case '0': case '1': case '2': case '3': 54693139Sru case '4': case '5': case '6': case '7': 54793139Sru seqstate = octal; 54893139Sru oval = c - '0'; 54993139Sru ocnt = 1; 55093139Sru break; 55193139Sru case 'b': 55293139Sru To_seq ('\b'); 55393139Sru seqstate = normal; 55493139Sru break; 55593139Sru case 'e': 55693139Sru To_seq ('\033'); 55793139Sru seqstate = normal; 55893139Sru break; 55993139Sru case 'n': 56093139Sru To_seq ('\n'); 56193139Sru seqstate = normal; 56293139Sru break; 56393139Sru case 'r': 56493139Sru To_seq ('\r'); 56593139Sru seqstate = normal; 56693139Sru break; 56793139Sru case 't': 56893139Sru To_seq ('\t'); 56993139Sru seqstate = normal; 57093139Sru break; 57193139Sru case 'm': 57293139Sru meta = 1; 57393139Sru seqstate = normal; 57493139Sru break; 57593139Sru case 'k': 57693139Sru seqstate = special_key; 57793139Sru break; 57893139Sru default: 579116525Sru /* Backslash followed by any other char 58093139Sru just means that char. */ 58193139Sru To_seq (c); 58293139Sru seqstate = normal; 58393139Sru break; 58493139Sru } 58593139Sru break; 58693139Sru 58793139Sru case octal: 58893139Sru switch (c) 58993139Sru { 59093139Sru case '0': case '1': case '2': case '3': 59193139Sru case '4': case '5': case '6': case '7': 59293139Sru if (++ocnt <= 3) 59393139Sru oval = oval * 8 + c - '0'; 59493139Sru if (ocnt == 3) 59593139Sru seqstate = normal; 59693139Sru break; 59793139Sru default: 59893139Sru ocnt = 4; 59993139Sru seqstate = normal; 60093139Sru rescan = 1; 60193139Sru break; 60293139Sru } 60393139Sru if (seqstate != octal) 60493139Sru { 60593139Sru if (oval) 60693139Sru To_seq (oval); 60793139Sru else 60893139Sru { 609146515Sru syntax_error (filename, lnum, 610146515Sru _("NUL character (\\000) not permitted"), 611146515Sru NULL, NULL, NULL, NULL); 61293139Sru error = 1; 61393139Sru } 61493139Sru } 61593139Sru break; 61693139Sru 61793139Sru case special_key: 61893139Sru To_seq (SK_ESCAPE); 61993139Sru switch (c) 62093139Sru { 62193139Sru case 'u': To_seq (SK_UP_ARROW); break; 62293139Sru case 'd': To_seq (SK_DOWN_ARROW); break; 62393139Sru case 'r': To_seq (SK_RIGHT_ARROW); break; 62493139Sru case 'l': To_seq (SK_LEFT_ARROW); break; 62593139Sru case 'U': To_seq (SK_PAGE_UP); break; 62693139Sru case 'D': To_seq (SK_PAGE_DOWN); break; 62793139Sru case 'h': To_seq (SK_HOME); break; 62893139Sru case 'e': To_seq (SK_END); break; 62993139Sru case 'x': To_seq (SK_DELETE); break; 63093139Sru default: To_seq (SK_LITERAL); rescan = 1; break; 63193139Sru } 63293139Sru seqstate = normal; 63393139Sru break; 63493139Sru 63593139Sru case control: 63693139Sru if (CONTROL (c)) 63793139Sru To_seq (CONTROL (c)); 63893139Sru else 63993139Sru { 640146515Sru syntax_error (filename, lnum, 641146515Sru (char *) _("NUL character (^%c) not permitted"), 642146515Sru (void *) (long) c, NULL, NULL, NULL); 64393139Sru error = 1; 64493139Sru } 64593139Sru seqstate = normal; 64693139Sru break; 64793139Sru } 64893139Sru break; 64993139Sru 65093139Sru case got_keyseq: 65193139Sru if (isspace (c) && c != '\n') 65293139Sru break; 65393139Sru state = get_action; 65493139Sru alen = 0; 65593139Sru /* fall through */ 65693139Sru case get_action: 65793139Sru if (c == '\n' || isspace (c)) 65893139Sru { 65993139Sru int a; 66093139Sru 66193139Sru state = got_action; 66293139Sru rescan = 1; 66393139Sru if (alen == 0) 66493139Sru { 665146515Sru syntax_error (filename, lnum, (char *) _("missing action name"), 666146515Sru (void *) (long) c, NULL, NULL, NULL); 66793139Sru error = 1; 66893139Sru } 66993139Sru else 67093139Sru { 67193139Sru act[alen] = '\0'; 67293139Sru a = lookup_action (act); 67393139Sru if (a != -1) 67493139Sru { 67593139Sru char av = a; 67693139Sru 67793139Sru if (!(add_to_section (§ions[section], seq, slen) 67893139Sru && add_to_section (§ions[section], "", 1) 67993139Sru && add_to_section (§ions[section], &av, 1))) 68093139Sru { 681146515Sru syntax_error (filename, lnum, _("section too long"), 682146515Sru NULL, NULL, NULL, NULL); 68393139Sru error = 1; 68493139Sru } 68593139Sru } 68693139Sru else 68793139Sru { 688146515Sru syntax_error (filename, lnum, _("unknown action `%s'"), 689146515Sru act, NULL, NULL, NULL); 69093139Sru error = 1; 69193139Sru } 69293139Sru } 69393139Sru } 69493139Sru else if (alen < sizeof act - 1) 69593139Sru act[alen++] = c; 69693139Sru else 69793139Sru { 698146515Sru syntax_error (filename, lnum, _("action name too long"), 699146515Sru NULL, NULL, NULL, NULL); 70093139Sru error = 1; 70193139Sru } 70293139Sru break; 703116525Sru 70493139Sru case got_action: 70593139Sru if (c == '#') 70693139Sru state = in_trailing_comment; 70793139Sru else if (c == '\n') 70893139Sru state = start_of_line; 70993139Sru else if (!isspace (c)) 71093139Sru { 711146515Sru syntax_error (filename, lnum, 712146515Sru _("extra characters following action `%s'"), 713146515Sru act, NULL, NULL, NULL); 71493139Sru error = 1; 71593139Sru } 71693139Sru break; 71793139Sru 71893139Sru case get_varname: 71993139Sru if (c == '=') 72093139Sru { 72193139Sru if (varlen == 0) 72293139Sru { 723146515Sru syntax_error (filename, lnum, _("missing variable name"), 724146515Sru NULL, NULL, NULL, NULL); 72593139Sru error = 1; 72693139Sru } 72793139Sru state = get_value; 72893139Sru vallen = 0; 72993139Sru } 73093139Sru else if (c == '\n' || isspace (c)) 73193139Sru { 732146515Sru syntax_error (filename, lnum, 733146515Sru _("missing `=' immediately after variable name"), 734146515Sru NULL, NULL, NULL, NULL); 73593139Sru error = 1; 73693139Sru } 73793139Sru else if (varlen < sizeof varn) 73893139Sru varn[varlen++] = c; 73993139Sru else 74093139Sru { 741146515Sru syntax_error (filename, lnum, _("variable name too long"), 742146515Sru NULL, NULL, NULL, NULL); 74393139Sru error = 1; 74493139Sru } 74593139Sru break; 746116525Sru 74793139Sru case get_value: 74893139Sru if (c == '\n') 74993139Sru { 75093139Sru state = start_of_line; 75193139Sru if (!(add_to_section (§ions[section], varn, varlen) 75293139Sru && add_to_section (§ions[section], "", 1) 75393139Sru && add_to_section (§ions[section], val, vallen) 75493139Sru && add_to_section (§ions[section], "", 1))) 75593139Sru { 756146515Sru syntax_error (filename, lnum, _("section too long"), 757146515Sru NULL, NULL, NULL, NULL); 75893139Sru error = 1; 75993139Sru } 76093139Sru } 76193139Sru else if (vallen < sizeof val) 76293139Sru val[vallen++] = c; 76393139Sru else 76493139Sru { 765146515Sru syntax_error (filename, lnum, _("value too long"), 766146515Sru NULL, NULL, NULL, NULL); 76793139Sru error = 1; 76893139Sru } 76993139Sru break; 770146515Sru 771146515Sru case get_equals: 772146515Sru case got_equals: 773146515Sru case got_varname: 774146515Sru break; 77593139Sru } 77693139Sru } 77793139Sru 77893139Sru#undef To_seq 77993139Sru 78093139Sru return !error; 78193139Sru} 78293139Sru 78393139Sru/* Add some characters to a section's data. Return true if all the 78493139Sru characters fit, or false if the section's size limit was exceeded. 78593139Sru */ 78693139Srustatic int 787146515Sruadd_to_section (struct sect *s, const char *str, unsigned int len) 78893139Sru{ 78993139Sru if (s->cur + len > sizeof s->data) 79093139Sru return 0; 791146515Sru strncpy ((char *) s->data + s->cur, str, len); 79293139Sru s->cur += len; 79393139Sru return 1; 79493139Sru} 79593139Sru 79693139Sru/* Translate from an action name to its numeric code. This uses the 79793139Sru auto-generated array in key.c. 79893139Sru */ 79993139Srustatic int 800146515Srulookup_action (const char *actname) 80193139Sru{ 80293139Sru int i; 80393139Sru 80493139Sru if (strcmp ("invalid", actname) == 0) 80593139Sru return A_INVALID; 80693139Sru for (i = 0; function_key_array[i].name != NULL; i++) 80793139Sru if (strcmp (function_key_array[i].name, actname) == 0) 80893139Sru return function_key_array[i].code; 80993139Sru return -1; 81093139Sru} 81193139Sru 81293139Sru/* Put an integer to an infokey file. 81393139Sru Integers are stored as two bytes, low order first, 81493139Sru in radix INFOKEY_RADIX. 81593139Sru */ 81693139Srustatic int 817146515Sruputint (int i, FILE *fp) 81893139Sru{ 81993139Sru return fputc (i % INFOKEY_RADIX, fp) != EOF 82093139Sru && fputc ((i / INFOKEY_RADIX) % INFOKEY_RADIX, fp) != EOF; 82193139Sru} 82293139Sru 82393139Sru/* Write an entire section to an infokey file. If the section is 82493139Sru empty, simply omit it. 82593139Sru */ 82693139Srustatic int 827146515Sruputsect (struct sect *s, int code, FILE *fp) 82893139Sru{ 82993139Sru if (s->cur == 0) 83093139Sru return 1; 83193139Sru return fputc (code, fp) != EOF 83293139Sru && putint (s->cur, fp) 83393139Sru && fwrite (s->data, s->cur, 1, fp) == 1; 83493139Sru} 83593139Sru 83693139Sru/* Write an entire infokey file, given an array containing its sections. 83793139Sru */ 83893139Srustatic int 839146515Sruwrite_infokey_file (FILE *fp, struct sect *sections) 84093139Sru{ 84193139Sru /* Get rid of sections with no effect. */ 84293139Sru if (sections[info].cur == 1 && sections[info].data[0] == 0) 84393139Sru sections[info].cur = 0; 84493139Sru if (sections[ea].cur == 1 && sections[ea].data[0] == 0) 84593139Sru sections[ea].cur = 0; 84693139Sru 84793139Sru /* Write all parts of the file out in order (no lseeks), 84893139Sru checking for errors all the way. */ 84993139Sru return fputc (INFOKEY_MAGIC_S0, fp) != EOF 85093139Sru && fputc (INFOKEY_MAGIC_S1, fp) != EOF 85193139Sru && fputc (INFOKEY_MAGIC_S2, fp) != EOF 85293139Sru && fputc (INFOKEY_MAGIC_S3, fp) != EOF 85393139Sru && fputs (VERSION, fp) != EOF 85493139Sru && fputc ('\0', fp) != EOF 85593139Sru && putsect (§ions[info], INFOKEY_SECTION_INFO, fp) 85693139Sru && putsect (§ions[ea], INFOKEY_SECTION_EA, fp) 85793139Sru && putsect (§ions[var], INFOKEY_SECTION_VAR, fp) 85893139Sru && fputc (INFOKEY_MAGIC_E0, fp) != EOF 85993139Sru && fputc (INFOKEY_MAGIC_E1, fp) != EOF 86093139Sru && fputc (INFOKEY_MAGIC_E2, fp) != EOF 86193139Sru && fputc (INFOKEY_MAGIC_E3, fp) != EOF; 86293139Sru} 86393139Sru 86493139Sru 86593139Sru/* Error handling. */ 86693139Sru 86793139Sru/* Give the user a "syntax error" message in the form 86893139Sru progname: "filename", line N: message 86993139Sru */ 87093139Srustatic void 871146515Sruerror_message (int error_code, const char *fmt, 872146515Sru const void *a1, const void *a2, const void *a3, const void *a4) 87393139Sru{ 87493139Sru fprintf (stderr, "%s: ", program_name); 87593139Sru fprintf (stderr, fmt, a1, a2, a3, a4); 87693139Sru if (error_code) 87793139Sru fprintf (stderr, " - %s", strerror (error_code)); 87893139Sru fprintf (stderr, "\n"); 87993139Sru} 88093139Sru 88193139Sru/* Give the user a generic error message in the form 88293139Sru progname: message 88393139Sru */ 88493139Srustatic void 885146515Srusyntax_error (const char *filename, 886146515Sru unsigned int linenum, const char *fmt, 887146515Sru const void *a1, const void *a2, const void *a3, const void *a4) 88893139Sru{ 88993139Sru fprintf (stderr, "%s: ", program_name); 89093139Sru fprintf (stderr, _("\"%s\", line %u: "), filename, linenum); 89193139Sru fprintf (stderr, fmt, a1, a2, a3, a4); 89293139Sru fprintf (stderr, "\n"); 89393139Sru} 89493139Sru 89593139Sru/* Produce a gentle rtfm. */ 89693139Srustatic void 897146515Srusuggest_help (void) 89893139Sru{ 89993139Sru fprintf (stderr, _("Try --help for more information.\n")); 90093139Sru} 90193139Sru 90293139Sru/* Produce a scaled down description of the available options to Info. */ 90393139Srustatic void 904146515Srushort_help (void) 90593139Sru{ 906100513Sru printf (_("\ 90793139SruUsage: %s [OPTION]... [INPUT-FILE]\n\ 90893139Sru\n\ 90993139SruCompile infokey source file to infokey file. Reads INPUT-FILE (default\n\ 91093139Sru$HOME/.infokey) and writes compiled key file to (by default) $HOME/.info.\n\ 91193139Sru\n\ 91293139SruOptions:\n\ 91393139Sru --output FILE output to FILE instead of $HOME/.info\n\ 91493139Sru --help display this help and exit.\n\ 91593139Sru --version display version information and exit.\n\ 916100513Sru"), program_name); 917100513Sru 918100513Sru puts (_("\n\ 91993139SruEmail bug reports to bug-texinfo@gnu.org,\n\ 92093139Srugeneral questions and discussion to help-texinfo@gnu.org.\n\ 921100513SruTexinfo home page: http://www.gnu.org/software/texinfo/")); 922100513Sru 92393139Sru xexit (0); 92493139Sru} 925