2 /* Copyright (C) 1989-2014 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
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/>. */
25 cset cs_field_name = csalpha;
35 input_item(string &, const char *, int = 1);
40 int get_location(const char **, int *);
42 friend class input_stack;
45 input_item::input_item(string &s, const char *fn, int ln)
46 : filename(strsave(fn)), first_lineno(ln)
49 ptr = buffer.contents();
50 end = ptr + buffer.length();
53 input_item::~input_item()
58 inline int input_item::peek_char()
63 return (unsigned char)*ptr;
66 inline int input_item::get_char()
71 return (unsigned char)*ptr++;
74 inline void input_item::skip_char()
79 int input_item::get_location(const char **filenamep, int *linenop)
81 *filenamep = filename;
82 if (ptr == buffer.contents())
83 *linenop = first_lineno;
85 int ln = first_lineno;
86 const char *e = ptr - 1;
87 for (const char *p = buffer.contents(); p < e; p++)
96 static input_item *top;
99 static int get_char();
100 static int peek_char();
101 static void skip_char() { top->skip_char(); }
102 static void push_file(const char *);
103 static void push_string(string &, const char *, int);
104 static void error(const char *format,
105 const errarg &arg1 = empty_errarg,
106 const errarg &arg2 = empty_errarg,
107 const errarg &arg3 = empty_errarg);
110 input_item *input_stack::top = 0;
112 void input_stack::init()
115 input_item *tem = top;
121 int input_stack::get_char()
124 int c = top->get_char();
127 input_item *tem = top;
134 int input_stack::peek_char()
137 int c = top->peek_char();
140 input_item *tem = top;
147 void input_stack::push_file(const char *fn)
150 if (strcmp(fn, "-") == 0) {
152 fn = "<standard input>";
158 error("can't open `%1': %2", fn, strerror(errno));
167 if (bol && c == '.') {
168 // replace lines beginning with .R1 or .R2 with a blank line
172 if (c == '1' || c == '2') {
175 if (compatible_flag || c == ' ' || c == '\n' || c == EOF) {
176 while (c != '\n' && c != EOF)
195 if (invalid_input_char(c))
196 error_with_file_and_line(fn, lineno,
197 "invalid input character code %1", int(c));
210 if (buf.length() > 0 && buf[buf.length() - 1] != '\n')
212 input_item *it = new input_item(buf, fn);
217 void input_stack::push_string(string &s, const char *filename, int lineno)
219 input_item *it = new input_item(s, filename, lineno);
224 void input_stack::error(const char *format, const errarg &arg1,
225 const errarg &arg2, const errarg &arg3)
227 const char *filename;
229 for (input_item *it = top; it; it = it->next)
230 if (it->get_location(&filename, &lineno)) {
231 error_with_file_and_line(filename, lineno, format, arg1, arg2, arg3);
234 ::error(format, arg1, arg2, arg3);
237 void command_error(const char *format, const errarg &arg1,
238 const errarg &arg2, const errarg &arg3)
240 input_stack::error(format, arg1, arg2, arg3);
243 // # not recognized in ""
244 // \<newline> is recognized in ""
245 // # does not conceal newline
246 // if missing closing quote, word extends to end of line
247 // no special treatment of \ other than before newline
248 // \<newline> not recognized after #
249 // ; allowed as alternative to newline
250 // ; not recognized in ""
251 // don't clear word_buffer; just append on
252 // return -1 for EOF, 0 for newline, 1 for word
254 int get_word(string &word_buffer)
256 int c = input_stack::get_char();
260 c = input_stack::get_char();
261 } while (c != '\n' && c != EOF);
264 if (c == '\\' && input_stack::peek_char() == '\n')
265 input_stack::skip_char();
266 else if (c != ' ' && c != '\t')
268 c = input_stack::get_char();
272 if (c == '\n' || c == ';')
276 c = input_stack::peek_char();
277 if (c == EOF || c == '\n')
279 input_stack::skip_char();
281 int d = input_stack::peek_char();
283 input_stack::skip_char();
287 else if (c == '\\') {
288 int d = input_stack::peek_char();
290 input_stack::skip_char();
301 c = input_stack::peek_char();
302 if (c == ' ' || c == '\t' || c == '\n' || c == '#' || c == ';')
304 input_stack::skip_char();
306 int d = input_stack::peek_char();
308 input_stack::skip_char();
323 // This is for debugging.
325 static void echo_command(int argc, argument *argv)
327 for (int i = 0; i < argc; i++)
328 fprintf(stderr, "%s\n", argv[i].s);
331 static void include_command(int argc, argument *argv)
334 input_stack::push_file(argv[0].s);
337 static void capitalize_command(int argc, argument *argv)
340 capitalize_fields = argv[0].s;
342 capitalize_fields.clear();
345 static void accumulate_command(int, argument *)
350 static void no_accumulate_command(int, argument *)
355 static void move_punctuation_command(int, argument *)
357 move_punctuation = 1;
360 static void no_move_punctuation_command(int, argument *)
362 move_punctuation = 0;
365 static void sort_command(int argc, argument *argv)
370 sort_fields = argv[0].s;
374 static void no_sort_command(int, argument *)
379 static void articles_command(int argc, argument *argv)
383 for (i = 0; i < argc; i++) {
384 articles += argv[i].s;
387 int len = articles.length();
388 for (i = 0; i < len; i++)
389 articles[i] = cmlower(articles[i]);
392 static void database_command(int argc, argument *argv)
394 for (int i = 0; i < argc; i++)
395 database_list.add_file(argv[i].s);
398 static void default_database_command(int, argument *)
403 static void no_default_database_command(int, argument *)
408 static void bibliography_command(int argc, argument *argv)
410 have_bibliography = 1;
411 const char *saved_filename = current_filename;
412 int saved_lineno = current_lineno;
413 int saved_label_in_text = label_in_text;
416 fputs(".]<\n", stdout);
417 for (int i = 0; i < argc; i++)
422 fputs(".]>\n", stdout);
423 current_filename = saved_filename;
424 current_lineno = saved_lineno;
425 label_in_text = saved_label_in_text;
428 static void annotate_command(int argc, argument *argv)
431 annotation_field = argv[0].s[0];
433 annotation_field = 'X';
435 annotation_macro = argv[1].s;
437 annotation_macro = "AP";
440 static void no_annotate_command(int, argument *)
442 annotation_macro.clear();
443 annotation_field = -1;
446 static void reverse_command(int, argument *argv)
448 reverse_fields = argv[0].s;
451 static void no_reverse_command(int, argument *)
453 reverse_fields.clear();
456 static void abbreviate_command(int argc, argument *argv)
458 abbreviate_fields = argv[0].s;
459 period_before_initial = argc > 1 ? argv[1].s : ". ";
460 period_before_last_name = argc > 2 ? argv[2].s : ". ";
461 period_before_other = argc > 3 ? argv[3].s : ". ";
462 period_before_hyphen = argc > 4 ? argv[4].s : ".";
465 static void no_abbreviate_command(int, argument *)
467 abbreviate_fields.clear();
470 string search_ignore_fields;
472 static void search_ignore_command(int argc, argument *argv)
475 search_ignore_fields = argv[0].s;
477 search_ignore_fields = "XYZ";
478 search_ignore_fields += '\0';
479 linear_ignore_fields = search_ignore_fields.contents();
482 static void no_search_ignore_command(int, argument *)
484 linear_ignore_fields = "";
487 static void search_truncate_command(int argc, argument *argv)
490 linear_truncate_len = argv[0].n;
492 linear_truncate_len = 6;
495 static void no_search_truncate_command(int, argument *)
497 linear_truncate_len = -1;
500 static void discard_command(int argc, argument *argv)
503 discard_fields = "XYZ";
505 discard_fields = argv[0].s;
509 static void no_discard_command(int, argument *)
511 discard_fields.clear();
514 static void label_command(int, argument *argv)
516 set_label_spec(argv[0].s);
519 static void abbreviate_label_ranges_command(int argc, argument *argv)
521 abbreviate_label_ranges = 1;
522 label_range_indicator = argc > 0 ? argv[0].s : "-";
525 static void no_abbreviate_label_ranges_command(int, argument *)
527 abbreviate_label_ranges = 0;
530 static void label_in_reference_command(int, argument *)
532 label_in_reference = 1;
535 static void no_label_in_reference_command(int, argument *)
537 label_in_reference = 0;
540 static void label_in_text_command(int, argument *)
545 static void no_label_in_text_command(int, argument *)
550 static void sort_adjacent_labels_command(int, argument *)
552 sort_adjacent_labels = 1;
555 static void no_sort_adjacent_labels_command(int, argument *)
557 sort_adjacent_labels = 0;
560 static void date_as_label_command(int argc, argument *argv)
562 if (set_date_label_spec(argc > 0 ? argv[0].s : "D%a*"))
566 static void no_date_as_label_command(int, argument *)
571 static void short_label_command(int, argument *argv)
573 if (set_short_label_spec(argv[0].s))
574 short_label_flag = 1;
577 static void no_short_label_command(int, argument *)
579 short_label_flag = 0;
582 static void compatible_command(int, argument *)
587 static void no_compatible_command(int, argument *)
592 static void join_authors_command(int argc, argument *argv)
594 join_authors_exactly_two = argv[0].s;
595 join_authors_default = argc > 1 ? argv[1].s : argv[0].s;
596 join_authors_last_two = argc == 3 ? argv[2].s : argv[0].s;
599 static void bracket_label_command(int, argument *argv)
601 pre_label = argv[0].s;
602 post_label = argv[1].s;
603 sep_label = argv[2].s;
606 static void separate_label_second_parts_command(int, argument *argv)
608 separate_label_second_parts = argv[0].s;
611 static void et_al_command(int argc, argument *argv)
614 et_al_min_elide = argv[1].n;
615 if (et_al_min_elide < 1)
617 et_al_min_total = argc >= 3 ? argv[2].n : 0;
620 static void no_et_al_command(int, argument *)
626 typedef void (*command_t)(int, argument *);
628 /* arg_types is a string describing the numbers and types of arguments.
629 s means a string, i means an integer, f is a list of fields, F is
631 ? means that the previous argument is optional, * means that the
632 previous argument can occur any number of times. */
637 const char *arg_types;
638 } command_table[] = {
639 { "include", include_command, "s" },
640 { "echo", echo_command, "s*" },
641 { "capitalize", capitalize_command, "f?" },
642 { "accumulate", accumulate_command, "" },
643 { "no-accumulate", no_accumulate_command, "" },
644 { "move-punctuation", move_punctuation_command, "" },
645 { "no-move-punctuation", no_move_punctuation_command, "" },
646 { "sort", sort_command, "s?" },
647 { "no-sort", no_sort_command, "" },
648 { "articles", articles_command, "s*" },
649 { "database", database_command, "ss*" },
650 { "default-database", default_database_command, "" },
651 { "no-default-database", no_default_database_command, "" },
652 { "bibliography", bibliography_command, "ss*" },
653 { "annotate", annotate_command, "F?s?" },
654 { "no-annotate", no_annotate_command, "" },
655 { "reverse", reverse_command, "s" },
656 { "no-reverse", no_reverse_command, "" },
657 { "abbreviate", abbreviate_command, "ss?s?s?s?" },
658 { "no-abbreviate", no_abbreviate_command, "" },
659 { "search-ignore", search_ignore_command, "f?" },
660 { "no-search-ignore", no_search_ignore_command, "" },
661 { "search-truncate", search_truncate_command, "i?" },
662 { "no-search-truncate", no_search_truncate_command, "" },
663 { "discard", discard_command, "f?" },
664 { "no-discard", no_discard_command, "" },
665 { "label", label_command, "s" },
666 { "abbreviate-label-ranges", abbreviate_label_ranges_command, "s?" },
667 { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command, "" },
668 { "label-in-reference", label_in_reference_command, "" },
669 { "no-label-in-reference", no_label_in_reference_command, "" },
670 { "label-in-text", label_in_text_command, "" },
671 { "no-label-in-text", no_label_in_text_command, "" },
672 { "sort-adjacent-labels", sort_adjacent_labels_command, "" },
673 { "no-sort-adjacent-labels", no_sort_adjacent_labels_command, "" },
674 { "date-as-label", date_as_label_command, "s?" },
675 { "no-date-as-label", no_date_as_label_command, "" },
676 { "short-label", short_label_command, "s" },
677 { "no-short-label", no_short_label_command, "" },
678 { "compatible", compatible_command, "" },
679 { "no-compatible", no_compatible_command, "" },
680 { "join-authors", join_authors_command, "sss?" },
681 { "bracket-label", bracket_label_command, "sss" },
682 { "separate-label-second-parts", separate_label_second_parts_command, "s" },
683 { "et-al", et_al_command, "sii?" },
684 { "no-et-al", no_et_al_command, "" },
687 static int check_args(const char *types, const char *name,
688 int argc, argument *argv)
695 else if (types[1] == '*') {
696 assert(types[2] == '\0');
700 input_stack::error("missing argument for command `%1'", name);
710 long n = strtol(argv->s, &ptr, 10);
711 if ((n == 0 && ptr == argv->s)
713 input_stack::error("argument %1 for command `%2' must be an integer",
722 for (const char *ptr = argv->s; *ptr != '\0'; ptr++)
723 if (!cs_field_name(*ptr)) {
724 input_stack::error("argument %1 for command `%2' must be a list of fields",
731 if (argv->s[0] == '\0' || argv->s[1] != '\0'
732 || !cs_field_name(argv->s[0])) {
733 input_stack::error("argument %1 for command `%2' must be a field name",
743 else if (types[1] != '*')
750 input_stack::error("too many arguments for command `%1'", name);
756 static void execute_command(const char *name, int argc, argument *argv)
758 for (unsigned int i = 0;
759 i < sizeof(command_table)/sizeof(command_table[0]); i++)
760 if (strcmp(name, command_table[i].name) == 0) {
761 if (check_args(command_table[i].arg_types, name, argc, argv))
762 (*command_table[i].func)(argc, argv);
765 input_stack::error("unknown command `%1'", name);
768 static void command_loop()
773 int res = get_word(command);
781 while ((res = get_word(command)) == 1) {
785 argument *argv = new argument[argc];
786 const char *ptr = command.contents();
787 for (int i = 0; i < argc; i++)
788 argv[i].s = ptr = strchr(ptr, '\0') + 1;
789 execute_command(command.contents(), argc, argv);
796 void process_commands(const char *file)
799 input_stack::push_file(file);
803 void process_commands(string &s, const char *file, int lineno)
806 input_stack::push_string(s, file, lineno);