xref: /original-bsd/contrib/bib/src/bib.c (revision 5e5b7b99)
1 #ifndef lint
2 static char sccsid[] = "@(#)bib.c	2.11	05/27/93";
3 #endif not lint
4 /*
5         Bib - bibliographic formatter
6 
7         Authored by: Tim Budd, University of Arizona, 1983.
8                 lookup routines written by gary levin 2/82
9 
10                 version 7/4/83
11 
12         Various modifications suggested by:
13                 David Cherveny - Duke University Medical Center
14                 Phil Garrison - UC Berkeley
15                 M. J. Hawley - Yale University
16 
17 	       version 8/23/1988
18 
19 	 Adapted to use TiB style macro calls (i.e. |macro|)
20 	       A. Dain Samples
21 
22                                                         */
23 
24 # include <stdio.h>
25 # include <ctype.h>
26 # include "bib.h"
27 
28 # define HUNTSIZE 512                /* maximum size of hunt string         */
29 # define MAXREFS  300                /* maximum number of references        */
30 # define MAXATONCE 35                /* maximum references at one location  */
31 
32 # define getch(c,fd) (c = getc(fd))
33 # define echoc(c,ifd,ofd) (getch(c,ifd) == EOF ? c : putc(c,ofd))
34 # define testc(c,d,ifd,ofd) (getch(c, ifd) == d ? putc(c, ofd) : 0)
35 
36 /* global variables */
37    FILE *rfd;                   /* reference temporary file              */
38 #ifndef INCORE
39    char reffile[] = TMPREFFILE ;/* temporary file (see bib.h)            */
40 #endif not INCORE
41    struct refinfo refinfo[MAXREFS];	/* reference information */
42    struct refinfo *refssearch();
43    struct refinfo *refshash[HASHSIZE];
44    long int rend = 1;           /* last position in rfd (first char unused)*/
45    int numrefs = 0;            /* number of references generated so far */
46    FILE *tfd;                   /* output of pass 1 of file(s)           */
47    char bibtmpfile[] = TMPTEXTFILE ; /* output of pass 1                    */
48    char *common = COMFILE;       /* common word file                      */
49    int  findex = false;         /* can we read the file INDEX ?          */
50 
51 char *programName;
52 
53 /* global variables in bibargs */
54    extern int foot, doacite, sort, max_klen, personal;
55    extern int hyphen, ordcite, biblineno;
56    extern char sortstr[], pfile[], citetemplate[], bibfname[];
57    extern int TibOption;
58 
59 #include <signal.h>
60 
61 main(argc, argv)
62    int argc;
63    char **argv;
64 {  int rcomp();
65    int intr();
66 
67    /* the file INDEX in the current directory is the default index,
68       if it is present */
69 
70    InitDirectory(BMACLIB,N_BMACLIB);
71    InitDirectory(COMFILE,N_COMFILE);
72    InitDirectory(DEFSTYLE,N_DEFSTYLE);
73 
74    signal(SIGINT, intr);
75    rfd = fopen( INDXFILE , "r");
76    if (rfd != NULL) {
77       findex = true;
78       fclose(rfd);
79       }
80 
81 #ifndef INCORE
82    /* open temporaries, reffile will contain references collected in
83       pass 1, and bibtmpfile will contain text.
84    */
85    mktemp(reffile);
86    rfd = fopen(reffile,"w+");
87    if (rfd == NULL)
88       error("can't open temporary reference file, %s", reffile);
89    putc('x', rfd);      /* put garbage in first position (not used) */
90 #endif not INCORE
91    mktemp(bibtmpfile);
92    tfd = fopen(bibtmpfile,"w");
93    if (tfd == NULL)
94       error("can't open temporary output file, %s", bibtmpfile);
95 
96     /*
97        pass1 - read files, looking for citations
98                arguments are read by doargs (bibargs.c)
99     */
100 
101    if (doargs(argc, argv, DEFSTYLE ) == 0) { /* may not return */
102       strcpy(bibfname, "<stdin>");
103       rdtext(stdin);
104       }
105 
106    /*
107     sort references, make citations, add disambiguating characters
108    */
109 
110    if (sort)
111       qsort(refinfo, numrefs, sizeof(struct refinfo), rcomp);
112    makecites();
113    disambiguate();
114 
115    /*
116    reopen temporaries
117    */
118 
119    fclose(tfd);
120    tfd = fopen(bibtmpfile,"r");
121    if (tfd == NULL)
122       error("can't open temporary output file %s for reading", bibtmpfile);
123    /*
124    pass 2 - reread files, replacing references
125    */
126    pass2(tfd, stdout);
127    cleanup(0);
128 }
129 /* interrupt processing */
130 intr()
131 {
132    cleanup(1);
133 }
134 /* clean up and exit */
135 cleanup(val)
136 {
137    fclose(tfd);
138 #ifndef INCORE
139    fclose(rfd);
140    unlink(reffile);
141 #endif INCORE
142 #ifndef DEBUG
143    unlink(bibtmpfile);
144 #endif DEBUG
145    exit(val);
146 }
147 
148 /* rdtext - read and process a text file, looking for [. commands */
149    rdtext(fd)
150    FILE *fd;
151 {  char lastc, c, d;
152 
153    lastc = '\0';
154    biblineno = 1;
155    while (getch(c, fd) != EOF)
156       if (c == '[' || c == '{')
157          if (getch(d, fd) == '.') { /* found a reference */
158             if (c == '{') { if (lastc) putc(lastc, tfd);}
159             else
160                switch (lastc) {
161                   case '\0': break;
162                   case ' ': fputs("\\*([<", tfd); break;
163                   case '.': case ',': case '?': case ':':
164                   case ';': case '!': case '"': case '\'':
165                             fputs("\\*([", tfd);  /* fall through */
166                   default:  putc(lastc, tfd); break;
167                   }
168             rdcite(fd, c);
169             if (c == '[')
170                switch (lastc) {
171                   case '\0': break;
172                   case ' ': fputs("\\*(>]", tfd); break;
173                   case '.': case ',': case '?': case ':':
174                   case ';': case '!': case '"': case '\'':
175                             fprintf(tfd,"\\*(%c]", lastc); break;
176                   }
177             lastc = '\0';
178             }
179          else {
180             if (lastc != '\0') putc(lastc, tfd);
181             ungetc(d, fd);
182             lastc = c;
183             }
184       else {
185          if (lastc != '\0') putc(lastc, tfd);
186          lastc = c;
187          if (c == '\n') biblineno++;
188          }
189    if (lastc != '\0') putc(lastc, tfd);
190 }
191 
192 /* rdcite - read citation information inside a [. command */
193    rdcite(fd, ch)
194    FILE *fd;
195    char ch;
196 {  int getref();
197    char huntstr[HUNTSIZE], c, info[HUNTSIZE];
198 
199    if (ch == '[')
200       if (doacite) fputs("\\*([[", tfd);
201    else
202       if (doacite) fputs("\\*([{", tfd);
203    huntstr[0] = info[0] = 0;
204    while (getch(c, fd) != EOF)
205       switch (c) {
206          case ',':
207 	    citemark(info, huntstr, "");
208             huntstr[0] = info[0] = 0;
209             break;
210          case '.':
211             while (getch(c, fd) == '.') ;
212             if (c == ']') {
213 	       citemark(info, huntstr, "\\*(]]");
214                return;
215                }
216             else if (c == '}') {
217 	       citemark(info, huntstr, "\\*(}]");
218                return;
219                }
220             else
221                addc(huntstr, c);
222             break;
223 
224          case '{':
225             while (getch(c, fd) != '}')
226                if (c == EOF) {
227                   error("ill formed reference");
228                   }
229                 else
230                   addc(info, c);
231             break;
232 
233          case '\n':
234             biblineno++;
235          case '\t':
236             c = ' ';   /* fall through */
237 
238          default:
239             addc(huntstr,c);
240          }
241    error("end of file reading citation");
242 }
243 char	ncitetemplate[64];
244 int	changecite;
245 citemark(info, huntstr, tail)
246 	char *info, *huntstr, *tail;
247 {
248 	char c = CITEMARK;
249         long int  n;
250 	/*
251 	 *	getref sets ncitetemplate as a side effect
252 	 */
253 	n = getref(huntstr);
254 	if (ncitetemplate[0]){
255 		fprintf(tfd, "%c%s%c", FMTSTART, ncitetemplate, FMTEND);
256 		ncitetemplate[0] = 0;
257 	}
258 	fprintf(tfd, "%c%d%c%s%c%s", c ,n, c, info, CITEEND, doacite?tail:"");
259 
260 }
261 
262 /* addc - add a character to hunt string */
263 addc(huntstr, c)
264    char huntstr[HUNTSIZE], c;
265 {  int  i;
266 
267    i = strlen(huntstr);
268    if (i > HUNTSIZE)
269       error("citation too long, max of %d", HUNTSIZE);
270    huntstr[i] = c;
271    huntstr[i+1] = 0;
272 }
273 
274 /* getref - if an item was already referenced, return its reference index
275                 otherwise create a new entry */
276 int getref(huntstr)
277    char huntstr[HUNTSIZE];
278 {  char rf[REFSIZE], *r, *hunt();
279    int	match(), getwrd();
280    char	*realhstr;
281    int hash;
282    struct refinfo *rp;
283    int	lg;
284 
285    realhstr = huntstr;
286    if (strncmp(huntstr, "$C$", 3) == 0){
287 	char *from, *to;
288 	changecite++;
289 	for(from = huntstr + 3, to = ncitetemplate; *from; from++, to++){
290 		switch(*from){
291 		case '\0':
292 		case ' ':
293 		case '\n':
294 		case '\t':	goto outcopy;
295 		default:	*to = *from;
296 		}
297 	}
298    outcopy: ;
299 	*to = 0;
300 	*from = 0;
301 	realhstr = from + 1;
302    }
303    r = hunt(realhstr);
304    if (r != NULL) {
305       /* expand defined string */
306       strcpy(rf, r);
307       free(r);
308       expand(rf);
309       /* see if reference has already been cited */
310       if (foot == false && (rp = refssearch(rf))){
311 		return(rp - refinfo);
312       }
313       /* didn't match any existing reference, create new one */
314       if (numrefs >= MAXREFS)
315 	error("too many references, max of %d", MAXREFS);
316       hash = strhash(rf);
317       lg = strlen(rf) + 1;
318       refinfo[numrefs].ri_pos = rend;
319       refinfo[numrefs].ri_length = lg;
320       refinfo[numrefs].ri_hp = refshash[hash];
321       refinfo[numrefs].ri_n = numrefs;
322       refshash[hash] = &refinfo[numrefs];
323       wrref(&refinfo[numrefs], rf);
324       return(numrefs++);
325       }
326    else {
327       bibwarning("no reference matching %s\n", realhstr);
328       return(-1);
329       }
330 }
331 
332 struct refinfo *refssearch(rf)
333    char *rf;
334 {
335    char ref[REFSIZE];
336    reg	int i;
337    int	lg;
338    reg	struct refinfo *rp;
339    lg = strlen(rf) + 1;
340    for (rp = refshash[strhash(rf)]; rp; rp = rp->ri_hp){
341 	     if (rp->ri_length == lg){
342 		     rdref(rp, ref);
343 		     if (strcmp(ref, rf) == 0)
344 			return(rp);
345 	     }
346    }
347    return(0);
348 }
349 /* hunt - hunt for reference from either personal or system index */
350 /* the old versions would stop at the first index file where a citation
351  * matched.  This is NOT what is desired.  I have changed it so that it still
352  * returns the first citation found, but also reports the existence of
353  * duplicate entries in an INDEX file as well as across INDEX files.
354  * Also, we do NOT assume that the SYSINDEX has been Tib'd.  Therefore,
355  * if tib style expansion is in effect, the SYSINDEX is not searched.
356  * (Besides which, on Sun systems at least, the SYSINDEX files are
357  * created by refer, not bib, so we can't use them very effectively
358  * anyway.  Besides which again, everything in SYSINDEX is in our
359  * local files anyway.)
360  *                   - ads 8/88
361  */
362 char *hunt(huntstr)
363    char huntstr[];
364 {  char *found, *fhunt(), *r, *tp, *sp, fname[120];
365 
366    found = NULL;
367    if (personal) {
368       for (tp = fname, sp = pfile; ; sp++)
369          if (*sp == ',' || *sp == '\0') {
370             *tp = '\0';
371             if ((r = fhunt(fname, huntstr)) != NULL) {
372 		if (found != NULL) {
373 		    /* we need an option to suppress this message -ads 5/89 */
374 		    bibwarning("multiple INDEX files match citation %s\n",
375 							huntstr);
376 		    return (found);
377 		    }
378 		found = r;
379 		}
380             if (*sp == '\0')
381                break;
382             tp = fname;
383             }
384          else *tp++ = *sp;
385       if (found != NULL) return (found);
386       }
387    else if (findex) {
388       if ((r = fhunt(INDXFILE , huntstr)) != NULL)
389          return(r);
390       }
391    if (!TibOption) {
392       if ((r = fhunt(SYSINDEX , huntstr)) != NULL)
393 	 return(r);
394       }
395    return(NULL);
396 }
397 
398 /* fhunt - hunt from a specific file */
399    char *fhunt(file, huntstr)
400    char file[], huntstr[];
401 {  char *p, *r, *locate();
402 
403    r = locate(huntstr, file, max_klen, common);
404 
405    if (r == NULL)
406       return(NULL);  /* error */
407    if (*r == 0)
408       return(NULL);  /* no match */
409 
410    for (p = r; *p; p++)
411       if (*p == '\n')
412          if (*(p+1) == '\n') { /* end */
413             if (*(p+2) != 0)
414                bibwarning("multiple references match %s\n",huntstr);
415             *(p+1) = 0;
416             break;
417             }
418          else if (*(p+1) != '%' && *(p+1) != '.') /* unnecessary newline */
419             *p = ' ';
420    return(r);
421 }
422 struct cite{
423 	int	num;
424 	char	*info;
425 };
426 citesort(p1, p2)
427 	struct cite *p1, *p2;
428 {
429 	return(p1->num - p2->num);
430 }
431 
432 /* putrefs - gather contiguous references together, sort them if called
433    for, hyphenate if necessary, and dump them out */
434 int putrefs(ifd, ofd, footrefs, fn)
435 FILE *ifd, *ofd;
436 int  fn, footrefs[];
437 {
438 	struct cite cites[MAXATONCE];
439 	char	infoword[HUNTSIZE];    /* information line */
440 	reg	int i;
441 	reg	char *p;
442 	reg	int  ncites, n, j;         /* number of citations being dumped */
443 	char	c, *walloc();
444 	int neg;
445 	/*
446 	 * first gather contiguous references together,
447 	 * and order them if required
448 	 */
449 
450 	ncites = 0;
451 	do {
452 		neg = 1;
453 		n = 0;
454 		do{
455 			getch(c, ifd);
456 			if (isdigit(c))
457 				n = 10 * n + (c - '0');
458 			else if (c == '-')
459 				neg *= -1;
460 			else if (c == CITEMARK)
461 				break;
462 			else
463 				error("bad cite char 0%03o in pass two",c);
464 		} while(1);
465 		if (neg < 0) {     /* reference not found */
466 			cites[ncites].num = -1;
467 			cites[ncites].info = 0;
468 			ncites++;
469 		} else {
470 			/*
471 			 * Find reference n in the references
472 			 */
473 			int i;
474 			for (i = 0; i < numrefs; i++){
475 				if (refinfo[i].ri_n == n){
476 					cites[ncites].num = i;
477 					cites[ncites].info = 0;
478 					ncites++;
479 					break;
480 				}
481 			}
482 			if (i == numrefs)
483 				error("citation	%d not found in pass 2", n);
484 		}
485 		if (getch(c, ifd) != CITEEND) {
486 			for (p = infoword; c != CITEEND ; ) {
487 				*p++ = c;
488 				getch(c, ifd);
489 			}
490 			*p = 0;
491 			cites[ncites-1].info = walloc(infoword);
492 		}
493 		getch(c, ifd);
494 	} while (c == CITEMARK);
495 	ungetc(c, ifd);
496 	if (ordcite)
497 		qsort(cites, ncites, sizeof(struct cite), citesort);
498 
499 	/* now dump out values */
500 	for (i = 0; i < ncites; i++) {
501 		if (cites[i].num >= 0) {
502 			if (changecite){
503 				char tempcite[128];
504 				char ref[REFSIZE];
505 				struct refinfo *p;
506 				/*
507 				 * rebuild the citation string,
508 				 * using the current template in effect
509 				 */
510 				p = &refinfo[cites[i].num];
511 				rdref(p, ref);
512 				bldcite(tempcite, cites[i].num, ref);
513 				strcat(tempcite, p->ri_disambig);
514 				if (doacite) fputs(tempcite, ofd);
515 			} else {
516 				if (doacite) fputs(refinfo[cites[i].num].ri_cite, ofd);
517 			}
518 			if (!doacite) fputs("\\&", ofd);
519 		}
520 		if (cites[i].info) {
521 			if (doacite) fputs(cites[i].info, ofd);
522 			if (!doacite) fputs("\\&", ofd);
523 			free(cites[i].info);
524 		}
525 		if (hyphen) {
526 			for (j = 1;
527 			     j + i <= ncites && cites[i+j].num == cites[i].num + j;
528 			     j++)/*VOID*/;
529 			if (j + i > ncites)
530 				j = ncites;
531 			else
532 				j = j + i - 1;
533 		} else {
534 			j = i;
535 		}
536 		if (j > i + 1) {
537 			fputs("\\*(]-", ofd);
538 			i = j - 1;
539 		} else if (i != ncites - 1) {
540 			fputs("\\*(],", ofd);
541 		}
542 		if (foot) {
543 			fn++;
544 			footrefs[fn] = cites[i].num;
545 		}
546 	}
547 	return(fn);
548 }
549 
550 /* pass2 - read pass 1 files entering citation */
551    pass2(ifd, ofd)
552    FILE *ifd, *ofd;
553 {
554    char c;
555    int  i, fn, footrefs[25], dumped;
556 
557    fn = -1;
558    dumped = foot;
559    while (getch(c, ifd) != EOF) {
560       while (c == '\n') {
561          putc(c, ofd);
562          if (foot && fn >= 0) {
563             for (i = 0; i <= fn; i++)
564                 dumpref(footrefs[i], ofd);
565             fn = -1;
566             }
567          if (testc(c, '.', ifd, ofd))
568             if (testc(c, '[', ifd, ofd))
569                if (testc(c, ']', ifd, ofd)) {
570                   while (echoc(c, ifd, ofd) != '\n')
571                      ;
572                   dumped = true;
573                   for (i = 0; i < numrefs; i++){
574                      dumpref(i, ofd);
575 		  }
576                   getch(c, ifd);
577                   }
578          }
579       if (c == FMTSTART)
580 	 changefmt(ifd);
581       else if (c == CITEMARK)
582          fn = putrefs(ifd, ofd, footrefs, fn);
583       else if (c != EOF)
584          putc(c, ofd);
585       }
586    if (dumped == false)
587       bibwarning("Warning: references never dumped\n","");
588 }
589 /*
590  *	change citation format
591  */
592 changefmt(ifd)
593 	FILE	*ifd;
594 {
595 	char	c;
596 	char	*to;
597 	to = ncitetemplate;
598 	while (getch(c, ifd) != FMTEND)
599 		*to++ = c;
600 	*to = 0;
601 	strcpy(citetemplate, ncitetemplate);
602 }
603