+/*
+ bib - bibliographic formatter
+ timothy a. budd, 1/82
+ lookup routines supplied by gary levin 2/82
+ reworked several new features added, 11/82.
+*/
+# include <stdio.h>
+# include <ctype.h>
+# include "bib.h"
+
+# define HUNTSIZE 512 /* maximum size of hunt string */
+# define MAXFIELD 250 /* maximum field length */
+# define MAXREFS 300 /* maximum number of references */
+# define MAXATONCE 35 /* maximum references at one location */
+
+# define getch(c,fd) (c = getc(fd))
+# define echoc(c,ifd,ofd) (getch(c,ifd) == EOF ? c : putc(c,ofd))
+# define testc(c,d,ifd,ofd) (getch(c, ifd) == d ? putc(c, ofd) : 0)
+
+/* global variables */
+ FILE *rfd; /* reference temporary file */
+ char reffile[] = TMPREFFILE ;/* temporary file (see bib.h) */
+ long int refspos[MAXREFS]; /* reference seek positions */
+ long int rend = 1; /* last position in rfd (first char unused)*/
+ int numrefs = -1; /* number of references generated so far */
+ FILE *tfd; /* output of pass 1 of file(s) */
+ char tmpfile[] = TMPTEXTFILE ; /* output of pass 1 */
+ char common[] = COMFILE ; /* common word file */
+ char *citestr[MAXREFS]; /* citation strings */
+ int findex = false; /* can we read the file INDEX ? */
+
+/* global variables in bibargs */
+ extern int foot, sort, personal;
+ extern int hyphen, ordcite;
+ extern char sortstr[], pfile[], citetemplate[];
+
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{ int rcomp();
+
+ /* the file INDEX in the current directory is the default index,
+ if it is present */
+
+ rfd = fopen( INDXFILE , "r");
+ if (rfd != NULL) {
+ findex = true;
+ fclose(rfd);
+ }
+
+ /* open temporaries, reffile will contain references collected in
+ pass 1, and tmpfile will contain text.
+ */
+ mktemp(reffile);
+ rfd = fopen(reffile,"w+");
+ if (rfd == NULL)
+ error("can't open temporary reference file");
+ mktemp(tmpfile);
+ tfd = fopen(tmpfile,"w");
+ if (tfd == NULL)
+ error("can't open temporary output file");
+
+ /*
+ pass1 - read files, looking for citations
+ arguments are read by doargs (bibargs.c)
+ */
+
+ if (doargs(argc, argv, DEFSTYLE ) == 0)
+ rdtext(stdin);
+
+ /*
+ sort references, make citations, add disambiguating characters
+ */
+
+ if (sort)
+ qsort(refspos, numrefs+1, sizeof(long), rcomp);
+ makecites(citestr);
+ disambiguate();
+
+ /*
+ reopen temporaries
+ */
+
+ fclose(tfd);
+ tfd = fopen(tmpfile,"r");
+ if (tfd == NULL)
+ error("can't open temporary output file for reading");
+
+ /*
+ pass 2 - reread files, replacing references
+ */
+
+ pass2(tfd, stdout);
+
+ /*
+ clean up
+ */
+
+ fclose(tfd);
+ fclose(rfd);
+ unlink(tmpfile);
+ unlink(reffile);
+ exit(0);
+}
+
+/* rdtext - read and process a text file, looking for [. commands */
+ rdtext(fd)
+ FILE *fd;
+{ char lastc, c, d;
+
+ lastc = 0;
+ while (getch(c, fd) != EOF)
+ if (c == '[' || c == '{')
+ if (getch(d, fd) == '.') { /* found a reference */
+ if (c == '{') { if (lastc) putc(lastc, tfd);}
+ else
+ if (lastc == ' ') fputs("\\*([<", tfd);
+ else if (lastc == '.') fputs("\\*([.", tfd);
+ else if (lastc == ',') fputs("\\*([,", tfd);
+ else if (lastc) putc(lastc, tfd);
+ rdcite(fd, c);
+ if (c == '[')
+ if (lastc == ' ') fputs("\\*(>]", tfd);
+ else if (lastc == '.') fputs("\\*(.]", tfd);
+ else if (lastc == ',') fputs("\\*(,]", tfd);
+ lastc = 0;
+ }
+ else {
+ if (lastc) putc(lastc, tfd);
+ ungetc(d, fd);
+ lastc = c;
+ }
+ else {
+ if (lastc) putc(lastc, tfd);
+ lastc = c;
+ }
+ if (lastc) putc(lastc, tfd);
+}
+
+/* rdcite - read citation information inside a [. command */
+ rdcite(fd, ch)
+ FILE *fd;
+ char ch;
+{ long int n, getref();
+ char huntstr[HUNTSIZE], c, info[HUNTSIZE];
+
+ if (ch == '[')
+ fputs("\\*([[", tfd);
+ else
+ fputs("\\*([{", tfd);
+ huntstr[0] = info[0] = 0;
+ while (getch(c, fd) != EOF)
+ switch (c) {
+ case ',':
+ n = getref(huntstr);
+ if (n > 0)
+ fprintf(tfd, "%c%ld%c%s%c", CITEMARK, n, CITEMARK, info, CITEEND);
+ else
+ fprintf(tfd, "%c0%c%s%s%c", CITEMARK, CITEMARK,
+ huntstr, info, CITEEND);
+ huntstr[0] = info[0] = 0;
+ break;
+
+ case '.':
+ while (getch(c, fd) == '.') ;
+ if (c == ']') {
+ n = getref(huntstr);
+ if (n > 0)
+ fprintf(tfd, "%c%ld%c%s%c\\*(]]", CITEMARK, n,
+ CITEMARK, info, CITEEND);
+ else
+ fprintf(tfd, "%c0%c%s%s%c\\*(]]", CITEMARK, CITEMARK,
+ huntstr, info, CITEEND);
+ return;
+ }
+ else if (c == '}') {
+ n = getref(huntstr);
+ if (n > 0)
+ fprintf(tfd, "%c%ld%c%s%c\\*(}]", CITEMARK, n,
+ CITEMARK, info, CITEEND);
+ else
+ fprintf(tfd, "%c0%c%s%s%c\\*(}]", CITEMARK, CITEMARK,
+ huntstr, info, CITEEND);
+ return;
+ }
+ else
+ addc(huntstr, c);
+ break;
+
+ case '{':
+ while (getch(c, fd) != '}')
+ if (c == EOF) {
+ fprintf(stderr, "Error: ill formed reference\n");
+ exit(1);
+ }
+ else
+ addc(info, c);
+ break;
+
+ case '\n':
+ case '\t':
+ c = ' '; /* fall through */
+
+ default:
+ addc(huntstr,c);
+ }
+ error("end of file reading citation");
+}
+
+/* addc - add a character to hunt string */
+ addc(huntstr, c)
+ char huntstr[HUNTSIZE], c;
+{ int i;
+
+ i = strlen(huntstr);
+ if (i > HUNTSIZE)
+ error("citation too long");
+ huntstr[i] = c;
+ huntstr[i+1] = 0;
+}
+
+/* getref - if an item was already referenced, return its pointer in
+ the reference file, otherwise create a new entry */
+ long int getref(huntstr)
+ char huntstr[HUNTSIZE];
+{ char rf[REFSIZE], ref[REFSIZE], *r, *hunt();
+ int i, match(), getwrd();
+
+ r = hunt(huntstr);
+ if (r != NULL) {
+ /* exapand defined string */
+ strcpy(rf, r);
+ free(r);
+ expand(rf);
+
+ /* see if reference has already been cited */
+
+ if (foot == false)
+ for (i = 0; i <= numrefs; i++) {
+ rdref(refspos[i], ref);
+ if (strcmp(ref, rf) == 0)
+ return(refspos[i]);
+ }
+
+ /* didn't match any existing reference, create new one */
+
+ numrefs++;
+ refspos[numrefs] = rend;
+ fseek(rfd, rend, 0);
+ i = strlen(rf) + 1;
+ fwrite(rf, 1, i, rfd);
+ rend = rend + i;
+ return(refspos[numrefs]);
+ }
+ else {
+ fprintf(stderr,"no reference matching %s\n", huntstr);
+ return( (long) -1 );
+ }
+}
+
+/* rdref - read text for an already cited reference */
+ rdref(i, ref)
+ long int i;
+ char ref[REFSIZE];
+{
+ ref[0] = 0;
+ fseek(rfd, i, 0);
+ fread(ref, 1, REFSIZE, rfd);
+}
+
+/* hunt - hunt for reference from either personal or system index */
+ char *hunt(huntstr)
+ char huntstr[];
+{ char *fhunt(), *r, *p, *q, fname[120];
+
+ if (personal) {
+ for (p = fname, q = pfile; ; q++)
+ if (*q == ',' || *q == 0) {
+ *p = 0;
+ if ((r = fhunt(fname, huntstr)) != NULL)
+ return(r);
+ else if (*q == 0)
+ break;
+ p = fname;
+ }
+ else *p++ = *q;
+ }
+ else if (findex) {
+ if ((r = fhunt( INDXFILE , huntstr)) != NULL)
+ return(r);
+ }
+ if ((r = fhunt(SYSINDEX , huntstr)) != NULL)
+ return(r);
+ return(NULL);
+}
+
+/* fhunt - hunt from a specific file */
+ char *fhunt(file, huntstr)
+ char file[], huntstr[];
+{ char *p, *r, *locate();
+
+ r = locate(huntstr, file, 6, common);
+
+ if (r == NULL)
+ return(NULL); /* error */
+ if (*r == 0)
+ return(NULL); /* no match */
+
+ for (p = r; *p; p++)
+ if (*p == '\n')
+ if (*(p+1) == '\n') { /* end */
+ if (*(p+2) != 0)
+ fprintf(stderr,"multiple references match %s\n",huntstr);
+ *(p+1) = 0;
+ break;
+ }
+ else if (*(p+1) != '%' && *(p+1) != '.') /* unnecessary newline */
+ *p = ' ';
+ return(r);
+}
+
+/* rcomp - reference comparison routine for qsort utility */
+ int rcomp(ap, bp)
+ long int *ap, *bp;
+{ char ref1[REFSIZE], ref2[REFSIZE], field1[MAXFIELD], field2[MAXFIELD];
+ char *p, *q, *getfield();
+ int neg, res;
+
+ rdref(*ap, ref1);
+ rdref(*bp, ref2);
+ for (p = sortstr; *p; p = q) {
+ if (*p == '-') {
+ p++;
+ neg = true;
+ }
+ else
+ neg = false;
+ q = getfield(p, field1, ref1);
+ if (q == 0)
+ res = 1;
+ else if (getfield(p, field2, ref2) == 0)
+ res = -1;
+ else {
+ if (*p == 'A') {
+ if (isupper(field1[0]))
+ field1[0] -= 'A' - 'a';
+ if (isupper(field2[0]))
+ field2[0] -= 'A' - 'a';
+ }
+ res = strcmp(field1, field2);
+ }
+ if (neg)
+ res = - res;
+ if (res != 0)
+ break;
+ }
+ if (res == 0)
+ if (ap < bp)
+ res = -1;
+ else
+ res = 1;
+ return(res);
+}
+
+/* makecites - make citation strings */
+ makecites(citestr)
+ char *citestr[];
+{ char ref[REFSIZE], tempcite[100], *malloc();
+ int i;
+
+ for (i = 0; i <= numrefs; i++) {
+ rdref(refspos[i], ref);
+ bldcite(tempcite, i, ref);
+ citestr[i] = malloc(2 + strlen(tempcite)); /* leave room for disambig */
+ if (citestr[i] == NULL)
+ error("out of storage");
+ strcpy(citestr[i], tempcite);
+ }
+}
+
+/* bldcite - build a single citation string */
+ bldcite(cp, i, ref)
+ char *cp, ref[];
+ int i;
+{ char *p, *q, c, *fp, *np, field[REFSIZE], temp[100], *getfield();
+ int j;
+
+ getfield("F", field, ref);
+ if (field[0] != 0)
+ for (p = field; *p; p++)
+ *cp++ = *p;
+ else {
+ p = citetemplate;
+ field[0] = 0;
+ while (c = *p++)
+ if (isalpha(c)) {
+ q = getfield(p-1, field, ref);
+ if (q != 0) {
+ p = q;
+ for (fp = field; *fp; )
+ *cp++ = *fp++;
+ }
+ }
+ else if (c == '1') {
+ sprintf(field,"%d",1 + i);
+ for (fp = field; *fp; )
+ *cp++ = *fp++;
+ }
+ else if (c == '2') {
+ if (getname(1, field, temp, ref)) {
+ np = cp;
+ fp = field;
+ for (j = 1; j <= 3; j++)
+ if (*fp != 0)
+ *cp++ = *fp++;
+ if (getname(2, field, temp, ref))
+ np[2] = field[0];
+ if (getname(3, field, temp, ref)) {
+ np[1] = np[2];
+ np[2] = field[0];
+ }
+ }
+ }
+ else if (c == '{') {
+ while (*p ^= '}')
+ if (*p == 0)
+ error("unexpected end of citation template");
+ else
+ *cp++ = *p++;
+ p++;
+ }
+ else if (c == '<') {
+ while (*p ^= '>')
+ if (*p == 0)
+ error("unexpected end of citation template");
+ else
+ *cp++ = *p++;
+ p++;
+ }
+ else if (c != '@')
+ *cp++ = c;
+ }
+ *cp++ = 0;
+}
+
+/* getfield - get a single field from reference */
+ char *getfield(ptr, field, ref)
+ char *ptr, field[], ref[];
+{ char *p, *q, temp[100];
+ int n, len, i, getname();
+
+ field[0] = 0;
+ if (*ptr == 'A')
+ getname(1, field, temp, ref);
+ else
+ for (p = ref; *p; p++)
+ if (*p == '%' && *(p+1) == *ptr) {
+ for (p = p + 2; *p == ' '; p++)
+ ;
+ for (q = field; *p != '\n'; )
+ *q++ = *p++;
+ *q = 0;
+ break;
+ }
+ n = 0;
+ len = strlen(field);
+ if (*++ptr == '-') {
+ for (ptr++; isdigit(*ptr); ptr++)
+ n = 10 * n + (*ptr - '0');
+ if (n > len)
+ n = 0;
+ else
+ n = len - n;
+ for (i = 0; field[i] = field[i+n]; i++)
+ ;
+ }
+ else if (isdigit(*ptr)) {
+ for (; isdigit(*ptr); ptr++)
+ n = 10 * n + (*ptr - '0');
+ if (n > len)
+ n = len;
+ field[n] = 0;
+ }
+
+ if (*ptr == 'u') {
+ ptr++;
+ for (p = field; *p; p++)
+ if (islower(*p))
+ *p = (*p - 'a') + 'A';
+ }
+ else if (*ptr == 'l') {
+ ptr++;
+ for (p = field; *p; p++)
+ if (isupper(*p))
+ *p = (*p - 'A') + 'a';
+ }
+ return(ptr);
+}
+
+/* getname - get the nth name field from reference, breaking into
+ first and last names */
+ int getname(n, last, first, ref)
+ int n;
+ char last[], first[], ref[];
+{ char *p;
+
+ for (p = ref; *p; p++)
+ if (*p == '%' & *(p+1) == 'A') {
+ n--;
+ if (n == 0) {
+ for (p = p + 2; *p == ' '; p++) ;
+ breakname(p, first, last) ;
+ return(true);
+ }
+ }
+ return(false);
+}
+
+/* disambiguate - compare adjacent citation strings, and if equal, add
+ single character disambiguators */
+ disambiguate()
+{ int i, j;
+ char adstr[2];
+
+ for (i = 0; i < numrefs; i = j) {
+ j = i + 1;
+ if (strcmp(citestr[i], citestr[j])==0) {
+ adstr[0] = 'a'; adstr[1] = 0;
+ for (j = i+1; strcmp(citestr[i],citestr[j]) == 0; j++) {
+ adstr[0] = 'a' + (j-i);
+ strcat(citestr[j], adstr);
+ if (j == numrefs)
+ break;
+ }
+ adstr[0] = 'a';
+ strcat(citestr[i], adstr);
+ }
+ }
+}
+
+/* putrefs - gather contiguous references together, sort them if called
+ for, hyphenate if necessary, and dump them out */
+int putrefs(ifd, ofd, footrefs, fn)
+FILE *ifd, *ofd;
+int fn, footrefs[];
+{ int citenums[MAXATONCE]; /* reference numbers */
+ char *citeinfo[MAXATONCE]; /* reference information */
+ char infoword[HUNTSIZE]; /* information line */
+ int rtop, n, i, j; /* number of citations being dumped */
+ char c, *p, *walloc();
+
+/* first gather contiguous references together, and order them if
+ required */
+
+ rtop = -1;
+ do {
+ n = 0;
+ while (isdigit(getch(c, ifd)))
+ n = 10 * n + (c - '0');
+ if (c ^= CITEMARK)
+ error("inconsistant citation found in pass two");
+ if (n == 0) { /* reference not found */
+ rtop++;
+ j = rtop;
+ citenums[j] = -1;
+ citeinfo[j] = 0;
+ }
+ else {
+ for (i = 0; i <= numrefs; i++)
+ if (refspos[i] == n) { /* its the ith item in reference list */
+ rtop++;
+ j = rtop;
+ if (ordcite)
+ for ( ; j > 0 && citenums[j-1] > i; j--) {
+ citenums[j] = citenums[j-1];
+ citeinfo[j] = citeinfo[j-1];
+ }
+ citenums[j] = i;
+ citeinfo[j] = 0;
+ break;
+ }
+ if (i > numrefs)
+ error("citation not found in pass two");
+ }
+ if (getch(c, ifd) != CITEEND) {
+ for (p = infoword; c != CITEEND ; ) {
+ *p++ = c;
+ getch(c, ifd);
+ }
+ *p = 0;
+ citeinfo[j] = walloc(infoword);
+ }
+ getch(c, ifd);
+ } while (c == CITEMARK);
+ ungetc(c, ifd);
+
+ /* now dump out values */
+ for (i = 0; i <= rtop; i++) {
+ if (citenums[i] >= 0)
+ fputs(citestr[citenums[i]], ofd);
+ if (citeinfo[i]) {
+ fputs(citeinfo[i], ofd);
+ free(citeinfo[i]);
+ }
+ if (hyphen) {
+ for (j = 1; j + i <= rtop && citenums[i+j] == citenums[i] + j; j++);
+ if (j + i > rtop) j = rtop;
+ else j = j + i - 1;
+ }
+ else
+ j = i;
+ if (j > i + 1) {
+ fputs("\\*(]-", ofd);
+ i = j - 1;
+ }
+ else if (i != rtop)
+ fputs("\\*(],", ofd);
+ if (foot) {
+ fn++;
+ footrefs[fn] = citenums[i];
+ }
+ }
+ return(fn);
+}
+
+/* pass2 - read pass 1 files entering citation */
+ pass2(ifd, ofd)
+ FILE *ifd, *ofd;
+{
+ char c;
+ int i, fn, footrefs[25], dumped;
+
+ fn = -1;
+ dumped = foot;
+ while (getch(c, ifd) != EOF) {
+ while (c == '\n') {
+ putc(c, ofd);
+ if (foot && fn >= 0) {
+ for (i = 0; i <= fn; i++)
+ dumpref(footrefs[i], ofd);
+ fn = -1;
+ }
+ if (testc(c, '.', ifd, ofd))
+ if (testc(c, '[', ifd, ofd))
+ if (testc(c, ']', ifd, ofd)) {
+ while (echoc(c, ifd, ofd) != '\n')
+ ;
+ dumped = true;
+ for (i = 0; i <= numrefs; i++)
+ dumpref(i, ofd);
+ getch(c, ifd);
+ }
+ }
+ if (c == CITEMARK)
+ fn = putrefs(ifd, ofd, footrefs, fn);
+ else if (c != EOF)
+ putc(c, ofd);
+ }
+ if (dumped == false)
+ fprintf(stderr,"Warning: references never dumped\n");
+}
+
+
+/* dumpref - dump reference number i */
+ dumpref(i, ofd)
+ int i;
+ FILE *ofd;
+{ char ref[REFSIZE], *p, line[REFSIZE];
+ int numauths, maxauths, numeds, maxeds;
+
+ rdref(refspos[i], ref);
+ maxauths = maxeds = 0;
+ numauths = numeds = 0;
+ for (p = ref; *p; p++)
+ if (*p == '%')
+ if (*(p+1) == 'A') maxauths++;
+ else if (*(p+1) == 'E') maxeds++;
+ fprintf(ofd, ".[-\n");
+ fprintf(ofd, ".ds [F %s\n",citestr[i]);
+ fseek(rfd, (long) refspos[i], 0);
+ while (fgets(line, REFSIZE, rfd) != NULL) {
+ if (line[0] == 0) break;
+ else if (line[0] == '.') fprintf(ofd,"%s",line);
+ else {
+ if (line[0] == '%') {
+ for (p = &line[2]; *p == ' '; p++);
+ if (line[1] == 'A') numauths++;
+ else if (line[1] == 'E') numeds++;
+
+ doline(line[1], p, numauths, maxauths, numeds, maxeds, ofd);
+ }
+ }
+ }
+ fprintf(ofd,".][\n");
+}