#include        "cc.h"
#include        "y.tab.h"

/*
* known debug flags
*      -o file         output file
*      -s tag          debug structure output
*      -D name         define
*      -I path         include
*      -H sym          help symbol output
*      -B
*      -L
*      -a
*      -d
*      -i
*      -r
*      -t
*      -v
*      -w
*/

void
main(int argc, char *argv[])
{
       char ofile[100], incfile[20], *p, *q, *r;
       int nproc, nout, status, i, c;
       Sym *s, *t;

       memset(debug, 0, sizeof(debug));
       cinit();
       ginit();

       outfile = 0;
       include[ninclude++] = ".";
       ARGBEGIN {
       default:
               c = ARGC();
               if(c >= 0 && c < sizeof(debug))
                       debug[c]++;
               break;

       case 'o':
               outfile = ARGF();
               break;

       case 's':
               debug['H']++;
               suedebug = ARGF();
               break;

       case 'D':
               p = ARGF();
               if(p)
                       dodefine(p);
               break;

       case 'I':
               p = ARGF();
               if(p)
                       include[ninclude++] = p;
               break;

       case 'H':
               debug['H']++;
               p = ARGF();
               if(p) {
                       i = 0;
                       t = 0;
                       q = utfrune(p, ':');
                       if(q) {
                               *q++ = 0;
                               r = utfrune(q, ':');
                               if(r) {
                                       *r++ = 0;
                                       i = atol(r);
                                       r = utfrrune(q, '/');
                                       if(r)
                                               q = r+1;
                                       t = slookup(q);
                               } else
                                       i = atol(q);
                       }
                       s = slookup(p);
                       if(!s->ref) {
                               ALLOC(s->ref, Ref);
                               s->ref->class = CHELP;
                               s->ref->lineno = i;
                               s->ref->sym = t;
                       }
               }
               break;
       } ARGEND
       if(argc < 1 && outfile == 0) {
               print("usage: %cc [-options] files\n", thechar);
               errorexit();
       }
       nproc = 1;
       if(p = getenv("NPROC"))
               nproc = atol(p);        /* */
       if(argc > 1) {
               c = 0;
               nout = 0;
               for(;;) {
                       while(nout < nproc && argc > 0) {
                               i = fork();
                               if(i < 0) {
                                       i = mywait(&status);
                                       if(i < 0) {
                                               print("cannot create a process\n");
                                               errorexit();
                                       }
                                       if(status)
                                               c++;
                                       nout--;
                                       continue;
                               }
                               if(i == 0)
                                       goto child;
                               nout++;
                               argc--;
                               argv++;
                       }
                       i = mywait(&status);
                       if(i < 0) {
                               if(c)
                                       errorexit();
                               exits(0);
                       }
                       if(status)
                               c++;
                       nout--;
               }
       }

child:
       if(argc < 1)
               strcpy(ofile, "stdin");
       else
               strcpy(ofile, *argv);
       if(p = utfrrune(ofile, '/')) {
               include[0] = ofile;
               *p++ = 0;
       } else
               p = ofile;
       if(outfile == 0) {
               outfile = p;
               if(p = utfrrune(outfile, '.'))
                       if(p[1] == 'c' && p[2] == 0)
                               p[0] = 0;
               p = utfrune(outfile, 0);
               p[0] = '.';
               p[1] = thechar;
               p[2] = 0;
       }
       if(unix()) {
               strcpy(incfile, "/usr/%include");
               p = utfrrune(incfile, '%');
               if(p)
                       *p = thechar;
       } else {
               strcpy(incfile, "/");
               strcat(incfile, thestring);
               strcat(incfile, "/include");
       }
       if(p = getenv("INCLUDE")) {
               include[ninclude++] = p;
       } else {
               include[ninclude++] = incfile;
               include[ninclude++] = "/sys/include";
       }
       if(debug['H'])
               outfile = 0;
       else
               close(mycreat(outfile, 0664));
       newio();
       if(argc < 1)
               newfile("stdin", 0);
       else
               newfile(*argv, -1);
       yyparse();
       if(!debug['H'])
               gclean();
       reftrace();
       if(nerrors)
               errorexit();
       exits(0);
}

void
errorexit(void)
{
       if(outfile)
               remove(outfile);
       exits("error");
}

void
pushio(void)
{
       Io *i;

       i = iostack;
       if(i == I) {
               yyerror("botch in pushio");
               errorexit();
       }
       i->p = fi.p;
       i->c = fi.c;
}

void
newio(void)
{
       Io *i;
       static pushdepth = 0;

       i = iofree;
       if(i == I) {
               pushdepth++;
               if(pushdepth > 1000) {
                       yyerror("macro/io expansion too deep");
                       errorexit();
               }
               ALLOC(i, Io);
       } else
               iofree = i->link;
       i->c = 0;
       i->f = -1;
       ionext = i;
}

void
newfile(char *s, int f)
{
       Io *i;

       i = ionext;
       i->link = iostack;
       iostack = i;
       i->f = f;
       if(f < 0)
               i->f = open(s, 0);
       if(i->f < 0) {
               yyerror("%cc: %s: %s", thechar, myerrstr(i->f), s);
               errorexit();
       }
       fi.c = 0;
       linehist(s, 0);
}

Sym*
slookup(char *s)
{

       strcpy(symb, s);
       return lookup();
}

Sym*
lookup(void)
{
       Sym *s;
       Ref *r;
       ulong h;
       long g;
       char *p, c0;
       int i;

       g = 0;
       for(p=symb; *p;) {
               g = g * 3;
               g += *p++;
       }
       if(g < 0)
               g = ~g;
       h = g;
       if(p >= symb+CNNAME) {
               /*
                * long names are first CNNAME/2-2 chars
                * last CNNAME/2-2 chars and three pieces of hash
                */
               memmove(symb+(CNNAME/2-2), p-(CNNAME/2-2), CNNAME/2-2);
               for(i=0; i<3; i++) {
                       c0 = g % 62;
                       g = g / 62;
                       if(c0 < 26)
                               c0 += 'a';
                       else
                       if(c0 < 52)
                               c0 += 'A'-26;
                       else
                               c0 += '0'-52;
                       symb[CNNAME-4+i] = c0;
               }
               symb[CNNAME-1] = 0;
       }
       h %= NHASH;
       c0 = symb[0];
       for(s = hash[h]; s != S; s = s->link) {
               if(s->name[0] != c0)
                       continue;
               if(strcmp(s->name, symb) == 0) {
                       if(s->ref) {
                               ALLOC(r, Ref);
                               r->link = s->ref;
                               s->ref = r;
                               r->lineno = lineno;
                       }
                       return s;
               }
       }
       ALLOC(s, Sym);
       strcpy(s->name, symb);
       s->link = hash[h];
       hash[h] = s;
       syminit(s);

       return s;
}

void
syminit(Sym *s)
{

       s->lexical = LNAME;
       s->block = 0;
       s->offset = 0;
       s->type = T;
       s->suetag = T;
       s->class = CXXX;
       s->ref = 0;
       s->aused = 0;
}

#define EOF     (-1)
#define IGN     (-2)
#define ESC     (1<<20)
#define GETC()  ((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff))

enum
{
       Numdec          = 1<<0,
       Numlong         = 1<<1,
       Numuns          = 1<<2,
       Numvlong        = 1<<3,
       Numflt          = 1<<4,
};

long
yylex(void)
{
       long c, c1;
       char *cp;
       Rune rune;
       Sym *s;

       if(peekc != IGN) {
               c = peekc;
               peekc = IGN;
               goto l1;
       }
l0:
       c = GETC();

l1:
       if(c >= Runeself) {
               /*
                * extension --
                *      all multibyte runes are alpha
                */
               cp = symb;
               goto talph;
       }
       if(isspace(c)) {
               if(c == '\n')
                       lineno++;
               goto l0;
       }
       if(isalpha(c)) {
               cp = symb;
               if(c != 'L')
                       goto talph;
               *cp++ = c;
               c = GETC();
               if(c == '\'') {
                       /* L'x' */
                       c = escchar('\'', 1, 0);
                       if(c == EOF)
                               c = '\'';
                       c1 = escchar('\'', 1, 0);
                       if(c1 != EOF) {
                               yyerror("missing '");
                               peekc = c1;
                       }
                       yylval.lval = castto(c, TUSHORT);
                       return LUCONST;
               }
               if(c == '"') {
                       cp = hunk;
                       c1 = 0;
                       strcpy(symb, "\"<string>\"");
                       goto caselq;
               }
               goto talph;
       }
       if(isdigit(c))
               goto tnum;
       switch(c)
       {

       case EOF:
               peekc = EOF;
               return -1;

       case '_':
               cp = symb;
               goto talph;

       case '#':
               domacro();
               goto l0;

       case '.':
               c1 = GETC();
               if(isdigit(c1)) {
                       cp = symb;
                       *cp++ = c;
                       c = c1;
                       c1 = 0;
                       goto casedot;
               }
               break;

       case '"':
               strcpy(symb, "\"L<string>\"");
               cp = hunk;
               c1 = 0;

       caseq:
               /* "..." */
               for(;;) {
                       c = escchar('"', 0, 1);
                       if(c == EOF)
                               break;
                       if(c & ESC) {
                               ALLOCN(cp, c1, 1);
                               cp[c1] = c;
                               c1++;
                       } else {
                               rune = c;
                               c = runelen(rune);
                               ALLOCN(cp, c1, c);
                               runetochar(cp+c1, &rune);
                               c1 += c;
                       }
               }
               for(;;) {
                       /* it takes 2 peekc's to skip comments */
                       c = getc();
                       if(isspace(c))
                               continue;
                       if(c == '"')
                               goto caseq;
                       unget(c);
                       break;
               }
               lnstring = c1+1;
               do {
                       ALLOCN(cp, c1, 1);
                       cp[c1++] = 0;
               } while(c1 & 3);
               yylval.sval = cp;
               return LSTRING;

       caselq:
               /* L"..." */
               for(;;) {
                       c = escchar('"', 1, 0);
                       if(c == EOF)
                               break;
                       ALLOCN(cp, c1, sizeof(ushort));
                       *(ushort*)(cp + c1) = c;
                       c1 += sizeof(ushort);
               }
               for(;;) {
                       /* it takes 2 peekc's to skip comments */
                       c = getc();
                       if(isspace(c))
                               continue;
                       if(c == '"')
                               goto caselq;
                       unget(c);
                       break;
               }
               lnstring = c1+sizeof(ushort);
               do {
                       ALLOCN(cp, c1, sizeof(ushort));
                       *(ushort*)(cp + c1) = 0;
                       c1 += sizeof(ushort);
               } while(c1 & 3);
               yylval.rval = (ushort*)cp;
               return LLSTRING;

       case '\'':
               /* '.' */
               c = escchar('\'', 0, 0);
               if(c == EOF)
                       c = '\'';
               c1 = escchar('\'', 0, 0);
               if(c1 != EOF) {
                       yyerror("missing '");
                       peekc = c1;
               }
               if(ovflo(c, TUCHAR))
                       yyerror("overflow in character constant: 0x%x", c);
               c = castto(c, TCHAR);
               yylval.lval = c;
               return LCONST;

       case '/':
               c1 = GETC();
               if(c1 == '*') {
                       for(;;) {
                               c = getr();
                               while(c == '*') {
                                       c = getr();
                                       if(c == '/')
                                               goto l0;
                               }
                               if(c == EOF) {
                                       yyerror("eof in comment");
                                       errorexit();
                               }
                       }
               }
               if(c1 == '=')
                       return LDVE;
               break;

       case '*':
               c1 = GETC();
               if(c1 == '=')
                       return LMLE;
               break;

       case '%':
               c1 = GETC();
               if(c1 == '=')
                       return LMDE;
               break;

       case '+':
               c1 = GETC();
               if(c1 == '+')
                       return LPP;
               if(c1 == '=')
                       return LPE;
               break;

       case '-':
               c1 = GETC();
               if(c1 == '-')
                       return LMM;
               if(c1 == '=')
                       return LME;
               if(c1 == '>')
                       return LMG;
               break;

       case '>':
               c1 = GETC();
               if(c1 == '>') {
                       c = LRSH;
                       c1 = GETC();
                       if(c1 == '=')
                               return LRSHE;
                       break;
               }
               if(c1 == '=')
                       return LGE;
               break;

       case '<':
               c1 = GETC();
               if(c1 == '<') {
                       c = LLSH;
                       c1 = GETC();
                       if(c1 == '=')
                               return LLSHE;
                       break;
               }
               if(c1 == '=')
                       return LLE;
               break;

       case '=':
               c1 = GETC();
               if(c1 == '=')
                       return LEQ;
               break;

       case '!':
               c1 = GETC();
               if(c1 == '=')
                       return LNE;
               break;

       case '&':
               c1 = GETC();
               if(c1 == '&')
                       return LANDAND;
               if(c1 == '=')
                       return LANDE;
               break;

       case '|':
               c1 = GETC();
               if(c1 == '|')
                       return LOROR;
               if(c1 == '=')
                       return LORE;
               break;

       case '^':
               c1 = GETC();
               if(c1 == '=')
                       return LXORE;
               break;

       default:
               return c;
       }
       peekc = c1;
       return c;

talph:
       /*
        * cp is set to symb and some
        * prefix has been stored
        */
       for(;;) {
               if(c >= Runeself) {
                       for(c1=0;;) {
                               cp[c1++] = c;
                               if(fullrune(cp, c1))
                                       break;
                               c = GETC();
                       }
                       cp += c1;
                       c = GETC();
                       continue;
               }
               if(!isalnum(c) && c != '_')
                       break;
               *cp++ = c;
               c = GETC();
       }
       *cp = 0;
       if(debug['L'])
               print("%L %s\n", lineno, symb);
       peekc = c;
       s = lookup();
       if(s->macro) {
               if(s->ref)
                       s->ref->class = CMACRO;
               newio();
               cp = ionext->b;
               macexpand(s, cp);
               pushio();
               ionext->link = iostack;
               iostack = ionext;
               fi.p = cp;
               fi.c = strlen(cp);
               if(peekc != IGN) {
                       cp[fi.c++] = peekc;
                       cp[fi.c] = 0;
                       peekc = IGN;
               }
               goto l0;
       }
       yylval.sym = s;
       if(s->class == CTYPEDEF)
               return LTYPE;
       if(s->lexical == LNAME)
               return LNAME;
       if(s->ref)
               s->ref->class = CLEXICAL;
       return s->lexical;

tnum:
       c1 = 0;
       cp = symb;
       if(c != '0') {
               c1 |= Numdec;
               for(;;) {
                       *cp++ = c;
                       c = GETC();
                       if(isdigit(c))
                               continue;
                       goto dc;
               }
       }
       *cp++ = c;
       c = GETC();
       if(c == 'x' || c == 'X')
               for(;;) {
                       *cp++ = c;
                       c = GETC();
                       if(isdigit(c))
                               continue;
                       if(c >= 'a' && c <= 'f')
                               continue;
                       if(c >= 'A' && c <= 'F')
                               continue;
                       if(cp == symb+2)
                               yyerror("malformed hex constant");
                       goto ncu;
               }
       if(c < '0' || c > '7')
               goto dc;
       for(;;) {
               if(c >= '0' && c <= '7') {
                       *cp++ = c;
                       c = GETC();
                       continue;
               }
               goto ncu;
       }

dc:
       if(c == '.')
               goto casedot;
       if(c == 'e' || c == 'E')
               goto casee;

ncu:
       *cp = 0;
       if(c == 'U' || c == 'u') {
               c = GETC();
               c1 |= Numuns;
               if(c == 'L' || c == 'l') {
                       c = GETC();
                       c1 |= Numlong;
               }
       } else
       if(c == 'L' || c == 'l') {
               c = GETC();
               c1 |= Numlong;
               if(c == 'U' || c == 'u') {
                       c = GETC();
                       c1 |= Numuns;
               } else
               if(c == 'L' || c == 'l') {
                       c = GETC();
                       c1 |= Numvlong;
               }
       }
       peekc = c;
       if(c1 & Numvlong) {
               if(FPCHIP) {
                       if(mpatof(symb, &yylval.dval)) {
                               yyerror("overflow in vl constant");
                               yylval.dval = 0;
                       }
                       return LVLCONST;
               }
               if(!fperror) {
                       yyerror("compiler cannot interpret vl constants");
                       fperror = 1;
               }
               yylval.lval = 1;
               return LCONST;
       }
       if(mpatol(symb, &yylval.lval))
               yyerror("overflow in integer constant");
       if(tint == types[TSHORT]) {
               /* does it fit in a short */
               if(!ovflo(yylval.lval, TSHORT))
                       goto nret;
               /* does it fit in a ushort */
               if(!ovflo(yylval.lval, TUSHORT)) {
                       if(c1 & (Numuns|Numlong))
                               goto nret;
                       if(c1 & Numdec) {
                               c1 |= Numlong;
                               goto ndiag;
                       }
                       c1 |= Numuns;
                       goto nret;
               }
               /* does it fit in a long */
               if(!ovflo(yylval.lval, TLONG)) {
                       if(c1 & Numlong)
                               goto nret;
                       c1 |= Numlong;
                       goto ndiag;
               }
               /* then a ulong */
               if(c1 & Numlong) {
                       if(c1 & Numuns)
                               goto nret;
                       c1 |= Numuns;
                       if(c1 & Numdec)
                               goto ndiag;
                       goto nret;
               }
               c1 |= Numuns|Numlong;
               goto ndiag;
       }
       /* does it fit in a long */
       if(!ovflo(yylval.lval, TLONG))
               goto nret;
       /* then a ulong */
       if(c1 & Numuns)
               goto nret;
       c1 |= Numuns;
       if(c1 & Numdec)
               goto ndiag;
       goto nret;

casedot:
       for(;;) {
               *cp++ = c;
               c = GETC();
               if(!isdigit(c))
                       break;
       }
       if(c != 'e' && c != 'E')
               goto caseout;

casee:
       *cp++ = 'e';
       c = GETC();
       if(c == '+' || c == '-') {
               *cp++ = c;
               c = GETC();
       }
       if(!isdigit(c))
               yyerror("malformed fp constant exponent");
       while(isdigit(c)) {
               *cp++ = c;
               c = GETC();
       }

caseout:
       if(c == 'L' || c == 'l') {
               c = GETC();
               c1 |= Numlong;
       } else
       if(c == 'F' || c == 'f') {
               c = GETC();
               c1 |= Numflt;
       }
       *cp = 0;
       peekc = c;
       if(FPCHIP) {
               if(mpatof(symb, &yylval.dval)) {
                       yyerror("overflow in float constant");
                       yylval.dval = 0;
               }
               if(c1 & Numflt)
                       return LFCONST;
               return LDCONST;
       }
       if(!fperror) {
               yyerror("compiler cannot interpret fp constants");
               fperror = 1;
       }
       yylval.lval = 1;
       return LCONST;

ndiag:
       nearln = lineno;
       warn(Z, "constant promotion");

nret:
       if(c1 & Numlong) {
               if(c1 & Numuns)
                       return LULCONST;
               return LLCONST;
       }
       if(c1 & Numuns)
               return LUCONST;
       return LCONST;
}

int
getc(void)
{
       int c;

       if(peekc != IGN) {
               c = peekc;
               peekc = IGN;
       } else
               c = GETC();
       if(c == '\n')
               lineno++;
       if(c == EOF) {
               yyerror("End of file");
               errorexit();
       }
       return c;
}

long
getr(void)
{
       int c, i;
       char str[UTFmax+1];
       Rune rune;


       c = getc();
       if(c < Runeself)
               return c;
       i = 0;
       str[i++] = c;

loop:
       c = getc();
       str[i++] = c;
       if(!fullrune(str, i))
               goto loop;
       c = chartorune(&rune, str);
       if(rune == Runeerror && c == 1) {
               nearln = lineno;
               diag(Z, "illegal rune in string");
               for(c=0; c<i; c++)
                       print(" %.2x", *(uchar*)(str+c));
               print("\n");
       }
       return rune;
}

int
getnsc(void)
{
       int c;

       if(peekc != IGN) {
               c = peekc;
               peekc = IGN;
       } else
               c = GETC();
       for(;;) {
               if(!isspace(c))
                       return c;
               if(c == '\n') {
                       lineno++;
                       return c;
               }
               c = GETC();
       }
       return 0;
}

void
unget(int c)
{

       peekc = c;
       if(c == '\n')
               lineno--;
}

long
escchar(long e, int longflg, int escflg)
{
       long c, l;
       int i;

loop:
       c = getr();
       if(c == '\n') {
               yyerror("newline in string");
               return EOF;
       }
       if(c != '\\') {
               if(c == e)
                       c = EOF;
               return c;
       }
       c = getr();
       if(c == 'x') {
               /*
                * note this is not ansi,
                * supposed to only accept 2 hex
                */
               i = 2;
               if(longflg)
                       i = 4;
               l = 0;
               for(; i>0; i--) {
                       c = getc();
                       if(c >= '0' && c <= '7') {
                               l = l*16 + c-'0';
                               continue;
                       }
                       if(c >= 'a' && c <= 'f') {
                               l = l*16 + c-'a' + 10;
                               continue;
                       }
                       if(c >= 'A' && c <= 'F') {
                               l = l*16 + c-'A' + 10;
                               continue;
                       }
                       unget(c);
                       break;
               }
               if(escflg)
                       l |= ESC;
               return l;
       }
       if(c >= '0' && c <= '7') {
               /*
                * note this is not ansi,
                * supposed to only accept 3 oct
                */
               i = 2;
               if(longflg)
                       i = 5;
               l = c - '0';
               for(; i>0; i--) {
                       c = getc();
                       if(c >= '0' && c <= '7') {
                               l = l*8 + c-'0';
                               continue;
                       }
                       unget(c);
               }
               if(escflg)
                       l |= ESC;
               return l;
       }
       switch(c)
       {
       case '\n':      goto loop;
       case 'n':       return '\n';
       case 't':       return '\t';
       case 'b':       return '\b';
       case 'r':       return '\r';
       case 'f':       return '\f';
       case 'a':       return '\a';
       case 'v':       return '\v';
       }
       return c;
}

struct
{
       char    *name;
       ushort  lexical;
} itab[] =
{
       "auto",         LAUTO,
       "break",        LBREAK,
       "case",         LCASE,
       "char",         LCHAR,
       "const",        LCONSTNT,
       "continue",     LCONTINUE,
       "default",      LDEFAULT,
       "do",           LDO,
       "double",       LDOUBLE,
       "else",         LELSE,
       "enum",         LENUM,
       "extern",       LEXTERN,
       "float",        LFLOAT,
       "for",          LFOR,
       "goto",         LGOTO,
       "if",           LIF,
       "int",          LINT,
       "long",         LLONG,
       "register",     LREGISTER,
       "return",       LRETURN,
       "SET",          LSET,
       "short",        LSHORT,
       "signed",       LSIGNED,
       "sizeof",       LSIZEOF,
       "static",       LSTATIC,
       "struct",       LSTRUCT,
       "switch",       LSWITCH,
       "typedef",      LTYPEDEF,
       "union",        LUNION,
       "unsigned",     LUNSIGNED,
       "USED",         LUSED,
       "void",         LVOID,
       "volatile",     LVOLATILE,
       "while",        LWHILE,
       0
};

void
cinit(void)
{
       Sym *s;
       int i;
       Type *t;

       nerrors = 0;
       lineno = 1;
       iostack = I;
       iofree = I;
       peekc = IGN;
       nhunk = 0;

       for(i=0; i<NHASH; i++)
               hash[i] = S;
       for(i=0; itab[i].name; i++) {
               s = slookup(itab[i].name);
               s->lexical = itab[i].lexical;
       }
       blockno = 0;
       autobn = 0;
       autoffset = 0;

       types[TXXX] = T;
       types[TCHAR] = typ(TCHAR, T);
       types[TUCHAR] = typ(TUCHAR, T);
       types[TSHORT] = typ(TSHORT, T);
       types[TUSHORT] = typ(TUSHORT, T);
       types[TLONG] = typ(TLONG, T);
       types[TULONG] = typ(TULONG, T);
       types[TVLONG] = typ(TVLONG, T);
       types[TFLOAT] = typ(TFLOAT, T);
       types[TDOUBLE] = typ(TDOUBLE, T);
       types[TVOID] = typ(TVOID, T);
       types[TENUM] = typ(TENUM, T);
       types[TFUNC] = typ(TFUNC, types[TLONG]);
       types[TIND] = typ(TIND, types[TVOID]);

       tint = types[TLONG];                    /* assuming long, ginit knows */
       tuint = types[TULONG];

       t = typ(TARRAY, types[TCHAR]);
       t->width = 0;
       symstring = slookup(".string");
       symstring->class = CSTATIC;
       symstring->type = t;

       t = typ(TARRAY, types[TCHAR]);
       t->width = 0;

       nodproto = new(OPROTO, Z, Z);
       dclstack = D;

       fmtinstall('O', Oconv);
       fmtinstall('T', Tconv);
       fmtinstall('F', FNconv);
       fmtinstall('L', Lconv);
       fmtinstall('Q', Qconv);
       fmtinstall('|', VBconv);
}

int
filbuf(void)
{
       Io *i;

loop:
       i = iostack;
       if(i == I)
               return EOF;
       if(i->f < 0)
               goto pop;
       fi.c = read(i->f, i->b, BUFSIZ) - 1;
       if(fi.c < 0) {
               close(i->f);
               while((ulong)hunk & 3) {
                       hunk++;
                       nhunk--;
               }
               linehist(0, 0);
               goto pop;
       }
       fi.p = i->b + 1;
       return i->b[0] & 0xff;

pop:
       iostack = i->link;
       i->link = iofree;
       iofree = i;
       i = iostack;
       if(i == I)
               return EOF;
       fi.p = i->p;
       fi.c = i->c;
       if(--fi.c < 0)
               goto loop;
       return *fi.p++ & 0xff;
}

int
Oconv(void *o, Fconv *fp)
{
       int a;

       a = *(int*)o;
       if(a < OXXX || a > OEND)
               strconv(xOconv(a), fp);
       else
               strconv(onames[a], fp);
       return sizeof(a);
}

int
Lconv(void *o, Fconv *fp)
{
       char str[STRINGSZ], s[STRINGSZ];
       Hist *h;
       struct
       {
               Hist*   incl;   /* start of this include file */
               long    idel;   /* delta line number to apply to include */
               Hist*   line;   /* start of this #line directive */
               long    ldel;   /* delta line number to apply to #line */
       } a[HISTSZ];
       long l, d;
       int i, n;

       l = *(long*)o;
       n = 0;
       for(h = hist; h != H; h = h->link) {
               if(l < h->line)
                       break;
               if(h->name) {
                       if(h->offset != 0) {            /* #line directive, not #pragma */
                               if(n > 0 && n < HISTSZ && h->offset >= 0) {
                                       a[n-1].line = h;
                                       a[n-1].ldel = h->line - h->offset + 1;
                               }
                       } else {
                               if(n < HISTSZ) {        /* beginning of file */
                                       a[n].incl = h;
                                       a[n].idel = h->line;
                                       a[n].line = 0;
                               }
                               n++;
                       }
                       continue;
               }
               n--;
               if(n > 0 && n < HISTSZ) {
                       d = h->line - a[n].incl->line;
                       a[n-1].ldel += d;
                       a[n-1].idel += d;
               }
       }
       if(n > HISTSZ)
               n = HISTSZ;
       str[0] = 0;
       for(i=n-1; i>=0; i--) {
               if(i != n-1) {
                       if(fp->f3)
                               break;
                       strcat(str, " ");
               }
               if(a[i].line)
                       sprint(s, "%s:%ld[%s:%ld]",
                               a[i].line->name, l-a[i].ldel+1,
                               a[i].incl->name, l-a[i].idel+1);
               else
                       sprint(s, "%s:%ld",
                               a[i].incl->name, l-a[i].idel+1);
               if(strlen(s)+strlen(str) >= STRINGSZ-10)
                       break;
               strcat(str, s);
               l = a[i].incl->line - 1;        /* now print out start of this file */
       }
       if(n == 0)
               strcat(str, "<eof>");
       strconv(str, fp);
       return sizeof(l);
}

int
Tconv(void *o, Fconv *fp)
{
       char str[STRINGSZ], s[STRINGSZ];
       Type *t, *t1;
       int et;

       str[0] = 0;
       for(t = *(Type**)o; t != T; t = t->link) {
               et = t->etype;
               if(str[0])
                       strcat(str, " ");
               sprint(s, "%s", tnames[et]);
               if(strlen(str) + strlen(s) < STRINGSZ)
                       strcat(str, s);
               if(et == TFUNC && (t1 = t->down)) {
                       sprint(s, "(%T", t1);
                       strcat(str, s);
                       while(t1 = t1->down) {
                               sprint(s, ", %T", t1);
                               strcat(str, s);
                       }
                       strcat(str, ")");
               }
               if(et == TARRAY) {
                       sprint(s, "[%ld]", t->width);
                       strcat(str, s);
               }
               if(t->nbits) {
                       sprint(s, " %d:%d", t->shift, t->nbits);
                       if(strlen(str) + strlen(s) < STRINGSZ)
                               strcat(str, s);
               }
               if(typesu[et]) {
                       if(t->tag) {
                               strcat(str, " ");
                               strcat(str, t->tag->name);
                       } else
                               strcat(str, " {}");
                       break;
               }
       }
       strconv(str, fp);
       return sizeof(t);
}

int
FNconv(void *o, Fconv *fp)
{
       char str[STRINGSZ];
       Node *n;

       n = *(Node**)o;
       strcpy(str, "<indirect>");
       if(n != Z && (n->op == ONAME || n->op == ODOT))
               strcpy(str, n->sym->name);
       strconv(str, fp);
       return sizeof(n);
}

int
Qconv(void *o, Fconv *fp)
{
       char str[STRINGSZ], *s;
       long b;
       int i;

       str[0] = 0;
       for(b = *(long*)o; b;) {
               i = bitno(b);
               if(str[0])
                       strcat(str, " ");
               s = qnames[i];
               if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
                       break;
               strcat(str, s);
               b &= ~(1L << i);
       }
       strconv(str, fp);
       return sizeof(b);
}

int
VBconv(void *o, Fconv *fp)
{
       char str[STRINGSZ];
       int i, n, t, pc;
       extern printcol;

       n = *(int*)o;
       pc = printcol;
       i = 0;
       while(pc < n) {
               t = (pc+8) & ~7;
               if(t <= n) {
                       str[i++] = '\t';
                       pc = t;
                       continue;
               }
               str[i++] = ' ';
               pc++;
       }
       str[i] = 0;
       strconv(str, fp);
       return sizeof(n);
}

/*
* fake malloc
*/
void*
malloc(long n)
{
       void *p;

       while(n & 3)
               n++;
       while(nhunk < n)
               gethunk();
       p = hunk;
       nhunk -= n;
       hunk += n;
       return p;
}

void
free(void *p)
{
       USED(p);
}

void*
calloc(long m, long n)
{
       void *p;

       n *= m;
       p = malloc(n);
       memset(p, 0, n);
       return p;
}

void*
realloc(void *p, long n)
{

       USED(p);
       USED(n);
       fprint(2, "realloc called\n");
       abort();
       return 0;
}