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