/*
       Tib - TeX bibliographic formatter

       Written by J.C. Alexander, University of Maryland
               based on "bib," a troff formatter,
                     written by Tim Budd, University of Arizona.  */

#include <stdio.h>
#include <ctype.h>
#include "tib.h"

#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[120];           /* temporary file (see tib.h)            */
  long int refspos[MAXREFS];   /* reference seek positions              */
  int unlmark[MAXREFS];        /* underline flags                       */
  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 tmp_file[120];           /* 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 tibargs */
  extern int foot, sort, personal, underline, silent, loccit, ibidflag[];
  extern int hyphen, ordcite, biblineno, stndout, locflag[], optch2[], c0;
  extern int inputflag;
  extern char sortstr[], pfile[], citetemplate[], bibfname[], dirsp2[];
  extern char suffix[];

main(argc, argv)
  int argc;
  char **argv;
{  int rcomp1();
  FILE *ofd;                   /* output file                           */
  char *outname;
  char *outfname(), *getenv(), *pcom, *pdefst, *ptemp;
  int undrline();
  char headerline[240];
  char *indx = INDXFILE;
  char *tfgets(), argchk[3];
  int i, strcmp(), clnup();

  /* header */
  for (i = 1; i < argc; i++) {
     strcpy(argchk,OPTCH);
     strcat(argchk,"z");
     if (strcmp(argv[i],argchk) == 0)
         silent = true;
     strcpy(argchk,OPTCH);
     strcat(argchk,"|");
     if (strcmp(argv[i],argchk) == 0)
        silent = true;
  }
  if (silent == false)
     fprintf (stderr, "Tib -- version %s, released %s.\n", VERSION, RDATE);

  /* the file INDEX in the current directory is the default index,
     if it is present */

  rfd = fopen( INDXFILE , "r");
  if (rfd != NULL) {
     findex = true;
     tfgets(headerline,240,rfd);
     fclose(rfd);
     chkindex(indx, headerline, false);
     }

  /* get file names from environment */

  pcom = getenv("COMFILE");
  if (pcom != NULL)
     strcpy(common,pcom);
  ptemp = getenv("TMPDIR");
  if (ptemp != NULL) {
     strcpy(reffile,ptemp);
     strcat(reffile,dirsp2);
     strcat(reffile,"tibrXXXXXX");
     strcpy(tmp_file,ptemp);
     strcat(tmp_file,dirsp2);
     strcat(tmp_file,"tibpXXXXXX");
  }
  else {
     strcat(reffile,TMPREFFILE);
     strcat(tmp_file,TMPTEXTFILE);
  }

  /* open temporaries, reffile will contain references collected in
     pass 1, and tmp_file will contain text. */

  mktemp(reffile);
  rfd = fopen(reffile,"w+");
  if (rfd == NULL)
     error("can't open temporary reference file");
  putc('x', rfd);      /* put garbage in first position (not used) */
  mktemp(tmp_file);
  tfd = fopen(tmp_file,"w");
  if (tfd == NULL)
     error("can't open temporary output file");

   /*
      pass1 - read files, looking for citations
              arguments are read by doargs (tibargs.c)
   */

  pdefst = getenv("DEFSTYLE");
  if (pdefst == NULL) {
     if (doargs(argc, argv, DEFSTYLE ) == 0) {
        strcpy(bibfname, "<stdin>");
        rdtext(stdin);
        }
  }
  else {
     if (doargs(argc, argv, pdefst ) == 0) {
        strcpy(bibfname, "<stdin>");
        rdtext(stdin);
        }
  }

  /*
   sort references, make citations, add disambiguating characters
  */

  if (silent == 0)
     fprintf(stderr, "Processing citations ...\n");
  if (sort)
     qsort(refspos, numrefs+1, sizeof(long), rcomp1);
  makecites(citestr);
  if (underline)
     undrline();
  if (citetemplate[0] != '0')
     disambiguate();

  /*
  reopen temporaries
  */

  fclose(tfd);
  tfd = fopen(tmp_file,"r");
  if (tfd == NULL)
     error("can't open temporary output file for reading");

  /*
  pass 2 - reread files, replacing references
  */

  if (stndout == true && bibfname != "<stdin>")
     pass2(tfd, stdout);
  else {
     outname = outfname (bibfname);
     ofd = fopen (outname, "w");
     if (ofd == NULL) {
         fprintf(stderr,"can't open %s\n", outname);
         clnup();
         exit(1);
         }
     else {
         if (silent == 0)
            fprintf(stderr, "Writing output file ...\n");
         pass2 (tfd, ofd);
         fprintf(ofd,"\n");
         fclose (ofd);
         fprintf (stderr,"Output of tib in %s.\n", outname);
     }

  }
  fclose(tfd);
  fclose(rfd);
  clnup();
  exit(0);
}

  /*   clean up   */
  clnup()
{
  unlink(tmp_file);
  unlink(reffile);
  return;
}
/* getwd - get the next 'word' (terminated by non-alpha) from a file */
char *
  getwd(fd,getwdflag)
  FILE *fd;
  int getwdflag;
       /* The next word is terminated by non-alpha if getwdflag = 0,
          by whitespace if getwdflag != 0 */
       /* The result is returned in str.                        */
{  char c;
  char str[120], *pstr;
  int bracketflag;     /* 1 if file name is enclosed in brackets */

  bracketflag = 0;
  pstr = str;
  *pstr = NULL;

  getch(c,fd);
  if (getwdflag == 0) {
    while (isalpha(c) != 0) {
     *pstr++ = c;
     getch(c,fd);
     }
  }
  else {
    while (c == ' '
           || c == '\n'
           || c == '\t'
           || c == '\%'
           || c == '{'
           || c == EOF) {
      if (c == EOF)
        error("end of file after \\input");
      if (c == '{') bracketflag = 1;
      if (c == '\n') biblineno++;
      if (c == '\%') {
        while (c != '\n') getch(c,fd);
        ungetc(c,fd);
      }
      getch(c,fd);
    }
    if (bracketflag == 0) {
      while (c != ' '
         && c != '\n'
         && c != '\t'
         && c != EOF) {
      *pstr++ = c;
      getch(c,fd);
      }
    }
    else {
      while (c != '}') {
        if (c == EOF
            || c == '\n'
            || c == '\%') error("no right brackets on \\input file name");
      *pstr++ = c;
      getch(c,fd);
        while (c == ' ' || c == '\t') getch(c,fd);
      }
    }
  }
  *pstr = NULL;
  if (bracketflag == 0)
    ungetc(c,fd);              /*  put the first char after wd back */
  return(str);
}

/* rdtext - read and process a text file, looking for [. commands */
  rdtext(fd)
  FILE *fd;
{  char lastc, c, d;
  char cmd[120], *getwd();
  char lastlc;
  int sortdefs();
  int percentflag; /* is there % so far on line? */
  int putback;
  int fctr = 0;                /* count of input/include files in stack */
  FILE *fstack[32];            /* stack of input/include files          */
  char fnm[32][120];           /* stack of input/include files names    */
  char bibfnamet[120];         /* temporary file name                   */
  int biblinenostack[32];      /* stack of line numbers                 */

  sortdefs();
  lastc = '\0';
  lastlc = '\0';
  percentflag = false;
  biblineno = 1;
  fprintf(tfd,"%cmessage {DOCUMENT TEXT}\n",'\\');
  while (getch(c, fd) != EOF || fctr != 0) {
     if (c == EOF && fctr != 0) {      /* pop file from stack */
        fprintf(tfd,"%c",lastc);
        fclose(fd);
        strcpy(bibfnamet, bibfname);
        strcpy(bibfname, fnm[--fctr]);
        biblineno = biblinenostack[fctr];
        fd = fstack[fctr];
        fprintf(stderr, "... processing of %s complete, returning to %s ...\n", bibfnamet, bibfname);
        fprintf(tfd,"\\message{)}%%");
        c = NULL;
        }

     if (c == '\\' && percentflag == false && inputflag == true) {
        if (lastc != NULL) putc(lastc,tfd);
        strcpy(cmd,getwd(fd,0));
        if (strcmp(cmd,"input")==0) {
           /* found an input file */
           strcpy(fnm[fctr], bibfname);        /* push current file */
           biblinenostack[fctr] = biblineno;
           biblineno = 1;
           if (fctr == 32)
             error("\\input files nested too deeply");
           fstack[fctr++] = fd;
           strcpy(bibfname,getwd(fd,1));        /* get new file name   */
           putback = false;
           fd = fopen(bibfname,"r");
           if (fd == NULL) {                   /* file not found;      */
              strcpy(bibfnamet,bibfname);      /* try adding .tex      */
              strcat(bibfname,suffix);
              fd = fopen(bibfname,"r");
              if (fd == NULL) {                /* still can't be found; */
                 strcpy(bibfname, fnm[--fctr]);        /* pop file     */
                 fd = fstack[fctr];
                 biblineno = biblinenostack[fctr];
                 if (stndout == false && silent == false)
                    fprintf (stderr, "'%s', line %d: unable to locate %s, assuming it has no references\n", bibfname, biblineno, bibfnamet);
                 putback = true;
                 }
              }
           if (putback == false) {
              if (stndout == false && silent == false)
                 fprintf(stderr, "Reading input document file %s and looking up references ...\n", bibfname);
              fprintf(tfd,"\\message{(%s input by Tib}%%\n", bibfname);
              }
           else {
              fprintf(tfd,"%c",'\\');          /* pass chars on to     */
              fprintf(tfd,"%s",cmd);           /* output file          */
              fprintf(tfd,"%c",' ');
              fprintf(tfd,"%s",bibfnamet);
              putback = false;
              }
           }
        else {                                 /* this is just text;  */
           fprintf(tfd,"%c",'\\');             /* pass it to the file */
           fprintf(tfd,"%s",cmd);
           }
        c = NULL;
        lastc = NULL;
       }

     if (c == '\%')
        if (percentflag == false)
           if (lastc != '\\') {
              percentflag = true;
              if (isalpha(lastc) == 0)
                 if (lastc != '\n')
                    if (lastc != '\0')
                       if (lastc != ']')
                          if (lastc != '>') {
                             lastlc = lastc;
                             lastc = '\0';
                             }
              }
     if ((c == '[' || c == '<') && lastc != '\\')
         if (getch(d, fd) == '.') { /* found a reference */
           if (c == '<') { if (lastc) fprintf(tfd,"%c",lastc);}
           else
              switch (lastc) {
                 case '\0':
                 case ' ': fprintf(tfd,"%cLspace ",'\\'); break;
                 case '.': fprintf(tfd,"%cLperiod ",'\\'); break;
                 case ',': fprintf(tfd,"%cLcomma ",'\\'); break;
                 case '?': fprintf(tfd,"%cLquest ",'\\'); break;
                 case ':': fprintf(tfd,"%cLcolon ",'\\'); break;
                 case ';': fprintf(tfd,"%cLscolon ",'\\'); break;
                 case '!': fprintf(tfd,"%cLbang ",'\\'); break;
                 case '\'': fprintf(tfd,"%cLquote ",'\\'); break;
                 case '"': fprintf(tfd,"%cLqquote ",'\\'); break;
                 case '`': fprintf(tfd,"%cLrquote ",'\\'); break;
                 default:  fprintf(tfd,"%c",lastc); break;
                 }
           rdcite(fd, c);
           if (c == '[')
              switch (lastc) {
                 case '\0': break;
                 case ' ': fprintf(tfd,"%cRspace{}",'\\'); break;
                 case '\n': fprintf(tfd,"%cRspace{}",'\\'); break;
                 case '.': fprintf(tfd,"%cRperiod{}",'\\'); break;
                 case ',': fprintf(tfd,"%cRcomma{}",'\\'); break;
                 case '?': fprintf(tfd,"%cRquest{}",'\\'); break;
                 case ':': fprintf(tfd,"%cRcolon{}",'\\'); break;
                 case ';': fprintf(tfd,"%cRscolon{}",'\\'); break;
                 case '!': fprintf(tfd,"%cRbang{}",'\\'); break;
                 case '\'': fprintf(tfd,"%cRquote{}",'\\'); break;
                 case '"': fprintf(tfd,"%cRqquote{}",'\\'); break;
                 case '`': fprintf(tfd,"%cRrquote{}",'\\'); break;
                 }
           lastc = '\0';
           }
        else {
           if (lastc != '\0') putc(lastc, tfd);
           ungetc(d, fd);
           lastc = c;
           }
     else {
        if (lastc != '\0') putc(lastc, tfd);
        lastc = c;
        if (c == '\n' || c == EOF) {
           biblineno++;
           percentflag = false;
           if (lastlc != '\0') {
              putc(c,tfd);
              getch(d, fd);
              if (d == ' ' || d == '\n' || d == '\t' || d == '\%'
                  || d == '.') {
                ungetc(d, fd);
                putc(lastlc,tfd);
                lastlc = '\0';
                putc('\%',tfd);
/*               putc('\n',tfd); */
                }
              else {
                ungetc(d, fd);
                lastc = lastlc;
                lastlc = '\0';
                }
              }
           }
        }
     }
  if (lastc != '\0') 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], footch[2], c0ch[2];

  if (foot)
    strcpy(footch,"\n");
  else
    strcpy(footch,"");
  if (c0)
    strcpy(c0ch,"\n");
  else
    strcpy(c0ch,"");
  if (ch == '[')
     fprintf(tfd,"%cLcitemark ", '\\');
  else
     fprintf(tfd,"%cLAcitemark ", '\\');
  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%s%cRcitemark %s", CITEMARK, n,
                        CITEMARK, info, CITEEND, c0ch,'\\',footch);
/*                  if (foot) {
                    getch(c, fd);
                    if (c == ' '|| c== '\n')
                       fprintf(tfd, "%c ", '\\');
                    else
                       putc(c, tfd);
                    fprintf(tfd, "\n");
                    }*/
                 }
              else {
                 fprintf(tfd, "%c0%c%s%s%c%s%cRcitemark%s ", CITEMARK, CITEMARK,
                               huntstr, info, CITEEND,c0ch, '\\',footch);
/*                  if (foot) {
                    getch(c, fd);
                    if (c == ' '|| c== '\n')
                       fprintf(tfd, "%c ", '\\');
                    else
                       putc(c, tfd);
                    fprintf(tfd, "\n");
                    }*/
                 }
              return;
              }
           else if (c == '>') {
              n = getref(huntstr);
              if (n > 0) {
                 fprintf(tfd, "%c%ld%c%s%c%s%cRAcitemark{}%s", CITEMARK, n,
                              CITEMARK, info, CITEEND,c0ch,'\\',footch);
                 }
              else {
                 fprintf(tfd, "%c0%c%s%s%c%s%cRAcitemark{}%s", CITEMARK,
                            CITEMARK,huntstr, info, CITEEND,c0ch,'\\',footch);
                 }
              return;
              }
           else
              addc(huntstr, c);
           break;

        case '<':
           strcat(info, "\\LIcitemark{}");
           while (getch(c, fd) != '>')
              if (c == EOF) {
                 fprintf(stderr, "Error: ill formed reference\n");
                 clnup();
                 exit(1);
                 }
              else
                 addc(info, c);
           strcat(info, "\\RIcitemark ");
           break;

        case '\n':
           biblineno++;
        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) {
     /* expand defined string */
     strcpy(rf, r);
     free(r);
     expnd2(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]);
         }

     if (loccit) {
        ibidflag[numrefs+1] = false;
        locflag[numrefs+1] = false;
        for (i = 0; i <= numrefs; i++) {
            rdref(refspos[i], ref);
            if (strcmp(ref, rf) == 0) {
               locflag[numrefs+1] = true;
               break;
            }
        }
     rdref(refspos[numrefs], ref);
     if (strcmp(ref, rf) == 0)
        ibidflag[numrefs+1] = true;
     }

     /* didn't match any existing reference, create new one */

     numrefs++;
     refspos[numrefs] = rend;
#ifdef READWRITE
     fixrfd( WRITE );                 /* fix access mode of rfd, if nec. */
#else
     fseek(rfd, rend, 0);             /* go to end of rfd */
#endif
     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 );
     }
}

/* hunt - hunt for reference from either personal or system index */
  char *hunt(huntstr)
  char huntstr[];
{  char *fhunt(), *r, *p, *q, fname[120];
  char *getenv(), *pindx;

  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);
     }
  pindx = getenv("SYSINDEX");
  if (pindx == NULL) {
     if ((r = fhunt(SYSINDEX , huntstr)) != NULL)
        return(r);
     }
  else {
     if ((r = fhunt(pindx , 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);
}

/* 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("\\Citehyphen ", ofd);
        i = j - 1;
        }
     else if (i != rtop)
        if (!c0) fputs("\\Citecomma\n", ofd);
     if ((foot) || (c0)) {
        fn++;
        footrefs[fn] = citenums[i];
        }
     }
  return(fn);
}

/* pass2 - read pass 1 files entering citation */
  pass2(ifd, ofd)
  FILE *ifd, *ofd;
{
  char c, d, e;
  int  i, fn, footrefs[25], dumped;

  fn = -1;
  if ((foot) || (c0)) dumped = true;
     else dumped = false;
  while (getch(c, ifd) != EOF) {
     while (c == '\n') {
        putc(c, ofd);
        if (((foot) || (c0)) && fn >= 0) {
           for (i = 0; i <= fn; i++)
               dumpref(footrefs[i], ofd);
           fn = -1;
           }
        if (getch(c,ifd) != '.')
           ;
        else if (getch(d,ifd) != '[')
           ungetc(d,ifd);
        else if (getch(e,ifd) != ']') {
           ungetc(e,ifd);
           ungetc(d,ifd);}
        else if ((foot == false) && (c0 == false)) {
           while (echoc(c, ifd, ofd) != '\n')
              ;
           dumped = true;
           fprintf(ofd,"\\message{REFERENCE LIST}\n\n");
           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: reference list not made -- .[] not encountered\n","");
}