home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / gnu / groff-1.09-src.lha / src / amiga / groff-1.09 / refer / ref.cc < prev    next >
C/C++ Source or Header  |  1993-03-30  |  27KB  |  1,145 lines

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