#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
#include "error.h"

// Convert globbish pattern to regular expression
// The wildcards are
//
//      *       any non-slash characters
//      ...     any characters including /
//      ?       any single character except /
//      [a-z]   character class
//      [~a-z]  negated character class
//

Reprog*
glob2regexp(char *glob)
{
       char *s, *p, *w;
       Reprog *re;
       int boe;        // beginning of path element

       s = malloc(20*(strlen(glob)+1));
       if(s == nil)
               return nil;
       w = s;
       boe = 1;
       *w++ = '^';
       *w++ = '(';
       for(p=glob; *p; p++){
               if(p[0] == '.' && p[1] == '.' && p[2] == '.'){
                       strcpy(w, ".*");
                       w += strlen(w);
                       p += 3-1;
                       boe = 0;
                       continue;
               }
               if(p[0] == '*'){
                       if(boe)
                               strcpy(w, "([^./][^/]*)?");
                       else
                               strcpy(w, "[^/]*");
                       w += strlen(w);
                       boe = 0;
                       continue;
               }
               if(p[0] == '?'){
                       if(boe)
                               strcpy(w, "[^./]");
                       else
                               strcpy(w, "[^/]");
                       w += strlen(w);
                       boe = 0;
                       continue;
               }
               if(p[0] == '['){
                       *w++ = '[';
                       if(*++p == '~'){
                               *w++ = '^';
                               p++;
                       }
                       while(*p != ']'){
                               if(*p == '/')
                                       goto syntax;
                               if(*p == '^' || *p == '\\')
                                       *w++ = '\\';
                               *w++ = *p++;
                       }
                       *w++ = ']';
                       boe = 0;
                       continue;
               }
               if(strchr("()|^$[]*?+\\.", *p)){
                       *w++ = '\\';
                       *w++ = *p;
                       boe = 0;
                       continue;
               }
               if(*p == '/'){
                       *w++ = '/';
                       boe = 1;
                       continue;
               }
               *w++ = *p;
               boe = 0;
               continue;
       }
       *w++ = ')';
       *w++ = '$';
       *w = 0;

       re = regcomp(s);
       if(re == nil){
       syntax:
               free(s);
               werrstr("glob syntax error");
               return nil;
       }
       free(s);
       return re;
}

typedef struct Pattern Pattern;
struct Pattern
{
       Reprog *re;
       int include;
};

Pattern *pattern;
int npattern;

void
loadexcludefile(char *file)
{
       Biobuf *b;
       char *p, *q;
       int n, inc;
       Reprog *re;

       if((b = Bopen(file, OREAD)) == nil)
               sysfatal("open %s: %r", file);
       for(n=1; (p=Brdstr(b, '\n', 1)) != nil; free(p), n++){
               q = p+strlen(p);
               while(q > p && isspace((uchar)*(q-1)))
                       *--q = 0;
               switch(p[0]){
               case '\0':
               case '#':
                       continue;
               }

               inc = 0;
               if(strncmp(p, "include ", 8) == 0){
                       inc = 1;
               }else if(strncmp(p, "exclude ", 8) == 0){
                       inc = 0;
               }else
                       sysfatal("%s:%d: line does not begin with include or exclude", file, n);

               if(strchr(p+8, ' '))
                       fprint(2, "%s:%d: warning: space in pattern\n", file, n);

               if((re = glob2regexp(p+8)) == nil)
                       sysfatal("%s:%d: bad glob pattern", file, n);

               pattern = vtrealloc(pattern, (npattern+1)*sizeof pattern[0]);
               pattern[npattern].re = re;
               pattern[npattern].include = inc;
               npattern++;
       }
       Bterm(b);
}

void
excludepattern(char *p)
{
       Reprog *re;

       if((re = glob2regexp(p)) == nil)
               sysfatal("bad glob pattern %s", p);

       pattern = vtrealloc(pattern, (npattern+1)*sizeof pattern[0]);
       pattern[npattern].re = re;
       pattern[npattern].include = 0;
       npattern++;
}

int
includefile(char *file)
{
       Pattern *p, *ep;

       for(p=pattern, ep=p+npattern; p<ep; p++)
               if(regexec(p->re, file, nil, 0))
                       return p->include;
       return 1;
}