#include        "mk.h"

char *infile;
int mkinline;
static int rhead(char *, Word **, Word **, int *, char **);
static char *rbody(Biobuf*);
extern Word *target1;

void
parse(char *f, int fd, int varoverride)
{
       int hline;
       char *body;
       Word *head, *tail;
       int attr, set, pid;
       char *prog, *p;
       int newfd;
       Biobuf in;
       Bufblock *buf;

       if(fd < 0){
               perror(f);
               Exit();
       }
       ipush();
       infile = strdup(f);
       mkinline = 1;
       Binit(&in, fd, OREAD);
       buf = newbuf();
       while(assline(&in, buf)){
               hline = mkinline;
               switch(rhead(buf->start, &head, &tail, &attr, &prog))
               {
               case '<':
                       p = wtos(tail, ' ');
                       if(*p == 0){
                               SYNERR(-1);
                               fprint(2, "missing include file name\n");
                               Exit();
                       }
                       newfd = open(p, OREAD);
                       if(newfd < 0){
                               fprint(2, "warning: skipping missing include file: ");
                               perror(p);
                       } else
                               parse(p, newfd, 0);
                       break;
               case '|':
                       p = wtos(tail, ' ');
                       if(*p == 0){
                               SYNERR(-1);
                               fprint(2, "missing include program name\n");
                               Exit();
                       }
                       execinit();
                       pid=pipecmd(p, envy, &newfd);
                       if(newfd < 0){
                               fprint(2, "warning: skipping missing program file: ");
                               perror(p);
                       } else
                               parse(p, newfd, 0);
                       while(waitup(-3, &pid) >= 0)
                               ;
                       if(pid != 0){
                               fprint(2, "bad include program status\n");
                               Exit();
                       }
                       break;
               case ':':
                       body = rbody(&in);
                       addrules(head, tail, body, attr, hline, prog);
                       break;
               case '=':
                       if(head->next){
                               SYNERR(-1);
                               fprint(2, "multiple vars on left side of assignment\n");
                               Exit();
                       }
                       if(symlook(head->s, S_OVERRIDE, 0)){
                               set = varoverride;
                       } else {
                               set = 1;
                               if(varoverride)
                                       symlook(head->s, S_OVERRIDE, (void *)"");
                       }
                       if(set){
/*
char *cp;
dumpw("tail", tail);
cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp);
*/
                               setvar(head->s, (void *) tail);
                               symlook(head->s, S_WESET, (void *)"");
                       }
                       if(attr)
                               symlook(head->s, S_NOEXPORT, (void *)"");
                       break;
               default:
                       SYNERR(hline);
                       fprint(2, "expected one of :<=\n");
                       Exit();
                       break;
               }
       }
       close(fd);
       freebuf(buf);
       ipop();
}

void
addrules(Word *head, Word *tail, char *body, int attr, int hline, char *prog)
{
       Word *w;

       assert(/*addrules args*/ head && body);
               /* tuck away first non-meta rule as default target*/
       if(target1 == 0 && !(attr&REGEXP)){
               for(w = head; w; w = w->next)
                       if(charin(w->s, "%&"))
                               break;
               if(w == 0)
                       target1 = wdup(head);
       }
       for(w = head; w; w = w->next)
               addrule(w->s, tail, body, head, attr, hline, prog);
}

static int
rhead(char *line, Word **h, Word **t, int *attr, char **prog)
{
       char *p;
       char *pp;
       int sep;
       Rune r;
       int n;
       Word *w;

       p = charin(line,":=<");
       if(p == 0)
               return('?');
       sep = *p;
       *p++ = 0;
       if(sep == '<' && *p == '|'){
               sep = '|';
               p++;
       }
       *attr = 0;
       *prog = 0;
       if(sep == '='){
               pp = charin(p, termchars);      /* termchars is shell-dependent */
               if (pp && *pp == '=') {
                       while (p != pp) {
                               n = chartorune(&r, p);
                               switch(r)
                               {
                               default:
                                       SYNERR(-1);
                                       fprint(2, "unknown attribute '%c'\n",*p);
                                       Exit();
                               case 'U':
                                       *attr = 1;
                                       break;
                               }
                               p += n;
                       }
                       p++;            /* skip trailing '=' */
               }
       }
       if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){
               while (*p) {
                       n = chartorune(&r, p);
                       if (r == ':')
                               break;
                       p += n;
                       switch(r)
                       {
                       default:
                               SYNERR(-1);
                               fprint(2, "unknown attribute '%c'\n", p[-1]);
                               Exit();
                       case 'D':
                               *attr |= DEL;
                               break;
                       case 'E':
                               *attr |= NOMINUSE;
                               break;
                       case 'n':
                               *attr |= NOVIRT;
                               break;
                       case 'N':
                               *attr |= NOREC;
                               break;
                       case 'P':
                               pp = utfrune(p, ':');
                               if (pp == 0 || *pp == 0)
                                       goto eos;
                               *pp = 0;
                               *prog = strdup(p);
                               *pp = ':';
                               p = pp;
                               break;
                       case 'Q':
                               *attr |= QUIET;
                               break;
                       case 'R':
                               *attr |= REGEXP;
                               break;
                       case 'U':
                               *attr |= UPD;
                               break;
                       case 'V':
                               *attr |= VIR;
                               break;
                       }
               }
               if (*p++ != ':') {
       eos:
                       SYNERR(-1);
                       fprint(2, "missing trailing :\n");
                       Exit();
               }
       }
       *h = w = stow(line);
       if(*w->s == 0 && sep != '<' && sep != '|') {
               SYNERR(mkinline-1);
               fprint(2, "no var on left side of assignment/rule\n");
               Exit();
       }
       *t = stow(p);
       return(sep);
}

static char *
rbody(Biobuf *in)
{
       Bufblock *buf;
       int r, lastr;
       char *p;

       lastr = '\n';
       buf = newbuf();
       for(;;){
               r = Bgetrune(in);
               if (r < 0)
                       break;
               if (lastr == '\n') {
                       if (r == '#')
                               rinsert(buf, r);
                       else if (r != ' ' && r != '\t') {
                               Bungetrune(in);
                               break;
                       }
               } else
                       rinsert(buf, r);
               lastr = r;
               if (r == '\n')
                       mkinline++;
       }
       insert(buf, 0);
       p = strdup(buf->start);
       freebuf(buf);
       return p;
}

struct input
{
       char *file;
       int line;
       struct input *next;
};
static struct input *inputs = 0;

void
ipush(void)
{
       struct input *in, *me;

       me = (struct input *)Malloc(sizeof(*me));
       me->file = infile;
       me->line = mkinline;
       me->next = 0;
       if(inputs == 0)
               inputs = me;
       else {
               for(in = inputs; in->next; )
                       in = in->next;
               in->next = me;
       }
}

void
ipop(void)
{
       struct input *in, *me;

       assert(/*pop input list*/ inputs != 0);
       if(inputs->next == 0){
               me = inputs;
               inputs = 0;
       } else {
               for(in = inputs; in->next->next; )
                       in = in->next;
               me = in->next;
               in->next = 0;
       }
       infile = me->file;
       mkinline = me->line;
       free((char *)me);
}