/*
Things to check:
	strdup
	malloc
	free



*/
/* Deal with filtering a news (or pssibly a mail) item
---------------------------------------------------------------------
Todo:
  Add case sensitive rexp()
---------------------------------------------------------------------

*/
/* Improve to handle \ on end of line as continuation */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>

#include "regex.h"
#ifndef TESTING
#include "group.h"
#endif

#ifdef TESTING
#define FALSE 0
#define TRUE (!FALSE)
#include "dmalloc.h"
#else
#define ISDNEWS 1
#endif


#ifdef ISDNEWS
#include "dnews.h"
#include "lib.h"
/* brian */
#include "feed.h"
#endif

char* the_item = NULL;
int match_wild(char *p,char *text);
static int trace = FALSE;
#define strcmpnc mystrcmpnc
#define strncmpnc mystrncmpnc
int parts_subject(char *subject, char *subj, char *fname, int *parti, int *partmax);
char *ncpy(char *dst, char *src, int len);
int strncmpnc(const char *s1, const char *s2, int len);
int df_concat(char *p[],int np);
int df_attach_add_one(char *bf);
int df_attach_add(char *restofline);

#define DFILTER_MAIN 1
#include "dfilter.h"
#ifndef BFSZ
#define BFSZ 1000
#endif
int run_anymod(Exp *exp);

struct FNS {char *name; int narg; int (*compile)(Exp *e,char *arg1, char *arg2); int (*run)(Exp *exp); } ;
static struct FNS flist[]={
	"constant",1,comp_constant,run_constant,
	"rexp",2,comp_rexp,run_rexp,
	"splitrexp",2,comp_rexp,run_splitrexp,
	"lastrexp",2,comp_rexp,run_lastrexp,
	"rexp_case",2,comp_rexp_case,run_rexp,
	"inpath",1,comp_inpath,run_rexp,
	"match",2,comp_match,run_match,
	"strcmp",2,comp_equals,run_equals,
	"size",0,comp_size,run_size,
	"matchall",2,comp_matchall,run_matchall,
	"matchone",2,comp_matchone,run_matchone,
	"split",2,comp_matchone,run_split,
	"isin",2,comp_isin,run_isin,
	"isinlist",2,comp_isin,run_isinlist,
	"isflag",1,comp_ifflag,run_ifflag,
	"exists",1,comp_exists,run_exists,
	"isbinary",0,comp_size,run_isbinary,
	"isbinaryn",1,comp_isbinaryn,run_isbinaryn,
	"ishtml",0,comp_size,run_ishtml,
	"isencodedtext",0,comp_size,run_isencodedtext,
	"isencodedhtml",0,comp_size,run_isencodedhtml,
	"isencodedurl",0,comp_size,run_isencodedurl,
	"isencodedexe",0,comp_size,run_isencodedexe,
	"isuuencoded",0,comp_size,run_isuuencoded,
	"isbase64",0,comp_size,run_isbase64,
	"isimage",0,comp_size,run_isimage,
	"lines",0,comp_size,run_lines,
	"allmod",1,comp_allmod,run_allmod,
	"anymod",1,comp_allmod,run_anymod,
	"get",1,comp_get,run_get,
	"islocalpost",0,comp_size,run_ispost,
	"attach",1,comp_attach,run_attach,
	"isinc",2,comp_isin,run_isinc,
	NULL,0,NULL,NULL
};

char *clean_str(char *s,char *s2)
{
	char *pos,c;
	int pos2=0;
	
	if(!s2) return s;
	pos=s;
	while(c=*pos) {
		if((c>='A'&&c<='Z')||(c>='a'&&c<='z')||(c>='0'&&c<='9')||c==' ')
			*(s2+(pos2++))=c;
		pos++;
	}
	*(s2+pos2)='\0';
	return s2;
}

char *item_read(char *fname)
{
	static char bf[20000];
	static char fbf[90000];
	FILE *f;
	char *s;
	f = fopen(fname,"r");
	if (f==NULL) {
		perror(fname); abort();
	}
	for (;!feof(f);) {
		if (fgets(bf,1000,f)==NULL) break;
		s = strchr(bf,'\n');
		if (s!=NULL) *s = 0;
		strcat(fbf,bf);
		strcat(fbf,"\r\n");
	}
	fclosep(f);
	return fbf;
}
#ifdef TESTING
int main()
{
	FProg *fprog;
	int r;
	char *item;
	
	char reason[BFSZ];

	item = item_read("test.txt");

	dfilter_emsg(printf);
	dfilter_trace(TRUE);
	fprog = dfilter_compile("filter.rul");
	if (fprog==NULL) return 0;
	r = dfilter_run(fprog,item,strlen(item),reason);
	if (r) printf("ACCEPTED IT (%s)\n",reason);
	else printf("REJECTED IT (%s)\n",reason);
	dfilter_free(fprog);
#ifdef TESTING
	d_check();
#endif
	return 0;
}
#define imsg printf
#define dmsg printf

#endif
enum {O_AND,O_LESSTHAN,O_GREATERTHAN,O_EQUALS};
static char *operators[]={"and","lessthan","greaterthan","equals"};
static char cur_fname[BFSZ];
#define MAX_LINE 10000
#undef mymalloc
#undef myfree
#define mymalloc(x) mymalloc2(x,M_DFILTER)
#define myfree(x) myfree2(x,M_DFILTER)
static int lineno;
static char derr[BFSZ];
static int global_np;
static char **global_p;
static int skip_filteri,skip_fromi,skip_dupi,lastflag;
static int setlast;
static int (*fmsg)(const char *format, ... );
void dfilter_emsg(int (*msgfn)( const char *format, ... ))
{
	fmsg = msgfn;
}
void dfilter_trace(int x)
{
	trace = x;
}
char *gp(int n)
{
	if (n>=global_np) return "(NULL_TOKEN)";
	return global_p[n];
}
FProg *curprog;

FProg *dfilter_compile(char *fname) 
{
	char bf[MAX_LINE];
	char tbf[MAX_LINE];
	static char *p[1000];
	int np;
	FILE *f;
	int xupto;
	char *s;
	FProg *fp;
	Linex *l,*last=NULL;
	char xbf[BFSZ];
	int k;

	lineno = 0;
	fmsg("Compiling %s\n",fname);
	df_var_free();
	strcpy(derr,"No Error");
	f = fopen(fname,"r");
	if (f==NULL) {lprintf(derr,200,"Can't read (%s) (%s)\n",fname,strerror(errno)); return NULL;}
	/* Improve to handle \ on end of line as continuation */
	fp = mymalloc(sizeof(FProg));  memset(fp,0,sizeof(FProg));
	curprog = fp;
	for (;!feof(f);) {
		xupto = 0;
		strcpy(bf,"");
again:		if (fgets(tbf,MAX_LINE-1,f)==NULL) break;
		lineno++;
		s = strchr(tbf,'\n'); if (s!=NULL) *s = 0;
		strcat(bf,tbf);

		k = strspn(tbf," \t");
		if (tbf[k]!='#') {
			if (strlen(bf)<10000) if (tbf[strlen(tbf)-1]=='\\') {
				bf[strlen(bf)-1] = 0;
				goto again;
			}
		}
		strncpy(xbf,bf,100); xbf[100] = 0;
		if (trace) fmsg("dfilter: Compile %.55s%s\n",xbf,(strlen(xbf)>55) ? "..." : "");
		np = df_token(bf,p,1000); global_np = np; global_p = p;
		if (np<0) { fmsg("Terminating quote missing, on line %d,  %.40s\n",lineno,xbf); continue;	}
		if (np<1) continue;
		if (strncmp(gp(0),"#",1)==0) continue;
		
		df_var_replace(p,np); 
		np = df_concat(p,np); 
#ifdef TESTING
		{int t;
		printf("Tok: ");
		for (t=0; t<np; t++) printf("{%s} ",gp(t));
		printf("\n");
		}
#endif
		l = NULL;
		if (!df_find_action(&l,p,np,&xupto)) /*{
			fmsg("Found action on it's own exp=%p\n",l->elist);
		} else */
		{
		if (strcmp(gp(0),"else")==0) l = df_comp_else(p,np);
		else {
			if (np<2) {
				fmsg("Invalid statement %.40s\n",gp(0));
				continue;	
			}
			if (strcmp(gp(1),"=")==0) {
				df_comp_assign(p,np);
				continue;
			} else if (strcmp(gp(0),"if")==0) {
				l = df_comp_if(p,np);
				if (l==NULL) goto giveup;
			} else if (strcmp(gp(0),"end")==0) l = df_comp_end(p,np);
			else {
				fmsg("Invalid statement (%.40s)\n",gp(0));
				continue;
			}
		}
		}
		l->lineno = lineno;
		l->src = mystrdup(xbf,SD_FILTER);
		if (fp->lines==NULL) fp->lines = l;
		else last->next = l;
		last = l;
	}	
	fclosep(f);

	skip_filteri = df_flag_findadd("skip_filter");
	skip_fromi = df_flag_findadd("skip_from");
	skip_dupi = df_flag_findadd("skip_dup");
	/* brian - define a special flag/counter "last" that is set to the value of the last function called */
	lastflag=df_flag_findadd("_last");
	setlast=TRUE;

	df_var_free();
	return fp;
giveup:
	fclosep(f);
	df_var_free();
	dfilter_free(fp);
	return NULL;
}
int df_concat(char *p[],int np)
{
	int i;
	static char bf[MAX_LINE];
	char *out=bf;
	int dst;
	dst=0;
	for (i=0; i<(np); ) {
		if (i>=(np-1)) goto rest;
		if (strcmp(p[i+1],"+")==0) {
			strcpy(out,p[i]);
			for (;i<(np-1);) {
				if (strcmp(p[i+1],"+")!=0) break;
				strcat(out,p[i+2]);
				i += 2;
			}
			p[dst++] = out;
			out += strlen(out)+1;
			i += 1;
		} else {
rest:			p[dst++] = p[i++]; 
		}
	}
	return dst;
}
int skip_filter(void)
{
	return df_flag_get(skip_filteri);
}
int skip_from(void)
{
	return df_flag_get(skip_fromi);
}
int skip_dup(void)
{
	return df_flag_get(skip_dupi);
}
enum {A_NULL,A_ACCEPT,A_REJECT,A_THEN,A_ELSE,A_END,A_SETFLAG,A_CLEARFLAG,A_PRINT,A_SET,A_ADD,A_SUB,A_MUL,A_DIV,A_ADDVAR,A_SUBVAR,A_EQ,A_SCORE_ONE,A_SCORE_ALL};
static char *actions[]={"A_NONE","A_ACCEPT","A_REJECT","A_THEN","A_ELSE","A_ENDIF","SETFLAG","CLEARFLAG","PRINT","SET","ADD","SUB","MUL","DIV","ADDVAR","SUBVAR","EQ","SCORE_ONE","SCORE_ALL"};
#define MAX_HEAD 32000
static char *hdrs[MAX_HEADERS];
static int hdrs_fake[MAX_HEADERS];
static char item_head[MAX_HEAD];
static int item_head_len;
static int item_size;
static int item_lines;
static int item_isuuencoded;
static int item_isbinary;
static int item_ishtml;
static int item_isbase64;
static int item_isimage;

static int item_isencodedhtml;
static int item_isencodedtext;
static int item_isencodedurl;
static int item_isencodedexe;
static int item_istext;
static int item_istexthtml;

static int flags[MAX_FLAGS];
void df_setflag(int p, int t)
{
	if ((p<0) || (p>=MAX_FLAGS)) return;
	flags[p] = t;
}

void df_addflag(int p, int t)
{
    if ((p<0) || (p>=MAX_FLAGS)) return;
    flags[p] += t;
}

void df_subflag(int p, int t)
{
    if ((p<0) || (p>=MAX_FLAGS)) return;
    flags[p] -= t;
}

void df_mulflag(int p, int t)
{
    if ((p<0) || (p>=MAX_FLAGS)) return;
    flags[p] *= t;
}
void df_divflag(int p, int t)
{
    if ((p<0) || (p>=MAX_FLAGS)) return;
    flags[p] /= t;
}
void df_eqflag(int p, int t)
{
	/* p=destination t=source */
    if ((p<0) || (p>=MAX_FLAGS)) return;
    if ((t<0) || (t>=MAX_FLAGS)) return;
    flags[p] = flags[t];
}
void df_addvarflag(int p, int t)
{
	/* p=destination t=source */
    if ((p<0) || (p>=MAX_FLAGS)) return;
    if ((t<0) || (t>=MAX_FLAGS)) return;
    flags[p] += flags[t];
}
void df_subvarflag(int p, int t)
{
	/* p=destination t=source */
    if ((p<0) || (p>=MAX_FLAGS)) return;
    if ((t<0) || (t>=MAX_FLAGS)) return;
    flags[p] -= flags[t];
}

void df_scoreoneflag(int p)
{
    if ((p<0) || (p>=MAX_FLAGS)) return;
#ifdef TESTING
    flags[p] += 999; /* brian add score_one function call here */
#else
    if (the_item==NULL) { imsg("score: scoreone - the item pointer was null\n"); return; }
    flags[p] += score_one(the_item,NULL);
#endif 
}

void df_scoreallflag(int p)
{
    if ((p<0) || (p>=MAX_FLAGS)) return;
#ifdef TESTING
    flags[p] += 888; /* brian add score_all function call here */
#else
    if (the_item==NULL) { imsg("score: scoreall - the item pointer was null\n"); return; }
    flags[p] += score_all(the_item);
#endif
}
int df_attach_add(char *restofline)
{
	char tmp[BFSZ];
	char *s;
	char bf[BFSZ];
	ncpy(tmp,restofline,100);
	s = strchr(tmp,'"');
	if (s!=NULL) {
		ncpy(bf,s+1,100);
		s = strchr(bf,'"');
		if (s!=NULL) *s = 0;
		df_attach_add_one(bf);
		return TRUE;
	}
	s = strchr(tmp,'.');
	if (s!=NULL) {
		for (;*s!=0;s++) if (*s==' ') break;
	}
	if (s==NULL) {
		for (s=tmp;*s!=0;s++) if (*s==' ') break;
	}
	if (s!=NULL) *s = 0;
	df_attach_add_one(tmp);
	return TRUE;
}
int df_attach_add_one(char *bf)
{
	if (strlen(cur_fname) + 100 + strlen(bf) > BFSZ) return FALSE;
	if (strlen(cur_fname)!=0) {
		strcat(cur_fname,",");
	}
	strcat(cur_fname,bf);
	return TRUE;
}
static int nbin;
int df_hdrs_process(char *item)
{
	int lens[MAX_HEADERS];
	int i;
	char longbf[MAX_LINE];
	int n;
	int nlines = 100;
	int slen;
	int t;
	int ispart=FALSE;
	char *hadd;
	char bf[MAX_LINE];
	int upto = 0;
	int gotbegin;
	int lasthead = -1;
	int this_text = FALSE;
	int this_base64 = FALSE;
	int this_html = FALSE;
	int ismime = FALSE;
	the_item = item;
	nbin = 0;
	/* Step thru item and define each header if found in 
		curprog->headers[] 
		Also define 'body' and 'head'
		And 'size'
	*/	
	strcpy(cur_fname,"");
	item_isuuencoded = item_isbinary = item_ishtml = 0;
	item_isbase64 = item_isimage = item_istext = 0;

	item_isencodedhtml = item_isencodedtext = item_isencodedurl = item_isencodedexe = 0;

	item_head_len = 0;

	for (i=0; i<curprog->nheaders; i++) {
		lens[i] = strlen(curprog->headers[i]);
		hdrs_fake[i] = FALSE;
		if (hdrs[i]!=NULL) mystrfree(hdrs[i],SD_FILTER);
		hdrs[i] = NULL;
	}
	hadd = item_head;
	lasthead = -1;
	for (;TRUE;) {
		if (!df_getline(item,&upto,bf,MAX_LINE-100)) goto skip_body;
		if ((bf[0]==' ') || (bf[0]=='\t')) {
			if (lasthead>=0) {
				i = lasthead;
				ncpy(longbf,hdrs[lasthead],MAX_LINE-100);
				strcat(longbf," ");
				if ( (strlen(longbf)+strlen(bf)) < (MAX_LINE-100) ) {
					strcat(longbf,bf);
					mystrfree(hdrs[i],SD_FILTER);
					hdrs[i] = mystrdup(longbf,SD_FILTER);
				}
			}
		} else {
			lasthead = -1;
		}
		for (i=0; i<curprog->nheaders; i++) {
			if (strncmpnc(bf,curprog->headers[i],lens[i])==0) {
				if (hdrs[i]!=NULL) mystrfree(hdrs[i],SD_FILTER);
				hdrs[i] = mystrdup(bf+lens[i]+2,SD_FILTER);
				lasthead = i;
				break;
			}
		}
		if (strncmpnc(bf,"Content-Transfer-Encoding: base64",33)==0) {item_isbase64 = TRUE; };
		if (strncmpnc(bf,"Content-Type: image",19)==0) {item_isimage = TRUE; };
		if (strncmpnc(bf,"Content-Type: text/html",23)==0) {item_ishtml = this_html = TRUE; };
		if (strncmpnc(bf,"Content-Type: text",18)==0) {item_istext = this_text = TRUE;};
		if (strncmpnc(bf,"Content",7)==0 || bf[0]==' ' || bf[0]=='\t') {
    		  if (strstr(bf,"name=")!=NULL) {
		    	char *s1;
			s1 = strstr(bf,"name=");
    
			if ((s1==bf) || s1[-1]==' ' || s1[-1]=='\t' || s1[-1]==';' || s1[-1]=='e') {
    			    df_attach_add(s1+5);
			}
		  }
		}
		
		if (strncmpnc(bf,"Subject: ",9)==0) {
			int tmp3,tmp4;
			char tmp1[BFSZ],tmp2[BFSZ];
			ispart = parts_subject(bf+9, tmp1,tmp2,&tmp3,&tmp4);
		}
		item_isencodedtext = item_isbase64 && this_text; 
		item_isencodedhtml = item_isbase64 && this_html;
		
		if (strcmp(bf,"")==0) break;	
		n = strlen(bf);
		if (hadd-item_head+n+1000>MAX_HEAD) continue;
		strcpy(hadd,bf); strcat(hadd,"\n");
		hadd+= n+1;
	}
	this_text = this_html = FALSE;
	for (i=0; i<curprog->nheaders; i++) {
		if (strcmp("body",curprog->headers[i])==0) {
			hdrs[i] = item + upto;
			hdrs_fake[i] = TRUE;
		}
	}
	
	gotbegin = FALSE;
	if (ispart) {
		gotbegin = TRUE;
		item_isuuencoded = TRUE;
	}

	nlines = atoi(config_get(C_DFILTER_BINARY_LINES));
	if (nlines==0) nlines = 100;
	for (t=0; t<nlines; t++) {
		if (!df_getline(item,&upto,bf,MAX_LINE-100)) goto skip_body;
		slen = strlen(bf);
		/* Is it uuencoded data */
		if (strncmpnc(bf,"=ybegin",7)==0) {
			item_isbinary = TRUE;
			nbin += 30;
		}
		if (strncmpnc(bf,"begin ",6)==0) {
			gotbegin = TRUE;
			/* Allow signatures the outlook puts all over the place */
			if (strstr(bf,".vcf")!=NULL) gotbegin = FALSE;
			strlwr(bf);
			if (strstr(bf,".jpg")!=NULL) item_isimage = TRUE;
			if (strstr(bf,".jpeg")!=NULL) item_isimage = TRUE;
			if (strstr(bf,".bmp")!=NULL) item_isimage = TRUE;
			if (strstr(bf,".tif")!=NULL) item_isimage = TRUE;
			if (strstr(bf,".gif")!=NULL) item_isimage = TRUE;

			if (strstr(bf,".txt")!=NULL) item_isencodedtext = TRUE;
			if (strstr(bf,".text")!=NULL) item_isencodedtext = TRUE;
			if (strstr(bf,".htm")!=NULL) item_isencodedhtml = TRUE;
			/*if (strstr(bf,".html")!=NULL) item_isencodedhtml = TRUE;*/
			if (strstr(bf,".url")!=NULL) item_isencodedurl = TRUE;
			if (strstr(bf,".exe")!=NULL) item_isencodedexe = TRUE;
			df_attach_add(bf+6);
		}
		if (gotbegin) {
			if (bf[0]=='M') if (slen==61) {
				item_isuuencoded = TRUE;
				nbin++;
			}
		}
		if (item_isbase64) {
                  if (slen>40) if (strchr(bf,' ')==NULL) nbin++;
		}

		/* Does it look like html */		
		strlwr(bf);
		if (t<3) if (strstr(bf,"<html>")!=NULL) item_ishtml = TRUE;  
		
		/* Does it look like base64 stuff and is it an image */
		if (strncmpnc(bf,"Content-Transfer-Encoding: base64",33)==0) {item_isbase64 = this_base64 = TRUE; };
		if (strncmpnc(bf,"Content-Type: image",19)==0) {item_isimage = TRUE; };
		if (strncmpnc(bf,"Content-Type: text",18)==0) {item_istext = this_text = TRUE;};
		if (strncmpnc(bf,"Content-Type: text/html",23)==0) {item_ishtml = this_html = TRUE;};
		if (strncmpnc(bf,"Content-Type:",13)==0) {ismime = TRUE;};
		if (strcmp(bf,"")==0)
		{
			item_isencodedtext = this_base64&&this_text;
			item_isencodedhtml = this_base64&&this_html;
			this_base64 = this_text = this_html = FALSE;
		}
		if (ismime) if (strstr(bf,"name=")!=NULL) {
			char *s1;
			s1 = strstr(bf,"name=");
			df_attach_add(s1+5);
		}

	}
skip_body:
	if (item_isuuencoded) item_isbinary = TRUE;
	if (item_isbase64) item_isbinary = TRUE;
	if (item_isimage) item_isbinary = TRUE;
	for (i=0; i<curprog->nheaders; i++) {
		if (strcmp("head",curprog->headers[i])==0) {
			hdrs[i] = item_head;
			hdrs_fake[i] = TRUE;
		}
	}
	for (i=0; i<curprog->nheaders; i++) {
/*		fmsg("Header (%.40s) found (%.40s)\n",curprog->headers[i],hdrs[i]);*/
	}

	for (i=0; i<curprog->nflags; i++) {
		flags[i] = FALSE;
	}

	item_lines = count_lines(item);
	return TRUE;
}
int df_hdrs_exists(int x)
{
	if (hdrs[x]==NULL) return FALSE;
	return TRUE;
}
char *df_hdrs_get(int x)
{
	if (hdrs[x]==NULL) return "";
	return hdrs[x];
}
int df_hdrs_free(void)
{
	int i;
	for (i=0; i<curprog->nheaders; i++) {
		if (!hdrs_fake[i]) if (hdrs[i]!=NULL) mystrfree(hdrs[i],SD_FILTER);
		hdrs[i] = NULL;
	}
	return TRUE;
}

char *str_show(char *s);

int dfilter_sub_header(char *bf,int sz) {
	char end[BFSZ];
	char *s;
	char *e;
	int x;
	int r=FALSE;
	s=strstr(bf,"<hdr:");
	if (s!=NULL) {
		*s=0;
		s+=5;
		e=strchr(s,'>');
		if (e==NULL) return FALSE;
		*e=0;
		e++;
		ncpy(end,e,BFSZ-1);
		x=df_header_find(s);
		if (x==-1) {
			ncat(bf,"<header ",sz);
			ncat(bf,s,sz);
			ncat(bf," was not present in item>",sz);
		}
		else {
			ncat(bf,str_show(df_hdrs_get(x)),sz);
		}
		ncat(bf,end,sz);
		r=TRUE;
	}
	/* variables only exist at compile time - bugger
	s=strstr(bf,"<$");
	if (s!=NULL) {
		*s=0;
		s+=1;
		e=strchr(s,'>');
		if (e==NULL) return r;
		*e=0;
		e++;
		ncpy(end,e,BFSZ-1);
		emsg("***** df_var_get(\"%s\")\n",s);
		e=df_var_get(s);
		if (e!=NULL) emsg("***** returned (%s)\n",e);
		else emsg ("***** was NULL\n");
		emsg("'%s' '%s'\n",df_var_get(s),df_var_get(s+1));
		if (e!=NULL) ncat(bf,str_show(e),sz);
		ncat(bf,end,sz);
		r=TRUE;
	}
	*/
	return r;
}

int dfilter_run(FProg *fp, char *item, int len,char *reason)
{
	Linex *l;
	int r,r1,r2;
	int op;
	int notin = 0;
	Exp *e;
	strcpy(reason,"No Rule Applied");
	curprog = fp;
	item_size = len;
//printf("item len %d\n",len);
	stat_start(X_FHDRS);
	df_hdrs_process(item);
	stat_end(X_FHDRS);
	for (l=fp->lines;l!=NULL;l=l->next) {
/*fmsg("drun:%d %saction=%s Line: %.40s...\n",notin,notin>0 ? "SKIP " : "",actions[l->action],l->src);*/
		if (trace) if (strlen(l->src) < 1000) 
			fmsg("drun: %saction=%s Line: %.40s...\n",notin>0 ? "SKIP " : "",actions[l->action],l->src);
		if (notin>0) {
			if (l->action==A_THEN) notin++;	
			if (l->action==A_END) {notin--; continue;}
			if (notin==1) if (l->action==A_ELSE) {notin--; continue;}
			continue;
		} else {
			if (l->action==A_ELSE) {notin++; continue;}
		}
		e = l->elist;
		if (e==NULL) {
			goto doit;
		}
		for (; e!=NULL; e=e->next) {
			r = FALSE;
#ifdef TESTING
			if (trace) fmsg("dfilter: %s%s header:%d p2=%s op=(%.40s)\n"
				,e->isnot ? "!" : "",flist[e->fn].name,e->header,e->p2,operators[e->binop]);
#endif
			if (e->fn>=0) r = (*flist[e->fn].run)(e);
			if (e->isnot) r = !r;
			if ((setlast)&&(flist[e->fn].run!=run_constant)) df_setflag(lastflag,r); /*brian*/
			if (trace) fmsg("drun: Result=%d\n",r);
			if (e->binop!=O_AND) if (e->next!=NULL) {
				op = e->binop;
				e = e->next;
				r1 = r;
				if (e->fn>=0) r2 = (*flist[e->fn].run)(e);
				if (e->isnot) r2 = !r2;
				if ((setlast)&&(flist[e->fn].run!=run_constant)) df_setflag(lastflag,r2); /*brian*/
				if (trace) fmsg("drun: Result %d %s %d\n",r1,operators[op],r2);
				r = 0;
				if (op==O_GREATERTHAN) {if (r1>r2) r = 1;}
				else if (op==O_LESSTHAN) {if (r1<r2) r = 1;}
				else if (op==O_EQUALS) {if (r1==r2) r = 1;}
			}
			if (l->action!=A_THEN) if (e->next!=NULL) if (!r) break; /* Next Line*/
			if (l->action==A_THEN) {
				if (!r) {
					notin++;	
					break;
				}
			} else if (r && (e->next==NULL)) {
doit:			if (l->action==A_ACCEPT) {
					lprintf(reason,BFSZ-1,"line %d: %.200s",l->lineno,l->reason);
					dfilter_sub_header(reason,BFSZ-1);
					goto return_true;
				}
				if (l->action==A_REJECT) {
					lprintf(reason,BFSZ-1,"line %d: %.200s",l->lineno,l->reason);
					dfilter_sub_header(reason,BFSZ-1);
					goto return_false;
				}
				if (l->action==A_PRINT) {
					fmsg("PRINT line %d:%s\n",l->lineno,l->reason); 
				}
				if (l->action==A_SETFLAG) df_setflag(l->p1,TRUE);
				if (l->action==A_CLEARFLAG) df_setflag(l->p1,FALSE);
				if (l->action==A_SET) df_setflag(l->p1,l->p2);
				if (l->action==A_ADD) df_addflag(l->p1,l->p2);
				if (l->action==A_SUB) df_subflag(l->p1,l->p2);
				if (l->action==A_MUL) df_mulflag(l->p1,l->p2);
				if (l->action==A_DIV) df_divflag(l->p1,l->p2);
				if (l->action==A_ADDVAR) df_addvarflag(l->p1,l->p2);
				if (l->action==A_SUBVAR) df_subvarflag(l->p1,l->p2);
				if (l->action==A_EQ) df_eqflag(l->p1,l->p2);
				if (l->action==A_SCORE_ONE) df_scoreoneflag(l->p1);
				if (l->action==A_SCORE_ALL) df_scoreallflag(l->p1);
			}
			if (e==NULL) break;
		}
	}
	if (trace) fmsg("dfilter: No rules applied, so accepting\n");
return_true:
	df_hdrs_free();
	return TRUE;
return_false:
	df_hdrs_free();
	return FALSE;
}

void dline_free(Linex *l)
{
	Exp *e,*enext;
	for (e = l->elist; e!=NULL; e=enext) {
			enext = e->next;
			if (e->p2!=NULL) mystrfree(e->p2,SD_FILTER);
			if (e->mask!=NULL) regfree((regex_t *) e->mask);
			if (e->mask!=NULL) mystrfree(e->mask,SD_FILTER);
			if (e->reg!=NULL) regfree((regex_t *) e->reg);
			if (e->reg!=NULL) myfree(e->reg);
			myfree(e);
	}
}

void dfilter_free(FProg *fp)
{
	
	int i;		
	Linex *l,*lnext;
	for (l=fp->lines;l!=NULL;l=lnext) {
		lnext = l->next;

		dline_free(l);
		if (l->src!=NULL) mystrfree(l->src,SD_FILTER);
		if (l->reason!=NULL) mystrfree(l->reason,SD_FILTER);
		myfree(l);
	}

	for (i=0; i<fp->nheaders; i++) {
		mystrfree(fp->headers[i],SD_FILTER);
	}
	fp->nheaders = 0;

	for (i=0; i<fp->nflags; i++) {
		myfree(fp->flags[i]);
	}
	fp->nflags = 0;
	myfree(fp);
}
char *dfilter_error(void)
{
	return derr;
}


/* Handle   $a = "xxx" + "yyy" */
int  df_comp_assign(char *p[], int np)
{
	char bf[MAX_LINE];
	int i;
	int len=0;
	df_var_replace(p,np);
	for (i=2; i<np; i+=2) {
		if (len+strlen(gp(i))+100>MAX_LINE) {
			fmsg("dfilter: Line too long in assign\n");
			return FALSE;
		}
		strcpy(bf+len,gp(i));
		len += strlen(gp(i));
		if (np>=i+2) if (strcmp(gp(i+1),"+")!=0) {
			fmsg("dfilter: Missing '+' in assignment\n");
			return FALSE;
		}
	}
	df_var_set(gp(0),bf);	
	return TRUE;

}
typedef struct PAIRS {char *var; char *val; struct PAIRS *next;} Pairs;
static Pairs *vlist;
void df_var_set(char *var, char *val)
{
	Pairs *v;
	if (strncmp(var,"$",1)==0) { 
	    for (v=vlist;v!=NULL;v=v->next) if (strcmp(var,v->var)==0) {
		    fmsg("Duplicate definition of variable (%s) line %d\n",var,lineno);
		    mystrfree(v->val,SD_FILTER);
		    v->val = mystrdup(val,SD_FILTER);
		    return;
		}
	}
	else
	{
		fmsg("Variable name must begin with $ (%s) line %d\n",var,lineno);
		return;
	}
	v = mymalloc(sizeof(Pairs));
	v->next = vlist;
	vlist = v;
	v->var = mystrdup(var,SD_FILTER);
	v->val = mystrdup(val,SD_FILTER);
}

void df_var_free(void)
{
	Pairs *v,*vnext;
	for (v=vlist;v!=NULL;v=vnext) {
		vnext = v->next;
		mystrfree(v->val,SD_FILTER);
		mystrfree(v->var,SD_FILTER);
		myfree(v);
	}
	vlist = NULL;		
}
char *df_var_get(char *var)
{
	Pairs *v;
	for (v=vlist;v!=NULL;v=v->next) {
		if (strcmp(var,v->var)==0) {
			return v->val;
		}
	}
	return NULL;
}
int df_var_replace(char *p[], int np)
{
	int i;
	char *v;
	for (i=1; i<np; i++) {
		if (strncmp(gp(i),"$",1)==0) {
			v = df_var_get(gp(i));
			if (v==NULL) {
				fmsg("dfilter: Undefined variable, line %d variable %s\n",lineno,gp(i));
			} else p[i] = v;
		}
	}
	return TRUE;
}
/*
Process if to, Replace variables 
	if rexp ( subject , [Ss][Ee][Xx] ) and !isbinary() reject Naughty Subject 

if exp [and exp...] action  


------------------------------------------------------------------------
if !match("nntp-posting-host",$local_hosts) then
	if rexp("subject",$bad_subjects) reject "Naughty Subject" 
else
	if rexp("subject","[Ss][Ee][Xx]") and match("domain","*.sex.*") reject "Naughty Subject in non naughty group"
	if inpath($bad_cancel_paths) reject "Came from spam domain"

------------------------------------------------------------------------
*/

Linex *df_comp_if (char *p[], int np)
{
	int upto = 1;
	Exp *e,*elast=NULL;
	Linex *eline;
	df_var_replace(p,np);
	eline = mymalloc(sizeof(Linex));
	memset(eline,0,sizeof(Linex));
	if (strcmp(gp(upto),"(")!=0) {
		fmsg("If must be followed by left bracket not (%.40s)\n",gp(upto));
		return eline;
	}	
	upto++;
	for (;upto<np;) {
		e = df_comp_exp(p,np,&upto);
		if (e==NULL) {
			dline_free(eline);
			return NULL;
		}
		e->binop = O_AND;
		if (e==NULL) break;
		if (eline->elist==NULL) eline->elist = e;
		else elast->next = e;
		elast = e;
		/* Expecting right bracket or operator <>= */
		if (strcmp(gp(upto),"<")==0) {
			e->binop = O_LESSTHAN;
		} else if (strcmp(gp(upto),">")==0) {
			e->binop = O_GREATERTHAN;
		} else if (strcmp(gp(upto),"=")==0) {
			e->binop = O_EQUALS;
		} else if (strcmp(gp(upto),")")==0) {
			upto++;
			if (strcmp(gp(upto),"and")==0) {
				upto++;
				if (strcmp(gp(upto),"(")!=0) {
					fmsg("If must be followed by left bracket not (%.40s)\n",gp(upto));
					return eline;
				}	
			} else break;
		} else {
			fmsg("If exp must be followed by right bracket not (%.40s)\n",gp(upto));
			return eline;
		}
		upto++;
	}
	if (upto>=np) {
		fmsg("Expecting action, e.g. reject,accept,then..\n");
	}
	if (!df_find_action(&eline,p,np,&upto)) {
		fmsg("Unknown action (%.40s)\n",gp(upto));	
	}
	return eline;
}
Linex *df_comp_else(char *p[], int np)
{
	int upto = 1;
	Linex *eline;
	eline = mymalloc(sizeof(Linex));
	memset(eline,0,sizeof(Linex));
	eline->action = A_ELSE;
	return eline;
}
Linex *df_comp_end(char *p[], int np)
{
	int upto = 1;
	Linex *eline;
	eline = mymalloc(sizeof(Linex));
	memset(eline,0,sizeof(Linex));
	eline->action = A_END;
	return eline;
}
/*
! match ( nntp-posting-host , x.y,z,a.b.c )
rexp ( subject , [Ss][Ee][Xx] )
inpath ( xxx,mci )
typedef struct EXP {int fn, int header, int isnot; char *p2} Exp;
*/

Exp *df_comp_exp(char *p[], int np, int *upto)
{
	int u = *upto;
	int start=*upto;
	int f,r;
	Exp *e;

	if (u>=np) {
		fmsg("No expression on line %d\n",lineno);
		return NULL;
	}
	e = mymalloc(sizeof(Exp));
	memset(e,0,sizeof(Exp));
	e->fn = -1;
	if (strcmp(gp(u),"!")==0) {
		u++;
		e->isnot = TRUE;
	}
	if (u>=np) goto failed;
/*

rexp ( subject , [Ss][Ee][Xx] )
inpath ( xxx,mci )

*/
	for (f = 0; flist[f].name!=NULL; f++) {
		if (strcmp(gp(u),flist[f].name)==0) {
			if (flist[f].narg==0) {
				if (strcmp(gp(u+1),"(")!=0) goto failed2;
				if (strcmp(gp(u+2),")")!=0) goto failed2;
			}
			if (flist[f].narg==1) {
				if (strcmp(gp(u+1),"(")!=0) goto failed2;
				if (strcmp(gp(u+3),")")!=0) goto failed2;
			}
			if (flist[f].narg==2) {
				if (strcmp(gp(u+1),"(")!=0) goto failed2;
				if (strcmp(gp(u+3),",")!=0) goto failed2;
				if (strcmp(gp(u+5),")")!=0) goto failed2;
			}
			r = (*flist[f].compile)(e,gp(u+2),gp(u+4));
			e->fn = f;
			if (!r) goto failedc;
			u += 2 + 2*flist[f].narg;
			if (flist[f].narg==0) u++;
			*upto = u;
			return e;
		}

	}
	if (isdigit(*gp(u))||((*gp(u)=='-')&&(*(gp(u)+1)!=0)&&(isdigit(*(gp(u)+1))))) {
		f = 0;
		r = (*flist[f].compile)(e,gp(u),gp(u));
		e->fn = f;
		if (!r) goto failedc;
		u += 1;
		*upto = u;
		return e;
	}
	fmsg("No such function (%.40s) on line %d\n",gp(u),lineno);

failed:
	myfree(e);
	fmsg("Invalid expression on line %d %s...\n",lineno,gp(start));
	return NULL;
failedc:
	myfree(e);
	fmsg("Cant compile expression on line %d %s...\n",lineno,gp(start));
	return NULL;
failed2:
	myfree(e);
	fmsg("Invalid expression braces on line %d %s...\n",lineno,gp(start));
	return NULL;
}
/*-----------------------------------------------------------------*/
/* Compile functions, fill out 'exp' structure */
/* typedef struct EXP {int fn, int header, int isnot; char *p2} Exp;*/
int comp_constant(Exp *exp, char *arg1, char *arg2)
{
	exp->p2 = mystrdup("",SD_FILTER);
	exp->header = atoi(arg1);
	return TRUE;
}
int comp_match(Exp *exp, char *arg1, char *arg2)
{
	exp->p2 = mystrdup(arg2,SD_FILTER);
	exp->header = df_header_findadd(arg1);
	return TRUE;
}
int comp_exists(Exp *exp, char *arg1, char *arg2)
{
	exp->header = df_header_findadd(arg1);
	return TRUE;
}
int comp_allmod(Exp *exp, char *arg1, char *arg2)
{
	exp->header = df_header_findadd(arg1);
	return TRUE;
}
int comp_matchall(Exp *exp, char *arg1, char *arg2)
{
	exp->p2 = mystrdup(arg2,SD_FILTER);
	exp->header = df_header_findadd(arg1);
	return TRUE;
}
int comp_matchone(Exp *exp, char *arg1, char *arg2)
{
	exp->p2 = mystrdup(arg2,SD_FILTER);
	exp->header = df_header_findadd(arg1);
	return TRUE;
}
int comp_attach(Exp *exp, char *arg1, char *arg2)
{
	exp->p2 = mystrdup(arg1,SD_FILTER);
	return TRUE;
}
int comp_isin(Exp *exp, char *arg1, char *arg2)
{
	exp->p2 = mystrdup(arg2,SD_FILTER);
	exp->header = df_header_findadd(arg1);
	return TRUE;
}
int comp_isbinaryn(Exp *exp, char *arg1, char *arg2)
{
	exp->header = atoi(arg1);
	return TRUE;
}
int comp_ifflag(Exp *exp, char *arg1, char *arg2)
{
	exp->header = df_flag_findadd(arg1);
	return TRUE;
}
int comp_get(Exp *exp, char *arg1, char *arg2)
{
	exp->header = df_flag_findadd(arg1);
	return TRUE;
}
int comp_equals(Exp *exp, char *arg1, char *arg2)
{
	exp->p2 = mystrdup(arg2,SD_FILTER);
	exp->header = df_header_findadd(arg1);
	return TRUE;
}
int comp_inpath(Exp *exp, char *arg1, char *arg2)
{
	return FALSE;
}
int comp_rexp(Exp *exp, char *arg1, char *arg2)
{
	regex_t *reg;
	int r;
	char ebf[BFSZ];
	reg = mymalloc(sizeof(regex_t));
	memset(reg,0,sizeof(*reg));
	exp->p2 = mystrdup(arg2,SD_FILTER);
	exp->header = df_header_findadd(arg1);
	r = regcomp(reg,arg2,REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE);
	if (r!=0) {
		regerror(r,reg,ebf,BFSZ-1);
		fmsg("Regular expression failed (%.40s) (%.40s)\n",ebf,arg2);
		myfree(reg);
		return FALSE;
	}
	exp->reg = (char *) reg;
	return TRUE;
}

/*
Fake lookahead operator 

  xxx(bbbb|ccc)xxx|free(?!bsd|cats)xx(b)x|pic(?!ton)ture


  /(/)
output=  xxxxxxx|freexxx|picture
mask  =  free(bsd|cats)xxx|pic(ton)ure

  (free(?!bsd|cats)xxx|pic(?!ton)ture)


*/

int lookahead_mask(char *outbf,char *maskbf)
{
	char *s;
	char *p;
	char amask[BFSZ];
	int depth;
	char *start;
	char *out;

	strcpy(maskbf,"");
	p = outbf;
	for (;TRUE;) {
		p = strstr(p,"(?!"); if (p==NULL) break;
		depth = 0;
		for (s=p;TRUE;s--) {
			if (s==outbf) break;
			if (depth==0) {
				if (s[-1]=='(') break;
				if (s[-1]=='|') break;
			}
			if (s[-1]==')') depth++;
			if (s[-1]=='(') depth--;
		}
		start = s;
		for (s=p;*s!=0;s++) if (*s==')') break;
		if (*s!=0) s++;
		depth = 0;
		for (;*s!=0;s++) {
			if (depth==0) {
				if (*s==')') break;
				if (*s=='|') break;
			}
			if (*s==')') depth--;
			if (*s=='(') depth++;
		}
		ncpy(amask,start,s-start);
		p = s;		
		for (s=amask,out=amask;*s!=0;) {
			*out++ = *s++;
			if (strncmp(s-1,"(?!",3)==0) s+=2;
		}
		*out++ = 0;
		if (strlen(maskbf)>0) strcat(maskbf,"|");
		strcat(maskbf,amask);
	}
	return TRUE;
}

int lookahead_out(char *outbf)
{
	char *s;
	char *out;
	int gotone = FALSE;
	out = outbf;
	for (s = outbf;*s!=0;s++) {
		if (strncmp(s,"(?!",3)==0) {
		 if (strchr(s,')') !=NULL) {
		  gotone = TRUE;
		  s+=3;
		  for (;*s!=0;s++) if (*s==')') break;
		 } else goto xxx;
		} else {
xxx:		*out++ = *s;
		}
		if (*s==0) break;
	}
	*out++ = 0;	
	return gotone;
}

int gnu_comp_rexp(Exp *exp, char *arg1, char *prog)
{
	regex_t *reg,*regm;
	int r;
	char ebf[BFSZ];
	char *outbf,*maskbf;
	char *out,*mask,*last;
	int depth=0;
	int found=FALSE;
	char *s;
	int gotone = FALSE;
	reg = mymalloc(sizeof(regex_t));
	memset(reg,0,sizeof(*reg));
	regm = mymalloc(sizeof(regex_t));
	memset(regm,0,sizeof(*reg));

	/* Search for a(?!b|c)d in string */
	maskbf = mymalloc(strlen(prog)+2000);
	outbf = mymalloc(strlen(prog)+2000);
	out = outbf; mask = maskbf;

	last = maskbf;
	for (s=prog;*s!=0;s++) {
		if (strncmp(s,"\\s",2)==0) {
			strcpy(out,"[ \t]"); out+= strlen(out);	
			s++;
			continue;
		}
		if (strncmp(s,"\\S",2)==0) {
			strcpy(out,"[^ \t]"); out+= strlen(out);	
			s++;
			continue;
		}
		if (strncmp(s,"\\d",2)==0) {
			strcpy(out,"[0-9]"); out+= strlen(out);	
			s++;
			continue;
		}
		if (strncmp(s,"\\D",2)==0) {
			strcpy(out,"[^0-9]"); out+= strlen(out);	
			s++;
			continue;
		}
		*out++ = *s;
	}
	*out++ = 0;
	
	/*printf("Before (%s)\n",outbf);*/
	lookahead_mask(outbf,maskbf);
	/*printf("Mask (%s)\n",maskbf);*/
	found= lookahead_out(outbf);
	/*printf("Out  (%s)\n",outbf);*/

	if (found) {
	 exp->p2 = mystrdup(prog,SD_FILTER);
	 exp->header = df_header_findadd(arg1);
	 r = regcomp(reg,outbf,REG_EXTENDED  | REG_ICASE | REG_NOSUB | REG_NEWLINE);
	 if (r!=0) {
		regerror(r,reg,ebf,BFSZ-1);
		fmsg("Regular expression failed (%.40s) (%.40s)\n",ebf,outbf);
		mystrfree(outbf,SD_FILTER); mystrfree(maskbf,SD_FILTER); maskbf = NULL; outbf = NULL;
		myfree(reg);
		myfree(regm);
		return FALSE;
	 }
	 exp->reg = (char *) reg;

/*printf("Comp Maskbf [%s]\n",maskbf);*/
	 r = regcomp(regm,maskbf,REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE);
	 if (r!=0) {
		if (exp->reg!=NULL) regfree((regex_t *) exp->reg);
		if (exp->reg!=NULL) myfree(exp->reg);
		exp->reg = NULL;		 
		mystrfree(outbf,SD_FILTER); mystrfree(maskbf,SD_FILTER); maskbf = NULL; outbf = NULL;
		regerror(r,reg,ebf,BFSZ-1);
		fmsg("Regular expression failed (%.40s) (%.40s)\n",ebf,maskbf);
		return FALSE;
	 }
/*printf("Comp Maskbf pointer %p\n",regm);*/
	 exp->mask = (char *) regm;
	 mystrfree(outbf,SD_FILTER); mystrfree(maskbf,SD_FILTER); maskbf = NULL; outbf = NULL;
	 return TRUE;

	}
	/* */

	myfree(regm);
	mystrfree(maskbf,SD_FILTER); maskbf = NULL; 
	exp->p2 = mystrdup(prog,SD_FILTER);
	exp->header = df_header_findadd(arg1);
	r = regcomp(reg,outbf,REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE);
	mystrfree(outbf,SD_FILTER); outbf = NULL;
	if (r!=0) {
		regerror(r,reg,ebf,BFSZ-1);
		fmsg("Regular expression failed (%.40s) (%.40s)\n",ebf,prog);
		myfree(reg);
		return FALSE;
	}
	exp->reg = (char *) reg;
	return TRUE;
	
}

int comp_rexp_case(Exp *exp, char *arg1, char *arg2)
{
	regex_t *reg;
	int r;
	char ebf[BFSZ];
	reg = mymalloc(sizeof(regex_t));
	memset(reg,0,sizeof(*reg));
	exp->p2 = mystrdup(arg2,SD_FILTER);
	exp->header = df_header_findadd(arg1);
	r = regcomp(reg,arg2,REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
	if (r!=0) {
		regerror(r,reg,ebf,BFSZ-1);
		fmsg("Regular expression failed (%.40s) (%.40s)\n",ebf,arg2);
		myfree(reg);
		return FALSE;
	}
	exp->reg = (char *) reg;
	return TRUE;
}
int comp_size(Exp *exp, char *arg1, char *arg2)
{
	exp->p2 = mystrdup("noargs",SD_FILTER);
	exp->header = 0;
	return TRUE;
}
/* Add matchany(header,value) and matchall() */

/*-----------------------------------------------------------------*/
/* Run expressions */
char *str_show(char *s)
{
	static char bf[100];
	ncpy(bf,s,99);
	for (s=bf;*s!=0;s++) {
		if (*s=='\r') *s = '.';
		if (*s=='\n') *s = '.';
	}
	return bf;
}

int run_rexp(Exp *exp)
{
	int r=FALSE;
	regex_t *reg;
	regex_t *mask;

#ifdef TESTING
	if (trace) fmsg("drun: exp: rexp(%s,%s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
#else
    if (trace) fmsg("drun: exp: rexp(%.40s,%.200s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
#endif
	mask = (regex_t *) exp->mask;
	if (mask!=NULL) {
/*printf("Comp Maskbf pointer %p\n",mask);*/

		r = regexec(mask,df_hdrs_get(exp->header),0,NULL,0);
		if (r==0) {
			/*fmsg("drun: exp: Mask matched\n");*/
			return FALSE;
		} /*else fmsg("drun: exp: Mask DIDNt MATCH \n");*/
	} /*else fmsg("drun: exp: NO Mask at all\n");*/

	reg = (regex_t *) exp->reg;
	if (reg!=NULL) r = regexec(reg,df_hdrs_get(exp->header)
		,0,NULL,0);
	else fmsg("Panic, reg expression NULL\n");
	if (reg!=NULL) {
		if (r==0) dmsg("reg_match: Found from (%.30s)\n",reg_error());
	}
	if (r==0) r = TRUE; else r = FALSE;
	return r;
}

int run_splitrexp(Exp *exp)
{
	char header[BFSZ];
	char *s;
	int r;
	int x;
	regex_t *reg;
	regex_t *mask;

#ifdef TESTING
	if (trace) fmsg("drun: exp: splitrexp(%s,%s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
#else
    if (trace) fmsg("drun: exp: splitrexp(%.40s,%.200s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
#endif
	
	ncpy(header,df_hdrs_get(exp->header),BFSZ-10);
	
	mask = (regex_t *) exp->mask;
	reg = (regex_t *) exp->reg;
	/* would be nice if we had a third parameter to specify the split tokens */
	s=strtok(header,/*exp->p2*/"!, \t|");
	r=0;
	for (;s!=NULL;) {
		if (mask!=NULL) {
			x = regexec(mask,s,0,NULL,0);
			if (x==0) {
				s=strtok(NULL,/*exp->p2*/"!, \t|");
				continue;
			}
		}
		if (reg!=NULL) x = regexec(reg,s,0,NULL,0);
		else fmsg("Panic, split reg expression NULL\n");
		if (reg!=NULL) {
			if (x==0) dmsg("splitreg_match: Found from (%.30s)\n",reg_error());
		}
		if (x==0) r++; /* this one matched */
		s=strtok(NULL,/*exp->p2*/"!, \t|");
	}
	return r;
}

int run_lastrexp(Exp *exp)
{
	char header[BFSZ];
	char last[BFSZ];
	int r=FALSE;
	regex_t *reg;
	regex_t *mask;
	char *s;

#ifdef TESTING
	if (trace) fmsg("drun: exp: lastrexp(%s,%s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
#else
    if (trace) fmsg("drun: exp: lastrexp(%.40s,%.200s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
#endif
	ncpy(header,df_hdrs_get(exp->header),BFSZ-10);
	s=strtok(header,/*exp->p2*/"!, \t|");
	for (;s!=NULL;)	{
		ncpy(last,s,BFSZ-10);
		s=strtok(NULL,/*exp->p2*/"!, \t|");
	}

	mask = (regex_t *) exp->mask;
	if (mask!=NULL) {
/*printf("Comp Maskbf pointer %p\n",mask);*/

		r = regexec(mask,last,0,NULL,0);
		if (r==0) {
			/*fmsg("drun: exp: Mask matched\n");*/
			return FALSE;
		} /*else fmsg("drun: exp: Mask DIDNt MATCH \n");*/
	} /*else fmsg("drun: exp: NO Mask at all\n");*/

	reg = (regex_t *) exp->reg;
	if (reg!=NULL) r = regexec(reg,last,0,NULL,0);
	else fmsg("Panic, reg expression NULL\n");
	if (reg!=NULL) {
		if (r==0) dmsg("reg_match: Found from (%.30s)\n",reg_error());
	}
	if (r==0) r = TRUE; else r = FALSE;
	return r;
}

int run_constant(Exp *exp)
{
	int r=FALSE;
	if (trace) fmsg("drun: exp: constant(%d)\n",exp->header);
	return exp->header;
}
int run_size(Exp *exp)
{
	return item_size;
}

int run_lines(Exp *exp)
{
	return item_lines;
}
int run_isencodedtext(Exp *exp)
{
	return item_isencodedtext;
}
int run_isencodedhtml(Exp *exp)
{
	return item_isencodedhtml;
}
int run_isencodedurl(Exp *exp)
{
	return item_isencodedurl;
}
int run_isencodedexe(Exp *exp)
{
	return item_isencodedexe;
}
int run_isuuencoded(Exp *exp)
{
	return item_isuuencoded;
}
int run_isbinary(Exp *exp)
{
	if (item_isbinary) if (nbin>=20) return TRUE;
	return FALSE;
}
int run_isbinaryn(Exp *exp)
{
	if (item_isbinary) if (nbin>=exp->header) return TRUE;
	return FALSE;
}
int run_ishtml(Exp *exp)
{
	return item_ishtml;
}
int run_isbase64(Exp *exp)
{
	return item_isbase64;
}
int run_isimage(Exp *exp)
{
	return item_isimage;
}
int feed_ispost(void);
int run_ispost(Exp *exp)
{
	return feed_ispost();
}
int run_ifflag(Exp *exp)
{
	int r=FALSE;
	if (trace) fmsg("drun: exp: ifflag(%d)\n",df_flag_get(exp->header));
	r = df_flag_get(exp->header);
	return r;
}
int run_get(Exp *exp)
{
	int r=FALSE;
	if (trace) fmsg("drun: exp: get(%d)\n",df_flag_get(exp->header));
	r = df_flag_get(exp->header);
	return r;
}
int run_exists(Exp *exp)
{
	/*int r=FALSE;

	if (trace) fmsg("drun: exp: exists(%.40s)\n",str_show(df_hdrs_get(exp->header)));
	if (strlen(df_hdrs_get(exp->header))>0) r = TRUE;
	
	return r;*/
	if (trace) fmsg("drun: exp: exists(%.40s)\n",str_show(df_hdrs_get(exp->header)));
	return df_hdrs_exists(exp->header);
}

int run_isinlist(Exp *exp)
{
	char list[BFSZ];
	char next[BFSZ];
	char *w;
	char *g;
	int r=0;
	char *s;
	char *b;
	int wild;
	if (trace) fmsg("drun: exp: isinlist(%.40s,%.40s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
	s = mystrdup(df_hdrs_get(exp->header),SD_FILTER);
	strlwr(s);
	strlwr(exp->p2);
	ncpy(list,exp->p2,BFSZ-10);
	w = strtok(list,"\t ,");
	while (w!=NULL) {
		wild=0;
		b=strchr(w,'.');
		if (b!=NULL) {
			wild=1;
			*b=0;
			ncpy(next,++b,BFSZ-10);
		}
		g = strstr(s,w);
		while (g!=NULL) {
			if (wild) {
				g+=strlen(w);
				if (*g==0) break;
				g++;
				if (strlen(next)==0) {
					r++;
				    g=strstr(g,w);
				} else if (strncmp(g,next,strlen(next))==0) {
				   r++;
				   g=strstr(g+strlen(next),w);
				} else {
				   g=strstr(g-1,w);
				}
			} else {
				r++;
				g=strstr(g+strlen(w),w);
			}
		}
		if (wild) *b='.';
		w = strtok(NULL,"\t ,");
	}
	mystrfree(s,SD_FILTER);
	return r;
}

int run_isin(Exp *exp)
{
	int r=FALSE;
	char *s;
	if (trace) fmsg("drun: exp: isin(%.40s,%.40s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
	s = mystrdup(df_hdrs_get(exp->header),SD_FILTER);
	strlwr(s);
	strlwr(exp->p2);
	if (strstr(s,exp->p2)!=NULL) r = TRUE;
	mystrfree(s,SD_FILTER);
	return r;
}
int run_isinc(Exp *exp)
{
	int r=FALSE;
	char *s,*s2;
	if (trace) fmsg("drun: exp: isin(%.40s,%.40s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
	s = mystrdup(df_hdrs_get(exp->header),SD_FILTER);
	strlwr(s);
	strlwr(exp->p2);
	if((s2=mystrdup(exp->p2,SD_FILTER))==NULL) return TRUE;
	if (strstr(clean_str(s,s),clean_str(exp->p2,s2))!=NULL) r = TRUE;
	mystrfree(s,SD_FILTER);
	mystrfree(s2,SD_FILTER);
	return r;
}
int run_matchall(Exp *exp)
{
	int r;
	if (trace) fmsg("drun: exp: matchall(%.40s,%.40s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
	r = df_group_match_list(exp->p2,df_hdrs_get(exp->header));
	if (r==1) r = TRUE; else r = FALSE; 
	return r;
}
int run_matchone(Exp *exp)
{
	int r;
	if (trace) fmsg("drun: exp: matchany(%.40s,%.40s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
	r = df_group_match_list(exp->p2,df_hdrs_get(exp->header));
	if (r>0) r = TRUE; else r = FALSE; 
	return r;
}
/* brian */
int run_split(Exp *exp)
{
	char header[BFSZ];
	char *s;
	int r;
	if (trace) fmsg("drun: exp: split(%.40s,%.40s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
	
	ncpy(header,df_hdrs_get(exp->header),BFSZ-10);
	s=strtok(header,exp->p2/*"!, \t|"*/);
	r=0;
	for (;s!=NULL;) {
		/*if (df_group_match_list(exp->p2,s))*/ r++;
		s=strtok(NULL,exp->p2/*"!, \t|"*/);
	}
	/*if (r>0) r = TRUE; else r = FALSE;*/
	return r;
}

int run_attach(Exp *exp)
{
	int r;
	if (trace) fmsg("drun: exp: attach(%.40s,%.40s)\n",str_show(cur_fname),exp->p2);
	if (strlen(cur_fname)==0) return FALSE;
	r = df_group_match_list(exp->p2,cur_fname);
	if (r>0) r = TRUE; else r = FALSE; 
	return r;
}
int run_match(Exp *exp)
{
	int r=FALSE;
	if (trace) fmsg("drun: exp: match(%.40s,%.40s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
	if (df_group_match(exp->p2,df_hdrs_get(exp->header))) r = TRUE;
	return r;
}
int run_allmod(Exp *exp)
{
#ifndef TESTING
	int r;
	if (trace) fmsg("drun: exp: allmod(%.40s)\n",str_show(df_hdrs_get(exp->header)));
    r = df_match_mod(df_hdrs_get(exp->header));
	if (r==1) r=TRUE; else r=FALSE;
	return r;
#else
	return FALSE;
#endif
}
int run_anymod(Exp *exp)
{
	int r;
	if (trace) fmsg("drun: exp: allmod(%.40s)\n",str_show(df_hdrs_get(exp->header)));
	r = df_match_mod(df_hdrs_get(exp->header));
	if (r>0) return TRUE;
	return FALSE;
}

#ifndef TESTING
int df_ismod(char *group)
{
    int i;
	i = group_find(group);
    if ((i>=0) && (group_ismod(i)))
        return TRUE;
	else
	    return FALSE;
}
int df_match_mod(char *groups)
{
    char *p = groups;
	int any = FALSE;
	int all = TRUE;
	char bf[BFSZ];
	char *end;
	int len;

	for (;*p!=0;) {
		for (end=p;*end!=0;end++) {
			if (*end==',') break;
			if (*end=='!') break;
			if (*end=='|') break;
		}
		len = end-p;
		if (len>BFSZ-4) len = BFSZ-10;
		ncpy(bf,p,len);
		if (df_ismod(bf)) any = TRUE;
		else all = FALSE;
		if (*end==0) break;
		p=end+1;
	}
	if (all) return 1; /* all groups moderated */
	if (any) return 2; /* at least one moderated */
	return 0; /* no moderated groups */
}
#endif

int run_equals(Exp *exp)
{
	int r=FALSE;
	if (trace) fmsg("drun: exp: equals(%.40s,%.40s)\n",str_show(df_hdrs_get(exp->header)),exp->p2);
	if (strcmp(df_hdrs_get(exp->header),exp->p2)==0) r = TRUE;
	return r;
}
/*
if rexp ( subject , "This is\" \r\n \\  " ) reject Naughty Subject 
*/
#define sep "\r\n \t"
int df_token(char *line, char *p[], int maxp)
{
        int n=0;
	int z;
	static char bf[MAX_LINE+10];
	char *out = bf;
	char *s;
        for (s=line;*s!=0;) {
                z = strspn(s,sep);  /* Skip spaces */
		s += z;
                if (*s==0) break;
		p[n++] = out;
		*out = 0;
		if (*s=='\n') break;
		if (*s=='"') {
		 for (s++;*s!=0;) {
			if (*s=='"') {s++; *out = 0; goto next_tok;}
			if (*s=='\\') {
				if (s[1]=='"') { *out++ = '"';	s+= 2; continue; }
				if (s[1]=='r') { *out++ = '\r';	s+= 2; continue; }
				if (s[1]=='n') { *out++ = '\n';	s+= 2; continue; }
				if (s[1]=='\\') { *out++ = '\\';	s+= 2; continue; }
			}
			*out++ = *s++;
		 }
		 return -1;
		} else {
		 for (;*s!=0;) {
			if (strchr(" \t\r\n",*s)!=NULL) { *out = 0;break; }
			if (strchr("()!,<>=+",s[1])!=NULL) { *out++ = *s++; break; }
			if (strchr("()!,<>=+",*s)!=NULL) {
				*out++ = *s++;
				*out = 0;
				break;
			}
			*out++ = *s++;
		 }
		}
next_tok:
		*out++ = 0;
        }
	*out++ = 0;
        p[n] = NULL;
        return n;
}
/*enum {A_ACCEPT=1,A_REJECT};*/
int df_find_action(Linex **peline,char *p[],int np,int *upto)
{
	Linex *eline = *peline;
	int dofree= FALSE;
	if (eline==NULL) {
		*peline = mymalloc(sizeof(Linex));
		eline = *peline;
		dofree = TRUE;
		memset(eline,0,sizeof(Linex));
	}
	if (strcmp(gp(*upto),"accept")==0) {
		eline->action = A_ACCEPT;
		eline->reason = mystrdup(gp(*upto + 1),SD_FILTER);
		*upto += 2;
		return TRUE;
	}
	if (strcmp(gp(*upto),"then")==0) {
		if (*upto!=(np-1)) {
			fmsg("'then' must be the last statement on the line\n");
		} else {
			eline->action = A_THEN;
			*upto += 1;
			return TRUE;
		}
	}
	if (strcmp(gp(*upto),"reject")==0) {
		eline->action = A_REJECT;
		eline->reason = mystrdup(gp(*upto + 1),SD_FILTER);
		*upto += 2;
		return TRUE;
	}
	if (strcmp(gp(*upto),"print")==0) {
		eline->action = A_PRINT;
		eline->reason = mystrdup(gp(*upto + 1),SD_FILTER);
		*upto += 2;
		return TRUE;
	}
	if (strcmp(gp(*upto),"setflag")==0) {
		eline->action = A_SETFLAG;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		*upto += 4;
		return TRUE;
	}
	if (strcmp(gp(*upto),"set")==0) {
		eline->action = A_SET;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		eline->p2 = atoi(gp(*upto+4));
		*upto += 6;
		return TRUE;
	}
	if (strcmp(gp(*upto),"add")==0) {
		eline->action = A_ADD;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		eline->p2 = atoi(gp(*upto+4));
		*upto += 6;
		return TRUE;
	}
	if (strcmp(gp(*upto),"sub")==0) {
		eline->action = A_SUB;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		eline->p2 = atoi(gp(*upto+4));
		*upto += 6;
		return TRUE;
	}
	if (strcmp(gp(*upto),"mul")==0) {
		eline->action = A_MUL;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		eline->p2 = atoi(gp(*upto+4));
		*upto += 6;
		return TRUE;
	}
	if (strcmp(gp(*upto),"div")==0) {
		eline->action = A_DIV;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		eline->p2 = atoi(gp(*upto+4));
		*upto += 6;
		return TRUE;
	}
	if (strcmp(gp(*upto),"addvar")==0) {
		eline->action = A_ADDVAR;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		eline->p2 = df_flag_findadd(gp(*upto+4));
		*upto += 6;
		return TRUE;
	}
	if (strcmp(gp(*upto),"subvar")==0) {
		eline->action = A_SUBVAR;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		eline->p2 = df_flag_findadd(gp(*upto+4));
		*upto += 6;
		return TRUE;
	}
	if (strcmp(gp(*upto),"eq")==0) {
		eline->action = A_EQ;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		eline->p2 = df_flag_findadd(gp(*upto+4));
		*upto += 6;
		return TRUE;
	}
	if (strcmp(gp(*upto),"clearflag")==0) {
		eline->action = A_CLEARFLAG;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		*upto += 4;
		return TRUE;
	}
	if (strcmp(gp(*upto),"score_one")==0) {
		eline->action = A_SCORE_ONE;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		*upto += 4;
		return TRUE;
	}
	if (strcmp(gp(*upto),"score_all")==0) {
		eline->action = A_SCORE_ALL;
		eline->p1 = df_flag_findadd(gp(*upto+2));
		*upto += 4;
		return TRUE;
	}
	if (dofree) myfree(eline);
	return FALSE;
}
int df_header_findadd(char *header)
{
	int i;
	char bf[BFSZ];
	char *s;
	strncpy(bf,header,100); bf[100]=0; strlwr(bf);
	s = strchr(bf,':'); if (s!=NULL) *s = 0;
	for (i=0; i<curprog->nheaders; i++) {
		if (strcmp(curprog->headers[i],bf)==0) return i;
	}
	if (curprog->nheaders+10>MAX_HEADERS) {
		fmsg("Too many different headers in rules file");
		return 0;
	}
	curprog->headers[curprog->nheaders++] = mystrdup(bf,SD_FILTER);
	return curprog->nheaders-1;
}

int df_header_find(char *header)
{
	int i;
	char bf[BFSZ];
	char *s;
	strncpy(bf,header,100); bf[100]=0; strlwr(bf);
	s = strchr(bf,':'); if (s!=NULL) *s = 0;
	for (i=0; i<curprog->nheaders; i++) {
		if (strcmp(curprog->headers[i],bf)==0) return i;
	}
	return -1; /* not found */
}

int df_flag_findadd(char *header)
{
	int i;
	char bf[BFSZ];
	char *s;
	strncpy(bf,header,100); bf[100]=0; strlwr(bf);
	s = strchr(bf,':'); if (s!=NULL) *s = 0;
	for (i=0; i<curprog->nflags; i++) {
		if (strcmp(curprog->flags[i],bf)==0) return i;
	}
	if (curprog->nflags+10>MAX_FLAGS) {
		fmsg("Too many different flags in rules file");
		return 0;
	}
	curprog->flags[curprog->nflags++] = mystrdup(bf,SD_FILTER);
	return curprog->nflags-1;
}
int df_flag_get(int x)
{
	if ((x<0) || (x>=MAX_FLAGS)) return FALSE;
	return flags[x];
}
#ifdef TESTING
char *ncpy(char *dst, char *src, int len)
{
        char *xdst = dst;
        int xlen = len;
        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        xdst[xlen] = 0;
        return xdst;
}
#endif
int df_getline(char *item, int *upto, char *bf, int bfsz)
{
	char *s;
	char *s2;
	char *s3;
	int len;

	s = item;
	if (s==NULL) return FALSE;
	s += *upto;
	for (s2=s;*s2!=0;s2++) if (*s2=='\n') break;
	if (*s2==0) return FALSE;
	len = s2-s;
	*upto = s2 - item + 1;
	if (len<0) len = 0; if (len>=bfsz) len = bfsz-1;
	ncpy(bf,s,len);
	s3 = strchr(bf,'\r');
	if (s3!=NULL) *s3 = 0;
	return TRUE;
}
static const unsigned char charmap[] = {
        0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
        0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
        0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
        0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
        0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
        0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
        0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
        0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
        0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
        0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
        0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
        0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
        0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
        0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
        0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
        0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
        0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
        0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
        0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
        0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
        0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
        0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
        0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
        0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
        0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
        0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};

int strcmpnc(char *s1, char *s2) {
        const unsigned char *cm = charmap,
             *us1 = (const unsigned char *)s1,
             *us2 = (const unsigned char *)s2;

        while (cm[*us1] == cm[*us2++])
                if (*us1++ == '\0')
                        return (0);
        return (cm[*us1] - cm[*--us2]);
}



int strncmpnc(const char *s1, const char *s2, int len)
{
        int i;
/*      char tmp1[200],tmp2[200];
        ncpy(tmp1,s1,len);
        ncpy(tmp2,s2,len);
        if (trace) fmsg("Compare nocase {%s} {%s} %d \n",tmp1,tmp2,len);
*/
        for (i=0;i<len;i++,s1++,s2++) {
                if (tolower(*s1)<tolower(*s2)) return -1;
                if (tolower(*s1)>tolower(*s2)) return 1;
                if (*s1 == 0) return -1;
                if (*s2 == 0) return 1;
        }
        return 0;
}

int df_group_match_list(char *wild, char *grps)
{
	/* return 1 if all groups match, 2 if any match */
	char *p = grps;
	int any = FALSE;
	int all = TRUE;
	char bf[BFSZ];
	char *end;
	int len;

	for (;*p!=0;) {
		for (end=p;*end!=0;end++) {
			if (*end==',') break;
			if (*end=='!') break;
			if (*end=='|') break;
			if (*end==' ') break; /*brian*/
			if (*end=='\t') break; /*brian*/
		}
		len = end-p;
		if (len>BFSZ-4) len = BFSZ-10;
		ncpy(bf,p,len);
		if (df_group_match(wild,bf)) any = TRUE;
		else all = FALSE;
		if (*end==0) break;
		p=end+1;
	}
	if (all) return 1;
	if (any) return 2;
	return 0;
}
static int lastall;
int df_group_match(char *wild, char *grp2)
{
        char bf[1000];
        char grp[BFSZ];
        int m=TRUE,lastm;
        char *s,*p,*w;
        int done,len;
        int all;

        lastall = FALSE;
        ncpy(grp,grp2,BFSZ-10); strlwr(grp);
        lastm = FALSE;
        p = wild;
        done = FALSE;
        for (;!done;) {
                s = strchr(p,',');
                if (s==NULL) {s = wild + strlen(wild); done = TRUE;}
                if (*p==0) break;
                len = s - p;
                if (len>100) len = 100;
                strncpy(bf,p,len); bf[len] = 0;
                strlwr(bf); w = bf;

                m = TRUE;
                all = FALSE;
                if (*w=='!') {
                        w++;
                        m = FALSE;
                }
                if (*w=='@') {
                        w++;
                        m = FALSE;
                        all = TRUE;
                }
                if (match_wild(w,grp)) { lastm = m; lastall = all;} 

                p = s + 1;
        }
        return lastm;
}
#ifndef EOL
#define EOL "\r\n"
#endif
int count_lines(char  *item)
{
        char *s = item;
        int n=0;
	if (s==NULL) return 0;
	
        for (;*s!=0;s++) {
		if (*s=='\n') n++;
        }
        return n;
}
