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