/* Copyright (C) 2000-2002 Lavtech.com corp. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
*/

#include "udm_config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <ctype.h>

#include "udmsearch.h"
#include "udm_db.h"
#include "udm_http.h"
#include "udm_parsehtml.h"
#include "udm_host.h"
#include "udm_contentencoding.h"
#include "udm_utils.h"

/******************* Template functions ********************/


static size_t out_string(FILE * stream, char * dst, size_t dst_len, const char * src){
	if(src){
		if(stream)fputs(src,stream);
		if(dst){
			strncat(dst, src, dst_len - 1);
			return(strlen(src));
		}
	}
	return(0);
}

static char * HiLightDup(const char * src,const char * beg, const char * end){
	size_t len=1;
	size_t blen=strlen(beg);
	size_t elen=strlen(end);
	const char * s;
	char  * res, *d;
	
	for(s=src;*s;s++){
		switch(*s){
			case '\2':
				len+=blen;
				break;
			case '\3':
				len+=elen;
				break;
			default:
				len++;
		}
	}
	res=(char*)malloc(len);
	for(s=src,d=res;*s;s++){
		switch(*s){
			case '\2':
				strcpy(d,beg);
				d+=blen;
				break;
			case '\3':
				strcpy(d,end);
				d+=elen;
				break;
			default:
				*d=*s;
				d++;
		}
	}
	*d='\0';
	return(res);
}

static size_t PrintTextTemplate(UDM_AGENT *A, FILE * stream, char * dst, size_t dst_len, UDM_VARLIST * vars, const char * templ) {
	const char * s;
	size_t dlen=0;
	const char * HlBeg=UdmVarListFindStrTxt(vars,"HlBeg","");
	const char * HlEnd=UdmVarListFindStrTxt(vars,"HlEnd","");
	
	for(s=templ; (*s) && ((stream) || (dlen < dst_len)); s++){
		int type=0;
		char *value=NULL, *eval=NULL, *newvalue = NULL;
		char empty[]="";
		size_t maxlen=0;
		size_t curlen=0;

		if(*s=='$'){
			const char * vbeg=NULL, * vend;
			int pcount=0;
			
			if(!strncmp(s,"$(",2)){vbeg=s+2;type='(';}
			else	if(!strncmp(s,"$%(",3)){vbeg=s+3;type='%';}
			else	if(!strncmp(s,"$&(",3)){vbeg=s+3;type='&';}
			else	if(!strncmp(s,"$^(",3)){vbeg=s+3;type='^';}
			
			for (vend=s; vend[0]; vend++){
				if(vend[0]=='(')pcount++;
				if(vend[0]==')' && (--pcount)==0)
					break;
			}
			
			if((type)&&(vend)){
				UDM_VAR * var;
				size_t len;
				char name[100]="";
				char * sem;
				
				len=(vend-vbeg);
				if(len>=sizeof(name))len=sizeof(name)-1;
				strncpy(name,vbeg,len);name[len]='\0';
				if((sem=strchr(name,':'))){
					*sem=0;
					maxlen=atoi(sem+1);
				}
				
				if ((A->doccount == 0) && !strcasecmp(name, "ndocs")) {
				  UdmURLAction(A, NULL, UDM_URL_ACTION_DOCCOUNT);
				  UdmVarListReplaceInt(vars, "ndocs", A->doccount);
				}

				if((var=UdmVarListFind(vars,name))){
				        switch(type) {
					case '&': value = var->val; break;
					default:  value = (var->txt_val) ? var->txt_val : var->val;
					}
					if(!value)value=empty;
				}else{	
					value=empty;
				}
				
				s=vend;
			}else{
				type=0;
			}
		}
		if(!value)value=empty;
		curlen=strlen(value);
		
		if((curlen>maxlen)&&(maxlen>0)){
			char *p = value + maxlen, *S, *e;
			S = e = p;
			if (isdigit(*e)) {
			  while(isdigit(*S) && S > (value + 1)) S--;
			  S--;
			  if (*S == '&' && S[1] == '#') {
			    while(isdigit(*e)) e++;
			    if (*e == ';') p = e + 1;
			  } 
			} else if (isalpha(*e)) {
			  while(isalpha(*S) && S > value) S--;
			  if (*S == '&') {
			    while(isalpha(*e)) e++;
			    if (*e == ';') p = e + 1;
			  }
			} else if (*e == ';' && S < value ) {
			  S--;
			  if (isdigit(*S)) {
			    while(isdigit(*S) && S > (value + 1)) S--;
			    S--;
			    if (*S == '&' && S[1] == '#') {
			      p = e + 1;
			    }
			  } else if (isalpha(*S)) {
			    while(isalpha(*S) && S > value) S--;
			    if (*S == '&') {
			      p = e + 1;
			    }
			  }
			}
			if ((newvalue = (char*)malloc((size_t)(p - value) + 8))) {
			  char *c2, *c3;
			  strncpy(newvalue, value, (size_t)(p - value));
			  newvalue[(p - value)] = '\0';
			  c2 = strrchr(newvalue, '\2');
			  c3 = strrchr(newvalue, '\3');
			  if ((c2 != NULL) && ((c3 == NULL) || (c2 > c3))) {
			    strcpy(newvalue + (p - value), "\3...");
			  } else {
			    strcpy(newvalue + (p - value), "...");
			  }
			  value = newvalue;
			}
		}
		switch(type){
			case '(': 
				eval = UdmRemoveHiLightDup(value);
				dlen+=out_string(stream, dst + dlen, dst_len - dlen, eval);
				UDM_FREE(eval);
				break;
			case '&':
			case '^':
				eval=HiLightDup(value,HlBeg,HlEnd);
				dlen+=out_string(stream, dst + dlen, dst_len - dlen, eval);
				UDM_FREE(eval);
				break;
			case '%':
				eval=(char*)malloc(strlen(value)*3);
				UdmEscapeURL(eval,value);
				dlen+=out_string(stream, dst + dlen, dst_len - dlen, eval);
				UDM_FREE(eval);break;
			default:	/* One character */
				if((stream)&&(*s))fputc(*s,stream);
				if(dst){
					dst[dlen]=*s;
					dlen++;
					dst[dlen]='\0';
				}
		}
		UDM_FREE(newvalue);
	}
	return dlen;
}

static char * GetHtmlTok(const char * src,const char ** lt){
	char * res;
	size_t len;
	if((!src)&&!(src=*lt))return(NULL);
	if(*src=='<'){
		/* Find ">" and skip "<" */
		if((*lt=strchr(src,'>')))(*lt)++;
	}else{
		/* Find tag beginning */
		*lt=strchr(src,'<');
	}
	if(!(*lt)){
		/* Last token */
		res = (char*)strdup(src);
	}else{
		/* Token in the middle */
		len=(*lt)-src;
		res=(char*)malloc(len+2);
		strncpy(res,src,len);
		res[len]='\0';
	}
	return(res);
}

/* 
	FIXME: add support to include into buffer
	FIXME: currently stream only is supported
*/

static void TemplateInclude(UDM_AGENT *Agent,FILE *stream,UDM_VARLIST *vars,const char *tok){
	size_t i;
	UDM_HTMLTOK ltag, *tag = &ltag;
	const char *last;
	char *tag_content = NULL;
	UDM_DOCUMENT * Inc=UdmDocInit(NULL);
	
	size_t max_doc_size = (size_t)UdmVarListFindInt(vars,"MaxDocSize",UDM_MAXDOCSIZE);

	if(!Inc->Buf.buf)Inc->Buf.buf=malloc(max_doc_size);
	Inc->Buf.maxsize=max_doc_size;

	UdmHTMLTOKInit(tag);
	UdmHTMLToken(tok, &last, tag);
	for(i = 0; i < ltag.ntoks; i++) {
		if (ISTAG(i, "content")) {
			tag_content = strndup(ltag.toks[i].val, ltag.toks[i].vlen);
			break;
		}
	}
	if(tag_content){
		const char *ce;
		size_t vurlen = 256 + 4 * strlen(tag_content);
		char  *vurl = (char*)malloc(vurlen);
					
		PrintTextTemplate(Agent, NULL, vurl, vurlen, vars, tag_content);
		UdmURLParse(&Inc->CurURL,vurl);
		UDM_FREE(vurl);
		UdmVarListReplaceStr(&Inc->RequestHeaders, "Host", UDM_NULL2EMPTY(Inc->CurURL.hostname));
		Inc->connp.hostname = (char*)strdup(UDM_NULL2EMPTY(Inc->CurURL.hostname));
		Inc->connp.port = Inc->CurURL.port ? Inc->CurURL.port : Inc->CurURL.default_port;
		
		if(UdmHostLookup(&Agent->Conf->Hosts, &Inc->connp)){
		}
					
		if(UdmGetURL(Agent,Inc)==UDM_OK){
			UdmParseHTTPResponse(Agent,Inc);
			if(Inc->Buf.content){
				ce=UdmVarListFindStr(&Inc->Sections,"Content-Encoding","");
#ifdef HAVE_ZLIB
				if(!strcasecmp(ce,"gzip") || !strcasecmp(ce,"x-gzip")){
					UdmUnGzip(Inc);
				}else
				if(!strcasecmp(ce,"deflate")){
					UdmInflate(Inc);
				}else
				if(!strcasecmp(ce,"compress") || !strcasecmp(ce,"x-compress")){
					UdmUncompress(Inc);
				}
#endif
				if(stream){
					fprintf(stream,"%s",Inc->Buf.content);
				}else{
					/* FIXME: add printing to string */
				}
			}
		}
		UDM_FREE(tag_content);
	}
	UdmDocFree(Inc);
}

static size_t TemplateTag(UDM_AGENT *Agent,FILE *stream,char *dst,size_t dst_len,UDM_VARLIST *vars,const char *tok){
	char * opt;
	UDM_HTMLTOK ltag, *tag = &ltag;
	const char *last;
	UDM_VAR * var=NULL;
	char * vname = NULL, *value = NULL;
	size_t i, res = 0;
	
	opt=(char*)malloc(strlen(tok)+200);
	UdmHTMLTOKInit(tag);
	UdmHTMLToken(tok, &last, tag);
	sprintf(opt, "<");

	for (i = 0; i < ltag.ntoks; i++) {
		if (ISTAG(i, "selected")) {
			vname = strndup(ltag.toks[i].val, ltag.toks[i].vlen);
		} else if (ISTAG(i, "value")) {
			value = strndup(ltag.toks[i].val, ltag.toks[i].vlen);
			sprintf(UDM_STREND(opt), "value=\"%s\" ", value);
		} else if (ISTAG(i, "/")) { // the closing slash is necessary for some xhtml tags
			sprintf(UDM_STREND(opt), " %s", "/");
		} else {
			char *tname = strndup(ltag.toks[i].name, ltag.toks[i].nlen);
			if (ltag.toks[i].vlen) {
				char *tval = strndup(ltag.toks[i].val, ltag.toks[i].vlen);
				sprintf(UDM_STREND(opt), "%s=\"%s\" ", tname, tval);
				UDM_FREE(tval);
			} else {
	 			sprintf(UDM_STREND(opt), "%s ", tname);
	 		}
	 		UDM_FREE(tname);
		}
	}

	if(vname) {
		var = UdmVarListFindWithValue(vars, UdmTrim(vname, "$()"), value ? value:"");
	}

	sprintf(UDM_STREND(opt), "%s>", var ? "SELECTED":"");

	if (vname) { UDM_FREE(vname); }
	if (value) { UDM_FREE(value); }

	res = PrintTextTemplate(Agent, stream, dst, dst_len, vars, opt);
	UDM_FREE(opt);
	return res;
}

#define UDM_IFSTACKMAX	15

typedef struct udm_if_stack_item_st {
	int	condition;
	int	showelse;
} UDM_IFITEM;

typedef struct udm_if_stack_st {
	size_t		pos;
	UDM_IFITEM	Items[UDM_IFSTACKMAX+1];
} UDM_IFSTACK;

static void UdmIfStackInit(UDM_IFSTACK *S){
	UDM_IFITEM	*top=&S->Items[0];
	
	bzero((void*)S, sizeof(*S));
	top->condition=1;
	top->showelse=1;
}

static UDM_IFITEM *UdmIfStackPush(UDM_IFSTACK *S){
	if(S->pos<UDM_IFSTACKMAX){
		UDM_IFITEM 	*cur=&S->Items[S->pos+1];
		UDM_IFITEM	*prev=&S->Items[S->pos];
		
		S->pos++;
		cur->condition=prev->condition;
		cur->showelse=prev->condition;
	}
	return &S->Items[S->pos];
}

static UDM_IFITEM *UdmIfStackPop(UDM_IFSTACK *S){
	if(S->pos>0)S->pos--;
	return &S->Items[S->pos];
}

static void HTMLTokToVarList(UDM_VARLIST *vars,UDM_HTMLTOK *tag){
	size_t	toks;
	
	for(toks=0;toks<tag->ntoks;toks++){
		char *vr=tag->toks[toks].name ? strndup(tag->toks[toks].name,tag->toks[toks].nlen) : (char*)strdup("");
		char *vl=tag->toks[toks].val ? strndup(tag->toks[toks].val,tag->toks[toks].vlen) : (char*)strdup("");
		UdmVarListAddStr(vars,vr,vl);
		UDM_FREE(vr);
		UDM_FREE(vl);
	}
}

static void TemplateSet(UDM_AGENT *Agent,UDM_VARLIST *vars,const char *tok,UDM_IFSTACK *is){
	UDM_HTMLTOK	tag;
	UDM_VARLIST	attr;
	const char	*var,*val,*hlast=NULL;
	
	UdmHTMLTOKInit(&tag);
	UdmHTMLToken(tok,&hlast,&tag);
	UdmVarListInit(&attr);
	HTMLTokToVarList(&attr,&tag);
	
	var=UdmVarListFindStr(&attr,"Name","");
	val=UdmVarListFindStr(&attr,"Content","");
	UdmVarListReplaceStr(vars,var,val);
	
	UdmVarListFree(&attr);
}

static void TemplateCopy(UDM_AGENT *Agent,UDM_VARLIST *vars,const char *tok,UDM_IFSTACK *is){
	UDM_HTMLTOK	tag;
	UDM_VARLIST	attr;
	const char	*var,*val,*hlast=NULL;
	
	UdmHTMLTOKInit(&tag);
	UdmHTMLToken(tok,&hlast,&tag);
	UdmVarListInit(&attr);
	HTMLTokToVarList(&attr,&tag);
	
	var=UdmVarListFindStr(&attr,"Name","");
	val=UdmVarListFindStr(&attr,"Content","");
	val=UdmVarListFindStr(vars,val,"");
	UdmVarListReplaceStr(vars,var,val);
	
	UdmVarListFree(&attr);
}


static void TemplateCondition(UDM_AGENT *Agent,UDM_VARLIST *vars,const char *tok,UDM_IFSTACK *is){
	UDM_HTMLTOK	tag;
	UDM_VARLIST	attr;
	const char	*var,*val,*hlast=NULL;
	UDM_IFITEM	*it=&is->Items[is->pos];
	
	UdmHTMLTOKInit(&tag);
	UdmHTMLToken(tok,&hlast,&tag);
	UdmVarListInit(&attr);
	
	HTMLTokToVarList(&attr,&tag);
	
	var=UdmVarListFindStr(&attr,"Name","");
	val=UdmVarListFindStr(&attr,"Content","");
	var=UdmVarListFindStr(vars,var,"");
	
	if(!(strncasecmp(tok,"<!IFNOT",7))){
		it->condition=strcasecmp(var,val);
	}else
	if( !(strncasecmp(tok, "<!IF", 4)) || !(strncasecmp(tok, "<!ELIF", 6)) || !(strncasecmp(tok, "<!ELSEIF", 8)) ) {
		it->condition=!strcasecmp(var,val);
	}
	
	UdmVarListFree(&attr);
}

static void TemplateIf(UDM_AGENT *Agent,UDM_VARLIST *vars,const char *tok,UDM_IFSTACK *is){
	UDM_IFITEM	*it=UdmIfStackPush(is);
	
	if(it->condition){
		TemplateCondition(Agent,vars,tok,is);
		if(it->condition)it->showelse=0;
	}
}

static void TemplateEndIf(UDM_AGENT *Agent,UDM_VARLIST *vars,const char *tok,UDM_IFSTACK *is){
	UdmIfStackPop(is);
}

static void TemplateElseIf(UDM_AGENT *Agent,UDM_VARLIST *vars,const char *tok,UDM_IFSTACK *is){
	UDM_IFITEM	*it=&is->Items[is->pos];
	
	if(it->showelse){
		TemplateCondition(Agent,vars,tok,is);
		if(it->condition)it->showelse=0;
	}else{
		it->condition=0;
	}
}

static void TemplateElse(UDM_AGENT *Agent,UDM_VARLIST *vars,const char *tok,UDM_IFSTACK *is){
	UDM_IFITEM	*it=&is->Items[is->pos];
	it->condition=it->showelse;
}



static void PrintHtmlTemplate(UDM_AGENT * Agent, FILE * stream, char * dst, size_t dst_len, UDM_VARLIST * vars, const char * template){
	const char	*lt;
	char		*tok;
	size_t		dlen=0;
	UDM_IFSTACK	is;
	
	UdmIfStackInit(&is);
	
	tok=GetHtmlTok(template,&lt);
	while(tok){
		if(!(strncasecmp(tok,"<!SET",5))){
			TemplateSet(Agent,vars,tok,&is);
		}else
		if(!(strncasecmp(tok,"<!COPY",6))){
			TemplateCopy(Agent,vars,tok,&is);
		}else
		if(!(strncasecmp(tok,"<!IF",4))){
			TemplateIf(Agent,vars,tok,&is);
		}else
		if(!(strncasecmp(tok,"<!ELSEIF",8))){
			TemplateElseIf(Agent,vars,tok,&is);
		}else
		if(!(strncasecmp(tok,"<!ELIF",6))){
			TemplateElseIf(Agent,vars,tok,&is);
		}else
		if(!(strncasecmp(tok,"<!ELSE",6))){
			TemplateElse(Agent,vars,tok,&is);
		}else
		if(!(strncasecmp(tok,"<!ENDIF",7))){
			TemplateEndIf(Agent,vars,tok,&is);
		}else
		if(!(strncasecmp(tok,"<!/IF",5))){
			TemplateEndIf(Agent,vars,tok,&is);
		}else
		if(is.Items[is.pos].condition){
			if(!(strncasecmp(tok,"<OPTION",7))){
				dlen += TemplateTag(Agent,stream,dst+dlen,dst_len-dlen,vars,tok);
			}else
			if(!(strncasecmp(tok,"<INPUT",6))){
				dlen += TemplateTag(Agent,stream,dst+dlen,dst_len-dlen,vars,tok);
			}else
			if(!strncasecmp(tok,"<!INCLUDE",9)){
				if(Agent)TemplateInclude(Agent,stream,vars,tok);
			}else{
				dlen += PrintTextTemplate(Agent, stream, dst + dlen, dst_len - dlen, vars, tok);
			}
		}
		UDM_FREE(tok);
		tok=GetHtmlTok(NULL,&lt);
	}
}

void __UDMCALL UdmTemplatePrint(UDM_AGENT * Agent, FILE *stream, char *dst, size_t dst_len, UDM_VARLIST *vars, UDM_VARLIST *tm, const char *w){
	size_t	t;
	size_t	matches=0;
	size_t	format=(size_t)UdmVarListFindInt(vars,"o",0);
	UDM_VAR	*First=NULL;
	
	if(dst)*dst='\0';
	for(t=0;t<tm->nvars;t++){
		if(!strcasecmp(w,tm->Var[t].name)){
			if(!First)First=&tm->Var[t];
			if(matches==format){
				PrintHtmlTemplate(Agent, stream, dst, dst_len, vars, tm->Var[t].val);
				return;
			}
			matches++;
		}
	}
	if (First) PrintHtmlTemplate(Agent, stream, dst, dst_len, vars, First->val);
	return;
}

static int ParseVariable(UDM_ENV *Env,UDM_VARLIST *vars,char *str){
	char *tok,*lt;
	
	if((tok = udm_strtok_r(str, " \t\r\n", &lt))) {
		char * arg=NULL;
					
		if(!strcasecmp(str,"Affix")){
			char aname[1024];
			char * args[5];
			size_t narg=0;
					
			while((tok)&&(narg<5)){
				args[narg++]=tok;
				tok = udm_strtok_r(NULL, " \t", &lt);
			}
			if(narg!=4){
				sprintf(Env->errstr,"Bad Affix command");
				return UDM_ERROR;
			}
			if(args[3][0]=='/')strncpy(aname,args[3],sizeof(aname)-1);
			else	udm_snprintf(aname,sizeof(aname)-1,"%s/%s",UDM_CONF_DIR,args[3]);
			if(UdmImportAffixes(Env,args[1],args[2],aname)){
				udm_snprintf(Env->errstr,sizeof(Env->errstr)-1,"Can't load affix :%s",aname);
				return UDM_ERROR;
			}
		}else
		if(!strcasecmp(str,"Spell")){
			char aname[1024];
			char * args[5];
			size_t narg=0;

			while((tok)&&(narg<5)){
				args[narg++]=tok;
				tok = udm_strtok_r(NULL, " \t", &lt);
			}
			if(narg!=4){
				sprintf(Env->errstr,"Bad Spell command");
				return UDM_ERROR;
			}
			if(args[3][0]=='/')strncpy(aname,args[3],sizeof(aname)-1);
			else	udm_snprintf(aname,sizeof(aname)-1,"%s/%s",UDM_CONF_DIR,args[3]);
			if(UdmImportDictionary(Env,args[1],args[2],aname,0,"")){
				udm_snprintf(Env->errstr,sizeof(Env->errstr)-1,"Can't load dictionary :%s",aname);
				return UDM_ERROR;
			}
		}else
		if(!strcasecmp(str, "IspellUsePrefixes")) {
			char sel[8];
			int val = 1;
			sscanf(str+17, "%4s", sel);
			if (!strncasecmp(sel, "no", 2)) {
				val = 0;
			}
			UdmVarListReplaceInt(&Env->Vars, "IspellUsePrefixes", val);
		}else
		if(!strcasecmp(str,"StopwordFile")){
			char aname[1024];
			
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			if(arg){
				if(arg[0]=='/')strncpy(aname,arg,sizeof(aname)-1);
				else	udm_snprintf(aname,sizeof(aname)-1,"%s/%s",UDM_CONF_DIR,arg);
				if(UdmStopListLoad(Env,aname)){
					return UDM_ERROR;
				}
			}else{
				sprintf(Env->errstr,"Bad StopwordFile command");
				return UDM_ERROR;
			}
		}else
		if(!strcasecmp(str,"Synonym")){
			char aname[1024];
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			if(arg){
				if(arg[0]=='/')strncpy(aname,arg,sizeof(aname)-1);
				else	udm_snprintf(aname,sizeof(aname)-1,"%s/%s",UDM_CONF_DIR,arg);
				if(UdmSynonymListLoad(Env,aname)){
					return UDM_ERROR;
				}
			}else{
				sprintf(Env->errstr,"Bad Synonym command");
				return UDM_ERROR;
			}
		}else
		if(!strcasecmp(str,"DBAddr")){
			if((arg = udm_strtok_r(NULL, " \t\r\n", &lt))) {
			  UdmDBListAdd(&Env->dbl, arg, UDM_OPEN_MODE_READ);
/*			  UdmVarListReplaceStr(vars,tok,arg);*/
			}
		}else
		if((str[0]=='R'||str[0]=='r')&&(str[1]>='0')&&(str[1]<='9')){
			float r;
			int ir;
			arg = udm_strtok_r(NULL, " =\t\r\n", &lt); 
			if(arg){
				r = (float)UDM_ATOF(arg);
				srand((unsigned)time(0));
				r = r * rand() / RAND_MAX; ir = (int)r;
				UdmVarListReplaceInt(vars,str,ir);
			}
		}else
		if(!strcasecmp(str,"HlBeg")){
			UdmVarListReplaceStr(vars,"HlBeg",lt);
		}else
		if(!strcasecmp(str,"HlEnd")){
			UdmVarListReplaceStr(vars,"HlEnd",lt);
		}else
		if(!strcasecmp(str,"DateFormat")){
			UdmVarListReplaceStr(vars, "DateFormat", lt);
		}else
		if(!strcasecmp(str,"Limit")){
			char * sc, * nm;
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			if((sc=strchr(arg,':'))){
				*sc='\0';sc++;
				nm=(char*)malloc(strlen(arg)+8);
				sprintf(nm,"Limit-%s",arg);
				UdmVarListReplaceStr(vars,nm,sc);
				UDM_FREE(nm);
			}
		}else
		if(!strcasecmp(str,"CrossWords")){
			UdmVarListReplaceStr(vars,"CrossWords",lt);
		}else
		if(!strcasecmp(str,"Alias")){
			char * arg1;
			
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			arg1 = udm_strtok_r(NULL, " \t\r\n", &lt);
			if(arg1){
				UDM_MATCH Alias;
				Alias.pattern=arg;
				Alias.arg=arg1;
				Alias.match_type=UDM_MATCH_BEGIN;
				Alias.case_sense=0;
				UdmMatchListAdd(&Env->Aliases,&Alias,Env->errstr,sizeof(Env->errstr));
			}
		}else
		if(!strcasecmp(str,"MaxWordLen")){
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			if(arg)Env->WordParam.max_word_len=atoi(arg);
		}else
		if(!strcasecmp(str,"MinWordLen")){
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			if(arg)Env->WordParam.min_word_len=atoi(arg);
		}else
		if(!strcasecmp(str,"ExcerptSize")){
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			if (arg) UdmVarListReplaceInt(vars, tok, atoi(arg));
		}else
		if(!strcasecmp(str,"LocalCharset")){
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			if (arg) {
				UdmVarListReplaceStr(vars, tok, arg);
				Env->lcs = UdmGetCharSet(arg);
			}
		}else
		if(!strcasecmp(str,"BrowserCharset")){
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			if (arg) {
				UdmVarListReplaceStr(vars, tok, arg);
				Env->bcs = UdmGetCharSet(arg);
			}
		}else
		if(!strcasecmp(str,"Mime")){
			char *mime_arg[4];
			int argn = 0;

			tok = udm_strtok_r(NULL, " \t\r\n", &lt);
			while((tok)&&(argn<3)){
                                 mime_arg[argn++]=tok;
                                 tok = udm_strtok_r(NULL, " \t", &lt);
			}
			if(argn != 3){
				sprintf(Env->errstr, "Error: too %s arguments for Mime command\n", (argn < 3 ? "few" : "many"));
				return UDM_ERROR;
			}else{
				UDM_PARSER P;
				P.from_mime = mime_arg[0];
				P.to_mime = mime_arg[1];
				P.cmd = mime_arg[2];
				UdmParserAdd(&Env->Parsers, &P);
			}
		}else{
			arg = udm_strtok_r(NULL, " \t\r\n", &lt);
			UdmVarListReplaceStr(vars,tok,arg);
		}
	}
	return UDM_OK;
}

/* Load template  */
int UdmTemplateLoad(UDM_ENV * Env,UDM_VARLIST * vars,const char * tname, UDM_VARLIST *tmpl){
	FILE		*file;
	char		str[1024];
	char		ostr[1024];
	const char	*dbaddr=NULL;
	int		variables=0;
	char		cursection[128]="";
	char		*cur=NULL;
	char		nameletter[]=
				"abcdefghijklmnopqrstuvwxyz"
				"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
				"0123456789._";
	
	if(!(file=fopen(tname,"r"))){
		udm_snprintf(Env->errstr,sizeof(Env->errstr)-1,"Unable to open template '%s': %s",tname,strerror(errno));
		return(1);
	}
	
	while(fgets(str,sizeof(str)-1,file)){
		char	*s;
		
		str[sizeof(str)-1]='\0';
		strcpy(ostr,str);
		
		s=UdmTrim(str," \r\n");
		
		if(!strcasecmp(s,"<!--variables")){
			variables=1;
			continue;
		}
		
		if(!strcmp(s,"-->") && variables){
			variables=0;
			continue;
		}
		
		if(variables){
			int r;
			if(!*s)continue;
			if(*s=='#')continue;
			
			if(UDM_OK!=(r=ParseVariable(Env,vars,s)))
				return r;
			continue;
		}
		
		if(!memcmp(s,"<!--",4)){
			char *e;
			
			for(e=s+4;(*e)&&(strchr(nameletter,*e)||(*e=='/'));e++);
			
			if(!strcmp(e,"-->")){
				*e='\0';
				s+=4;
				
				if(s[0]=='/'){
					if(!strcasecmp(s+1,cursection) && cursection[0]){
						UDM_VAR *I;
						tmpl->Var=(UDM_VAR*)realloc(tmpl->Var,(tmpl->nvars+1)*sizeof(UDM_VAR));
						I=&tmpl->Var[tmpl->nvars];
						I->name = (char*)strdup(cursection);
						I->val = (char*)strdup(cur?cur:"");
						I->txt_val = (char*)strdup(cur?cur:"");
						tmpl->nvars++;
						cursection[0]='\0';
						UDM_FREE(cur);
						continue;
					}
				}else
				if(s[1]){
					strncpy(cursection,s,sizeof(cursection));
					cursection[sizeof(cursection)-1]='\0';
					continue;
				}
			}
		}
		
		if(!cursection[0])
			continue;
		
		if(!cur){
			cur = (char*)strdup(ostr);
		}else{
			cur=(char*)realloc(cur,strlen(cur)+strlen(ostr)+1);
			strcat(cur,ostr);
		}
	}
	fclose(file);
	UDM_FREE(cur);
	
	if(Env->Spells.nspell) {
		UdmSortDictionary(&Env->Spells);
		UdmSortAffixes(&Env->Affixes, &Env->Spells);
	}
	UdmSynonymListSort(&Env->Synonyms);
	
	
#ifdef HAVE_SQL
	if(Env->dbl.nitems == 0) {
	  if (UDM_OK!=UdmDBListAdd(&Env->dbl, dbaddr = "mysql://localhost/mnogosearch", UDM_OPEN_MODE_READ)) {
	    sprintf(Env->errstr,"Invalid DBAddr: '%s'", (!dbaddr)?"NULL":dbaddr);
	    return UDM_ERROR;
	  }
	}
#endif
	if(Env->dbl.nitems == 0) {
	  if (UDM_OK!=UdmDBListAdd(&Env->dbl, dbaddr =  "searchd://localhost/", UDM_OPEN_MODE_READ)) {
	    sprintf(Env->errstr,"Invalid DBAddr: '%s'", (!dbaddr)?"NULL":dbaddr);
	    return UDM_ERROR;
	  }
	}
	
	return UDM_OK;
}

