/* 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);