1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2004
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "refer.h"
23114402Sru#include "refid.h"
24114402Sru#include "search.h"
25114402Sru#include "command.h"
26114402Sru
27114402Srucset cs_field_name = csalpha;
28114402Sru
29114402Sruclass input_item {
30114402Sru  input_item *next;
31114402Sru  char *filename;
32114402Sru  int first_lineno;
33114402Sru  string buffer;
34114402Sru  const char *ptr;
35114402Sru  const char *end;
36114402Srupublic:
37114402Sru  input_item(string &, const char *, int = 1);
38114402Sru  ~input_item();
39114402Sru  int get_char();
40114402Sru  int peek_char();
41114402Sru  void skip_char();
42114402Sru  int get_location(const char **, int *);
43114402Sru
44114402Sru  friend class input_stack;
45114402Sru};
46114402Sru
47114402Sruinput_item::input_item(string &s, const char *fn, int ln)
48114402Sru: filename(strsave(fn)), first_lineno(ln)
49114402Sru{
50114402Sru  buffer.move(s);
51114402Sru  ptr = buffer.contents();
52114402Sru  end = ptr + buffer.length();
53114402Sru}
54114402Sru
55114402Sruinput_item::~input_item()
56114402Sru{
57114402Sru  a_delete filename;
58114402Sru}
59114402Sru
60114402Sruinline int input_item::peek_char()
61114402Sru{
62114402Sru  if (ptr >= end)
63114402Sru    return EOF;
64114402Sru  else
65114402Sru    return (unsigned char)*ptr;
66114402Sru}
67114402Sru
68114402Sruinline int input_item::get_char()
69114402Sru{
70114402Sru  if (ptr >= end)
71114402Sru    return EOF;
72114402Sru  else
73114402Sru    return (unsigned char)*ptr++;
74114402Sru}
75114402Sru
76114402Sruinline void input_item::skip_char()
77114402Sru{
78114402Sru  ptr++;
79114402Sru}
80114402Sru
81114402Sruint input_item::get_location(const char **filenamep, int *linenop)
82114402Sru{
83114402Sru  *filenamep = filename;
84114402Sru  if (ptr == buffer.contents())
85114402Sru    *linenop = first_lineno;
86114402Sru  else {
87114402Sru    int ln = first_lineno;
88114402Sru    const char *e = ptr - 1;
89114402Sru    for (const char *p = buffer.contents(); p < e; p++)
90114402Sru      if (*p == '\n')
91114402Sru	ln++;
92114402Sru    *linenop = ln;
93114402Sru  }
94114402Sru  return 1;
95114402Sru}
96114402Sru
97114402Sruclass input_stack {
98114402Sru  static input_item *top;
99114402Srupublic:
100114402Sru  static void init();
101114402Sru  static int get_char();
102114402Sru  static int peek_char();
103114402Sru  static void skip_char() { top->skip_char(); }
104114402Sru  static void push_file(const char *);
105114402Sru  static void push_string(string &, const char *, int);
106114402Sru  static void error(const char *format,
107114402Sru		    const errarg &arg1 = empty_errarg,
108114402Sru		    const errarg &arg2 = empty_errarg,
109114402Sru		    const errarg &arg3 = empty_errarg);
110114402Sru};
111114402Sru
112114402Sruinput_item *input_stack::top = 0;
113114402Sru
114114402Sruvoid input_stack::init()
115114402Sru{
116114402Sru  while (top) {
117114402Sru    input_item *tem = top;
118114402Sru    top = top->next;
119114402Sru    delete tem;
120114402Sru  }
121114402Sru}
122114402Sru
123114402Sruint input_stack::get_char()
124114402Sru{
125114402Sru  while (top) {
126114402Sru    int c = top->get_char();
127114402Sru    if (c >= 0)
128114402Sru      return c;
129114402Sru    input_item *tem = top;
130114402Sru    top = top->next;
131114402Sru    delete tem;
132114402Sru  }
133114402Sru  return -1;
134114402Sru}
135114402Sru
136114402Sruint input_stack::peek_char()
137114402Sru{
138114402Sru  while (top) {
139114402Sru    int c = top->peek_char();
140114402Sru    if (c >= 0)
141114402Sru      return c;
142114402Sru    input_item *tem = top;
143114402Sru    top = top->next;
144114402Sru    delete tem;
145114402Sru  }
146114402Sru  return -1;
147114402Sru}
148114402Sru
149114402Sruvoid input_stack::push_file(const char *fn)
150114402Sru{
151114402Sru  FILE *fp;
152114402Sru  if (strcmp(fn, "-") == 0) {
153114402Sru    fp = stdin;
154114402Sru    fn = "<standard input>";
155114402Sru  }
156114402Sru  else {
157114402Sru    errno = 0;
158114402Sru    fp = fopen(fn, "r");
159114402Sru    if (fp == 0) {
160114402Sru      error("can't open `%1': %2", fn, strerror(errno));
161114402Sru      return;
162114402Sru    }
163114402Sru  }
164114402Sru  string buf;
165114402Sru  int bol = 1;
166114402Sru  int lineno = 1;
167114402Sru  for (;;) {
168114402Sru    int c = getc(fp);
169114402Sru    if (bol && c == '.') {
170114402Sru      // replace lines beginning with .R1 or .R2 with a blank line
171114402Sru      c = getc(fp);
172114402Sru      if (c == 'R') {
173114402Sru	c = getc(fp);
174114402Sru	if (c == '1' || c == '2') {
175114402Sru	  int cc = c;
176114402Sru	  c = getc(fp);
177114402Sru	  if (compatible_flag || c == ' ' || c == '\n' || c == EOF) {
178114402Sru	    while (c != '\n' && c != EOF)
179114402Sru	      c = getc(fp);
180114402Sru	  }
181114402Sru	  else {
182114402Sru	    buf += '.';
183114402Sru	    buf += 'R';
184114402Sru	    buf += cc;
185114402Sru	  }
186114402Sru	}
187114402Sru	else {
188114402Sru	  buf += '.';
189114402Sru	  buf += 'R';
190114402Sru	}
191114402Sru      }
192114402Sru      else
193114402Sru	buf += '.';
194114402Sru    }
195114402Sru    if (c == EOF)
196114402Sru      break;
197114402Sru    if (invalid_input_char(c))
198114402Sru      error_with_file_and_line(fn, lineno,
199114402Sru			       "invalid input character code %1", int(c));
200114402Sru    else {
201114402Sru      buf += c;
202114402Sru      if (c == '\n') {
203114402Sru	bol = 1;
204114402Sru	lineno++;
205114402Sru      }
206114402Sru      else
207114402Sru	bol = 0;
208114402Sru    }
209114402Sru  }
210114402Sru  if (fp != stdin)
211114402Sru    fclose(fp);
212114402Sru  if (buf.length() > 0 && buf[buf.length() - 1] != '\n')
213114402Sru    buf += '\n';
214114402Sru  input_item *it = new input_item(buf, fn);
215114402Sru  it->next = top;
216114402Sru  top = it;
217114402Sru}
218114402Sru
219114402Sruvoid input_stack::push_string(string &s, const char *filename, int lineno)
220114402Sru{
221114402Sru  input_item *it = new input_item(s, filename, lineno);
222114402Sru  it->next = top;
223114402Sru  top = it;
224114402Sru}
225114402Sru
226114402Sruvoid input_stack::error(const char *format, const errarg &arg1,
227114402Sru			const errarg &arg2, const errarg &arg3)
228114402Sru{
229114402Sru  const char *filename;
230114402Sru  int lineno;
231114402Sru  for (input_item *it = top; it; it = it->next)
232114402Sru    if (it->get_location(&filename, &lineno)) {
233114402Sru      error_with_file_and_line(filename, lineno, format, arg1, arg2, arg3);
234114402Sru      return;
235114402Sru    }
236114402Sru  ::error(format, arg1, arg2, arg3);
237114402Sru}
238114402Sru
239114402Sruvoid command_error(const char *format, const errarg &arg1,
240114402Sru		   const errarg &arg2, const errarg &arg3)
241114402Sru{
242114402Sru  input_stack::error(format, arg1, arg2, arg3);
243114402Sru}
244114402Sru
245114402Sru// # not recognized in ""
246114402Sru// \<newline> is recognized in ""
247114402Sru// # does not conceal newline
248114402Sru// if missing closing quote, word extends to end of line
249114402Sru// no special treatment of \ other than before newline
250114402Sru// \<newline> not recognized after #
251114402Sru// ; allowed as alternative to newline
252114402Sru// ; not recognized in ""
253114402Sru// don't clear word_buffer; just append on
254114402Sru// return -1 for EOF, 0 for newline, 1 for word
255114402Sru
256114402Sruint get_word(string &word_buffer)
257114402Sru{
258114402Sru  int c = input_stack::get_char();
259114402Sru  for (;;) {
260114402Sru    if (c == '#') {
261114402Sru      do {
262114402Sru	c = input_stack::get_char();
263114402Sru      } while (c != '\n' && c != EOF);
264114402Sru      break;
265114402Sru    }
266114402Sru    if (c == '\\' && input_stack::peek_char() == '\n')
267114402Sru      input_stack::skip_char();
268114402Sru    else if (c != ' ' && c != '\t')
269114402Sru      break;
270114402Sru    c = input_stack::get_char();
271114402Sru  }
272114402Sru  if (c == EOF)
273114402Sru    return -1;
274114402Sru  if (c == '\n' || c == ';')
275114402Sru    return 0;
276114402Sru  if (c == '"') {
277114402Sru    for (;;) {
278114402Sru      c = input_stack::peek_char();
279114402Sru      if (c == EOF || c == '\n')
280114402Sru	break;
281114402Sru      input_stack::skip_char();
282114402Sru      if (c == '"') {
283114402Sru	int d = input_stack::peek_char();
284114402Sru	if (d == '"')
285114402Sru	  input_stack::skip_char();
286114402Sru	else
287114402Sru	  break;
288114402Sru      }
289114402Sru      else if (c == '\\') {
290114402Sru	int d = input_stack::peek_char();
291114402Sru	if (d == '\n')
292114402Sru	  input_stack::skip_char();
293114402Sru	else
294114402Sru	  word_buffer += '\\';
295114402Sru      }
296114402Sru      else
297114402Sru	word_buffer += c;
298114402Sru    }
299114402Sru    return 1;
300114402Sru  }
301114402Sru  word_buffer += c;
302114402Sru  for (;;) {
303114402Sru    c = input_stack::peek_char();
304114402Sru    if (c == ' ' || c == '\t' || c == '\n' || c == '#' || c == ';')
305114402Sru      break;
306114402Sru    input_stack::skip_char();
307114402Sru    if (c == '\\') {
308114402Sru      int d = input_stack::peek_char();
309114402Sru      if (d == '\n')
310114402Sru	input_stack::skip_char();
311114402Sru      else
312114402Sru	word_buffer += '\\';
313114402Sru    }
314114402Sru    else
315114402Sru      word_buffer += c;
316114402Sru  }
317114402Sru  return 1;
318114402Sru}
319114402Sru
320114402Sruunion argument {
321114402Sru  const char *s;
322114402Sru  int n;
323114402Sru};
324114402Sru
325114402Sru// This is for debugging.
326114402Sru
327114402Srustatic void echo_command(int argc, argument *argv)
328114402Sru{
329114402Sru  for (int i = 0; i < argc; i++)
330114402Sru    fprintf(stderr, "%s\n", argv[i].s);
331114402Sru}
332114402Sru
333114402Srustatic void include_command(int argc, argument *argv)
334114402Sru{
335114402Sru  assert(argc == 1);
336114402Sru  input_stack::push_file(argv[0].s);
337114402Sru}
338114402Sru
339114402Srustatic void capitalize_command(int argc, argument *argv)
340114402Sru{
341114402Sru  if (argc > 0)
342114402Sru    capitalize_fields = argv[0].s;
343114402Sru  else
344114402Sru    capitalize_fields.clear();
345114402Sru}
346114402Sru
347114402Srustatic void accumulate_command(int, argument *)
348114402Sru{
349114402Sru  accumulate = 1;
350114402Sru}
351114402Sru
352114402Srustatic void no_accumulate_command(int, argument *)
353114402Sru{
354114402Sru  accumulate = 0;
355114402Sru}
356114402Sru
357114402Srustatic void move_punctuation_command(int, argument *)
358114402Sru{
359114402Sru  move_punctuation = 1;
360114402Sru}
361114402Sru
362114402Srustatic void no_move_punctuation_command(int, argument *)
363114402Sru{
364114402Sru  move_punctuation = 0;
365114402Sru}
366114402Sru
367114402Srustatic void sort_command(int argc, argument *argv)
368114402Sru{
369114402Sru  if (argc == 0)
370114402Sru    sort_fields = "AD";
371114402Sru  else
372114402Sru    sort_fields = argv[0].s;
373114402Sru  accumulate = 1;
374114402Sru}
375114402Sru
376114402Srustatic void no_sort_command(int, argument *)
377114402Sru{
378114402Sru  sort_fields.clear();
379114402Sru}
380114402Sru
381114402Srustatic void articles_command(int argc, argument *argv)
382114402Sru{
383114402Sru  articles.clear();
384114402Sru  int i;
385114402Sru  for (i = 0; i < argc; i++) {
386114402Sru    articles += argv[i].s;
387114402Sru    articles += '\0';
388114402Sru  }
389114402Sru  int len = articles.length();
390114402Sru  for (i = 0; i < len; i++)
391114402Sru    articles[i] = cmlower(articles[i]);
392114402Sru}
393114402Sru
394114402Srustatic void database_command(int argc, argument *argv)
395114402Sru{
396114402Sru  for (int i = 0; i < argc; i++)
397114402Sru    database_list.add_file(argv[i].s);
398114402Sru}
399114402Sru
400114402Srustatic void default_database_command(int, argument *)
401114402Sru{
402114402Sru  search_default = 1;
403114402Sru}
404114402Sru
405114402Srustatic void no_default_database_command(int, argument *)
406114402Sru{
407114402Sru  search_default = 0;
408114402Sru}
409114402Sru
410114402Srustatic void bibliography_command(int argc, argument *argv)
411114402Sru{
412114402Sru  const char *saved_filename = current_filename;
413114402Sru  int saved_lineno = current_lineno;
414114402Sru  int saved_label_in_text = label_in_text;
415114402Sru  label_in_text = 0;
416114402Sru  if (!accumulate)
417114402Sru    fputs(".]<\n", stdout);
418114402Sru  for (int i = 0; i < argc; i++)
419114402Sru    do_bib(argv[i].s);
420114402Sru  if (accumulate)
421114402Sru    output_references();
422114402Sru  else
423114402Sru    fputs(".]>\n", stdout);
424114402Sru  current_filename = saved_filename;
425114402Sru  current_lineno = saved_lineno;
426114402Sru  label_in_text = saved_label_in_text;
427114402Sru}
428114402Sru
429114402Srustatic void annotate_command(int argc, argument *argv)
430114402Sru{
431114402Sru  if (argc > 0)
432114402Sru    annotation_field = argv[0].s[0];
433114402Sru  else
434114402Sru    annotation_field = 'X';
435114402Sru  if (argc == 2)
436114402Sru    annotation_macro = argv[1].s;
437114402Sru  else
438114402Sru    annotation_macro = "AP";
439114402Sru}
440114402Sru
441114402Srustatic void no_annotate_command(int, argument *)
442114402Sru{
443114402Sru  annotation_macro.clear();
444114402Sru  annotation_field = -1;
445114402Sru}
446114402Sru
447114402Srustatic void reverse_command(int, argument *argv)
448114402Sru{
449114402Sru  reverse_fields = argv[0].s;
450114402Sru}
451114402Sru
452114402Srustatic void no_reverse_command(int, argument *)
453114402Sru{
454114402Sru  reverse_fields.clear();
455114402Sru}
456114402Sru
457114402Srustatic void abbreviate_command(int argc, argument *argv)
458114402Sru{
459114402Sru  abbreviate_fields = argv[0].s;
460114402Sru  period_before_initial = argc > 1 ? argv[1].s : ". ";
461114402Sru  period_before_last_name = argc > 2 ? argv[2].s : ". ";
462114402Sru  period_before_other = argc > 3 ? argv[3].s : ". ";
463114402Sru  period_before_hyphen = argc > 4 ? argv[4].s : ".";
464114402Sru}
465114402Sru
466114402Srustatic void no_abbreviate_command(int, argument *)
467114402Sru{
468114402Sru  abbreviate_fields.clear();
469114402Sru}
470114402Sru
471114402Srustring search_ignore_fields;
472114402Sru
473114402Srustatic void search_ignore_command(int argc, argument *argv)
474114402Sru{
475114402Sru  if (argc > 0)
476114402Sru    search_ignore_fields = argv[0].s;
477114402Sru  else
478114402Sru    search_ignore_fields = "XYZ";
479114402Sru  search_ignore_fields += '\0';
480114402Sru  linear_ignore_fields = search_ignore_fields.contents();
481114402Sru}
482114402Sru
483114402Srustatic void no_search_ignore_command(int, argument *)
484114402Sru{
485114402Sru  linear_ignore_fields = "";
486114402Sru}
487114402Sru
488114402Srustatic void search_truncate_command(int argc, argument *argv)
489114402Sru{
490114402Sru  if (argc > 0)
491114402Sru    linear_truncate_len = argv[0].n;
492114402Sru  else
493114402Sru    linear_truncate_len = 6;
494114402Sru}
495114402Sru
496114402Srustatic void no_search_truncate_command(int, argument *)
497114402Sru{
498114402Sru  linear_truncate_len = -1;
499114402Sru}
500114402Sru
501114402Srustatic void discard_command(int argc, argument *argv)
502114402Sru{
503114402Sru  if (argc == 0)
504114402Sru    discard_fields = "XYZ";
505114402Sru  else
506114402Sru    discard_fields = argv[0].s;
507114402Sru  accumulate = 1;
508114402Sru}
509114402Sru
510114402Srustatic void no_discard_command(int, argument *)
511114402Sru{
512114402Sru  discard_fields.clear();
513114402Sru}
514114402Sru
515114402Srustatic void label_command(int, argument *argv)
516114402Sru{
517114402Sru  set_label_spec(argv[0].s);
518114402Sru}
519114402Sru
520114402Srustatic void abbreviate_label_ranges_command(int argc, argument *argv)
521114402Sru{
522114402Sru  abbreviate_label_ranges = 1;
523114402Sru  label_range_indicator = argc > 0 ? argv[0].s : "-";
524114402Sru}
525114402Sru
526114402Srustatic void no_abbreviate_label_ranges_command(int, argument *)
527114402Sru{
528114402Sru  abbreviate_label_ranges = 0;
529114402Sru}
530114402Sru
531114402Srustatic void label_in_reference_command(int, argument *)
532114402Sru{
533114402Sru  label_in_reference = 1;
534114402Sru}
535114402Sru
536114402Srustatic void no_label_in_reference_command(int, argument *)
537114402Sru{
538114402Sru  label_in_reference = 0;
539114402Sru}
540114402Sru
541114402Srustatic void label_in_text_command(int, argument *)
542114402Sru{
543114402Sru  label_in_text = 1;
544114402Sru}
545114402Sru
546114402Srustatic void no_label_in_text_command(int, argument *)
547114402Sru{
548114402Sru  label_in_text = 0;
549114402Sru}
550114402Sru
551114402Srustatic void sort_adjacent_labels_command(int, argument *)
552114402Sru{
553114402Sru  sort_adjacent_labels = 1;
554114402Sru}
555114402Sru
556114402Srustatic void no_sort_adjacent_labels_command(int, argument *)
557114402Sru{
558114402Sru  sort_adjacent_labels = 0;
559114402Sru}
560114402Sru
561114402Srustatic void date_as_label_command(int argc, argument *argv)
562114402Sru{
563114402Sru  if (set_date_label_spec(argc > 0 ? argv[0].s : "D%a*"))
564114402Sru    date_as_label = 1;
565114402Sru}
566114402Sru
567114402Srustatic void no_date_as_label_command(int, argument *)
568114402Sru{
569114402Sru  date_as_label = 0;
570114402Sru}
571114402Sru
572114402Srustatic void short_label_command(int, argument *argv)
573114402Sru{
574114402Sru  if (set_short_label_spec(argv[0].s))
575114402Sru    short_label_flag = 1;
576114402Sru}
577114402Sru
578114402Srustatic void no_short_label_command(int, argument *)
579114402Sru{
580114402Sru  short_label_flag = 0;
581114402Sru}
582114402Sru
583114402Srustatic void compatible_command(int, argument *)
584114402Sru{
585114402Sru  compatible_flag = 1;
586114402Sru}
587114402Sru
588114402Srustatic void no_compatible_command(int, argument *)
589114402Sru{
590114402Sru  compatible_flag = 0;
591114402Sru}
592114402Sru
593114402Srustatic void join_authors_command(int argc, argument *argv)
594114402Sru{
595114402Sru  join_authors_exactly_two = argv[0].s;
596114402Sru  join_authors_default = argc > 1 ? argv[1].s : argv[0].s;
597114402Sru  join_authors_last_two = argc == 3 ? argv[2].s : argv[0].s;
598114402Sru}
599114402Sru
600114402Srustatic void bracket_label_command(int, argument *argv)
601114402Sru{
602114402Sru  pre_label = argv[0].s;
603114402Sru  post_label = argv[1].s;
604114402Sru  sep_label = argv[2].s;
605114402Sru}
606114402Sru
607114402Srustatic void separate_label_second_parts_command(int, argument *argv)
608114402Sru{
609114402Sru  separate_label_second_parts = argv[0].s;
610114402Sru}
611114402Sru
612114402Srustatic void et_al_command(int argc, argument *argv)
613114402Sru{
614114402Sru  et_al = argv[0].s;
615114402Sru  et_al_min_elide = argv[1].n;
616114402Sru  if (et_al_min_elide < 1)
617114402Sru    et_al_min_elide = 1;
618114402Sru  et_al_min_total = argc >= 3 ? argv[2].n : 0;
619114402Sru}
620114402Sru
621114402Srustatic void no_et_al_command(int, argument *)
622114402Sru{
623114402Sru  et_al.clear();
624114402Sru  et_al_min_elide = 0;
625114402Sru}
626114402Sru
627114402Srutypedef void (*command_t)(int, argument *);
628114402Sru
629114402Sru/* arg_types is a string describing the numbers and types of arguments.
630114402Srus means a string, i means an integer, f is a list of fields, F is
631114402Srua single field,
632114402Sru? means that the previous argument is optional, * means that the
633114402Sruprevious argument can occur any number of times. */
634114402Sru
635151497Srustruct S {
636114402Sru  const char *name;
637114402Sru  command_t func;
638114402Sru  const char *arg_types;
639114402Sru} command_table[] = {
640114402Sru  { "include", include_command, "s" },
641114402Sru  { "echo", echo_command, "s*" },
642114402Sru  { "capitalize", capitalize_command, "f?" },
643114402Sru  { "accumulate", accumulate_command, "" },
644114402Sru  { "no-accumulate", no_accumulate_command, "" },
645114402Sru  { "move-punctuation", move_punctuation_command, "" },
646114402Sru  { "no-move-punctuation", no_move_punctuation_command, "" },
647114402Sru  { "sort", sort_command, "s?" },
648114402Sru  { "no-sort", no_sort_command, "" },
649114402Sru  { "articles", articles_command, "s*" },
650114402Sru  { "database", database_command, "ss*" },
651114402Sru  { "default-database", default_database_command, "" },
652114402Sru  { "no-default-database", no_default_database_command, "" },
653114402Sru  { "bibliography", bibliography_command, "ss*" },
654114402Sru  { "annotate", annotate_command, "F?s?" },
655114402Sru  { "no-annotate", no_annotate_command, "" },
656114402Sru  { "reverse", reverse_command, "s" },
657114402Sru  { "no-reverse", no_reverse_command, "" },
658114402Sru  { "abbreviate", abbreviate_command, "ss?s?s?s?" },
659114402Sru  { "no-abbreviate", no_abbreviate_command, "" },
660114402Sru  { "search-ignore", search_ignore_command, "f?" },
661114402Sru  { "no-search-ignore", no_search_ignore_command, "" },
662114402Sru  { "search-truncate", search_truncate_command, "i?" },
663114402Sru  { "no-search-truncate", no_search_truncate_command, "" },
664114402Sru  { "discard", discard_command, "f?" },
665114402Sru  { "no-discard", no_discard_command, "" },
666114402Sru  { "label", label_command, "s" },
667114402Sru  { "abbreviate-label-ranges", abbreviate_label_ranges_command, "s?" },
668114402Sru  { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command, "" },
669114402Sru  { "label-in-reference", label_in_reference_command, "" },
670114402Sru  { "no-label-in-reference", no_label_in_reference_command, "" },
671114402Sru  { "label-in-text", label_in_text_command, "" },
672114402Sru  { "no-label-in-text", no_label_in_text_command, "" },
673114402Sru  { "sort-adjacent-labels", sort_adjacent_labels_command, "" },
674114402Sru  { "no-sort-adjacent-labels", no_sort_adjacent_labels_command, "" },
675114402Sru  { "date-as-label", date_as_label_command, "s?" },
676114402Sru  { "no-date-as-label", no_date_as_label_command, "" },
677114402Sru  { "short-label", short_label_command, "s" },
678114402Sru  { "no-short-label", no_short_label_command, "" },
679114402Sru  { "compatible", compatible_command, "" },
680114402Sru  { "no-compatible", no_compatible_command, "" },
681114402Sru  { "join-authors", join_authors_command, "sss?" },
682114402Sru  { "bracket-label", bracket_label_command, "sss" },
683114402Sru  { "separate-label-second-parts", separate_label_second_parts_command, "s" },
684114402Sru  { "et-al", et_al_command, "sii?" },
685114402Sru  { "no-et-al", no_et_al_command, "" },
686114402Sru};
687114402Sru
688114402Srustatic int check_args(const char *types, const char *name,
689114402Sru		      int argc, argument *argv)
690114402Sru{
691114402Sru  int argno = 0;
692114402Sru  while (*types) {
693114402Sru    if (argc == 0) {
694114402Sru      if (types[1] == '?')
695114402Sru	break;
696114402Sru      else if (types[1] == '*') {
697114402Sru	assert(types[2] == '\0');
698114402Sru	break;
699114402Sru      }
700114402Sru      else {
701114402Sru	input_stack::error("missing argument for command `%1'", name);
702114402Sru	return 0;
703114402Sru      }
704114402Sru    }
705114402Sru    switch (*types) {
706114402Sru    case 's':
707114402Sru      break;
708114402Sru    case 'i':
709114402Sru      {
710114402Sru	char *ptr;
711114402Sru	long n = strtol(argv->s, &ptr, 10);
712114402Sru	if ((n == 0 && ptr == argv->s)
713114402Sru	    || *ptr != '\0') {
714114402Sru	  input_stack::error("argument %1 for command `%2' must be an integer",
715114402Sru			     argno + 1, name);
716114402Sru	  return 0;
717114402Sru	}
718114402Sru	argv->n = (int)n;
719114402Sru	break;
720114402Sru      }
721114402Sru    case 'f':
722114402Sru      {
723114402Sru	for (const char *ptr = argv->s; *ptr != '\0'; ptr++)
724114402Sru	  if (!cs_field_name(*ptr)) {
725114402Sru	    input_stack::error("argument %1 for command `%2' must be a list of fields",
726114402Sru			     argno + 1, name);
727114402Sru	    return 0;
728114402Sru	  }
729114402Sru	break;
730114402Sru      }
731114402Sru    case 'F':
732114402Sru      if (argv->s[0] == '\0' || argv->s[1] != '\0'
733114402Sru	  || !cs_field_name(argv->s[0])) {
734114402Sru	input_stack::error("argument %1 for command `%2' must be a field name",
735114402Sru			   argno + 1, name);
736114402Sru	return 0;
737114402Sru      }
738114402Sru      break;
739114402Sru    default:
740114402Sru      assert(0);
741114402Sru    }
742114402Sru    if (types[1] == '?')
743114402Sru      types += 2;
744114402Sru    else if (types[1] != '*')
745114402Sru      types += 1;
746114402Sru    --argc;
747114402Sru    ++argv;
748114402Sru    ++argno;
749114402Sru  }
750114402Sru  if (argc > 0) {
751114402Sru    input_stack::error("too many arguments for command `%1'", name);
752114402Sru    return 0;
753114402Sru  }
754114402Sru  return 1;
755114402Sru}
756114402Sru
757114402Srustatic void execute_command(const char *name, int argc, argument *argv)
758114402Sru{
759114402Sru  for (unsigned int i = 0;
760114402Sru       i < sizeof(command_table)/sizeof(command_table[0]); i++)
761114402Sru    if (strcmp(name, command_table[i].name) == 0) {
762114402Sru      if (check_args(command_table[i].arg_types, name, argc, argv))
763114402Sru	(*command_table[i].func)(argc, argv);
764114402Sru      return;
765114402Sru    }
766114402Sru  input_stack::error("unknown command `%1'", name);
767114402Sru}
768114402Sru
769114402Srustatic void command_loop()
770114402Sru{
771114402Sru  string command;
772114402Sru  for (;;) {
773114402Sru    command.clear();
774114402Sru    int res = get_word(command);
775114402Sru    if (res != 1) {
776114402Sru      if (res == 0)
777114402Sru	continue;
778114402Sru      break;
779114402Sru    }
780114402Sru    int argc = 0;
781114402Sru    command += '\0';
782114402Sru    while ((res = get_word(command)) == 1) {
783114402Sru      argc++;
784114402Sru      command += '\0';
785114402Sru    }
786114402Sru    argument *argv = new argument[argc];
787114402Sru    const char *ptr = command.contents();
788114402Sru    for (int i = 0; i < argc; i++)
789114402Sru      argv[i].s = ptr = strchr(ptr, '\0') + 1;
790114402Sru    execute_command(command.contents(), argc, argv);
791114402Sru    a_delete argv;
792114402Sru    if (res == -1)
793114402Sru      break;
794114402Sru  }
795114402Sru}
796114402Sru
797114402Sruvoid process_commands(const char *file)
798114402Sru{
799114402Sru  input_stack::init();
800114402Sru  input_stack::push_file(file);
801114402Sru  command_loop();
802114402Sru}
803114402Sru
804114402Sruvoid process_commands(string &s, const char *file, int lineno)
805114402Sru{
806114402Sru  input_stack::init();
807114402Sru  input_stack::push_string(s, file, lineno);
808114402Sru  command_loop();
809114402Sru}
810