diff options
Diffstat (limited to 'source4/lib/ejs/ejs.c')
-rw-r--r-- | source4/lib/ejs/ejs.c | 1060 |
1 files changed, 1060 insertions, 0 deletions
diff --git a/source4/lib/ejs/ejs.c b/source4/lib/ejs/ejs.c new file mode 100644 index 0000000000..41af795370 --- /dev/null +++ b/source4/lib/ejs/ejs.c @@ -0,0 +1,1060 @@ +/* + * @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 "lib/ejs/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 + +/****************************** 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 = 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); + + 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: + *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; + EjsInput *oldBlock; + 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 + */ + oldBlock = ep->input; + 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); + } + + /* + * Restore the old evaluation block + */ + ep->input = oldBlock; + + 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; + + mprAssert(ep); + mprAssert(args); + + msgbuf = NULL; + mprAllocVsprintf(&msgbuf, MPR_MAX_STRING, fmt, args); + + if (ep) { + ip = ep->input; + if (ip) { + mprAllocSprintf(&errbuf, MPR_MAX_STRING, + "%s\nError on line %d. Offending line: %s\n\n", + msgbuf, ip->lineNumber, ip->line); + } else { + mprAllocSprintf(&errbuf, MPR_MAX_STRING, "%s\n", msgbuf); + } + 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 *varName_c, 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, varName_c, flags); + currentVar = 0; + propertyName = 0; + + varName = mprStrdup(varName_c); + next = varName; + + 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 + */ |