Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / groff / src / preproc / refer / command.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2004, 2006, 2009
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20
21 #include "refer.h"
22 #include "refid.h"
23 #include "search.h"
24 #include "command.h"
25
26 cset cs_field_name = csalpha;
27
28 class input_item {
29   input_item *next;
30   char *filename;
31   int first_lineno;
32   string buffer;
33   const char *ptr;
34   const char *end;
35 public:
36   input_item(string &, const char *, int = 1);
37   ~input_item();
38   int get_char();
39   int peek_char();
40   void skip_char();
41   int get_location(const char **, int *);
42
43   friend class input_stack;
44 };
45
46 input_item::input_item(string &s, const char *fn, int ln)
47 : filename(strsave(fn)), first_lineno(ln)
48 {
49   buffer.move(s);
50   ptr = buffer.contents();
51   end = ptr + buffer.length();
52 }
53
54 input_item::~input_item()
55 {
56   a_delete filename;
57 }
58
59 inline int input_item::peek_char()
60 {
61   if (ptr >= end)
62     return EOF;
63   else
64     return (unsigned char)*ptr;
65 }
66
67 inline int input_item::get_char()
68 {
69   if (ptr >= end)
70     return EOF;
71   else
72     return (unsigned char)*ptr++;
73 }
74
75 inline void input_item::skip_char()
76 {
77   ptr++;
78 }
79
80 int input_item::get_location(const char **filenamep, int *linenop)
81 {
82   *filenamep = filename;
83   if (ptr == buffer.contents())
84     *linenop = first_lineno;
85   else {
86     int ln = first_lineno;
87     const char *e = ptr - 1;
88     for (const char *p = buffer.contents(); p < e; p++)
89       if (*p == '\n')
90         ln++;
91     *linenop = ln;
92   }
93   return 1;
94 }
95
96 class input_stack {
97   static input_item *top;
98 public:
99   static void init();
100   static int get_char();
101   static int peek_char();
102   static void skip_char() { top->skip_char(); }
103   static void push_file(const char *);
104   static void push_string(string &, const char *, int);
105   static void error(const char *format,
106                     const errarg &arg1 = empty_errarg,
107                     const errarg &arg2 = empty_errarg,
108                     const errarg &arg3 = empty_errarg);
109 };
110
111 input_item *input_stack::top = 0;
112
113 void input_stack::init()
114 {
115   while (top) {
116     input_item *tem = top;
117     top = top->next;
118     delete tem;
119   }
120 }
121
122 int input_stack::get_char()
123 {
124   while (top) {
125     int c = top->get_char();
126     if (c >= 0)
127       return c;
128     input_item *tem = top;
129     top = top->next;
130     delete tem;
131   }
132   return -1;
133 }
134
135 int input_stack::peek_char()
136 {
137   while (top) {
138     int c = top->peek_char();
139     if (c >= 0)
140       return c;
141     input_item *tem = top;
142     top = top->next;
143     delete tem;
144   }
145   return -1;
146 }
147
148 void input_stack::push_file(const char *fn)
149 {
150   FILE *fp;
151   if (strcmp(fn, "-") == 0) {
152     fp = stdin;
153     fn = "<standard input>";
154   }
155   else {
156     errno = 0;
157     fp = fopen(fn, "r");
158     if (fp == 0) {
159       error("can't open `%1': %2", fn, strerror(errno));
160       return;
161     }
162   }
163   string buf;
164   int bol = 1;
165   int lineno = 1;
166   for (;;) {
167     int c = getc(fp);
168     if (bol && c == '.') {
169       // replace lines beginning with .R1 or .R2 with a blank line
170       c = getc(fp);
171       if (c == 'R') {
172         c = getc(fp);
173         if (c == '1' || c == '2') {
174           int cc = c;
175           c = getc(fp);
176           if (compatible_flag || c == ' ' || c == '\n' || c == EOF) {
177             while (c != '\n' && c != EOF)
178               c = getc(fp);
179           }
180           else {
181             buf += '.';
182             buf += 'R';
183             buf += cc;
184           }
185         }
186         else {
187           buf += '.';
188           buf += 'R';
189         }
190       }
191       else
192         buf += '.';
193     }
194     if (c == EOF)
195       break;
196     if (invalid_input_char(c))
197       error_with_file_and_line(fn, lineno,
198                                "invalid input character code %1", int(c));
199     else {
200       buf += c;
201       if (c == '\n') {
202         bol = 1;
203         lineno++;
204       }
205       else
206         bol = 0;
207     }
208   }
209   if (fp != stdin)
210     fclose(fp);
211   if (buf.length() > 0 && buf[buf.length() - 1] != '\n')
212     buf += '\n';
213   input_item *it = new input_item(buf, fn);
214   it->next = top;
215   top = it;
216 }
217
218 void input_stack::push_string(string &s, const char *filename, int lineno)
219 {
220   input_item *it = new input_item(s, filename, lineno);
221   it->next = top;
222   top = it;
223 }
224
225 void input_stack::error(const char *format, const errarg &arg1,
226                         const errarg &arg2, const errarg &arg3)
227 {
228   const char *filename;
229   int lineno;
230   for (input_item *it = top; it; it = it->next)
231     if (it->get_location(&filename, &lineno)) {
232       error_with_file_and_line(filename, lineno, format, arg1, arg2, arg3);
233       return;
234     }
235   ::error(format, arg1, arg2, arg3);
236 }
237
238 void command_error(const char *format, const errarg &arg1,
239                    const errarg &arg2, const errarg &arg3)
240 {
241   input_stack::error(format, arg1, arg2, arg3);
242 }
243
244 // # not recognized in ""
245 // \<newline> is recognized in ""
246 // # does not conceal newline
247 // if missing closing quote, word extends to end of line
248 // no special treatment of \ other than before newline
249 // \<newline> not recognized after #
250 // ; allowed as alternative to newline
251 // ; not recognized in ""
252 // don't clear word_buffer; just append on
253 // return -1 for EOF, 0 for newline, 1 for word
254
255 int get_word(string &word_buffer)
256 {
257   int c = input_stack::get_char();
258   for (;;) {
259     if (c == '#') {
260       do {
261         c = input_stack::get_char();
262       } while (c != '\n' && c != EOF);
263       break;
264     }
265     if (c == '\\' && input_stack::peek_char() == '\n')
266       input_stack::skip_char();
267     else if (c != ' ' && c != '\t')
268       break;
269     c = input_stack::get_char();
270   }
271   if (c == EOF)
272     return -1;
273   if (c == '\n' || c == ';')
274     return 0;
275   if (c == '"') {
276     for (;;) {
277       c = input_stack::peek_char();
278       if (c == EOF || c == '\n')
279         break;
280       input_stack::skip_char();
281       if (c == '"') {
282         int d = input_stack::peek_char();
283         if (d == '"')
284           input_stack::skip_char();
285         else
286           break;
287       }
288       else if (c == '\\') {
289         int d = input_stack::peek_char();
290         if (d == '\n')
291           input_stack::skip_char();
292         else
293           word_buffer += '\\';
294       }
295       else
296         word_buffer += c;
297     }
298     return 1;
299   }
300   word_buffer += c;
301   for (;;) {
302     c = input_stack::peek_char();
303     if (c == ' ' || c == '\t' || c == '\n' || c == '#' || c == ';')
304       break;
305     input_stack::skip_char();
306     if (c == '\\') {
307       int d = input_stack::peek_char();
308       if (d == '\n')
309         input_stack::skip_char();
310       else
311         word_buffer += '\\';
312     }
313     else
314       word_buffer += c;
315   }
316   return 1;
317 }
318
319 union argument {
320   const char *s;
321   int n;
322 };
323
324 // This is for debugging.
325
326 static void echo_command(int argc, argument *argv)
327 {
328   for (int i = 0; i < argc; i++)
329     fprintf(stderr, "%s\n", argv[i].s);
330 }
331
332 static void include_command(int argc, argument *argv)
333 {
334   assert(argc == 1);
335   input_stack::push_file(argv[0].s);
336 }
337
338 static void capitalize_command(int argc, argument *argv)
339 {
340   if (argc > 0)
341     capitalize_fields = argv[0].s;
342   else
343     capitalize_fields.clear();
344 }
345
346 static void accumulate_command(int, argument *)
347 {
348   accumulate = 1;
349 }
350
351 static void no_accumulate_command(int, argument *)
352 {
353   accumulate = 0;
354 }
355
356 static void move_punctuation_command(int, argument *)
357 {
358   move_punctuation = 1;
359 }
360
361 static void no_move_punctuation_command(int, argument *)
362 {
363   move_punctuation = 0;
364 }
365
366 static void sort_command(int argc, argument *argv)
367 {
368   if (argc == 0)
369     sort_fields = "AD";
370   else
371     sort_fields = argv[0].s;
372   accumulate = 1;
373 }
374
375 static void no_sort_command(int, argument *)
376 {
377   sort_fields.clear();
378 }
379
380 static void articles_command(int argc, argument *argv)
381 {
382   articles.clear();
383   int i;
384   for (i = 0; i < argc; i++) {
385     articles += argv[i].s;
386     articles += '\0';
387   }
388   int len = articles.length();
389   for (i = 0; i < len; i++)
390     articles[i] = cmlower(articles[i]);
391 }
392
393 static void database_command(int argc, argument *argv)
394 {
395   for (int i = 0; i < argc; i++)
396     database_list.add_file(argv[i].s);
397 }
398
399 static void default_database_command(int, argument *)
400 {
401   search_default = 1;
402 }
403
404 static void no_default_database_command(int, argument *)
405 {
406   search_default = 0;
407 }
408
409 static void bibliography_command(int argc, argument *argv)
410 {
411   have_bibliography = 1;
412   const char *saved_filename = current_filename;
413   int saved_lineno = current_lineno;
414   int saved_label_in_text = label_in_text;
415   label_in_text = 0;
416   if (!accumulate)
417     fputs(".]<\n", stdout);
418   for (int i = 0; i < argc; i++)
419     do_bib(argv[i].s);
420   if (accumulate)
421     output_references();
422   else
423     fputs(".]>\n", stdout);
424   current_filename = saved_filename;
425   current_lineno = saved_lineno;
426   label_in_text = saved_label_in_text;
427 }
428
429 static void annotate_command(int argc, argument *argv)
430 {
431   if (argc > 0)
432     annotation_field = argv[0].s[0];
433   else
434     annotation_field = 'X';
435   if (argc == 2)
436     annotation_macro = argv[1].s;
437   else
438     annotation_macro = "AP";
439 }
440
441 static void no_annotate_command(int, argument *)
442 {
443   annotation_macro.clear();
444   annotation_field = -1;
445 }
446
447 static void reverse_command(int, argument *argv)
448 {
449   reverse_fields = argv[0].s;
450 }
451
452 static void no_reverse_command(int, argument *)
453 {
454   reverse_fields.clear();
455 }
456
457 static void abbreviate_command(int argc, argument *argv)
458 {
459   abbreviate_fields = argv[0].s;
460   period_before_initial = argc > 1 ? argv[1].s : ". ";
461   period_before_last_name = argc > 2 ? argv[2].s : ". ";
462   period_before_other = argc > 3 ? argv[3].s : ". ";
463   period_before_hyphen = argc > 4 ? argv[4].s : ".";
464 }
465
466 static void no_abbreviate_command(int, argument *)
467 {
468   abbreviate_fields.clear();
469 }
470
471 string search_ignore_fields;
472
473 static void search_ignore_command(int argc, argument *argv)
474 {
475   if (argc > 0)
476     search_ignore_fields = argv[0].s;
477   else
478     search_ignore_fields = "XYZ";
479   search_ignore_fields += '\0';
480   linear_ignore_fields = search_ignore_fields.contents();
481 }
482
483 static void no_search_ignore_command(int, argument *)
484 {
485   linear_ignore_fields = "";
486 }
487
488 static void search_truncate_command(int argc, argument *argv)
489 {
490   if (argc > 0)
491     linear_truncate_len = argv[0].n;
492   else
493     linear_truncate_len = 6;
494 }
495
496 static void no_search_truncate_command(int, argument *)
497 {
498   linear_truncate_len = -1;
499 }
500
501 static void discard_command(int argc, argument *argv)
502 {
503   if (argc == 0)
504     discard_fields = "XYZ";
505   else
506     discard_fields = argv[0].s;
507   accumulate = 1;
508 }
509
510 static void no_discard_command(int, argument *)
511 {
512   discard_fields.clear();
513 }
514
515 static void label_command(int, argument *argv)
516 {
517   set_label_spec(argv[0].s);
518 }
519
520 static void abbreviate_label_ranges_command(int argc, argument *argv)
521 {
522   abbreviate_label_ranges = 1;
523   label_range_indicator = argc > 0 ? argv[0].s : "-";
524 }
525
526 static void no_abbreviate_label_ranges_command(int, argument *)
527 {
528   abbreviate_label_ranges = 0;
529 }
530
531 static void label_in_reference_command(int, argument *)
532 {
533   label_in_reference = 1;
534 }
535
536 static void no_label_in_reference_command(int, argument *)
537 {
538   label_in_reference = 0;
539 }
540
541 static void label_in_text_command(int, argument *)
542 {
543   label_in_text = 1;
544 }
545
546 static void no_label_in_text_command(int, argument *)
547 {
548   label_in_text = 0;
549 }
550
551 static void sort_adjacent_labels_command(int, argument *)
552 {
553   sort_adjacent_labels = 1;
554 }
555
556 static void no_sort_adjacent_labels_command(int, argument *)
557 {
558   sort_adjacent_labels = 0;
559 }
560
561 static void date_as_label_command(int argc, argument *argv)
562 {
563   if (set_date_label_spec(argc > 0 ? argv[0].s : "D%a*"))
564     date_as_label = 1;
565 }
566
567 static void no_date_as_label_command(int, argument *)
568 {
569   date_as_label = 0;
570 }
571
572 static void short_label_command(int, argument *argv)
573 {
574   if (set_short_label_spec(argv[0].s))
575     short_label_flag = 1;
576 }
577
578 static void no_short_label_command(int, argument *)
579 {
580   short_label_flag = 0;
581 }
582
583 static void compatible_command(int, argument *)
584 {
585   compatible_flag = 1;
586 }
587
588 static void no_compatible_command(int, argument *)
589 {
590   compatible_flag = 0;
591 }
592
593 static void join_authors_command(int argc, argument *argv)
594 {
595   join_authors_exactly_two = argv[0].s;
596   join_authors_default = argc > 1 ? argv[1].s : argv[0].s;
597   join_authors_last_two = argc == 3 ? argv[2].s : argv[0].s;
598 }
599
600 static void bracket_label_command(int, argument *argv)
601 {
602   pre_label = argv[0].s;
603   post_label = argv[1].s;
604   sep_label = argv[2].s;
605 }
606
607 static void separate_label_second_parts_command(int, argument *argv)
608 {
609   separate_label_second_parts = argv[0].s;
610 }
611
612 static void et_al_command(int argc, argument *argv)
613 {
614   et_al = argv[0].s;
615   et_al_min_elide = argv[1].n;
616   if (et_al_min_elide < 1)
617     et_al_min_elide = 1;
618   et_al_min_total = argc >= 3 ? argv[2].n : 0;
619 }
620
621 static void no_et_al_command(int, argument *)
622 {
623   et_al.clear();
624   et_al_min_elide = 0;
625 }
626
627 typedef void (*command_t)(int, argument *);
628
629 /* arg_types is a string describing the numbers and types of arguments.
630 s means a string, i means an integer, f is a list of fields, F is
631 a single field,
632 ? means that the previous argument is optional, * means that the
633 previous argument can occur any number of times. */
634
635 struct S {
636   const char *name;
637   command_t func;
638   const char *arg_types;
639 } command_table[] = {
640   { "include", include_command, "s" },
641   { "echo", echo_command, "s*" },
642   { "capitalize", capitalize_command, "f?" },
643   { "accumulate", accumulate_command, "" },
644   { "no-accumulate", no_accumulate_command, "" },
645   { "move-punctuation", move_punctuation_command, "" },
646   { "no-move-punctuation", no_move_punctuation_command, "" },
647   { "sort", sort_command, "s?" },
648   { "no-sort", no_sort_command, "" },
649   { "articles", articles_command, "s*" },
650   { "database", database_command, "ss*" },
651   { "default-database", default_database_command, "" },
652   { "no-default-database", no_default_database_command, "" },
653   { "bibliography", bibliography_command, "ss*" },
654   { "annotate", annotate_command, "F?s?" },
655   { "no-annotate", no_annotate_command, "" },
656   { "reverse", reverse_command, "s" },
657   { "no-reverse", no_reverse_command, "" },
658   { "abbreviate", abbreviate_command, "ss?s?s?s?" },
659   { "no-abbreviate", no_abbreviate_command, "" },
660   { "search-ignore", search_ignore_command, "f?" },
661   { "no-search-ignore", no_search_ignore_command, "" },
662   { "search-truncate", search_truncate_command, "i?" },
663   { "no-search-truncate", no_search_truncate_command, "" },
664   { "discard", discard_command, "f?" },
665   { "no-discard", no_discard_command, "" },
666   { "label", label_command, "s" },
667   { "abbreviate-label-ranges", abbreviate_label_ranges_command, "s?" },
668   { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command, "" },
669   { "label-in-reference", label_in_reference_command, "" },
670   { "no-label-in-reference", no_label_in_reference_command, "" },
671   { "label-in-text", label_in_text_command, "" },
672   { "no-label-in-text", no_label_in_text_command, "" },
673   { "sort-adjacent-labels", sort_adjacent_labels_command, "" },
674   { "no-sort-adjacent-labels", no_sort_adjacent_labels_command, "" },
675   { "date-as-label", date_as_label_command, "s?" },
676   { "no-date-as-label", no_date_as_label_command, "" },
677   { "short-label", short_label_command, "s" },
678   { "no-short-label", no_short_label_command, "" },
679   { "compatible", compatible_command, "" },
680   { "no-compatible", no_compatible_command, "" },
681   { "join-authors", join_authors_command, "sss?" },
682   { "bracket-label", bracket_label_command, "sss" },
683   { "separate-label-second-parts", separate_label_second_parts_command, "s" },
684   { "et-al", et_al_command, "sii?" },
685   { "no-et-al", no_et_al_command, "" },
686 };
687
688 static int check_args(const char *types, const char *name,
689                       int argc, argument *argv)
690 {
691   int argno = 0;
692   while (*types) {
693     if (argc == 0) {
694       if (types[1] == '?')
695         break;
696       else if (types[1] == '*') {
697         assert(types[2] == '\0');
698         break;
699       }
700       else {
701         input_stack::error("missing argument for command `%1'", name);
702         return 0;
703       }
704     }
705     switch (*types) {
706     case 's':
707       break;
708     case 'i':
709       {
710         char *ptr;
711         long n = strtol(argv->s, &ptr, 10);
712         if ((n == 0 && ptr == argv->s)
713             || *ptr != '\0') {
714           input_stack::error("argument %1 for command `%2' must be an integer",
715                              argno + 1, name);
716           return 0;
717         }
718         argv->n = (int)n;
719         break;
720       }
721     case 'f':
722       {
723         for (const char *ptr = argv->s; *ptr != '\0'; ptr++)
724           if (!cs_field_name(*ptr)) {
725             input_stack::error("argument %1 for command `%2' must be a list of fields",
726                              argno + 1, name);
727             return 0;
728           }
729         break;
730       }
731     case 'F':
732       if (argv->s[0] == '\0' || argv->s[1] != '\0'
733           || !cs_field_name(argv->s[0])) {
734         input_stack::error("argument %1 for command `%2' must be a field name",
735                            argno + 1, name);
736         return 0;
737       }
738       break;
739     default:
740       assert(0);
741     }
742     if (types[1] == '?')
743       types += 2;
744     else if (types[1] != '*')
745       types += 1;
746     --argc;
747     ++argv;
748     ++argno;
749   }
750   if (argc > 0) {
751     input_stack::error("too many arguments for command `%1'", name);
752     return 0;
753   }
754   return 1;
755 }
756
757 static void execute_command(const char *name, int argc, argument *argv)
758 {
759   for (unsigned int i = 0;
760        i < sizeof(command_table)/sizeof(command_table[0]); i++)
761     if (strcmp(name, command_table[i].name) == 0) {
762       if (check_args(command_table[i].arg_types, name, argc, argv))
763         (*command_table[i].func)(argc, argv);
764       return;
765     }
766   input_stack::error("unknown command `%1'", name);
767 }
768
769 static void command_loop()
770 {
771   string command;
772   for (;;) {
773     command.clear();
774     int res = get_word(command);
775     if (res != 1) {
776       if (res == 0)
777         continue;
778       break;
779     }
780     int argc = 0;
781     command += '\0';
782     while ((res = get_word(command)) == 1) {
783       argc++;
784       command += '\0';
785     }
786     argument *argv = new argument[argc];
787     const char *ptr = command.contents();
788     for (int i = 0; i < argc; i++)
789       argv[i].s = ptr = strchr(ptr, '\0') + 1;
790     execute_command(command.contents(), argc, argv);
791     a_delete argv;
792     if (res == -1)
793       break;
794   }
795 }
796
797 void process_commands(const char *file)
798 {
799   input_stack::init();
800   input_stack::push_file(file);
801   command_loop();
802 }
803
804 void process_commands(string &s, const char *file, int lineno)
805 {
806   input_stack::init();
807   input_stack::push_string(s, file, lineno);
808   command_loop();
809 }