#include "e.h"
#include "y.tab.h"
#include <ctype.h>
#include <errno.h>

Infile  infile[10];
Infile  *curfile = infile;

#define MAXSRC  50
Src     src[MAXSRC];    /* input source stack */
Src     *srcp   = src;

extern int getarg(char *);
extern  void eprint(void);

void pushsrc(int type, char *ptr)       /* new input source */
{
       if (++srcp >= src + MAXSRC)
               ERROR "inputs nested too deep" FATAL;
       srcp->type = type;
       srcp->sp = ptr;
       if (dbg > 1) {
               printf("\n%3d ", srcp - src);
               switch (srcp->type) {
               case File:
                       printf("push file %s\n", ((Infile *)ptr)->fname);
                       break;
               case Macro:
                       printf("push macro <%s>\n", ptr);
                       break;
               case Char:
                       printf("push char <%c>\n", *ptr);
                       break;
               case String:
                       printf("push string <%s>\n", ptr);
                       break;
               case Free:
                       printf("push free <%s>\n", ptr);
                       break;
               default:
                       ERROR "pushed bad type %d\n", srcp->type FATAL;
               }
       }
}

void popsrc(void)       /* restore an old one */
{
       if (srcp <= src)
               ERROR "too many inputs popped" FATAL;
       if (dbg > 1) {
               printf("%3d ", srcp - src);
               switch (srcp->type) {
               case File:
                       printf("pop file\n");
                       break;
               case Macro:
                       printf("pop macro\n");
                       break;
               case Char:
                       printf("pop char <%c>\n", *srcp->sp);
                       break;
               case String:
                       printf("pop string\n");
                       break;
               case Free:
                       printf("pop free\n");
                       break;
               default:
                       ERROR "pop weird input %d\n", srcp->type FATAL;
               }
       }
       srcp--;
}

Arg     args[10];       /* argument frames */
Arg     *argfp = args;  /* frame pointer */
int     argcnt;         /* number of arguments seen so far */

void dodef(tbl *stp)    /* collect args and switch input to defn */
{
       int i, len;
       char *p;
       Arg *ap;

       ap = argfp+1;
       if (ap >= args+10)
               ERROR "more than arguments\n" FATAL;
       argcnt = 0;
       if (input() != '(')
               ERROR "disaster in dodef\n"FATAL;
       if (ap->argval == 0)
               ap->argval = malloc(1000);
       for (p = ap->argval; (len = getarg(p)) != -1; p += len) {
               ap->argstk[argcnt++] = p;
               if (input() == ')')
                       break;
       }
       for (i = argcnt; i < MAXARGS; i++)
               ap->argstk[i] = "";
       if (dbg)
               for (i = 0; i < argcnt; i++)
                       printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
       argfp = ap;
       pushsrc(Macro, stp->cval);
}

getarg(char *p) /* pick up single argument, store in p, return length */
{
       int n, c, npar;

       n = npar = 0;
       for ( ;; ) {
               c = input();
               if (c == EOF)
                       ERROR "end of file in getarg!\n" FATAL;
               if (npar == 0 && (c == ',' || c == ')'))
                       break;
               if (c == '"')   /* copy quoted stuff intact */
                       do {
                               *p++ = c;
                               n++;
                       } while ((c = input()) != '"' && c != EOF);
               else if (c == '(')
                       npar++;
               else if (c == ')')
                       npar--;
               n++;
               *p++ = c;
       }
       *p = 0;
       unput(c);
       return(n + 1);
}

#define PBSIZE  2000
char    pbuf[PBSIZE];           /* pushback buffer */
char    *pb     = pbuf-1;       /* next pushed back character */

char    ebuf[200];              /* collect input here for error reporting */
char    *ep     = ebuf;

input(void)
{
       register int c = 0;

 loop:
       switch (srcp->type) {
       case File:
               c = getc(curfile->fin);
               if (c == EOF) {
                       if (curfile == infile)
                               break;
                       if (curfile->fin != stdin) {
                               fclose(curfile->fin);
                               free(curfile->fname);   /* assumes allocated */
                       }
                       curfile--;
                       printf(".lf %d %s\n", curfile->lineno, curfile->fname);
                       popsrc();
                       goto loop;
               }
               if (c == '\n')
                       curfile->lineno++;
               break;
       case Char:
               if (pb >= pbuf) {
                       c = *pb--;
                       popsrc();
                       break;
               } else {        /* can't happen? */
                       popsrc();
                       goto loop;
               }
       case String:
               c = *srcp->sp++;
               if (c == '\0') {
                       popsrc();
                       goto loop;
               } else {
                       if (*srcp->sp == '\0')  /* empty, so pop */
                               popsrc();
                       break;
               }
       case Macro:
               c = *srcp->sp++;
               if (c == '\0') {
                       if (--argfp < args)
                               ERROR "argfp underflow" FATAL;
                       popsrc();
                       goto loop;
               } else if (c == '$' && isdigit(*srcp->sp)) {
                       int n = 0;
                       while (isdigit(*srcp->sp))
                               n = 10 * n + *srcp->sp++ - '0';
                       if (n > 0 && n <= MAXARGS)
                               pushsrc(String, argfp->argstk[n-1]);
                       goto loop;
               }
               break;
       case Free:      /* free string */
               free(srcp->sp);
               popsrc();
               goto loop;
       }
       if (ep >= ebuf + sizeof ebuf)
               ep = ebuf;
       *ep++ = c;
       return c;
}


unput(int c)
{
       if (++pb >= pbuf + sizeof pbuf)
               ERROR "pushback overflow\n"FATAL;
       if (--ep < ebuf)
               ep = ebuf + sizeof(ebuf) - 1;
       *pb = c;
       pushsrc(Char, pb);
       return c;
}

void pbstr(char *s)
{
       pushsrc(String, s);
}

void error(int die, char *s)
{
       extern char *cmdname;

       if (synerr)
               return;
       fprintf(stderr, "%s: ", cmdname);
       fprintf(stderr, s);
       if (errno > 0)
               perror("???");
       if (curfile->fin)
               fprintf(stderr, " near %s:%d",
                       curfile->fname, curfile->lineno+1);
       fprintf(stderr, "\n");
       eprint();
       synerr = 1;
       errno = 0;
       if (die) {
               if (dbg)
                       abort();
               else
                       exit(1);
       }
}

void yyerror(char *s)
{
       error(0, s);    /* temporary */
}

char errbuf[200];

void eprint(void)       /* try to print context around error */
{
       char *p, *q;

       if (ep == ebuf)
               return;                         /* no context */
       p = ep - 1;
       if (p > ebuf && *p == '\n')
               p--;
       for ( ; p >= ebuf && *p != '\n'; p--)
               ;
       while (*p == '\n')
               p++;
       fprintf(stderr, " context is\n\t");
       for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
               ;
       while (p < q)
               putc(*p++, stderr);
       fprintf(stderr, " >>> ");
       while (p < ep)
               putc(*p++, stderr);
       fprintf(stderr, " <<< ");
       while (pb >= pbuf)
               putc(*pb--, stderr);
       if (curfile->fin)
               fgets(ebuf, sizeof ebuf, curfile->fin);
       fprintf(stderr, "%s", ebuf);
       pbstr("\n.EN\n");       /* safety first */
       ep = ebuf;
}