LCOV - code coverage report
Current view: top level - preproc/refer - refer.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 226 828 27.3 %
Date: 2026-01-16 17:51:41 Functions: 11 26 42.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 1989-2025 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 <assert.h>
      24             : #include <errno.h>
      25             : #include <stdio.h> // EOF, FILE, fclose(), ferror(), fflush(), fopen(),
      26             :                    // fprintf(), getc(), printf(), putc(), rewind(),
      27             :                    // setbuf(), sprintf(), stderr, stdin, stdout,
      28             :                    // ungetc()
      29             : #include <stdlib.h> // getenv(), qsort(), strtol()
      30             : #include <string.h> // strcat(), strchr(), strcmp(), strcpy(),
      31             :                     // strerror()
      32             : 
      33             : #include "refer.h" // includes cset.h
      34             : #include "refid.h"
      35             : #include "ref.h"
      36             : #include "token.h"
      37             : #include "search.h"
      38             : #include "command.h"
      39             : 
      40             : extern "C" const char *Version_string;
      41             : 
      42             : const char PRE_LABEL_MARKER = '\013';
      43             : const char POST_LABEL_MARKER = '\014';
      44             : const char LABEL_MARKER = '\015'; // label_type is added on
      45             : 
      46             : #define FORCE_LEFT_BRACKET 04
      47             : #define FORCE_RIGHT_BRACKET 010
      48             : 
      49             : static FILE *outfp = stdout;
      50             : 
      51             : string capitalize_fields;
      52             : string reverse_fields;
      53             : string abbreviate_fields;
      54             : string period_before_last_name = ". ";
      55             : string period_before_initial = ".";
      56             : string period_before_hyphen = "";
      57             : string period_before_other = ". ";
      58             : string sort_fields;
      59             : int annotation_field = -1;
      60             : string annotation_macro;
      61             : string discard_fields = "XYZ";
      62             : string pre_label = "\\*([.";
      63             : string post_label = "\\*(.]";
      64             : string sep_label = ", ";
      65             : int have_bibliography = 0;
      66             : int accumulate = 0;
      67             : int move_punctuation = 0;
      68             : int abbreviate_label_ranges = 0;
      69             : string label_range_indicator;
      70             : int label_in_text = 1;
      71             : int label_in_reference = 1;
      72             : int date_as_label = 0;
      73             : int sort_adjacent_labels = 0;
      74             : // Join exactly two authors with this.
      75             : string join_authors_exactly_two = " and ";
      76             : // When there are more than two authors join the last two with this.
      77             : string join_authors_last_two = ", and ";
      78             : // Otherwise join authors with this.
      79             : string join_authors_default = ", ";
      80             : string separate_label_second_parts = ", ";
      81             : // Use this string to represent that there are other authors.
      82             : string et_al = " et al";
      83             : // Use et al only if it can replace at least this many authors.
      84             : int et_al_min_elide = 2;
      85             : // Use et al only if the total number of authors is at least this.
      86             : int et_al_min_total = 3;
      87             : 
      88             : 
      89             : int compatible_flag = 0;
      90             : 
      91             : int short_label_flag = 0;
      92             : 
      93             : static bool recognize_R1_R2 = true;
      94             : 
      95             : search_list database_list;
      96             : int search_default = 1;
      97             : static int default_database_loaded = 0;
      98             : 
      99             : static reference **citation = 0;
     100             : static int ncitations = 0;
     101             : static int citation_max = 0;
     102             : 
     103             : static reference **reference_hash_table = 0;
     104             : static int hash_table_size;
     105             : static int nreferences = 0;
     106             : 
     107             : static int need_syncing = 0;
     108             : string pending_line;
     109             : string pending_lf_lines;
     110             : 
     111             : static void output_pending_line();
     112             : static unsigned immediately_handle_reference(const string &);
     113             : static void immediately_output_references();
     114             : static unsigned store_reference(const string &);
     115             : static void divert_to_temporary_file();
     116             : static reference *make_reference(const string &, unsigned *);
     117             : static void usage(FILE *stream);
     118             : static void do_file(const char *);
     119             : static void split_punct(string &line, string &punct);
     120             : static void output_citation_group(reference **v, int n, label_type,
     121             :                                   FILE *fp);
     122             : static void possibly_load_default_database();
     123             : 
     124           6 : int main(int argc, char **argv)
     125             : {
     126           6 :   program_name = argv[0];
     127             :   static char stderr_buf[BUFSIZ];
     128           6 :   setbuf(stderr, stderr_buf);
     129           6 :   outfp = stdout;
     130           6 :   int finished_options = 0;
     131           6 :   int bib_flag = 0;
     132           6 :   int done_spec = 0;
     133             : 
     134             :   // TODO: Migrate to getopt_long; see, e.g., src/preproc/eqn/main.cpp.
     135           8 :   for (--argc, ++argv;
     136           8 :        !finished_options && argc > 0 && argv[0][0] == '-'
     137          10 :        && argv[0][1] != '\0';
     138             :        argv++, argc--) {
     139           2 :     const char *opt = argv[0] + 1;
     140           4 :     while (opt != 0 && *opt != '\0') {
     141           2 :       switch (*opt) {
     142           0 :       case 'C':
     143           0 :         compatible_flag = 1;
     144           0 :         opt++;
     145           0 :         break;
     146           0 :       case 'B':
     147           0 :         bib_flag = 1;
     148           0 :         label_in_reference = 0;
     149           0 :         label_in_text = 0;
     150           0 :         ++opt;
     151           0 :         if (*opt == '\0') {
     152           0 :           annotation_field = 'X';
     153           0 :           annotation_macro = "AP";
     154             :         }
     155           0 :         else if (csalnum(opt[0]) && opt[1] == '.' && opt[2] != '\0') {
     156           0 :           annotation_field = opt[0];
     157           0 :           annotation_macro = opt + 2;
     158             :         }
     159           0 :         opt = 0 /* nullptr */;
     160           0 :         break;
     161           0 :       case 'P':
     162           0 :         move_punctuation = 1;
     163           0 :         opt++;
     164           0 :         break;
     165           0 :       case 'R':
     166           0 :         recognize_R1_R2 = false;
     167           0 :         opt++;
     168           0 :         break;
     169           0 :       case 'S':
     170             :         // Not a very useful spec.
     171           0 :         set_label_spec("(A.n|Q)', '(D.y|D)");
     172           0 :         done_spec = 1;
     173           0 :         pre_label = " (";
     174           0 :         post_label = ")";
     175           0 :         sep_label = "; ";
     176           0 :         opt++;
     177           0 :         break;
     178           0 :       case 'V':
     179           0 :         do_verify = true;
     180           0 :         opt++;
     181           0 :         break;
     182           0 :       case 'f':
     183             :         {
     184           0 :           const char *num = 0;
     185           0 :           if (*++opt == '\0') {
     186           0 :             if (argc > 1) {
     187           0 :               num = *++argv;
     188           0 :               --argc;
     189             :             }
     190             :             else {
     191           0 :               error("command-line option 'f' requires an argument");
     192           0 :               usage(stderr);
     193           0 :               exit(2);
     194             :             }
     195             :           }
     196             :           else {
     197           0 :             num = opt;
     198           0 :             opt = 0 /* nullptr */;
     199             :           }
     200             :           const char *ptr;
     201           0 :           for (ptr = num; *ptr; ptr++)
     202           0 :             if (!csdigit(*ptr)) {
     203           0 :               error("invalid character '%1' in argument to command-line"
     204           0 :                     " option 'f'; ignoring", *ptr);
     205           0 :               break;
     206             :             }
     207           0 :           if (*ptr == '\0') {
     208           0 :             string spec;
     209           0 :             spec = '%';
     210           0 :             spec += num;
     211           0 :             spec += '\0';
     212           0 :             set_label_spec(spec.contents());
     213           0 :             done_spec = 1;
     214             :           }
     215           0 :           break;
     216             :         }
     217           0 :       case 'b':
     218           0 :         label_in_text = 0;
     219           0 :         label_in_reference = 0;
     220           0 :         opt++;
     221           0 :         break;
     222           1 :       case 'e':
     223           1 :         accumulate = 1;
     224           1 :         opt++;
     225           1 :         break;
     226           0 :       case 'c':
     227           0 :         capitalize_fields = ++opt;
     228           0 :         opt = 0 /* nullptr */;
     229           0 :         break;
     230           0 :       case 'k':
     231             :         {
     232             :           char buf[5];
     233           0 :           if (csalpha(*++opt))
     234           0 :             buf[0] = *opt++;
     235             :           else {
     236           0 :             if (*opt != '\0')
     237           0 :               error("invalid field name '%1' in argument to"
     238           0 :                     " command-line option 'k'; assuming 'L'", *opt++);
     239           0 :             buf[0] = 'L';
     240             :           }
     241           0 :           buf[1] = '~';
     242           0 :           buf[2] = '%';
     243           0 :           buf[3] = 'a';
     244           0 :           buf[4] = '\0';
     245           0 :           set_label_spec(buf);
     246           0 :           done_spec = 1;
     247             :         }
     248           0 :         break;
     249           0 :       case 'a':
     250             :         {
     251             :           const char *ptr;
     252           0 :           for (ptr = ++opt; *ptr; ptr++)
     253           0 :             if (!csdigit(*ptr)) {
     254           0 :               error("invalid integer '%1' in argument to command-line"
     255           0 :                     " option 'a'; ignoring", opt);
     256           0 :               break;
     257             :             }
     258           0 :           if (*ptr == '\0') {
     259           0 :             reverse_fields = 'A';
     260           0 :             reverse_fields += opt;
     261             :           }
     262           0 :           opt = 0 /* nullptr */;
     263             :         }
     264           0 :         break;
     265           0 :       case 'i':
     266           0 :         linear_ignore_fields = ++opt;
     267           0 :         opt = 0 /* nullptr */;
     268           0 :         break;
     269           0 :       case 'l':
     270             :         {
     271             :           char buf[INT_DIGITS*2 + 11]; // A.n+2D.y-3%a
     272           0 :           strcpy(buf, "A.n");
     273           0 :           if (*++opt != '\0' && *opt != ',') {
     274             :             char *ptr;
     275           0 :             long n = strtol(opt, &ptr, 10);
     276           0 :             if (ptr == opt) {
     277           0 :               error("invalid integer '%1' in argument to command-line"
     278           0 :                     " option 'l'; ignoring", opt);
     279           0 :               opt = 0 /* nullptr */;
     280           0 :               break;
     281             :             }
     282           0 :             if (n < 0)
     283           0 :               n = 0;
     284           0 :             opt = ptr;
     285           0 :             sprintf(strchr(buf, '\0'), "+%ld", n);
     286             :           }
     287           0 :           strcat(buf, "D.y");
     288           0 :           if (',' == *opt)
     289           0 :             opt++;
     290           0 :           if (*opt != '\0') {
     291             :             char *ptr;
     292           0 :             long n = strtol(opt, &ptr, 10);
     293           0 :             if (ptr == opt) {
     294           0 :               error("invalid integer '%1' in argument to command-line"
     295           0 :                     " option 'l'; ignoring", opt);
     296           0 :               opt = 0 /* nullptr */;
     297           0 :               break;
     298             :             }
     299           0 :             if (n < 0)
     300           0 :               n = 0;
     301           0 :             sprintf(strchr(buf, '\0'), "-%ld", n);
     302           0 :             opt = ptr;
     303           0 :             if (*opt != '\0') {
     304           0 :               error("argument to 'l' option not of form 'm,n';"
     305             :                     " ignoring");
     306           0 :               while ((*opt != '\0') && (*opt != ' '))
     307           0 :                 opt++;
     308           0 :               break;
     309             :             }
     310             :           }
     311           0 :           strcat(buf, "%a");
     312           0 :           if (!set_label_spec(buf))
     313           0 :             assert(0 == "set_label_spec() failed");
     314           0 :           done_spec = 1;
     315             :         }
     316           0 :         break;
     317           0 :       case 'n':
     318           0 :         search_default = 0;
     319           0 :         opt++;
     320           0 :         break;
     321           1 :       case 'p':
     322             :         {
     323           1 :           const char *filename = 0;
     324           1 :           if ('\0' == *++opt) {
     325           1 :             if (argc > 1) {
     326           1 :               filename = *++argv;
     327           1 :               argc--;
     328             :             }
     329             :             else {
     330           0 :               error("option 'p' requires an argument");
     331           0 :               usage(stderr);
     332           0 :               exit(2);
     333             :             }
     334             :           }
     335             :           else {
     336           0 :             filename = opt;
     337           0 :             opt = 0 /* nullptr */;
     338             :           }
     339           1 :           database_list.add_file(filename);
     340             :         }
     341           1 :         break;
     342           0 :       case 's':
     343           0 :         if ('\0' == *++opt)
     344           0 :           sort_fields = "AD";
     345             :         else {
     346           0 :           sort_fields = opt;
     347           0 :           opt = 0 /* nullptr */;
     348             :         }
     349           0 :         accumulate = 1;
     350           0 :         break;
     351           0 :       case 't':
     352             :         {
     353             :           char *ptr;
     354           0 :           long n = strtol(opt, &ptr, 10);
     355           0 :           if (ptr == opt) {
     356           0 :               error("invalid integer '%1' in argument to command-line"
     357           0 :                     " option 't'; ignoring", opt);
     358           0 :             opt = 0 /* nullptr */;
     359           0 :             break;
     360             :           }
     361           0 :           if (n < 1)
     362           0 :             n = 1;
     363           0 :           linear_truncate_len = int(n);
     364           0 :           opt = ptr;
     365           0 :           break;
     366             :         }
     367           0 :       case '-':
     368           0 :         if (opt[1] == '\0') {
     369           0 :           finished_options = 1;
     370           0 :           opt++;
     371           0 :           break;
     372             :         }
     373           0 :         if (strcmp(opt, "-version") == 0) {
     374             :       case 'v':
     375           0 :           printf("GNU refer (groff) version %s\n", Version_string);
     376           0 :           exit(EXIT_SUCCESS);
     377             :           break;
     378             :         }
     379           0 :         if (strcmp(opt, "-help") == 0) {
     380           0 :           usage(stdout);
     381           0 :           exit(EXIT_SUCCESS);
     382             :           break;
     383             :         }
     384             :         // fall through
     385             :       default:
     386           0 :         error("unrecognized option '%1'", opt);
     387           0 :         usage(stderr);
     388           0 :         exit(2);
     389             :         break;
     390             :       }
     391             :     }
     392             :   }
     393           6 :   if (!done_spec)
     394           6 :     set_label_spec("%1");
     395           6 :   if (argc <= 0) {
     396           6 :     if (bib_flag)
     397           0 :       do_bib("-");
     398             :     else
     399           6 :       do_file("-");
     400             :   }
     401             :   else {
     402           0 :     for (int i = 0; i < argc; i++) {
     403           0 :       if (bib_flag)
     404           0 :         do_bib(argv[i]);
     405             :       else
     406           0 :         do_file(argv[i]);
     407             :     }
     408             :   }
     409           6 :   if (accumulate)
     410           0 :     output_references();
     411           6 :   if (ferror(stdout))
     412           0 :     fatal("error status on standard output stream");
     413           6 :   if (fflush(stdout) < 0)
     414           0 :     fatal("cannot flush standard output stream: %1", strerror(errno));
     415           6 :   return 0;
     416             : }
     417             : 
     418           0 : static void usage(FILE *stream)
     419             : {
     420           0 :   fprintf(stream,
     421             : "usage: %s [-bCenPRS] [-aN] [-cXYZ] [-fN] [-iXYZ] [-kX] [-lM,N]"
     422             : " [-p db-file] [-sXYZ] [-tN] [-Bl.m] [file ...]\n"
     423             : "usage: %s {-v | --version}\n"
     424             : "usage: %s --help\n",
     425             :           program_name, program_name, program_name);
     426           0 :   if (stdout == stream)
     427           0 :     fputs("\n"
     428             : "GNU refer is a troff(1) preprocessor that prepares bibliographic\n"
     429             : "citations by looking up keywords specified in a roff(7) document,\n"
     430             : "obviating the need to type such annotations, and permitting the\n"
     431             : "citation style in formatted output to be altered independently and\n"
     432             : "systematically.  See the refer(1) manual page.\n",
     433             :           stream);
     434           0 : }
     435             : 
     436           0 : static void possibly_load_default_database()
     437             : {
     438           0 :   if (search_default && !default_database_loaded) {
     439           0 :     char *filename = getenv("REFER");
     440           0 :     if (filename)
     441           0 :       database_list.add_file(filename);
     442             :     else
     443           0 :       database_list.add_file(DEFAULT_INDEX, 1);
     444           0 :     default_database_loaded = 1;
     445             :   }
     446           0 : }
     447             : 
     448           0 : static bool is_list(const string &str)
     449             : {
     450           0 :   const char *start = str.contents();
     451           0 :   const char *end = start + str.length();
     452           0 :   while (end > start && csspace(end[-1]))
     453           0 :     end--;
     454           0 :   while (start < end && csspace(*start))
     455           0 :     start++;
     456           0 :   return end - start == 6 && memcmp(start, "$LIST$", 6) == 0;
     457             : }
     458             : 
     459           6 : static void do_file(const char *filename)
     460             : {
     461             :   FILE *fp;
     462           6 :   if (strcmp(filename, "-") == 0) {
     463           6 :     fp = stdin;
     464             :   }
     465             :   else {
     466           0 :     errno = 0;
     467           0 :     fp = fopen(filename, "r");
     468           0 :     if (fp == 0) {
     469           0 :       error("cannot open '%1': %2", filename, strerror(errno));
     470           0 :       return;
     471             :     }
     472             :   }
     473          12 :   string fn(filename);
     474           6 :   fn += '\0';
     475           6 :   normalize_file_name_for_lf_request(fn);
     476           6 :   current_lineno = 1;
     477           6 :   current_filename = fn.contents();
     478           6 :   (void) fprintf(outfp, ".lf %d %s%s\n", current_lineno,
     479           6 :         ('"' == current_filename[0]) ? "" : "\"", current_filename);
     480          12 :   string line;
     481             :   for (;;) {
     482          15 :     line.clear();
     483             :     for (;;) {
     484          37 :       int c = getc(fp);
     485          37 :       if (EOF == c) {
     486           6 :         if (line.length() > 0)
     487           0 :           line += '\n';
     488           6 :         break;
     489             :       }
     490          31 :       if (is_invalid_input_char(c))
     491           1 :         error("invalid input character code %1; ignoring", c);
     492             :       else {
     493          30 :         line += c;
     494          30 :         if ('\n' == c)
     495           9 :           break;
     496             :       }
     497          22 :     }
     498          15 :     int len = line.length();
     499          15 :     if (len == 0)
     500           6 :       break;
     501           9 :     current_lineno++;
     502           9 :     if (len >= 2 && line[0] == '.' && line[1] == '[') {
     503           0 :       int start_lineno = current_lineno;
     504           0 :       bool at_start_of_line = true;
     505           0 :       string str;
     506           0 :       string post;
     507           0 :       string pre(line.contents() + 2, line.length() - 3);
     508             :       for (;;) {
     509           0 :         int c = getc(fp);
     510           0 :         if (EOF == c) {
     511           0 :           error_with_file_and_line(current_filename, start_lineno,
     512             :                                    "missing '.]' line");
     513           0 :           break;
     514             :         }
     515           0 :         if (at_start_of_line)
     516           0 :           current_lineno++;
     517           0 :         if (at_start_of_line && '.' == c) {
     518           0 :           int d = getc(fp);
     519           0 :           if (d == ']') {
     520           0 :             while ((d = getc(fp)) != '\n' && d != EOF) {
     521           0 :               if (is_invalid_input_char(d))
     522           0 :                 error("invalid input character code %1; ignoring", d);
     523             :               else
     524           0 :                 post += d;
     525             :             }
     526           0 :             break;
     527             :           }
     528           0 :           if (d != EOF)
     529           0 :             ungetc(d, fp);
     530             :         }
     531           0 :         if (is_invalid_input_char(c))
     532           0 :           error("invalid input character code %1; ignoring", c);
     533             :         else
     534           0 :           str += c;
     535           0 :         at_start_of_line = ('\n' == c);
     536           0 :       }
     537           0 :       if (is_list(str)) {
     538           0 :         output_pending_line();
     539           0 :         if (accumulate)
     540           0 :           output_references();
     541             :         else
     542           0 :           error("found '$LIST$' but not accumulating references");
     543             :       }
     544             :       else {
     545           0 :         unsigned flags = (accumulate
     546           0 :                           ? store_reference(str)
     547           0 :                           : immediately_handle_reference(str));
     548           0 :         if (label_in_text) {
     549           0 :           if (accumulate && outfp == stdout)
     550           0 :             divert_to_temporary_file();
     551           0 :           if (pending_line.length() == 0) {
     552           0 :             warning("cannot attach citation to empty previous line");
     553             :           }
     554             :           else
     555           0 :             pending_line.set_length(pending_line.length() - 1);
     556           0 :           string punct;
     557           0 :           if (move_punctuation)
     558           0 :             split_punct(pending_line, punct);
     559           0 :           int have_text = pre.length() > 0 || post.length() > 0;
     560           0 :           label_type lt = label_type(flags & ~(FORCE_LEFT_BRACKET
     561             :                                                |FORCE_RIGHT_BRACKET));
     562           0 :           if ((flags & FORCE_LEFT_BRACKET) || !have_text)
     563           0 :             pending_line += PRE_LABEL_MARKER;
     564           0 :           pending_line += pre;
     565           0 :           char lm = LABEL_MARKER + int(lt);
     566           0 :           pending_line += lm;
     567           0 :           pending_line += post;
     568           0 :           if ((flags & FORCE_RIGHT_BRACKET) || !have_text)
     569           0 :             pending_line += POST_LABEL_MARKER;
     570           0 :           pending_line += punct;
     571           0 :           pending_line += '\n';
     572             :         }
     573             :       }
     574           0 :       need_syncing = 1;
     575             :     }
     576           9 :     else if (len >= 4
     577           6 :              && '.' == line[0] && 'l' == line[1] && 'f' == line[2]
     578          15 :              && (compatible_flag || '\n' == line[3] || ' ' == line[3]))
     579             :     {
     580           0 :       pending_lf_lines += line;
     581           0 :       line += '\0';
     582           0 :       if (interpret_lf_request_arguments(line.contents() + 3))
     583           0 :         current_lineno--;
     584             :     }
     585           9 :     else if (recognize_R1_R2
     586           9 :              && len >= 4
     587           6 :              && '.' == line[0] && 'R' == line[1] && '1' == line[2]
     588          18 :              && (compatible_flag || '\n' == line[3] || ' ' == line[3]))
     589             :     {
     590           6 :       line.clear();
     591           6 :       int start_lineno = current_lineno;
     592           6 :       bool at_start_of_line = true;
     593             :       for (;;) {
     594         261 :         int c = getc(fp);
     595         261 :         if (c != EOF && at_start_of_line)
     596          14 :           current_lineno++;
     597         261 :         if (at_start_of_line && '.' == c) {
     598           6 :           c = getc(fp);
     599           6 :           if ('R' == c) {
     600           6 :             c = getc(fp);
     601           6 :             if ('2' == c) {
     602           6 :               c = getc(fp);
     603           6 :               if (compatible_flag || ' ' == c || '\n' == c || EOF == c)
     604             :               {
     605           6 :                 while (c != EOF && c != '\n')
     606           0 :                   c = getc(fp);
     607           6 :                 break;
     608             :               }
     609             :               else {
     610           0 :                 line += '.';
     611           0 :                 line += 'R';
     612           0 :                 line += '2';
     613             :               }
     614             :             }
     615             :             else {
     616           0 :               line += '.';
     617           0 :               line += 'R';
     618             :             }
     619             :           }
     620             :           else
     621           0 :             line += '.';
     622             :         }
     623         255 :         if (EOF == c) {
     624           0 :           error_with_file_and_line(current_filename, start_lineno,
     625             :                                    "missing '.R2' line");
     626           0 :           break;
     627             :         }
     628         255 :         if (is_invalid_input_char(c))
     629           1 :           error_with_file_and_line(current_filename, start_lineno,
     630             :                                    "invalid input character code %1;"
     631           2 :                                    " ignoring", c);
     632             :         else {
     633         254 :           line += c;
     634         254 :           at_start_of_line = ('\n' == c);
     635             :         }
     636         255 :       }
     637           6 :       output_pending_line();
     638           6 :       if (accumulate)
     639           1 :         output_references();
     640             :       else
     641           5 :         nreferences = 0;
     642           6 :       process_commands(line, current_filename, start_lineno + 1);
     643           6 :       need_syncing = 1;
     644             :     }
     645             :     else {
     646           3 :       output_pending_line();
     647           3 :       pending_line = line;
     648             :     }
     649           9 :   }
     650           6 :   need_syncing = 0;
     651           6 :   output_pending_line();
     652           6 :   if (fp != stdin)
     653           0 :     fclose(fp);
     654             : }
     655             : 
     656             : class label_processing_state {
     657             :   enum {
     658             :     NORMAL,
     659             :     PENDING_LABEL,
     660             :     PENDING_LABEL_POST,
     661             :     PENDING_LABEL_POST_PRE,
     662             :     PENDING_POST
     663             :     } state;
     664             :   label_type type;              // type of pending labels
     665             :   int count;                    // number of pending labels
     666             :   reference **rptr;             // pointer to next reference
     667             :   int rcount;                   // number of references left
     668             :   FILE *fp;
     669             :   int handle_pending(int c);
     670             : public:
     671             :   label_processing_state(reference **, int, FILE *);
     672             :   ~label_processing_state();
     673             :   void process(int c);
     674             : };
     675             : 
     676          15 : static void output_pending_line()
     677             : {
     678          15 :   if (label_in_text && !accumulate && ncitations > 0) {
     679           0 :     label_processing_state state(citation, ncitations, outfp);
     680           0 :     int len = pending_line.length();
     681           0 :     for (int i = 0; i < len; i++)
     682           0 :       state.process((unsigned char)(pending_line[i]));
     683             :   }
     684             :   else
     685          15 :     put_string(pending_line, outfp);
     686          15 :   pending_line.clear();
     687          15 :   if (pending_lf_lines.length() > 0) {
     688           0 :     put_string(pending_lf_lines, outfp);
     689           0 :     pending_lf_lines.clear();
     690             :   }
     691          15 :   if (!accumulate)
     692          13 :     immediately_output_references();
     693          15 :   if (need_syncing) {
     694           1 :     fprintf(outfp, ".lf %d %s%s\n", current_lineno,
     695           1 :           ('"' == current_filename[0]) ? "" : "\"", current_filename);
     696           1 :     need_syncing = 0;
     697             :   }
     698          15 : }
     699             : 
     700           0 : static void split_punct(string &line, string &punct)
     701             : {
     702           0 :   const char *start = line.contents();
     703           0 :   const char *end = start + line.length();
     704           0 :   const char *ptr = start;
     705           0 :   const char *last_token_start = 0;
     706             :   for (;;) {
     707           0 :     if (ptr >= end)
     708           0 :       break;
     709           0 :     last_token_start = ptr;
     710           0 :     if (*ptr == PRE_LABEL_MARKER || *ptr == POST_LABEL_MARKER
     711           0 :         || (*ptr >= LABEL_MARKER
     712           0 :             && *ptr < LABEL_MARKER + N_LABEL_TYPES))
     713           0 :       ptr++;
     714           0 :     else if (!get_token(&ptr, end))
     715           0 :       break;
     716             :   }
     717           0 :   if (last_token_start) {
     718           0 :     const token_info *ti = lookup_token(last_token_start, end);
     719           0 :     if (ti->is_punct()) {
     720           0 :       punct.append(last_token_start, end - last_token_start);
     721           0 :       line.set_length(last_token_start - start);
     722             :     }
     723             :   }
     724           0 : }
     725             : 
     726           0 : static void divert_to_temporary_file()
     727             : {
     728           0 :   outfp = xtmpfile();
     729           0 : }
     730             : 
     731           5 : static void store_citation(reference *ref)
     732             : {
     733           5 :   if (ncitations >= citation_max) {
     734           2 :     if (citation == 0)
     735           2 :       citation = new reference*[citation_max = 100];
     736             :     else {
     737           0 :       reference **old_citation = citation;
     738           0 :       citation_max *= 2;
     739           0 :       citation = new reference *[citation_max];
     740           0 :       memcpy(citation, old_citation, ncitations*sizeof(reference *));
     741           0 :       delete[] old_citation;
     742             :     }
     743             :   }
     744           5 :   citation[ncitations++] = ref;
     745           5 : }
     746             : 
     747           0 : static unsigned store_reference(const string &str)
     748             : {
     749           0 :   if (reference_hash_table == 0) {
     750           0 :     reference_hash_table = new reference *[17];
     751           0 :     hash_table_size = 17;
     752           0 :     for (int i = 0; i < hash_table_size; i++)
     753           0 :       reference_hash_table[i] = 0;
     754             :   }
     755             :   unsigned flags;
     756           0 :   reference *ref = make_reference(str, &flags);
     757           0 :   ref->compute_hash_code();
     758           0 :   unsigned h = ref->hash();
     759             :   reference **ptr;
     760           0 :   for (ptr = reference_hash_table + (h % hash_table_size);
     761           0 :        *ptr != 0;
     762           0 :        ((ptr == reference_hash_table)
     763           0 :         ? (ptr = reference_hash_table + hash_table_size - 1)
     764             :         : --ptr))
     765           0 :     if (same_reference(**ptr, *ref))
     766           0 :       break;
     767           0 :   if (*ptr != 0) {
     768           0 :     if (ref->is_merged())
     769           0 :       warning("fields ignored because reference already used");
     770           0 :     delete ref;
     771           0 :     ref = *ptr;
     772             :   }
     773             :   else {
     774           0 :     *ptr = ref;
     775           0 :     ref->set_number(nreferences);
     776           0 :     nreferences++;
     777           0 :     ref->pre_compute_label();
     778           0 :     ref->compute_sort_key();
     779           0 :     if (nreferences*2 >= hash_table_size) {
     780             :       // Rehash it.
     781           0 :       reference **old_table = reference_hash_table;
     782           0 :       int old_size = hash_table_size;
     783           0 :       hash_table_size = next_size(hash_table_size);
     784           0 :       reference_hash_table = new reference*[hash_table_size];
     785             :       int i;
     786           0 :       for (i = 0; i < hash_table_size; i++)
     787           0 :         reference_hash_table[i] = 0;
     788           0 :       for (i = 0; i < old_size; i++)
     789           0 :         if (old_table[i]) {
     790             :           reference **p;
     791           0 :           for (p = (reference_hash_table
     792           0 :                     + (old_table[i]->hash() % hash_table_size));
     793           0 :                *p;
     794           0 :                ((p == reference_hash_table)
     795           0 :                 ? (p = reference_hash_table + hash_table_size - 1)
     796             :                 : --p))
     797             :             ;
     798           0 :           *p = old_table[i];
     799             :         }
     800           0 :       delete[] old_table;
     801             :     }
     802             :   }
     803           0 :   if (label_in_text)
     804           0 :     store_citation(ref);
     805           0 :   return flags;
     806             : }
     807             : 
     808           5 : unsigned immediately_handle_reference(const string &str)
     809             : {
     810             :   unsigned flags;
     811           5 :   reference *ref = make_reference(str, &flags);
     812           5 :   ref->set_number(nreferences);
     813           5 :   if (label_in_text || label_in_reference) {
     814           5 :     ref->pre_compute_label();
     815           5 :     ref->immediate_compute_label();
     816             :   }
     817           5 :   nreferences++;
     818           5 :   store_citation(ref);
     819           5 :   return flags;
     820             : }
     821             : 
     822          18 : static void immediately_output_references()
     823             : {
     824          23 :   for (int i = 0; i < ncitations; i++) {
     825           5 :     reference *ref = citation[i];
     826           5 :     if (label_in_reference) {
     827           5 :       fputs(".ds [F ", outfp);
     828           5 :       const string &label = ref->get_label(NORMAL_LABEL);
     829           5 :       if (label.length() > 0
     830           5 :           && (label[0] == ' ' || label[0] == '\\' || label[0] == '"'))
     831           0 :         putc('"', outfp);
     832           5 :       put_string(label, outfp);
     833           5 :       putc('\n', outfp);
     834             :     }
     835           5 :     ref->output(outfp);
     836           5 :     delete ref;
     837             :   }
     838          18 :   ncitations = 0;
     839          18 : }
     840             : 
     841           0 : static void output_citation_group(reference **v, int n, label_type type,
     842             :                                   FILE *fp)
     843             : {
     844           0 :   if (sort_adjacent_labels) {
     845             :     // Do an insertion sort.  Usually n will be very small.
     846           0 :     for (int i = 1; i < n; i++) {
     847           0 :       int num = v[i]->get_number();
     848           0 :       reference *temp = v[i];
     849             :       int j;
     850           0 :       for (j = i - 1; j >= 0 && v[j]->get_number() > num; j--)
     851           0 :         v[j + 1] = v[j];
     852           0 :       v[j + 1] = temp;
     853             :     }
     854             :   }
     855             :   // This messes up if !accumulate.
     856           0 :   if (accumulate && n > 1) {
     857             :     // remove duplicates
     858           0 :     int j = 1;
     859           0 :     for (int i = 1; i < n; i++)
     860           0 :       if (v[i]->get_label(type) != v[i - 1]->get_label(type))
     861           0 :         v[j++] = v[i];
     862           0 :     n = j;
     863             :   }
     864           0 :   string merged_label;
     865           0 :   for (int i = 0; i < n; i++) {
     866           0 :     int nmerged = v[i]->merge_labels(v + i + 1, n - i - 1, type,
     867             :         merged_label);
     868           0 :     if (nmerged > 0) {
     869           0 :       put_string(merged_label, fp);
     870           0 :       i += nmerged;
     871             :     }
     872             :     else
     873           0 :       put_string(v[i]->get_label(type), fp);
     874           0 :     if (i < n - 1)
     875           0 :       put_string(sep_label, fp);
     876             :   }
     877           0 : }
     878             : 
     879             : 
     880           0 : label_processing_state::label_processing_state(reference **p, int n,
     881           0 :                                                FILE *f)
     882           0 : : state(NORMAL), count(0), rptr(p), rcount(n), fp(f)
     883             : {
     884           0 : }
     885             : 
     886           0 : label_processing_state::~label_processing_state()
     887             : {
     888           0 :   int handled = handle_pending(EOF);
     889           0 :   assert(!handled);
     890           0 :   assert(rcount == 0);
     891           0 : }
     892             : 
     893           0 : int label_processing_state::handle_pending(int c)
     894             : {
     895           0 :   switch (state) {
     896           0 :   case NORMAL:
     897           0 :     break;
     898           0 :   case PENDING_LABEL:
     899           0 :     if (POST_LABEL_MARKER == c) {
     900           0 :       state = PENDING_LABEL_POST;
     901           0 :       return 1;
     902             :     }
     903             :     else {
     904           0 :       output_citation_group(rptr, count, type, fp);
     905           0 :       rptr += count ;
     906           0 :       rcount -= count;
     907           0 :       state = NORMAL;
     908             :     }
     909           0 :     break;
     910           0 :   case PENDING_LABEL_POST:
     911           0 :     if (PRE_LABEL_MARKER == c) {
     912           0 :       state = PENDING_LABEL_POST_PRE;
     913           0 :       return 1;
     914             :     }
     915             :     else {
     916           0 :       output_citation_group(rptr, count, type, fp);
     917           0 :       rptr += count;
     918           0 :       rcount -= count;
     919           0 :       put_string(post_label, fp);
     920           0 :       state = NORMAL;
     921             :     }
     922           0 :     break;
     923           0 :   case PENDING_LABEL_POST_PRE:
     924           0 :     if (c >= LABEL_MARKER
     925           0 :         && c < LABEL_MARKER + N_LABEL_TYPES
     926           0 :         && c - LABEL_MARKER == type) {
     927           0 :       count += 1;
     928           0 :       state = PENDING_LABEL;
     929           0 :       return 1;
     930             :     }
     931             :     else {
     932           0 :       output_citation_group(rptr, count, type, fp);
     933           0 :       rptr += count;
     934           0 :       rcount -= count;
     935           0 :       put_string(sep_label, fp);
     936           0 :       state = NORMAL;
     937             :     }
     938           0 :     break;
     939           0 :   case PENDING_POST:
     940           0 :     if (PRE_LABEL_MARKER == c) {
     941           0 :       put_string(sep_label, fp);
     942           0 :       state = NORMAL;
     943           0 :       return 1;
     944             :     }
     945             :     else {
     946           0 :       put_string(post_label, fp);
     947           0 :       state = NORMAL;
     948             :     }
     949           0 :     break;
     950             :   }
     951           0 :   return 0;
     952             : }
     953             : 
     954           0 : void label_processing_state::process(int c)
     955             : {
     956           0 :   if (handle_pending(c))
     957           0 :     return;
     958           0 :   assert(state == NORMAL);
     959           0 :   switch (c) {
     960           0 :   case PRE_LABEL_MARKER:
     961           0 :     put_string(pre_label, fp);
     962           0 :     state = NORMAL;
     963           0 :     break;
     964           0 :   case POST_LABEL_MARKER:
     965           0 :     state = PENDING_POST;
     966           0 :     break;
     967           0 :   case LABEL_MARKER:
     968             :   case LABEL_MARKER + 1:
     969           0 :     count = 1;
     970           0 :     state = PENDING_LABEL;
     971           0 :     type = label_type(c - LABEL_MARKER);
     972           0 :     break;
     973           0 :   default:
     974           0 :     state = NORMAL;
     975           0 :     putc(c, fp);
     976           0 :     break;
     977             :   }
     978             : }
     979             : 
     980             : extern "C" {
     981             : 
     982           0 : int rcompare(const void *p1, const void *p2)
     983             : {
     984             :   // XXX: Would it make more sense to make non-const copies of p1, p2?
     985           0 :   return compare_reference(
     986             :       **static_cast<reference **>(const_cast<void *>(p1)),
     987           0 :       **static_cast<reference **>(const_cast<void *>(p2)));
     988             : }
     989             : 
     990             : }
     991             : 
     992           1 : void output_references()
     993             : {
     994           1 :   assert(accumulate);
     995           1 :   if (!hash_table_size) {
     996           1 :     if (have_bibliography)
     997           0 :       error("nothing to reference (probably 'bibliography' before"
     998             :             " 'sort')");
     999           1 :     accumulate = 0;
    1000           1 :     nreferences = 0;
    1001           1 :     return;
    1002             :   }
    1003           0 :   if (nreferences > 0) {
    1004           0 :     int j = 0;
    1005             :     int i;
    1006           0 :     for (i = 0; i < hash_table_size; i++)
    1007           0 :       if (reference_hash_table[i] != 0)
    1008           0 :         reference_hash_table[j++] = reference_hash_table[i];
    1009           0 :     assert(j == nreferences);
    1010           0 :     for (; j < hash_table_size; j++)
    1011           0 :       reference_hash_table[j] = 0;
    1012           0 :     qsort(reference_hash_table, nreferences, sizeof(reference*),
    1013             :           rcompare);
    1014           0 :     for (i = 0; i < nreferences; i++)
    1015           0 :       reference_hash_table[i]->set_number(i);
    1016           0 :     compute_labels(reference_hash_table, nreferences);
    1017             :   }
    1018           0 :   if (outfp != stdout) {
    1019           0 :     rewind(outfp);
    1020             :     {
    1021           0 :       label_processing_state state(citation, ncitations, stdout);
    1022             :       int c;
    1023           0 :       while ((c = getc(outfp)) != EOF)
    1024           0 :         state.process(c);
    1025             :     }
    1026           0 :     ncitations = 0;
    1027           0 :     fclose(outfp);
    1028           0 :     outfp = stdout;
    1029             :   }
    1030           0 :   if (nreferences > 0) {
    1031           0 :     fputs(".]<\n", outfp);
    1032           0 :     for (int i = 0; i < nreferences; i++) {
    1033           0 :       if (sort_fields.length() > 0)
    1034           0 :         reference_hash_table[i]->print_sort_key_comment(outfp);
    1035           0 :       if (label_in_reference) {
    1036           0 :         fputs(".ds [F ", outfp);
    1037             :         const string &label
    1038           0 :           = reference_hash_table[i]->get_label(NORMAL_LABEL);
    1039           0 :         if (label.length() > 0
    1040           0 :             && (label[0] == ' ' || label[0] == '\\' || label[0] == '"'))
    1041           0 :           putc('"', outfp);
    1042           0 :         put_string(label, outfp);
    1043           0 :         putc('\n', outfp);
    1044             :       }
    1045           0 :       reference_hash_table[i]->output(outfp);
    1046           0 :       delete reference_hash_table[i];
    1047           0 :       reference_hash_table[i] = 0;
    1048             :     }
    1049           0 :     fputs(".]>\n", outfp);
    1050           0 :     nreferences = 0;
    1051             :   }
    1052           0 :   clear_labels();
    1053             : }
    1054             : 
    1055           0 : static reference *find_reference(const char *query, int query_len)
    1056             : {
    1057             :   // This is so that error messages look better.
    1058           0 :   while (query_len > 0 && csspace(query[query_len - 1]))
    1059           0 :     query_len--;
    1060           0 :   string str;
    1061           0 :   for (int i = 0; i < query_len; i++)
    1062           0 :     str += query[i] == '\n' ? ' ' : query[i];
    1063           0 :   str += '\0';
    1064           0 :   possibly_load_default_database();
    1065           0 :   search_list_iterator iter(&database_list, str.contents());
    1066           0 :   reference_id rid;
    1067             :   const char *start;
    1068             :   int len;
    1069           0 :   if (!iter.next(&start, &len, &rid)) {
    1070           0 :     error("no reference matches '%1'", str.contents());
    1071           0 :     return 0 /* nullptr */;
    1072             :   }
    1073           0 :   const char *end = start + len;
    1074           0 :   while (start < end) {
    1075           0 :     if (*start == '%')
    1076           0 :       break;
    1077           0 :     while (start < end && *start++ != '\n')
    1078             :       ;
    1079             :   }
    1080           0 :   if (start >= end) {
    1081           0 :     error("reference matching '%1' has no fields",
    1082           0 :           str.contents());
    1083           0 :     return 0 /* nullptr */;
    1084             :   }
    1085           0 :   reference *result = new reference(start, end - start, &rid);
    1086           0 :   if (iter.next(&start, &len, &rid))
    1087           0 :     warning("multiple references match '%1'", str.contents());
    1088           0 :   return result;
    1089             : }
    1090             : 
    1091           5 : static reference *make_reference(const string &str, unsigned *flagsp)
    1092             : {
    1093           5 :   const char *start = str.contents();
    1094           5 :   const char *end = start + str.length();
    1095           5 :   const char *ptr = start;
    1096           5 :   while (ptr < end) {
    1097           5 :     if (*ptr == '%')
    1098           5 :       break;
    1099           0 :     while (ptr < end && *ptr++ != '\n')
    1100             :       ;
    1101             :   }
    1102           5 :   *flagsp = 0;
    1103           5 :   for (; start < ptr; start++) {
    1104           0 :     if (*start == '#')
    1105           0 :       *flagsp = (SHORT_LABEL | (*flagsp & (FORCE_RIGHT_BRACKET
    1106             :                                            | FORCE_LEFT_BRACKET)));
    1107           0 :     else if (*start == '[')
    1108           0 :       *flagsp |= FORCE_LEFT_BRACKET;
    1109           0 :     else if (*start == ']')
    1110           0 :       *flagsp |= FORCE_RIGHT_BRACKET;
    1111           0 :     else if (!csspace(*start))
    1112           0 :       break;
    1113             :   }
    1114           5 :   if (start >= end) {
    1115           0 :     error("empty reference");
    1116           0 :     return new reference;
    1117             :   }
    1118           5 :   reference *database_ref = 0;
    1119           5 :   if (start < ptr)
    1120           0 :     database_ref = find_reference(start, ptr - start);
    1121           5 :   reference *inline_ref = 0;
    1122           5 :   if (ptr < end)
    1123           5 :     inline_ref = new reference(ptr, end - ptr);
    1124           5 :   if (inline_ref) {
    1125           5 :     if (database_ref) {
    1126           0 :       database_ref->merge(*inline_ref);
    1127           0 :       delete inline_ref;
    1128           0 :       return database_ref;
    1129             :     }
    1130             :     else
    1131           5 :       return inline_ref;
    1132             :   }
    1133           0 :   else if (database_ref)
    1134           0 :     return database_ref;
    1135             :   else
    1136           0 :     return new reference;
    1137             : }
    1138             : 
    1139           5 : static void do_ref(const string &str)
    1140             : {
    1141           5 :   if (accumulate)
    1142           0 :     (void) store_reference(str);
    1143             :   else {
    1144           5 :     (void) immediately_handle_reference(str);
    1145           5 :     immediately_output_references();
    1146             :   }
    1147           5 : }
    1148             : 
    1149           0 : static void trim_blanks(string &str)
    1150             : {
    1151           0 :   const char *start = str.contents();
    1152           0 :   const char *end = start + str.length();
    1153           0 :   while (end > start && end[-1] != '\n' && csspace(end[-1]))
    1154           0 :     --end;
    1155           0 :   str.set_length(end - start);
    1156           0 : }
    1157             : 
    1158           3 : void do_bib(const char *filename)
    1159             : {
    1160             :   FILE *fp;
    1161           3 :   if (strcmp(filename, "-") == 0)
    1162           0 :     fp = stdin;
    1163             :   else {
    1164           3 :     errno = 0;
    1165           3 :     fp = fopen(filename, "r");
    1166           3 :     if (fp == 0) {
    1167           0 :       error("cannot open '%1': %2", filename, strerror(errno));
    1168           0 :       return;
    1169             :     }
    1170           3 :     current_filename = filename;
    1171             :   }
    1172           3 :   current_lineno = 1;
    1173             :   enum {
    1174             :     START, MIDDLE, BODY, BODY_START, BODY_BLANK, BODY_DOT
    1175           3 :     } state = START;
    1176           6 :   string body;
    1177             :   for (;;) {
    1178         464 :     int c = getc(fp);
    1179         464 :     if (EOF == c)
    1180           3 :       break;
    1181         461 :     if (is_invalid_input_char(c)) {
    1182          10 :       error("invalid input character code %1; ignoring", c);
    1183          10 :       continue;
    1184             :     }
    1185         451 :     switch (state) {
    1186           5 :     case START:
    1187           5 :       if ('%' == c) {
    1188           5 :         body = c;
    1189           5 :         state = BODY;
    1190             :       }
    1191           0 :       else if (c != '\n')
    1192           0 :         state = MIDDLE;
    1193           5 :       break;
    1194           0 :     case MIDDLE:
    1195           0 :       if ('\n' == c)
    1196           0 :         state = START;
    1197           0 :       break;
    1198         432 :     case BODY:
    1199         432 :       body += c;
    1200         432 :       if ('\n' == c)
    1201          17 :         state = BODY_START;
    1202         432 :       break;
    1203          14 :     case BODY_START:
    1204          14 :       if ('\n' == c) {
    1205           2 :         do_ref(body);
    1206           2 :         state = START;
    1207             :       }
    1208          12 :       else if ('.' == c)
    1209           0 :         state = BODY_DOT;
    1210          12 :       else if (csspace(c)) {
    1211           0 :         state = BODY_BLANK;
    1212           0 :         body += c;
    1213             :       }
    1214             :       else {
    1215          12 :         body += c;
    1216          12 :         state = BODY;
    1217             :       }
    1218          14 :       break;
    1219           0 :     case BODY_BLANK:
    1220           0 :       if ('\n' == c) {
    1221           0 :         trim_blanks(body);
    1222           0 :         do_ref(body);
    1223           0 :         state = START;
    1224             :       }
    1225           0 :       else if (csspace(c))
    1226           0 :         body += c;
    1227             :       else {
    1228           0 :         body += c;
    1229           0 :         state = BODY;
    1230             :       }
    1231           0 :       break;
    1232           0 :     case BODY_DOT:
    1233           0 :       if (']' == c) {
    1234           0 :         do_ref(body);
    1235           0 :         state = MIDDLE;
    1236             :       }
    1237             :       else {
    1238           0 :         body += '.';
    1239           0 :         body += c;
    1240           0 :         state = ('\n' == c) ? BODY_START : BODY;
    1241             :       }
    1242           0 :       break;
    1243           0 :     default:
    1244           0 :       assert(0 == "unhandled case while parsing bibliography file");
    1245             :     }
    1246         451 :     if ('\n' == c)
    1247          19 :       current_lineno++;
    1248         461 :   }
    1249           3 :   switch (state) {
    1250           0 :   case START:
    1251             :   case MIDDLE:
    1252           0 :     break;
    1253           0 :   case BODY:
    1254           0 :     body += '\n';
    1255           0 :     do_ref(body);
    1256           0 :     break;
    1257           3 :   case BODY_DOT:
    1258             :   case BODY_START:
    1259           3 :     do_ref(body);
    1260           3 :     break;
    1261           0 :   case BODY_BLANK:
    1262           0 :     trim_blanks(body);
    1263           0 :     do_ref(body);
    1264           0 :     break;
    1265             :   }
    1266           3 :   fclose(fp);
    1267             : }
    1268             : 
    1269             : // from the Dragon Book
    1270             : 
    1271        2112 : unsigned hash_string(const char *s, int len)
    1272             : {
    1273        2112 :   const char *end = s + len;
    1274        2112 :   unsigned h = 0, g;
    1275        8856 :   while (s < end) {
    1276        6744 :     h <<= 4;
    1277        6744 :     h += *s++;
    1278        6744 :     if ((g = h & 0xf0000000) != 0) {
    1279         372 :       h ^= g >> 24;
    1280         372 :       h ^= g;
    1281             :     }
    1282             :   }
    1283        2112 :   return h;
    1284             : }
    1285             : 
    1286           0 : int next_size(int n)
    1287             : {
    1288             :   static const int table_sizes[] = {
    1289             :     101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009,
    1290             :     80021, 160001, 500009, 1000003, 2000003, 4000037, 8000009,
    1291             :     16000057, 32000011, 64000031, 128000003, 0
    1292             :   };
    1293             : 
    1294             :   const int *p;
    1295           0 :   for (p = table_sizes; *p <= n && *p != 0; p++)
    1296             :     ;
    1297           0 :   assert(*p != 0);
    1298           0 :   return *p;
    1299             : }
    1300             : 
    1301             : // Local Variables:
    1302             : // fill-column: 72
    1303             : // mode: C++
    1304             : // End:
    1305             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14