/*
 * $Id: Awk.cpp,v 1.22 2007-01-25 14:14:55 bacon Exp $
 */

#include "stdafx.h"
#include "ase.h"
#include "Awk.h"
#include "AwkExtio.h"
#include "Buffer.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wctype.h>
#include <stdio.h>

#define DBGOUT(x) do { if (debug) OutputDebugString (x); } while(0)
#define DBGOUT2(awk,x) do { if (awk->debug) OutputDebugString (x); } while(0)

STDMETHODIMP CAwk::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&IID_IAwk,
	};

	for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
	{
		if (/*Inline*/IsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

CAwk::CAwk (): 
	handle (NULL), 
	read_src_buf (NULL), 
	write_src_buf (NULL),
	write_extio_buf (NULL),
	bfn_list (NULL),
	entry_point (NULL),
	debug (FALSE),
	use_longlong (FALSE)
{
	/* TODO: what is the best default option? */
	option = 
		ASE_AWK_IMPLICIT | 
		ASE_AWK_EXPLICIT | 
		ASE_AWK_UNIQUEFN | 
		ASE_AWK_IDIV |
		ASE_AWK_SHADING | 
		ASE_AWK_SHIFT | 
		ASE_AWK_EXTIO | 
		ASE_AWK_BLOCKLESS | 
		ASE_AWK_STRBASEONE | 
		ASE_AWK_STRIPSPACES | 
		ASE_AWK_NEXTOFILE |
		ASE_AWK_CRLF;
	memset (&max_depth, 0, sizeof(max_depth));

	errnum    = 0;
	errlin    = 0;
	errmsg[0] = ASE_T('\0');
}

CAwk::~CAwk ()
{
	while (bfn_list != NULL)
	{
		bfn_t* next = bfn_list->next;
		free (bfn_list->name.ptr);
		free (bfn_list);
		bfn_list = next;
	}

	if (entry_point != NULL) SysFreeString (entry_point);

	if (write_extio_buf != NULL) write_extio_buf->Release ();
	if (write_src_buf != NULL) write_src_buf->Release ();
	if (read_src_buf != NULL) read_src_buf->Release ();

	if (handle != NULL) 
	{
		ase_awk_close (handle);
		handle = NULL;
		DBGOUT (_T("closed awk"));
	}
}

static void* awk_malloc (ase_size_t n, void* custom_data)
{
	return malloc (n);
}

static void* awk_realloc (void* ptr, ase_size_t n, void* custom_data)
{
	return realloc (ptr, n);
}

static void awk_free (void* ptr, void* custom_data)
{
	free (ptr);
}

static ase_real_t awk_pow (ase_real_t x, ase_real_t y)
{
	return pow (x, y);
}

static void awk_abort (void* custom_data)
{
	abort ();
}

static int awk_sprintf (
	ase_char_t* buf, ase_size_t len, const ase_char_t* fmt, ...)
{
	int n;
	va_list ap;

	va_start (ap, fmt);
	n = _vsntprintf (buf, len, fmt, ap);
	if (n < 0 || (ase_size_t)n >= len)
	{
		if (len > 0) buf[len-1] = ASE_T('\0');
		n = -1;
	}
	va_end (ap);
	return n;
}

static void awk_aprintf (const ase_char_t* fmt, ...)
{
	va_list ap;
	int n;
	ase_char_t buf[1024];

	va_start (ap, fmt);
	n = _vsntprintf (buf, ASE_COUNTOF(buf), fmt, ap);
	if (n < 0) buf[ASE_COUNTOF(buf)-1] = ASE_T('\0');

	MessageBox (NULL, buf, ASE_T("Assertion Failure"), MB_OK|MB_ICONERROR);
	va_end (ap);
}

static void awk_dprintf (const ase_char_t* fmt, ...)
{
	va_list ap;
	va_start (ap, fmt);
	_vftprintf (stderr, fmt, ap);
	va_end (ap);
}

static ase_ssize_t __read_source (
	int cmd, void* arg, ase_char_t* data, ase_size_t count)
{
	CAwk* awk = (CAwk*)arg;

	if (cmd == ASE_AWK_IO_OPEN) 
	{
		return (ase_ssize_t)awk->Fire_OpenSource (0);
	}
	else if (cmd == ASE_AWK_IO_CLOSE)
	{
		return (ase_ssize_t)awk->Fire_CloseSource (0);
	}
	else if (cmd == ASE_AWK_IO_READ)
	{
		if (awk->read_src_buf == NULL)
		{
			HRESULT hr = CoCreateInstance (
				CLSID_Buffer, NULL, CLSCTX_ALL, 
				IID_IBuffer, (void**)&awk->read_src_buf);
			if (FAILED(hr)) 
			{
				DBGOUT2 (awk, _T("cannot create source input buffer"));
				return -1;
			}

			awk->read_src_pos = 0;
			awk->read_src_len = 0;
		}

		CBuffer* tmp = (CBuffer*)awk->read_src_buf;

		if (awk->read_src_pos >= awk->read_src_len)
		{
			INT n = awk->Fire_ReadSource (awk->read_src_buf);
			if (n <= 0) return (ase_ssize_t)n;

			if (SysStringLen(tmp->str) < (UINT)n) return -1;
			awk->read_src_pos = 0;
			awk->read_src_len = n;
		}

		ASE_AWK_ASSERT (awk->handle, 
			awk->read_src_pos < awk->read_src_len);

		BSTR str = tmp->str;
		INT left = awk->read_src_len - awk->read_src_pos;
		if (left > (ase_ssize_t)count)
		{
			memcpy (data,
				((TCHAR*)str)+awk->read_src_pos,
				count * ASE_SIZEOF(ase_char_t));
			awk->read_src_pos += count;
			return count;
		}
		else
		{
			memcpy (data, 
				((TCHAR*)str)+awk->read_src_pos,
				left * ASE_SIZEOF(ase_char_t));
			awk->read_src_pos = 0;
			awk->read_src_len = 0;
			return (ase_ssize_t)left;
		}
	}

	return -1;
}

static ase_ssize_t __write_source (
	int cmd, void* arg, ase_char_t* data, ase_size_t count)
{
	CAwk* awk = (CAwk*)arg;

	if (cmd == ASE_AWK_IO_OPEN) 
	{
		return (ase_ssize_t)awk->Fire_OpenSource (1);
	}
	else if (cmd == ASE_AWK_IO_CLOSE)
	{
		return (ase_ssize_t)awk->Fire_CloseSource (1);
	}
	else if (cmd == ASE_AWK_IO_WRITE)
	{
		HRESULT hr;

		if (awk->write_src_buf == NULL)
		{
			hr = CoCreateInstance (
				CLSID_Buffer, NULL, CLSCTX_ALL, 
				IID_IBuffer, (void**)&awk->write_src_buf);
			if (FAILED(hr)) 
			{
				DBGOUT2 (awk, _T("cannot create source output buffer"));
				return -1;
			}
		}

		CBuffer* tmp = (CBuffer*)awk->write_src_buf;
		if (tmp->PutValue (data, count) == FALSE) 
		{
			DBGOUT2 (awk, _T("cannot set source output buffer"));
			return -1; 
		}

		INT n = awk->Fire_WriteSource (awk->write_src_buf);
		if (n > (INT)count) return -1; 
		return (ase_ssize_t)n;
	}

	return -1;
}

static int __handle_bfn (
	ase_awk_run_t* run, const ase_char_t* fnm, ase_size_t fnl)
{
	CAwk* awk = (CAwk*)ase_awk_getruncustomdata (run);
	long nargs = (long)ase_awk_getnargs (run);
	ase_awk_val_t* v;

	SAFEARRAY* aa;
	SAFEARRAYBOUND bound[1];

	bound[0].lLbound = 0;
	bound[0].cElements = nargs;

	aa = SafeArrayCreate (VT_VARIANT, ASE_COUNTOF(bound), bound);
	if (aa == NULL)
	{
		ase_char_t buf[128];
		_sntprintf (buf, ASE_COUNTOF(buf), 
			_T("out of memory in creating argument array for '%.*s'\n"),
			fnl, fnm);
		ase_awk_setrunerror (run, ASE_AWK_ENOMEM, 0, buf);
		return -1;
	}

	for (long i = 0; i < nargs; i++)
	{
		VARIANT arg;

		VariantInit (&arg);

		v = ase_awk_getarg (run, i);

		if (v->type == ASE_AWK_VAL_INT)
		{
			if (awk->use_longlong)
			{
				arg.vt = VT_I8;
				arg.llVal = ((ase_awk_val_int_t*)v)->val;
			}
			else
			{
				arg.vt = VT_I4;
				arg.lVal = (LONG)((ase_awk_val_int_t*)v)->val;
			}
		}
		else if (v->type == ASE_AWK_VAL_REAL)
		{
			arg.vt = VT_R8;
			arg.dblVal = ((ase_awk_val_real_t*)v)->val;
		}
		else if (v->type == ASE_AWK_VAL_STR)
		{
			BSTR tmp = SysAllocStringLen (
				((ase_awk_val_str_t*)v)->buf,
				((ase_awk_val_str_t*)v)->len);

			if (arg.bstrVal == NULL)
			{
				VariantClear (&arg);
				SafeArrayDestroy (aa);

				ase_char_t buf[128];
				_sntprintf (buf, ASE_COUNTOF(buf), 
					_T("out of memory in handling '%.*s'\n"),
					fnl, fnm);
				ase_awk_setrunerror (run, ASE_AWK_ENOMEM, 0, buf);
				return -1;
			}

			arg.vt = VT_BSTR;
			arg.bstrVal = tmp;
		}
		else if (v->type == ASE_AWK_VAL_NIL)
		{
			arg.vt = VT_NULL;
		}

		HRESULT hr = SafeArrayPutElement (aa, &i, &arg);
		if (hr != S_OK)
		{
			VariantClear (&arg);
			SafeArrayDestroy (aa);			

			ase_char_t buf[128];
			_sntprintf (buf, ASE_COUNTOF(buf), 
				_T("out of memory in handling '%.*s'\n"),
				fnl, fnm);
			ase_awk_setrunerror (run, ASE_AWK_ENOMEM, 0, buf);
			return -1;
		}

		VariantClear (&arg);
	}

	BSTR name = SysAllocStringLen (fnm, fnl);
	if (name == NULL) 
	{
		SafeArrayDestroy (aa);

		ase_char_t buf[128];
		_sntprintf (buf, ASE_COUNTOF(buf), 
			_T("out of memory in handling '%.*s'\n"),
			fnl, fnm);
		ase_awk_setrunerror (run, ASE_AWK_ENOMEM, 0, buf);
					
		return -1;
	}

	ase_awk_val_t* ret;
	int n = awk->Fire_HandleBuiltinFunction (run, name, aa, &ret);
	if (n == 1)
	{
		ase_char_t buf[128];
		_sntprintf (buf, ASE_COUNTOF(buf), 
			_T("out of memory in handling '%.*s'\n"),
			fnl, fnm);
		ase_awk_setrunerror (run, ASE_AWK_ENOMEM, 0, buf);
		return -1;
	}
	else if (n == 2)
	{
		ase_char_t buf[128];
		_sntprintf (buf, ASE_COUNTOF(buf), 
			_T("no handler for '%.*s'\n"),
			fnl, fnm);
		ase_awk_setrunerror (run, ASE_AWK_ENOMEM, 0, buf);
		return -1;
	}
	else if (n == 3)
	{
		ase_char_t buf[128];
		_sntprintf (buf, ASE_COUNTOF(buf), 
			_T("return value not supported for '%.*s'\n"),
			fnl, fnm);
		ase_awk_setrunerror (run, ASE_AWK_ENOMEM, 0, buf);
		return -1;
	}

	/* name and aa are destroyed in HandleBuiltinFunction */
	//SafeArrayDestroy (aa);
	//SysFreeString (name);

	ase_awk_setretval (run, ret);
	return 0;
}

HRESULT CAwk::Parse (int* ret)
{
 	if (handle == NULL)
	{
		ase_awk_sysfns_t sysfns;

		memset (&sysfns, 0, sizeof(sysfns));
		sysfns.malloc = awk_malloc;
		sysfns.realloc = awk_realloc;
		sysfns.free = awk_free;

		sysfns.is_upper  = iswupper;
		sysfns.is_lower  = iswlower;
		sysfns.is_alpha  = iswalpha;
		sysfns.is_digit  = iswdigit;
		sysfns.is_xdigit = iswxdigit;
		sysfns.is_alnum  = iswalnum;
		sysfns.is_space  = iswspace;
		sysfns.is_print  = iswprint;
		sysfns.is_graph  = iswgraph;
		sysfns.is_cntrl  = iswcntrl;
		sysfns.is_punct  = iswpunct;
		sysfns.to_upper  = towupper;
		sysfns.to_lower  = towlower;

		sysfns.memcpy = memcpy;
		sysfns.memset = memset;
		sysfns.pow = awk_pow;
		sysfns.sprintf = awk_sprintf;
		sysfns.aprintf = awk_aprintf;
		sysfns.dprintf = awk_dprintf;
		sysfns.abort = awk_abort;

		handle = ase_awk_open (&sysfns, NULL, &errnum);
		if (handle == NULL)
		{
			errlin = 0;
			ase_awk_strxcpy (
				errmsg, ASE_COUNTOF(errmsg), 
				ase_awk_geterrstr(errnum));

			*ret = -1;

			DBGOUT (_T("cannot open awk"));
			return S_OK;
		}
		else DBGOUT (_T("opened awk successfully"));

		ase_awk_setoption (handle, option);

		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_BLOCK_PARSE, max_depth.block.parse);
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_BLOCK_RUN, max_depth.block.run);
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_EXPR_PARSE, max_depth.expr.parse);
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_EXPR_RUN, max_depth.expr.run);
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_REX_BUILD, max_depth.rex.build);
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_REX_MATCH, max_depth.rex.match);
	}

	ase_awk_clrbfn (handle);

	for (bfn_t* bfn = bfn_list; bfn != NULL; bfn = bfn->next)
	{
		if (ase_awk_addbfn (
			handle, bfn->name.ptr, bfn->name.len, 0, 
			bfn->min_args, bfn->max_args, NULL, 
			__handle_bfn) == NULL)
		{
			const ase_char_t* msg;

			DBGOUT (_T("cannot add the builtin function"));

			ase_awk_geterror (handle, &errnum, &errlin, &msg);
			ase_awk_strxcpy (errmsg, ASE_COUNTOF(errmsg), msg);

			*ret = -1;
			return S_OK;
		}
	}

	ase_awk_srcios_t srcios;

	srcios.in = __read_source;
	srcios.out = __write_source;
	srcios.custom_data = this;
	
	if (ase_awk_parse (handle, &srcios) == -1)
	{
		const ase_char_t* msg;

		ase_awk_geterror (handle, &errnum, &errlin, &msg);
		ase_awk_strxcpy (errmsg, ASE_COUNTOF(errmsg), msg);

		DBGOUT (_T("cannot parse the source code"));

		*ret = -1;
		return S_OK;
	}
	else DBGOUT (_T("parsed the source code successfully"));

	*ret = 0;
	return S_OK;
}

static ase_ssize_t __process_extio (
	int cmd, void* arg, ase_char_t* data, ase_size_t size)
{
	ase_awk_extio_t* epa = (ase_awk_extio_t*)arg;
	CAwk* awk = (CAwk*)epa->custom_data;

	if (cmd == ASE_AWK_IO_OPEN)
	{
		IAwkExtio* extio;
		CAwkExtio* extio2;
		IBuffer* read_buf;

		HRESULT hr = CoCreateInstance (
			CLSID_AwkExtio, NULL, CLSCTX_ALL, 
			IID_IAwkExtio, (void**)&extio);
		if (FAILED(hr)) 
		{
			DBGOUT2 (awk, _T("cannot create extio"));
			return -1; 
		}

		hr = CoCreateInstance (
			CLSID_Buffer, NULL, CLSCTX_ALL,
			IID_IBuffer, (void**)&read_buf);
		if (FAILED(hr)) 
		{
			DBGOUT2 (awk, _T("cannot create extio input buffer"));
			extio->Release ();
			return -1;
		}

		extio2 = (CAwkExtio*)extio;
		if (extio2->PutName (epa->name) == FALSE)
		{
			read_buf->Release ();
			extio->Release ();
			DBGOUT2 (awk, _T("cannot set the name of the extio input buffer"));
			return -1; 
		}
		extio2->type = epa->type & 0xFF;
		extio2->mode = epa->mode;

		read_buf->AddRef ();
		extio2->read_buf = read_buf;
		extio2->read_buf_pos = 0;
		extio2->read_buf_len = 0;

		INT n = awk->Fire_OpenExtio (extio);
		if (n >= 0)
		{
			extio->AddRef ();
			epa->handle = extio;
		}

		read_buf->Release ();
		extio->Release ();
		return n;
	}
	else if (cmd == ASE_AWK_IO_CLOSE)
	{
		IAwkExtio* extio;
		CAwkExtio* extio2;
	       
		extio = (IAwkExtio*)epa->handle;
		extio2 = (CAwkExtio*)extio;

		ASE_AWK_ASSERT (ase_awk_getrunawk(epa->run), extio != NULL);

		INT n = awk->Fire_CloseExtio (extio);
		if (n >= 0)
		{
			extio2->read_buf->Release();
			extio->Release();
			epa->handle = NULL;
		}

		return n;
	}
	else if (cmd == ASE_AWK_IO_READ)
	{
		IAwkExtio* extio;
		CAwkExtio* extio2;

		extio = (IAwkExtio*)epa->handle;
		extio2 = (CAwkExtio*)extio;

		ASE_AWK_ASSERT (ase_awk_getrunawk(epa->run), extio != NULL);

		CBuffer* tmp = (CBuffer*)extio2->read_buf;
		if (extio2->read_buf_pos >= extio2->read_buf_len)
		{
			INT n = awk->Fire_ReadExtio (extio, extio2->read_buf);
			if (n <= 0) return (ase_ssize_t)n;

			if (SysStringLen(tmp->str) < (UINT)n) return -1;
			extio2->read_buf_pos = 0;
			extio2->read_buf_len = n;
		}

		ASE_AWK_ASSERT (awk->handle, 
			extio2->read_buf_pos < extio2->read_buf_len);

		BSTR str = tmp->str;
		INT left = extio2->read_buf_len - extio2->read_buf_pos;
		if (left > (ase_ssize_t)size)
		{
			memcpy (data,
				((TCHAR*)str)+extio2->read_buf_pos,
				size * ASE_SIZEOF(ase_char_t));
			extio2->read_buf_pos += size;
			return size;
		}
		else
		{
			memcpy (data, 
				((TCHAR*)str)+extio2->read_buf_pos,
				left * ASE_SIZEOF(ase_char_t));
			extio2->read_buf_pos = 0;
			extio2->read_buf_len = 0;
			return (ase_ssize_t)left;
		}
	}
	else if (cmd == ASE_AWK_IO_WRITE)
	{
		HRESULT hr;
		IAwkExtio* extio = (IAwkExtio*)epa->handle;
		ASE_AWK_ASSERT (ase_awk_getrunawk(epa->run), extio != NULL);

		if (awk->write_extio_buf == NULL)
		{
			hr = CoCreateInstance (
				CLSID_Buffer, NULL, CLSCTX_ALL, 
				IID_IBuffer, (void**)&awk->write_extio_buf);
			if (FAILED(hr)) 
			{
				DBGOUT2 (awk, _T("cannot create extio output buffer"));
				return -1;
			}
		}

		CBuffer* tmp = (CBuffer*)awk->write_extio_buf;
		if (tmp->PutValue (data, size) == FALSE) return -1;

		INT n = awk->Fire_WriteExtio (extio, awk->write_extio_buf);
		if (n > (INT)size) return -1; 
		return (ase_ssize_t)n;
	}
	else if (cmd == ASE_AWK_IO_FLUSH)
	{
		IAwkExtio* extio = (IAwkExtio*)epa->handle;
		ASE_AWK_ASSERT (ase_awk_getrunawk(epa->run), extio != NULL);
		return awk->Fire_FlushExtio (extio);
	}
	else if (cmd == ASE_AWK_IO_NEXT)
	{
		IAwkExtio* extio = (IAwkExtio*)epa->handle;
		ASE_AWK_ASSERT (ase_awk_getrunawk(epa->run), extio != NULL);
		return awk->Fire_NextExtio (extio);
	}

	return -1;
}

HRESULT CAwk::Run (int* ret)
{
	const ase_char_t* entry = NULL;

	if (handle == NULL)
	{
		errnum = ASE_AWK_EINTERN;
		errlin = 0;
		_tcscpy (errmsg, _T("parse not called yet"));

		*ret = -1;
		return S_OK;
	}

	ase_awk_runios_t runios;
	runios.pipe = NULL;
	runios.coproc = NULL;
	runios.file = __process_extio;
	runios.console = __process_extio;
	runios.custom_data = this;

	if (entry_point != NULL && 
	    SysStringLen(entry_point) > 0) entry = entry_point;

	if (ase_awk_run (handle, entry, &runios, NULL, NULL, this) == -1)
	{
		const ase_char_t* msg;

		ase_awk_geterror (handle, &errnum, &errlin, &msg);
		ase_awk_strxcpy (errmsg, ASE_COUNTOF(errmsg), msg);

		DBGOUT (_T("cannot run the program"));
		*ret = -1;
		return S_OK;
	}
	else DBGOUT (_T("run the program successfully"));

	*ret = 0;
	return S_OK;
}

STDMETHODIMP CAwk::AddBuiltinFunction (
	BSTR name, int min_args, int max_args, int* ret)
{
	bfn_t* bfn;
	size_t name_len = SysStringLen(name);

	for (bfn = bfn_list; bfn != NULL; bfn = bfn->next)
	{
		if (ase_awk_strxncmp (
			bfn->name.ptr, bfn->name.len,
			name, name_len) == 0)
		{
			errnum = ASE_AWK_EEXIST;
			errlin = 0;
			_sntprintf (
				errmsg, ASE_COUNTOF(errmsg), 
				_T("'%.*s' added already\n"), 
				bfn->name.len, bfn->name.ptr);

			*ret = -1;
			return S_OK;
		}
	}

	bfn = (bfn_t*)malloc (sizeof(bfn_t));
	if (bfn == NULL) 
	{
		errnum = ASE_AWK_ENOMEM;
		errlin = 0;
		ase_awk_strxcpy (
			errmsg, ASE_COUNTOF(errmsg), 
			ase_awk_geterrstr(errnum));

		*ret = -1;
		return S_OK;
	}

	bfn->name.len = name_len;
	bfn->name.ptr = (TCHAR*)malloc (bfn->name.len * sizeof(TCHAR));
	if (bfn->name.ptr == NULL) 
	{
		free (bfn);

		errnum = ASE_AWK_ENOMEM;
		errlin = 0;
		ase_awk_strxcpy (
			errmsg, ASE_COUNTOF(errmsg), 
			ase_awk_geterrstr(errnum));

		*ret = -1;
		return S_OK;
	}
	memcpy (bfn->name.ptr, name, sizeof(TCHAR) * bfn->name.len);

	bfn->min_args = min_args;
	bfn->max_args = max_args;
	bfn->next = bfn_list;
	bfn_list = bfn;

	*ret = 0;
	return S_OK;
}

STDMETHODIMP CAwk::DeleteBuiltinFunction (BSTR name, int* ret)
{
	size_t name_len = SysStringLen(name);
	bfn_t* bfn, * next, * prev = NULL;

	for (bfn = bfn_list; bfn != NULL; bfn = next)
	{
		next = bfn->next;

		if (ase_awk_strxncmp (
			bfn->name.ptr, bfn->name.len,
			name, name_len) == 0)
		{
			free (bfn->name.ptr);
			free (bfn);

			if (prev == NULL) bfn_list = next;
			else prev->next = next;

			*ret = 0;
			return S_OK;
		}

		prev = bfn;
	}

	errnum = ASE_AWK_ENOENT;
	errlin = 0;
	ase_awk_strxcpy (
		errmsg, ASE_COUNTOF(errmsg), 
		ase_awk_geterrstr(errnum));

	*ret = -1;
	return S_OK;
}

STDMETHODIMP CAwk::get_ErrorCode(int *pVal)
{
	*pVal = errnum;
	return S_OK;
}

STDMETHODIMP CAwk::get_ErrorLine(int *pVal)
{
	*pVal = errlin;
	return S_OK;
}

STDMETHODIMP CAwk::get_ErrorMessage(BSTR *pVal)
{
	BSTR tmp = SysAllocStringLen (errmsg, _tcslen(errmsg));
	if (tmp == NULL) return E_OUTOFMEMORY;
	*pVal = tmp;
	return S_OK;
}

STDMETHODIMP CAwk::get_ImplicitVariable(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_IMPLICIT) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_ImplicitVariable(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_IMPLICIT;
	else option = option & ~ASE_AWK_IMPLICIT;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_ExplicitVariable(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_EXPLICIT) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_ExplicitVariable(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_EXPLICIT;
	else option = option & ~ASE_AWK_EXPLICIT;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_UniqueFunction(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_UNIQUEFN) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_UniqueFunction(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_UNIQUEFN;
	else option = option & ~ASE_AWK_UNIQUEFN;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_VariableShading(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_SHADING) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_VariableShading(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_SHADING;
	else option = option & ~ASE_AWK_SHADING;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_ShiftOperators(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_SHIFT) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_ShiftOperators(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_SHIFT;
	else option = option & ~ASE_AWK_SHIFT;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_IdivOperator(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_IDIV) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_IdivOperator(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_IDIV;
	else option = option & ~ASE_AWK_IDIV;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_ConcatString(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_STRCONCAT) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_ConcatString(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_STRCONCAT;
	else option = option & ~ASE_AWK_STRCONCAT;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_SupportExtio(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_EXTIO) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_SupportExtio(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_EXTIO;
	else option = option & ~ASE_AWK_EXTIO;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_SupportBlockless(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_BLOCKLESS) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_SupportBlockless(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_BLOCKLESS;
	else option = option & ~ASE_AWK_BLOCKLESS;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_StringBaseOne(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_STRBASEONE) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_StringBaseOne(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_STRBASEONE;
	else option = option & ~ASE_AWK_STRBASEONE;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_StripSpaces(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_STRIPSPACES) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_StripSpaces(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_STRIPSPACES;
	else option = option & ~ASE_AWK_STRIPSPACES;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_Nextofile(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_NEXTOFILE) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_Nextofile(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_NEXTOFILE;
	else option = option & ~ASE_AWK_NEXTOFILE;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_UseCrlf(BOOL *pVal)
{
	if (handle != NULL) option = ase_awk_getoption (handle);
	*pVal = (option & ASE_AWK_CRLF) == 1;
	return S_OK;
}

STDMETHODIMP CAwk::put_UseCrlf(BOOL newVal)
{
	if (newVal) option = option | ASE_AWK_CRLF;
	else option = option & ~ASE_AWK_CRLF;
	if (handle != NULL) ase_awk_setoption (handle, option);
	return S_OK;
}

STDMETHODIMP CAwk::get_MaxDepthForBlockParse(int *pVal)
{
	if (handle != NULL) 
	{
		max_depth.block.parse = 
			ase_awk_getmaxdepth (handle, ASE_AWK_DEPTH_BLOCK_PARSE);
	}
	*pVal = max_depth.block.parse;
	return S_OK;
}

STDMETHODIMP CAwk::put_MaxDepthForBlockParse(int newVal)
{
	max_depth.block.parse = newVal;
	if (handle != NULL) 
	{
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_BLOCK_PARSE, max_depth.block.parse);
	}
	return S_OK;
}

STDMETHODIMP CAwk::get_MaxDepthForBlockRun(int *pVal)
{
	if (handle != NULL) 
	{
		max_depth.block.run = 
			ase_awk_getmaxdepth (handle, ASE_AWK_DEPTH_BLOCK_RUN);
	}
	*pVal = max_depth.block.run;
	return S_OK;
}

STDMETHODIMP CAwk::put_MaxDepthForBlockRun(int newVal)
{
	max_depth.block.run = newVal;
	if (handle != NULL) 
	{
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_BLOCK_RUN, max_depth.block.run);
	}
	return S_OK;
}

STDMETHODIMP CAwk::get_MaxDepthForExprParse(int *pVal)
{
	if (handle != NULL) 
	{
		max_depth.expr.parse = 
			ase_awk_getmaxdepth (handle, ASE_AWK_DEPTH_EXPR_PARSE);
	}
	*pVal = max_depth.expr.parse;
	return S_OK;
}

STDMETHODIMP CAwk::put_MaxDepthForExprParse(int newVal)
{
	max_depth.expr.parse = newVal;
	if (handle != NULL) 
	{
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_EXPR_PARSE, max_depth.expr.parse);
	}
	return S_OK;
}

STDMETHODIMP CAwk::get_MaxDepthForExprRun(int *pVal)
{
	if (handle != NULL) 
	{
		max_depth.expr.run = 
			ase_awk_getmaxdepth (handle, ASE_AWK_DEPTH_EXPR_RUN);
	}
	*pVal = max_depth.expr.run;
	return S_OK;
}

STDMETHODIMP CAwk::put_MaxDepthForExprRun(int newVal)
{
	max_depth.expr.run = newVal;
	if (handle != NULL) 
	{
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_EXPR_RUN, max_depth.expr.run);
	}
	return S_OK;
}

STDMETHODIMP CAwk::get_MaxDepthForRexBuild(int *pVal)
{
	if (handle != NULL) 
	{
		max_depth.rex.build = 
			ase_awk_getmaxdepth (handle, ASE_AWK_DEPTH_REX_BUILD);
	}
	*pVal = max_depth.rex.build;
	return S_OK;
}

STDMETHODIMP CAwk::put_MaxDepthForRexBuild(int newVal)
{
	max_depth.rex.build = newVal;
	if (handle != NULL) 
	{
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_REX_BUILD, max_depth.rex.build);
	}
	return S_OK;
}

STDMETHODIMP CAwk::get_MaxDepthForRexMatch(int *pVal)
{
	if (handle != NULL) 
	{
		max_depth.rex.match = 
			ase_awk_getmaxdepth (handle, ASE_AWK_DEPTH_REX_MATCH);
	}
	*pVal = max_depth.rex.match;
	return S_OK;
}

STDMETHODIMP CAwk::put_MaxDepthForRexMatch(int newVal)
{
	max_depth.rex.match = newVal;
	if (handle != NULL) 
	{
		ase_awk_setmaxdepth (handle, 
			ASE_AWK_DEPTH_REX_MATCH, max_depth.rex.match);
	}
	return S_OK;
}

STDMETHODIMP CAwk::get_EntryPoint(BSTR *pVal)
{
	if (entry_point == NULL) *pVal = NULL;
	else
	{
		BSTR tmp = SysAllocStringLen (
			entry_point, SysStringLen(entry_point));
		if (tmp == NULL) return E_OUTOFMEMORY;
		*pVal = tmp;
	}

	return S_OK;
}

STDMETHODIMP CAwk::put_EntryPoint(BSTR newVal)
{
	if (entry_point != NULL) SysFreeString (entry_point);
	if (newVal == NULL) entry_point = newVal;
	else 
	{
		entry_point = SysAllocStringLen (newVal, SysStringLen(newVal));
		if (entry_point == NULL) return E_OUTOFMEMORY;
	}

	return S_OK;
}

STDMETHODIMP CAwk::get_Debug(BOOL *pVal)
{
	*pVal = debug;
	return S_OK;
}

STDMETHODIMP CAwk::put_Debug(BOOL newVal)
{
	debug = newVal;
	return S_OK;
}

STDMETHODIMP CAwk::get_UseLongLong(BOOL *pVal)
{
	*pVal = use_longlong;
	return S_OK;
}

STDMETHODIMP CAwk::put_UseLongLong(BOOL newVal)
{
	use_longlong = newVal;
	return S_OK;
}