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