/* Changes Samuel made:
 *		added a new static FILE * called out_stream, and a function tpl_set_outstream(FILE *newout),
 *		which sets out_stream to newout. I also added a function tpl_reset_outstream(), which sets
 *		out_stream to stdout. Oh, and out_stream is initialised to stdout.
 *		To use this, I changed all the prinfs to fprintfs, which print to out_stream.
 *
 *		The reset isn't really nessessary, it's really just tpl_set_outstream(stdout); so it might be
 *		better as a #define, or something, or not even existing, I don't really have the expertise to
 *		judge.
 */
/*  Generic CGI Template processing library */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
#include "defines.h"
#include "tpl.h"
#include "lib.h"
#define DMID 20
#include "debug.h"
#define BIGBFSZ 80000
typedef struct VLIST {char *var; char *val; struct VLIST *next;} Vlist;
#ifdef WIN32
//#define vsnprintf _vsnprintf
#endif
void emsg(char * arg_list, ...);
void imsg(char * arg_list, ...);
void dmsg(char * arg_list, ...);

static Vlist *vlist;

static FILE * out_stream=NULL;

int tpl_var_true( char *name, int t)
{
	if (t) tpl_var_add(name,"checked");
	else tpl_var_add(name,"");
	return TRUE;
}
int tpl_error(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char output[2000];

        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsnprintf(output,1000, format, arg_ptr); output[1000-1] = 0;
	tpl_var_add("error",output);
	emsg("%s",output);
	tpl_showfile("error.htm");
	return TRUE;
}

int tpl_var_add_safe(char *var, char *val)
{
	char tmp[BFSZ];
	return tpl_var_add(var,lib_web_encode(tmp,val,BFSZ-1));
}
int tpl_var_add(char *var, char *val)
{
	Vlist *v;
	for (v=vlist;v!=NULL;v=v->next) {
		if (strcmp(v->var,var)==0) {
			free(v->val);
			v->val = strdup(val);
			return TRUE;
		}
	}
	v = calloc(1,sizeof(Vlist));
	v->next = vlist;
	v->var = strdup(var);
	v->val = strdup(val);
	vlist = v;
	return TRUE;
}
char *tpl_var_find(char *var)
{
	Vlist *v;
	for (v=vlist;v!=NULL;v=v->next) {
		if (strcmp(v->var,var)==0) {
			return v->val;
		}
	}
	return "";
}
int tpl_var_free(void)
{
	Vlist *v,*vnext;
	for (v=vlist;v!=NULL;v=vnext) {
		vnext = v->next;
		free(v->var);
		v->var = NULL;
		free(v->val);
		v->val = NULL;
		free(v);
	}
	vlist = NULL;
	return TRUE;
}
int tpl_var_replace(char *line, char *out, int outlen)
{
	char *s;
	char *s2;
	char *bf=malloc(BIGBFSZ);
	for (s=line;*s!=0;s++) {
		if (strncmp(s,"||",2)==0) {
			s2 = strstr(s+2,"||");
			if (s2==NULL) continue;
			ncpy(bf,s+2,(int) (s2-s-4+2));
			//imsg("Found variable (%s)\n",bf);
			outlen -= lib_strlen(tpl_var_find(bf));
			if (outlen>0) strcpy(out,tpl_var_find(bf));
			out += lib_strlen(out);
			s = s2+1;
			continue;
		}
		outlen--;
		if (outlen<1) break;
		*out++ = *s;
	}
	*out++ = 0;
	free(bf);
	return TRUE;
}
static int nif,skipif;
int tpl_show_linex(char *bf);
int tpl_show_line(char *bf)
{
	int ret;
	char *tmp = malloc(BIGBFSZ);
	char *out;
	char *s;

	for (out=tmp,s=bf;*s!=0;) {
		if ((out-tmp)>=BIGBFSZ-1) break;
		if (*s=='|') {
			*out++ = 0;
			tpl_show_linex(tmp);
			for (out=tmp;*s!=0;) {
				if ((out-tmp)>=BIGBFSZ-1) break;
				*out++ = *s++;
				if (*s==' ') break;
				if (*s=='<') break;
				if (*s=='>') break;
			}
			*out++ = 0;
			tpl_show_linex(tmp);
			out = tmp;
		} else {
			*out++ = *s++;
		}
	}
	*out++ = 0;
	ret = tpl_show_linex(tmp);
	free(tmp);
	return ret;

}
int tpl_include_file(char *fname)
{
	FILE *f;
	char tmp[BFSZ];
	char bf[BFSZ];
	if (out_stream==NULL) out_stream = stdout;
	f = fopen(tpl_file(tmp,fname),"r");
	if (f==NULL) {
		fprintf(out_stream,"INCLUDE: Could not read (%s)\n",tpl_file(tmp,fname));
		return FALSE;
	}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		fprintf(out_stream,"%s",bf); /* Need to do standard variable replace here */
	}
	fclose(f);
	return TRUE;
}
int tpl_show_linex(char *bf)
{
	/*
		If ||begin_list|| then store until ||end_list||
		then call tpl_cb_fn which will display real lines
	*/
	char *out;
	char *var;
	char *s;
	char *vtpl;
	static char *tpl;

	out = malloc(BIGBFSZ);
	var = malloc(BIGBFSZ);

	if (skipif) {
		if (strncmp(bf,"||ifdef||",9)==0) nif++;
		if (strncmp(bf,"||else||",8)==0) {
			if (nif==0) skipif = FALSE;
			goto ret_true;
		}
		if (strncmp(bf,"||endif||",9)==0) {
			if (nif>0) nif--;
			if (nif==0) skipif = FALSE;
		}
		goto ret_true;
	}

	// Deal with SSI include syntax...
	// 	<!--#include virtual="/surgemail/ssi/head_includes.ssi" --> 
	if (strstr(bf,"<!--#include")!=NULL) {
		s = strstr(bf," virtual=\"");
		if (s!=NULL) {
			char fname[BFSZ];
			ncpy(fname,s+10,BFSZ-1);
			s = strchr(fname,'"');
			if (s!=NULL) *s = 0;
			tpl_include_file(fname);
		}
	}


	if (strncmp(bf,"||ifdef||",9)==0) {
		ncpy(var,bf+9,100);
		s = strstr(var,"||");
		if (s!=NULL) *s = 0;
		if (lib_strlen(tpl_var_find(var))==0) {
			skipif = TRUE; nif = 0;
		}
		goto ret_true;
	}
	if (strncmp(bf,"||endif||",9)==0) {
		goto ret_true;
	}
	if (strncmp(bf,"||else||",8)==0) {
		skipif = TRUE;
		nif = 0;
		goto ret_true;
	}
	if (strncmp(bf,"||begin_",8)==0) {
		if (tpl!=NULL) free(tpl);
		tpl = malloc(32000);
		strcpy(tpl,"");
		goto ret_true;
	}
	if (strncmp(bf,"||end_",6)==0) {
		if (tpl==NULL) goto ret_true;

		vtpl = tpl; tpl = NULL;
		tpl_cb_fn(bf+6,vtpl);
		free(vtpl); vtpl = NULL;
		goto ret_true;
	}
	if (tpl!=NULL) {
		if (lib_strlen(tpl) + lib_strlen(bf) + 10 > 32000) 	goto ret_false;
		strcat(tpl,bf);
		goto ret_true;
	}

	/*	This has been horribly hacked.  Without 'out' being allocated twice the
	 *	length of bf, 'out' is truncated if the parsed line exceeds the length
	 *	of bf.
	 */
	tpl_var_replace(bf,out,BIGBFSZ-1);
	if (out_stream==NULL) out_stream = stdout;
	fprintf(out_stream,"%s",out); /* Need to do standard variable replace here */
ret_true:
	free(out); free(var);
	return TRUE;
ret_false:
	free(out); free(var);
	return FALSE;
}
int tpl_showfile(char *fname)
{
	FILE *f;
	char bf[BFSZ];
	nif = skipif = 0;
	if (out_stream==NULL) out_stream = stdout;
	f = fopen(tpl_file(bf,fname),"r");
	if (f==NULL) {
		fprintf(out_stream,"HTTP/1.0 550 Sorry, no such template (%s)\r\n\r\nNo such template (%s)\r\n",tpl_file(bf,fname),tpl_file(bf,fname));
		return TRUE;
	}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		tpl_show_line(bf);
	}
	fclose(f);
	return TRUE;
}
static char templates[BFSZ];
int tpl_set_templates(char *s)
{
	strncpy(templates,s,200);
	return TRUE;
}
char *tpl_file(char *bf, char *fname)
{
	sprintf(bf,"%s/%s",templates,fname);
	return bf;
}
static char *call_name[100];
static int (*call_fn[100])(char *s);
static int ncall=0;
int tpl_callback(char *name,  int fn(char *s))
{
	call_name[ncall] = strdup(name);
	call_fn[ncall] = fn;
	ncall++;
	return TRUE;
}
int tpl_cb_fn(char *rest, char *tpl)
{
	int i;
	for (i=0; i<ncall; i++) {
		if (strncmp(rest,call_name[i],lib_strlen(call_name[i]))==0) {
			imsg("Running callback function (%s)\n",call_name[i]);
			(*call_fn[i])(tpl);
			return TRUE;
		}
	}
	if (out_stream==NULL) out_stream = stdout;
	fprintf(out_stream,"Unknown callback function used, panic (%s)\n",rest);
	return TRUE;
}

/* Samuel added this */
int tpl_set_outstream(FILE *newout)
{	out_stream = newout;
	return TRUE;
}

/* Samuel added this */
int tpl_reset_outstream()
{	out_stream = stdout;
	return TRUE;
}
