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