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