/*
 *	@file 	ejs.c
 *	@brief 	Embedded JavaScript (EJS) 
 *	@overview Main module interface logic.
 */
/********************************* Copyright **********************************/
/*
 *	@copy	default.g
 *	
 *	Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
 *	Portions Copyright (c) GoAhead Software, 1995-2000. All Rights Reserved.
 *	
 *	This software is distributed under commercial and open source licenses.
 *	You may use the GPL open source license described below or you may acquire 
 *	a commercial license from Mbedthis Software. You agree to be fully bound 
 *	by the terms of either license. Consult the LICENSE.TXT distributed with 
 *	this software for full details.
 *	
 *	This software is open source; 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. See the GNU General Public License for more 
 *	details at: http://www.mbedthis.com/downloads/gplLicense.html
 *	
 *	This program is distributed WITHOUT ANY WARRANTY; without even the 
 *	implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *	
 *	This GPL license does NOT permit incorporating this software into 
 *	proprietary programs. If you are unable to comply with the GPL, you must
 *	acquire a commercial license to use this software. Commercial licenses 
 *	for this software and support services are available from Mbedthis 
 *	Software at http://www.mbedthis.com 
 *	
 *	@end
 */
/********************************** Includes **********************************/

#include	"ejsInternal.h"

#if BLD_FEATURE_EJS

/********************************** Local Data ********************************/

/*
 *	These fields must be locked before any access when multithreaded
 */
static MprVar	master;					/* Master object */
static MprArray	*ejsList;				/* List of ej handles */

#if BLD_FEATURE_MULTITHREAD
static EjsLock		lock;
static EjsUnlock	unlock;
static void			*lockData;
#define ejsLock()	if (lock) { (lock)(lockData); } else
#define ejsUnlock()	if (unlock) { (unlock)(lockData); } else
#else
#define ejsLock()		
#define ejsUnlock()	
#endif


/*
  save/restore global ejs state - used to cope with simultaneous ejs requests
  this is a workaround for the use of global variables in ejs
*/
struct ejs_state_ctx {
	MprVar master;
	MprArray *ejsList;
};

void *ejs_save_state(void)
{
	struct ejs_state_ctx *ctx = talloc(talloc_autofree_context(), struct ejs_state_ctx);
	ctx->master = master;
	ctx->ejsList = ejsList;
	return ctx;
}

void ejs_restore_state(void *ptr)
{
	struct ejs_state_ctx *ctx = talloc_get_type(ptr, struct ejs_state_ctx);
	master = ctx->master;
	ejsList = ctx->ejsList;
	talloc_free(ctx);
}


/****************************** Forward Declarations **************************/

static char	*getNextVarToken(char **next, char *tokBuf, int tokBufLen);

/************************************* Code ***********************************/
/*
 *	Initialize the EJ subsystem
 */

int ejsOpen(EjsLock lockFn, EjsUnlock unlockFn, void *data)
{
	MprVar	*np;

#if BLD_FEATURE_MULTITHREAD
	if (lockFn) {
		lock = lockFn;
		unlock = unlockFn;
		lockData = data;
	}
#endif
	ejsLock();

	/*
	 *	Master is the top level object (above global). It is used to clone its
	 *	contents into the global scope for each. This is never visible to the
	 *	user, so don't use ejsCreateObj().
	 */
	master = mprCreateObjVar("master", EJS_SMALL_OBJ_HASH_SIZE);
	if (master.type == MPR_TYPE_UNDEFINED) {
		ejsUnlock();
		return MPR_ERR_CANT_ALLOCATE;
	}

	ejsList = mprCreateArray();
	ejsDefineStandardProperties(&master);

	/*
	 *	Make these objects immutable
	 */
	np = mprGetFirstProperty(&master, MPR_ENUM_FUNCTIONS | MPR_ENUM_DATA);
	while (np) {
		mprSetVarReadonly(np, 1);
		np = mprGetNextProperty(&master, np, MPR_ENUM_FUNCTIONS | 
			MPR_ENUM_DATA);
	}
	ejsUnlock();
	return 0;
}

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

void ejsClose()
{
	ejsLock();
	mprDestroyArray(ejsList);
	mprDestroyVar(&master);
	ejsUnlock();
}

/******************************************************************************/
/*
 *	Create and initialize an EJS engine
 */

EjsId ejsOpenEngine(EjsHandle primaryHandle, EjsHandle altHandle)
{
	MprVar	*np;
	Ejs		*ep;

	ep = (Ejs *)mprMalloc(sizeof(Ejs));
	if (ep == 0) {
		return (EjsId) -1;
	}
	memset(ep, 0, sizeof(Ejs));

	ejsLock();
	ep->eid = (EjsId) mprAddToArray(ejsList, ep);
	ejsUnlock();

	/*
	 *	Create array of local variable frames
	 */
	ep->frames = mprCreateArray();
	if (ep->frames == 0) {
		ejsCloseEngine(ep->eid);
		return (EjsId) -1;
	}
	ep->primaryHandle = primaryHandle;
	ep->altHandle = altHandle;

	/*
	 *	Create first frame: global variables
	 */
	ep->global = (MprVar*) mprMalloc(sizeof(MprVar));
	*ep->global = ejsCreateObj("global", EJS_OBJ_HASH_SIZE);
	if (ep->global->type == MPR_TYPE_UNDEFINED) {
		ejsCloseEngine(ep->eid);
		return (EjsId) -1;
	}
	mprAddToArray(ep->frames, ep->global);

	/*
	 *	Create first local variable frame
	 */
	ep->local = (MprVar*) mprMalloc(sizeof(MprVar));
	*ep->local = ejsCreateObj("local", EJS_OBJ_HASH_SIZE);
	if (ep->local->type == MPR_TYPE_UNDEFINED) {
		ejsCloseEngine(ep->eid);
		return (EjsId) -1;
	}
	mprAddToArray(ep->frames, ep->local);

	/*
	 *	Clone all master variables into the global frame. This does a
	 *	reference copy.
	 *
	 *		ejsDefineStandardProperties(ep->global);
	 */
	np = mprGetFirstProperty(&master, MPR_ENUM_FUNCTIONS | MPR_ENUM_DATA);
	while (np) {
		mprCreateProperty(ep->global, np->name, np);
		np = mprGetNextProperty(&master, np, MPR_ENUM_FUNCTIONS | 
			MPR_ENUM_DATA);
	}

	mprCreateProperty(ep->global, "global", ep->global);
	mprCreateProperty(ep->global, "this", ep->global);
	mprCreateProperty(ep->local, "local", ep->local);

	return ep->eid;
}

/******************************************************************************/
/*
 *	Close an EJS instance
 */

void ejsCloseEngine(EjsId eid)
{
	Ejs		*ep;
	MprVar	*vp;
	void	**handles;
	int		i;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return;
	}

	mprFree(ep->error);
	mprDestroyVar(&ep->result);
	mprDestroyVar(&ep->tokenNumber);

	if (ep->local) {
		mprDeleteProperty(ep->local, "local");
	}
	mprDeleteProperty(ep->global, "this");
	mprDeleteProperty(ep->global, "global");

	handles = ep->frames->handles;
	for (i = 0; i < ep->frames->max; i++) {
		vp = handles[i];
		if (vp) {
#if BLD_DEBUG
			if (vp->type == MPR_TYPE_OBJECT && vp->properties->refCount > 1) {
				mprLog(7, "ejsCloseEngine: %s has ref count %d\n",
					vp->name, vp->properties->refCount);
			}
#endif
			mprDestroyVar(vp);
			mprFree(vp);
			mprRemoveFromArray(ep->frames, i);
		}
	}
	mprDestroyArray(ep->frames);

	ejsLock();
	mprRemoveFromArray(ejsList, (int) ep->eid);
	ejsUnlock();

	mprFree(ep);
}

/******************************************************************************/
/*
 *	Evaluate an EJS script file
 */

int ejsEvalFile(EjsId eid, char *path, MprVar *result, char **emsg)
{
	struct stat	 sbuf;
	Ejs			*ep;
	char		*script;
	int			rc, fd;

	mprAssert(path && *path);

	if (emsg) {
		*emsg = NULL;
	}

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		goto error;
	}

	if ((fd = open(path, O_RDONLY | O_BINARY, 0666)) < 0) {
		ejsError(ep, "Can't open %s\n", path);
		goto error;
	}
	
	if (stat(path, &sbuf) < 0) {
		close(fd);
		ejsError(ep, "Cant stat %s", path);
		goto error;
	}
	
	if ((script = (char*) mprMalloc(sbuf.st_size + 1)) == NULL) {
		close(fd);
		ejsError(ep, "Cant malloc %d", (int) sbuf.st_size);
		goto error;
	}
	
	if (read(fd, script, sbuf.st_size) != (int) sbuf.st_size) {
		close(fd);
		mprFree(script);
		ejsError(ep, "Error reading %s", path);
		goto error;
	}
	
	script[sbuf.st_size] = '\0';
	close(fd);

	rc = ejsEvalBlock(eid, script, result, emsg);
	mprFree(script);

	return rc;

/*
 *	Error return
 */
error:
	if(emsg)
		*emsg = mprStrdup(ep->error);
	return -1;
}

/******************************************************************************/
/*
 *	Create a new variable scope block. This pushes the old local frame down
 *	the stack and creates a new local variables frame.
 */

int ejsOpenBlock(EjsId eid)
{
	Ejs		*ep;

	if((ep = ejsPtr(eid)) == NULL) {
		return -1;
	}

	ep->local = (MprVar*) mprMalloc(sizeof(MprVar));
	*ep->local = ejsCreateObj("localBlock", EJS_OBJ_HASH_SIZE);

	mprCreateProperty(ep->local, "local", ep->local);

	return mprAddToArray(ep->frames, ep->local);
}

/******************************************************************************/
/*
 *	Close a variable scope block opened via ejsOpenBlock. Pop back the old
 *	local variables frame.
 */

int ejsCloseBlock(EjsId eid, int fid)
{
	Ejs		*ep;

	if((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return -1;
	}

	/*
 	 *	Must remove self-references before destroying "local"
	 */
	mprDeleteProperty(ep->local, "local");

	mprDestroyVar(ep->local);
	mprFree(ep->local);

	mprRemoveFromArray(ep->frames, fid);
	ep->local = (MprVar*) ep->frames->handles[ep->frames->used - 1];

	return 0;
}

/******************************************************************************/
/*
 *	Create a new variable scope block and evaluate a script. All frames
 *	created during this context will be automatically deleted when complete.
 *	vp and emsg are optional. i.e. created local variables will be discarded
 *	when this routine returns.
 */

int ejsEvalBlock(EjsId eid, char *script, MprVar *vp, char **emsg)
{
	int		rc, fid;

	mprAssert(script);

	fid = ejsOpenBlock(eid);
	rc = ejsEvalScript(eid, script, vp, emsg);
	ejsCloseBlock(eid, fid);

	return rc;
}

/******************************************************************************/
/*
 *	Parse and evaluate a EJS. Return the result in *vp. The result is "owned"
 *	by EJ and the caller must not free it. Returns -1 on errors and zero 
 *	for success. On errors, emsg will be set to the reason. The caller must 
 *	free emsg.
 */

int ejsEvalScript(EjsId eid, char *script, MprVar *vp, char **emsg)
{
	Ejs			*ep;
	int			state;
	void		*endlessLoopTest;
	int			loopCounter;
	
	if (emsg) {
		*emsg = NULL;
	} 

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return -1;
	}

	mprDestroyVar(&ep->result);

	if (script == 0) {
		return 0;
	}

	/*
	 *	Allocate a new evaluation block, and save the old one
	 */
	ejsLexOpenScript(ep, script);

	/*
	 *	Do the actual parsing and evaluation
	 */
	loopCounter = 0;
	endlessLoopTest = NULL;
	ep->exitStatus = 0;

	do {
		state = ejsParse(ep, EJS_STATE_BEGIN, EJS_FLAGS_EXE);

		if (state == EJS_STATE_RET) {
			state = EJS_STATE_EOF;
		}
		/*
		 *	Stuck parser and endless recursion protection.
		 */
		if (endlessLoopTest == ep->input->scriptServp) {
			if (loopCounter++ > 10) {
				state = EJS_STATE_ERR;
				ejsError(ep, "Syntax error");
			}
		} else {
			endlessLoopTest = ep->input->scriptServp;
			loopCounter = 0;
		}
	} while (state != EJS_STATE_EOF && state != EJS_STATE_ERR);

	ejsLexCloseScript(ep);

	/*
	 *	Return any error string to the user
	 */
	if (state == EJS_STATE_ERR && emsg) {
		*emsg = mprStrdup(ep->error);
	}

	if (state == EJS_STATE_ERR) {
		return -1;
	}

	if (vp) {
		*vp = ep->result;
	}

	return ep->exitStatus;
}

/******************************************************************************/
/*
 *	Core error handling
 */

static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args) 
	PRINTF_ATTRIBUTE(2, 0);

static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args)
{
	EjsInput	*ip;
	char		*errbuf, *msgbuf;
	int frame = 0;

	mprAssert(ep);

	msgbuf = NULL;
	mprAllocVsprintf(&msgbuf, MPR_MAX_STRING, fmt, args);

	ip = ep->input;
	mprAllocSprintf(&errbuf, MPR_MAX_STRING, "%s\nBacktrace:\n", msgbuf);

	/* form a backtrace */
	while (ip) {
		char *msg2, *ebuf2;
		mprAllocSprintf(&msg2, MPR_MAX_STRING,
						"\t[%2d] %20s:%-4d -> %s\n",
						frame++, ip->procName?ip->procName:"", ip->lineNumber, ip->line);
		ebuf2 = mprRealloc(errbuf, strlen(errbuf) + strlen(msg2) + 1);
		if (ebuf2 == NULL) break;
		errbuf = ebuf2;
		memcpy(errbuf+strlen(errbuf), msg2, strlen(msg2)+1);
		mprFree(msg2);
		ip = ip->next;
	}
	mprFree(ep->error);
	ep->error = errbuf;
	mprFree(msgbuf);
}

/******************************************************************************/
/*
 *	Internal use function to set the error message
 */

void ejsError(Ejs* ep, const char* fmt, ...)
{
	va_list		args;

	va_start(args, fmt);
	ejsErrorCore(ep, fmt, args);
	va_end(args);
}

/******************************************************************************/
/*
 *	Public routine to set the error message
 */

void ejsSetErrorMsg(EjsId eid, const char* fmt, ...)
{
	va_list		args;
	Ejs			*ep;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return;
	}
	va_start(args, fmt);
	ejsErrorCore(ep, fmt, args);
	va_end(args);
}

/******************************************************************************/
/*
 *	Get the current line number
 */

int ejsGetLineNumber(EjsId eid)
{
	Ejs		*ep;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return -1;
	}
	return ep->input->lineNumber;
}

/******************************************************************************/
/*
 *	Return the local object
 */

MprVar *ejsGetLocalObject(EjsId eid)
{
	Ejs		*ep;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return 0; 
	}
	return ep->local;
}

/******************************************************************************/
/*
 *	Return the global object
 */

MprVar *ejsGetGlobalObject(EjsId eid)
{
	Ejs		*ep;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return 0;
	}
	return ep->global;
}

/******************************************************************************/
/*
 *	Copy the value of an object property. Return value is in "value".
 *	If deepCopy is true, copy all object/strings. Otherwise, object reference
 *	counts are incremented. Callers must always call mprDestroyVar on the 
 *	return value to prevent leaks.
 *
 *	Returns: -1 on errors or if the variable is not found.
 */

int ejsCopyVar(EjsId eid, const char *var, MprVar *value, bool deepCopy)
{
	Ejs			*ep;
	MprVar		*vp;

	mprAssert(var && *var);
	mprAssert(value);

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return -1;
	}

	if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) {
		return -1;
	}

	return mprCopyProperty(value, vp, deepCopy);
}

/******************************************************************************/
/*
 *	Return the value of an object property. Return value is in "value".
 *	Objects and strings are not copied and reference counts are not modified.
 *	Callers should NOT call mprDestroyVar. Returns: -1 on errors or if the 
 *	variable is not found.
 */

int ejsReadVar(EjsId eid, const char *var, MprVar *value)
{
	Ejs			*ep;
	MprVar		*vp;

	mprAssert(var && *var);
	mprAssert(value);

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return -1;
	}

	if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) {
		return -1;
	}

	return mprReadProperty(vp, value);
}

/******************************************************************************/
/*
 *	Set a variable that may be an arbitrarily complex object or array reference.
 *	Will always define in the top most variable frame.
 */

int ejsWriteVar(EjsId eid, const char *var, MprVar *value)
{
	Ejs			*ep;
	MprVar		*vp;

	mprAssert(var && *var);

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return -1;
	}

	if (ejsGetVarCore(ep, var, 0, &vp, EJS_FLAGS_CREATE) < 0) {
		return -1;
	}
	mprAssert(vp);

	/*
	 *	Only copy the value. Don't overwrite the object's name
	 */
	mprWriteProperty(vp, value);

	return 0;
}

/******************************************************************************/
/*
 *	Set a variable that may be an arbitrarily complex object or array reference.
 *	Will always define in the top most variable frame.
 */

int ejsWriteVarValue(EjsId eid, const char *var, MprVar value)
{
	return ejsWriteVar(eid, var, &value);
}

/******************************************************************************/
/*
 *	Delete a variable
 */

int ejsDeleteVar(EjsId eid, const char *var)
{
	Ejs			*ep;
	MprVar		*vp;
	MprVar		*obj;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return -1;
	}
	if (ejsGetVarCore(ep, var, &obj, &vp, 0) < 0) {
		return -1;
	}
	mprDeleteProperty(obj, vp->name);
	return 0;
}

/******************************************************************************/
/*
 *	Set the expression return value
 */

void ejsSetReturnValue(EjsId eid, MprVar value)
{
	Ejs		*ep;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return;
	}
	mprCopyVar(&ep->result, &value, MPR_SHALLOW_COPY);
}

/******************************************************************************/
/*
 *	Set the expression return value to a string value
 */

void ejsSetReturnString(EjsId eid, const char *str)
{
	Ejs		*ep;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return;
	}
	mprCopyVarValue(&ep->result, mprCreateStringVar(str, 0), MPR_SHALLOW_COPY);
}

/******************************************************************************/
/*
 *	Get the expression return value
 */

MprVar *ejsGetReturnValue(EjsId eid)
{
	Ejs		*ep;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return 0;
	}
	return &ep->result;
}

/******************************************************************************/
/*
 *	Define a C function. If eid < 0, then update the master object with this
 *	function. NOTE: in this case, functionName must be simple without any "." or
 *	"[]" elements. If eid >= 0, add to the specified script engine. In this
 *	case, functionName can be an arbitrary object reference and can contain "."
 *	or "[]".  
 */

void ejsDefineCFunction(EjsId eid, const char *functionName, MprCFunction fn, 
	void *thisPtr, int flags)
{
	if (eid < 0) {
		ejsLock();
		mprCreatePropertyValue(&master, functionName, 
			mprCreateCFunctionVar(fn, thisPtr, flags));
		ejsUnlock();
	} else {
		ejsWriteVarValue(eid, functionName, 
			mprCreateCFunctionVar(fn, thisPtr, flags));
	}
}

/******************************************************************************/
/*
 *	Define a C function with String arguments
 */

void ejsDefineStringCFunction(EjsId eid, const char *functionName, 
	MprStringCFunction fn, void *thisPtr, int flags)
{
	if (eid < 0) {
		ejsLock();
		mprCreatePropertyValue(&master, functionName, 
			mprCreateStringCFunctionVar(fn, thisPtr, flags));
		ejsUnlock();
	} else {
		ejsWriteVarValue(eid, functionName, 
			mprCreateStringCFunctionVar(fn, thisPtr, flags));
	}
}

/******************************************************************************/
/*
 *	Define a JavaScript function. Args should be comma separated.
 *	Body should not contain braces.
 */

void ejsDefineFunction(EjsId eid, const char *functionName, char *args, 
	char *body)
{
	MprVar		v;

	v = mprCreateFunctionVar(args, body, 0);
	if (eid < 0) {
		ejsLock();
		mprCreateProperty(&master, functionName, &v);
		ejsUnlock();
	} else {
		ejsWriteVar(eid, functionName, &v);
	}
	mprDestroyVar(&v);
}

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

void *ejsGetThisPtr(EjsId eid)
{
	Ejs		*ep;

	if ((ep = ejsPtr(eid)) == NULL) {
		mprAssert(ep);
		return 0;
	}
	return ep->thisPtr;
}

/******************************************************************************/
/*
 *	Find a variable given a variable name and return the parent object and 
 *	the variable itself, the variable . This routine supports variable names
 *	that may be objects or arrays but may NOT have expressions in the array
 *	indicies. Returns -1 on errors or if the variable is not found.
 */

int ejsGetVarCore(Ejs *ep, const char *vname, MprVar **obj, 
	MprVar **varValue, int flags)
{
	MprVar		*currentObj;
	MprVar		*currentVar;
	char		tokBuf[EJS_MAX_ID];
	char		*propertyName, *token, *next, *cp, *varName;

	if (obj) {
		*obj = 0;
	}
	if (varValue) {
		*varValue = 0;
	}
	currentObj = ejsFindObj(ep, 0, vname, flags);
	currentVar = 0;
	propertyName = 0;

	next = varName = mprStrdup(vname);

	token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));

	while (currentObj != 0 && token != 0 && *token) {
		
		if (*token == '[') {
			token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));

			propertyName = token;
			if (*propertyName == '\"') {
				propertyName++;
				if ((cp = strchr(propertyName, '\"')) != 0) {
					*cp = '\0';
				}
			} else if (*propertyName == '\'') {
				propertyName++;
				if ((cp = strchr(propertyName, '\'')) != 0) {
					*cp = '\0';
				}
			}

			currentObj = currentVar;
			currentVar = ejsFindProperty(ep, 0, currentObj, propertyName, 0);

			token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
			if (*token != ']') {
				mprFree(varName);
				return -1;
			}

		} else if (*token == '.') {
			token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
			if (!isalpha((int) token[0]) && 
					token[0] != '_' && token[0] != '$') {
				mprFree(varName);
				return -1;
			}

			propertyName = token;
			currentObj = currentVar;
			currentVar = ejsFindProperty(ep, 0, currentObj, token, 0);

		} else {
			currentVar = ejsFindProperty(ep, 0, currentObj, token, 0);
		}
		token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
	}
	mprFree(varName);

	if (currentVar == 0 && currentObj >= 0 && flags & EJS_FLAGS_CREATE) {
		currentVar = mprCreatePropertyValue(currentObj, propertyName, 
			mprCreateUndefinedVar());
	}
	if (obj) {
		*obj = currentObj;
	}
	
	/*
 	 *	Don't use mprCopyVar as it will copy the data
 	 */
	if (varValue) {
		*varValue = currentVar;
	}
	return currentVar ? 0 : -1;
}

/******************************************************************************/
/*
 *	Get the next token as part of a variable specification. This will return
 *	a pointer to the next token and will return a pointer to the next token 
 *	(after this one) in "next". The tokBuf holds the parsed token.
 */
static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen)
{
	char	*start, *cp;
	int		len;

	start = *next;
	while (isspace((int) *start) || *start == '\n' || *start == '\r') {
		start++;
	}
	cp = start;

	if (*cp == '.' || *cp == '[' || *cp == ']') {
		cp++;
	} else {
		while (*cp && *cp != '.' && *cp != '[' && *cp != ']' && 
				!isspace((int) *cp) && *cp != '\n' && *cp != '\r') {
			cp++;
		}
	}
	len = mprMemcpy(tokBuf, tokBufLen - 1, start, cp - start);
	tokBuf[len] = '\0';
	
	*next = cp;
	return tokBuf;
}

/******************************************************************************/
/*
 *	Get the EJS structure pointer
 */

Ejs *ejsPtr(EjsId eid)
{
	Ejs		*handle;
	int		intId;

	intId = (int) eid;

	ejsLock();
	mprAssert(0 <= intId && intId < ejsList->max);

	if (intId < 0 || intId >= ejsList->max || ejsList->handles[intId] == NULL) {
		mprAssert(0);
		ejsUnlock();
		return NULL;
	}
	handle = ejsList->handles[intId];
	ejsUnlock();
	return handle;
}

/******************************************************************************/
/*
 *	Utility routine to crack JavaScript arguments. Return the number of args
 *	seen. This routine only supports %s and %d type args.
 *
 *	Typical usage:
 *
 *		if (ejsParseArgs(argc, argv, "%s %d", &name, &age) < 2) {
 *			mprError("Insufficient args\n");
 *			return -1;
 *		}
 */ 

int ejsParseArgs(int argc, char **argv, char *fmt, ...)
{
	va_list	vargs;
	bool	*bp;
	char	*cp, **sp, *s;
	int		*ip, argn;

	va_start(vargs, fmt);

	if (argv == 0) {
		return 0;
	}

	for (argn = 0, cp = fmt; cp && *cp && argn < argc && argv[argn]; ) {
		if (*cp++ != '%') {
			continue;
		}

		s = argv[argn];
		switch (*cp) {
		case 'b':
			bp = va_arg(vargs, bool*);
			if (bp) {
				if (strcmp(s, "true") == 0 || s[0] == '1') {
					*bp = 1;
				} else {
					*bp = 0;
				}
			} else {
				*bp = 0;
			}
			break;

		case 'd':
			ip = va_arg(vargs, int*);
			*ip = atoi(s);
			break;

		case 's':
			sp = va_arg(vargs, char**);
			*sp = s;
			break;

		default:
			mprAssert(0);
		}
		argn++;
	}

	va_end(vargs);
	return argn;
}

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

#else
void ejsDummy() {}

/******************************************************************************/
#endif /* BLD_FEATURE_EJS */

/******************************************************************************/
/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim:tw=78
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */
