add sccs keywords per BSD conventions. Fix format strings so
[unix-history] / usr / src / contrib / bib / src / bib.c
#ifndef lint
static char sccsid[] = "@(#)bib.c 1.4 %G%";
#endif not lint
/*
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[];
char *bibfname; /* file name currently reading */
char *biblineno; /* line number in that file */
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){
bibfname = "<stdin>";
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;
biblineno = 0;
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
switch (lastc) {
case ' ': fputs("\\*([<", tfd);
break;
case '.': fputs("\\*([.", tfd);
break;
case ',': fputs("\\*([,", tfd);
break;
case '?': fputs("\\*([?", tfd);
break;
case ':': fputs("\\*([:", tfd);
break;
case ';': fputs("\\*([;", tfd);
break;
case '!': fputs("\\*([!", tfd);
break;
case '"': fputs("\\*([\"", tfd);
break;
case '\'': fputs("\\*(['", tfd);
break;
default : putc(lastc, tfd);
break;
}
rdcite(fd, c);
if (c == '[')
switch (lastc) {
case ' ': fputs("\\*(<]", tfd);
break;
case '.': fputs("\\*(.]", tfd);
break;
case ',': fputs("\\*(,]", tfd);
break;
case '?': fputs("\\*(?]", tfd);
break;
case ':': fputs("\\*(:]", tfd);
break;
case ';': fputs("\\*(;]", tfd);
break;
case '!': fputs("\\*(!]", tfd);
break;
case '"': fputs("\\*(\"]", tfd);
break;
case '\'': fputs("\\*(']", tfd);
break;
}
lastc = 0;
}
else {
if (lastc) putc(lastc, tfd);
ungetc(d, fd);
lastc = c;
}
else {
if (lastc) putc(lastc, tfd);
lastc = c;
if (c == '\n')
biblineno++;
}
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 {
bibwarning("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)
bibwarning("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;
int fields_found;
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);
fields_found = true;
if (q == 0) {
res = 1;
fields_found = false;
} else if (strcmp (field1, "") == 0) { /* field not found */
if (*p == 'A') {
getfield("F", field1, ref1);
if (strcmp (field1, "") == 0) {
getfield("I", field1, ref1);
if (strcmp (field1, "") == 0) {
res = 1;
fields_found = false;
}
}
} else {
res = 1;
fields_found = false;
}
}
if (getfield(p, field2, ref2) == 0) {
res = -1;
fields_found = false;
} else if (strcmp (field2, "") == 0) { /* field not found */
if (*p == 'A') {
getfield("F", field2, ref2);
if (strcmp (field2, "") == 0) {
getfield("I", field2, ref2);
if (strcmp (field2, "") == 0) {
res = -1;
fields_found = false;
}
}
} else {
res = -1;
fields_found = false;
}
}
if (fields_found) {
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' || *(p+1) == 'E')) {
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)
bibwarning("Warning: references never dumped\n", (char *)0);
}
/* 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");
}
/*
* print out a warning message
*/
bibwarning(msg, arg)
char *msg;
char *arg;
{
fprintf(stderr, "`%s', line %d: ", bibfname, biblineno);
fprintf(stderr, msg, arg);
}