#include        "mk.h"

static  Word            *subsub(Word*, char*, char*);
static  Word            *expandvar(char**);
static  Bufblock        *varname(char**);
static  Word            *extractpat(char*, char**, char*, char*);
static  int             submatch(char*, Word*, Word*, int*, char**);
static  Word            *varmatch(char *);

Word *
varsub(char **s)
{
       Bufblock *b;
       Word *w;

       if(**s == '{')          /* either ${name} or ${name: A%B==C%D}*/
               return expandvar(s);

       b = varname(s);
       if(b == 0)
               return 0;

       w = varmatch(b->start);
       freebuf(b);
       return w;
}

/*
*      extract a variable name
*/
static Bufblock*
varname(char **s)
{
       Bufblock *b;
       char *cp;
       Rune r;
       int n;

       b = newbuf();
       cp = *s;
       for(;;){
               n = chartorune(&r, cp);
               if (!WORDCHR(r))
                       break;
               rinsert(b, r);
               cp += n;
       }
       if (b->current == b->start){
               SYNERR(-1);
               fprint(2, "missing variable name <%s>\n", *s);
               freebuf(b);
               return 0;
       }
       *s = cp;
       insert(b, 0);
       return b;
}

static Word*
varmatch(char *name)
{
       Word *w;
       Symtab *sym;

       sym = symlook(name, S_VAR, 0);
       if(sym){
                       /* check for at least one non-NULL value */
               for (w = sym->u.ptr; w; w = w->next)
                       if(w->s && *w->s)
                               return wdup(w);
       }
       return 0;
}

static Word*
expandvar(char **s)
{
       Word *w;
       Bufblock *buf;
       Symtab *sym;
       char *cp, *begin, *end;

       begin = *s;
       (*s)++;                                         /* skip the '{' */
       buf = varname(s);
       if (buf == 0)
               return 0;
       cp = *s;
       if (*cp == '}') {                               /* ${name} variant*/
               (*s)++;                                 /* skip the '}' */
               w = varmatch(buf->start);
               freebuf(buf);
               return w;
       }
       if (*cp != ':') {
               SYNERR(-1);
               fprint(2, "bad variable name <%s>\n", buf->start);
               freebuf(buf);
               return 0;
       }
       cp++;
       end = charin(cp , "}");
       if(end == 0){
               SYNERR(-1);
               fprint(2, "missing '}': %s\n", begin);
               Exit();
       }
       *end = 0;
       *s = end+1;

       sym = symlook(buf->start, S_VAR, 0);
       if(sym == 0 || sym->u.value == 0)
               w = newword(buf->start);
       else
               w = subsub(sym->u.ptr, cp, end);
       freebuf(buf);
       return w;
}

static Word*
extractpat(char *s, char **r, char *term, char *end)
{
       int save;
       char *cp;
       Word *w;

       cp = charin(s, term);
       if(cp){
               *r = cp;
               if(cp == s)
                       return 0;
               save = *cp;
               *cp = 0;
               w = stow(s);
               *cp = save;
       } else {
               *r = end;
               w = stow(s);
       }
       return w;
}

static Word*
subsub(Word *v, char *s, char *end)
{
       int nmid;
       Word *head, *tail, *w, *h;
       Word *a, *b, *c, *d;
       Bufblock *buf;
       char *cp, *enda;

       a = extractpat(s, &cp, "=%&", end);
       b = c = d = 0;
       if(PERCENT(*cp))
               b = extractpat(cp+1, &cp, "=", end);
       if(*cp == '=')
               c = extractpat(cp+1, &cp, "&%", end);
       if(PERCENT(*cp))
               d = stow(cp+1);
       else if(*cp)
               d = stow(cp);

       head = tail = 0;
       buf = newbuf();
       for(; v; v = v->next){
               h = w = 0;
               if(submatch(v->s, a, b, &nmid, &enda)){
                       /* enda points to end of A match in source;
                        * nmid = number of chars between end of A and start of B
                        */
                       if(c){
                               h = w = wdup(c);
                               while(w->next)
                                       w = w->next;
                       }
                       if(PERCENT(*cp) && nmid > 0){
                               if(w){
                                       bufcpy(buf, w->s, strlen(w->s));
                                       bufcpy(buf, enda, nmid);
                                       insert(buf, 0);
                                       free(w->s);
                                       w->s = strdup(buf->start);
                               } else {
                                       bufcpy(buf, enda, nmid);
                                       insert(buf, 0);
                                       h = w = newword(buf->start);
                               }
                               buf->current = buf->start;
                       }
                       if(d && *d->s){
                               if(w){

                                       bufcpy(buf, w->s, strlen(w->s));
                                       bufcpy(buf, d->s, strlen(d->s));
                                       insert(buf, 0);
                                       free(w->s);
                                       w->s = strdup(buf->start);
                                       w->next = wdup(d->next);
                                       while(w->next)
                                               w = w->next;
                                       buf->current = buf->start;
                               } else
                                       h = w = wdup(d);
                       }
               }
               if(w == 0)
                       h = w = newword(v->s);

               if(head == 0)
                       head = h;
               else
                       tail->next = h;
               tail = w;
       }
       freebuf(buf);
       delword(a);
       delword(b);
       delword(c);
       delword(d);
       return head;
}

static int
submatch(char *s, Word *a, Word *b, int *nmid, char **enda)
{
       Word *w;
       int n;
       char *end;

       n = 0;
       for(w = a; w; w = w->next){
               n = strlen(w->s);
               if(strncmp(s, w->s, n) == 0)
                       break;
       }
       if(a && w == 0)         /*  a == NULL matches everything*/
               return 0;

       *enda = s+n;            /* pointer to end a A part match */
       *nmid = strlen(s)-n;    /* size of remainder of source */
       end = *enda+*nmid;
       for(w = b; w; w = w->next){
               n = strlen(w->s);
               if(strcmp(w->s, end-n) == 0){
                       *nmid -= n;
                       break;
               }
       }
       if(b && w == 0)         /* b == NULL matches everything */
               return 0;
       return 1;
}