LCOV - code coverage report
Current view: top level - preproc/refer - ref.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 111 793 14.0 %
Date: 2026-01-16 17:51:41 Functions: 8 48 16.7 %
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 "ref.h"
      28             : #include "token.h"
      29             : 
      30             : static const char *find_day(const char *, const char *, const char **);
      31             : static int find_month(const char *start, const char *end);
      32             : static void abbreviate_names(string &);
      33             : 
      34             : #define DEFAULT_ARTICLES "the\000a\000an"
      35             : 
      36             : string articles(DEFAULT_ARTICLES, sizeof(DEFAULT_ARTICLES));
      37             : 
      38             : // Multiple occurrences of fields are separated by FIELD_SEPARATOR.
      39             : const char FIELD_SEPARATOR = '\0';
      40             : 
      41             : const char MULTI_FIELD_NAMES[] = "AE";
      42             : const char *AUTHOR_FIELDS = "AQ";
      43             : 
      44             : enum { OTHER, JOURNAL_ARTICLE, BOOK, ARTICLE_IN_BOOK, TECH_REPORT, BELL_TM };
      45             : 
      46             : const char *reference_types[] = {
      47             :   "other",
      48             :   "journal-article",
      49             :   "book",
      50             :   "article-in-book",
      51             :   "tech-report",
      52             :   "bell-tm",
      53             : };
      54             : 
      55             : static string temp_fields[256];
      56             : 
      57           5 : reference::reference(const char *start, int len, reference_id *ridp)
      58             : : h(0), merged(0), no(-1), field(0), nfields(0), label_ptr(0),
      59           5 :   computed_authors(0), last_needed_author(-1), nauthors(-1)
      60             : {
      61             :   int i;
      62        1285 :   for (i = 0; i < 256; i++)
      63        1280 :     field_index[i] = NULL_FIELD_INDEX;
      64           5 :   if (ridp)
      65           0 :     rid = *ridp;
      66           5 :   if (start == 0)
      67           0 :     return;
      68           5 :   if (len <= 0)
      69           0 :     return;
      70           5 :   const char *end = start + len;
      71           5 :   const char *ptr = start;
      72           5 :   assert(*ptr == '%');
      73          22 :   while (ptr < end) {
      74          17 :     if (ptr + 1 < end && ptr[1] != '\0'
      75          51 :         && ((ptr[1] != '%' && ptr[1] == annotation_field)
      76          17 :             || (ptr + 2 < end && ptr[1] == '%' && ptr[2] != '\0'
      77           0 :                 && discard_fields.search(ptr[2]) < 0))) {
      78           0 :       if (ptr[1] == '%')
      79           0 :         ptr++;
      80           0 :       string &f = temp_fields[(unsigned char)ptr[1]];
      81           0 :       ptr += 2;
      82           0 :       while (ptr < end && csspace(*ptr))
      83           0 :         ptr++;
      84             :       for (;;) {
      85             :         for (;;) {
      86           0 :           if (ptr >= end) {
      87           0 :             f += '\n';
      88           0 :             break;
      89             :           }
      90           0 :           f += *ptr;
      91           0 :           if (*ptr++ == '\n')
      92           0 :             break;
      93             :         }
      94           0 :         if (ptr >= end || *ptr == '%')
      95             :           break;
      96             :       }
      97             :     }
      98          17 :     else if (ptr + 1 < end && ptr[1] != '\0' && ptr[1] != '%'
      99          34 :              && discard_fields.search(ptr[1]) < 0) {
     100          13 :       string &f = temp_fields[(unsigned char)ptr[1]];
     101          13 :       if (f.length() > 0) {
     102           0 :         if (strchr(MULTI_FIELD_NAMES, ptr[1]) != 0)
     103           0 :           f += FIELD_SEPARATOR;
     104             :         else
     105           0 :           f.clear();
     106             :       }
     107          13 :       ptr += 2;
     108          13 :       if (ptr < end) {
     109          13 :         if (*ptr == ' ')
     110          13 :           ptr++;
     111             :         for (;;) {
     112          13 :           const char *p = ptr;
     113         226 :           while (ptr < end && *ptr != '\n')
     114         213 :             ptr++;
     115             :           // strip trailing whitespace
     116          13 :           const char *q = ptr;
     117          13 :           while (q > p && q[-1] != '\n' && csspace(q[-1]))
     118           0 :             q--;
     119         226 :           while (p < q)
     120         213 :             f += *p++;
     121          13 :           if (ptr >= end)
     122           0 :             break;
     123          13 :           ptr++;
     124          13 :           if (ptr >= end)
     125           3 :             break;
     126          10 :           if (*ptr == '%')
     127          10 :             break;
     128           0 :           f += ' ';
     129           0 :         }
     130             :       }
     131             :     }
     132             :     else {
     133             :       // skip this field
     134             :       for (;;) {
     135         184 :         while (ptr < end && *ptr++ != '\n')
     136             :           ;
     137           4 :         if (ptr >= end || *ptr == '%')
     138             :           break;
     139             :       }
     140             :     }
     141             :   }
     142        1285 :   for (i = 0; i < 256; i++)
     143        1280 :     if (temp_fields[i].length() > 0)
     144          13 :       nfields++;
     145          18 :   field = new string[nfields];
     146           5 :   int j = 0;
     147        1285 :   for (i = 0; i < 256; i++)
     148        1280 :     if (temp_fields[i].length() > 0) {
     149          13 :       field[j].move(temp_fields[i]);
     150          13 :       if (abbreviate_fields.search(i) >= 0)
     151           0 :         abbreviate_names(field[j]);
     152          13 :       field_index[i] = j;
     153          13 :       j++;
     154             :     }
     155             : }
     156             : 
     157           5 : reference::~reference()
     158             : {
     159           5 :   if (nfields > 0)
     160          18 :     delete[] field;
     161           5 : }
     162             : 
     163             : // ref is the inline, this is the database ref
     164             : 
     165           0 : void reference::merge(reference &ref)
     166             : {
     167             :   int i;
     168           0 :   for (i = 0; i < 256; i++)
     169           0 :     if (field_index[i] != NULL_FIELD_INDEX)
     170           0 :       temp_fields[i].move(field[field_index[i]]);
     171           0 :   for (i = 0; i < 256; i++)
     172           0 :     if (ref.field_index[i] != NULL_FIELD_INDEX)
     173           0 :       temp_fields[i].move(ref.field[ref.field_index[i]]);
     174           0 :   for (i = 0; i < 256; i++)
     175           0 :     field_index[i] = NULL_FIELD_INDEX;
     176           0 :   int old_nfields = nfields;
     177           0 :   nfields = 0;
     178           0 :   for (i = 0; i < 256; i++)
     179           0 :     if (temp_fields[i].length() > 0)
     180           0 :       nfields++;
     181           0 :   if (nfields != old_nfields) {
     182           0 :     if (old_nfields > 0)
     183           0 :       delete[] field;
     184           0 :     field = new string[nfields];
     185             :   }
     186           0 :   int j = 0;
     187           0 :   for (i = 0; i < 256; i++)
     188           0 :     if (temp_fields[i].length() > 0) {
     189           0 :       field[j].move(temp_fields[i]);
     190           0 :       field_index[i] = j;
     191           0 :       j++;
     192             :     }
     193           0 :   merged = 1;
     194           0 : }
     195             : 
     196           0 : void reference::insert_field(unsigned char c, string &s)
     197             : {
     198           0 :   assert(s.length() > 0);
     199           0 :   if (field_index[c] != NULL_FIELD_INDEX) {
     200           0 :     field[field_index[c]].move(s);
     201           0 :     return;
     202             :   }
     203           0 :   assert(field_index[c] == NULL_FIELD_INDEX);
     204           0 :   string *old_field = field;
     205           0 :   field = new string[nfields + 1];
     206           0 :   int pos = 0;
     207             :   int i;
     208           0 :   for (i = 0; i < int(c); i++)
     209           0 :     if (field_index[i] != NULL_FIELD_INDEX)
     210           0 :       pos++;
     211           0 :   for (i = 0; i < pos; i++)
     212           0 :     field[i].move(old_field[i]);
     213           0 :   field[pos].move(s);
     214           0 :   for (i = pos; i < nfields; i++)
     215           0 :     field[i + 1].move(old_field[i]);
     216           0 :   if (nfields > 0)
     217           0 :     delete[] old_field;
     218           0 :   nfields++;
     219           0 :   field_index[c] = pos;
     220           0 :   for (i = c + 1; i < 256; i++)
     221           0 :     if (field_index[i] != NULL_FIELD_INDEX)
     222           0 :       field_index[i] += 1;
     223             : }
     224             : 
     225           0 : void reference::delete_field(unsigned char c)
     226             : {
     227           0 :   if (field_index[c] == NULL_FIELD_INDEX)
     228           0 :     return;
     229           0 :   string *old_field = field;
     230           0 :   field = new string[nfields - 1];
     231             :   int i;
     232           0 :   for (i = 0; i < int(field_index[c]); i++)
     233           0 :     field[i].move(old_field[i]);
     234           0 :   for (i = field_index[c]; i < nfields - 1; i++)
     235           0 :     field[i].move(old_field[i + 1]);
     236           0 :   if (nfields > 0)
     237           0 :     delete[] old_field;
     238           0 :   nfields--;
     239           0 :   field_index[c] = NULL_FIELD_INDEX;
     240           0 :   for (i = c + 1; i < 256; i++)
     241           0 :     if (field_index[i] != NULL_FIELD_INDEX)
     242           0 :       field_index[i] -= 1;
     243             : }
     244             : 
     245           0 : void reference::compute_hash_code()
     246             : {
     247           0 :   if (!rid.is_null())
     248           0 :     h = rid.hash();
     249             :   else {
     250           0 :     h = 0;
     251           0 :     for (int i = 0; i < nfields; i++)
     252           0 :       if (field[i].length() > 0) {
     253           0 :         h <<= 4;
     254           0 :         h ^= hash_string(field[i].contents(), field[i].length());
     255             :       }
     256             :   }
     257           0 : }
     258             : 
     259           5 : void reference::set_number(int n)
     260             : {
     261           5 :   no = n;
     262           5 : }
     263             : 
     264             : const char SORT_SEP = '\001';
     265             : const char SORT_SUB_SEP = '\002';
     266             : const char SORT_SUB_SUB_SEP = '\003';
     267             : 
     268             : // sep specifies additional word separators
     269             : 
     270           0 : void sortify_words(const char *s, const char *end, const char *sep,
     271             :                    string &result)
     272             : {
     273           0 :   int non_empty = 0;
     274           0 :   int need_separator = 0;
     275             :   for (;;) {
     276           0 :     const char *token_start = s;
     277           0 :     if (!get_token(&s, end))
     278           0 :       break;
     279           0 :     if ((s - token_start == 1
     280           0 :          && (*token_start == ' '
     281           0 :              || *token_start == '\n'
     282           0 :              || (sep && *token_start != '\0'
     283           0 :                  && strchr(sep, *token_start) != 0)))
     284           0 :         || (s - token_start == 2
     285           0 :             && token_start[0] == '\\' && token_start[1] == ' ')) {
     286           0 :       if (non_empty)
     287           0 :         need_separator = 1;
     288             :     }
     289             :     else {
     290           0 :       const token_info *ti = lookup_token(token_start, s);
     291           0 :       if (ti->sortify_non_empty(token_start, s)) {
     292           0 :         if (need_separator) {
     293           0 :           result += ' ';
     294           0 :           need_separator = 0;
     295             :         }
     296           0 :         ti->sortify(token_start, s, result);
     297           0 :         non_empty = 1;
     298             :       }
     299             :     }
     300           0 :   }
     301           0 : }
     302             : 
     303           0 : void sortify_word(const char *s, const char *end, string &result)
     304             : {
     305             :   for (;;) {
     306           0 :     const char *token_start = s;
     307           0 :     if (!get_token(&s, end))
     308           0 :       break;
     309           0 :     const token_info *ti = lookup_token(token_start, s);
     310           0 :     ti->sortify(token_start, s, result);
     311           0 :   }
     312           0 : }
     313             : 
     314           0 : void sortify_other(const char *s, int len, string &key)
     315             : {
     316           0 :   sortify_words(s, s + len, 0, key);
     317           0 : }
     318             : 
     319           0 : void sortify_title(const char *s, int len, string &key)
     320             : {
     321           0 :   const char *end = s + len;
     322           0 :   for (; s < end && (*s == ' ' || *s == '\n'); s++)
     323             :     ;
     324           0 :   const char *ptr = s;
     325             :   for (;;) {
     326           0 :     const char *token_start = ptr;
     327           0 :     if (!get_token(&ptr, end))
     328           0 :       break;
     329           0 :     if (ptr - token_start == 1
     330           0 :         && (*token_start == ' ' || *token_start == '\n'))
     331             :       break;
     332           0 :   }
     333           0 :   if (ptr < end) {
     334           0 :     unsigned int first_word_len = ptr - s - 1;
     335           0 :     const char *ae = articles.contents() + articles.length();
     336           0 :     for (const char *a = articles.contents();
     337           0 :          a < ae;
     338           0 :          a = strchr(a, '\0') + 1)
     339           0 :       if (first_word_len == strlen(a)) {
     340             :         unsigned int j;
     341           0 :         for (j = 0; j < first_word_len; j++)
     342           0 :           if (a[j] != cmlower(s[j]))
     343           0 :             break;
     344           0 :         if (j >= first_word_len) {
     345           0 :           s = ptr;
     346           0 :           for (; s < end && (*s == ' ' || *s == '\n'); s++)
     347             :             ;
     348           0 :           break;
     349             :         }
     350             :       }
     351             :   }
     352           0 :   sortify_words(s, end, 0, key);
     353           0 : }
     354             : 
     355           0 : void sortify_name(const char *s, int len, string &key)
     356             : {
     357             :   const char *last_name_end;
     358           0 :   const char *last_name = find_last_name(s, s + len, &last_name_end);
     359           0 :   sortify_word(last_name, last_name_end, key);
     360           0 :   key += SORT_SUB_SUB_SEP;
     361           0 :   if (last_name > s)
     362           0 :     sortify_words(s, last_name, ".", key);
     363           0 :   key += SORT_SUB_SUB_SEP;
     364           0 :   if (last_name_end < s + len)
     365           0 :     sortify_words(last_name_end, s + len, ".,", key);
     366           0 : }
     367             : 
     368           0 : void sortify_date(const char *s, int len, string &key)
     369             : {
     370             :   const char *year_end;
     371           0 :   const char *year_start = find_year(s, s + len, &year_end);
     372           0 :   if (!year_start) {
     373             :     // Things without years are often 'forthcoming', so it makes sense
     374             :     // that they sort after things with explicit years.
     375           0 :     key += 'A';
     376           0 :     sortify_words(s, s + len, 0, key);
     377           0 :     return;
     378             :   }
     379           0 :   int n = year_end - year_start;
     380           0 :   while (n < 4) {
     381           0 :     key += '0';
     382           0 :     n++;
     383             :   }
     384           0 :   while (year_start < year_end)
     385           0 :     key += *year_start++;
     386           0 :   int m = find_month(s, s + len);
     387           0 :   if (m < 0)
     388           0 :     return;
     389           0 :   key += 'A' + m;
     390             :   const char *day_end;
     391           0 :   const char *day_start = find_day(s, s + len, &day_end);
     392           0 :   if (!day_start)
     393           0 :     return;
     394           0 :   if (day_end - day_start == 1)
     395           0 :     key += '0';
     396           0 :   while (day_start < day_end)
     397           0 :     key += *day_start++;
     398             : }
     399             : 
     400             : // SORT_{SUB,SUB_SUB}_SEP can creep in from use of @ in label specification.
     401             : 
     402           0 : void sortify_label(const char *s, int len, string &key)
     403             : {
     404           0 :   const char *end = s + len;
     405             :   for (;;) {
     406             :     const char *ptr;
     407           0 :     for (ptr = s;
     408           0 :          ptr < end && *ptr != SORT_SUB_SEP && *ptr != SORT_SUB_SUB_SEP;
     409             :          ptr++)
     410             :       ;
     411           0 :     if (ptr > s)
     412           0 :       sortify_words(s, ptr, 0, key);
     413           0 :     s = ptr;
     414           0 :     if (s >= end)
     415           0 :       break;
     416           0 :     key += *s++;
     417           0 :   }
     418           0 : }
     419             : 
     420           0 : void reference::compute_sort_key()
     421             : {
     422           0 :   if (sort_fields.length() == 0)
     423           0 :     return;
     424           0 :   sort_fields += '\0';
     425           0 :   const char *sf = sort_fields.contents();
     426           0 :   int first_time = 1;
     427           0 :   while (*sf != '\0') {
     428           0 :     if (!first_time)
     429           0 :       sort_key += SORT_SEP;
     430           0 :     first_time = 0;
     431           0 :     char f = *sf++;
     432           0 :     int n = 1;
     433           0 :     if (*sf == '+') {
     434           0 :       n = INT_MAX;
     435           0 :       sf++;
     436             :     }
     437           0 :     else if (csdigit(*sf)) {
     438             :       char *ptr;
     439           0 :       long l = strtol(sf, &ptr, 10);
     440           0 :       if (ptr == sf)
     441             :         ;
     442             :       else {
     443           0 :         sf = ptr;
     444           0 :         if (l < 0) {
     445           0 :           n = 1;
     446             :         }
     447             :         else {
     448           0 :           n = int(l);
     449             :         }
     450             :       }
     451             :     }
     452           0 :     if (f == '.')
     453           0 :       sortify_label(label.contents(), label.length(), sort_key);
     454           0 :     else if (f == AUTHOR_FIELDS[0])
     455           0 :       sortify_authors(n, sort_key);
     456             :     else
     457           0 :       sortify_field(f, n, sort_key);
     458             :   }
     459           0 :   sort_fields.set_length(sort_fields.length() - 1);
     460             : }
     461             : 
     462           0 : void reference::sortify_authors(int n, string &result) const
     463             : {
     464           0 :   for (const char *p = AUTHOR_FIELDS; *p != '\0'; p++)
     465           0 :     if (contains_field(*p)) {
     466           0 :       sortify_field(*p, n, result);
     467           0 :       return;
     468             :     }
     469           0 :   sortify_field(AUTHOR_FIELDS[0], n, result);
     470             : }
     471             : 
     472           0 : void reference::canonicalize_authors(string &result) const
     473             : {
     474           0 :   int len = result.length();
     475           0 :   sortify_authors(INT_MAX, result);
     476           0 :   if (result.length() > len)
     477           0 :     result += SORT_SUB_SEP;
     478           0 : }
     479             : 
     480           0 : void reference::sortify_field(unsigned char f, int n, string &result) const
     481             : {
     482             :   typedef void (*sortify_t)(const char *, int, string &);
     483           0 :   sortify_t sortifier = sortify_other;
     484           0 :   switch (f) {
     485           0 :   case 'A':
     486             :   case 'E':
     487           0 :     sortifier = sortify_name;
     488           0 :     break;
     489           0 :   case 'D':
     490           0 :     sortifier = sortify_date;
     491           0 :     break;
     492           0 :   case 'B':
     493             :   case 'J':
     494             :   case 'T':
     495           0 :     sortifier = sortify_title;
     496           0 :     break;
     497             :   }
     498           0 :   int fi = field_index[(unsigned char)f];
     499           0 :   if (fi != NULL_FIELD_INDEX) {
     500           0 :     string &str = field[fi];
     501           0 :     const char *start = str.contents();
     502           0 :     const char *end = start + str.length();
     503           0 :     for (int i = 0; i < n && start < end; i++) {
     504           0 :       const char *p = start;
     505           0 :       while (start < end && *start != FIELD_SEPARATOR)
     506           0 :         start++;
     507           0 :       if (i > 0)
     508           0 :         result += SORT_SUB_SEP;
     509           0 :       (*sortifier)(p, start - p, result);
     510           0 :       if (start < end)
     511           0 :         start++;
     512             :     }
     513             :   }
     514           0 : }
     515             : 
     516           0 : int compare_reference(const reference &r1, const reference &r2)
     517             : {
     518           0 :   assert(r1.no >= 0);
     519           0 :   assert(r2.no >= 0);
     520           0 :   const char *s1 = r1.sort_key.contents();
     521           0 :   int n1 = r1.sort_key.length();
     522           0 :   const char *s2 = r2.sort_key.contents();
     523           0 :   int n2 = r2.sort_key.length();
     524           0 :   for (; n1 > 0 && n2 > 0; --n1, --n2, ++s1, ++s2)
     525           0 :     if (*s1 != *s2)
     526           0 :       return (int)(unsigned char)*s1 - (int)(unsigned char)*s2;
     527           0 :   if (n2 > 0)
     528           0 :     return -1;
     529           0 :   if (n1 > 0)
     530           0 :     return 1;
     531           0 :   return r1.no - r2.no;
     532             : }
     533             : 
     534           0 : int same_reference(const reference &r1, const reference &r2)
     535             : {
     536           0 :   if (!r1.rid.is_null() && r1.rid == r2.rid)
     537           0 :     return 1;
     538           0 :   if (r1.h != r2.h)
     539           0 :     return 0;
     540           0 :   if (r1.nfields != r2.nfields)
     541           0 :     return 0;
     542           0 :   int i = 0;
     543           0 :   for (i = 0; i < 256; i++)
     544           0 :     if (r1.field_index[i] != r2.field_index[i])
     545           0 :       return 0;
     546           0 :   for (i = 0; i < r1.nfields; i++)
     547           0 :     if (r1.field[i] != r2.field[i])
     548           0 :       return 0;
     549           0 :   return 1;
     550             : }
     551             : 
     552           0 : const char *find_last_name(const char *start, const char *end,
     553             :                            const char **endp)
     554             : {
     555           0 :   const char *ptr = start;
     556           0 :   const char *last_word = start;
     557             :   for (;;) {
     558           0 :     const char *token_start = ptr;
     559           0 :     if (!get_token(&ptr, end))
     560           0 :       break;
     561           0 :     if (ptr - token_start == 1) {
     562           0 :       if (*token_start == ',') {
     563           0 :         *endp = token_start;
     564           0 :         return last_word;
     565             :       }
     566           0 :       else if (*token_start == ' ' || *token_start == '\n') {
     567           0 :         if (ptr < end && *ptr != ' ' && *ptr != '\n')
     568           0 :           last_word = ptr;
     569             :       }
     570             :     }
     571           0 :   }
     572           0 :   *endp = end;
     573           0 :   return last_word;
     574             : }
     575             : 
     576           0 : void abbreviate_name(const char *ptr, const char *end, string &result)
     577             : {
     578             :   const char *last_name_end;
     579           0 :   const char *last_name_start = find_last_name(ptr, end, &last_name_end);
     580           0 :   int need_period = 0;
     581             :   for (;;) {
     582           0 :     const char *token_start = ptr;
     583           0 :     if (!get_token(&ptr, last_name_start))
     584           0 :       break;
     585           0 :     const token_info *ti = lookup_token(token_start, ptr);
     586           0 :     if (need_period) {
     587           0 :       if ((ptr - token_start == 1 && *token_start == ' ')
     588           0 :           || (ptr - token_start == 2 && token_start[0] == '\\'
     589           0 :               && token_start[1] == ' '))
     590           0 :         continue;
     591           0 :       if (ti->is_upper())
     592           0 :         result += period_before_initial;
     593             :       else
     594           0 :         result += period_before_other;
     595           0 :       need_period = 0;
     596             :     }
     597           0 :     result.append(token_start, ptr - token_start);
     598           0 :     if (ti->is_upper()) {
     599           0 :       const char *lower_ptr = ptr;
     600           0 :       int first_token = 1;
     601             :       for (;;) {
     602           0 :         token_start = ptr;
     603           0 :         if (!get_token(&ptr, last_name_start))
     604           0 :           break;
     605           0 :         if ((ptr - token_start == 1 && *token_start == ' ')
     606           0 :             || (ptr - token_start == 2 && token_start[0] == '\\'
     607           0 :                 && token_start[1] == ' '))
     608             :           break;
     609           0 :         ti = lookup_token(token_start, ptr);
     610           0 :         if (ti->is_hyphen()) {
     611           0 :           const char *ptr1 = ptr;
     612           0 :           if (get_token(&ptr1, last_name_start)) {
     613           0 :             ti = lookup_token(ptr, ptr1);
     614           0 :             if (ti->is_upper()) {
     615           0 :               result += period_before_hyphen;
     616           0 :               result.append(token_start, ptr1 - token_start);
     617           0 :               ptr = ptr1;
     618             :             }
     619             :           }
     620             :         }
     621           0 :         else if (ti->is_upper()) {
     622             :           // MacDougal -> MacD.
     623           0 :           result.append(lower_ptr, ptr - lower_ptr);
     624           0 :           lower_ptr = ptr;
     625           0 :           first_token = 1;
     626             :         }
     627           0 :         else if (first_token && ti->is_accent()) {
     628           0 :           result.append(token_start, ptr - token_start);
     629           0 :           lower_ptr = ptr;
     630             :         }
     631           0 :         first_token = 0;
     632           0 :       }
     633           0 :       need_period = 1;
     634             :     }
     635           0 :   }
     636           0 :   if (need_period)
     637           0 :     result += period_before_last_name;
     638           0 :   result.append(last_name_start, end - last_name_start);
     639           0 : }
     640             : 
     641           0 : static void abbreviate_names(string &result)
     642             : {
     643           0 :   string str;
     644           0 :   str.move(result);
     645           0 :   const char *ptr = str.contents();
     646           0 :   const char *end = ptr + str.length();
     647           0 :   while (ptr < end) {
     648           0 :     const char *name_end = (char *)memchr(ptr, FIELD_SEPARATOR, end - ptr);
     649           0 :     if (name_end == 0)
     650           0 :       name_end = end;
     651           0 :     abbreviate_name(ptr, name_end, result);
     652           0 :     if (name_end >= end)
     653           0 :       break;
     654           0 :     ptr = name_end + 1;
     655           0 :     result += FIELD_SEPARATOR;
     656             :   }
     657           0 : }
     658             : 
     659           0 : void reverse_name(const char *ptr, const char *name_end, string &result)
     660             : {
     661             :   const char *last_name_end;
     662           0 :   const char *last_name_start = find_last_name(ptr, name_end, &last_name_end);
     663           0 :   result.append(last_name_start, last_name_end - last_name_start);
     664           0 :   while (last_name_start > ptr
     665           0 :          && (last_name_start[-1] == ' ' || last_name_start[-1] == '\n'))
     666           0 :     last_name_start--;
     667           0 :   if (last_name_start > ptr) {
     668           0 :     result += ", ";
     669           0 :     result.append(ptr, last_name_start - ptr);
     670             :   }
     671           0 :   if (last_name_end < name_end)
     672           0 :     result.append(last_name_end, name_end - last_name_end);
     673           0 : }
     674             : 
     675           0 : void reverse_names(string &result, int n)
     676             : {
     677           0 :   if (n <= 0)
     678           0 :     return;
     679           0 :   string str;
     680           0 :   str.move(result);
     681           0 :   const char *ptr = str.contents();
     682           0 :   const char *end = ptr + str.length();
     683           0 :   while (ptr < end) {
     684           0 :     if (--n < 0) {
     685           0 :       result.append(ptr, end - ptr);
     686           0 :       break;
     687             :     }
     688           0 :     const char *name_end = (char *)memchr(ptr, FIELD_SEPARATOR, end - ptr);
     689           0 :     if (name_end == 0)
     690           0 :       name_end = end;
     691           0 :     reverse_name(ptr, name_end, result);
     692           0 :     if (name_end >= end)
     693           0 :       break;
     694           0 :     ptr = name_end + 1;
     695           0 :     result += FIELD_SEPARATOR;
     696             :   }
     697             : }
     698             : 
     699             : // Return number of field separators.
     700             : 
     701          13 : int join_fields(string &f)
     702             : {
     703          13 :   const char *ptr = f.contents();
     704          13 :   int len = f.length();
     705          13 :   int nfield_seps = 0;
     706             :   int j;
     707         226 :   for (j = 0; j < len; j++)
     708         213 :     if (ptr[j] == FIELD_SEPARATOR)
     709           0 :       nfield_seps++;
     710          13 :   if (nfield_seps == 0)
     711          13 :     return 0;
     712           0 :   string temp;
     713           0 :   int field_seps_left = nfield_seps;
     714           0 :   for (j = 0; j < len; j++) {
     715           0 :     if (ptr[j] == FIELD_SEPARATOR) {
     716           0 :       if (nfield_seps == 1)
     717           0 :         temp += join_authors_exactly_two;
     718           0 :       else if (--field_seps_left == 0)
     719           0 :         temp += join_authors_last_two;
     720             :       else
     721           0 :         temp += join_authors_default;
     722             :     }
     723             :     else
     724           0 :       temp += ptr[j];
     725             :   }
     726           0 :   f = temp;
     727           0 :   return nfield_seps;
     728             : }
     729             : 
     730           0 : void uppercase(const char *start, const char *end, string &result)
     731             : {
     732             :   for (;;) {
     733           0 :     const char *token_start = start;
     734           0 :     if (!get_token(&start, end))
     735           0 :       break;
     736           0 :     const token_info *ti = lookup_token(token_start, start);
     737           0 :     ti->upper_case(token_start, start, result);
     738           0 :   }
     739           0 : }
     740             : 
     741           0 : void lowercase(const char *start, const char *end, string &result)
     742             : {
     743             :   for (;;) {
     744           0 :     const char *token_start = start;
     745           0 :     if (!get_token(&start, end))
     746           0 :       break;
     747           0 :     const token_info *ti = lookup_token(token_start, start);
     748           0 :     ti->lower_case(token_start, start, result);
     749           0 :   }
     750           0 : }
     751             : 
     752           0 : void capitalize(const char *ptr, const char *end, string &result)
     753             : {
     754           0 :   int in_small_point_size = 0;
     755             :   for (;;) {
     756           0 :     const char *start = ptr;
     757           0 :     if (!get_token(&ptr, end))
     758           0 :       break;
     759           0 :     const token_info *ti = lookup_token(start, ptr);
     760           0 :     const char *char_end = ptr;
     761           0 :     int is_lower = ti->is_lower();
     762           0 :     if ((is_lower || ti->is_upper()) && get_token(&ptr, end)) {
     763           0 :       const token_info *ti2 = lookup_token(char_end, ptr);
     764           0 :       if (!ti2->is_accent())
     765           0 :         ptr = char_end;
     766             :     }
     767           0 :     if (is_lower) {
     768           0 :       if (!in_small_point_size) {
     769           0 :         result += "\\s-2";
     770           0 :         in_small_point_size = 1;
     771             :       }
     772           0 :       ti->upper_case(start, char_end, result);
     773           0 :       result.append(char_end, ptr - char_end);
     774             :     }
     775             :     else {
     776           0 :       if (in_small_point_size) {
     777           0 :         result += "\\s+2";
     778           0 :         in_small_point_size = 0;
     779             :       }
     780           0 :       result.append(start, ptr - start);
     781             :     }
     782           0 :   }
     783           0 :   if (in_small_point_size)
     784           0 :     result += "\\s+2";
     785           0 : }
     786             : 
     787           0 : void capitalize_field(string &str)
     788             : {
     789           0 :   string temp;
     790           0 :   capitalize(str.contents(), str.contents() + str.length(), temp);
     791           0 :   str.move(temp);
     792           0 : }
     793             : 
     794           8 : int is_terminated(const char *ptr, const char *end)
     795             : {
     796           8 :   const char *last_token = end;
     797             :   for (;;) {
     798         189 :     const char *p = ptr;
     799         189 :     if (!get_token(&ptr, end))
     800           8 :       break;
     801         181 :     last_token = p;
     802         181 :   }
     803           8 :   return end - last_token == 1
     804           8 :     && (*last_token == '.' || *last_token == '!' || *last_token == '?');
     805             : }
     806             : 
     807           5 : void reference::output(FILE *fp)
     808             : {
     809           5 :   fputs(".]-\n", fp);
     810        1285 :   for (int i = 0; i < 256; i++)
     811        1280 :     if (field_index[i] != NULL_FIELD_INDEX && i != annotation_field) {
     812          13 :       string &f = field[field_index[i]];
     813          13 :       if (!csdigit(i)) {
     814          13 :         int j = reverse_fields.search(i);
     815          13 :         if (j >= 0) {
     816             :           int n;
     817           0 :           int len = reverse_fields.length();
     818           0 :           if (++j < len && csdigit(reverse_fields[j])) {
     819           0 :             n = reverse_fields[j] - '0';
     820           0 :             for (++j; j < len && csdigit(reverse_fields[j]); j++)
     821             :               // should check for overflow
     822           0 :               n = n*10 + reverse_fields[j] - '0';
     823             :           }
     824             :           else
     825           0 :             n = INT_MAX;
     826           0 :           reverse_names(f, n);
     827             :         }
     828             :       }
     829          13 :       int is_multiple = join_fields(f) > 0;
     830          13 :       if (capitalize_fields.search(i) >= 0)
     831           0 :         capitalize_field(f);
     832          13 :       if (memchr(f.contents(), '\n', f.length()) == 0) {
     833          13 :         fprintf(fp, ".ds [%c ", i);
     834          13 :         if (f[0] == ' ' || f[0] == '\\' || f[0] == '"')
     835           0 :           putc('"', fp);
     836          13 :         put_string(f, fp);
     837          13 :         putc('\n', fp);
     838             :       }
     839             :       else {
     840           0 :         fprintf(fp, ".de [%c\n", i);
     841           0 :         put_string(f, fp);
     842           0 :         fputs("..\n", fp);
     843             :       }
     844          13 :       if (i == 'P') {
     845           0 :         int multiple_pages = 0;
     846           0 :         const char *s = f.contents();
     847           0 :         const char *end = f.contents() + f.length();
     848             :         for (;;) {
     849           0 :           const char *token_start = s;
     850           0 :           if (!get_token(&s, end))
     851           0 :             break;
     852           0 :           const token_info *ti = lookup_token(token_start, s);
     853           0 :           if (ti->is_hyphen() || ti->is_range_sep()) {
     854           0 :             multiple_pages = 1;
     855           0 :             break;
     856             :           }
     857           0 :         }
     858           0 :         fprintf(fp, ".nr [P %d\n", multiple_pages);
     859             :       }
     860          13 :       else if (i == 'E')
     861           0 :         fprintf(fp, ".nr [E %d\n", is_multiple);
     862             :     }
     863          20 :   for (const char *p = "TAO"; *p; p++) {
     864          15 :     int fi = field_index[(unsigned char)*p];
     865          15 :     if (fi != NULL_FIELD_INDEX) {
     866           8 :       string &f = field[fi];
     867           8 :       fprintf(fp, ".nr [%c %d\n", *p,
     868           8 :               is_terminated(f.contents(), f.contents() + f.length()));
     869             :     }
     870             :   }
     871           5 :   int t = classify();
     872           5 :   fprintf(fp, ".][ %d %s\n", t, reference_types[t]);
     873           5 :   if (annotation_macro.length() > 0 && annotation_field >= 0
     874           5 :       && field_index[annotation_field] != NULL_FIELD_INDEX) {
     875           0 :     putc('.', fp);
     876           0 :     put_string(annotation_macro, fp);
     877           0 :     putc('\n', fp);
     878           0 :     put_string(field[field_index[annotation_field]], fp);
     879             :   }
     880           5 : }
     881             : 
     882           0 : void reference::print_sort_key_comment(FILE *fp)
     883             : {
     884           0 :   fputs(".\\\"", fp);
     885           0 :   put_string(sort_key, fp);
     886           0 :   putc('\n', fp);
     887           0 : }
     888             : 
     889           0 : const char *find_year(const char *start, const char *end, const char **endp)
     890             : {
     891             :   for (;;) {
     892           0 :     while (start < end && !csdigit(*start))
     893           0 :       start++;
     894           0 :     const char *ptr = start;
     895           0 :     if (start == end)
     896           0 :       break;
     897           0 :     while (ptr < end && csdigit(*ptr))
     898           0 :       ptr++;
     899           0 :     if (ptr - start == 4 || ptr - start == 3
     900           0 :         || (ptr - start == 2
     901           0 :             && (start[0] >= '4' || (start[0] == '3' && start[1] >= '2')))) {
     902           0 :       *endp = ptr;
     903           0 :       return start;
     904             :     }
     905           0 :     start = ptr;
     906           0 :   }
     907           0 :   return 0;
     908             : }
     909             : 
     910           0 : static const char *find_day(const char *start, const char *end,
     911             :                             const char **endp)
     912             : {
     913             :   for (;;) {
     914           0 :     while (start < end && !csdigit(*start))
     915           0 :       start++;
     916           0 :     const char *ptr = start;
     917           0 :     if (start == end)
     918           0 :       break;
     919           0 :     while (ptr < end && csdigit(*ptr))
     920           0 :       ptr++;
     921           0 :     if ((ptr - start == 1 && start[0] != '0')
     922           0 :         || (ptr - start == 2 &&
     923           0 :             (start[0] == '1'
     924           0 :              || start[0] == '2'
     925           0 :              || (start[0] == '3' && start[1] <= '1')
     926           0 :              || (start[0] == '0' && start[1] != '0')))) {
     927           0 :       *endp = ptr;
     928           0 :       return start;
     929             :     }
     930           0 :     start = ptr;
     931           0 :   }
     932           0 :   return 0;
     933             : }
     934             : 
     935           0 : static int find_month(const char *start, const char *end)
     936             : {
     937             :   static const char *months[] = {
     938             :     "january",
     939             :     "february",
     940             :     "march",
     941             :     "april",
     942             :     "may",
     943             :     "june",
     944             :     "july",
     945             :     "august",
     946             :     "september",
     947             :     "october",
     948             :     "november",
     949             :     "december",
     950             :   };
     951             :   for (;;) {
     952           0 :     while (start < end && !csalpha(*start))
     953           0 :       start++;
     954           0 :     const char *ptr = start;
     955           0 :     if (start == end)
     956           0 :       break;
     957           0 :     while (ptr < end && csalpha(*ptr))
     958           0 :       ptr++;
     959           0 :     if (ptr - start >= 3) {
     960           0 :       for (unsigned int i = 0; i < countof(months); i++) {
     961           0 :         const char *q = months[i];
     962           0 :         const char *p = start;
     963           0 :         for (; p < ptr; p++, q++)
     964           0 :           if (cmlower(*p) != *q)
     965           0 :             break;
     966           0 :         if (p >= ptr)
     967           0 :           return i;
     968             :       }
     969             :     }
     970           0 :     start = ptr;
     971           0 :   }
     972           0 :   return -1;
     973             : }
     974             : 
     975          22 : int reference::contains_field(char c) const
     976             : {
     977          22 :   return field_index[(unsigned char)c] != NULL_FIELD_INDEX;
     978             : }
     979             : 
     980           5 : int reference::classify()
     981             : {
     982           5 :   if (contains_field('J'))
     983           0 :     return JOURNAL_ARTICLE;
     984           5 :   if (contains_field('B'))
     985           2 :     return ARTICLE_IN_BOOK;
     986           3 :   if (contains_field('G'))
     987           0 :     return TECH_REPORT;
     988           3 :   if (contains_field('R'))
     989           0 :     return TECH_REPORT;
     990           3 :   if (contains_field('I'))
     991           0 :     return BOOK;
     992           3 :   if (contains_field('M'))
     993           0 :     return BELL_TM;
     994           3 :   return OTHER;
     995             : }
     996             : 
     997           0 : const char *reference::get_year(const char **endp) const
     998             : {
     999           0 :   if (field_index['D'] != NULL_FIELD_INDEX) {
    1000           0 :     string &date = field[field_index['D']];
    1001           0 :     const char *start = date.contents();
    1002           0 :     const char *end = start + date.length();
    1003           0 :     return find_year(start, end, endp);
    1004             :   }
    1005             :   else
    1006           0 :     return 0;
    1007             : }
    1008             : 
    1009           0 : const char *reference::get_field(unsigned char c, const char **endp) const
    1010             : {
    1011           0 :   if (field_index[c] != NULL_FIELD_INDEX) {
    1012           0 :     string &f = field[field_index[c]];
    1013           0 :     const char *start = f.contents();
    1014           0 :     *endp = start + f.length();
    1015           0 :     return start;
    1016             :   }
    1017             :   else
    1018           0 :     return 0;
    1019             : }
    1020             : 
    1021           0 : const char *reference::get_date(const char **endp) const
    1022             : {
    1023           0 :   return get_field('D', endp);
    1024             : }
    1025             : 
    1026           0 : const char *nth_field(int i, const char *start, const char **endp)
    1027             : {
    1028           0 :   while (--i >= 0) {
    1029           0 :     start = (char *)memchr(start, FIELD_SEPARATOR, *endp - start);
    1030           0 :     if (!start)
    1031           0 :       return 0;
    1032           0 :     start++;
    1033             :   }
    1034           0 :   const char *e = (char *)memchr(start, FIELD_SEPARATOR, *endp - start);
    1035           0 :   if (e)
    1036           0 :     *endp = e;
    1037           0 :   return start;
    1038             : }
    1039             : 
    1040           0 : const char *reference::get_author(int i, const char **endp) const
    1041             : {
    1042           0 :   for (const char *f = AUTHOR_FIELDS; *f != '\0'; f++) {
    1043           0 :     const char *start = get_field(*f, endp);
    1044           0 :     if (start) {
    1045           0 :       if (strchr(MULTI_FIELD_NAMES, *f) != 0)
    1046           0 :         return nth_field(i, start, endp);
    1047           0 :       else if (i == 0)
    1048           0 :         return start;
    1049             :       else
    1050           0 :         return 0;
    1051             :     }
    1052             :   }
    1053           0 :   return 0;
    1054             : }
    1055             : 
    1056           0 : const char *reference::get_author_last_name(int i, const char **endp) const
    1057             : {
    1058           0 :   for (const char *f = AUTHOR_FIELDS; *f != '\0'; f++) {
    1059           0 :     const char *start = get_field(*f, endp);
    1060           0 :     if (start) {
    1061           0 :       if (strchr(MULTI_FIELD_NAMES, *f) != 0) {
    1062           0 :         start = nth_field(i, start, endp);
    1063           0 :         if (!start)
    1064           0 :           return 0;
    1065             :       }
    1066           0 :       if (*f == 'A')
    1067           0 :         return find_last_name(start, *endp, endp);
    1068             :       else
    1069           0 :         return start;
    1070             :     }
    1071             :   }
    1072           0 :   return 0;
    1073             : }
    1074             : 
    1075           0 : void reference::set_date(string &d)
    1076             : {
    1077           0 :   if (d.length() == 0)
    1078           0 :     delete_field('D');
    1079             :   else
    1080           0 :     insert_field('D', d);
    1081           0 : }
    1082             : 
    1083           0 : int same_year(const reference &r1, const reference &r2)
    1084             : {
    1085             :   const char *ye1;
    1086           0 :   const char *ys1 = r1.get_year(&ye1);
    1087             :   const char *ye2;
    1088           0 :   const char *ys2 = r2.get_year(&ye2);
    1089           0 :   if (ys1 == 0) {
    1090           0 :     if (ys2 == 0)
    1091           0 :       return same_date(r1, r2);
    1092             :     else
    1093           0 :       return 0;
    1094             :   }
    1095           0 :   else if (ys2 == 0)
    1096           0 :     return 0;
    1097           0 :   else if (ye1 - ys1 != ye2 - ys2)
    1098           0 :     return 0;
    1099             :   else
    1100           0 :     return memcmp(ys1, ys2, ye1 - ys1) == 0;
    1101             : }
    1102             : 
    1103           0 : int same_date(const reference &r1, const reference &r2)
    1104             : {
    1105             :   const char *e1;
    1106           0 :   const char *s1 = r1.get_date(&e1);
    1107             :   const char *e2;
    1108           0 :   const char *s2 = r2.get_date(&e2);
    1109           0 :   if (s1 == 0)
    1110           0 :     return s2 == 0;
    1111           0 :   else if (s2 == 0)
    1112           0 :     return 0;
    1113           0 :   else if (e1 - s1 != e2 - s2)
    1114           0 :     return 0;
    1115             :   else
    1116           0 :     return memcmp(s1, s2, e1 - s1) == 0;
    1117             : }
    1118             : 
    1119           0 : const char *reference::get_sort_field(int i, int si, int ssi,
    1120             :                                       const char **endp) const
    1121             : {
    1122           0 :   const char *start = sort_key.contents();
    1123           0 :   const char *end = start + sort_key.length();
    1124           0 :   if (i < 0) {
    1125           0 :     *endp = end;
    1126           0 :     return start;
    1127             :   }
    1128           0 :   while (--i >= 0) {
    1129           0 :     start = (char *)memchr(start, SORT_SEP, end - start);
    1130           0 :     if (!start)
    1131           0 :       return 0;
    1132           0 :     start++;
    1133             :   }
    1134           0 :   const char *e = (char *)memchr(start, SORT_SEP, end - start);
    1135           0 :   if (e)
    1136           0 :     end = e;
    1137           0 :   if (si < 0) {
    1138           0 :     *endp = end;
    1139           0 :     return start;
    1140             :   }
    1141           0 :   while (--si >= 0) {
    1142           0 :     start = (char *)memchr(start, SORT_SUB_SEP, end - start);
    1143           0 :     if (!start)
    1144           0 :       return 0;
    1145           0 :     start++;
    1146             :   }
    1147           0 :   e = (char *)memchr(start, SORT_SUB_SEP, end - start);
    1148           0 :   if (e)
    1149           0 :     end = e;
    1150           0 :   if (ssi < 0) {
    1151           0 :     *endp = end;
    1152           0 :     return start;
    1153             :   }
    1154           0 :   while (--ssi >= 0) {
    1155           0 :     start = (char *)memchr(start, SORT_SUB_SUB_SEP, end - start);
    1156           0 :     if (!start)
    1157           0 :       return 0;
    1158           0 :     start++;
    1159             :   }
    1160           0 :   e = (char *)memchr(start, SORT_SUB_SUB_SEP, end - start);
    1161           0 :   if (e)
    1162           0 :     end = e;
    1163           0 :   *endp = end;
    1164           0 :   return start;
    1165             : }
    1166             : 
    1167             : // Local Variables:
    1168             : // fill-column: 72
    1169             : // mode: C++
    1170             : // End:
    1171             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14