4 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2004
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
29 cset cs_field_name
= csalpha
;
39 input_item(string
&, const char *, int = 1);
44 int get_location(const char **, int *);
46 friend class input_stack
;
49 input_item::input_item(string
&s
, const char *fn
, int ln
)
50 : filename(strsave(fn
)), first_lineno(ln
)
53 ptr
= buffer
.contents();
54 end
= ptr
+ buffer
.length();
57 input_item::~input_item()
62 inline int input_item::peek_char()
67 return (unsigned char)*ptr
;
70 inline int input_item::get_char()
75 return (unsigned char)*ptr
++;
78 inline void input_item::skip_char()
83 int input_item::get_location(const char **filenamep
, int *linenop
)
85 *filenamep
= filename
;
86 if (ptr
== buffer
.contents())
87 *linenop
= first_lineno
;
89 int ln
= first_lineno
;
90 const char *e
= ptr
- 1;
91 for (const char *p
= buffer
.contents(); p
< e
; p
++)
100 static input_item
*top
;
103 static int get_char();
104 static int peek_char();
105 static void skip_char() { top
->skip_char(); }
106 static void push_file(const char *);
107 static void push_string(string
&, const char *, int);
108 static void error(const char *format
,
109 const errarg
&arg1
= empty_errarg
,
110 const errarg
&arg2
= empty_errarg
,
111 const errarg
&arg3
= empty_errarg
);
114 input_item
*input_stack::top
= 0;
116 void input_stack::init()
119 input_item
*tem
= top
;
125 int input_stack::get_char()
128 int c
= top
->get_char();
131 input_item
*tem
= top
;
138 int input_stack::peek_char()
141 int c
= top
->peek_char();
144 input_item
*tem
= top
;
151 void input_stack::push_file(const char *fn
)
154 if (strcmp(fn
, "-") == 0) {
156 fn
= "<standard input>";
162 error("can't open `%1': %2", fn
, strerror(errno
));
171 if (bol
&& c
== '.') {
172 // replace lines beginning with .R1 or .R2 with a blank line
176 if (c
== '1' || c
== '2') {
179 if (compatible_flag
|| c
== ' ' || c
== '\n' || c
== EOF
) {
180 while (c
!= '\n' && c
!= EOF
)
199 if (invalid_input_char(c
))
200 error_with_file_and_line(fn
, lineno
,
201 "invalid input character code %1", int(c
));
214 if (buf
.length() > 0 && buf
[buf
.length() - 1] != '\n')
216 input_item
*it
= new input_item(buf
, fn
);
221 void input_stack::push_string(string
&s
, const char *filename
, int lineno
)
223 input_item
*it
= new input_item(s
, filename
, lineno
);
228 void input_stack::error(const char *format
, const errarg
&arg1
,
229 const errarg
&arg2
, const errarg
&arg3
)
231 const char *filename
;
233 for (input_item
*it
= top
; it
; it
= it
->next
)
234 if (it
->get_location(&filename
, &lineno
)) {
235 error_with_file_and_line(filename
, lineno
, format
, arg1
, arg2
, arg3
);
238 ::error(format
, arg1
, arg2
, arg3
);
241 void command_error(const char *format
, const errarg
&arg1
,
242 const errarg
&arg2
, const errarg
&arg3
)
244 input_stack::error(format
, arg1
, arg2
, arg3
);
247 // # not recognized in ""
248 // \<newline> is recognized in ""
249 // # does not conceal newline
250 // if missing closing quote, word extends to end of line
251 // no special treatment of \ other than before newline
252 // \<newline> not recognized after #
253 // ; allowed as alternative to newline
254 // ; not recognized in ""
255 // don't clear word_buffer; just append on
256 // return -1 for EOF, 0 for newline, 1 for word
258 int get_word(string
&word_buffer
)
260 int c
= input_stack::get_char();
264 c
= input_stack::get_char();
265 } while (c
!= '\n' && c
!= EOF
);
268 if (c
== '\\' && input_stack::peek_char() == '\n')
269 input_stack::skip_char();
270 else if (c
!= ' ' && c
!= '\t')
272 c
= input_stack::get_char();
276 if (c
== '\n' || c
== ';')
280 c
= input_stack::peek_char();
281 if (c
== EOF
|| c
== '\n')
283 input_stack::skip_char();
285 int d
= input_stack::peek_char();
287 input_stack::skip_char();
291 else if (c
== '\\') {
292 int d
= input_stack::peek_char();
294 input_stack::skip_char();
305 c
= input_stack::peek_char();
306 if (c
== ' ' || c
== '\t' || c
== '\n' || c
== '#' || c
== ';')
308 input_stack::skip_char();
310 int d
= input_stack::peek_char();
312 input_stack::skip_char();
327 // This is for debugging.
329 static void echo_command(int argc
, argument
*argv
)
331 for (int i
= 0; i
< argc
; i
++)
332 fprintf(stderr
, "%s\n", argv
[i
].s
);
335 static void include_command(int argc
, argument
*argv
)
338 input_stack::push_file(argv
[0].s
);
341 static void capitalize_command(int argc
, argument
*argv
)
344 capitalize_fields
= argv
[0].s
;
346 capitalize_fields
.clear();
349 static void accumulate_command(int, argument
*)
354 static void no_accumulate_command(int, argument
*)
359 static void move_punctuation_command(int, argument
*)
361 move_punctuation
= 1;
364 static void no_move_punctuation_command(int, argument
*)
366 move_punctuation
= 0;
369 static void sort_command(int argc
, argument
*argv
)
374 sort_fields
= argv
[0].s
;
378 static void no_sort_command(int, argument
*)
383 static void articles_command(int argc
, argument
*argv
)
387 for (i
= 0; i
< argc
; i
++) {
388 articles
+= argv
[i
].s
;
391 int len
= articles
.length();
392 for (i
= 0; i
< len
; i
++)
393 articles
[i
] = cmlower(articles
[i
]);
396 static void database_command(int argc
, argument
*argv
)
398 for (int i
= 0; i
< argc
; i
++)
399 database_list
.add_file(argv
[i
].s
);
402 static void default_database_command(int, argument
*)
407 static void no_default_database_command(int, argument
*)
412 static void bibliography_command(int argc
, argument
*argv
)
414 const char *saved_filename
= current_filename
;
415 int saved_lineno
= current_lineno
;
416 int saved_label_in_text
= label_in_text
;
419 fputs(".]<\n", stdout
);
420 for (int i
= 0; i
< argc
; i
++)
425 fputs(".]>\n", stdout
);
426 current_filename
= saved_filename
;
427 current_lineno
= saved_lineno
;
428 label_in_text
= saved_label_in_text
;
431 static void annotate_command(int argc
, argument
*argv
)
434 annotation_field
= argv
[0].s
[0];
436 annotation_field
= 'X';
438 annotation_macro
= argv
[1].s
;
440 annotation_macro
= "AP";
443 static void no_annotate_command(int, argument
*)
445 annotation_macro
.clear();
446 annotation_field
= -1;
449 static void reverse_command(int, argument
*argv
)
451 reverse_fields
= argv
[0].s
;
454 static void no_reverse_command(int, argument
*)
456 reverse_fields
.clear();
459 static void abbreviate_command(int argc
, argument
*argv
)
461 abbreviate_fields
= argv
[0].s
;
462 period_before_initial
= argc
> 1 ? argv
[1].s
: ". ";
463 period_before_last_name
= argc
> 2 ? argv
[2].s
: ". ";
464 period_before_other
= argc
> 3 ? argv
[3].s
: ". ";
465 period_before_hyphen
= argc
> 4 ? argv
[4].s
: ".";
468 static void no_abbreviate_command(int, argument
*)
470 abbreviate_fields
.clear();
473 string search_ignore_fields
;
475 static void search_ignore_command(int argc
, argument
*argv
)
478 search_ignore_fields
= argv
[0].s
;
480 search_ignore_fields
= "XYZ";
481 search_ignore_fields
+= '\0';
482 linear_ignore_fields
= search_ignore_fields
.contents();
485 static void no_search_ignore_command(int, argument
*)
487 linear_ignore_fields
= "";
490 static void search_truncate_command(int argc
, argument
*argv
)
493 linear_truncate_len
= argv
[0].n
;
495 linear_truncate_len
= 6;
498 static void no_search_truncate_command(int, argument
*)
500 linear_truncate_len
= -1;
503 static void discard_command(int argc
, argument
*argv
)
506 discard_fields
= "XYZ";
508 discard_fields
= argv
[0].s
;
512 static void no_discard_command(int, argument
*)
514 discard_fields
.clear();
517 static void label_command(int, argument
*argv
)
519 set_label_spec(argv
[0].s
);
522 static void abbreviate_label_ranges_command(int argc
, argument
*argv
)
524 abbreviate_label_ranges
= 1;
525 label_range_indicator
= argc
> 0 ? argv
[0].s
: "-";
528 static void no_abbreviate_label_ranges_command(int, argument
*)
530 abbreviate_label_ranges
= 0;
533 static void label_in_reference_command(int, argument
*)
535 label_in_reference
= 1;
538 static void no_label_in_reference_command(int, argument
*)
540 label_in_reference
= 0;
543 static void label_in_text_command(int, argument
*)
548 static void no_label_in_text_command(int, argument
*)
553 static void sort_adjacent_labels_command(int, argument
*)
555 sort_adjacent_labels
= 1;
558 static void no_sort_adjacent_labels_command(int, argument
*)
560 sort_adjacent_labels
= 0;
563 static void date_as_label_command(int argc
, argument
*argv
)
565 if (set_date_label_spec(argc
> 0 ? argv
[0].s
: "D%a*"))
569 static void no_date_as_label_command(int, argument
*)
574 static void short_label_command(int, argument
*argv
)
576 if (set_short_label_spec(argv
[0].s
))
577 short_label_flag
= 1;
580 static void no_short_label_command(int, argument
*)
582 short_label_flag
= 0;
585 static void compatible_command(int, argument
*)
590 static void no_compatible_command(int, argument
*)
595 static void join_authors_command(int argc
, argument
*argv
)
597 join_authors_exactly_two
= argv
[0].s
;
598 join_authors_default
= argc
> 1 ? argv
[1].s
: argv
[0].s
;
599 join_authors_last_two
= argc
== 3 ? argv
[2].s
: argv
[0].s
;
602 static void bracket_label_command(int, argument
*argv
)
604 pre_label
= argv
[0].s
;
605 post_label
= argv
[1].s
;
606 sep_label
= argv
[2].s
;
609 static void separate_label_second_parts_command(int, argument
*argv
)
611 separate_label_second_parts
= argv
[0].s
;
614 static void et_al_command(int argc
, argument
*argv
)
617 et_al_min_elide
= argv
[1].n
;
618 if (et_al_min_elide
< 1)
620 et_al_min_total
= argc
>= 3 ? argv
[2].n
: 0;
623 static void no_et_al_command(int, argument
*)
629 typedef void (*command_t
)(int, argument
*);
631 /* arg_types is a string describing the numbers and types of arguments.
632 s means a string, i means an integer, f is a list of fields, F is
634 ? means that the previous argument is optional, * means that the
635 previous argument can occur any number of times. */
640 const char *arg_types
;
641 } command_table
[] = {
642 { "include", include_command
, "s" },
643 { "echo", echo_command
, "s*" },
644 { "capitalize", capitalize_command
, "f?" },
645 { "accumulate", accumulate_command
, "" },
646 { "no-accumulate", no_accumulate_command
, "" },
647 { "move-punctuation", move_punctuation_command
, "" },
648 { "no-move-punctuation", no_move_punctuation_command
, "" },
649 { "sort", sort_command
, "s?" },
650 { "no-sort", no_sort_command
, "" },
651 { "articles", articles_command
, "s*" },
652 { "database", database_command
, "ss*" },
653 { "default-database", default_database_command
, "" },
654 { "no-default-database", no_default_database_command
, "" },
655 { "bibliography", bibliography_command
, "ss*" },
656 { "annotate", annotate_command
, "F?s?" },
657 { "no-annotate", no_annotate_command
, "" },
658 { "reverse", reverse_command
, "s" },
659 { "no-reverse", no_reverse_command
, "" },
660 { "abbreviate", abbreviate_command
, "ss?s?s?s?" },
661 { "no-abbreviate", no_abbreviate_command
, "" },
662 { "search-ignore", search_ignore_command
, "f?" },
663 { "no-search-ignore", no_search_ignore_command
, "" },
664 { "search-truncate", search_truncate_command
, "i?" },
665 { "no-search-truncate", no_search_truncate_command
, "" },
666 { "discard", discard_command
, "f?" },
667 { "no-discard", no_discard_command
, "" },
668 { "label", label_command
, "s" },
669 { "abbreviate-label-ranges", abbreviate_label_ranges_command
, "s?" },
670 { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command
, "" },
671 { "label-in-reference", label_in_reference_command
, "" },
672 { "no-label-in-reference", no_label_in_reference_command
, "" },
673 { "label-in-text", label_in_text_command
, "" },
674 { "no-label-in-text", no_label_in_text_command
, "" },
675 { "sort-adjacent-labels", sort_adjacent_labels_command
, "" },
676 { "no-sort-adjacent-labels", no_sort_adjacent_labels_command
, "" },
677 { "date-as-label", date_as_label_command
, "s?" },
678 { "no-date-as-label", no_date_as_label_command
, "" },
679 { "short-label", short_label_command
, "s" },
680 { "no-short-label", no_short_label_command
, "" },
681 { "compatible", compatible_command
, "" },
682 { "no-compatible", no_compatible_command
, "" },
683 { "join-authors", join_authors_command
, "sss?" },
684 { "bracket-label", bracket_label_command
, "sss" },
685 { "separate-label-second-parts", separate_label_second_parts_command
, "s" },
686 { "et-al", et_al_command
, "sii?" },
687 { "no-et-al", no_et_al_command
, "" },
690 static int check_args(const char *types
, const char *name
,
691 int argc
, argument
*argv
)
698 else if (types
[1] == '*') {
699 assert(types
[2] == '\0');
703 input_stack::error("missing argument for command `%1'", name
);
713 long n
= strtol(argv
->s
, &ptr
, 10);
714 if ((n
== 0 && ptr
== argv
->s
)
716 input_stack::error("argument %1 for command `%2' must be an integer",
725 for (const char *ptr
= argv
->s
; *ptr
!= '\0'; ptr
++)
726 if (!cs_field_name(*ptr
)) {
727 input_stack::error("argument %1 for command `%2' must be a list of fields",
734 if (argv
->s
[0] == '\0' || argv
->s
[1] != '\0'
735 || !cs_field_name(argv
->s
[0])) {
736 input_stack::error("argument %1 for command `%2' must be a field name",
746 else if (types
[1] != '*')
753 input_stack::error("too many arguments for command `%1'", name
);
759 static void execute_command(const char *name
, int argc
, argument
*argv
)
761 for (unsigned int i
= 0;
762 i
< sizeof(command_table
)/sizeof(command_table
[0]); i
++)
763 if (strcmp(name
, command_table
[i
].name
) == 0) {
764 if (check_args(command_table
[i
].arg_types
, name
, argc
, argv
))
765 (*command_table
[i
].func
)(argc
, argv
);
768 input_stack::error("unknown command `%1'", name
);
771 static void command_loop()
776 int res
= get_word(command
);
784 while ((res
= get_word(command
)) == 1) {
788 argument
*argv
= new argument
[argc
];
789 const char *ptr
= command
.contents();
790 for (int i
= 0; i
< argc
; i
++)
791 argv
[i
].s
= ptr
= strchr(ptr
, '\0') + 1;
792 execute_command(command
.contents(), argc
, argv
);
799 void process_commands(const char *file
)
802 input_stack::push_file(file
);
806 void process_commands(string
&s
, const char *file
, int lineno
)
809 input_stack::push_string(s
, file
, lineno
);