LCOV - code coverage report
Current view: top level - preproc/refer - command.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 167 451 37.0 %
Date: 2026-01-16 17:51:41 Functions: 21 65 32.3 %
Legend: Lines: hit not hit

          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:

Generated by: LCOV version 1.14