[net.sources] new bib - source code

budd (12/08/82)

echo x - alpha.seek.c
cat >alpha.seek.c <<'!Funky!Stuff!'
#

# include "stdio.h"
# include "ctype.h"
# include "streams.h"
# define  nexttry           ((high+low)/2)

/*  alpha_seek(stream, word, s_size, fold)
        seeks the first line in stream that is at least word.
    assumes that stream is a sorted file of lines.  (last char must be \n)
    if fold, assumes that word is lowercase and folds stream to lowercase.
    s_size = size of stream
    returns 1 if word = line, 0 o.w.
*/
int alpha_seek(stream, word, s_size, fold)
FILE *stream;
char *word;
long int s_size;
int  fold;
{   long int high, low, mid;    /*  point to beginning of a line in stream  */
    int      ans;               /*  line(low) < word <= line(high)          */
    char     line[maxstr];


    /*  initialize low (return if first line >= word)       */
        low= 0L;
        pos(low); getline(stream, line);
        if (fold) foldline(line);
        ans= strcmp(line,word);

        if ( ans >= 0)
        {   pos(low);   return(ans==0); }

    /*  initialize high to "line" after last line           */
        high= s_size;

    mid= nextline(stream, nexttry );
    while (mid < high )
    {   getline(stream,line);
        if (fold) foldline(line);
        if (strcmp(line,word) < 0)    low=  mid;
        else                          high= mid;
        mid= nextline(stream, nexttry );
    }

    /* linear search from low to high   */
        low= nextline(stream,low);
        for(;;)
        {   if (low>=high)      break;

            getline(stream,line);
            if (fold) foldline(line);
            ans=strcmp(line,word);

            if (ans>=0)         break;
            low= ftell(stream);
        }

    pos(low);
    if (low=high)   return(0);
    else            return(ans==0);
}


/*  foldline(p):    change all uppercase to lowercase in string p
*/
foldline(p)
char *p;
{   for (; *p!=NULL;  p++)
    {   if (isupper(*p))    *p = tolower(*p);
    }
}
!Funky!Stuff!
echo x - bib.c
cat >bib.c <<'!Funky!Stuff!'
/*
   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");
}
!Funky!Stuff!
echo x - bib.h
cat >bib.h <<'!Funky!Stuff!'
/*   various arguments for bib and listrefs processors */

/* constants */

# define true  1
# define false 0
# define err  -1
# define REFSIZE 1024                /* maximum size of reference string    */

/* reference citation marker genrated in pass 1 */

# define CITEMARK (char) 02
# define CITEEND  (char) 03

/* file names */

        /* output of invert, input file for references */
# define INDXFILE "INDEX"
        /* pass1 reference collection file */
# define TMPREFFILE  "/usr/tmp/bibrXXXXXX"
        /* pass2 text collection file */
# define TMPTEXTFILE "/usr/tmp/bibpXXXXXX"
        /* temp file used in invert */
# define INVTEMPFILE "/usr/tmp/invertXXXXXX"
        /* common words */
# define COMFILE "/usr/lib/bmac/common"
        /* default system dictionary */
# define SYSINDEX "/usr/dict/papers/INDEX"
        /* where macro libraries live */
# define BMACLIB "/usr/lib/bmac"
        /* default style of references */
# define DEFSTYLE "/usr/lib/bmac/bib.stdsn"

/* size limits */

	/* maximum number of characters in common file */
# define MAXCOMM 300

char *malloc();
!Funky!Stuff!
echo x - bibargs.c
cat >bibargs.c <<'!Funky!Stuff!'
/*
        read argument strings for bib and listrefs
        do name formatting, printing lines, other actions common to both
                                                        */
# include <stdio.h>
# include <ctype.h>
# include "bib.h"
# define LINELENGTH 1024
# define MAXDEFS     500             /* maximum number of defined words */

/* global variables */
   int  abbrev       = false;   /* automatically abbreviate names            */
   int  capsmcap     = false;   /* print names in caps small caps (CACM form)*/
   int  numrev       = 0;       /* number of authors names to reverse        */
   int  edabbrev     = false;   /* abbreviate editors names ?                */
   int  edcapsmcap   = false;   /* print editors in cap small caps           */
   int  ednumrev     = 0;       /* number of editors to reverse              */
   int  sort         = false;   /* sort references ? (default no)            */
   int  foot         = false;   /* footnoted references ? (default endnotes) */
   int  hyphen       = false;   /* hypenate contiguous references            */
   int  ordcite      = true;    /* order multiple citations                  */
   char sortstr[80]  = "1";     /* sorting template                          */
   char trailstr[80] = "";      /* trailing characters to output             */
   char pfile[120];             /* private file name                         */
   int  personal = false;       /* personal file given ? (default no)        */
   char citetemplate[80] = "1"; /* citation template                         */
   char *words[MAXDEFS];        /* defined words                             */
   char *defs[MAXDEFS];         /* defined word definitions                  */
   int  wordtop = -1;           /* top of defined words array                */

/* where output goes */
   extern FILE *tfd;

/* doargs - read command argument line for both bib and listrefs
            set switch values
            call rdtext on file arguments, after dumping
            default style file if no alternative style is given
*/
   int doargs(argc, argv, defstyle)
   int argc;
   char **argv, defstyle[];
{  int numfiles, i, style;
   char *p, *q, *walloc();
   FILE *fd;

   numfiles = 0;
   style = true;
   words[0] = walloc("BMACLIB");
   defs[0]  = walloc(BMACLIB);
   wordtop++;
   fputs(".ds l] ",tfd);
   fputs(BMACLIB, tfd);
   fputs("\n", tfd);

   for (i = 1; i < argc; i++)
      if (argv[i][0] == '-')
         switch(argv[i][1]) {

            case 'a':  abbrev = true;
                       break;

            case 'c':  if (argv[i][2] == 0)
                          error("citation string expected");
                       else
                          for (p = citetemplate,q = &argv[i][2]; *p++ = *q++; );
                       break;

            case 'e':  for (p = &argv[i][2]; *p; p++)
                          if (*p == 'a')
                             edabbrev = true;
                           else if (*p == 'x')
                             edcapsmcap = true;
                           else if (*p == 'r') {
                             if (*(p+1))
                                ednumrev = atoi(p+1);
                              else
                                ednumrev = 1000;
                              break;
                              }
                       break;

            case 'f':  foot = true;
                       hyphen = false;
                       break;

            case 'h':  hyphen = ordcite = true;
                       break;

            case 'n':  for (p = &argv[i][2]; *p; p++)
                          if (*p == 'a')
                             abbrev = false;
                          else if (*p == 'f')
                             foot = false;
                          else if (*p == 'h')
                             hyphen = false;
                          else if (*p == 'o')
                             ordcite = false;
                          else if (*p == 'r')
                             numrev = 0;
                          else if (*p == 's')
                             sort = false;
                          else if (*p == 'x')
                             capsmcap = false;
                       break;

            case 'o':  ordcite = true;
                       break;

            case 'p':  if (argv[i][2])
                          p = &argv[i][2];
                       else {  /* take next arg */
                          i++;
                          p = argv[i];
                          }
                       strcpy(pfile, p);
                       personal = true;
                       break;

            case 'r':  if (argv[i][2] == 0)
                          numrev = 1000;
                       else
                          numrev = atoi(&argv[i][2]);
                       break;

            case 's':  sort = true;
                       if (argv[i][2])
                          for (p = sortstr,q = &argv[i][2]; *p++ = *q++; );
                       break;

            case 'i':
            case 't':  if (argv[i][1] == 't')
                          style = false;
                       if (argv[i][2])
                          p = &argv[i][2];
                       else { /* take next arg */
                          i++;
                          p = argv[i];
                          }
                       incfile(p);
                       break;

            case 'x':  capsmcap = true;
                       break;

            case 0:    if (style) {  /* no style command given, take default */
                          style = false;
                          incfile( defstyle );
                          }
                       rdtext(stdin);
                       numfiles++;
                       break;

            default:   fputs(argv[i], stderr);
                       error(": invalid switch");
            }
      else { /* file name */
         numfiles++;
         if (style) {
            style = false;
            incfile( defstyle );
            }
         fd = fopen(argv[i], "r");
         if (fd == NULL) {
            fputs(argv[i], stderr);
            error(": can't open");
            }
         else {
            rdtext(fd);
            fclose(fd);
            }
         }

   if (style) incfile( defstyle );
   return(numfiles);

}

/* incfile - read in an included file  */
incfile(np)
   char *np;
{  char name[120];
   FILE *fd;

   fd = fopen(np, "r");
   if (fd == NULL && *np != '/') {
      strcpy(name, "bib.");
      strcat(name, np);
      fd = fopen(name, "r");
      }
   if (fd == NULL && *np != '/') {
      strcpy(name,BMACLIB);
      strcat(name, "/bib.");
      strcat(name, np);
      fd = fopen(name, "r");
      }
   if (fd == NULL) {
      fprintf(stderr,"%s", np);
      error(": can't open");
      }
   setswitch(fd);
   fclose(fd);
}

/* error - report unrecoverable error message */
  error(str)
  char str[];
{
  fputs(str, stderr);
  putc('\n', stderr);
  exit(1);
}

/* tfgets - fgets which trims off newline */
   char *tfgets(line, n, ptr)
   char line[];
   int  n;
   FILE *ptr;
{  char *p;

   p = fgets(line, n, ptr);
   if (p == NULL)
      return(NULL);
   else
      for (p = line; *p; p++)
         if (*p == '\n')
            *p = 0;
   return(line);
}

/* getwrd - place next word from in[i] into out */
int getwrd(in, i, out)
   char in[], out[];
   int i;
{  int j;

   j = 0;
   while (in[i] == ' ' || in[i] == '\n' || in[i] == '\t')
      i++;
   if (in[i])
      while (in[i] && in[i] != ' ' && in[i] != '\t' && in[i] != '\n')
         out[j++] = in[i++];
   else
      i = 0;    /* signals end of in[i..]   */
   out[j] = 0;
   return (i);
}

/* walloc - allocate enough space for a word */
char *walloc(word)
   char *word;
{  char *i, *malloc();
   i = malloc(1 + strlen(word));
   if (i == NULL)
      error("out of storage");
   strcpy(i, word);
   return(i);
}

/* setswitch - set document switch settings from format file */
   setswitch(fp)
   FILE *fp;
{  char *p, line[LINELENGTH], dline[LINELENGTH], word[80];
   int  i, j, getwrd();

   while (tfgets(line, LINELENGTH, fp) != NULL)
      switch(line[0]) {

         case '#': break;

         case 'A': abbrev = true;
                   break;

         case 'C': for (p = &line[1]; *p == ' '; p++) ;
                   strcpy(citetemplate, p);
                   break;

         case 'D': if ((i = getwrd(line, 1, word)) == 0)
                      error("word expected in definition");
                   for (j = 0; j <= wordtop; j++)
                      if (strcmp(word, words[j]) == 0)
                         break;
                   if (j > wordtop) {
                      if ((j = ++wordtop) > MAXDEFS)
                         error("too many defintions");
                      words[wordtop] = walloc(word);
                      }
                   for (p = &line[i]; *p == ' '; p++) ;
                   for (strcpy(dline, p); dline[strlen(dline)-1] == '\\'; ){
                       dline[strlen(dline)-1] = '\n';
                       if (tfgets(line, LINELENGTH, fp) == NULL) break;
                       strcat(dline, line);
                       }
                   defs[j] = walloc(dline);
                   break;

         case 'E': for (p = &line[1]; *p; p++)
                      if (*p == 'A')
                         edabbrev = true;
                      else if (*p == 'X')
                         edcapsmcap = true;
                      else if (*p == 'R') {
                         if (*(p+1))
                            ednumrev = atoi(p+1);
                         else
                            ednumrev = 1000;
                         break;
                         }
                   break;

         case 'F': foot = true;
                   hyphen = false;
                   break;

         case 'I': for (p = &line[1]; *p == ' '; p++);
                   expand(p);
                   incfile(p);
                   break;

         case 'H': hyphen = ordcite = true;
                   break;

         case 'O': ordcite = true;
                   break;

         case 'R': if (line[1] == 0)
                      numrev = 1000;
                   else
                      numrev = atoi(&line[1]);
                   break;

         case 'S': sort = true;
                   for (p = &line[1]; *p == ' '; p++) ;
                   strcpy(sortstr, p);
                   break;

         case 'T': for (p = &line[1]; *p == ' '; p++) ;
                   strcpy(trailstr, p);
                   break;

         case 'X': capsmcap = true;
                   break;

         default:  fprintf(tfd,"%s\n",line);
                   while (fgets(line, LINELENGTH, fp) != NULL)
                      fputs(line, tfd);
                   return;
         }
   return;
}

/* isword - see if character is legit word char */
int iswordc(c)
char c;
{
   if (isalnum(c) || c == '&' || c == '_')
      return(true);
   return(false);
}

/* expand - expand reference, replacing defined words */
   expand(line)
   char *line;
{  char line2[REFSIZE], word[LINELENGTH], *p, *q, *w;
   int  replaced, i;

   replaced  = true;
   while (replaced) {
      replaced = false;
      p = line;
      q = line2;
      while (*p) {
         if (isalnum(*p)) {
            for (w = word; *p && iswordc(*p); )
               *w++ = *p++;
            *w = 0;
            for (i = 0; i <= wordtop; i++)
               if (strcmp(word, words[i]) == 0) {
                  strcpy(word, defs[i]);
                  replaced = true;
                  break;
                  }
            for (w = word; *w; )
               *q++ = *w++;
            }
         else
            *q++ = *p++;
         }
      *q = 0;
      p = line;
      q = line2;
      while (*p++ = *q++);
      }
}

/* breakname - break a name into first and last name */
   breakname(line, first, last)
   char line[], first[], last[];
{  char *p, *q, *r, *t, *f;

   for (t = line; *t != '\n'; t++);
   for (t--; isspace(*t); t--);

   /* now strip off last name */
   for (q = t; isspace(*q) == 0 || ((*q == ' ') & (*(q-1) == '\\')); q--)
      if (q == line)
         break;
   f = q;
   if (q != line)
      q++;

   for (; isspace(*f); f--);

   /* first name is start to f, last name is q to t */

   for (r = first, p = line, f++; p != f; )
      *r++ = *p++;
   *r = 0;
   for (r = last, p = q, t++; q != t; )
      *r++ = *q++;
   *r = 0;
}

/* match - see if string1 is a substring of string2 (case independent)*/
   int match(str1, str2)
   char str1[], str2[];
{  int  i, j;
   char a, b;

   for (i = 0; str2[i]; i++) {
      for (j = 0; str1[j]; j++) {
         if (isupper(a = str2[i+j]))
            a = (a - 'A') + 'a';
         if (isupper(b = str1[j]))
            b = (b - 'A') + 'a';
         if (a != b)
            break;
         }
      if (str1[j] == 0)
         return(true);
      }
   return(false);
}

/* scopy - append a copy of one string to another */
   char *scopy(p, q)
   char *p, *q;
{
   while (*p++ = *q++)
      ;
   return(--p);
}

/* bldname - build a name field
             doing abbreviations, reversals, and caps/small caps
*/
   bldname(first, last, name, reverse)
   char *first, *last, name[];
   int reverse;
{
   char newfirst[120], newlast[120], *p, *q, *f, *l, *scopy();
   int  flag;

   if (abbrev) {
      p = first;
      q = newfirst;
      flag = false;
      while (*p) {
         while (*p == ' ')
            p++;
         if (*p == 0)
            break;
         if (isupper(*p)) {
            if (flag)
               q = scopy(q, "\\*(a]");
            flag = true;
            *q++ = *p;
            *q++ = '.';
            }
         if (*++p == '.')
            p++;
         else while (*p != 0 && ! isspace(*p))
            p++;
         }
      *q = 0;
      f = newfirst;
      }
   else
      f = first;

   if (capsmcap) {
      p = last;
      q = newlast;
      flag = 0;  /* 1 - printing cap, 2 - printing small */
      while (*p)
         if (islower(*p)) {
            if (flag != 2)
               q = scopy(q, "\\s-2");
            flag = 2;
            *q++ = (*p++ - 'a') + 'A';
            }
         else {
            if (flag == 2)
               q = scopy(q,"\\s+2");
            flag = 1;
            *q++ = *p++;
            }
      if (flag == 2)
         q = scopy(q, "\\s+2");
      *q = 0;
      l = newlast;
      }
   else
      l = last;

   if (reverse)
      sprintf(name, "%s, %s\n", l, f);
   else
      sprintf(name, "%s %s\n", f, l);
}

/* prtauth - print author or editor field */
   prtauth(c, line, num, max, ofd, abbrev, capsmcap, numrev)
   char c, *line;
   int  num, max, abbrev, capsmcap, numrev;
   FILE *ofd;
{  char first[LINELENGTH], last[LINELENGTH];

   if (num <= numrev || abbrev || capsmcap) {
      breakname(line, first, last);
      bldname(first, last, line, num <= numrev);
      }
   if (num == 1)
      fprintf(ofd,".ds [%c %s", c, line);
   else if (num < max)
      fprintf(ofd,".as [%c \\*(c]%s", c, line);
   else if (max == 2)
      fprintf(ofd,".as [%c \\*(n]%s", c, line);
   else
      fprintf(ofd,".as [%c \\*(m]%s", c, line);
   if (num == max && index(trailstr, c))
      fprintf(ofd,".ds ]%c %c\n", c, line[strlen(line)-2]);
}

/* doline - actually print out a line of reference information */
   doline(c, line, numauths, maxauths, numeds, maxeds, ofd)
   char c, *line;
   int numauths, maxauths, numeds, maxeds;
   FILE *ofd;
{

   switch(c) {
      case 'A':
          prtauth(c, line, numauths, maxauths, ofd, abbrev, capsmcap, numrev);
          break;

       case 'E':
          prtauth(c, line, numeds, maxeds, ofd, edabbrev, edcapsmcap, ednumrev);
          if (numeds == maxeds)
             fprintf(ofd,".nr [E %d\n", maxeds);
          break;

       case 'P':
          if (index(line, '-'))
             fprintf(ofd,".nr [P 1\n");
          else
             fprintf(ofd,".nr [P 0\n");
          fprintf(ofd,".ds [P %s",line);
          if (index(trailstr, 'P'))
             fprintf(ofd,".ds ]P %c\n",line[strlen(line)-2]);
          break;

       case 'F':
       case 'K': break;

       default:
          fprintf(ofd,".ds [%c %s", c, line);
          if (index(trailstr, c))
             fprintf(ofd,".ds ]%c %c\n", c, line[strlen(line)-2]);
          }
}

!Funky!Stuff!
echo x - invert.c
cat >invert.c <<'!Funky!Stuff!'
#
/*  input:  records of lines, separated by blank lines
    output: key:file1 start/length ... start/length:file2 start/length ...
*/

# include "stdio.h"
# include "streams.h"
# include "bib.h"
# define isnull(x)  (*(x) == NULL)
# define makelow(c) ('A'<=(c) && (c)<='Z' ? (c)-'A'+'a' : c)

int     max_kcnt = 100;     /*  max number of keys                      */
int     max_klen =   6;     /*  max length of keys                      */
char    *ignore =           /*  string of line starts to ignore         */
            "CNOPVX";
char    *common =           /*  name of file of common words            */
            COMFILE;
char    *INDEX=             /*  name of output file                     */
            INDXFILE;

char    *tmpfile =          /*  name of temporary file                  */
            INVTEMPFILE;

int	silent = 0;	    /*  0 => statistics printed			*/
			    /*  1 => no statisitics printed		*/

char *sort_it =
        "sort -u +0 -1 +1 -2 +2n -3 +3n %s -o %s";
char sortcmd[maxstr];

int     argc;
char    **argv;

main(argcount,arglist)
int argcount;
char **arglist;
{   char            *filename;
    FILE            *input, *output;
    long int        start,length;
    char            word[maxstr];
    int             kcnt;
    char            tag_line[maxstr];

    long int	    records = 0;  /*  number of records read           */
    long int	    keys    = 0;  /*  number of keys read (occurences) */
    long int	    distinct;     /*  number of distinct keys          */
    long int	    shorten();

    argc= argcount-1;
    argv= arglist+1;
    mktemp(tmpfile);
    output= fopen(tmpfile,"w");

    for ( flags() ; argc>0 ; argc--, argv++ ,flags() )
    {   /* open input file              */
            filename=   *argv;
            input=      fopen(filename,"r");
            if (input==NULL)
            {   fprintf(stderr, "invert: error in open of %s\n", filename);
                continue;
            }
            start=      0L;
            length=     0L;

        for(;;) /* each record  */
        {   /* find start of next record (exit if none)     */
                start= nextrecord(input,start+length);
                if (start==EOF)   break;
            records++;
	    kcnt= 0;
            length= recsize(input,start);
            sprintf(tag_line, " %s %D %D\n", filename, start, length);

            while (ftell(input) < start+length && kcnt < max_kcnt)
            {   getword(input,word,ignore);
                makekey(word,max_klen,common);
                if (!isnull(word))
                {   fputs(word,output); fputs(tag_line,output);
                    kcnt++; keys++;
                }
            }
        }
        fclose(input);
    }
    fclose(output);

    sprintf(sortcmd, sort_it, tmpfile, tmpfile);
    system(sortcmd);

    distinct = shorten(tmpfile,INDEX);
    if( silent == 0 )
	fprintf(stderr,
	    "%D documents   %D distinct keys  %D key occurrences\n",
	    records, distinct, keys);
}



/*  Flag    Meaning                             Default
    -ki     Keys per record                     100
    -li     max Length of keys                  6
    -%str   ignore lines that begin with %x     CNOPVX
            where x is in str
            str is a seq of chars
    -cfile  file contains Common words          /usr/src/local/bib/common
            do not use common words as keys
    -pfile  name of output file                 INDEX
    -s	    do not print statistics		statistics printed
*/

# define    operand     (strlen(*argv+2)==0 ? (argv++,argc--,*argv) : *argv+2)

flags()
{   for (; argc>0 && *argv[0]=='-';  argc--,argv++)
    {   switch ((*argv)[1])
        {   case 'k':   max_kcnt= atoi(operand);
                        break;
            case 'l':   max_klen= atoi(operand);
                        break;
            case 'c':   common=  operand;
                        break;
            case '%':   ignore=  *argv+2;
                        break;
            case 'p':   INDEX=  operand;
                        break;
	    case 's':	silent= 1;
			break;
            default:    fprintf(stderr, "unknown flag '%s'\n", *argv);
        }
    }
}


/*  shorten(inf,outf): file "inf" consists of lines of the form:
        key file start length
    sorted by key and file.  replace lines with the same key
    with one line of the form:
        key:file1 start/length ... start/length:file2 start/length ...
    rename as file "outf"
    returns number of lines in output
*/
long shorten(inf,outf)
char *inf, *outf;
{   FILE *in, *out;
    char line[maxstr];
    char key[maxstr],  newkey[maxstr],
         file[maxstr], newfile[maxstr];
    long int start, length;
    long int lines = 0;

    in=  fopen(inf, "r");
    out= fopen(outf, "w");
    if (in==NULL || out==NULL)
    {   fprintf(stderr, "invert: error in opening file for compression\n");
        return(0);
    }

    getline(in,line);
    sscanf(line,"%s%s%D%D", key, file, &start, &length);
    fprintf(out, "%s :%s %D/%D", key, file, start, length);
    for ( getline(in, line) ; !feof(in);  getline(in, line))
    {   sscanf(line,"%s%s%D%D", newkey, newfile, &start, &length);
        if (strcmp(key,newkey)!=0)
        {   strcpy(key, newkey);
            strcpy(file, newfile);
            fprintf(out, "\n%s :%s %D/%D",  key, file, start, length);
	    lines++;
        }
        else if (strcmp(file,newfile)!=0)
        {   strcpy(file,newfile);
            fprintf(out, ":%s %D/%D", file, start, length);
        }
        else
            fprintf(out, " %D/%D", start, length);
    }
    fprintf(out, "\n");
    lines++;

    fclose(in); fclose(out);
    unlink(inf);
    return (lines);
}
!Funky!Stuff!
echo x - listrefs.c
cat >listrefs.c <<'!Funky!Stuff!'
/*
        list all documents in ref index file
                                                        */
# include <stdio.h>
# include <ctype.h>
# include "bib.h"
# include "streams.h"
# define MAXLINE 250

FILE *tfd;
int  count = 1;
char refs[REFSIZE], *rp;

main(argc, argv)
   int argc;
   char **argv;
{
   tfd = stdout;
   doargs(argc, argv, "/usr/lib/bmac/bib.list");
   exit(0);
}

/* rdtext - process a file */
   rdtext(ifile)
   FILE *ifile;
{
   long int start, length;
   int  i, numauths, numeds;
   char *p, c;

   start = length = 0L;

   for (;;) {
      start = nextrecord(ifile, start + length);
      if (start == EOF) break;
      length = recsize(ifile, start);

      /* count number of authors */
      numauths = numeds = 0;
      p = refs;
      for (i = length; i > 0; i--)
         if ((*p++ = getc(ifile)) == '%') {
            i--;
            c = *p++ = getc(ifile);
            if (c == 'A')
               numauths++;
            else if (c == 'E')
               numeds++;
            }

      *p = 0;
      expand(refs);
      rp = refs;
      dumpref(stdout, numauths, numeds);

     }
}

/* get a line from reference file */
   char refgets(line)
   char line[];
{
   char c, *p;

   if (*rp == 0)
      return(0);
   for (p = line;;) {
      while ((c = *rp++) != '\n')
         if (c == 0)
            return(0);
         else
            *p++ = c;
      c = *rp;
      if (c == 0)
         break;
      if (c == '.' || c == '%' || c == '\n')
         break;
      *p++ = ' ';
      }
   *p++ = '\n';
   *p = 0;
   return(' ');
}

/* dump reference */
   dumpref(ofile, maxauths, maxeds)
   FILE *ofile;
   int maxauths, maxeds;
{
   char line[250], *p;
   int  numauths, numeds;

   fprintf(tfd, ".[-\n");
   fprintf(tfd, ".ds [F %d\n", count++);
   numauths = numeds = 0;
   while (refgets(line) != 0) {
      if (line[0] == '\n')
         break;
      else if (line[0] == '.')
         fprintf(ofile, "%s\n", line);
      else {
         if (line[0] == '%') {
            for (p = &line[2]; isspace(*p); p++);
            if (line[1] == 'A')
               numauths++;
            else if (line[1] == 'E')
               numeds++;
            doline(line[1], p, numauths, maxauths, numeds, maxeds, ofile);
            }
         }
      }
   fprintf(tfd, ".][\n");
}
!Funky!Stuff!
echo x - locate.c
cat >locate.c <<'!Funky!Stuff!'
#

# include   "stdio.h"
# include   "streams.h"
# include   "ctype.h"
# define    maxrefs      200

struct reftype{
    char reffile[20];
    long int start, length;
    };

char *malloc();
char *rindex();
char *stripkeys();
int   fetchref();

/*  locate(keys, name, max_klen, common):
        Returns a string containing all references pointed to by name
        that contain all keys in keys.  Common is name of common word file.
    Pointer returned comes from malloc.  Use free to return storage.
    NB A zero length string returned if nothing is found.
       A NULL pointer indicates an error accessing the file "name".
*/
char *locate(keys,name,max_klen,common)
char *keys, *name, *common;
int  max_klen;          /* max key length */
{   static  char oldname[maxstr] = "";  /* oldname is name of stream index */
    static  FILE *index = NULL;
    static  long int i_size;            /* size of index                   */
    static  char oldtext[maxstr];       /* oldtext is the path to stream   */
    static  FILE *text = NULL;		/*  text.  if it is a relative     */
    static  int  pathlen;		/*  path, it is relative to index  */
					/*  directory.                     */
					/* oldname[0..pathlen-1] is index  */
					/*  directory                      */
    int  len;
    char key[maxstr];                   /* refs[i] is a line of index for  */
    struct reftype  refs[maxrefs];      /* all keys up to key              */

    int  refcnt, copied, comp;          /* refcnt = # of refs               */
                                        /* copied = # of refs copied        */
                                        /* comp   = # of refs compared      */
    struct reftype ref;
    char   str[maxstr];
    int    more;

    long int ans;
    int i,j;
    unsigned total;
    char *allrefs, *next;               /* all refs (separated by null line)*/
    char *p;

    /*  open index */
        if  (strcmp(oldname,name)!=0)
        {   if (index) fclose(index);
            if (text) fclose(text);
            strcpy(oldname,name);
            strcpy(oldtext,"");
            /*  determine pathlen   */
                p= rindex(oldname, '/');
                if      (p!=NULL)           pathlen= p-oldname+1;
                else                        pathlen= 0;

            index= fopen(oldname,"r");
            if (index==NULL)
            {   fprintf(stderr, "locate: cannot open %s\n", oldname);
		strcpy(oldname, "");
                return(NULL);
            }
            else
            {   fseek(index,0L,2);     /*  seeks last newline      */
                i_size= ftell(index);
            }

        }

    /*  load references to first key  */
        keys= stripkeys(keys,key, max_klen, common);
	if (*key==NULL)
	{ fprintf(stderr,"locate: no keys for citation\n");
	  allrefs = malloc(1);
	  if (allrefs==NULL)
	  {  fprintf(stderr, 
	       "locate: insufficient space for references\n");
	     exit(1);
	  }
	  *allrefs= NULL;
	  return(allrefs);
	}
        len= strlen(key);
        strcat(key," ");
        alpha_seek(index, key, i_size, 0);
        key[len]= NULL;                     /*  strip blank off */

        refcnt= 0;
        fscanf(index,"%s ", str);
        if (strcmp(str,key) == 0)
        {   str[0]= NULL;
            while (refcnt < maxrefs && fetchref(index, str, &ref) )
            {   refs[refcnt]= ref;
                refcnt++;
            }
        }

        if (refcnt==maxrefs)
            fprintf(stderr,
		"locate: first key (%s) matched too many refs\n", key);

    /*  intersect the reference sets for remaining keys with first set */
        while (*keys!=NULL)
        {   keys= stripkeys(keys, key, max_klen, common);
            if (*key==NULL) continue;

            len= strlen(key);
            strcat(key," ");
            alpha_seek(index, key, i_size, 0);
            key[len]= NULL;

            fscanf(index,"%s ", str);
            if (strcmp(str,key) != 0)  refcnt= 0;   /*  no matching refs */

            copied= 0; comp= 0; more= fetchref(index, str, &ref);
            while (comp < refcnt && more)
            {   /*  ans= ref-refs[comp]    */
                    ans= strcmp(ref.reffile, refs[comp].reffile);
                    if (ans==0)     ans= ref.start-refs[comp].start;
                    if (ans==0)     ans= ref.length-refs[comp].length;
                if (ans<0)  more= fetchref(index, str, &ref);
                if (ans==0) { refs[copied]= refs[comp]; comp++; copied++;
                              more= fetchref(index, str, &ref);}
                if (ans>0)  comp++;
            }

            refcnt= copied;
        }

    total= 0;
    for (i=0; i<refcnt; i++)    total += refs[i].length+1;

    allrefs= malloc(total+1);
    if (allrefs==NULL)
    {   fprintf(stderr, "locate: insufficient space for references\n");
	exit(1);
    }

    /* copy refs into allrefs */
        next= allrefs;
        for (i=0; i<refcnt; i++)
        {   /*  open text */
                if (strcmp(oldtext,refs[i].reffile) != 0)
                {   strcpy(oldtext,refs[i].reffile);
		    if (oldtext[0]=='/')
		    {   /* absolute path */
			strcpy(str,oldtext);
		    } else
		    {   /* relative name */
			strncpy(str, oldname, pathlen);  str[pathlen]= NULL;
			strcat(str, oldtext);
		    }
                    if (text) fclose(text);
                    text= fopen(str, "r");
                    if (text==NULL)
                    {   fprintf(stderr, "locate: cannot open %s\n", str);
			strcpy(oldtext, "");
                        return(NULL);
                    }
                }
            fseek(text, refs[i].start, 0);
            for (j=0; j<refs[i].length; j++)    *next++ = getc(text);
            *next++ = '\n';
        }
        *next = NULL;
    return(allrefs);
}



/*  stripkeys(line,key,max_klen, common):
        assigns to key the first key in line
        and returns a pointer to the position following the key
*/
char *stripkeys(line,key,max_klen,common)
char *line, *key;
int  max_klen;
char *common;
{   char *p;

    do
    {   while (isspace(*line))   line++;

        p= key;
        while (*line!=NULL && !isspace(*line))
        {   *p++ = *line++;
        }
        *p= NULL;

        makekey(key, max_klen, common);
    }   while (*key==NULL && *line!=NULL);
    return(line);
}

/*  read a reference pair from stream into *ref.  if file not given,
    use oldfile. return 1 if pair found, 0 ow.
*/
int fetchref(stream, oldfile, ref)
FILE *stream;
char *oldfile;
struct reftype *ref;
{   char cntl;

    fscanf(stream, "%c", &cntl);
    if (cntl=='\n') {return (0);}
    if (cntl==':')  fscanf(stream, "%s", oldfile);
    strcpy(ref->reffile, oldfile);
    fscanf(stream, "%D/%D", &ref->start, &ref->length);
    return(1);
}
!Funky!Stuff!
echo x - lookup.c
cat >lookup.c <<'!Funky!Stuff!'
# include "stdio.h"
# include "streams.h"
# include "bib.h"

char *locate();

int     max_klen =   6;     /*  max length of keys                      */
char    *common =           /*  name of file of common words            */
            COMFILE;
char    INDEX[maxstr] =     /*  name of index file                      */
            INDXFILE;

int     argc;
char    **argv;

main(argcount,arglist)
int argcount;
char **arglist;
{   char *refs;
    char keys[maxstr];
    char *p,*q;
    char one_index[maxstr];

    argc= argcount-1;
    argv= arglist+1;
    flags();

    /*  add SYSINDEX to search path.  all names are comma terminated */
	strcat(INDEX, ",");
	strcat(INDEX, SYSINDEX);
	strcat(INDEX, ",");

    while (fgets(keys,maxstr,stdin)!=NULL)
    {   for (p = one_index, q = INDEX; *q != 0 ; q++)
	    if (*q == ',' )
	    {   *p = 0;
	        refs = locate(keys, one_index, max_klen, common);
		if( refs==NULL )
		{   fprintf(stderr,
			"%s removed from index list.\n", one_index);
		    /* delete this file name (shift remainder on top) */
			strcpy(q-strlen(one_index),q+1);
			q = q-strlen(one_index)-1;
		}
                if (refs!=NULL && *refs!=NULL) break;
	        p = one_index;
	    }
	    else *p++ = *q;

        if (refs==NULL || *refs==NULL)  printf("No references found.\n");
        else                            printf("%s", refs);
        if (refs!=NULL) free(refs);
    }
    exit(0);
}

# define    operand     (strlen(*argv+2)==0 ? (argv++,argc--,*argv) : *argv+2)

flags()
{   for (; argc>0 && *argv[0]=='-';  argc--,argv++)
    {   switch ((*argv)[1])
        {   case 'l':   max_klen= atoi(operand);
                        break;
            case 'c':   common=  operand;
                        break;
            case 'p':   strcpy(INDEX,operand);
                        break;
            default:    fprintf(stderr, "unknown flag '%s'\n", *argv);
        }
    }
}
!Funky!Stuff!
echo x - makekey.c
cat >makekey.c <<'!Funky!Stuff!'
#

# include "stdio.h"
# include "ctype.h"
# include "bib.h"

char    commlist[MAXCOMM]=   /*  list of strings of common words         */
     "";
int firsttime = 1;

/*  makekey(p,max_klen,common):  compresses *p into a key
        folds upper to lower case.  ignores non-alphanumeric
        drops keys of length <= 1.
        drops words in common (name of file of words, one per line)
            (first call determines common for all later calls)
*/
makekey(p,max_klen,common)
char *p;
int  max_klen;          /* max key length */
char *common;
{   register char *from, *to, *stop;

    if (firsttime) {firsttime= 0; load_comm(common); }

    from= p; to= p; stop= max_klen+p;
    while (*from != NULL  &&  to < stop)
    {   if      (islower(*from))      *to++ = *from++;
        else if (isdigit(*from))      *to++ = *from++;
        else if (isupper(*from))    { *to++ = tolower(*from);  from++; }
        else                          from++;
    }
    *to= NULL;

    if (to<=p+1 ||
        lookup(commlist, p) )  *p= NULL;
}

/*  list is a string of null terminated strings, final string is null.
    p is a null terminated string.
    return 1 if p is a string in list, 0 ow.
*/
int lookup(list,p)
char *list, *p;
{   int len;
    len= strlen(list);
    while (len!=0 && strcmp(list,p)!=0)
    {   list += (len+1);
        len= strlen(list);
    }
    return(len!=0);
}

/*  read file common into commlist
*/
load_comm(common)
char *common;
{   FILE    *commfile;          /*  stream of common words                  */
    char *p, *stop;
    commfile= fopen(common,"r");
    if (commfile==NULL) fprintf(stderr, "cannot open '%s'\n", common);
    else
    {   /* read commfile into commlist  */
            p= commlist;    stop= commlist+MAXCOMM-1;
            while (p<stop && ((*p= getc(commfile))!=EOF))
            {   if (*p=='\n')   *p= NULL;
                p++;
            }
            if  (*p==EOF)  *p= NULL;
            else
            {   fprintf(stderr, "invert: too many common words\n");
                commlist[0]= NULL;
            }
        fclose(commfile);
    }
}

!Funky!Stuff!
echo x - streams.c
cat >streams.c <<'!Funky!Stuff!'
#

# include "stdio.h"
# include "streams.h"
# include "ctype.h"

/*  getword(stream,p,ignore):
        read next sequence of nonspaces on current line into *p.
    null if no more words on current line.
    %x (x in ignore) terminates line.
    all words of the form %a are returned as null.
    *p is a null terminated string (char p[maxstr]).
*/
getword(stream,p,ignore)
FILE *stream;
char *p, *ignore;
{   char c;
    char *oldp, *stop;

    oldp= p;
    stop= p+maxstr-1;
    do{ c= getc(stream);
    }   while (isspace(c) && c!='\n');

    while (!isspace(c))
    {   *p= c;
        if (p < stop)  p++;
        c= getc(stream);
    }
    *p= NULL;

    if (oldp[0]=='%')
    {   oldp[0]= NULL;
        if (index(ignore, oldp[1]) != NULL)
            while (c!='\n') c=getc(stream);
    }
}



/*  recsize(stream,start):
    returns length of record beginning at start
    (record ends at blank line or eof)
    assumes and retains stream positioned at start
*/
long int recsize(stream,start)
FILE *stream;
long int start;
{   char c;                 /*  length = # of chars from start to beginning */
    long int length;        /*  of current line.  c in current line.        */
    int nonspaces;          /*  nonspaces = # of nonspaces in current line. */

    nonspaces= 0;
    c= getc(stream);
    length= 0L;

    while ( (c!='\n' || nonspaces!=0) && c!=EOF)
    {   if      (c=='\n')
        {   length= ftell(stream)-start;
            nonspaces= 0;
        }
        else if (!isspace(c))    nonspaces++;

        c= getc(stream);
    }

    pos(start);
    return(length);
}


/*  nextrecord(stream,x): seeks in stream for first non-blank line
        at or after char x in stream. seeks to eof if x is past last record.
        x is the index of a character in the file (not eof).
    returns position in stream.  (returns EOF, if seeks to EOF)
*/
long int nextrecord(stream,x)
FILE *stream;
long int x;
{   long int start;         /*  position of the beginning of the line  */
    char c;                 /*      containing c                       */

    pos(x);
    start= x;
    /*  find start of first non-blank record        */
        for(;;)
        {   c= getc(stream);
            if      (c=='\n')           start= ftell(stream);
            else if (!isspace(c))       break;
        }

    if (feof(stream))   { pos(start);  start= EOF;  }
    else                pos(start);
    return(start);
}

/*  nextline(stream,x): seeks in stream after first newline at or after
        char x in stream. seeks to eof if x is in last line.
        x is the index of a character in the file (not eof).
    returns position in stream
*/
long int nextline(stream,x)
FILE *stream;
long int x;
{   pos(x);
    while (getc(stream)!='\n') ;
    return(ftell(stream));
}


/*  printline(stream): copies stream up to a newline
*/
printline(stream)
FILE *stream;
{   char c;
    while ((c=getc(stream)) != '\n' && c!=EOF)  putchar(c);
    putchar('\n');
}

/*  getline(stream,p):  store in *p next chars in stream up to \n
        advance stream past \n.
    limit of  maxstr-1 chars may be stored at p.
*/
getline(stream,p)
FILE *stream;
char *p;
{   char *stop;
    stop= p+maxstr-1;
    while ( (*p= getc(stream)) != '\n' && *p!=EOF)
        if (p<stop)    p++;
    *p= NULL;
}
!Funky!Stuff!
echo x - streams.h
cat >streams.h <<'!Funky!Stuff!'
#

long int nextrecord(), recsize(), nextline();

# define  maxstr            256
# define  pos(x)            fseek(stream,x,0)

!Funky!Stuff!
echo x - makefile
cat >makefile <<'!Funky!Stuff!'
.c.o:
	cc -g -c $*.c

INVo        = invert.o streams.o  makekey.o
INVc        = invert.c streams.c  makekey.c
LOOKo       = lookup.o locate.o alpha.seek.o streams.o makekey.o
LOOKc       = lookup.c locate.c alpha.seek.c streams.c makekey.c
NEWBIBo     = newbib.o newbibargs.o locate.o alpha.seek.o streams.o makekey.o
BIBo        = bib.o bibargs.o locate.o alpha.seek.o streams.o makekey.o
BIBc        = bib.c bibargs.c locate.c alpha.seek.c streams.c makekey.c
LISTo       = listrefs.o bibargs.o streams.o makekey.o
NEWLISTo    = listrefs.o newbibargs.o streams.o makekey.o
LISTc       = listrefs.c bibargs.c streams.c makekey.c

all:	invert lookup listrefs bib

invert:  $(INVo)
	cc -g  $(INVo)   -o invert

invert.lint:  $(INVc) bib.h streams.h
	lint  $(INVc) >invert.lint

lookup: $(LOOKo)
	cc -g  $(LOOKo)  -o lookup

lookup.lint: $(LOOKc) bib.h streams.h
	lint $(LOOKc) >lookup.lint

newbib: $(NEWBIBo)
	cc -g  $(NEWBIBo)  -o newbib

bib: $(BIBo)
	cc -g  $(BIBo)  -o bib

bib.lint: $(BIBc) bib.h streams.h
	lint $(BIBc) >bib.lint

listrefs: $(LISTo)
	cc -g  $(LISTo)  -o listrefs

newlisttrefs: $(NEWLISTo)
	cc -g  $(NEWLISTo)  -o newlistrefs

listrefs.lint: $(LISTc) bib.h streams.h
	lint $(LISTc) >listrefs.lint

bib.o:		bib.h
bibargs.o:	bib.h
listrefs.o:	bib.h
invert.o:       streams.h bib.h

locate.o:       streams.h bib.h
makekey.o:	bib.h
alpha.seek.o:   streams.h
streams.o:      streams.h

doc:
	bib -topenn bibdoc | tbl | vtroff -mtr

clean:	
	rm *.o
!Funky!Stuff!