/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/


#define STREAM_LIB

#include	<stdio.h>
#include	<fcntl.h>
#include	"machine/err.h"
#include	"stream.h"
#include	"xlerror.h"
#include	"memory_routine.h"
#include	"memory_debug.h"
#include	"task.h"
#include	"xl.h"
#include	"lock_level.h"

#define LOAD_SIZE	4096
/*
<Load option="raw/xl"> filename </Load>
*/

typedef struct delay_load_t {
	DELAY_FUNC		h;
	STREAM * 		st;
	void *			work;
	void			(*close_func)();
	struct delay_load_t *	next;
/*
	L_CHAR *		term;
*/
	AUTOMATON *		term_a;
	L_CHAR *		backbuffer;
	short			bb_size;
	short			bb_ptr;

	int			escapeflag;
} DELAY_LOAD_T;

void xl_load_gc(DELAY_LOAD_T * t);


SEM	df_lock;
DELAY_LOAD_T * 	df_list;

void
init_Load(XLISP_ENV * env)
{
extern XL_SEXP * xl_Load();
	df_lock = new_lock(LL_DF);
	set_env(env,l_string(std_cm,"Load"),
		get_func_prim(xl_Load,FO_APPLICATIVE,0,2,2));
}

void
sp_gc_load()
{
DELAY_LOAD_T ** dp, * d;

	for ( dp = &df_list ; *dp ; ) {
		d = *dp;
		if ( TEST_MEM(d) ) {
			dp = &d->next;
			continue;
		}
		if ( d->close_func )
			(*d->close_func)(CF_CLOSE,d->work);
		_s_close(d->st);
		d->close_func = 0;
		d->work = 0;
		d->st = 0;
		free_automaton(d->term_a);
		if ( d->backbuffer )
			d_f_ree(d->backbuffer);
		*dp = d->next;
	}
}

void
xl_load_gc(DELAY_LOAD_T * t)
{
	if ( t == 0 )
		return;
	if ( TEST_AND_SET(t) )
		return;
/*
	gc_text((char*)t->term);
*/
	if ( t->close_func )
		(*t->close_func)(CF_GC,t->work);
	return;
}

XL_SEXP * 
xl_delay_load(XL_SEXP * s)
{
DELAY_LOAD_T * df;
char * data;
int er,size;
int flag;

	df = (DELAY_LOAD_T*)s->delay.d.func;
	gc_push(df,xl_load_gc,"xl_delay_load");
	data = mmalloc(LOAD_SIZE,gc_text);
	flag = 0;
	for ( size = 0 ; size < LOAD_SIZE ; ) {
		er = s_read(df->st,data,LOAD_SIZE);
		if ( er < 0 ) {
			if ( errno == ESYS_AGAIN )
				continue;
			if ( errno == ESYS_INTR )
				continue;
			flag = 1;
			set_sexp_inh(0,XLT_PAIR,s,s);
			s->pair.cdr = 0;
			s->pair.car = get_error(
				s->h.file,
				s->h.line,
				XLE_SYSTEM_READ_FILE,
				l_string(std_cm,"load"),
				list(n_get_string("cannot read the file"),
					0));
			goto zero_end;
		}
		else if ( er == 0 ) {
			flag = 1;
			break;
		}
		size += er;
	}
	if ( size ) {
		set_sexp_inh(0,XLT_PAIR,s,s);
		s->pair.car = get_raw_set_data(data,size);
		if ( flag ) {
			s->pair.cdr = 0;
			goto zero_end;
		}
		else	s->pair.cdr = init_delay_func(&df->h);
	}
	else {
		set_sexp_inh(0,XLT_NULL,s,s);
		goto zero_end;
	}
	gc_pop(0,0);
	return s;
zero_end:

	if ( df->close_func )
		(*df->close_func)(CF_CLOSE,df->work);
	s_close(df->st);
	df->st = 0;
	df->close_func = 0;
	df->work = 0;
	gc_pop(0,0);
	return s;
}



int
load_long_char(L_CHAR * ch,STREAM * st)
{
unsigned char c;
int er;
	for ( ; ; ) {
		er = s_read(st,&c,1);
		if ( er <= 0 )
			return er;
		if ( (*st->h.cm->to_internal)
				(ch,st->h.cm_work,c) )
			return 1;
	}
}


typedef struct state_checker {
	AUTOMATON *	a;
	int		stptr;
	int		endptr;
} STATE_CHECKER;

int en_state_checker(STATE_CHECKER * sc,L_CHAR * data);


#define STC_OK		0
#define STC_REJECT	1
#define STC_UNKNOWN	2

int
en_state_checker(STATE_CHECKER * sc,L_CHAR * data)
{
A_STATE *	state;
int p;
int min_ptr,max_ptr;

	if ( sc->a == 0 ) {
		sc->stptr ++;
		return STC_REJECT;
	}
	min_ptr = -1;
	max_ptr = -1;
	state = search_state(sc->a,AF_INIT);
	if ( state == 0 ) {
		sc->stptr ++;
		return STC_REJECT;
	}
	for ( p = sc->stptr ; data[p] ; p ++ ) {
		if ( state->flags & AF_ACCEPT ) {
			if ( min_ptr < 0 )
				min_ptr = p;
			max_ptr = p;
		}
		state = get_next_state(state,data[p]);
		if ( state == 0 ) {
			if ( max_ptr >= 0 ) {
				sc->endptr = max_ptr;
				return STC_OK;
			}
			sc->stptr ++;
			return STC_REJECT;
		}
	}
	if ( state->flags & AF_ACCEPT ) {
		if ( state->table_size == 0 ) {
			sc->endptr = p;
			return STC_OK;
		}
		return STC_UNKNOWN;
	}
	return STC_UNKNOWN;
}

void
escape_handling(L_CHAR * d)
{
L_CHAR * ptr, * dg;
L_CHAR dd;
int len;

	ptr = d;
retry:
	for ( ; *ptr ; ptr ++ ) {
		if ( *ptr != '&' )
			continue;
		if ( ptr[1] != 'c' && ptr[1] != 'C' )
			continue;
		if ( ptr[2] != 'x' && ptr[1] != 'X' )
			continue;
		break;
	}
	if ( *ptr == 0 )
		return;
	dg = &ptr[3];
	dd = 0;
	for ( ; *dg ; dg ++ ) {
		if ( '0' <= *dg && *dg <= '9' )
			dd = (dd<<4) + (*dg) - '0';
		else if ( 'a' <= *dg && *dg <= 'f' )
			dd = (dd<<4) + (*dg) - 'a' + 10;
		else if ( 'A' <= *dg && *dg <= 'F' )
			dd = (dd<<4) + (*dg) - 'A' + 10;
		else if ( *dg == ';' ) {
			dg ++;
			break;
		}
		else goto retry;
	}
	*ptr++ = dd;
	len = l_strlen(dg);
	memmove(ptr,dg,(len+1)*sizeof(L_CHAR));
	goto retry;
}

XL_SEXP * 
xl_delay_load_text(XL_SEXP * s)
{
DELAY_LOAD_T * df;
L_CHAR * data;
int er;
int ptr;
int end_f;

STATE_CHECKER sc;

	df = (DELAY_LOAD_T*)s->delay.d.func;
	gc_push(df,xl_load_gc,"xl_delay_load_text");
	data = mmalloc(sizeof(L_CHAR),gc_text);
	ptr = 0;
	data[ptr] = 0;
	end_f = 0;

	sc.a = df->term_a;
	sc.stptr = 0;
	sc.endptr = -1;

	for ( ; ; ) {
		if ( df->bb_ptr < df->bb_size ) {
			data[ptr] = df->backbuffer[
					df->bb_ptr++];
		}
		else {
			if ( df->backbuffer ) {
				d_f_ree(df->backbuffer);
				df->backbuffer = 0;
				df->bb_size = df->bb_ptr = 0;
			}
			er = load_long_char(&data[ptr],df->st);
			if ( er < 0 ) {
				if ( errno == ESYS_AGAIN )
					continue;
				if ( errno == ESYS_INTR )
					continue;
				set_sexp_inh(0,XLT_PAIR,s,s);
				s->pair.cdr = 0;
				s->pair.car = get_error(
					s->h.file,
					s->h.line,
					XLE_SYSTEM_READ_FILE,
					l_string(std_cm,"load"),
					list(n_get_string(
						"cannot read the file"),
						0));
				gc_pop(0,0);
				return s;
			}
			else if ( er == 0 ) {
				s_close(df->st);
				df->st = 0;
				if ( df->backbuffer )
					d_f_ree(df->backbuffer);
				df->backbuffer = 0;
				free_automaton(df->term_a);
				df->term_a = 0;
				end_f = 1;
				break;
			}
		}
		ptr ++;
		data = mrealloc(data,sizeof(L_CHAR)*(ptr+1),gc_text);
		data[ptr] = 0;

		if ( en_state_checker(&sc,data) == 0 ) {
			if ( ptr - sc.endptr ) {
			L_CHAR * bb;
			int bb_size;
				if ( df->backbuffer ){
					bb = df->backbuffer;
					bb_size = df->bb_size 
						- df->bb_ptr;
				}
				else {
				 	bb = 0;
					bb_size = 0;
				}
				df->backbuffer =
					d_alloc(
						sizeof(L_CHAR)*
						((ptr-sc.endptr)
						+ bb_size));
				memcpy(df->backbuffer,
					&data[sc.endptr],
					sizeof(L_CHAR)*
					(ptr-sc.endptr));
				if ( bb && bb_size )
					memcpy(&df->backbuffer
						[ptr-sc.endptr],
						&bb[df->bb_ptr],
						bb_size);
				df->bb_size = ptr-sc.endptr+bb_size;
				df->bb_ptr = 0;
				if ( bb )
					d_f_ree(bb);
			}
			data[sc.stptr] = 0;
			break;
		}
/*
		if ( ptr < len )
			continue;
		if ( len && l_strcmp(&data[ptr-len],df->term) == 0 ) {
			data[ptr-len] = 0;
			break;
		}
*/
	}
	if ( end_f ) {
		if ( data[0] == 0 ) {
			set_sexp_inh(0,XLT_NULL,s,s);
		}
		else {
			set_sexp_inh(0,XLT_PAIR,s,s);
			if ( df->escapeflag )
				escape_handling(data);
			s->pair.car = get_string(data);
			s->pair.cdr = 0;
		}
	}
	else {
		set_sexp_inh(0,XLT_PAIR,s,s);
		if ( df->escapeflag )
			escape_handling(data);
		s->pair.car = get_string(data);
		s->pair.cdr = init_delay_func(&df->h);
	}
	gc_pop(0,0);
	return s;
}

int
set_encoding(XL_SEXP * ret)
{
XL_SEXP * en, * q;
XL_SYM_FIELD * sf;
L_CHAR * code;
int i;
	for ( i = 2 ; i > 0 ; i -- , ret = cdr(ret) ) {
		if ( get_type(ret) != XLT_PAIR )
			return 0;
		en = car(ret);
		if ( get_type(en) != XLT_PAIR )
			continue;
		q = car(en);
		if ( get_type(q) != XLT_SYMBOL )
			continue;
		if ( l_strcmp(q->symbol.data,l_string(std_cm,"?xml")) != 0 &&
			l_strcmp(q->symbol.data,l_string(std_cm,"?xl")) != 0 )
			continue;
		code = 0;
		for ( sf = q->symbol.field ; sf ; sf = sf->next ) {
			if ( l_strcmp(sf->name,l_string(std_cm,"encoding")) == 0 ) {
				code = sf->data;
				break;
			}
		}
		if ( code == 0 )
			continue;
		_q_xml_encoding(ret,code);
		return 1;
	}
	return 0;
}


XL_SEXP *
load_file(XLISP_ENV * env,XL_SEXP * s,int type,L_CHAR * filename,
	L_CHAR * encode,AUTOMATON * term_a,
	int flags,
	void (*func)(),void * work)
{
DELAY_LOAD_T * df;
STREAM * st;
XL_SEXP * ss,* ret,* r;
L_CHAR * access_filename;
void gc_gb_sexp();
L_CHAR * _s_path;

	if ( s && s->h.file)
		access_filename = get_script(s->h.file->name,filename);
	else 	access_filename = get_script(0,filename);

	if ( access_filename == 0 )
		goto access_error;
	switch ( type ) {
	case 0:
		st = s_open_file(n_string(std_cm,access_filename),O_RDONLY);
		if ( st == 0 )
			goto access_error;
		df = mmalloc(sizeof(*df),xl_load_gc);
		df->st = st;
		df->close_func = func;
		df->work = work;
		df->term_a = 0;
		df->backbuffer = 0;
		df->escapeflag = 0;
		df->h.func = xl_delay_load;
		df->h.gc_func = xl_load_gc;
		df->h.check_func = 0;
		lock_task(df_lock);
		df->next = df_list;
		df_list = df;
		unlock_task(df_lock,"xl_Load");
		d_f_ree(access_filename);
		return init_delay_func(&df->h);
	case 1:
	case 4:
		st = s_open_file(n_string(std_cm,access_filename),O_RDONLY);
		if ( st == 0 )
			goto access_error;

		ret = init_parse(st,access_filename,access_filename);
		if ( type == 4 && ret )
			ret->h.file->flags |= XLF_HTML;
		if ( ret )
			ret->h.file->flags |= flags;
		if ( encode )
			_q_xml_encoding(ret,encode);
		else	set_encoding(ret);
		set_close_func(ret->h.file,func,work);
		d_f_ree(access_filename);
		return ret;
	case 2:
		st = s_open_file(n_string(std_cm,access_filename),O_RDONLY);
		if ( st == 0 )
			goto access_error;
		_s_path = get_script_path();
		set_script_path(access_filename);
		gc_push(0,0,"Load");
		ss = init_parse(st,access_filename,access_filename);
		if ( ss )
			ss->h.file->flags |= flags;
		if ( encode )
			_q_xml_encoding(ss,encode);
		else	set_encoding(ss);
		set_close_func(ss->h.file,func,work);
		ret = 0;

		gc_pop(ss,gc_gb_sexp);

		gc_push(ss,gc_gb_sexp,"Load");		
		gc_push(ss,gc_gb_sexp,"load");

		for ( ; get_type(ss) == XLT_PAIR ; ) {

			lock_parse();
			lock_mem();
			r = car(ss);
			ss = cdr(ss);
			gc_pop(0,0);
			gc_push(0,0,"Load");
			gc_set_nl(r,gc_gb_sexp);
			gc_set_nl(ss,gc_gb_sexp);

			unlock_mem();
			unlock_parse("Load");
			ret = eval(env,r);
			if ( get_type(ret) == XLT_ERROR ) {
				d_f_ree(access_filename);
				gc_pop(ret,gc_gb_sexp);
				gc_pop(ret,gc_gb_sexp);
				set_script_path(_s_path);
				if ( _s_path )
					d_f_ree(_s_path);
				if ( ret->err.code == XLE_SYSTEM_LOOP_BREAK )
					return ret->err.data;
				return ret;
			}

/*
			ss = cdr(ss);
			gc_pop(ss,gc_gb_sexp);
			gc_push(ss,gc_gb_sexp,"Load");
*/
		}
		gc_pop(ret,gc_gb_sexp);
		gc_pop(ret,gc_gb_sexp);
		d_f_ree(access_filename);
		set_script_path(_s_path);
		if ( _s_path )
			d_f_ree(_s_path);
		return ret;
	case 3:
	case 5:
		st = s_open_file(n_string(std_cm,access_filename),O_RDONLY);
		if ( st == 0 )
			goto access_error;
		if ( encode )
			set_encoding_st(st,encode);
		df = mmalloc(sizeof(*df),xl_load_gc);
		df->st = st;
		df->close_func = func;
		df->work = work;
		df->term_a = term_a;
		df->backbuffer = 0;
		df->bb_size = 0;
		df->escapeflag = type-3;
		df->h.func = xl_delay_load_text;
		df->h.gc_func = xl_load_gc;
		df->h.check_func = 0;
		lock_task(df_lock);
		df->next = df_list;
		df_list = df;
		unlock_task(df_lock,"xl_Load");
		d_f_ree(access_filename);
		return init_delay_func(&df->h);
	}
	er_panic("load_file(1)");
access_error:
	if ( s )
		return get_error(
			s->h.file,
			s->h.line,
			XLE_PROTO_OPEN_FILE,
			l_string(std_cm,"load"),
			list(	n_get_string("cannot open the file"),
				get_string(filename),
				0));
	else	return get_error(
			0,
			0,
			XLE_PROTO_OPEN_FILE,
			l_string(std_cm,"load"),
			list(	n_get_string("cannot open the file"),
				get_string(filename),
				0));
}

XL_SEXP *
xl_Load(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV * arg_env,XL_SYM_FIELD * sf)
{
L_CHAR * filename;
int type;
XL_SEXP * ss;
L_CHAR * encode;
AUTOMATON * term_a;
int flags;

	type = 1;
	encode = 0;
	flags = 0;
	for ( ; sf ; sf = sf->next ) {
		if ( l_strcmp(sf->name,l_string(std_cm,"option"))
				== 0 || 
		     l_strcmp(sf->name,l_string(std_cm,"format.mode"))
				== 0 ) {
			if ( l_strcmp(sf->data,l_string(std_cm,"raw"))
					== 0 )
				type = 0;
			else if ( l_strcmp(sf->data,
					l_string(std_cm,"xl"))
					== 0 )
				type = 1;
			else if ( l_strcmp(sf->data,
					l_string(std_cm,"html"))
					== 0 ) {
				type = 4;
				flags = XLF_TEXT;
			}
			else if ( l_strcmp(sf->data,
					l_string(std_cm,"exec"))
					== 0 )
				type = 2;
			else if ( l_strcmp(sf->data,
					l_string(std_cm,"text"))
					== 0 )
				type = 3;
			else if ( l_strcmp(sf->data,
					l_string(std_cm,"text.escape"))
					== 0 )
				type = 5;
			else goto invalid_param;
		}
		else if ( l_strcmp(sf->name,l_string(std_cm,"format.text"))
				== 0 ) {
			if ( l_strcmp(sf->data,l_string(std_cm,"on")) == 0 ) {
				flags |= XLF_TEXT;
			}
			else {
				flags &= ~XLF_TEXT;
			}
		}
		else if ( l_strcmp(sf->name,l_string(std_cm,"encoding"))
				== 0 ) {
			encode = sf->data;
		}
		else if ( l_strcmp(sf->name,l_string(std_cm,"terminate"))
				== 0 ) {
			term_a = get_fa(sf->data);
		}
	}

	ss = get_el(s,1);
	switch ( get_type(ss) ) {
	case XLT_ERROR:
		return ss;
	case XLT_STRING:
		filename = ss->string.data;
		break;
	default:
		goto typemissmatch;
	}
	return load_file(env,s,type,filename,encode,term_a,flags,0,0);
typemissmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"load"),
		list(	n_get_string("type missmatch"),
			0));
invalid_param:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"load"),
		list(	n_get_string("invalid parameter"),
			0));
}


int
lock_file(
	XL_SEXP ** retp,
	CALL_LOCK_DESCRIPTER * clp,
	L_CHAR ** app,
	L_CHAR * _filename,
	int type,
	char * __fn)
{
XL_GETFILE * gf;
L_CHAR * target[2];
L_CHAR * fn;
XL_SEXP * ret;
L_CHAR * access_path;
int _ret;
int i;
CALL_LOCK_DESCRIPTER lr;

	_ret = 0;
	target[0] = target[1] = 0;
	fn = nl_copy_str(std_cm,__fn);
	ret = get_path(target,&gf,_filename,0,fn);
	if ( get_type(ret) == XLT_ERROR ) {
		_ret = -1;
		goto end;
	}
	d_f_ree(fn);

	for ( i = 0 ; i < 2 ; i ++ ) {
		switch ( gf->flags & XLGFM_LOCK ) {
		case XLGF_FULL_PATH:
			lr = call_lock(target[i],
					CLT_WRITE_LOCK,0,0);
			if ( cl_error_check(lr) ) {
				ret = get_cl_error(0,__fn);
				_ret = -1;
				goto end;
			}
			break;
		case XLGF_URL_PATH:
			lr = call_lock(_filename,
					CLT_WRITE_LOCK,0,0);
			if ( cl_error_check(lr) ) {
				ret = get_cl_error(0,__fn);
				_ret = -1;
				goto end;
			}
			break;
		}
		access_path = target[i];
		if ( ret->err.code != XLE_PROTO_OPEN_FILE ) {
			_ret = -1;
			goto end;
		}
	}
	if ( i == 2 ) {
		_ret = -1;
		ret = 0;
	}
	else {
		if ( target[1-i] )
			d_f_ree(target[1-i]);
	}
end:
	*retp = ret;
	*clp = lr;
	*app = access_path;
	return _ret;
}

int
unlock_file(
	CALL_LOCK_DESCRIPTER cl,
	L_CHAR * app)
{
	if( app )
		d_f_ree(app);
	call_unlock(cl);
	return 0;
}


