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