diff options
author | Andrew Tridgell <tridge@samba.org> | 2005-05-26 01:06:32 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:17:01 -0500 |
commit | d70912a26af49db468af7ec88e9689b8176e0576 (patch) | |
tree | a52d8b9930fe23eaea47c40b0af34f324e45f854 /source4/web_server | |
parent | e5613a3ce9932fb76aef9f721cadabe69bd23be8 (diff) | |
download | samba-d70912a26af49db468af7ec88e9689b8176e0576.tar.gz samba-d70912a26af49db468af7ec88e9689b8176e0576.tar.bz2 samba-d70912a26af49db468af7ec88e9689b8176e0576.zip |
r6981: first version of the builtin web server for Samba4
This includes an embedded server side scripting system called 'esp'
(see http://www.appwebserver.org/products/esp/esp.html) and javascript
based scripting language called 'esj' (see
http://www.appwebserver.org/products/ejs/ejs.html)
The justification for including this scripting language is that it
should make it much easier to write a high quality web interface for
Samba4. The scripting language can call into any Samba4 library code
(so for example it will be able to make ldb and loadparm calls), plus
it provides easy support for forms, cookies, sessions etc.
There is still quite a bit more work to do on the web server, but
there is enough here now for people to look at and comment. I will be
committing some sample web pages that test esp functionality shortly.
(This used to be commit 26f0ba92c0c565ac9e4cb5a079d795d4262497dd)
Diffstat (limited to 'source4/web_server')
-rw-r--r-- | source4/web_server/config.mk | 39 | ||||
-rw-r--r-- | source4/web_server/ejs/config.h | 147 | ||||
-rw-r--r-- | source4/web_server/ejs/ejs.c | 1058 | ||||
-rw-r--r-- | source4/web_server/ejs/ejs.h | 131 | ||||
-rw-r--r-- | source4/web_server/ejs/ejsInternal.h | 292 | ||||
-rw-r--r-- | source4/web_server/ejs/ejsLex.c | 910 | ||||
-rw-r--r-- | source4/web_server/ejs/ejsParser.c | 2358 | ||||
-rw-r--r-- | source4/web_server/ejs/ejsProcs.c | 705 | ||||
-rw-r--r-- | source4/web_server/ejs/miniMpr.c | 512 | ||||
-rw-r--r-- | source4/web_server/ejs/miniMpr.h | 287 | ||||
-rw-r--r-- | source4/web_server/ejs/mpr.h | 2259 | ||||
-rw-r--r-- | source4/web_server/ejs/mprOs.h | 627 | ||||
-rw-r--r-- | source4/web_server/ejs/var.c | 2161 | ||||
-rw-r--r-- | source4/web_server/ejs/var.h | 482 | ||||
-rw-r--r-- | source4/web_server/esp/esp.c | 1042 | ||||
-rw-r--r-- | source4/web_server/esp/esp.h | 279 | ||||
-rw-r--r-- | source4/web_server/esp/espEnv.h | 128 | ||||
-rw-r--r-- | source4/web_server/esp/espProcs.c | 230 | ||||
-rw-r--r-- | source4/web_server/http.c | 627 | ||||
-rw-r--r-- | source4/web_server/web_server.c | 252 | ||||
-rw-r--r-- | source4/web_server/web_server.h | 49 |
21 files changed, 14575 insertions, 0 deletions
diff --git a/source4/web_server/config.mk b/source4/web_server/config.mk new file mode 100644 index 0000000000..34d8aff1e6 --- /dev/null +++ b/source4/web_server/config.mk @@ -0,0 +1,39 @@ +# web server subsystem + +####################### +# Start SUBSYSTEM EJS +[SUBSYSTEM::EJS] +ADD_OBJ_FILES = \ + web_server/ejs/ejs.o \ + web_server/ejs/ejsLex.o \ + web_server/ejs/ejsParser.o \ + web_server/ejs/ejsProcs.o \ + web_server/ejs/miniMpr.o \ + web_server/ejs/var.o +NOPROTO=YES +# End SUBSYSTEM EJS +####################### + +####################### +# Start SUBSYSTEM ESP +[SUBSYSTEM::ESP] +ADD_OBJ_FILES = \ + web_server/esp/esp.o \ + web_server/esp/espProcs.o +REQUIRED_SUBSYSTEMS = EJS +NOPROTO=YES +# End SUBSYSTEM ESP +####################### + + + +####################### +# Start SUBSYSTEM WEB +[SUBSYSTEM::WEB] +INIT_OBJ_FILES = \ + web_server/web_server.o +ADD_OBJ_FILES = \ + web_server/http.o +REQUIRED_SUBSYSTEMS = ESP +# End SUBSYSTEM WEB +####################### diff --git a/source4/web_server/ejs/config.h b/source4/web_server/ejs/config.h new file mode 100644 index 0000000000..ec350890df --- /dev/null +++ b/source4/web_server/ejs/config.h @@ -0,0 +1,147 @@ +// +// config.h -- Build configuration file. +// +// WARNING: DO NOT EDIT. This file is generated by configure. +// +// If you wish to modify the defaults, then edit conf/config.defaults.* and +// then run "configure --reset". +// +//////////////////////////////////////////////////////////////////////////////// + +#define BLD_PRODUCT "Samba4" +#define BLD_NAME "Samba4 SWAT" +#define BLD_VERSION "4" +#define BLD_NUMBER "1" +#define BLD_TYPE "DEBUG" +#define BLD_DEFAULTS "normal" +#define BLD_PACKAGES "" +#define BLD_APPWEB_CONFIG "normal.conf" +#define BLD_APPWEB 0 +#define BLD_COMPANY "Mbedthis" +#define BLD_DEBUG 1 +#define BLD_DIRS "bootstrap include obj bin mpr ejs esp http doc appWeb appWebSamples images" +#define BLD_HTTP_PORT 7777 +#define BLD_LIB_VERSION "1.0.0" +#define BLD_SSL_PORT 4443 +#define BLD_CLEAN_INSTALL "0" +#define BLD_LICENSE "gpl" +#define BLD_HOST_SYSTEM "i686-pc-linux-gnu" +#define BLD_BUILD_SYSTEM "i686-pc-linux-gnu" +#define BLD_HOST_OS "LINUX" +#define BLD_HOST_CPU_ARCH MPR_CPU_IX86 +#define BLD_HOST_CPU "i686" +#define BLD_HOST_UNIX 1 +#define BLD_BUILD_OS "LINUX" +#define BLD_BUILD_CPU_ARCH MPR_CPU_IX86 +#define BLD_BUILD_CPU i686 +#define BLD_BUILD_UNIX 1 +#define BLD_ROOT_PREFIX "/" +#define BLD_FEATURE_ACCESS_LOG 0 +#define BLD_FEATURE_ADMIN_MODULE 0 +#define BLD_FEATURE_ASPNET_MODULE 0 +#define BLD_FEATURE_ASSERT 1 +#define BLD_FEATURE_AUTH_MODULE 0 +#define BLD_FEATURE_C_API_MODULE 1 +#define BLD_FEATURE_C_API_CLIENT 0 +#define BLD_FEATURE_CGI_MODULE 0 +#define BLD_FEATURE_COMPAT_MODULE 0 +#define BLD_FEATURE_CONFIG_PARSE 0 +#define BLD_FEATURE_CONFIG_SAVE 0 +#define BLD_FEATURE_COOKIE 0 +#define BLD_FEATURE_COPY_MODULE 0 +#define BLD_FEATURE_DIGEST 0 +#define BLD_FEATURE_DLL 0 +#define BLD_FEATURE_EGI_MODULE 0 +#define BLD_FEATURE_EJS 1 +#define BLD_FEATURE_ESP_MODULE 1 +#define BLD_FEATURE_EVAL_PERIOD 30 +#define BLD_FEATURE_FLOATING_POINT 0 +#define BLD_FEATURE_IF_MODIFIED 0 +#define BLD_FEATURE_INT64 0 +#define BLD_FEATURE_KEEP_ALIVE 0 +#define BLD_FEATURE_LEGACY_API 0 +#define BLD_FEATURE_LIB_STDCPP 0 +#define BLD_FEATURE_LICENSE 0 +#define BLD_FEATURE_LOG 0 +#define BLD_FEATURE_MULTITHREAD 0 +#define BLD_FEATURE_MALLOC 0 +#define BLD_FEATURE_MALLOC_STATS 0 +#define BLD_FEATURE_MALLOC_LEAK 0 +#define BLD_FEATURE_MALLOC_HOOK 0 +#define BLD_FEATURE_NUM_TYPE int +#define BLD_FEATURE_NUM_TYPE_ID MPR_TYPE_INT +#define BLD_FEATURE_ROMFS 0 +#define BLD_FEATURE_RUN_AS_SERVICE 0 +#define BLD_FEATURE_SAFE_STRINGS 0 +#define BLD_FEATURE_SAMPLES 0 +#define BLD_FEATURE_SESSION 1 +#define BLD_FEATURE_SHARED 0 +#define BLD_FEATURE_SQUEEZE 0 +#define BLD_FEATURE_SSL_MODULE 0 +#define BLD_FEATURE_STATIC 1 +#define BLD_FEATURE_STATIC_LINK_LIBC 0 +#define BLD_FEATURE_TEST 0 +#define BLD_FEATURE_UPLOAD_MODULE 0 +#define BLD_FEATURE_XDB_MODULE 0 +#define BLD_FEATURE_ADMIN_MODULE_BUILTIN 0 +#define BLD_FEATURE_ASPNET_MODULE_BUILTIN 0 +#define BLD_FEATURE_AUTH_MODULE_BUILTIN 0 +#define BLD_FEATURE_C_API_MODULE_BUILTIN 0 +#define BLD_FEATURE_CGI_MODULE_BUILTIN 0 +#define BLD_FEATURE_COMPAT_MODULE_BUILTIN 0 +#define BLD_FEATURE_COPY_MODULE_BUILTIN 0 +#define BLD_FEATURE_EGI_MODULE_BUILTIN 0 +#define BLD_FEATURE_ESP_MODULE_BUILTIN 0 +#define BLD_FEATURE_SSL_MODULE_BUILTIN 0 +#define BLD_FEATURE_UPLOAD_MODULE_BUILTIN 0 +#define BLD_FEATURE_XDB_MODULE_BUILTIN 0 +#define BLD_FEATURE_ADMIN_MODULE_LOADABLE 0 +#define BLD_FEATURE_ASPNET_MODULE_LOADABLE 0 +#define BLD_FEATURE_AUTH_MODULE_LOADABLE 0 +#define BLD_FEATURE_C_API_MODULE_LOADABLE 0 +#define BLD_FEATURE_CGI_MODULE_LOADABLE 0 +#define BLD_FEATURE_COMPAT_MODULE_LOADABLE 0 +#define BLD_FEATURE_COPY_MODULE_LOADABLE 0 +#define BLD_FEATURE_EGI_MODULE_LOADABLE 0 +#define BLD_FEATURE_ESP_MODULE_LOADABLE 0 +#define BLD_FEATURE_SSL_MODULE_LOADABLE 0 +#define BLD_FEATURE_UPLOAD_MODULE_LOADABLE 0 +#define BLD_FEATURE_XDB_MODULE_LOADABLE 0 +#define BLD_AR_FOR_BUILD "ar" +#define BLD_CC_FOR_BUILD "cc" +#define BLD_CSC_FOR_BUILD "" +#define BLD_JAVAC_FOR_BUILD "" +#define BLD_LD_FOR_BUILD "ld" +#define BLD_RANLIB_FOR_BUILD "" +#define BLD_NM_FOR_BUILD "nm" +#define BLD_CFLAGS_FOR_BUILD "" +#define BLD_IFLAGS_FOR_BUILD "" +#define BLD_LDFLAGS_FOR_BUILD "" +#define BLD_ARCHIVE_FOR_BUILD ".a" +#define BLD_EXE_FOR_BUILD "" +#define BLD_OBJ_FOR_BUILD ".o" +#define BLD_PIOBJ_FOR_BUILD ".lo" +#define BLD_CLASS_FOR_BUILD ".class" +#define BLD_SHLIB_FOR_BUILD "" +#define BLD_SHOBJ_FOR_BUILD ".so" +#define BLD_AR_FOR_HOST "ar" +#define BLD_CC_FOR_HOST "cc" +#define BLD_CSC_FOR_HOST "csc" +#define BLD_JAVAC_FOR_HOST "javac" +#define BLD_LD_FOR_HOST "ld" +#define BLD_RANLIB_FOR_HOST "true" +#define BLD_NM_FOR_HOST "nm" +#define BLD_CFLAGS_FOR_HOST "" +#define BLD_IFLAGS_FOR_HOST "" +#define BLD_LDFLAGS_FOR_HOST "" +#define BLD_ARCHIVE_FOR_HOST ".a" +#define BLD_EXE_FOR_HOST "" +#define BLD_OBJ_FOR_HOST ".o" +#define BLD_PIOBJ_FOR_HOST ".lo" +#define BLD_CLASS_FOR_HOST ".class" +#define BLD_SHLIB_FOR_HOST "" +#define BLD_SHOBJ_FOR_HOST ".so" +#define BLD_TOOLS_DIR "${BLD_TOP}/bin" +#define BLD_BIN_DIR "${BLD_TOP}/bin" +#define BLD_INC_DIR "/usr/include/${BLD_PRODUCT}" +#define BLD_EXP_OBJ_DIR "${BLD_TOP}/obj" diff --git a/source4/web_server/ejs/ejs.c b/source4/web_server/ejs/ejs.c new file mode 100644 index 0000000000..2d85ad1330 --- /dev/null +++ b/source4/web_server/ejs/ejs.c @@ -0,0 +1,1058 @@ +/* + * @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 "web_server/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", 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 + */ + +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, 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, 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, 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 + */ diff --git a/source4/web_server/ejs/ejs.h b/source4/web_server/ejs/ejs.h new file mode 100644 index 0000000000..c1d087cf61 --- /dev/null +++ b/source4/web_server/ejs/ejs.h @@ -0,0 +1,131 @@ +/* + * @file ejs.h + * @brief Primary Embedded Javascript (ECMAScript) header. + * @overview This Embedded Javascript (EJS) header defines the + * public API. This API should only be used by those directly + * using EJS without using Embedded Server Pages (ESP). ESP + * wraps all relevant APIs to expose a single consistent API. + * \n\n + * This API requires the mpr/var.h facilities to create and + * manage objects and properties. + */ +/********************************* 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 **********************************/ + +#ifndef _h_EJS +#define _h_EJS 1 + +#include "web_server/ejs/miniMpr.h" +#include "web_server/ejs/var.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************* Prototypes *********************************/ + +typedef MprVarHandle EjsId; +typedef MprVarHandle EjsHandle; + +/* + * Multithreaded lock routines + */ +typedef void (*EjsLock)(void *lockData); +typedef void (*EjsUnlock)(void *lockData); + +/********************************* Prototypes *********************************/ +/* + * Module management + */ +extern int ejsOpen(EjsLock lock, EjsUnlock unlock, void *lockData); +extern void ejsClose(void); +extern EjsId ejsOpenEngine(EjsHandle primaryHandle, EjsHandle altHandle); +extern void ejsCloseEngine(EjsId eid); + +/* + * Evaluation functions + */ +extern int ejsEvalFile(EjsId eid, char *path, MprVar *result, char **emsg); +extern int ejsEvalScript(EjsId eid, char *script, MprVar *result, + char **emsg); +extern int ejsRunFunction(int eid, MprVar *obj, const char *functionName, + MprArray *args); + +/* + * Composite variable get / set routines. Can also use the MPR property + * routines on an object variable. + */ +extern MprVar ejsCreateObj(const char *name, int hashSize); +extern MprVar ejsCreateArray(const char *name, int hashSize); +extern bool ejsDestroyVar(MprVar *obj); +extern int ejsCopyVar(EjsId eid, const char *var, MprVar *value, bool copyRef); +extern int ejsReadVar(EjsId eid, const char *var, MprVar *value); +extern int ejsWriteVar(EjsId eid, const char *var, MprVar *value); +extern int ejsWriteVarValue(EjsId eid, const char *var, MprVar value); +extern int ejsDeleteVar(EjsId eid, const char *var); + +extern MprVar *ejsGetLocalObject(EjsId eid); +extern MprVar *ejsGetGlobalObject(EjsId eid); + +/* + * Function routines + */ +extern void ejsDefineFunction(EjsId eid, char *functionName, char *args, + char *body); +extern void ejsDefineCFunction(EjsId eid, char *functionName, + MprCFunction fn, void *thisPtr, int flags); +extern void ejsDefineStringCFunction(EjsId eid, const char *functionName, + MprStringCFunction fn, void *thisPtr, int flags); +extern void *ejsGetThisPtr(EjsId eid); +extern MprVar *ejsGetReturnValue(EjsId eid); +extern int ejsGetLineNumber(EjsId eid); +extern int ejsParseArgs(int argc, char **argv, char *fmt, ...); +extern void ejsSetErrorMsg(EjsId eid, const char* fmt, ...); +extern void ejsSetReturnValue(EjsId eid, MprVar value); +extern void ejsSetReturnString(EjsId eid, const char *str); + +#ifdef __cplusplus +} +#endif +#endif /* _h_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 + */ diff --git a/source4/web_server/ejs/ejsInternal.h b/source4/web_server/ejs/ejsInternal.h new file mode 100644 index 0000000000..fe79afb870 --- /dev/null +++ b/source4/web_server/ejs/ejsInternal.h @@ -0,0 +1,292 @@ +/* + * @file ejsInternal.h + * @brief Private header for Embedded Javascript (ECMAScript) + * @overview This Embedded Javascript header defines the private Embedded + * Javascript internal structures. + */ +/********************************* 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 ***********************************/ + +#ifndef _h_EJS_INTERNAL +#define _h_EJS_INTERNAL 1 + +#include "web_server/ejs/ejs.h" + +/********************************** Defines ***********************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Constants + */ + +#if BLD_FEATURE_SQUEEZE + #define EJS_PARSE_INCR 256 /* Growth factor */ + #define EJS_MAX_RECURSE 25 /* Sanity for maximum recursion */ + #define EJS_MAX_ID 128 /* Maximum ID length */ + #define EJS_OBJ_HASH_SIZE 13 /* Object hash table size */ + #define EJS_SMALL_OBJ_HASH_SIZE 11 /* Small object hash size */ + #define EJS_LIST_INCR 8 /* Growth increment for lists */ +#else + #define EJS_PARSE_INCR 1024 /* Growth factor */ + #define EJS_MAX_RECURSE 100 /* Sanity for maximum recursion */ + #define EJS_MAX_ID 256 /* Maximum ID length */ + #define EJS_OBJ_HASH_SIZE 29 /* Object hash table size */ + #define EJS_SMALL_OBJ_HASH_SIZE 11 /* Small object hash size */ + #define EJS_LIST_INCR 16 /* Growth increment for lists */ +#endif +#define EJS_TOKEN_STACK 4 /* Put back token stack */ + +/* + * Lexical analyser tokens + */ +#define EJS_TOK_ERR -1 /* Any error */ +#define EJS_TOK_LPAREN 1 /* ( */ +#define EJS_TOK_RPAREN 2 /* ) */ +#define EJS_TOK_IF 3 /* if */ +#define EJS_TOK_ELSE 4 /* else */ +#define EJS_TOK_LBRACE 5 /* { */ +#define EJS_TOK_RBRACE 6 /* } */ +#define EJS_TOK_LOGICAL 7 /* ||, &&, ! */ +#define EJS_TOK_EXPR 8 /* +, -, /, % */ +#define EJS_TOK_SEMI 9 /* ; */ +#define EJS_TOK_LITERAL 10 /* literal string */ +#define EJS_TOK_FUNCTION_NAME 11 /* functionName */ +#define EJS_TOK_NEWLINE 12 /* newline white space */ +#define EJS_TOK_ID 13 /* Identifier */ +#define EJS_TOK_EOF 14 /* End of script */ +#define EJS_TOK_COMMA 15 /* Comma */ +#define EJS_TOK_VAR 16 /* var */ +#define EJS_TOK_ASSIGNMENT 17 /* = */ +#define EJS_TOK_FOR 18 /* for */ +#define EJS_TOK_INC_DEC 19 /* ++, -- */ +#define EJS_TOK_RETURN 20 /* return */ +#define EJS_TOK_PERIOD 21 /* . */ +#define EJS_TOK_LBRACKET 22 /* [ */ +#define EJS_TOK_RBRACKET 23 /* ] */ +#define EJS_TOK_NEW 24 /* new */ +#define EJS_TOK_DELETE 25 /* delete */ +#define EJS_TOK_IN 26 /* in */ +#define EJS_TOK_FUNCTION 27 /* function */ +#define EJS_TOK_NUMBER 28 /* Number */ + +/* + * Expression operators + */ +#define EJS_EXPR_LESS 1 /* < */ +#define EJS_EXPR_LESSEQ 2 /* <= */ +#define EJS_EXPR_GREATER 3 /* > */ +#define EJS_EXPR_GREATEREQ 4 /* >= */ +#define EJS_EXPR_EQ 5 /* == */ +#define EJS_EXPR_NOTEQ 6 /* != */ +#define EJS_EXPR_PLUS 7 /* + */ +#define EJS_EXPR_MINUS 8 /* - */ +#define EJS_EXPR_DIV 9 /* / */ +#define EJS_EXPR_MOD 10 /* % */ +#define EJS_EXPR_LSHIFT 11 /* << */ +#define EJS_EXPR_RSHIFT 12 /* >> */ +#define EJS_EXPR_MUL 13 /* * */ +#define EJS_EXPR_ASSIGNMENT 14 /* = */ +#define EJS_EXPR_INC 15 /* ++ */ +#define EJS_EXPR_DEC 16 /* -- */ +#define EJS_EXPR_BOOL_COMP 17 /* ! */ + +/* + * Conditional operators + */ +#define EJS_COND_AND 1 /* && */ +#define EJS_COND_OR 2 /* || */ +#define EJS_COND_NOT 3 /* ! */ + +/* + * States + */ +#define EJS_STATE_ERR -1 /* Error state */ +#define EJS_STATE_EOF 1 /* End of file */ +#define EJS_STATE_COND 2 /* Parsing a "(conditional)" stmt */ +#define EJS_STATE_COND_DONE 3 +#define EJS_STATE_RELEXP 4 /* Parsing a relational expr */ +#define EJS_STATE_RELEXP_DONE 5 +#define EJS_STATE_EXPR 6 /* Parsing an expression */ +#define EJS_STATE_EXPR_DONE 7 +#define EJS_STATE_STMT 8 /* Parsing General statement */ +#define EJS_STATE_STMT_DONE 9 +#define EJS_STATE_STMT_BLOCK_DONE 10 /* End of block "}" */ +#define EJS_STATE_ARG_LIST 11 /* Function arg list */ +#define EJS_STATE_ARG_LIST_DONE 12 +#define EJS_STATE_DEC_LIST 16 /* Declaration list */ +#define EJS_STATE_DEC_LIST_DONE 17 +#define EJS_STATE_DEC 18 /* Declaration statement */ +#define EJS_STATE_DEC_DONE 19 +#define EJS_STATE_RET 20 /* Return statement */ + +#define EJS_STATE_BEGIN EJS_STATE_STMT + +/* + * General parsing flags. + */ +#define EJS_FLAGS_EXE 0x1 /* Execute statements */ +#define EJS_FLAGS_LOCAL 0x2 /* Get local vars only */ +#define EJS_FLAGS_GLOBAL 0x4 /* Get global vars only */ +#define EJS_FLAGS_CREATE 0x8 /* Create var */ +#define EJS_FLAGS_ASSIGNMENT 0x10 /* In assignment stmt */ +#define EJS_FLAGS_DELETE 0x20 /* Deleting a variable */ +#define EJS_FLAGS_FOREACH 0x40 /* In foreach */ +#define EJS_FLAGS_NEW 0x80 /* In a new stmt() */ +#define EJS_FLAGS_EXIT 0x100 /* Must exit */ + +/* + * Putback token + */ + +typedef struct EjsToken { + char *token; /* Token string */ + int id; /* Token ID */ +} EjsToken; + +/* + * EJ evaluation block structure + */ +typedef struct ejEval { + EjsToken putBack[EJS_TOKEN_STACK]; /* Put back token stack */ + int putBackIndex; /* Top of stack index */ + MprStr line; /* Current line */ + int lineLength; /* Current line length */ + int lineNumber; /* Parse line number */ + int lineColumn; /* Column in line */ + MprStr script; /* Input script for parsing */ + char *scriptServp; /* Next token in the script */ + int scriptSize; /* Length of script */ + MprStr tokbuf; /* Current token */ + char *tokEndp; /* Pointer past end of token */ + char *tokServp; /* Pointer to next token char */ + int tokSize; /* Size of token buffer */ +} EjsInput; + +/* + * Function call structure + */ +typedef struct { + MprArray *args; /* Args for function */ + MprVar *fn; /* Function definition */ + char *procName; /* Function name */ +} EjsProc; + +/* + * Per EJS structure + */ +typedef struct ej { + EjsHandle altHandle; /* alternate callback handle */ + MprVar *currentObj; /* Ptr to current object */ + MprVar *currentProperty; /* Ptr to current property */ + EjsId eid; /* Halloc handle */ + char *error; /* Error message */ + int exitStatus; /* Status to exit() */ + int flags; /* Flags */ + MprArray *frames; /* List of variable frames */ + MprVar *global; /* Global object */ + EjsInput *input; /* Input evaluation block */ + MprVar *local; /* Local object */ + EjsHandle primaryHandle; /* primary callback handle */ + EjsProc *proc; /* Current function */ + MprVar result; /* Variable result */ + void *thisPtr; /* C++ ptr for functions */ + int tid; /* Current token id */ + char *token; /* Pointer to token string */ + MprVar tokenNumber; /* Parsed number */ +} Ejs; + +typedef int EjsBlock; /* Scope block id */ + +/* + * Function callback when using Alternate handles. + */ +typedef int (*EjsAltStringCFunction)(EjsHandle userHandle, EjsHandle altHandle, + int argc, char **argv); +typedef int (*EjsAltCFunction)(EjsHandle userHandle, EjsHandle altHandle, + int argc, MprVar **argv); + +/******************************** Prototypes **********************************/ +/* + * Ejs Lex + */ +extern int ejsLexOpenScript(Ejs* ep, char *script); +extern void ejsLexCloseScript(Ejs* ep); +extern int ejsInitInputState(EjsInput *ip); +extern void ejsLexSaveInputState(Ejs* ep, EjsInput* state); +extern void ejsLexFreeInputState(Ejs* ep, EjsInput* state); +extern void ejsLexRestoreInputState(Ejs* ep, EjsInput* state); +extern int ejsLexGetToken(Ejs* ep, int state); +extern void ejsLexPutbackToken(Ejs* ep, int tid, char *string); + +/* + * Parsing + */ +extern MprVar *ejsFindObj(Ejs *ep, int state, const char *property, int flags); +extern MprVar *ejsFindProperty(Ejs *ep, int state, MprVar *obj, + char *property, int flags); +extern int ejsGetVarCore(Ejs *ep, const char *var, MprVar **obj, + MprVar **varValue, int flags); +extern int ejsParse(Ejs *ep, int state, int flags); +extern Ejs *ejsPtr(EjsId eid); +extern void ejsSetExitStatus(int eid, int status); +extern void ejsSetFlags(int orFlags, int andFlags); + +/* + * Create variable scope blocks + */ +extern EjsBlock ejsOpenBlock(EjsId eid); +extern int ejsCloseBlock(EjsId eid, EjsBlock vid); +extern int ejsEvalBlock(EjsId eid, char *script, MprVar *v, char **err); +extern int ejsDefineStandardProperties(MprVar *objVar); + +/* + * Error handling + */ +extern void ejsError(Ejs *ep, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif +#endif /* _h_EJS_INTERNAL */ + +/* + * 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 + */ diff --git a/source4/web_server/ejs/ejsLex.c b/source4/web_server/ejs/ejsLex.c new file mode 100644 index 0000000000..24e48d5ac3 --- /dev/null +++ b/source4/web_server/ejs/ejsLex.c @@ -0,0 +1,910 @@ +/* + * @file ejsLex.c + * @brief EJS Lexical Analyser + * @overview EJS lexical analyser. This implementes a lexical analyser + * for a subset of the JavaScript language. + */ +/********************************* 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 "web_server/ejs/ejsInternal.h" + +#if BLD_FEATURE_EJS + +/****************************** Forward Declarations **************************/ + +static int getLexicalToken(Ejs *ep, int state); +static int tokenAddChar(Ejs *ep, int c); +static int inputGetc(Ejs *ep); +static void inputPutback(Ejs *ep, int c); +static int charConvert(Ejs *ep, int base, int maxDig); + +/************************************* Code ***********************************/ +/* + * Open a new input script + */ + +int ejsLexOpenScript(Ejs *ep, char *script) +{ + EjsInput *ip; + + mprAssert(ep); + mprAssert(script); + + if ((ep->input = mprMalloc(sizeof(EjsInput))) == NULL) { + return -1; + } + ip = ep->input; + memset(ip, 0, sizeof(*ip)); + +/* + * Create the parse token buffer and script buffer + */ + ip->tokbuf = mprMalloc(EJS_PARSE_INCR); + ip->tokSize = EJS_PARSE_INCR; + ip->tokServp = ip->tokbuf; + ip->tokEndp = ip->tokbuf; + + ip->script = mprStrdup(script); + ip->scriptSize = strlen(script); + ip->scriptServp = ip->script; + + ip->lineNumber = 1; + ip->lineLength = 0; + ip->lineColumn = 0; + ip->line = NULL; + + ip->putBackIndex = -1; + + return 0; +} + +/******************************************************************************/ +/* + * Close the input script + */ + +void ejsLexCloseScript(Ejs *ep) +{ + EjsInput *ip; + int i; + + mprAssert(ep); + + ip = ep->input; + mprAssert(ip); + + for (i = 0; i < EJS_TOKEN_STACK; i++) { + mprFree(ip->putBack[i].token); + ip->putBack[i].token = 0; + } + + mprFree(ip->line); + mprFree(ip->tokbuf); + mprFree(ip->script); + + mprFree(ip); +} + +/******************************************************************************/ +/* + * Initialize an input state structure + */ + +int ejsInitInputState(EjsInput *ip) +{ + mprAssert(ip); + + memset(ip, 0, sizeof(*ip)); + ip->putBackIndex = -1; + + return 0; +} +/******************************************************************************/ +/* + * Save the input state + */ + +void ejsLexSaveInputState(Ejs *ep, EjsInput *state) +{ + EjsInput *ip; + int i; + + mprAssert(ep); + + ip = ep->input; + mprAssert(ip); + + *state = *ip; + + for (i = 0; i < ip->putBackIndex; i++) { + state->putBack[i].token = mprStrdup(ip->putBack[i].token); + state->putBack[i].id = ip->putBack[i].id; + } + for (; i < EJS_TOKEN_STACK; i++) { + state->putBack[i].token = 0; + } + + state->line = mprMalloc(ip->lineLength); + mprStrcpy(state->line, ip->lineLength, ip->line); + + state->lineColumn = ip->lineColumn; + state->lineNumber = ip->lineNumber; + state->lineLength = ip->lineLength; +} + +/******************************************************************************/ +/* + * Restore the input state + */ + +void ejsLexRestoreInputState(Ejs *ep, EjsInput *state) +{ + EjsInput *ip; + int i; + + mprAssert(ep); + mprAssert(state); + + ip = ep->input; + mprAssert(ip); + + ip->tokbuf = state->tokbuf; + ip->tokServp = state->tokServp; + ip->tokEndp = state->tokEndp; + ip->tokSize = state->tokSize; + + ip->script = state->script; + ip->scriptServp = state->scriptServp; + ip->scriptSize = state->scriptSize; + + ip->putBackIndex = state->putBackIndex; + for (i = 0; i < ip->putBackIndex; i++) { + mprFree(ip->putBack[i].token); + ip->putBack[i].id = state->putBack[i].id; + ip->putBack[i].token = mprStrdup(state->putBack[i].token); + } + + mprFree(ip->line); + ip->line = mprMalloc(state->lineLength); + mprStrcpy(ip->line, state->lineLength, state->line); + + ip->lineColumn = state->lineColumn; + ip->lineNumber = state->lineNumber; + ip->lineLength = state->lineLength; +} + +/******************************************************************************/ +/* + * Free a saved input state + */ + +void ejsLexFreeInputState(Ejs *ep, EjsInput *state) +{ + int i; + + mprAssert(ep); + mprAssert(state); + + for (i = 0; i < EJS_TOKEN_STACK; i++) { + mprFree(state->putBack[i].token); + } + state->putBackIndex = -1; + mprFree(state->line); + state->lineLength = 0; + state->lineColumn = 0; +} + +/******************************************************************************/ +/* + * Get the next EJS token + */ + +int ejsLexGetToken(Ejs *ep, int state) +{ + mprAssert(ep); + + ep->tid = getLexicalToken(ep, state); + return ep->tid; +} + +/******************************************************************************/ + +/* + * Check for reserved words "if", "else", "var", "for", "foreach", + * "delete", "function", and "return". "new", "in" and "function" + * done below. "true", "false", "null", "undefined" are handled + * as global objects. + * + * Other reserved words not supported: + * "break", "case", "catch", "continue", "default", "do", + * "finally", "instanceof", "switch", "this", "throw", "try", + * "typeof", "while", "with" + * + * ECMA extensions reserved words (not supported): + * "abstract", "boolean", "byte", "char", "class", "const", + * "debugger", "double", "enum", "export", "extends", + * "final", "float", "goto", "implements", "import", "int", + * "interface", "long", "native", "package", "private", + * "protected", "public", "short", "static", "super", + * "synchronized", "throws", "transient", "volatile" + */ + +static int checkReservedWord(Ejs *ep, int state, int c, int tid) +{ + if (state == EJS_STATE_STMT) { + if (strcmp(ep->token, "if") == 0) { + inputPutback(ep, c); + return EJS_TOK_IF; + } else if (strcmp(ep->token, "else") == 0) { + inputPutback(ep, c); + return EJS_TOK_ELSE; + } else if (strcmp(ep->token, "var") == 0) { + inputPutback(ep, c); + return EJS_TOK_VAR; + } else if (strcmp(ep->token, "for") == 0) { + inputPutback(ep, c); + return EJS_TOK_FOR; + } else if (strcmp(ep->token, "delete") == 0) { + inputPutback(ep, c); + return EJS_TOK_DELETE; + } else if (strcmp(ep->token, "function") == 0) { + inputPutback(ep, c); + return EJS_TOK_FUNCTION; + } else if (strcmp(ep->token, "return") == 0) { + if ((c == ';') || (c == '(')) { + inputPutback(ep, c); + } + return EJS_TOK_RETURN; + } + } else if (state == EJS_STATE_EXPR) { + if (strcmp(ep->token, "new") == 0) { + inputPutback(ep, c); + return EJS_TOK_NEW; + } else if (strcmp(ep->token, "in") == 0) { + inputPutback(ep, c); + return EJS_TOK_IN; + } else if (strcmp(ep->token, "function") == 0) { + inputPutback(ep, c); + return EJS_TOK_FUNCTION; + } + } + return tid; +} + +/******************************************************************************/ +/* + * Get the next EJS token + */ + +static int getLexicalToken(Ejs *ep, int state) +{ + MprType type; + EjsInput *ip; + int done, tid, c, quote, style, idx; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + ep->tid = -1; + tid = -1; + type = BLD_FEATURE_NUM_TYPE_ID; + + /* + * Use a putback tokens first. Don't free strings as caller needs access. + */ + if (ip->putBackIndex >= 0) { + idx = ip->putBackIndex; + tid = ip->putBack[idx].id; + ep->token = (char*) ip->putBack[idx].token; + tid = checkReservedWord(ep, state, 0, tid); + ip->putBackIndex--; + return tid; + } + ep->token = ip->tokServp = ip->tokEndp = ip->tokbuf; + *ip->tokServp = '\0'; + + if ((c = inputGetc(ep)) < 0) { + return EJS_TOK_EOF; + } + + /* + * Main lexical analyser + */ + for (done = 0; !done; ) { + switch (c) { + case -1: + return EJS_TOK_EOF; + + case ' ': + case '\t': + case '\r': + do { + if ((c = inputGetc(ep)) < 0) + break; + } while (c == ' ' || c == '\t' || c == '\r'); + break; + + case '\n': + return EJS_TOK_NEWLINE; + + case '(': + tokenAddChar(ep, c); + return EJS_TOK_LPAREN; + + case ')': + tokenAddChar(ep, c); + return EJS_TOK_RPAREN; + + case '[': + tokenAddChar(ep, c); + return EJS_TOK_LBRACKET; + + case ']': + tokenAddChar(ep, c); + return EJS_TOK_RBRACKET; + + case '.': + tokenAddChar(ep, c); + return EJS_TOK_PERIOD; + + case '{': + tokenAddChar(ep, c); + return EJS_TOK_LBRACE; + + case '}': + tokenAddChar(ep, c); + return EJS_TOK_RBRACE; + + case '+': + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + if (c != '+' ) { + inputPutback(ep, c); + tokenAddChar(ep, EJS_EXPR_PLUS); + return EJS_TOK_EXPR; + } + tokenAddChar(ep, EJS_EXPR_INC); + return EJS_TOK_INC_DEC; + + case '-': + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + if (c != '-' ) { + inputPutback(ep, c); + tokenAddChar(ep, EJS_EXPR_MINUS); + return EJS_TOK_EXPR; + } + tokenAddChar(ep, EJS_EXPR_DEC); + return EJS_TOK_INC_DEC; + + case '*': + tokenAddChar(ep, EJS_EXPR_MUL); + return EJS_TOK_EXPR; + + case '%': + tokenAddChar(ep, EJS_EXPR_MOD); + return EJS_TOK_EXPR; + + case '/': + /* + * Handle the division operator and comments + */ + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + if (c != '*' && c != '/') { + inputPutback(ep, c); + tokenAddChar(ep, EJS_EXPR_DIV); + return EJS_TOK_EXPR; + } + style = c; + /* + * Eat comments. Both C and C++ comment styles are supported. + */ + while (1) { + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + if (c == '\n' && style == '/') { + break; + } else if (c == '*') { + c = inputGetc(ep); + if (style == '/') { + if (c == '\n') { + break; + } + } else { + if (c == '/') { + break; + } + } + } + } + /* + * Continue looking for a token, so get the next character + */ + if ((c = inputGetc(ep)) < 0) { + return EJS_TOK_EOF; + } + break; + + case '<': /* < and <= */ + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + if (c == '<') { + tokenAddChar(ep, EJS_EXPR_LSHIFT); + return EJS_TOK_EXPR; + } else if (c == '=') { + tokenAddChar(ep, EJS_EXPR_LESSEQ); + return EJS_TOK_EXPR; + } + tokenAddChar(ep, EJS_EXPR_LESS); + inputPutback(ep, c); + return EJS_TOK_EXPR; + + case '>': /* > and >= */ + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + if (c == '>') { + tokenAddChar(ep, EJS_EXPR_RSHIFT); + return EJS_TOK_EXPR; + } else if (c == '=') { + tokenAddChar(ep, EJS_EXPR_GREATEREQ); + return EJS_TOK_EXPR; + } + tokenAddChar(ep, EJS_EXPR_GREATER); + inputPutback(ep, c); + return EJS_TOK_EXPR; + + case '=': /* "==" */ + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + if (c == '=') { + tokenAddChar(ep, EJS_EXPR_EQ); + return EJS_TOK_EXPR; + } + inputPutback(ep, c); + return EJS_TOK_ASSIGNMENT; + + case '!': /* "!=" or "!"*/ + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + if (c == '=') { + tokenAddChar(ep, EJS_EXPR_NOTEQ); + return EJS_TOK_EXPR; + } + inputPutback(ep, c); + tokenAddChar(ep, EJS_EXPR_BOOL_COMP); + return EJS_TOK_EXPR; + + case ';': + tokenAddChar(ep, c); + return EJS_TOK_SEMI; + + case ',': + tokenAddChar(ep, c); + return EJS_TOK_COMMA; + + case '|': /* "||" */ + if ((c = inputGetc(ep)) < 0 || c != '|') { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + tokenAddChar(ep, EJS_COND_OR); + return EJS_TOK_LOGICAL; + + case '&': /* "&&" */ + if ((c = inputGetc(ep)) < 0 || c != '&') { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + tokenAddChar(ep, EJS_COND_AND); + return EJS_TOK_LOGICAL; + + case '\"': /* String quote */ + case '\'': + quote = c; + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Syntax Error"); + return EJS_TOK_ERR; + } + + while (c != quote) { + /* + * Check for escape sequence characters + */ + if (c == '\\') { + c = inputGetc(ep); + + if (isdigit(c)) { + /* + * Octal support, \101 maps to 65 = 'A'. Put first + * char back so converter will work properly. + */ + inputPutback(ep, c); + c = charConvert(ep, 8, 3); + + } else { + switch (c) { + case 'n': + c = '\n'; break; + case 'b': + c = '\b'; break; + case 'f': + c = '\f'; break; + case 'r': + c = '\r'; break; + case 't': + c = '\t'; break; + case 'x': + /* + * Hex support, \x41 maps to 65 = 'A' + */ + c = charConvert(ep, 16, 2); + break; + case 'u': + /* + * Unicode support, \x0401 maps to 65 = 'A' + */ + c = charConvert(ep, 16, 2); + c = c*16 + charConvert(ep, 16, 2); + + break; + case '\'': + case '\"': + case '\\': + break; + default: + ejsError(ep, "Invalid Escape Sequence"); + return EJS_TOK_ERR; + } + } + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + } else { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + } + if ((c = inputGetc(ep)) < 0) { + ejsError(ep, "Unmatched Quote"); + return EJS_TOK_ERR; + } + } + return EJS_TOK_LITERAL; + + case '0': + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + if (tolower(c) == 'x') { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + } + if (! isdigit(c)) { +#if BLD_FEATURE_FLOATING_POINT + if (c == '.' || tolower(c) == 'e' || c == '+' || c == '-') { + /* Fall through */ + type = MPR_TYPE_FLOAT; + } else +#endif + { + mprDestroyVar(&ep->tokenNumber); + ep->tokenNumber = mprParseVar(ep->token, type); + inputPutback(ep, c); + return EJS_TOK_NUMBER; + } + } + /* Fall through to get more digits */ + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + do { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } +#if BLD_FEATURE_FLOATING_POINT + if (c == '.' || tolower(c) == 'e' || c == '+' || c == '-') { + type = MPR_TYPE_FLOAT; + } + } while (isdigit(c) || c == '.' || tolower(c) == 'e' || + c == '+' || c == '-'); +#else + } while (isdigit(c)); +#endif + + mprDestroyVar(&ep->tokenNumber); + ep->tokenNumber = mprParseVar(ep->token, type); + inputPutback(ep, c); + return EJS_TOK_NUMBER; + + default: + /* + * Identifiers or a function names + */ + while (1) { + if (c == '\\') { + if ((c = inputGetc(ep)) < 0) { + break; + } + if (c == '\n' || c == '\r') { + break; + } + } else if (tokenAddChar(ep, c) < 0) { + break; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + if (!isalnum(c) && c != '$' && c != '_' && c != '\\') { + break; + } + } + if (*ep->token == '\0') { + c = inputGetc(ep); + break; + } + if (! isalpha((int) *ep->token) && *ep->token != '$' && + *ep->token != '_') { + ejsError(ep, "Invalid identifier %s", ep->token); + return EJS_TOK_ERR; + } + + tid = checkReservedWord(ep, state, c, EJS_TOK_ID); + if (tid != EJS_TOK_ID) { + return tid; + } + + /* + * Skip white space after token to find out whether this is + * a function or not. + */ + while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + if ((c = inputGetc(ep)) < 0) + break; + } + + tid = EJS_TOK_ID; + done++; + } + } + + /* + * Putback the last extra character for next time + */ + inputPutback(ep, c); + return tid; +} + +/******************************************************************************/ +/* + * Convert a hex or octal character back to binary, return original char if + * not a hex digit + */ + +static int charConvert(Ejs *ep, int base, int maxDig) +{ + int i, c, lval, convChar; + + lval = 0; + for (i = 0; i < maxDig; i++) { + if ((c = inputGetc(ep)) < 0) { + break; + } + /* + * Initialize to out of range value + */ + convChar = base; + if (isdigit(c)) { + convChar = c - '0'; + } else if (c >= 'a' && c <= 'f') { + convChar = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + convChar = c - 'A' + 10; + } + /* + * If unexpected character then return it to buffer. + */ + if (convChar >= base) { + inputPutback(ep, c); + break; + } + lval = (lval * base) + convChar; + } + return lval; +} + +/******************************************************************************/ +/* + * Putback the last token read. Accept at most one push back token. + */ + +void ejsLexPutbackToken(Ejs *ep, int tid, char *string) +{ + EjsInput *ip; + int idx; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + ip->putBackIndex += 1; + idx = ip->putBackIndex; + ip->putBack[idx].id = tid; + + if (ip->putBack[idx].token) { + if (ip->putBack[idx].token == string) { + return; + } + mprFree(ip->putBack[idx].token); + } + ip->putBack[idx].token = mprStrdup(string); +} + +/******************************************************************************/ +/* + * Add a character to the token buffer + */ + +static int tokenAddChar(Ejs *ep, int c) +{ + EjsInput *ip; + uchar *oldbuf; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + if (ip->tokEndp >= &ip->tokbuf[ip->tokSize - 1]) { + ip->tokSize += EJS_PARSE_INCR; + oldbuf = ip->tokbuf; + ip->tokbuf = mprRealloc(ip->tokbuf, ip->tokSize); + if (ip->tokbuf == 0) { + ejsError(ep, "Token too big"); + return -1; + } + ip->tokEndp += (int) ((uchar*) ip->tokbuf - oldbuf); + ip->tokServp += (int) ((uchar*) ip->tokbuf - oldbuf); + ep->token += (int) ((uchar*) ip->tokbuf - oldbuf); + } + *ip->tokEndp++ = c; + *ip->tokEndp = '\0'; + + return 0; +} + +/******************************************************************************/ +/* + * Get another input character + */ + +static int inputGetc(Ejs *ep) +{ + EjsInput *ip; + int c; + + mprAssert(ep); + ip = ep->input; + + if (ip->scriptSize <= 0) { + return -1; + } + + c = (uchar) (*ip->scriptServp++); + ip->scriptSize--; + + /* + * For debugging, accumulate the line number and the currenly parsed line + */ + if (c == '\n') { +#if BLD_DEBUG && 0 + if (ip->lineColumn > 0) { + printf("PARSED: %s\n", ip->line); + } +#endif + ip->lineNumber++; + ip->lineColumn = 0; + } else { + if ((ip->lineColumn + 2) >= ip->lineLength) { + ip->lineLength += 80; + ip->line = mprRealloc(ip->line, ip->lineLength * sizeof(char)); + } + ip->line[ip->lineColumn++] = c; + ip->line[ip->lineColumn] = '\0'; + } + return c; +} + +/******************************************************************************/ +/* + * Putback a character onto the input queue + */ + +static void inputPutback(Ejs *ep, int c) +{ + EjsInput *ip; + + mprAssert(ep); + + if (c != 0) { + ip = ep->input; + *--ip->scriptServp = c; + ip->scriptSize++; + ip->lineColumn--; + ip->line[ip->lineColumn] = '\0'; + } +} + +/******************************************************************************/ + +#else +void ejsLexDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/web_server/ejs/ejsParser.c b/source4/web_server/ejs/ejsParser.c new file mode 100644 index 0000000000..a7d27fb0c9 --- /dev/null +++ b/source4/web_server/ejs/ejsParser.c @@ -0,0 +1,2358 @@ +/* + * @file ejsParser.c + * @brief EJS Parser and Execution + */ +/********************************* 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 "web_server/ejs/ejsInternal.h" + +#if BLD_FEATURE_EJS + +/****************************** Forward Declarations **************************/ + +static void appendValue(MprVar *v1, MprVar *v2); +static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs); +static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs); +#if BLD_FEATURE_FLOATING_POINT +static int evalFloatExpr(Ejs *ep, double l, int rel, double r); +#endif +static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r); +static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r); +static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs); +static int evalFunction(Ejs *ep, MprVar *obj, int flags); +static void freeProc(EjsProc *proc); +static int parseArgs(Ejs *ep, int state, int flags); +static int parseAssignment(Ejs *ep, int state, int flags, char *id, + char *fullName); +static int parseCond(Ejs *ep, int state, int flags); +static int parseDeclaration(Ejs *ep, int state, int flags); +static int parseExpr(Ejs *ep, int state, int flags); +static int parseFor(Ejs *ep, int state, int flags); +static int parseForIn(Ejs *ep, int state, int flags); +static int parseFunctionDec(Ejs *ep, int state, int flags); +static int parseFunction(Ejs *ep, int state, int flags, char *id); +static int parseId(Ejs *ep, int state, int flags, char **id, + char **fullName, int *fullNameLen, int *done); +static int parseInc(Ejs *ep, int state, int flags); +static int parseIf(Ejs *ep, int state, int flags, int *done); +static int parseStmt(Ejs *ep, int state, int flags); +static void removeNewlines(Ejs *ep, int state); +static void updateResult(Ejs *ep, int state, int flags, MprVar *vp); + +/************************************* Code ***********************************/ +/* + * Recursive descent parser for EJS + */ + +int ejsParse(Ejs *ep, int state, int flags) +{ + mprAssert(ep); + + switch (state) { + /* + * Any statement, function arguments or conditional expressions + */ + case EJS_STATE_STMT: + if ((state = parseStmt(ep, state, flags)) != EJS_STATE_STMT_DONE && + state != EJS_STATE_EOF && state != EJS_STATE_STMT_BLOCK_DONE && + state != EJS_STATE_RET) { + state = EJS_STATE_ERR; + } + break; + + case EJS_STATE_DEC: + if ((state = parseStmt(ep, state, flags)) != EJS_STATE_DEC_DONE && + state != EJS_STATE_EOF) { + state = EJS_STATE_ERR; + } + break; + + case EJS_STATE_EXPR: + if ((state = parseStmt(ep, state, flags)) != EJS_STATE_EXPR_DONE && + state != EJS_STATE_EOF) { + state = EJS_STATE_ERR; + } + break; + + /* + * Variable declaration list + */ + case EJS_STATE_DEC_LIST: + state = parseDeclaration(ep, state, flags); + break; + + /* + * Function argument string + */ + case EJS_STATE_ARG_LIST: + state = parseArgs(ep, state, flags); + break; + + /* + * Logical condition list (relational operations separated by &&, ||) + */ + case EJS_STATE_COND: + state = parseCond(ep, state, flags); + break; + + /* + * Expression list + */ + case EJS_STATE_RELEXP: + state = parseExpr(ep, state, flags); + break; + } + + if (state == EJS_STATE_ERR && ep->error == NULL) { + ejsError(ep, "Syntax error"); + } + return state; +} + +/******************************************************************************/ +/* + * Parse any statement including functions and simple relational operations + */ + +static int parseStmt(Ejs *ep, int state, int flags) +{ + EjsProc *saveProc; + MprVar *vp, *saveObj; + char *id, *fullName, *initToken; + int done, expectSemi, tid, fullNameLen, rel; + int initId; + + mprAssert(ep); + + expectSemi = 0; + saveProc = NULL; + id = 0; + fullName = 0; + fullNameLen = 0; + + ep->currentObj = 0; + ep->currentProperty = 0; + + for (done = 0; !done && state != EJS_STATE_ERR; ) { + tid = ejsLexGetToken(ep, state); + + switch (tid) { + default: + ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token); + done++; + break; + + case EJS_TOK_EXPR: + rel = (int) *ep->token; + if (state == EJS_STATE_EXPR) { + ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token); + } + done++; + break; + + case EJS_TOK_LOGICAL: + ejsLexPutbackToken(ep, tid, ep->token); + done++; + break; + + case EJS_TOK_ERR: + state = EJS_STATE_ERR; + done++; + break; + + case EJS_TOK_EOF: + state = EJS_STATE_EOF; + done++; + break; + + case EJS_TOK_NEWLINE: + break; + + case EJS_TOK_SEMI: + /* + * This case is when we discover no statement and just a lone ';' + */ + if (state != EJS_STATE_STMT) { + ejsLexPutbackToken(ep, tid, ep->token); + } + done++; + break; + + case EJS_TOK_PERIOD: + if (flags & EJS_FLAGS_EXE) { + if (ep->currentProperty == 0) { + ejsError(ep, "Undefined object \"%s\"\n", id); + goto error; + } + } + ep->currentObj = ep->currentProperty; + + if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) { + ejsError(ep, "Bad property after '.': %s\n", ep->token); + goto error; + } + mprFree(id); + id = mprStrdup(ep->token); + + vp = ejsFindProperty(ep, state, ep->currentObj, id, flags); + updateResult(ep, state, flags, vp); + +#if BLD_DEBUG + fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, fullNameLen, + 0, ".", 0); +#endif + + ep->currentProperty = vp; + ejsLexPutbackToken(ep, tid, ep->token); + break; + + case EJS_TOK_LBRACKET: + ep->currentObj = ep->currentProperty; + saveObj = ep->currentObj; + if (ejsParse(ep, EJS_STATE_RELEXP, flags) != EJS_STATE_RELEXP_DONE){ + goto error; + } + ep->currentObj = saveObj; + + mprFree(id); + mprVarToString(&id, MPR_MAX_STRING, 0, &ep->result); + + if (id[0] == '\0') { + if (flags & EJS_FLAGS_EXE) { + ejsError(ep, + "[] expression evaluates to the empty string\n"); + goto error; + } + } else { + vp = ejsFindProperty(ep, state, ep->currentObj, id, flags); + ep->currentProperty = vp; + updateResult(ep, state, flags, vp); + } + +#if BLD_DEBUG + if (id[0] && strlen(id) < (MPR_MAX_VAR / 2)) { + /* + * If not executing yet, id may not be known + */ + fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, + fullNameLen, 0, "[", id, "]", 0); + } +#endif + + if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_RBRACKET) { + ejsError(ep, "Missing ']'\n"); + goto error; + } + break; + + case EJS_TOK_ID: + state = parseId(ep, state, flags, &id, &fullName, &fullNameLen, + &done); + if (done && state == EJS_STATE_STMT) { + expectSemi++; + } + break; + + case EJS_TOK_ASSIGNMENT: + state = parseAssignment(ep, state, flags, id, fullName); + if (state == EJS_STATE_STMT) { + expectSemi++; + done++; + } + break; + + case EJS_TOK_INC_DEC: + state = parseInc(ep, state, flags); + if (state == EJS_STATE_STMT) { + expectSemi++; + } + break; + + case EJS_TOK_NEW: + if (ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_NEW) + != EJS_STATE_EXPR_DONE) { + goto error; + } + break; + + case EJS_TOK_DELETE: + if (ejsParse(ep, EJS_STATE_EXPR, + flags | EJS_FLAGS_DELETE) != EJS_STATE_EXPR_DONE) { + goto error; + } + mprDeleteProperty(ep->currentObj, ep->currentProperty->name); + done++; + break; + + case EJS_TOK_FUNCTION: + state = parseFunctionDec(ep, state, flags); + done++; + break; + + case EJS_TOK_LITERAL: + /* + * Set the result to the string literal + */ + mprCopyVarValue(&ep->result, mprCreateStringVar(ep->token, 0), + MPR_SHALLOW_COPY); + if (state == EJS_STATE_STMT) { + expectSemi++; + } + done++; + break; + + case EJS_TOK_NUMBER: + /* + * Set the result to the parsed number + */ + mprCopyVar(&ep->result, &ep->tokenNumber, 0); + if (state == EJS_STATE_STMT) { + expectSemi++; + } + done++; + break; + + case EJS_TOK_FUNCTION_NAME: + state = parseFunction(ep, state, flags, id); + if (state == EJS_STATE_STMT) { + expectSemi++; + } + if (ep->flags & EJS_FLAGS_EXIT) { + state = EJS_STATE_RET; + } + done++; + break; + + case EJS_TOK_IF: + state = parseIf(ep, state, flags, &done); + if (state == EJS_STATE_RET) { + goto doneParse; + } + break; + + case EJS_TOK_FOR: + if (state != EJS_STATE_STMT) { + goto error; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) { + goto error; + } + /* + * Need to peek 2-3 tokens ahead and see if this is a + * for ([var] x in set) + * or + * for (init ; whileCond; incr) + */ + initId = ejsLexGetToken(ep, EJS_STATE_EXPR); + if (initId == EJS_TOK_ID && strcmp(ep->token, "var") == 0) { + /* Simply eat var tokens */ + initId = ejsLexGetToken(ep, EJS_STATE_EXPR); + } + initToken = mprStrdup(ep->token); + + tid = ejsLexGetToken(ep, EJS_STATE_EXPR); + + ejsLexPutbackToken(ep, tid, ep->token); + ejsLexPutbackToken(ep, initId, initToken); + mprFree(initToken); + + if (tid == EJS_TOK_IN) { + if ((state = parseForIn(ep, state, flags)) < 0) { + goto error; + } + } else { + if ((state = parseFor(ep, state, flags)) < 0) { + goto error; + } + } + done++; + break; + + case EJS_TOK_VAR: + if (ejsParse(ep, EJS_STATE_DEC_LIST, flags) + != EJS_STATE_DEC_LIST_DONE) { + goto error; + } + done++; + break; + + case EJS_TOK_COMMA: + ejsLexPutbackToken(ep, tid, ep->token); + done++; + break; + + case EJS_TOK_LPAREN: + if (state == EJS_STATE_EXPR) { + if (ejsParse(ep, EJS_STATE_RELEXP, flags) + != EJS_STATE_RELEXP_DONE) { + goto error; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + goto error; + } + } + done++; + break; + + case EJS_TOK_RPAREN: + ejsLexPutbackToken(ep, tid, ep->token); + done++; + break; + + case EJS_TOK_LBRACE: + /* + * This handles any code in braces except "if () {} else {}" + */ + if (state != EJS_STATE_STMT) { + goto error; + } + + /* + * Parse will return EJS_STATE_STMT_BLOCK_DONE when the RBRACE + * is seen. + */ + do { + state = ejsParse(ep, EJS_STATE_STMT, flags); + } while (state == EJS_STATE_STMT_DONE); + + if (state != EJS_STATE_RET) { + if (ejsLexGetToken(ep, state) != EJS_TOK_RBRACE) { + goto error; + } + state = EJS_STATE_STMT_DONE; + } + done++; + break; + + case EJS_TOK_RBRACE: + if (state == EJS_STATE_STMT) { + ejsLexPutbackToken(ep, tid, ep->token); + state = EJS_STATE_STMT_BLOCK_DONE; + done++; + break; + } + goto error; + + case EJS_TOK_RETURN: + if (ejsParse(ep, EJS_STATE_RELEXP, flags) + != EJS_STATE_RELEXP_DONE) { + goto error; + } + if (flags & EJS_FLAGS_EXE) { + while (ejsLexGetToken(ep, state) != EJS_TOK_EOF) { + ; + } + state = EJS_STATE_RET; + done++; + } + break; + } + } + + if (expectSemi) { + tid = ejsLexGetToken(ep, state); + if (tid != EJS_TOK_SEMI && tid != EJS_TOK_NEWLINE && + tid != EJS_TOK_EOF) { + goto error; + } + + /* + * Skip newline after semi-colon + */ + removeNewlines(ep, state); + } + +/* + * Free resources and return the correct status + */ +doneParse: + mprFree(id); + mprFree(fullName); + + /* + * Advance the state + */ + switch (state) { + case EJS_STATE_STMT: + return EJS_STATE_STMT_DONE; + + case EJS_STATE_DEC: + return EJS_STATE_DEC_DONE; + + case EJS_STATE_EXPR: + return EJS_STATE_EXPR_DONE; + + case EJS_STATE_STMT_DONE: + case EJS_STATE_STMT_BLOCK_DONE: + case EJS_STATE_EOF: + case EJS_STATE_RET: + return state; + + default: + return EJS_STATE_ERR; + } + +/* + * Common error exit + */ +error: + state = EJS_STATE_ERR; + goto doneParse; +} + +/******************************************************************************/ +/* + * Parse function arguments + */ + +static int parseArgs(Ejs *ep, int state, int flags) +{ + int tid; + + mprAssert(ep); + + do { + /* + * Peek and see if there are no args + */ + tid = ejsLexGetToken(ep, state); + ejsLexPutbackToken(ep, tid, ep->token); + if (tid == EJS_TOK_RPAREN) { + break; + } + + state = ejsParse(ep, EJS_STATE_RELEXP, flags); + if (state == EJS_STATE_EOF || state == EJS_STATE_ERR) { + return state; + } + if (state == EJS_STATE_RELEXP_DONE) { + if (flags & EJS_FLAGS_EXE) { + mprAssert(ep->proc->args); + mprAddToArray(ep->proc->args, + mprDupVar(&ep->result, MPR_SHALLOW_COPY)); + } + } + /* + * Peek at the next token, continue if more args (ie. comma seen) + */ + tid = ejsLexGetToken(ep, state); + if (tid != EJS_TOK_COMMA) { + ejsLexPutbackToken(ep, tid, ep->token); + } + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_RPAREN && state != EJS_STATE_RELEXP_DONE) { + return EJS_STATE_ERR; + } + return EJS_STATE_ARG_LIST_DONE; +} + +/******************************************************************************/ +/* + * Parse an assignment statement + */ + +static int parseAssignment(Ejs *ep, int state, int flags, char *id, + char *fullName) +{ + MprVar *vp, *saveProperty, *saveObj; + + if (id == 0) { + return -1; + } + + saveObj = ep->currentObj; + saveProperty = ep->currentProperty; + if (ejsParse(ep, EJS_STATE_RELEXP, flags | EJS_FLAGS_ASSIGNMENT) + != EJS_STATE_RELEXP_DONE) { + return -1; + } + ep->currentObj = saveObj; + ep->currentProperty = saveProperty; + + if (! (flags & EJS_FLAGS_EXE)) { + return state; + } + + if (ep->currentProperty) { + /* + * Update the variable. Update the property name if not + * yet defined. + */ + if (ep->currentProperty->name == 0 || + ep->currentProperty->name[0] == '\0') { + mprSetVarName(ep->currentProperty, id); + } + if (mprWriteProperty(ep->currentProperty, &ep->result) < 0){ + ejsError(ep, "Can't write to variable\n"); + return -1; + } + + } else { + /* + * Create the variable + */ + if (ep->currentObj) { + if (ep->currentObj->type != MPR_TYPE_OBJECT) { + if (strcmp(ep->currentObj->name, "session") == 0) { + ejsError(ep, "Variable \"%s\" is not an array or object." + "If using ESP, you need useSession(); in your page.", + ep->currentObj->name); + } else { + ejsError(ep, "Variable \"%s\" is not an array or object", + ep->currentObj->name); + } + return -1; + } + vp = mprCreateProperty(ep->currentObj, id, &ep->result); + + } else { + /* + * Standard says: "var x" means declare locally. + * "x = 2" means declare globally if x is undefined. + */ + if (state == EJS_STATE_DEC) { + vp = mprCreateProperty(ep->local, id, &ep->result); + } else { + vp = mprCreateProperty(ep->global, id, &ep->result); + } + } +#if BLD_DEBUG + mprSetVarFullName(vp, fullName); +#endif + } + return state; +} + +/******************************************************************************/ +/* + * Parse conditional expression (relational ops separated by ||, &&) + */ + +static int parseCond(Ejs *ep, int state, int flags) +{ + MprVar lhs, rhs; + int tid, operator; + + mprAssert(ep); + + mprDestroyVar(&ep->result); + rhs = lhs = mprCreateUndefinedVar(); + operator = 0; + + do { + /* + * Recurse to handle one side of a conditional. Accumulate the + * left hand side and the final result in ep->result. + */ + state = ejsParse(ep, EJS_STATE_RELEXP, flags); + if (state != EJS_STATE_RELEXP_DONE) { + state = EJS_STATE_ERR; + break; + } + + if (operator > 0) { + mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY); + if (evalCond(ep, &lhs, operator, &rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + } + mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY); + + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_LOGICAL) { + operator = (int) *ep->token; + + } else if (tid == EJS_TOK_RPAREN || tid == EJS_TOK_SEMI) { + ejsLexPutbackToken(ep, tid, ep->token); + state = EJS_STATE_COND_DONE; + break; + + } else { + ejsLexPutbackToken(ep, tid, ep->token); + } + tid = (state == EJS_STATE_RELEXP_DONE); + + } while (state == EJS_STATE_RELEXP_DONE); + + mprDestroyVar(&lhs); + mprDestroyVar(&rhs); + return state; +} + +/******************************************************************************/ +/* + * Parse variable declaration list. Declarations can be of the following forms: + * var x; + * var x, y, z; + * var x = 1 + 2 / 3, y = 2 + 4; + * + * We set the variable to NULL if there is no associated assignment. + */ + +static int parseDeclaration(Ejs *ep, int state, int flags) +{ + int tid; + + mprAssert(ep); + + do { + if ((tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) { + return EJS_STATE_ERR; + } + ejsLexPutbackToken(ep, tid, ep->token); + + /* + * Parse the entire assignment or simple identifier declaration + */ + if (ejsParse(ep, EJS_STATE_DEC, flags) != EJS_STATE_DEC_DONE) { + return EJS_STATE_ERR; + } + + /* + * Peek at the next token, continue if comma seen + */ + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_SEMI) { + return EJS_STATE_DEC_LIST_DONE; + } else if (tid != EJS_TOK_COMMA) { + return EJS_STATE_ERR; + } + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_SEMI) { + return EJS_STATE_ERR; + } + return EJS_STATE_DEC_LIST_DONE; +} + +/******************************************************************************/ +/* + * Parse expression (leftHandSide operator rightHandSide) + */ + +static int parseExpr(Ejs *ep, int state, int flags) +{ + MprVar lhs, rhs; + int rel, tid; + + mprAssert(ep); + + mprDestroyVar(&ep->result); + rhs = lhs = mprCreateUndefinedVar(); + rel = 0; + tid = 0; + + do { + /* + * This loop will handle an entire expression list. We call parse + * to evalutate each term which returns the result in ep->result. + */ + if (tid == EJS_TOK_LOGICAL) { + state = ejsParse(ep, EJS_STATE_RELEXP, flags); + if (state != EJS_STATE_RELEXP_DONE) { + state = EJS_STATE_ERR; + break; + } + } else { + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_EXPR && (int) *ep->token == EJS_EXPR_MINUS) { + lhs = mprCreateIntegerVar(0); + rel = (int) *ep->token; + } else { + ejsLexPutbackToken(ep, tid, ep->token); + } + + state = ejsParse(ep, EJS_STATE_EXPR, flags); + if (state != EJS_STATE_EXPR_DONE) { + state = EJS_STATE_ERR; + break; + } + } + + if (rel > 0) { + mprCopyVar(&rhs, &ep->result, MPR_SHALLOW_COPY); + if (tid == EJS_TOK_LOGICAL) { + if (evalCond(ep, &lhs, rel, &rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + } else { + if (evalExpr(ep, &lhs, rel, &rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + } + } + mprCopyVar(&lhs, &ep->result, MPR_SHALLOW_COPY); + + if ((tid = ejsLexGetToken(ep, state)) == EJS_TOK_EXPR || + tid == EJS_TOK_INC_DEC || tid == EJS_TOK_LOGICAL) { + rel = (int) *ep->token; + + } else { + ejsLexPutbackToken(ep, tid, ep->token); + state = EJS_STATE_RELEXP_DONE; + } + + } while (state == EJS_STATE_EXPR_DONE); + + mprDestroyVar(&lhs); + mprDestroyVar(&rhs); + + return state; +} + +/******************************************************************************/ +/* + * Parse the "for ... in" statement. Format for the statement is: + * + * for (var in expr) { + * body; + * } + */ + +static int parseForIn(Ejs *ep, int state, int flags) +{ + EjsInput endScript, bodyScript; + MprVar *iteratorVar, *setVar, *vp, v; + int forFlags, tid; + + mprAssert(ep); + + tid = ejsLexGetToken(ep, state); + if (tid != EJS_TOK_ID) { + return -1; + } + ejsLexPutbackToken(ep, tid, ep->token); + + if (ejsParse(ep, EJS_STATE_EXPR, EJS_FLAGS_FOREACH | EJS_FLAGS_EXE) + != EJS_STATE_EXPR_DONE) { + return -1; + } + if (ep->currentProperty == 0) { + return -1; + } + iteratorVar = ep->currentProperty; + + if (ejsLexGetToken(ep, state) != EJS_TOK_IN) { + return -1; + } + + /* + * Get the set + */ + tid = ejsLexGetToken(ep, state); + if (tid != EJS_TOK_ID) { + return -1; + } + ejsLexPutbackToken(ep, tid, ep->token); + + if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) { + return -1; + } + if (ep->currentProperty == 0 && flags & EJS_FLAGS_EXE) { + return -1; + } + setVar = ep->currentProperty; + + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + return -1; + } + + /* + * Parse the body and remember the end of the body script + */ + forFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, &bodyScript); + if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) { + ejsLexFreeInputState(ep, &bodyScript); + return -1; + } + ejsInitInputState(&endScript); + ejsLexSaveInputState(ep, &endScript); + + /* + * Now actually do the for loop. + */ + if (flags & EJS_FLAGS_EXE) { + if (setVar->type == MPR_TYPE_OBJECT) { + vp = mprGetFirstProperty(setVar, MPR_ENUM_DATA); + while (vp) { + if (strcmp(vp->name, "length") != 0) { + v = mprCreateStringVar(vp->name, 0); + if (mprWriteProperty(iteratorVar, &v) < 0) { + ejsError(ep, "Can't write to variable\n"); + ejsLexFreeInputState(ep, &bodyScript); + ejsLexFreeInputState(ep, &endScript); + return -1; + } + + ejsLexRestoreInputState(ep, &bodyScript); + switch (ejsParse(ep, EJS_STATE_STMT, flags)) { + case EJS_STATE_RET: + return EJS_STATE_RET; + case EJS_STATE_STMT_DONE: + break; + default: + ejsLexFreeInputState(ep, &endScript); + ejsLexFreeInputState(ep, &bodyScript); + return -1; + } + } + vp = mprGetNextProperty(setVar, vp, MPR_ENUM_DATA); + } + } else { + ejsError(ep, "Variable \"%s\" is not an array or object", + setVar->name); + ejsLexFreeInputState(ep, &endScript); + ejsLexFreeInputState(ep, &bodyScript); + return -1; + } + } + ejsLexRestoreInputState(ep, &endScript); + + ejsLexFreeInputState(ep, &endScript); + ejsLexFreeInputState(ep, &bodyScript); + + return state; +} + +/******************************************************************************/ +/* + * Parse the for statement. Format for the expression is: + * + * for (initial; condition; incr) { + * body; + * } + */ + +static int parseFor(Ejs *ep, int state, int flags) +{ + EjsInput condScript, endScript, bodyScript, incrScript; + int forFlags, cond; + + ejsInitInputState(&endScript); + ejsInitInputState(&bodyScript); + ejsInitInputState(&incrScript); + ejsInitInputState(&condScript); + + mprAssert(ep); + + /* + * Evaluate the for loop initialization statement + */ + if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE) { + return -1; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) { + return -1; + } + + /* + * The first time through, we save the current input context just prior + * to each step: prior to the conditional, the loop increment and + * the loop body. + */ + ejsLexSaveInputState(ep, &condScript); + if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) { + goto error; + } + cond = (ep->result.boolean != 0); + + if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) { + goto error; + } + + /* + * Don't execute the loop increment statement or the body + * first time. + */ + forFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, &incrScript); + if (ejsParse(ep, EJS_STATE_EXPR, forFlags) != EJS_STATE_EXPR_DONE) { + goto error; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + goto error; + } + + /* + * Parse the body and remember the end of the body script + */ + ejsLexSaveInputState(ep, &bodyScript); + if (ejsParse(ep, EJS_STATE_STMT, forFlags) != EJS_STATE_STMT_DONE) { + goto error; + } + ejsLexSaveInputState(ep, &endScript); + + /* + * Now actually do the for loop. Note loop has been rotated + */ + while (cond && (flags & EJS_FLAGS_EXE)) { + /* + * Evaluate the body + */ + ejsLexRestoreInputState(ep, &bodyScript); + + switch (ejsParse(ep, EJS_STATE_STMT, flags)) { + case EJS_STATE_RET: + return EJS_STATE_RET; + case EJS_STATE_STMT_DONE: + break; + default: + goto error; + } + /* + * Evaluate the increment script + */ + ejsLexRestoreInputState(ep, &incrScript); + if (ejsParse(ep, EJS_STATE_EXPR, flags) != EJS_STATE_EXPR_DONE){ + goto error; + } + /* + * Evaluate the condition + */ + ejsLexRestoreInputState(ep, &condScript); + if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) { + goto error; + } + mprAssert(ep->result.type == MPR_TYPE_BOOL); + cond = (ep->result.boolean != 0); + } + + ejsLexRestoreInputState(ep, &endScript); + +done: + ejsLexFreeInputState(ep, &condScript); + ejsLexFreeInputState(ep, &incrScript); + ejsLexFreeInputState(ep, &endScript); + ejsLexFreeInputState(ep, &bodyScript); + return state; + +error: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse a function declaration + */ + +static int parseFunctionDec(Ejs *ep, int state, int flags) +{ + EjsInput endScript, bodyScript; + MprVar v, *currentObj, *vp; + char *procName; + int len, tid, bodyFlags; + + mprAssert(ep); + mprAssert(ejsPtr(ep->eid)); + + /* + * function <name>(arg, arg, arg) { body }; + * function name(arg, arg, arg) { body }; + */ + + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_ID) { + procName = mprStrdup(ep->token); + tid = ejsLexGetToken(ep, state); + } else { + procName = 0; + } + if (tid != EJS_TOK_LPAREN) { + mprFree(procName); + return EJS_STATE_ERR; + } + + /* + * Hand craft the function value structure. + */ + v = mprCreateFunctionVar(0, 0, 0); + tid = ejsLexGetToken(ep, state); + while (tid == EJS_TOK_ID) { + mprAddToArray(v.function.args, mprStrdup(ep->token)); + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_RPAREN || tid != EJS_TOK_COMMA) { + break; + } + tid = ejsLexGetToken(ep, state); + } + if (tid != EJS_TOK_RPAREN) { + mprFree(procName); + mprDestroyVar(&v); + return EJS_STATE_ERR; + } + + /* Allow new lines before opening brace */ + do { + tid = ejsLexGetToken(ep, state); + } while (tid == EJS_TOK_NEWLINE); + + if (tid != EJS_TOK_LBRACE) { + mprFree(procName); + mprDestroyVar(&v); + return EJS_STATE_ERR; + } + + /* + * Parse the function body. Turn execute off. + */ + bodyFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, &bodyScript); + + do { + state = ejsParse(ep, EJS_STATE_STMT, bodyFlags); + } while (state == EJS_STATE_STMT_DONE); + + tid = ejsLexGetToken(ep, state); + if (state != EJS_STATE_STMT_BLOCK_DONE || tid != EJS_TOK_RBRACE) { + mprFree(procName); + mprDestroyVar(&v); + ejsLexFreeInputState(ep, &bodyScript); + return EJS_STATE_ERR; + } + ejsLexSaveInputState(ep, &endScript); + + /* + * Save the function body between the starting and ending parse positions. + * Overwrite the trailing '}' with a null. + */ + len = endScript.scriptServp - bodyScript.scriptServp; + v.function.body = mprMalloc(len + 1); + memcpy(v.function.body, bodyScript.scriptServp, len); + + if (len <= 0) { + v.function.body[0] = '\0'; + } else { + v.function.body[len - 1] = '\0'; + } + ejsLexFreeInputState(ep, &bodyScript); + ejsLexFreeInputState(ep, &endScript); + + /* + * If we are in an assignment, don't register the function name, rather + * return the function structure in the parser result. + */ + if (flags & EJS_FLAGS_ASSIGNMENT) { + mprCopyVar(&ep->result, &v, MPR_SHALLOW_COPY); + } else { + currentObj = ejsFindObj(ep, 0, procName, flags); + vp = mprSetProperty(currentObj, procName, &v); + } + + mprFree(procName); + mprDestroyVar(&v); + + return EJS_STATE_STMT; +} + +/******************************************************************************/ +/* + * Parse a function name and invoke the function + */ + +static int parseFunction(Ejs *ep, int state, int flags, char *id) +{ + EjsProc proc, *saveProc; + MprVar *saveObj; + + /* + * Must save any current ep->proc value for the current stack frame + * to allow for recursive function calls. + */ + saveProc = (ep->proc) ? ep->proc: 0; + + memset(&proc, 0, sizeof(EjsProc)); + proc.procName = mprStrdup(id); + proc.fn = ep->currentProperty; + proc.args = mprCreateArray(); + ep->proc = &proc; + + mprDestroyVar(&ep->result); + + saveObj = ep->currentObj; + if (ejsParse(ep, EJS_STATE_ARG_LIST, flags) != EJS_STATE_ARG_LIST_DONE) { + freeProc(&proc); + ep->proc = saveProc; + return -1; + } + ep->currentObj = saveObj; + + /* + * Evaluate the function if required + */ + if (flags & EJS_FLAGS_EXE) { + if (evalFunction(ep, ep->currentObj, flags) < 0) { + freeProc(&proc); + ep->proc = saveProc; + return -1; + } + } + + freeProc(&proc); + ep->proc = saveProc; + + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + return -1; + } + return state; +} + +/******************************************************************************/ +/* + * Parse an identifier. This is a segment of a fully qualified variable. + * May come here for an initial identifier or for property names + * after a "." or "[...]". + */ + +static int parseId(Ejs *ep, int state, int flags, char **id, char **fullName, + int *fullNameLen, int *done) +{ + int tid; + + mprFree(*id); + *id = mprStrdup(ep->token); +#if BLD_DEBUG + *fullNameLen = mprReallocStrcat(fullName, MPR_MAX_VAR, *fullNameLen, + 0, *id, 0); +#endif + if (ep->currentObj == 0) { + ep->currentObj = ejsFindObj(ep, state, *id, flags); + } + + /* + * Find the referenced variable and store it in currentProperty. + */ + ep->currentProperty = ejsFindProperty(ep, state, ep->currentObj, + *id, flags); + updateResult(ep, state, flags, ep->currentProperty); + +#if BLD_DEBUG + if (ep->currentProperty && (ep->currentProperty->name == 0 || + ep->currentProperty->name[0] == '\0')) { + mprSetVarName(ep->currentProperty, *id); + } +#endif + + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_LPAREN) { + if (ep->currentProperty == 0) { + ejsError(ep, "Function name not defined \"%s\"\n", *id); + return -1; + } + ejsLexPutbackToken(ep, EJS_TOK_FUNCTION_NAME, ep->token); + return state; + } + + if (tid == EJS_TOK_PERIOD || tid == EJS_TOK_LBRACKET || + tid == EJS_TOK_ASSIGNMENT || tid == EJS_TOK_INC_DEC) { + ejsLexPutbackToken(ep, tid, ep->token); + return state; + } + + /* + * Only come here for variable access and declarations. + * Assignment handled elsewhere. + */ + if (flags & EJS_FLAGS_EXE) { + if (state == EJS_STATE_DEC) { + /* + * Declare a variable. Standard allows: var x ; var x ; + */ +#if DISABLED + if (ep->currentProperty != 0) { + ejsError(ep, "Variable already defined \"%s\"\n", *id); + return -1; + } +#endif + /* + * Create or overwrite if it already exists + */ + mprSetPropertyValue(ep->currentObj, *id, + mprCreateUndefinedVar()); + ep->currentProperty = 0; + mprDestroyVar(&ep->result); + + } else if (flags & EJS_FLAGS_FOREACH) { + if (ep->currentProperty == 0) { + ep->currentProperty = + mprCreatePropertyValue(ep->currentObj, *id, + mprCreateUndefinedVar()); + } + + } else { + if (ep->currentProperty == 0) { + if (ep->currentObj == ep->global || + ep->currentObj == ep->local) { + ejsError(ep, "Undefined variable \"%s\"\n", *id); + return -1; + } + ep->currentProperty = mprCreatePropertyValue(ep->currentObj, + *id, mprCreateUndefinedVar()); + } + } + } + ejsLexPutbackToken(ep, tid, ep->token); + if (tid == EJS_TOK_RBRACKET || tid == EJS_TOK_COMMA || + tid == EJS_TOK_IN) { + *done = 1; + } + return state; +} + +/******************************************************************************/ +/* + * Parse an "if" statement + */ + +static int parseIf(Ejs *ep, int state, int flags, int *done) +{ + bool ifResult; + int thenFlags, elseFlags, tid; + + if (state != EJS_STATE_STMT) { + return -1; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) { + return -1; + } + + /* + * Evaluate the entire condition list "(condition)" + */ + if (ejsParse(ep, EJS_STATE_COND, flags) != EJS_STATE_COND_DONE) { + return -1; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + return -1; + } + + /* + * This is the "then" case. We need to always parse both cases and + * execute only the relevant case. + */ + ifResult = mprVarToBool(&ep->result); + if (ifResult) { + thenFlags = flags; + elseFlags = flags & ~EJS_FLAGS_EXE; + } else { + thenFlags = flags & ~EJS_FLAGS_EXE; + elseFlags = flags; + } + + /* + * Process the "then" case. + */ + switch (ejsParse(ep, EJS_STATE_STMT, thenFlags)) { + case EJS_STATE_RET: + state = EJS_STATE_RET; + return state; + case EJS_STATE_STMT_DONE: + break; + default: + return -1; + } + + /* + * Check to see if there is an "else" case + */ + removeNewlines(ep, state); + tid = ejsLexGetToken(ep, state); + if (tid != EJS_TOK_ELSE) { + ejsLexPutbackToken(ep, tid, ep->token); + *done = 1; + return state; + } + + /* + * Process the "else" case. + */ + switch (ejsParse(ep, EJS_STATE_STMT, elseFlags)) { + case EJS_STATE_RET: + state = EJS_STATE_RET; + return state; + case EJS_STATE_STMT_DONE: + break; + default: + return -1; + } + *done = 1; + return state; +} + +/******************************************************************************/ +/* + * Parse an "++" or "--" statement + */ + +static int parseInc(Ejs *ep, int state, int flags) +{ + MprVar one; + + if (! (flags & EJS_FLAGS_EXE)) { + return state; + } + + if (ep->currentProperty == 0) { + ejsError(ep, "Undefined variable \"%s\"\n", ep->token); + return -1; + } + one = mprCreateIntegerVar(1); + if (evalExpr(ep, ep->currentProperty, (int) *ep->token, + &one) < 0) { + return -1; + } + if (mprWriteProperty(ep->currentProperty, &ep->result) < 0) { + ejsError(ep, "Can't write to variable\n"); + return -1; + } + return state; +} + +/******************************************************************************/ +/* + * Evaluate a condition. Implements &&, ||, !. Returns with a boolean result + * in ep->result. Returns -1 on errors, zero if successful. + */ + +static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) +{ + bool l, r, lval; + + mprAssert(rel > 0); + + l = mprVarToBool(lhs); + r = mprVarToBool(rhs); + + switch (rel) { + case EJS_COND_AND: + lval = l && r; + break; + case EJS_COND_OR: + lval = l || r; + break; + default: + ejsError(ep, "Bad operator %d", rel); + return -1; + } + + mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate an operation. Returns with the result in ep->result. Returns -1 + * on errors, otherwise zero is returned. + */ + +static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) +{ + char *str; + MprNum lval, num; + int rc; + + mprAssert(rel > 0); + str = 0; + lval = 0; + + /* + * Type conversion. This is tricky and must be according to the standard. + * Only numbers (including floats) and strings can be compared. All other + * types are first converted to numbers by preference and if that fails, + * to strings. + * + * First convert objects to comparable types. The "===" operator will + * test the sameness of object references. Here, we coerce to comparable + * types first. + */ + if (lhs->type == MPR_TYPE_OBJECT) { + if (ejsRunFunction(ep->eid, lhs, "toValue", 0) == 0) { + mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY); + } else { + if (ejsRunFunction(ep->eid, lhs, "toString", 0) == 0) { + mprCopyVar(lhs, &ep->result, MPR_SHALLOW_COPY); + } + } + /* Nothing more can be done */ + } + + if (rhs->type == MPR_TYPE_OBJECT) { + if (ejsRunFunction(ep->eid, rhs, "toValue", 0) == 0) { + mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY); + } else { + if (ejsRunFunction(ep->eid, rhs, "toString", 0) == 0) { + mprCopyVar(rhs, &ep->result, MPR_SHALLOW_COPY); + } + } + /* Nothing more can be done */ + } + + /* + * From here on, lhs and rhs may contain allocated data (strings), so + * we must always destroy before overwriting. + */ + + /* + * Only allow a few bool operations. Otherwise convert to number. + */ + if (lhs->type == MPR_TYPE_BOOL && rhs->type == MPR_TYPE_BOOL && + (rel != EJS_EXPR_EQ && rel != EJS_EXPR_NOTEQ && + rel != EJS_EXPR_BOOL_COMP)) { + num = mprVarToNumber(lhs); + mprDestroyVar(lhs); + *lhs = mprCreateNumberVar(num); + } + + /* + * Types do not match, so try to coerce the right operand to match the left + * But first, try to convert a left operand that is a numeric stored as a + * string, into a numeric. + */ + if (lhs->type != rhs->type) { + if (lhs->type == MPR_TYPE_STRING) { + if (isdigit((int) lhs->string[0])) { + num = mprVarToNumber(lhs); + mprDestroyVar(lhs); + *lhs = mprCreateNumberVar(num); + /* Examine further below */ + + } else { + /* + * Convert the RHS to a string + */ + mprVarToString(&str, MPR_MAX_STRING, 0, rhs); + mprDestroyVar(rhs); + *rhs = mprCreateStringVar(str, 1); + mprFree(str); + } + +#if BLD_FEATURE_FLOATING_POINT + } else if (lhs->type == MPR_TYPE_FLOAT) { + /* + * Convert rhs to floating + */ + double f = mprVarToFloat(rhs); + mprDestroyVar(rhs); + *rhs = mprCreateFloatVar(f); + +#endif +#if BLD_FEATURE_INT64 + } else if (lhs->type == MPR_TYPE_INT64) { + /* + * Convert the rhs to 64 bit + */ + int64 n = mprVarToInteger64(rhs); + mprDestroyVar(rhs); + *rhs = mprCreateInteger64Var(n); +#endif + } else if (lhs->type == MPR_TYPE_BOOL || lhs->type == MPR_TYPE_INT) { + + if (rhs->type == MPR_TYPE_STRING) { + /* + * Convert to lhs to a string + */ + mprVarToString(&str, MPR_MAX_STRING, 0, lhs); + mprDestroyVar(lhs); + *lhs = mprCreateStringVar(str, 1); + mprFree(str); + +#if BLD_FEATURE_FLOATING_POINT + } else if (rhs->type == MPR_TYPE_FLOAT) { + /* + * Convert lhs to floating + */ + double f = mprVarToFloat(lhs); + mprDestroyVar(lhs); + *lhs = mprCreateFloatVar(f); +#endif + + } else { + /* + * Convert both operands to numbers + */ + num = mprVarToNumber(lhs); + mprDestroyVar(lhs); + *lhs = mprCreateNumberVar(num); + + num = mprVarToNumber(rhs); + mprDestroyVar(rhs); + *rhs = mprCreateNumberVar(num); + } + } + } + + /* + * We have failed to coerce the types to be the same. Special case here + * for undefined and null. We need to allow comparisions against these + * special values. + */ + if (lhs->type == MPR_TYPE_UNDEFINED || lhs->type == MPR_TYPE_NULL) { + switch (rel) { + case EJS_EXPR_EQ: + lval = lhs->type == rhs->type; + break; + case EJS_EXPR_NOTEQ: + lval = lhs->type != rhs->type; + break; + default: + lval = 0; + } + mprCopyVarValue(&ep->result, mprCreateBoolVar((bool) lval), 0); + return 0; + } + + /* + * Types are the same here + */ + switch (lhs->type) { + default: + case MPR_TYPE_UNDEFINED: + case MPR_TYPE_NULL: + /* Should be handled above */ + mprAssert(0); + return 0; + + case MPR_TYPE_STRING_CFUNCTION: + case MPR_TYPE_CFUNCTION: + case MPR_TYPE_FUNCTION: + case MPR_TYPE_OBJECT: + mprCopyVarValue(&ep->result, mprCreateBoolVar(0), 0); + return 0; + + case MPR_TYPE_BOOL: + rc = evalBoolExpr(ep, lhs->boolean, rel, rhs->boolean); + break; + +#if BLD_FEATURE_FLOATING_POINT + case MPR_TYPE_FLOAT: + rc = evalFloatExpr(ep, lhs->floating, rel, rhs->floating); + break; +#endif + + case MPR_TYPE_INT: + rc = evalNumericExpr(ep, (MprNum) lhs->integer, rel, + (MprNum) rhs->integer); + break; + +#if BLD_FEATURE_INT64 + case MPR_TYPE_INT64: + rc = evalNumericExpr(ep, (MprNum) lhs->integer64, rel, + (MprNum) rhs->integer64); + break; +#endif + + case MPR_TYPE_STRING: + rc = evalStringExpr(ep, lhs, rel, rhs); + } + return rc; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Expressions with floating operands + */ + +static int evalFloatExpr(Ejs *ep, double l, int rel, double r) +{ + double lval; + bool logical; + + lval = 0; + logical = 0; + + switch (rel) { + case EJS_EXPR_PLUS: + lval = l + r; + break; + case EJS_EXPR_INC: + lval = l + 1; + break; + case EJS_EXPR_MINUS: + lval = l - r; + break; + case EJS_EXPR_DEC: + lval = l - 1; + break; + case EJS_EXPR_MUL: + lval = l * r; + break; + case EJS_EXPR_DIV: + lval = l / r; + break; + default: + logical++; + break; + } + + /* + * Logical operators + */ + if (logical) { + + switch (rel) { + case EJS_EXPR_EQ: + lval = l == r; + break; + case EJS_EXPR_NOTEQ: + lval = l != r; + break; + case EJS_EXPR_LESS: + lval = (l < r) ? 1 : 0; + break; + case EJS_EXPR_LESSEQ: + lval = (l <= r) ? 1 : 0; + break; + case EJS_EXPR_GREATER: + lval = (l > r) ? 1 : 0; + break; + case EJS_EXPR_GREATEREQ: + lval = (l >= r) ? 1 : 0; + break; + case EJS_EXPR_BOOL_COMP: + lval = (r == 0) ? 1 : 0; + break; + default: + ejsError(ep, "Bad operator %d", rel); + return -1; + } + mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0); + + } else { + mprCopyVarValue(&ep->result, mprCreateFloatVar(lval), 0); + } + return 0; +} + +#endif /* BLD_FEATURE_FLOATING_POINT */ +/******************************************************************************/ +/* + * Expressions with boolean operands + */ + +static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r) +{ + bool lval; + + switch (rel) { + case EJS_EXPR_EQ: + lval = l == r; + break; + case EJS_EXPR_NOTEQ: + lval = l != r; + break; + case EJS_EXPR_BOOL_COMP: + lval = (r == 0) ? 1 : 0; + break; + default: + ejsError(ep, "Bad operator %d", rel); + return -1; + } + mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0); + return 0; +} + +/******************************************************************************/ +/* + * Expressions with numeric operands + */ + +static int evalNumericExpr(Ejs *ep, MprNum l, int rel, MprNum r) +{ + MprNum lval; + bool logical; + + lval = 0; + logical = 0; + + switch (rel) { + case EJS_EXPR_PLUS: + lval = l + r; + break; + case EJS_EXPR_INC: + lval = l + 1; + break; + case EJS_EXPR_MINUS: + lval = l - r; + break; + case EJS_EXPR_DEC: + lval = l - 1; + break; + case EJS_EXPR_MUL: + lval = l * r; + break; + case EJS_EXPR_DIV: + if (r != 0) { + lval = l / r; + } else { + ejsError(ep, "Divide by zero"); + return -1; + } + break; + case EJS_EXPR_MOD: + if (r != 0) { + lval = l % r; + } else { + ejsError(ep, "Modulo zero"); + return -1; + } + break; + case EJS_EXPR_LSHIFT: + lval = l << r; + break; + case EJS_EXPR_RSHIFT: + lval = l >> r; + break; + + default: + logical++; + break; + } + + /* + * Logical operators + */ + if (logical) { + + switch (rel) { + case EJS_EXPR_EQ: + lval = l == r; + break; + case EJS_EXPR_NOTEQ: + lval = l != r; + break; + case EJS_EXPR_LESS: + lval = (l < r) ? 1 : 0; + break; + case EJS_EXPR_LESSEQ: + lval = (l <= r) ? 1 : 0; + break; + case EJS_EXPR_GREATER: + lval = (l > r) ? 1 : 0; + break; + case EJS_EXPR_GREATEREQ: + lval = (l >= r) ? 1 : 0; + break; + case EJS_EXPR_BOOL_COMP: + lval = (r == 0) ? 1 : 0; + break; + default: + ejsError(ep, "Bad operator %d", rel); + return -1; + } + mprCopyVarValue(&ep->result, mprCreateBoolVar(lval != 0), 0); + + } else { + mprCopyVarValue(&ep->result, mprCreateNumberVar(lval), 0); + } + return 0; +} + +/******************************************************************************/ +/* + * Expressions with string operands + */ + +static int evalStringExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) +{ + int lval; + + mprAssert(ep); + mprAssert(lhs); + mprAssert(rhs); + + switch (rel) { + case EJS_EXPR_LESS: + lval = strcmp(lhs->string, rhs->string) < 0; + break; + case EJS_EXPR_LESSEQ: + lval = strcmp(lhs->string, rhs->string) <= 0; + break; + case EJS_EXPR_GREATER: + lval = strcmp(lhs->string, rhs->string) > 0; + break; + case EJS_EXPR_GREATEREQ: + lval = strcmp(lhs->string, rhs->string) >= 0; + break; + case EJS_EXPR_EQ: + lval = strcmp(lhs->string, rhs->string) == 0; + break; + case EJS_EXPR_NOTEQ: + lval = strcmp(lhs->string, rhs->string) != 0; + break; + case EJS_EXPR_PLUS: + /* + * This differs from all the above operations. We append rhs to lhs. + */ + mprDestroyVar(&ep->result); + appendValue(&ep->result, lhs); + appendValue(&ep->result, rhs); + return 0; + + case EJS_EXPR_INC: + case EJS_EXPR_DEC: + case EJS_EXPR_MINUS: + case EJS_EXPR_DIV: + case EJS_EXPR_MOD: + case EJS_EXPR_LSHIFT: + case EJS_EXPR_RSHIFT: + default: + ejsError(ep, "Bad operator"); + return -1; + } + + mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate a function. obj is set to the current object if a function is being + * run. + */ + +static int evalFunction(Ejs *ep, MprVar *obj, int flags) +{ + EjsProc *proc; + MprVar arguments, callee, thisObject, *prototype, **argValues; + MprArray *formalArgs, *actualArgs; + char buf[16], **argNames, **argBuf; + int i, rc, fid; + + mprAssert(ep); + mprAssert(ejsPtr(ep->eid)); + + rc = -1; + proc = ep->proc; + prototype = proc->fn; + actualArgs = proc->args; + argValues = (MprVar**) actualArgs->handles; + + /* + * Create a new variable stack frame. ie. new local variables. + */ + fid = ejsOpenBlock(ep->eid); + + if (flags & EJS_FLAGS_NEW) { + /* + * Create a new bare object and pass it into the constructor as the + * "this" local variable. + */ + thisObject = ejsCreateObj("this", EJS_OBJ_HASH_SIZE); + mprCreatePropertyValue(ep->local, "this", thisObject); + + } else if (obj) { + mprCreateProperty(ep->local, "this", obj); + } + + switch (prototype->type) { + default: + mprAssert(0); + break; + + case MPR_TYPE_STRING_CFUNCTION: + if (actualArgs->used > 0) { + argBuf = mprMalloc(actualArgs->used * sizeof(char*)); + for (i = 0; i < actualArgs->used; i++) { + mprVarToString(&argBuf[i], MPR_MAX_STRING, 0, argValues[i]); + } + } else { + argBuf = 0; + } + + /* + * Call the function depending on the various handle flags + */ + ep->thisPtr = prototype->cFunctionWithStrings.thisPtr; + if (prototype->flags & MPR_VAR_ALT_HANDLE) { + rc = ((EjsAltStringCFunction) prototype->cFunctionWithStrings.fn) + (ep->eid, ep->altHandle, actualArgs->used, argBuf); + } else if (prototype->flags & MPR_VAR_SCRIPT_HANDLE) { + rc = (prototype->cFunctionWithStrings.fn)(ep->eid, + actualArgs->used, argBuf); + } else { + rc = (prototype->cFunctionWithStrings.fn)(ep->primaryHandle, + actualArgs->used, argBuf); + } + + if (actualArgs->used > 0) { + for (i = 0; i < actualArgs->used; i++) { + mprFree(argBuf[i]); + } + mprFree(argBuf); + } + ep->thisPtr = 0; + break; + + case MPR_TYPE_CFUNCTION: + /* + * Call the function depending on the various handle flags + */ + ep->thisPtr = prototype->cFunction.thisPtr; + if (prototype->flags & MPR_VAR_ALT_HANDLE) { + rc = ((EjsAltCFunction) prototype->cFunction.fn) + (ep->eid, ep->altHandle, actualArgs->used, argValues); + } else if (prototype->flags & MPR_VAR_SCRIPT_HANDLE) { + rc = (prototype->cFunction.fn)(ep->eid, actualArgs->used, + argValues); + } else { + rc = (prototype->cFunction.fn)(ep->primaryHandle, + actualArgs->used, argValues); + } + ep->thisPtr = 0; + break; + + case MPR_TYPE_FUNCTION: + + formalArgs = prototype->function.args; + argNames = (char**) formalArgs->handles; + +#if FUTURE + if (formalArgs->used != actualArgs->used) { + ejsError(ep, "Bad number of args. Should be %d", formalArgs->used); + return -1; + } +#endif + + /* + * Create the arguments and callee variables + */ + arguments = ejsCreateObj("arguments", EJS_SMALL_OBJ_HASH_SIZE); + callee = ejsCreateObj("callee", EJS_SMALL_OBJ_HASH_SIZE); + + /* + * Overwrite the length property + */ + mprCreatePropertyValue(&arguments, "length", + mprCreateIntegerVar(actualArgs->used)); + mprCreatePropertyValue(&callee, "length", + mprCreateIntegerVar(formalArgs->used)); + + /* + * Define all the agruments to be set to the actual parameters + */ + for (i = 0; i < formalArgs->used; i++) { + mprCreateProperty(ep->local, argNames[i], argValues[i]); + mprItoa(i, buf, sizeof(buf)); + mprCreateProperty(&arguments, buf, argValues[i]); + } + + mprCreateProperty(&arguments, "callee", &callee); + mprCreateProperty(ep->local, "arguments", &arguments); + + /* + * Can destroy our variables here as they are now referenced via + * "local" + */ + mprDestroyVar(&callee); + mprDestroyVar(&arguments); + + /* + * Actually run the function + */ + rc = ejsEvalScript(ep->eid, prototype->function.body, 0, 0); + break; + } + + ejsCloseBlock(ep->eid, fid); + + /* + * New statements return the newly created object as the result of the + * command + */ + if (flags & EJS_FLAGS_NEW) { + mprDestroyVar(&ep->result); + /* + * Don't copy, we want to assign the actual object into result. + * (mprCopyVar would inc the refCount to 2). + */ + ep->result = thisObject; + } + return rc; +} + +/******************************************************************************/ +/* + * Run a function + */ + +int ejsRunFunction(int eid, MprVar *obj, const char *functionName, MprArray *args) +{ + EjsProc proc, *saveProc; + Ejs *ep; + int rc; + + mprAssert(obj); + mprAssert(functionName && *functionName); + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return MPR_ERR_NOT_FOUND; + } + saveProc = ep->proc; + ep->proc = &proc; + + memset(&proc, 0, sizeof(EjsProc)); + mprDestroyVar(&ep->result); + + proc.fn = mprGetProperty(obj, functionName, 0); + if (proc.fn == 0 || proc.fn->type == MPR_TYPE_UNDEFINED) { + ep->proc = saveProc; + return MPR_ERR_NOT_FOUND; + } + proc.procName = mprStrdup(functionName); + if (args == 0) { + proc.args = mprCreateArray(); + rc = evalFunction(ep, obj, 0); + } else { + proc.args = args; + rc = evalFunction(ep, obj, 0); + proc.args = 0; + } + + freeProc(&proc); + ep->proc = saveProc; + + return rc; +} + +/******************************************************************************/ +/* + * Find which object contains the property given the current context. + * Only used for top level properties. + */ + +MprVar *ejsFindObj(Ejs *ep, int state, const char *property, int flags) +{ + MprVar *vp; + MprVar *obj; + + mprAssert(ep); + mprAssert(property && *property); + + if (flags & EJS_FLAGS_GLOBAL) { + obj = ep->global; + + } else if (state == EJS_STATE_DEC || flags & EJS_FLAGS_LOCAL) { + obj = ep->local; + + } else { + /* First look local, then look global */ + vp = mprGetProperty(ep->local, property, 0); + if (vp) { + obj = ep->local; + } else if (mprGetProperty(ep->local, property, 0)) { + obj = ep->local; + } else { + obj = ep->global; + } + } + return obj; +} + +/******************************************************************************/ +/* + * Find an object property given a object and a property name. We + * intelligently look in the local and global namespaces depending on + * our state. If not found in local or global, try base classes for function + * names only. Returns the property or NULL. + */ + +MprVar *ejsFindProperty(Ejs *ep, int state, MprVar *obj, char *property, + int flags) +{ + MprVar *vp; + + mprAssert(ep); + if (flags & EJS_FLAGS_EXE) { + mprAssert(property && *property); + } + + if (obj != 0) { +#if FUTURE && MB + op = obj; + do { + vp = mprGetProperty(op, property, 0); + if (vp != 0) { + if (op != obj && mprVarIsFunction(vp->type)) { + } + break; + } + op = op->baseObj; + } while (op); +#endif + vp = mprGetProperty(obj, property, 0); + + } else { + if (state == EJS_STATE_DEC) { + vp = mprGetProperty(ep->local, property, 0); + + } else { + /* Look local first, then global */ + vp = mprGetProperty(ep->local, property, 0); + if (vp == NULL) { + vp = mprGetProperty(ep->global, property, 0); + } + } + } + return vp; +} + +/******************************************************************************/ +/* + * Update result + */ + +static void updateResult(Ejs *ep, int state, int flags, MprVar *vp) +{ + if (flags & EJS_FLAGS_EXE && state != EJS_STATE_DEC) { + mprDestroyVar(&ep->result); + if (vp) { + mprCopyProperty(&ep->result, vp, MPR_SHALLOW_COPY); + } + } +} + +/******************************************************************************/ +/* + * Append to the pointer value + */ + +static void appendValue(MprVar *dest, MprVar *src) +{ + char *value, *oldBuf, *buf; + int len, oldLen; + + mprAssert(dest); + + mprVarToString(&value, MPR_MAX_STRING, 0, src); + + if (mprVarIsValid(dest)) { + len = strlen(value); + oldBuf = dest->string; + oldLen = strlen(oldBuf); + buf = mprRealloc(oldBuf, (len + oldLen + 1) * sizeof(char)); + dest->string = buf; + strncpy(&buf[oldLen], value, len+1); + } else { + *dest = mprCreateStringVar(value, 1); + } + mprFree(value); +} + +/******************************************************************************/ +/* + * Exit with status + */ + +void ejsSetExitStatus(int eid, int status) +{ + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return; + } + ep->exitStatus = status; + ep->flags |= EJS_FLAGS_EXIT; +} + +/******************************************************************************/ +/* + * Free an argument list + */ + +static void freeProc(EjsProc *proc) +{ + MprVar **argValues; + int i; + + if (proc->args) { + argValues = (MprVar**) proc->args->handles; + + for (i = 0; i < proc->args->max; i++) { + if (argValues[i]) { + mprDestroyVar(argValues[i]); + mprFree(argValues[i]); + mprRemoveFromArray(proc->args, i); + } + } + + mprDestroyArray(proc->args); + } + + if (proc->procName) { + mprFree(proc->procName); + proc->procName = NULL; + } +} + +/******************************************************************************/ +/* + * This function removes any new lines. Used for else cases, etc. + */ + +static void removeNewlines(Ejs *ep, int state) +{ + int tid; + + do { + tid = ejsLexGetToken(ep, state); + } while (tid == EJS_TOK_NEWLINE); + + ejsLexPutbackToken(ep, tid, ep->token); +} + +/******************************************************************************/ + +#else +void ejsParserDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/web_server/ejs/ejsProcs.c b/source4/web_server/ejs/ejsProcs.c new file mode 100644 index 0000000000..db9e5068c2 --- /dev/null +++ b/source4/web_server/ejs/ejsProcs.c @@ -0,0 +1,705 @@ +/* + * @file ejsProc.c + * @brief EJS support functions + */ +/********************************* 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 "web_server/ejs/ejsInternal.h" + +#if BLD_FEATURE_EJS + +/****************************** Forward Declarations **************************/ +/* + * Object constructors + */ +static int objectConsProc(EjsHandle eid, int argc, MprVar **argv); +static int arrayConsProc(EjsHandle eid, int argc, MprVar **argv); +static int booleanConsProc(EjsHandle eid, int argc, MprVar **agv); +static int numberConsProc(EjsHandle eid, int argc, MprVar **argv); +static int stringConsProc(EjsHandle eid, int argc, MprVar **argv); + +/* + * Core functions + */ +static int toStringProc(EjsHandle eid, int argc, MprVar **argv); +static int valueOfProc(EjsHandle eid, int argc, MprVar **argv); + +/* + * Triggers + */ +static MprVarTriggerStatus lengthTrigger(MprVarTriggerOp op, + MprProperties *parentProperties, MprVar *prop, MprVar *newValue, + bool copyRef); + +/******************************************************************************/ +/* + * Routine to create the base common to all object types + */ + +MprVar ejsCreateObj(const char *name, int hashSize) +{ + MprVar o; + + o = mprCreateObjVar(name, hashSize); + if (o.type == MPR_TYPE_UNDEFINED) { + mprAssert(0); + return o; + } + + mprCreatePropertyValue(&o, "toString", + mprCreateCFunctionVar(toStringProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(&o, "valueOf", + mprCreateCFunctionVar(valueOfProc, 0, MPR_VAR_SCRIPT_HANDLE)); + return o; +} + +/******************************************************************************/ +/* + * Routine to destroy a variable + */ + +bool ejsDestroyVar(MprVar *obj) +{ + return mprDestroyVar(obj); +} + +/******************************************************************************/ +/* + * Routine to create the base array type + */ + +MprVar ejsCreateArray(const char *name, int size) +{ + MprVar obj, *lp, undef; + char idx[16]; + int i; + + /* Sanity limit for size of hash table */ + + obj = ejsCreateObj(name, max(size, 503)); + if (obj.type == MPR_TYPE_UNDEFINED) { + mprAssert(0); + return obj; + } + + undef = mprCreateUndefinedVar(); + for (i = 0; i < size; i++) { + mprItoa(i, idx, sizeof(idx)); + mprCreateProperty(&obj, idx, &undef); + } + + lp = mprCreatePropertyValue(&obj, "length", mprCreateIntegerVar(size)); + mprAssert(lp); + + mprSetVarReadonly(lp, 1); + mprAddVarTrigger(lp, lengthTrigger); + + return obj; +} + +/******************************************************************************/ +/******************************** Constructors ********************************/ +/******************************************************************************/ +/* + * Object constructor. Nothing really done here. For future expansion. + */ + +static int objectConsProc(EjsHandle eid, int argc, MprVar **argv) +{ +#if UNUSED + MprVar *obj; + Ejs *ep; + + if((ep = ejsPtr(eid)) == NULL) { + return -1; + } + + obj = mprGetProperty(ep->local, "this", 0); + mprAssert(obj); +#endif + return 0; +} + +/******************************************************************************/ +/* + * Array constructor + */ + +static int arrayConsProc(EjsHandle eid, int argc, MprVar **argv) +{ + MprVar *obj, *lp, undef; + Ejs *ep; + char idx[16]; + int i, max; + + objectConsProc(eid, argc, argv); + + if((ep = ejsPtr(eid)) == NULL) { + return -1; + } + obj = mprGetProperty(ep->local, "this", 0); + mprAssert(obj); + + + if (argc == 1) { + /* + * x = new Array(size); + */ + undef = mprCreateUndefinedVar(); + max = (int) mprVarToInteger(argv[0]); + for (i = 0; i < max; i++) { + mprItoa(i, idx, sizeof(idx)); + mprCreateProperty(obj, idx, &undef); + } + } else if (argc > 1) { + /* + * x = new Array(element0, element1, ..., elementN): + */ + max = argc; + for (i = 0; i < max; i++) { + mprItoa(i, idx, sizeof(idx)); + mprCreateProperty(obj, idx, argv[i]); + } + + } else { + max = 0; + } + + lp = mprCreatePropertyValue(obj, "length", mprCreateIntegerVar(max)); + mprAssert(lp); + + mprSetVarReadonly(lp, 1); + mprAddVarTrigger(lp, lengthTrigger); + + return 0; +} + +/******************************************************************************/ +/* + * Boolean constructor + */ + +static int booleanConsProc(EjsHandle eid, int argc, MprVar **argv) +{ + objectConsProc(eid, argc, argv); + return 0; +} + +/******************************************************************************/ +#if FUTURE +/* + * Date constructor + */ + +static int dateConsProc(EjsHandle eid, int argc, MprVar **argv) +{ + objectConsProc(eid, argc, argv); + return 0; +} + +#endif +/******************************************************************************/ +/* + * Number constructor + */ + +static int numberConsProc(EjsHandle eid, int argc, MprVar **argv) +{ + objectConsProc(eid, argc, argv); + return 0; +} + +/******************************************************************************/ +/* + * String constructor + */ + +static int stringConsProc(EjsHandle eid, int argc, MprVar **argv) +{ + objectConsProc(eid, argc, argv); + return 0; +} + +/******************************************************************************/ +/********************************** Functions *********************************/ +/******************************************************************************/ + +static int toStringProc(EjsHandle eid, int argc, MprVar **argv) +{ + MprVar *obj; + Ejs *ep; + char *buf; + int radix; + + if (argc == 0) { + radix = 10; + + } else if (argc == 1) { + radix = (int) mprVarToInteger(argv[0]); + + } else { + mprAssert(0); + return -1; + } + + if((ep = ejsPtr(eid)) == NULL) { + return -1; + } + + obj = mprGetProperty(ep->local, "this", 0); + mprAssert(obj); + + mprVarToString(&buf, MPR_MAX_STRING, 0, obj); + mprCopyVarValue(&ep->result, mprCreateStringVar(buf, 0), MPR_SHALLOW_COPY); + mprFree(buf); + + return 0; +} + +/******************************************************************************/ + +static int valueOfProc(EjsHandle eid, int argc, MprVar **argv) +{ + MprVar *obj; + Ejs *ep; + + if (argc != 0) { + mprAssert(0); + return -1; + } + + if((ep = ejsPtr(eid)) == NULL) { + return -1; + } + + obj = mprGetProperty(ep->local, "this", 0); + mprAssert(obj); + + switch (obj->type) { + default: + case MPR_TYPE_UNDEFINED: + case MPR_TYPE_NULL: + case MPR_TYPE_CFUNCTION: + case MPR_TYPE_OBJECT: + case MPR_TYPE_FUNCTION: + case MPR_TYPE_STRING_CFUNCTION: + mprCopyVar(&ep->result, obj, MPR_SHALLOW_COPY); + break; + + case MPR_TYPE_STRING: + mprCopyVarValue(&ep->result, mprCreateIntegerVar(atoi(obj->string)), 0); + break; + + case MPR_TYPE_BOOL: + case MPR_TYPE_INT: +#if BLD_FEATURE_INT64 + case MPR_TYPE_INT64: +#endif +#if BLD_FEATURE_FLOATING_POINT + case MPR_TYPE_FLOAT: +#endif + mprCopyVar(&ep->result, obj, 0); + break; + } + return 0; +} + +/******************************************************************************/ +/* + * Var access trigger on the Array.length property. Return the count of + * enumerable properties (don't count functions). + */ + +static MprVarTriggerStatus lengthTrigger(MprVarTriggerOp op, + MprProperties *parentProperties, MprVar *prop, MprVar *newValue, + bool copyRef) +{ + switch (op) { + case MPR_VAR_READ: + /* + * Subtract one for the length property + * FUTURE -- need an API to access parentProperties + * FUTURE -- contradiction to be read-only yet allow USE_NEW_VALUE. + * API needs finer control. + */ + *newValue = mprCreateIntegerVar(parentProperties->numDataItems - 1); + return MPR_TRIGGER_USE_NEW_VALUE; + + case MPR_VAR_WRITE: + return MPR_TRIGGER_ABORT; + + case MPR_VAR_CREATE_PROPERTY: + case MPR_VAR_DELETE_PROPERTY: + case MPR_VAR_DELETE: + default: + break; + } + return MPR_TRIGGER_PROCEED; +} + +/******************************************************************************/ +/**************************** Extension Functions *****************************/ +/******************************************************************************/ +/* + * Assert + */ + +static int assertProc(EjsHandle eid, int argc, MprVar **argv) +{ + bool b; + + if (argc < 1) { + ejsSetErrorMsg(eid, "usage: assert(condition)\n"); + return -1; + } + b = mprVarToBool(argv[0]); + if (b == 0) { + ejsSetErrorMsg(eid, "Assertion failure\n"); + return -1; + } + ejsSetReturnValue(eid, mprCreateBoolVar(b)); + return 0; +} + +/******************************************************************************/ +/* + * Exit + */ + +static int exitProc(EjsHandle eid, int argc, MprVar **argv) +{ + int status; + + if (argc < 1) { + ejsSetErrorMsg(eid, "usage: exit(status)\n"); + return -1; + } + status = (int) mprVarToInteger(argv[0]); + ejsSetExitStatus(eid, status); + + ejsSetReturnValue(eid, mprCreateStringVar("", 0)); + return 0; +} + +/******************************************************************************/ + +static void printVar(MprVar *vp, int recurseCount, int indent) +{ + MprVar *np; + char *buf; + int i; + + if (recurseCount > 5) { + write(1, "Skipping - recursion too deep\n", 29); + return; + } + + for (i = 0; i < indent; i++) { + write(1, " ", 2); + } + + if (vp->type == MPR_TYPE_OBJECT) { + if (vp->name) { + write(1, vp->name, strlen(vp->name)); + } else { + write(1, "unknown", 7); + } + write(1, ": {\n", 4); + np = mprGetFirstProperty(vp, MPR_ENUM_DATA); + while (np) { + if (strcmp(np->name, "local") == 0 || + strcmp(np->name, "global") == 0 || + strcmp(np->name, "this") == 0) { + np = mprGetNextProperty(vp, np, MPR_ENUM_DATA); + continue; + } + printVar(np, recurseCount + 1, indent + 1); + np = mprGetNextProperty(vp, np, MPR_ENUM_DATA); + if (np) { + write(1, ",\n", 2); + } + } + write(1, "\n", 1); + for (i = 0; i < indent; i++) { + write(1, " ", 2); + } + write(1, "}", 1); + + } else { + if (vp->name) { + write(1, vp->name, strlen(vp->name)); + } else { + write(1, "unknown", 7); + } + write(1, ": ", 2); + + /* FUTURE -- other types ? */ + mprVarToString(&buf, MPR_MAX_STRING, 0, vp); + if (vp->type == MPR_TYPE_STRING) { + write(1, "\"", 1); + } + write(1, buf, strlen(buf)); + if (vp->type == MPR_TYPE_STRING) { + write(1, "\"", 1); + } + mprFree(buf); + } +} + +/******************************************************************************/ +/* + * Print the args to stdout + */ + +static int printVarsProc(EjsHandle eid, int argc, MprVar **argv) +{ + MprVar *vp; + char *buf; + int i; + + for (i = 0; i < argc; i++) { + vp = argv[i]; + switch (vp->type) { + case MPR_TYPE_OBJECT: + printVar(vp, 0, 0); + break; + default: + mprVarToString(&buf, MPR_MAX_STRING, 0, vp); + write(1, buf, strlen(buf)); + mprFree(buf); + break; + } + } + write(1, "\n", 1); + + ejsSetReturnValue(eid, mprCreateStringVar("", 0)); + return 0; +} + +/******************************************************************************/ +/* + * Print the args to stdout + */ + +static int printProc(EjsHandle eid, int argc, MprVar **argv) +{ + char *buf; + int i; + + for (i = 0; i < argc; i++) { + mprVarToString(&buf, MPR_MAX_STRING, 0, argv[i]); + write(1, buf, strlen(buf)); + mprFree(buf); + } + return 0; +} + +/******************************************************************************/ +/* + * println + */ + +static int printlnProc(EjsHandle eid, int argc, MprVar **argv) +{ + printProc(eid, argc, argv); + write(1, "\n", 1); + return 0; +} + +/******************************************************************************/ +/* + * Trace + */ + +static int traceProc(EjsHandle eid, int argc, char **argv) +{ + if (argc == 1) { + mprLog(0, argv[0]); + + } else if (argc == 2) { + mprLog(atoi(argv[0]), argv[1]); + + } else { + ejsSetErrorMsg(eid, "Usage: trace([level], message)"); + return -1; + } + ejsSetReturnString(eid, ""); + return 0; +} + +/******************************************************************************/ +/* + * Return the object reference count + */ + +static int refCountProc(EjsHandle eid, int argc, MprVar **argv) +{ + MprVar *vp; + int count; + + vp = argv[0]; + if (vp->type == MPR_TYPE_OBJECT) { + count = mprGetVarRefCount(vp); + ejsSetReturnValue(eid, mprCreateIntegerVar(count)); + } else { + ejsSetReturnValue(eid, mprCreateIntegerVar(0)); + } + + return 0; +} + +/******************************************************************************/ +/* + * Evaluate a sub-script. It is evaluated in the same variable scope as + * the calling script / function. + */ + +static int evalScriptProc(EjsHandle eid, int argc, MprVar **argv) +{ + MprVar *arg; + char *emsg; + int i; + + ejsSetReturnValue(eid, mprCreateUndefinedVar()); + + for (i = 0; i < argc; i++) { + arg = argv[i]; + if (arg->type != MPR_TYPE_STRING) { + continue; + } + if (ejsEvalScript(eid, arg->string, 0, &emsg) < 0) { + ejsSetErrorMsg(eid, "%s", emsg); + mprFree(emsg); + return -1; + } + } + /* + * Return with the value of the last expression + */ + return 0; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* + * Define the standard properties and functions inherited by all script engines. + */ + +int ejsDefineStandardProperties(MprVar *obj) +{ +#if BLD_FEATURE_FLOATING_POINT + double d = 0.0; + + /* FUTURE - this generates warnings on some systems. This is okay. */ + + mprCreatePropertyValue(obj, "NaN", mprCreateFloatVar(0.0 / d)); + d = MAX_FLOAT; + mprCreatePropertyValue(obj, "Infinity", mprCreateFloatVar(d * d)); +#endif + mprCreatePropertyValue(obj, "null", mprCreateNullVar()); + mprCreatePropertyValue(obj, "undefined", mprCreateUndefinedVar()); + mprCreatePropertyValue(obj, "true", mprCreateBoolVar(1)); + mprCreatePropertyValue(obj, "false", mprCreateBoolVar(0)); + +#if BLD_FEATURE_LEGACY_API + /* + * DEPRECATED: 2.0. + * So that ESP/ASP can ignore "language=javascript" statements + */ + mprCreatePropertyValue(obj, "javascript", mprCreateIntegerVar(0)); +#endif + + /* + * Extension functions + */ + mprCreatePropertyValue(obj, "assert", + mprCreateCFunctionVar(assertProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "eval", + mprCreateCFunctionVar(evalScriptProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "exit", + mprCreateCFunctionVar(exitProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "refCount", + mprCreateCFunctionVar(refCountProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "print", + mprCreateCFunctionVar(printProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "println", + mprCreateCFunctionVar(printlnProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "printVars", + mprCreateCFunctionVar(printVarsProc,0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "trace", + mprCreateStringCFunctionVar(traceProc, 0, MPR_VAR_SCRIPT_HANDLE)); + + /* + * Constructors + */ + mprCreatePropertyValue(obj, "Array", + mprCreateCFunctionVar(arrayConsProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "Boolean", + mprCreateCFunctionVar(booleanConsProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "Object", + mprCreateCFunctionVar(objectConsProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "Number", + mprCreateCFunctionVar(numberConsProc, 0, MPR_VAR_SCRIPT_HANDLE)); + mprCreatePropertyValue(obj, "String", + mprCreateCFunctionVar(stringConsProc, 0, MPR_VAR_SCRIPT_HANDLE)); + + /* mprCreatePropertyValue(obj, "Date", + * mprCreateCFunctionVar(dateConsProc, 0, MPR_VAR_SCRIPT_HANDLE)); + * mprCreatePropertyValue(obj, "Regexp", + * mprCreateCFunctionVar(regexpConsProc, 0, MPR_VAR_SCRIPT_HANDLE)); + */ + + /* + * Can we use on var x = "string text"; + */ + return 0; +} + +/******************************************************************************/ + +#else +void ejsProcsDummy() {} + +/******************************************************************************/ +#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 + */ diff --git a/source4/web_server/ejs/miniMpr.c b/source4/web_server/ejs/miniMpr.c new file mode 100644 index 0000000000..38241e5c71 --- /dev/null +++ b/source4/web_server/ejs/miniMpr.c @@ -0,0 +1,512 @@ +/* + * @file miniMpr.cpp + * @brief Mini Mbedthis Portable Runtime (MPR) + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 + */ + +#include "web_server/ejs/miniMpr.h" + +/************************************ Code ************************************/ +#if !BLD_APPWEB +#if !BLD_GOAHEAD_WEBSERVER + +static TALLOC_CTX *mpr_ctx; + +void mprFreeAll(void) +{ + talloc_free(mpr_ctx); + mpr_ctx = NULL; +} + +void mprSetCtx(TALLOC_CTX *ctx) +{ + talloc_free(mpr_ctx); + mpr_ctx = talloc_new(ctx); +} + +void mprFree(void *ptr) +{ + talloc_free(ptr); +} + +void *mprMalloc(uint size) +{ + return talloc_size(mpr_ctx, size); +} + +/******************************************************************************/ + +void *mprRealloc(void *ptr, uint size) +{ + return talloc_realloc_size(mpr_ctx, ptr, size); +} + +/******************************************************************************/ + +char *mprStrdup(const char *str) +{ + if (str == 0) { + str = ""; + } + return talloc_strdup(mpr_ctx, str); +} + +/*****************************************************************************/ + +int mprAllocSprintf(char **msgbuf, int maxSize, const char *fmt, ...) +{ + va_list args; + char *buf; + int count; + + va_start(args, fmt); + buf = mprMalloc(maxSize + 1); + count = mtVsprintf(buf, maxSize, fmt, args); + *msgbuf = buf; + va_end(args); + return count; +} + +/*****************************************************************************/ + +int mprAllocVsprintf(char **msgbuf, int maxSize, const char *fmt, va_list args) +{ + char *buf; + int count; + + buf = mprMalloc(maxSize + 1); + count = mtVsprintf(buf, maxSize, fmt, args); + *msgbuf = buf; + return count; +} + + +/*****************************************************************************/ +/* + * Format a number as a string. FUTURE -- reverse args to be standard. + * ie. mprItoa(char *userBuf, int bufsize, int value); + */ + +char *mprItoa(int value, char *buf, int width) +{ + char numBuf[16]; + char *cp, *dp, *endp; + int negative; + + cp = &numBuf[sizeof(numBuf)]; + *--cp = '\0'; + + if (value < 0) { + negative = 1; + value = -value; + width--; + } else { + negative = 0; + } + + do { + *--cp = '0' + (value % 10); + value /= 10; + } while (value > 0); + + if (negative) { + *--cp = '-'; + } + + dp = buf; + endp = &buf[width]; + while (dp < endp && *cp) { + *dp++ = *cp++; + } + *dp++ = '\0'; + return buf; +} + +/*****************************************************************************/ + +void mprLog(int level, const char *fmt, ...) +{ + va_list args; + char *buf; + + if (DEBUGLVL(level)) { + va_start(args, fmt); + mprAllocVsprintf(&buf, MPR_MAX_STRING, fmt, args); + va_end(args); + DEBUG(level, ("mprLog: %s", buf)); + mprFree(buf); + } +} + +/*****************************************************************************/ + +void mprBreakpoint(const char *file, int line, const char *cond) +{ + /* + * Optionally break into the debugger here + */ + mprLog(0, "ASSERT at %s:%d, %s\n", file, line, cond); +} + +#endif /* !BLD_GOAHEAD_WEBSERVER */ +/*****************************************************************************/ +/* + * Create a general growable array structure + */ + +MprArray *mprCreateArray() +{ + MprArray *array; + int size; + + array = (MprArray*) mprMalloc(sizeof(MprArray)); + if (array == 0) { + return 0; + } + memset(array, 0, sizeof(MprArray)); + + size = MPR_ARRAY_INCR * sizeof(void*); + array->handles = (void**) mprMalloc(size); + if (array->handles == 0) { + mprFree(array); + return 0; + } + memset(array->handles, 0, size); + array->max = MPR_ARRAY_INCR; + array->used = 0; + return array; +} + +/*****************************************************************************/ +/* + * Dispose of the array. Callers responsibility to dispose of handle entries. + */ + +void mprDestroyArray(MprArray *array) +{ + mprAssert(array); + mprAssert(array->max >= 0); + mprAssert(array->used >= 0); + + mprFree(array->handles); + mprFree(array); +} + +/*****************************************************************************/ +/* + * Add an item to the array + */ + +int mprAddToArray(MprArray *array, void *item) +{ + int memsize, idx, len; + + mprAssert(array); + mprAssert(array->max >= 0); + mprAssert(array->used >= 0); + + if (array->used < array->max) { + idx = array->used++; + mprAssert(idx >= 0 && idx < array->max); + mprAssert(array->handles[idx] == 0); + array->handles[idx] = item; + return idx; + } + + for (idx = array->used; idx < array->max; idx++) { + if (array->handles[idx] == 0) { + array->used++; + mprAssert(array->handles[idx] == 0); + array->handles[idx] = item; + return idx; + } + } + + len = array->max + MPR_ARRAY_INCR; + memsize = len * sizeof(void*); + array->handles = (void**) mprRealloc((void*) array->handles, memsize); + if (array->handles == NULL) { + return -1; + } + memset(&array->handles[array->max], 0, sizeof(void*) * MPR_ARRAY_INCR); + array->max = len; + array->used++; + + mprAssert(idx >= 0 && idx < array->max); + mprAssert(array->handles[idx] == 0); + + array->handles[idx] = item; + return idx; +} + +/*****************************************************************************/ +/* + * Remove from the array + */ + +int mprRemoveFromArray(MprArray *array, int idx) +{ + mprAssert(array); + mprAssert(array->max > 0); + mprAssert(idx >= 0 && idx < array->max); + mprAssert(array->handles[idx] != 0); + mprAssert(array->used > 0); + + array->handles[idx] = 0; + return --array->used; +} + +/*****************************************************************************/ +/* + * Thread-safe wrapping of strtok. Note "str" is modifed as per strtok() + */ + +char *mprStrTok(char *str, const char *delim, char **tok) +{ + char *start, *end; + int i; + + start = str ? str : *tok; + + if (start == 0) { + return 0; + } + + i = strspn(start, delim); + start += i; + if (*start == '\0') { + *tok = 0; + return 0; + } + end = strpbrk(start, delim); + if (end) { + *end++ = '\0'; + i = strspn(end, delim); + end += i; + } + *tok = end; + return start; +} + +/*****************************************************************************/ + +static int mprCoreStrcat(int alloc, char **destp, int destMax, int existingLen, + const char *delim, const char *src, va_list args) +{ + va_list ap; + char *dest, *dp; + const char *str; + int sepLen, addBytes, required; + + mprAssert(destp); + mprAssert(destMax > 0); + mprAssert(src); + + dest = *destp; + sepLen = (delim) ? strlen(delim) : 0; + +#ifdef __va_copy + __va_copy(ap, args); +#else + ap = args; +#endif + addBytes = 0; + str = src; + while (str) { + addBytes += strlen(str) + sepLen; + str = va_arg(ap, const char*); + } + + if (existingLen > 0) { + addBytes += sepLen; + } + required = existingLen + addBytes + 1; + if (required >= destMax) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + + if (alloc) { + if (dest == 0) { + dest = (char*) mprMalloc(required); + } else { + dest = (char*) mprRealloc(dest, required); + } + } else { + dest = (char*) *destp; + } + + dp = &dest[existingLen]; + if (delim) { + strcpy(dp, delim); + dp += sepLen; + } + + if (addBytes > 0) { +#ifdef __va_copy + __va_copy(ap, args); +#else + ap = args; +#endif + str = src; + while (str) { + strcpy(dp, str); + dp += strlen(str); + str = va_arg(ap, char*); + if (delim && str) { + strcpy(dp, delim); + dp += sepLen; + } + } + } else if (dest == 0) { + dest = (char*) mprMalloc(1); + } + *dp = '\0'; + + *destp = dest; + mprAssert(dp < &dest[required]); + return required - 1; +} + +/*****************************************************************************/ + +int mprReallocStrcat(char **destp, int destMax, int existingLen, + const char *delim, const char *src,...) +{ + va_list ap; + int rc; + + va_start(ap, src); + rc = mprCoreStrcat(1, destp, destMax, existingLen, delim, src, ap); + va_end(ap); + return rc; +} + +/*****************************************************************************/ +/* + * Return the directory portion of a pathname into the users buffer. + */ + +int mprGetDirName(char *buf, int bufsize, char *path) +{ + char *cp; + int dlen; + + mprAssert(path); + mprAssert(buf); + mprAssert(bufsize > 0); + + cp = strrchr(path, '/'); + if (cp == 0) { +#if WIN + cp = strrchr(path, '\\'); + if (cp == 0) +#endif + { + buf[0] = '\0'; + return 0; + } + } + + if (cp == path && cp[1] == '\0') { + strcpy(buf, "."); + return 0; + } + + dlen = cp - path; + if (dlen < bufsize) { + if (dlen == 0) { + dlen++; + } + mprMemcpy(buf, bufsize, path, dlen); + buf[dlen] = '\0'; + return 0; + } + return MPR_ERR_WONT_FIT; +} + +/*****************************************************************************/ + +int mprStrcpy(char *dest, int destMax, const char *src) +{ + int len; + + mprAssert(dest); + mprAssert(destMax > 0); + mprAssert(src); + + len = strlen(src); + if (len >= destMax && len > 0) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + if (len > 0) { + memcpy(dest, src, len); + dest[len] = '\0'; + } else { + *dest = '\0'; + len = 0; + } + return len; +} + +/*****************************************************************************/ + +int mprMemcpy(char *dest, int destMax, const char *src, int nbytes) +{ + mprAssert(dest); + mprAssert(destMax > nbytes); + mprAssert(src); + mprAssert(nbytes > 0); + + if (nbytes > destMax) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + if (nbytes > 0) { + memcpy(dest, src, nbytes); + return nbytes; + } else { + return 0; + } +} + +/*****************************************************************************/ +#else +void miniMprDummy() {} +#endif // !BLD_APPWEB && !BLD_GOAHEAD_WEBSERVER + +/* + * 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 + */ diff --git a/source4/web_server/ejs/miniMpr.h b/source4/web_server/ejs/miniMpr.h new file mode 100644 index 0000000000..28a990c3a2 --- /dev/null +++ b/source4/web_server/ejs/miniMpr.h @@ -0,0 +1,287 @@ +/* + * @file miniMpr.h + * @brief Mini Mbedthis Portable Runtime (MPR) Environment. + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 + */ +#ifndef _h_MINI_MPR +#define _h_MINI_MPR 1 + +/********************************** Includes **********************************/ +/* + * Find out about our configuration + */ + #include "includes.h" + +/* allow this library to use strcpy() */ +#undef strcpy + #include "config.h" + +#if BLD_APPWEB + /* + * If building within AppWeb, use the full MPR + */ + #include "mpr.h" +#else + + #include <ctype.h> + #include <fcntl.h> + #include <stdarg.h> + #include <stdlib.h> + #include <stdio.h> + #include <string.h> + #include <sys/stat.h> + +#if !WIN + #include <unistd.h> +#endif + +#if CE + #include <io.h> + #include "CE/wincompat.h" +#endif + +#if LYNX + #include <unistd.h> +#endif + +#if QNX4 + #include <dirent.h> +#endif + +/********************************** Defines ***********************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if BLD_FEATURE_SQUEEZE +/// +/// Reasonable length of a file path name to use in most cases where you know +/// the expected file name and it is certain to be less than this limit. +/// +#define MPR_MAX_FNAME 128 +#define MPR_MAX_STRING 512 +#define MPR_DEFAULT_HASH_SIZE 23 // Default size of hash table index +#define MPR_MAX_HEAP_SIZE (32 * 1024) +#else +#define MPR_MAX_FNAME 256 +#define MPR_MAX_STRING 4096 +#define MPR_DEFAULT_HASH_SIZE 43 // Default size of hash table index +#define MPR_MAX_HEAP_SIZE (64 * 1024) +#endif + +/* + * Useful for debugging + */ +#define MPR_L __FILE__, __LINE__ + +#if BLD_FEATURE_ASSERT +#define mprAssert(C) \ + if (C) ; else mprBreakpoint(__FILE__, __LINE__, #C) +#else + #define mprAssert(C) if (1) ; else +#endif + +/// +/// Standard MPR return and error codes +/// +#define MPR_ERR_BASE (-200) ///< Error code +#define MPR_ERR_GENERAL (MPR_ERR_BASE - 1) ///< Error code +#define MPR_ERR_ABORTED (MPR_ERR_BASE - 2) ///< Error code +#define MPR_ERR_ALREADY_EXISTS (MPR_ERR_BASE - 3) ///< Error code +#define MPR_ERR_BAD_ARGS (MPR_ERR_BASE - 4) ///< Error code +#define MPR_ERR_BAD_FORMAT (MPR_ERR_BASE - 5) ///< Error code +#define MPR_ERR_BAD_HANDLE (MPR_ERR_BASE - 6) ///< Error code +#define MPR_ERR_BAD_STATE (MPR_ERR_BASE - 7) ///< Error code +#define MPR_ERR_BAD_SYNTAX (MPR_ERR_BASE - 8) ///< Error code +#define MPR_ERR_BAD_TYPE (MPR_ERR_BASE - 9) ///< Error code +#define MPR_ERR_BAD_VALUE (MPR_ERR_BASE - 10) ///< Error code +#define MPR_ERR_BUSY (MPR_ERR_BASE - 11) ///< Error code +#define MPR_ERR_CANT_ACCESS (MPR_ERR_BASE - 12) ///< Error code +#define MPR_ERR_CANT_COMPLETE (MPR_ERR_BASE - 13) ///< Error code +#define MPR_ERR_CANT_CREATE (MPR_ERR_BASE - 14) ///< Error code +#define MPR_ERR_CANT_INITIALIZE (MPR_ERR_BASE - 15) ///< Error code +#define MPR_ERR_CANT_OPEN (MPR_ERR_BASE - 16) ///< Error code +#define MPR_ERR_CANT_READ (MPR_ERR_BASE - 17) ///< Error code +#define MPR_ERR_CANT_WRITE (MPR_ERR_BASE - 18) ///< Error code +#define MPR_ERR_DELETED (MPR_ERR_BASE - 19) ///< Error code +#define MPR_ERR_NETWORK (MPR_ERR_BASE - 20) ///< Error code +#define MPR_ERR_NOT_FOUND (MPR_ERR_BASE - 21) ///< Error code +#define MPR_ERR_NOT_INITIALIZED (MPR_ERR_BASE - 22) ///< Error code +#define MPR_ERR_NOT_READY (MPR_ERR_BASE - 23) ///< Error code +#define MPR_ERR_READ_ONLY (MPR_ERR_BASE - 24) ///< Error code +#define MPR_ERR_TIMEOUT (MPR_ERR_BASE - 25) ///< Error code +#define MPR_ERR_TOO_MANY (MPR_ERR_BASE - 26) ///< Error code +#define MPR_ERR_WONT_FIT (MPR_ERR_BASE - 27) ///< Error code +#define MPR_ERR_WOULD_BLOCK (MPR_ERR_BASE - 28) ///< Error code +#define MPR_ERR_CANT_ALLOCATE (MPR_ERR_BASE - 29) ///< Error code +#define MPR_ERR_MAX (MPR_ERR_BASE - 30) ///< Error code + +// +// Standard error severity and trace levels. These are ored with the error +// severities below. The MPR_LOG_MASK is used to extract the trace level +// from a flags word. We expect most apps to run with level 2 trace. +// +#define MPR_FATAL 0 ///< Fatal error. Cant continue. +#define MPR_ERROR 1 ///< Hard error +#define MPR_WARN 2 ///< Soft warning +#define MPR_CONFIG 2 ///< Essential configuration settings +#define MPR_INFO 3 ///< Informational only +#define MPR_DEBUG 4 ///< Debug information +#define MPR_VERBOSE 9 ///< Highest level of trace +#define MPR_LOG_MASK 0xf ///< Level mask + +// +// Error flags. Specify where the error should be sent to. Note that the +// product.xml setting "headless" will modify how errors are reported. +// Assert errors are trapped when in DEV mode. Otherwise ignored. +// +#define MPR_TRAP 0x10 ///< Assert error -- trap in debugger +#define MPR_LOG 0x20 ///< Log the error in the O/S event log +#define MPR_USER 0x40 ///< Display to the user +#define MPR_ALERT 0x80 ///< Send a management alert +#define MPR_TRACE 0x100 ///< Trace + +// +// Error format flags +// +#define MPR_RAW 0x200 // Raw trace output + +// +// Error line number information +// +#define MPR_L __FILE__, __LINE__ + +typedef char* MprStr; + +#ifndef __cplusplus +typedef unsigned char uchar; +typedef int bool; +#endif + +/* + * Porters: put other operating system type defines here + */ +#if WIN + typedef unsigned int uint; + typedef __int64 int64; + typedef unsigned __int64 uint64; +#else + #define O_BINARY 0 + typedef unsigned int uint; + __extension__ typedef long long int int64; + __extension__ typedef unsigned long long int uint64; +#endif + +/* + * Flexible array data type + */ +typedef struct { + int max; /* Size of the handles array */ + int used; /* Count of used entries in handles */ + void **handles; +} MprArray; + +#if BLD_FEATURE_SQUEEZE +#define MPR_ARRAY_INCR 8 +#else +#define MPR_ARRAY_INCR 16 +#endif + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +/********************************* Prototypes *********************************/ +/* + * If running in the GoAhead WebServer, map some MPR routines to WebServer + * equivalents. + */ + +#if BLD_GOAHEAD_WEBSERVER +#include "uemf.h" +#define mprMalloc(size) balloc(B_L, size) +#define mprFree(ptr) bfreeSafe(B_L, ptr) +#define mprRealloc(ptr, size) brealloc(B_L, ptr, size) +#define mprStrdup(ptr) bstrdup(B_L, ptr) +#define mprAllocSprintf fmtAlloc +#define mprAllocVsprintf fmtValloc +#define mprSprintf fmtStatic +#define mprItoa stritoa +#define mprLog trace +#define mprBreakpoint(file, line, cond) \ + error(file, line, E_BLD_FEATURE_ASSERT, T("%s"), cond) + +#else /* !BLD_GOAHEAD_WEBSERVER */ +//#define mprMalloc malloc +#define mprSprintf snprintf +#define mtVsprintf vsnprintf +extern void *mprMalloc(uint size); +extern void *mprRealloc(void *ptr, uint size); +extern void mprFree(void *ptr); +extern char *mprStrdup(const char *str); +extern int mprAllocVsprintf(char **msgbuf, int maxSize, const char *fmt, + va_list args); +extern int mprAllocSprintf(char **msgbuf, int maxSize, const char *fmt, ...); +extern char *mprItoa(int num, char *buf, int width); +extern void mprLog(int level, const char *fmt, ...); +extern void mprBreakpoint(const char *file, int line, const char *msg); +#endif /* BLD_GOAHEAD_WEBSERVER */ + +extern MprArray *mprCreateArray(void); +extern void mprDestroyArray(MprArray *array); +extern int mprAddToArray(MprArray *array, void *item); +extern int mprRemoveFromArray(MprArray *array, int idx); +extern char *mprStrTok(char *str, const char *delim, char **tok); + +extern int mprGetDirName(char *buf, int bufsize, char *path); +extern int mprReallocStrcat(char **dest, int max, int existingLen, + const char *delim, const char *src, ...); +extern int mprStrcpy(char *dest, int destMax, const char *src); +extern int mprMemcpy(char *dest, int destMax, const char *src, int nbytes); + +extern void mprFreeAll(void); +extern void mprSetCtx(TALLOC_CTX *ctx); + +#ifdef __cplusplus +} +#endif +#endif /* !BLD_APPWEB */ +#endif /* _h_MINI_MPR */ + +/*****************************************************************************/ + +/* + * 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 + */ diff --git a/source4/web_server/ejs/mpr.h b/source4/web_server/ejs/mpr.h new file mode 100644 index 0000000000..848518befa --- /dev/null +++ b/source4/web_server/ejs/mpr.h @@ -0,0 +1,2259 @@ +/// +/// @file mpr.h +/// @brief Header for the Mbedthis Portable Runtime (MPR) +// @copy default +// +// Copyright (c) Mbedthis Software LLC, 2003-2005. 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 +//////////////////////////////// Documentation ///////////////////////////////// +// +// See mpr.dox for additional documentation. +// +//////////////////////////////////////////////////////////////////////////////// + +#error foo + +#ifndef _h_MPR +#define _h_MPR 1 +/////////////////////////////////// Includes /////////////////////////////////// + +#include "web_server/ejs/config.h" +#include "web_server/ejs/mprOs.h" + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////// C API //////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" { +#else +typedef int bool; +#endif + +extern void mprBreakpoint(const char *file, int line, const char *msg); + +#if BLD_FEATURE_ASSERT +#define mprAssert(C) \ + if (C) ; else mprError(MPR_L, MPR_TRAP, "%s", #C) +#define inlineAssert(C) \ + if (C) ; else mprBreakpoint(MPR_L, #C) +#else + #define mprAssert(C) if (1) ; else + #define inlineAssert(C) if (1) ; else +#endif + +/////////////////////////////////// Constants ////////////////////////////////// + +#if BLD_FEATURE_SQUEEZE +/// +/// Maximum length of a file path name. Reduced from the system maximum to +/// save memory space. +/// +#define MPR_MAX_PATH 256 ///< Reasonable max path name +#else +#define MPR_MAX_PATH 1024 +#endif + +#if BLD_FEATURE_SQUEEZE +/// +/// Reasonable length of a file name used by the product. Use where you know +/// the expected file name and it is certain to be less than this limit. +/// +#define MPR_MAX_FNAME 128 ///< Reasonable size of a file name +#define MPR_DEFAULT_ALLOC 64 // Default small alloc size +#define MPR_DEFAULT_HASH_SIZE 23 // Default size of hash table index +#define MPR_DEFAULT_STACK (32 * 1024) // Default stack size +#else +#define MPR_MAX_FNAME 256 +#define MPR_DEFAULT_ALLOC 128 // Default small alloc size +#define MPR_DEFAULT_HASH_SIZE 43 // Default size of hash table index +#define MPR_DEFAULT_STACK (64 * 1024) // Default stack size +#endif + +#if BLD_FEATURE_SQUEEZE +#define MPR_MAX_ARGC 32 // Reasonable max of args +#define MPR_MAX_STRING 512 // Maximum string size +#define MPR_MAX_LOG_STRING 512 // Maximum log message +#define MPR_MAX_URL 256 ///< Reasonable size of a URL +#define MPR_BUFSIZE 512 ///< Reasonable size for buffers +// FUTURE -- could be named better +#define MPR_MAX_HEAP_SIZE (32 * 1024) // Maximum heap allocation size +#else +#define MPR_MAX_ARGC 128 // Reasonable max of args +#define MPR_MAX_STRING 4096 ///< Maximum string size +#define MPR_MAX_LOG_STRING 8192 // Maximum log message +#define MPR_MAX_URL 1024 ///< Reasonable size of a URL +#define MPR_BUFSIZE 1024 // Reasonable size for buffers +#define MPR_MAX_HEAP_SIZE (64 * 1024) // Maximum heap allocation size +#endif + +#define MPR_DEFAULT_BREAK_PORT 9473 +#define MPR_TIMEOUT_LOG_STAMP 3600000 // Time between log time stamps +#define MPR_TIMEOUT_PRUNER 60000 // Time between pruner runs +#define MPR_TIMEOUT_STOP_TASK 10000 // Timeout to stop running tasks +#define MPR_TIMEOUT_STOP_THREAD 10000 // Timeout to stop running threads +#define MPR_TIMEOUT_CMD_WAIT 50 // Poll for cmd processes +#define MPR_TIMEOUT_STOP 5000 // Wait when stopping resources +#define MPR_NUM_DIALOG 5 // Maximum number of user dialogs +#define MPR_MAX_LOG_SIZE 5 // Default size of a log file (MB) +#define MPR_MAX_IP_NAME 128 // Max size of an IP name +#define MPR_MAX_IP_ADDR 16 // Max size of an IP string addr +#define MPR_MAX_IP_ADDR_PORT 32 // Max size of IP with port + +#define MPR_TEST_TIMEOUT 10000 // Ten seconds +#define MPR_TEST_LONG_TIMEOUT 300000 // 5 minutes +#define MPR_TEST_SHORT_TIMEOUT 200 // 1/5 sec +#define MPR_TEST_NAP 50 // When we must not block + +#if BLD_FEATURE_MULTITHREAD +#define MPR_DEFAULT_MIN_THREADS 0 // Default min threads (0) +#define MPR_DEFAULT_MAX_THREADS 10 // Default max threads (10) +#else +#define MPR_DEFAULT_MIN_THREADS 0 // Default min threads +#define MPR_DEFAULT_MAX_THREADS 0 // Default max threads +#endif + +// +// MPR priorities (0 to 99) +// +#define MPR_BACKGROUND_PRIORITY 15 // May only get CPU if idle +#define MPR_LOW_PRIORITY 25 +#define MPR_NORMAL_PRIORITY 50 +#define MPR_HIGH_PRIORITY 75 +#define MPR_CRITICAL_PRIORITY 99 // May not yield + +#define MPR_SELECT_PRIORITY 75 // Run select at higher priority +#define MPR_POOL_PRIORITY 50 // Normal + +// +// Debug control +// +#define MPR_MAX_BLOCKED_LOCKS 100 // Max threads blocked on lock +#define MPR_MAX_RECURSION 15 // Max recursion with one thread +#define MPR_MAX_LOCKS 512 // Total lock count max +#define MPR_MAX_LOCK_TIME (60 * 1000) // Time in millisecs to hold a lock + +// +// Service / daemon control +// +#define MPR_INSTALL_SERVICE 1 +#define MPR_UNINSTALL_SERVICE 2 +#define MPR_GO_SERVICE 3 +#define MPR_STOP_SERVICE 4 + +// +// Parameter values for serviceEvents(loopOnce) +// +#define MPR_LOOP_ONCE 1 +#define MPR_LOOP_FOREVER 0 + +// +// Select service flags +// +#define MPR_ASYNC_SELECT 0x1 // Using async select in windows +#define MPR_BREAK_REQUESTED 0x2 // Breakout of a select wait +#define MPR_WAITING_FOR_SELECT 0x4 // Waiting for select to complete + +///////////////////////////////// Error Codes ////////////////////////////////// + +// +// Standard MPR return and error codes +// + +/// Base error code +#define MPR_ERR_BASE (-200) +/// General error +#define MPR_ERR_GENERAL (MPR_ERR_BASE - 1) +/// Action aborted +#define MPR_ERR_ABORTED (MPR_ERR_BASE - 2) +/// Item already exists +#define MPR_ERR_ALREADY_EXISTS (MPR_ERR_BASE - 3) +/// Bad arguments or paramaeters +#define MPR_ERR_BAD_ARGS (MPR_ERR_BASE - 4) +/// Bad input format +#define MPR_ERR_BAD_FORMAT (MPR_ERR_BASE - 5) +#define MPR_ERR_BAD_HANDLE (MPR_ERR_BASE - 6) +/// Module is in a bad state +#define MPR_ERR_BAD_STATE (MPR_ERR_BASE - 7) +/// Input has bad syntax +#define MPR_ERR_BAD_SYNTAX (MPR_ERR_BASE - 8) +#define MPR_ERR_BAD_TYPE (MPR_ERR_BASE - 9) +#define MPR_ERR_BAD_VALUE (MPR_ERR_BASE - 10) +#define MPR_ERR_BUSY (MPR_ERR_BASE - 11) +/// Cant access the file or resource +#define MPR_ERR_CANT_ACCESS (MPR_ERR_BASE - 12) +#define MPR_ERR_CANT_COMPLETE (MPR_ERR_BASE - 13) +/// Cant create the file or resource +#define MPR_ERR_CANT_CREATE (MPR_ERR_BASE - 14) +#define MPR_ERR_CANT_INITIALIZE (MPR_ERR_BASE - 15) +/// Cant open the file or resource +#define MPR_ERR_CANT_OPEN (MPR_ERR_BASE - 16) +/// Cant read from the file or resource +#define MPR_ERR_CANT_READ (MPR_ERR_BASE - 17) +/// Cant write to the file or resource +#define MPR_ERR_CANT_WRITE (MPR_ERR_BASE - 18) +#define MPR_ERR_DELETED (MPR_ERR_BASE - 19) +#define MPR_ERR_NETWORK (MPR_ERR_BASE - 20) +#define MPR_ERR_NOT_FOUND (MPR_ERR_BASE - 21) +/// Module or resource is not initialized +#define MPR_ERR_NOT_INITIALIZED (MPR_ERR_BASE - 22) +#define MPR_ERR_NOT_READY (MPR_ERR_BASE - 23) +#define MPR_ERR_READ_ONLY (MPR_ERR_BASE - 24) +/// The operation timed out +#define MPR_ERR_NOT_INITIALIZED (MPR_ERR_BASE - 22) +#define MPR_ERR_TIMEOUT (MPR_ERR_BASE - 25) +#define MPR_ERR_TOO_MANY (MPR_ERR_BASE - 26) +#define MPR_ERR_WONT_FIT (MPR_ERR_BASE - 27) +#define MPR_ERR_WOULD_BLOCK (MPR_ERR_BASE - 28) +#define MPR_ERR_CANT_ALLOCATE (MPR_ERR_BASE - 29) +#define MPR_ERR_MAX (MPR_ERR_BASE - 30) + +// +// Standard error severity and trace levels. These are ored with the error +// severities below. The MPR_LOG_MASK is used to extract the trace level +// from a flags word. We expect most apps to run with level 2 trace. +// +#define MPR_FATAL 0 ///< Fatal error trace level. Cant continue. +#define MPR_ERROR 1 ///< Hard error trace level +#define MPR_WARN 2 ///< Soft warning trace level +#define MPR_CONFIG 2 ///< Configuration settings trace level. +#define MPR_INFO 3 ///< Informational trace only +#define MPR_DEBUG 4 ///< Debug information trace level +#define MPR_VERBOSE 9 ///< Highest level of trace +#define MPR_LOG_MASK 0xf ///< Level mask + +// +// Error flags. Specify where the error should be sent to. Note that the +// product.xml setting "headless" will modify how errors are reported. +// Assert errors are trapped when in DEV mode. Otherwise ignored. +// +#define MPR_TRAP 0x10 // Assert mprError flags -- trap in debugger +#define MPR_LOG 0x20 ///< Log to the O/S event log mprError flag +#define MPR_USER 0x40 ///< Display to the user mprError flag +#define MPR_ALERT 0x80 // Send a management alert +#define MPR_TRACE 0x100 // Trace + +// +// Error format flags +// +#define MPR_RAW 0x200 // Raw trace output + +// +// Error line number information +// +#define MPR_L __FILE__, __LINE__ + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Function Remappings //////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Unsafe functions that should not be used. Define UNSAFE_STRINGS_OK before +// including mpr.h if you really want to use these functions. A better approach +// is to undefine them just prior to using them in your C/C++ source file. +// +#if BLD_FEATURE_SAFE_STRINGS + +#if BLD_FEATURE_PHP4_MODULE || BLD_FEATURE_PHP5_MODULE +#ifndef UNSAFE_FUNCTIONS_OK +#define UNSAFE_FUNCTIONS_OK 1 +#endif +#endif + +#ifndef UNSAFE_FUNCTIONS_OK +#define sprintf UseMprSprintfInstead +#define printf UseMprPrintfInstead +#define fprintf UseMprFprintfInstead +#define vsprintf UseMprVsprintfInstead +#define strtok UseMprStrTokInstead +#define gethostbyname UseMprGetHostByNameInstead +#define ctime UseMprCtimeInstead +#define asctime UseMprAsctimeInstead +#define localtime UseMprLocaltimeInstead +#define gmtime UseMprGmtimeInstead +#define malloc UseMprMallocInstead +#define free UseMprFreeInstead +#define realloc UseMprReallocInstead +#define calloc UseMprCallocInstead +#define strncpy UseMprStrcpyInstead +#define inet_ntoa UseMprInetNtoaInstead + +// +// FUTURE -- to the whole way +// +//#define strlen UseMprStrlenInstead +//#define strcpy UseMprStrcpyInstead + +#endif // UNSAFE_FUNCTIONS_OK +#endif // BLD_FEATURE_SAFE_STRINGS + +// +// File information structure +// +struct MprFileInfo { + uint size; // File length + uint mtime; // Modified time + uint inode; // Inode number + bool isDir; // Set if directory + bool isReg; // Set if a regular file +}; + +// +// Mpr time structure. Used for mprGetTime(). Matches requirements of select(). +// +struct MprTime { + long sec; // Seconds + long usec; // Microseconds (NOT milliseconds) +}; +typedef struct MprTime MprTime; + +#ifdef __cplusplus +} // extern "C" +#endif + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// C++ APIs /////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +#if __cplusplus + +class MprBuf; +class MprFile; +class MprFileSystem; +class MprHashTable; +class MprLink; +class MprList; +class MprPoolService; +class MprPoolThread; +class MprSelectService; +class MprSelectHandler; +class MprSocket; +class MprSocketService; +class MprStringData; +class MprStringList; +class MprTask; +class MprTimerService; +class MprTimer; + +#if BLD_FEATURE_LOG +class MprLogModule; +class MprLogService; +class MprLogListener; +class MprLogToFile; +class MprLogToWindow; +#endif + +#if BLD_FEATURE_CGI_MODULE +class MprCmd; +class MprCmdFiles; +class MprCmdService; +#endif + +#if BLD_FEATURE_MULTITHREAD +class MprCond; +class MprMutex; +class MprThreadService; +class MprThread; +#else +typedef void *MprMutex; +#endif + +#if BLD_FEATURE_RUN_AS_SERVICE && WIN +class MprWinService; +#endif + +/////////////////////////////////// MprLink //////////////////////////////////// + +// +// This define is used by classes that include a Link field member, to get +// the base class pointer +// +#define MPR_GET_BASE(type, ptr, field) \ + ((type) ((int) ptr - ((int) &(((type) 1)->field) - (int) ((type) 1)))) + +// +// The MprLink class enables subclassed objects to be inserted in a MprList. It +// provides forward and back links for fast insertion, removal and iteration. +// To use MprLink, subclasses must inherit MprLink as a base class. Use +// MprList for the dummy list header and MprLink for the list members. +// This class is NOT thread-safe. Callers must do their own thread +// synchronization. It is designed to be "inline", very fast and no-frills. +// + +class MprLink { + // FUTURE -- revert to protected + public: + MprLink *next; // Ptr to next member in list + MprLink *prev; // Ptr to prev member in list + MprList *head; // Ptr to the list head + + public: + MprLink() { ///< Constructor + next = prev = this; + head = 0; + }; + ~MprLink() {}; ///< Destructor + MprList *getList() { return head; };///< Return the owning list + + inline void insertAfter(MprLink *item); ///< Insert after this member + inline void insertPrior(MprLink *item); ///< Insert prior to this member +}; + +/////////////////////////////////// MprList //////////////////////////////////// +// +// The MprList class defines the list (dummy) header for doubly linked objects. +// It provides forward and back links for fast insertion, removal and +// iteration. To use MprLink, subclasses must inherit MprLink as a base class. +// Use MprList for the dummy list header and MprLink for the list members. +// This class is NOT thread-safe. Callers must do their own thread +// synchronization. It is designed to be "inline", very fast and no-frills. +// + +class MprList : public MprLink { + protected: + int numItems; // Number of items in the list + + public: + MprList() { + numItems = 0; + head = this; + next = prev = this; + }; + ~MprList() { + }; + inline void insert(MprLink *item) { /// Add to the end of the list + inlineAssert(item->head == 0); + if (item->head == 0) { + numItems++; + } + item->head = this; + item->next = head; + item->prev = head->prev; + prev->next = item; + prev = item; + }; + inline MprLink *remove(MprLink *item) { /// Remove this item + inlineAssert(item->head == this); + item->next->prev = item->prev; + item->prev->next = item->next; + item->prev = 0; + item->next = 0; + inlineAssert(numItems > 0); + if (item->head == head) { + numItems--; + } + item->head = 0; + return next; + }; + inline MprLink *getFirst() { /// Get first item + return (next == head) ? 0 : next; + }; + inline MprLink *getLast() { /// Get last item + return (next == head) ? 0 : prev; + }; + inline MprLink *getNext(MprLink *item) { /// Get next item + inlineAssert(item->head == this); + return (item->next == head) ? 0 : item->next; + }; + inline MprLink *getPrev(MprLink *item) { /// Get previous item + inlineAssert(item->head == this); + return (item->prev == head) ? 0 : item->prev; + }; + int getNumItems() { /// Get number of items. + return numItems; + }; + bool emptyOk() { + if ((head == this) && (next == head) && + (prev == head) && (numItems == 0)) { + return 1; + } else { + return 0; + } + }; + + friend class MprLink; +}; + +// +// Inline methods for MprLink +// +inline void MprLink::insertAfter(MprLink *item) { + inlineAssert(item->head == 0); + item->head = head; + next->prev = item; + item->prev = this; + item->next = next; + next = item; + head->numItems++; + }; +inline void MprLink::insertPrior(MprLink *item) { + inlineAssert(item->head == 0); + item->head = head; + prev->next = item; + item->next = this; + item->prev = prev; + prev = item; + head->numItems++; + }; + +//////////////////////////////// MprStringList ///////////////////////////////// + +class MprStringData : public MprLink { + public: + MprStr string; + + public: + MprStringData(char *s); + ~MprStringData(); + inline char *getValue() { return string; }; +}; + +class MprStringList : public MprList { + public: + MprStringList(char *str); + MprStringList(); + ~MprStringList(); + void insert(char *str); + void parse(char *str); +}; + +////////////////////////////////// MprCmdLine ////////////////////////////////// + +class MprCmdLine { + private: + int argc; + char **argv; // Not duped + void *argvBuf; // Storage for argv and args + bool inSwitch; + int optc; + int optind; + char *switches; // Not duped + + public: + MprCmdLine(int argc, char **argv, char *switches); + MprCmdLine(char *args, char *switches); + ~MprCmdLine(); + char **getArgv() { return argv; }; + int getArgc() { return argc; }; + int next(char **argp = 0); + int firstArg(); +}; + +/////////////////////////////////// MprCond //////////////////////////////////// +#if BLD_FEATURE_MULTITHREAD + +// +// Condition variable for multi-thread synchronization +// +// Condition variables can be used to coordinate threads when running in a +// multi-threaded mode. These condition variables are level triggered in that +// a condition can be signalled prior to another thread waiting. That thread +// will then not block if it calls waitForCond(). +// + +class MprCond { + private: + #if BLD_DEBUG + // + // This must be the first item in the class + // + MprLink link; // Cond-var leak monitoring + #endif + #if LINUX || MACOSX || SOLARIS + pthread_cond_t + cv; // Pthreads condition variable + #endif + #if WIN + HANDLE cv; // Windows event handle + int numWaiting; // Number waiting to be signalled + #endif + #if VXWORKS + SEM_ID cv; // Condition variable + #endif + MprMutex *mutex; // Thread sync + int triggered; // Value of the condition + +#if UNUSED + int wakeAll; // Wake all waiters on signalCond() +#endif + + public: + MprCond(); + ~MprCond(); + int waitForCond(long timeout = -1); + void signalCond(); + +#if UNUSED + int multiWait(MprMutex *externalMutex, long timeout = -1); + void reset(); + void signalAll(); +#endif +}; + +#endif // BLD_FEATURE_MULTITHREAD +/////////////////////////////////// MprDebug /////////////////////////////////// +#if BLD_DEBUG + +class MprDebug { + public: +#if BLD_FEATURE_MULTITHREAD + int getMutexNum(); +#endif +}; + +#endif +/////////////////////////////////// MprFile //////////////////////////////////// + +class MprFile { + private: + MprBuf *inBuf; + int fd; + public: + MprFile(); + virtual ~MprFile(); + virtual int open(char* path, int flags, int mode); + virtual void close(); + virtual char *gets(char *buf, int len); + virtual int read(void* buf, int len); + virtual int write(void* buf, int len); + virtual long lseek(long offset, int origin); +}; + +//////////////////////////////// MprFileSystem ///////////////////////////////// + +class MprFileSystem { + private: + public: + MprFileSystem(); + virtual ~MprFileSystem(); + virtual MprFile *newFile(); + virtual int stat(char* path, MprFileInfo* info); + virtual bool isDir(char* path); + virtual void setRoot(char* path); +}; + +/////////////////////////////////// MprMutex /////////////////////////////////// +#if BLD_FEATURE_MULTITHREAD + +// +// Mutual exclusion locks +// +class MprMutex { + public: + #if BLD_DEBUG + MprLink link; // Must be the first in the class + #endif + #if WIN + CRITICAL_SECTION cs; // O/S critical section + #endif + #if LINUX || MACOSX || SOLARIS + pthread_mutex_t cs; // O/S critical section + #endif + #if VXWORKS + SEM_ID cs; // Semaphore + #endif + + public: + MprMutex(); + ~MprMutex(); + int tryLock(); + + // FUTURE -- should do Windows inline also. + + #if LINUX || MACOSX || SOLARIS + inline void lock() { pthread_mutex_lock(&cs); }; + inline void unlock() { pthread_mutex_unlock(&cs); }; + #elif VXWORKS && 0 + inline void lock() { semTake(cs, WAIT_FOREVER); }; + inline void unlock() { semGive(cs); }; + #else + void lock(); + void unlock(); + #endif + + friend class MprCond; +}; + +#endif // BLD_FEATURE_MULTITHREAD +////////////////////////////////////// Mpr ///////////////////////////////////// +// +// Mpr flags +// +#define MPR_EXITING 0x1 // Mpr is exiting +#define MPR_ASYNC_SELECTING 0x2 // Using async select +#define MPR_HEADLESS 0x4 // No user interface +#define MPR_SERVICE_THREAD 0x10 // Using a service thread for events +#define MPR_IS_DAEMON 0x20 // Is a daemon / service +#define MPR_STOPPED 0x40 // Mpr services stopped +#define MPR_STARTED 0x80 // Mpr services started +#define MPR_KILLABLE 0x100 // MPR can be killed (need a pid file) +#define MPR_USER_START_FLAGS (MPR_SERVICE_THREAD | MPR_KILLABLE) + +typedef int (*MprDllEntryProc)(void *arg); + +// +// The Mbedthis Portable Runtime (MPR) internal state class. +// +class Mpr { + private: + MprStr appName; // One word name of the application + MprStr appTitle; // Displayable name of the product + int buildNumber; // Build number + MprStr buildType; // Build type + MprHashTable *configSettings; // Configuration settings for app + MprStr cpu; // Procesor + MprStr domainName; // Domain portion + MprStr hostName; // Host name (fully qualified name) + MprStr installDir; // Installation directory (!= cwd) + MprStr os; // Operating system + MprStr serverName; // Server name portion (no domain) + MprStr version; // Application version number (x.y.z) + + // + // FUTURE -- Convert to flags + // + int flags; // Processing state + int headless; // Run headless + bool runAsService; // Running as a service (daemon) + bool eventsThread; // Running an events thread + +#if WIN + long appInstance; // Application instance (windows) + HWND hwnd; // Window handle +#endif + +#if BLD_FEATURE_LOG + MprLogModule *defaultLog; // Default log module +#endif + +#if BLD_FEATURE_MULTITHREAD + MprMutex *mutex; // Thread synchronization + MprMutex *timeMutex; // Thread sync for mprGetTime + MprMutex *eventsMutex; // Thread sync for serviceEvents +#endif + + public: +#if BLD_FEATURE_LOG + MprLogService *logService; // Log service object +#endif + MprPoolService *poolService; // Pool service object + MprSelectService *selectService; // Select service object + MprSocketService *socketService; // MprSocket service object + MprTimerService *timerService; // Timer service object + MprList modules; // List of modules + +#if BLD_FEATURE_CGI_MODULE + MprCmdService *cmdService; // Run command service object +#endif +#if BLD_FEATURE_MULTITHREAD + MprThreadService *threadService; // Thread service object +#endif + + public: + // + // Published API + // + Mpr(char *appName); + ~Mpr(); +#if BLD_FEATURE_LOG + void addListener(MprLogListener *lp); +#endif + bool getAsyncSelectMode(); + int getIdleTime(); + bool isExiting(void); + int runTasks(); + int runTimers(); + void serviceEvents(bool loopOnce, int maxTimeout); + void setAsyncSelectMode(bool on); + int setLogSpec(char *spec); + int start(int startFlags = 0); + int stop(bool immediateStop); + void terminate(bool graceful = 1); +#if WIN + void setHwnd(HWND appHwnd); + void setSocketHwnd(HWND socketHwnd); + void setSocketMessage(int msgId); +#endif + + // + // Unpublished API + // + + int configure(char *configFile); + char *getAppName(void); + char *getAppTitle(void); + int getBuildNumber(void); + char *getBuildType(void); + char *getCpu(void); + char *getDomainName(void); + int getFds(fd_set *readInterest, fd_set *writeInterest, + fd_set *exceptInterest, int *maxFd, int *lastGet); + int getHeadless(void); + char *getHostName(void); + char *getInstallDir(void); + char *getOs(void); + char *getServerName(void); + char *getVersion(void); + bool isRunningEventsThread() { return eventsThread; }; + bool isService(); + int killMpr(); + int makeDaemon(int parentExit); + void serviceIO(int readyFds, fd_set *readFds, fd_set *writeFds, + fd_set *exceptFds); + void serviceIO(int sockFd, int winMask); + void setAppName(char *name); + void setAppTitle(char *name); + void setBuildNumber(int buildNumber); + void setBuildType(char *type); + void setCpu(char *cpu); + void setDomainName(char *host); + void setHeadless(int headless); + void setHostName(char *host); + void setInstallDir(char *dir); + void setOs(char *os); + void setPriority(int pri); + void setServerName(char *host); + void setService(bool service); + void setVersion(char *version); + void setWebServerAddress(char *path); + void writeToOsLog(char *msg, int etype); + +#if BLD_FEATURE_MULTITHREAD + void startEventsThread(); + void setMinPoolThreads(int n); + void setMaxPoolThreads(int n); + int getMinPoolThreads(); + int getMaxPoolThreads(); + int getNextThreadNum(); + MprThread *getCurrentThread(); + inline void lock() { + if (mutex) { + mutex->lock(); + } + }; + inline void unlock() { + if (mutex) { + mutex->unlock(); + } + }; + void timeLock() { timeMutex->lock(); }; + void timeUnlock() { timeMutex->unlock(); }; +#else + int getMaxPoolThreads() { return 0; }; + inline void lock() {}; + inline void unlock() {}; + inline void timeLock() {}; + inline void timeUnlock() {}; +#endif + +#if WIN + HWND getHwnd(); + long getInst(); + void setInst(long inst); +#endif + +#if BLD_FEATURE_DLL + int loadDll(char *path, char *fnName, void *arg, void **handle); + void unloadDll(void *handle); +#endif + +#if BLD_FEATURE_XML_CONFIG + int openConfigFile(char *path); + int openXmlFile(char *path); + int readXmlInt(MprHashTable *symTab, char *key, int *value); + int readXmlStr(MprHashTable *symTab, char *key, char **value); +#endif + char *getConfigStr(char *key, char *defaultValue); + int getConfigInt(char *key, int defaultValue); + +#if BLD_FEATURE_CGI_MODULE + MprCmdService *getCmdService() { return cmdService; }; +#endif + + private: + int platformInitialize(); + int platformTerminate(); + int platformStart(int startFlags); + int platformStop(); +}; + +#if MPR_IN_MPR +extern Mpr *mpr; // Default global Mpr class +#endif + +extern Mpr *mprGetMpr(); + +//////////////////////////////// MprHashEntry ////////////////////////////////// +// +// Master hash entry type. Not thread safe. +// + +class MprHashEntry : public MprLink { + private: + MprStr key; + MprList *bucket; + + public: + MprHashEntry(); + MprHashEntry(char *key); + virtual ~MprHashEntry(); + char *getKey(); + void setKey(char *key); + friend class MprHashTable; +}; + +// +// String entry type +// +class MprStringHashEntry : public MprHashEntry { + private: + MprStr value; + public: + MprStringHashEntry(char *key, char *str); + virtual ~MprStringHashEntry(); + char *getValue() { return value; }; +}; + +// +// Static string (not duplicated) +// +class MprStaticHashEntry : public MprHashEntry { + private: + char *value; + public: + MprStaticHashEntry(char *key, char *str); + virtual ~MprStaticHashEntry(); + char *getValue() { return value; }; +}; + +#if PERHAPS +// +// Object string (not duplicated) +// +class MprObjectHashEntry : public MprHashEntry { + private: + char *value; + public: + MprObjectHashEntry(char *key, char *str); + virtual ~MprObjectHashEntry(); + char *getValue() { return value; }; +}; +#endif + +///////////////////////////////// MprHashTable ///////////////////////////////// + +class MprHashTable { + private: + MprList *buckets; + int size; + int count; + + public: + MprHashTable(int hash_size = MPR_DEFAULT_HASH_SIZE); + ~MprHashTable(); + MprHashEntry *getFirst(); + MprHashEntry *getNext(MprHashEntry *ep); + int getNumItems() { return count; }; + int insert(MprHashEntry *entry); + MprHashEntry *lookup(char *key); + int remove(char *key); + int remove(MprHashEntry *entry); + int removeAll(); + + private: + MprHashEntry *lookupInner(char *key, MprList **bucket); + int hashIndex(char *key); + int getNextPrime(int size); +}; + +//////////////////////////////// MprLogService ///////////////////////////////// +#if BLD_FEATURE_LOG + +class MprLogListener : public MprLink { + private: + int maxSize; // Max size of log + public: + // + // Published API + // + MprLogListener(); ///< Constructor + virtual ~MprLogListener(); ///< Destructor + virtual void logEvent(char *module, int flags, int level, char *thread, + char *msg); + virtual int start(); + + // + // Unpublished API + // + virtual void shuttingDown(); + virtual int setLogSpec(char *path, int maxSize); + virtual void stop(); +}; + + +class MprLogToFile : public MprLogListener { + private: + int logFd; // MprLogService file handle + MprStr logFileName; // Current name of log file + bool timeStamps; // Output high res time stamps + uint maxSize; // Maximum extent of trace file + int rotationCount; // Number of times logs rotated + MprTimer *timer; // Log file time stamp timer + public: + MprLogToFile(); ///< Constructor + ~MprLogToFile(); ///< Destructor + void logEvent(char *module, int flags, int level, char *thread, + char *msg); + void enableTimeStamps(bool on); + void logConfig(); + void rotate(); + void shuttingDown(); + int setLogSpec(char *path, int maxSize); + int start(); + void stop(); + void writeTimeStamp(); +}; + + +class MprLogToWindow : public MprLogListener { + private: + public: + MprLogToWindow(); ///< Constructor + ~MprLogToWindow(); ///< Destructor + void logEvent(char *module, int flags, int level, char *thread, + char *msg); +}; + + +// +// Overall logging service +// +class MprLogService { + private: + MprList listeners; // Users registered for output + uint defaultLevel; // Default level for modules + MprLogModule *defaultModule; // Default log module for this log + MprList moduleList; // List of modules to trace + char *moduleSpecs; // Module level spec string + char *logSpec; // Saved logging spec string + bool logging; // Logging is enabled +#if BLD_FEATURE_MULTITHREAD + MprMutex *mutex; // Mutex lock +#endif + + public: + MprLogService(); + ~MprLogService(void); + void addListener(MprLogListener *lp); + void insertModule(MprLogModule *module); + void error(char *file, int line, int flags, char *fmt, + va_list args); + MprLogModule *getDefaultModule() { return defaultModule; }; + int getDefaultLevel() { return defaultLevel; }; + char *getLogSpec() { return logSpec; }; + char *getModuleSpecs(); + bool isLogging(); + void removeListener(MprLogListener *lp); + void removeModule(MprLogModule *module); + void setDefaultLevel(int l); + void setDefaultModule(MprLogModule *m) { defaultModule = m; }; + int setLogSpec(char *fileSpec); + void shuttingDown(); + void start(); + void stop(); + void traceCore(int level, int flags, MprLogModule *module, + char *fmt, va_list ap); + void writeHeader(); + void writeLogStamp(); + +#if BLD_FEATURE_MULTITHREAD + void lock() { + if (mutex) { + mutex->lock(); + } + }; + void unlock() { + if (mutex) { + mutex->unlock(); + } + }; +#else + inline void lock() {}; + inline void unlock() {}; +#endif + + private: + void output(MprLogModule *module, int flags, int level, + char *msg); + void breakpoint(char *file, int line); +}; + +extern void mprLog(int level, MprLogModule *module, char *fmt, ...); +extern void mprLog(int level, int flags, MprLogModule *module, + char *fmt, ...); +extern void mprLog(char *fmt, ...); + +#else // !BLD_FEATURE_LOG +// +// If logging is not enabled, we inline these functions to nothing +// +class MprLogModule { + void *x; + public: + MprLogModule(char *name) {} +}; +inline void mprLog(int level, MprLogModule *module, char *fmt, ...) {}; +inline void mprLog(char *fmt, ...) {} +extern "C" void mprLog(int level, char *fmt, ...); +#endif + +///////////////////////////////// MprLogModule ///////////////////////////////// +#if BLD_FEATURE_LOG +// +// Class to describe a trace log module +// + +class MprLogModule : public MprLink { + private: + MprStr name; + int level; // Current trace output level + bool enabled; + + public: + MprLogModule(char *name); + ~MprLogModule(); + void innerMprLogModule(char *name); + int getLevel(void) { return level; }; + void disable() { enabled = 0; }; + void enable() { enabled = 1; }; + int getEnabled() { return enabled; }; + char *getName() { return name; }; + void setLevel(int l) { level = l; }; +}; + +#endif // BLD_FEATURE_LOG +//////////////////////////////////// MprBuf //////////////////////////////////// + +typedef int (*MprBufProc)(MprBuf* bp, void *arg); + +class MprBuf { + private: + uchar *buf; // Actual buffer for data + uchar *endbuf; // Pointer one past the end of buffer + uchar *start; // Pointer to next data char + uchar *end; // Pointer one past the last data char + int buflen; // Current size of buffer + int maxsize; // Max size the buffer can ever grow + int growBy; // Next growth increment to use + MprBufProc refillProc; // Function to refill the buffer + void *refillArg; // Arg to refill proc + + public: + MprBuf(); + MprBuf(int initialSize, int maxsize = -1); + ~MprBuf(); + inline void addNull() { + *((char*) end) = (char) '\0'; + }; + void adjustStart(int size); + void adjustEnd(int size); + void copyDown(); + inline void flush() { + start = buf; + end = buf; + }; + inline int get() { + if (start == end) { + return -1; + } + int c = (uchar) *start++; + if (start >= endbuf) { + start = buf; + } + return c; + }; + int get(uchar *buf, int len); + inline char *getBuf() { return (char*) buf; }; + inline char *getEnd() { return (char*) end; }; + inline char *getEndBuf() { return (char*) endbuf; }; + inline int getLength() { + return ((start > end) ? + (buflen + (end - start)) : (end - start)); + }; + inline int getLinearData() { + return min((endbuf - start), getLength()); + } + inline int getLinearSpace() { + int len = getLength(); + int space = buflen - len - 1; + return min((endbuf - end), space); + } + inline int getSpace() { + return buflen - getLength() - 1; + }; + inline char *getStart() { return (char*) start; }; + inline int getSize() { return buflen; }; + int insert(char c); + inline int look() { + if (start == end) { + return -1; + } + return* start; + } + int lookLast(); + int put(char c); + int put(char *str); + int putInt(int i); + int putFmt(char *fmt, ...); + int put(uchar *buf, int len); + inline int refill() { + return (refillProc) ? + (refillProc)(this, refillArg) : 0; + }; + inline void resetIfEmpty() { + if (getLength() == 0) { + flush(); + } + }; + void setBuf(uchar *userBuf, int size); + void setBuf(int initialSize, int maxSize); + void setRefillProc(MprBufProc fn, void *arg) { + refillProc = fn; + refillArg = arg; + }; + uchar *takeBuffer(); + private: + int grow(); +}; + +//////////////////////////////// MprCmdService ///////////////////////////////// +#if BLD_FEATURE_CGI_MODULE +// +// Flags for MprCmd +// +#define MPR_CMD_BACKGROUND 0x1 // Continue running if MPR exits +#define MPR_CMD_REAP_MAX 24 + +// +// Cmd service control +// + +class MprCmdService { + private: + MprList cmdList; // List of commands + MprMutex *mutex; // Multi-thread sync + ulong completedCmds[MPR_CMD_REAP_MAX]; + int exitStatus[MPR_CMD_REAP_MAX]; + MprTimer *timer; // Timer to poll for child completion + + public: + MprCmdService(); + ~MprCmdService(); + void cmdWatcher(); + void cmdWatcherTimer(MprTimer *tp); + void insertCmd(MprCmd* rp); + void removeCmd(MprCmd* rp); + int start(); + void startWatcher(); + int stop(); + +#if LINUX || SOLARIS || VXWORKS + void processStatus(int pid, int status); + void initSignals(); +#endif + +#if BLD_FEATURE_MULTITHREAD + void lock() { mutex->lock(); }; + void unlock() { mutex->unlock(); }; +#else + inline void lock() {}; + inline void unlock() {}; +#endif +}; + +//////////////////////////////////// MprCmd //////////////////////////////////// + +// +// Cmd procs must return the number of bytes read or -1 for errors. +// +typedef void (*MprCmdProc)(MprCmd* rp, void *data); + +#define MPR_CMD_EOF -1 // EOF return value + +// +// Flags +// +#define MPR_CMD_DETACHED 0x1 +#define MPR_CMD_NEW_SESSION 0x2 +#define MPR_CMD_CHDIR 0x4 +#define MPR_CMD_WAIT 0x8 +#define MPR_CMD_SHOW 0x10 +#define MPR_CMD_USER_FLAGS 0x1f + +#define MPR_CMD_COMPLETE 0x40 +#define MPR_CMD_DISPOSED 0x80 +#define MPR_CMD_STDIO_MADE 0x100 + +// +// Indicies for clientFd and serverFd +// +#define MPR_CMD_IN 0 // Stdin for the server +#define MPR_CMD_OUT 1 // Stdout for the server +#define MPR_CMD_MAX_FD 2 + +#define MPR_CMD_WATCHER_NAP 100 // Timeout for watcher while polling +#define MPR_CMD_WATCHER_TIMEOUT 5000 // Timeout for child to start + +class MprCmdFiles { + public: + MprStr name[MPR_CMD_MAX_FD]; + int fd[MPR_CMD_MAX_FD]; + int clientFd[MPR_CMD_MAX_FD]; + public: + MprCmdFiles(); + ~MprCmdFiles(); +}; + +// +// MprCmd class +// +class MprCmd : public MprLink { + private: + MprStr cwd; // Current working dir for the process + void *data; // Callback user data + int exitStatus; // Command exit status + int flags; // Control flags (userFlags not here) + MprCmdFiles files; // Stdin, stdout for the command + int inUse; // Used by dispose() + MprLogModule *log; + MprCmdProc cmdDoneProc; // Handler for client completion + +#if VXWORKS + MprSelectHandler + *handler; + int waitFd; // Pipe to await child exit +#endif + ulong process; // Id/handle of the created process + +#if BLD_FEATURE_MULTITHREAD +#if VXWORKS + MprCond *startCond; + MprCond *exitCond; +#endif + MprMutex *mutex; +#endif + + public: + MprCmd(); + ~MprCmd(); + void closeWriteFd(); + int dispose(); + int getExitCode(int *code = 0); + int getWriteFd(); + int getReadFd(); + void invokeCallback(MprMutex *callerMutex); + bool isRunning(); + int makeStdio(); + void setCwd(char *dir); + int setInuse(); + int start(char *cmd, int flags); + int start(char *cmd, char **argv, char **envp, + MprCmdProc completionProc, void *data, int flags); + void stop(); + + // + // Internal API + // + ulong getProcess() { return process; }; + void reset(); + void resetFiles(); + void setExitStatus(int status); + + private: + int waitForChild(int timeout); + +#if BLD_FEATURE_MULTITHREAD + void lock() { mutex->lock(); }; + void unlock() { mutex->unlock(); }; +#else + inline void lock() {}; + inline void unlock() {}; +#endif + + friend class MprCmdService; +}; + +#endif // BLD_FEATURE_CGI_MODULE +////////////////////////////// MprSelectService //////////////////////////////// +// +// Standard select bit mask options +// +#define MPR_READABLE 0x2 +#define MPR_WRITEABLE 0x4 +#define MPR_EXCEPTION 0x8 + +typedef void (*MprSelectProc)(void *data, int mask, int isMprPoolThread); + +class MprSelectService { + private: + struct sockaddr_in + breakAddress; // Breakout port socket address + int breakSock; // MprSocket to wakeup select service + int breakPort; // Port to talk to the select service + int breakRetries; // Retry attempts to open breakout port + MprList list; // List of select handlers + int flags; // State flags + int maskGeneration; // Generation number for mask changes + int listGeneration; // Generation number for list changes + MprLogModule *log; + int rebuildMasks; // Complete select mask rebuild required + int delayedFds[FD_SETSIZE]; + int maxDelayedFd; +#if WIN + int sockMessage; // MprSocket message for AsyncSelect + HWND hwnd; // Window handle to use for AsyncSelect +#endif + +#if BLD_FEATURE_MULTITHREAD + MprCond *cond; // Wait for select to breakout + MprMutex *mutex; // General multi-thread synchronization +#endif + + public: + // + // Published API + // + MprSelectService(); + ~MprSelectService(); + int getFds(fd_set *readInterest, fd_set *writeInterest, + fd_set *exceptInterest, int *maxFd, int *lastGet); + void serviceIO(int readyFds, fd_set *readFds, fd_set *writeFds, + fd_set *exceptFds); + void serviceIO(int sockFd, int winMask); + + // + // Unpublished API + // + int insertHandler(MprSelectHandler *sp); + void awaken(int wait = 0); + void delayedClose(int fd); + bool getAsyncSelectMode(); + int getFlags() { return flags; }; + int modifyHandler(MprSelectHandler *sp, bool wakeUp); + void removeHandler(MprSelectHandler *sp); + MprLogModule *getLog() { return log; }; + int openBreakoutPort(); + void setPort(int n); + int start(); + int stop(); + +#if BLD_FEATURE_MULTITHREAD + void lock() { mutex->lock(); }; + void unlock() { mutex->unlock(); }; +#else + inline void lock() {}; + inline void unlock() {}; +#endif + +#if WIN + HWND getHwnd() { return hwnd; }; + int getMessage() { return sockMessage; }; + void setHwnd(HWND h) { hwnd = (HWND) h; }; + void setMessage(int m) { sockMessage = m; }; + void setAsyncSelectMode(bool asyncSelect); +#endif +}; + +////////////////////////////// MprSelectHandler //////////////////////////////// +// +// Flags +// +#define MPR_SELECT_DISPOSED 0x1 +#define MPR_SELECT_RUNNING 0x2 +#define MPR_SELECT_CLOSEFD 0x4 +#define MPR_SELECT_CLIENT_CLOSED 0x8 // Client disconnection received + +class MprSelectHandler : public MprLink { + private: + int desiredMask; // Mask of desired events + int disableMask; // Mask of disabled events + int fd; // O/S File descriptor (sp->sock) + int flags; // Control flags + void *handlerData; // Argument to pass to proc. + int inUse; // Used by dispose() + MprLogModule *log; + int presentMask; // Mask of events that have been seen + int priority; // Priority if events handled by threads + +#if BLD_FEATURE_MULTITHREAD + MprCond *stoppingCond; // Synchronization when stopping +#endif + + public: + MprSelectService *selectService; // Select service pointer + MprSelectProc proc; // Select handler procedure + + public: + MprSelectHandler(int fd, int mask, MprSelectProc proc, + void *data, int priority); + bool dispose(); + void disableEvents(bool wakeUp); + void enableEvents(bool wakeUp); + int getFd() { return fd; }; + int getFlags() { return flags; }; + void runHandler(); + void selectProc(MprTask *tp); + void setInterest(int mask); + void setWinInterest(); + void setProc(MprSelectProc newProc, int mask); + void setCloseOnDispose(); + int stop(int timeout); + + private: + ~MprSelectHandler(); + + friend class MprSelectService; +}; + +//////////////////////////////// MprInterface ////////////////////////////////// + +class MprInterface : public MprLink { + public: + MprStr name; // Interface name + MprStr ipAddr; + MprStr broadcast; + MprStr mask; + public: + MprInterface(char *name, char *ipAddr, char *bcast, + char *mask); + ~MprInterface(); +}; + +////////////////////////////// MprSocketService //////////////////////////////// + +typedef void (*MprSocketIoProc)(void *data, MprSocket *sp, int mask, + int isMprPoolThread); +typedef void (*MprSocketAcceptProc)(void *data, MprSocket *sp, char *ip, + int port, MprSocket *lp, int isMprPoolThread); +// +// Mpr socket service class +// +class MprSocketService { + private: + MprList socketList; // List of all sockets + MprList ipList; // List of ip addresses + MprLogModule *log; + +#if BLD_FEATURE_MULTITHREAD + MprMutex *mutex; +#endif + + public: + MprSocketService(); + ~MprSocketService(); +#if BLD_FEATURE_LOG + MprLogModule *getLogModule() { return log; }; +#endif + MprList *getInterfaceList(); + void insertMprSocket(MprSocket *sp); + void removeMprSocket(MprSocket *sp); + int start(); + int stop(); + +#if BLD_FEATURE_MULTITHREAD + void lock() { mutex->lock(); }; + void unlock() { mutex->unlock(); }; +#else + inline void lock() {}; + inline void unlock() {}; +#endif + + private: + int getInterfaces(); +}; + +////////////////////////////////// MprSocket /////////////////////////////////// +// +// close flags +// +#define MPR_SHUTDOWN_READ 0 +#define MPR_SHUTDOWN_WRITE 1 +#define MPR_SHUTDOWN_BOTH 2 + +// +// Flags +// +#define MPR_SOCKET_BLOCK 0x1 // Use blocking I/O +#define MPR_SOCKET_BROADCAST 0x2 // Broadcast mode +#define MPR_SOCKET_CLOSED 0x4 // MprSocket has been closed +#define MPR_SOCKET_CONNECTING 0x8 // MprSocket has been closed +#define MPR_SOCKET_DATAGRAM 0x10 // Use datagrams +#define MPR_SOCKET_EOF 0x20 // Seen end of file +#define MPR_SOCKET_LISTENER 0x40 // MprSocket is server listener +#define MPR_SOCKET_NOREUSE 0x80 // Dont set SO_REUSEADDR option +#define MPR_SOCKET_NODELAY 0x100 // Disable Nagle algorithm +#define MPR_SOCKET_DISPOSED 0x200 // Delete requested + +#define MPR_SOCKET_READABLE 0x2 +#define MPR_SOCKET_WRITABLE 0x4 +#define MPR_SOCKET_EXCEPTION 0x8 + +class MprSocket : public MprLink +{ + private: + MprSocketAcceptProc + acceptCallback; // Accept callback + void *acceptData; // User accept callback data + int currentEvents; // Mask of ready events (FD_x) + int error; // Last error + MprSelectHandler *handler; // Select handler + int handlerMask; // Handler events of interest + int handlerPriority; // Handler priority + int interestEvents; // Mask of events to watch for + MprSocketIoProc ioCallback; // User I/O callback + void *ioData; // User io callback data + void *ioData2; // Secondary user io callback data + int inUse; // In use counter. Used by dispose() + MprStr ipAddr; // Host IP address + MprLogModule *log; // Pointer to MprSocketService module + int port; // Port to listen on + int selectEvents; // Events being selected + +#if BLD_FEATURE_MULTITHREAD + MprMutex *mutex; // Multi-thread sync +#endif + + protected: + int sock; // Actual socket handle + int flags; // Current state flags + bool secure; // MprSocket is using SSL + + public: + MprSocket(); + void forcedClose(); + void acceptProc(int isMprPoolThread); + bool getEof(); + int getError(); + int getFlags(); + char *getIpAddr() { return ipAddr; }; + int getPort(); + int getFd(); + bool getBlockingMode(); + void getAcceptCallback(MprSocketAcceptProc *fn, void **data); + void getCallback(MprSocketIoProc *fn, void **data, + void **data2, int *mask); + bool isSecure() { return secure; }; + int openServer(char *ipAddr, int portNum, + MprSocketAcceptProc acceptFn, void *data, int flags); + int openClient(char *hostName, int portNum, int flags); + void setBlockingMode(bool on); + int setBufSize(int sendSize, int recvSize); + // FUTURE -- rename: handler vs callback + void setCallback(MprSocketIoProc fn, void *data, + void *data2, int mask, int pri = MPR_NORMAL_PRIORITY); + int write(char *s); + + virtual ~MprSocket(); + virtual void close(int how); + virtual bool dispose(); + virtual void ioProc(int mask, int isMprPoolThread); + virtual MprSocket + *newSocket(); + virtual int read(char *buf, int len); + virtual int write(char *buf, int len); + +#if BLD_FEATURE_MULTITHREAD + void lock() { mutex->lock(); }; + void unlock() { mutex->unlock(); }; +#else + inline void lock() {}; + inline void unlock() {}; +#endif + + private: + void setMask(int handlerMask); + void setNoDelay(bool on); + + friend class MprSocketService; +}; + +//////////////////////////////// MprPoolService //////////////////////////////// + +#if BLD_DEBUG +class MprPoolStats { + public: + int maxThreads; // Configured max number of threads + int minThreads; // Configured minimum + int numThreads; // Configured minimum + int maxUse; // Max used + int pruneHighWater; // Peak thread use in last minute + int idleThreads; // Current idle + int busyThreads; // Current busy +}; +#endif + +// +// A task queue consists of a list of tasks and optional list of threads +// +typedef void (*MprTaskProc)(void *data, MprTask *tp); + +// +// Class for the overall thread pool service +// +class MprPoolService { + protected: // Allow MprPoolThreads to access + MprStr name; // Name of pool + int nextTaskNum; // Unique next task number + MprList runningTasks; // List of executing tasks + int stackSize; // Stack size for worker threads + MprList tasks; // Prioritized list of pending tasks + +#if BLD_FEATURE_MULTITHREAD + MprList busyThreads; // List of threads to service tasks + MprList idleThreads; // List of threads to service tasks + int maxThreads; // Max # threads in pool + int maxUseThreads; // Max threads ever used + int minThreads; // Max # threads in pool + MprMutex *mutex; // Per task synchronization + int nextThreadNum; // Unique next thread number + int numThreads; // Current number of threads in pool + int pruneHighWater; // Peak thread use in last minute + MprTimer *pruneTimer; // Timer for excess threads pruner + MprMutex *incMutex; // Per task synchronization +#endif + + public: + MprLogModule *log; + + public: + MprPoolService(char *name); + ~MprPoolService(); + int assignNextTask(MprPoolThread *pt); + void dequeueTask(MprTask *tp); +#if BLD_DEBUG + void getStats(MprPoolStats *ps); +#endif + void insertTask(MprTask *np, MprTask *tp); + void prune(); + void queueTask(MprTask *tp); + void queueRunningTask(MprTask *tp); + void removeTask(MprTask *tp); + int runTasks(); + void setStackSize(int n); + int start(); + int stop(int timeout); + +#if BLD_FEATURE_MULTITHREAD + void dispatchTasks(); + int getMinPoolThreads() { return minThreads; }; + int getNumPoolThreads() { + return idleThreads.getNumItems() + + busyThreads.getNumItems(); + }; + int getMaxPoolThreads() { return maxThreads; }; + int getNumIdleThreads() { return idleThreads.getNumItems(); }; + void lock(); + int getNextThreadNum(); + void removeThread(MprPoolThread *pt); + void setMinPoolThreads(int n); + void setMaxPoolThreads(int n); + void unlock(); +#else + inline void lock() {}; + inline void unlock() {}; + int getMaxPoolThreads() { return 0; }; +#endif + + friend class MprPoolThread; + friend class MprTask; +}; + +///////////////////////////////// MprPoolThread //////////////////////////////// +#if BLD_FEATURE_MULTITHREAD +// +// Flags +// +#define MPR_POOL_THREAD_SLEEPING 0x1 + +// +// Class for each thread in the thread pool +// +class MprPoolThread : public MprLink { + private: + MprPoolService *pool; // Which thread pool do we swim in + MprTask *currentTask; // Current task being run + int flags; + +#if BLD_FEATURE_MULTITHREAD + MprThread *thread; // Associated thread + MprCond *idleCond; // Used to wait for work +#endif + + public: + MprPoolThread(MprPoolService *pool, int stackSize); + ~MprPoolThread(); + MprPoolService *getMprPoolService() { return pool; }; + MprThread *getThread() { return thread; }; + MprTask *getCurrentTask() { return currentTask; }; + void makeIdle(); + void start(); + void setTask(MprTask *tp); + void threadMain(); + void wakeup(); +}; + +#endif +/////////////////////////////////// MprTask //////////////////////////////////// +// +// Flags +// +#define MPR_TASK_DISPOSED 0x1 +#define MPR_TASK_RUNNING 0x2 + +// +// Class for each task (unit of work) +// +class MprTask : public MprLink { + public: + void *data; // Task data + int flags; // Control flags + int inUse; // In use counter. Used by dispose() + MprPoolService *pool; // Managing pool + int priority; // Priority of event + MprTaskProc proc; // Procedure to service this event. + +#if BLD_FEATURE_MULTITHREAD + MprPoolThread *pt; // Pool thread servicing this task + MprCond *stoppingCond; // Synchronization for timer->dispose() +#endif + + public: + MprTask(MprTaskProc proc, void *data, + int priority = MPR_NORMAL_PRIORITY); + MprTask(MprPoolService *pool, MprTaskProc proc, + void *data, int priority = MPR_NORMAL_PRIORITY); + bool dispose(); + void start(); + int stop(int timeout); + +#if BLD_FEATURE_MULTITHREAD + MprPoolThread *getThread() { return pt; }; +#endif + + private: + ~MprTask(); + friend class MprPoolThread; + friend class MprPoolService; +}; + +/////////////////////////////// MprThreadService /////////////////////////////// +#if BLD_FEATURE_MULTITHREAD +// +// Threading primitives +// +typedef void (*MprThreadProc)(void *arg, MprThread *tp); + +class MprThreadService { + private: + MprList threads; // List of all threads + MprThread *mainThread; // Main application Mpr thread id + MprMutex *mutex; // Multi-thread sync + + public: + MprThreadService(); + ~MprThreadService(); + MprThread *getCurrentThread(); + void insertThread(MprThread *tp); + void removeThread(MprThread *tp); + int start(); + int stop(int timeout); + + inline void lock() { + if (mutex) { + mutex->lock(); + } + }; + inline void unlock() { + if (mutex) { + mutex->unlock(); + } + }; +}; + +/////////////////////////////////// MprThread ////////////////////////////////// + +class MprThread : public MprLink { + private: + #if WIN + int osThreadId; // O/S thread id + handle threadHandle; // Threads OS handle + #endif + #if LINUX || MACOSX || SOLARIS + pthread_t osThreadId; // O/S thread id + #endif + #if VXWORKS + int osThreadId; // O/S thread id (same as pid) + #endif + void *data; // Data argument + MprThreadProc entry; // Users thread entry point + MprStr name; // Name of thead for trace + MprMutex *mutex; // Multi-thread synchronization + int pid; // Owning process id + int priority; // Current priority + int stackSize; // Only VxWorks implements + + public: + MprThread(int pri, char *name); + // FUTURE -- move pri to last and default it. + MprThread(MprThreadProc proc, int pri, void *data, + char *name, int stackSize = 0); + ~MprThread(); + int getId() { return (int) osThreadId; }; + char *getName() { return name; }; + int getPriority() { return priority; }; + void lock() { mutex->lock(); }; + void setId(int id); + void setPriority(int priority); + void setStackSize(int size); + int start(); + void threadProc(); + void unlock() { mutex->unlock(); }; + + private: + int mapMprPriorityToOs(int mprPriority); + int mapOsPriorityToMpr(int nativePriority); +}; + +extern MprThread *mprGetCurrentThread(); +extern int mprGetMaxPoolThreads(); + +#endif // BLD_FEATURE_MULTITHREAD +/////////////////////////////// MprTimerService //////////////////////////////// + +#define MPR_TIMER_TOLERANCE 2 // Used in timer calculations + +// +// Timer service. One per MPR +// +class MprTimerService { + private: + int lastIdleTime; // Last return value from getIdleTime() + int lastRanTimers; // Last call to runTimers() + MprLogModule *log; // Log module to identify trace + MprList timerList; // List of all timers + +#if BLD_FEATURE_MULTITHREAD + MprMutex *mutex; // Multi-thread sync +#endif + + public: + MprTimerService(); + ~MprTimerService(); + void callTimer(MprTimer *tp); + int getIdleTime(); + int runTimers(); + int stop(); + int start(); + +#if BLD_FEATURE_MULTITHREAD + MprMutex *getMutex() { return mutex; }; + inline void lock() { mutex->lock(); }; + inline void unlock() { mutex->unlock(); }; +#else + inline void lock() {}; + inline void unlock() {}; +#endif + + protected: + void updateSelect(MprTimer *tp); + + friend class MprTimer; +}; + +//////////////////////////////////// MprTimer ////////////////////////////////// +// +// MprTimer callback function prototype +// +typedef void (*MprTimerProc)(void *data, MprTimer *tp); + +// +// MprTimer flags +// +#define MPR_TIMER_DISPOSED 0x1 +#define MPR_TIMER_RUNNING 0x2 +#define MPR_TIMER_TASK 0x4 +#define MPR_TIMER_AUTO_RESCHED 0x8 + +class MprTimer : public MprLink { + private: + void *data; // Argument to pass to callback + int flags; + int inUse; // In use counter. Used by dispose() + int period; // Reschedule period + MprTimerProc proc; // Callback procedure + MprTime time; // When timer is due to next run + MprTimerService *timerService; + +#if BLD_FEATURE_MULTITHREAD + MprCond *stoppingCond; // Synchronization when stopping +#endif + + public: + MprTimer(int msec, MprTimerProc routine, void *arg, + int userFlags = 0); + bool dispose(); + int getPeriod() { return period; }; + MprTimerService *getMprTimerService() { return timerService; }; + void reschedule(); + void reschedule(int msec); + int stop(int timeout); + +private: + ~MprTimer(); + friend class MprTimerService; +}; + +//////////////////////////////// MprWinService ///////////////////////////////// +#if BLD_FEATURE_RUN_AS_SERVICE && WIN + +class MprWinService { + private: + MprStr svcName; + public: + MprWinService(char *name); + ~MprWinService(); + int install(char *displayName, char *cmd); + int registerService(HANDLE threadHandle, HANDLE waitEvent); + int remove(int code); + int startDispatcher(LPSERVICE_MAIN_FUNCTION svcMain); + int start(); + int stop(int code); + void updateStatus(int status, int exitCode); +}; + +#endif +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Other C++ Stuff //////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +#if BLD_DEBUG && UNUSED +#if LINUX || MACOSX +#if MPR_CPU_IX86 + inline int64 mprGetHiResTime() { + int64 now; + __asm__ __volatile__ ("rdtsc" : "=A" (now)); + return now; + } +#endif +#endif +#if WIN + inline int64 mprGetHiResTime() { + int64 now; + QueryPerformanceCounter((LARGE_INTEGER*) &now); + return now; + } +#endif + +//////////////////////////////////////////////////////////////////////////////// + +inline int64 mprGetElapsedTime() +{ + static int64 lastMark = 0; + int64 now, elapsed; + + now = mprGetHiResTime(); + if (now > lastMark) { + elapsed = now - lastMark; + lastMark = now; + } else { + elapsed = lastMark - now + 1; + lastMark = now; + } + return elapsed; +}; + +#endif // BLD_DEBUG && UNUSED +#endif // __cplusplus + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////// C Prototypes ////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Generic array type + */ +typedef struct MprArray { + int max; /**< Size of the handles array. */ + int used; /**< Count of used entries in handles. */ + void **handles; +} MprArray; + +#if BLD_FEATURE_SQUEEZE +#define MPR_ARRAY_INCR 8 +#else +#define MPR_ARRAY_INCR 16 +#endif + +// +// Array +// +extern MprArray *mprCreateArray(); +extern void mprDestroyArray(MprArray *array); +extern int mprAddToArray(MprArray *array, void *item); +extern int mprRemoveFromArray(MprArray *array, int index); + +// +// Printf replacements +// +#define MPR_STDOUT 1 +#define MPR_STDERR 2 + +extern int mprPrintf(char *fmt, ...); +extern int mprStaticPrintf(char *fmt, ...); +extern int mprFprintf(int fd, char *fmt, ...); +extern int mprAllocSprintf(char **s, int n, char *fmt, ...); +extern int mprAllocVsprintf(char **s, int n, char *fmt, va_list arg); +extern int mprSprintf(char *s, int n, char *fmt, ...); +extern int mprVsprintf(char *s, int n, char *fmt, va_list arg); +extern char *mprItoa(int value, char *userBuf, int width); +extern int mprAtoi(char *str, int radix); + +// +// Safe string routines +// +extern int mprStrcpy(char *dest, int destMax, const char *src); +extern int mprMemcpy(char *dest, int destMax, const char *src, + int nbytes); +extern int mprAllocStrcpy(char **dest, int max, const char *src); +extern int mprReallocStrcpy(char **dest, int max, const char *src); +extern int mprAllocMemcpy(char **dest, int destMax, const char *src, + int nbytes); +extern int mprStrcat(char *dest, int max, const char *delim, + const char *src, ...); +extern int mprAllocStrcat(char **dest, int max, const char *delim, + const char *src, ...); +extern int mprReallocStrcat(char **dest, int max, int existingLen, + const char *delim, const char *src, ...); +extern int mprStrlen(char *src, int max); + +// FUTURE (rename to Strcmpi, Strncmpi) +extern int mprStrCmpAnyCase(char *s1, char *s2); +extern int mprStrCmpAnyCaseCount(char *s1, char *s2, int len); +extern char *mprStrLower(char *string); +extern char *mprStrUpper(char *string); +extern char *mprStrTrim(char *string, char c); + +// +// Reentrant string and time routines +// +extern char *mprStrTok(char *str, const char *sep, char **last); +extern char *mprGetWordTok(char *word, int wordLen, char *str, + const char *delim, char **tok); +extern int mprCtime(const time_t* timer, char *buf, int bufsize); +extern int mprGetTime(MprTime *tp); +extern int mprAsctime(const struct tm *timeptr, char *buf, int bufsiz); +extern struct tm *mprGmtime(time_t* now, struct tm *tmp); +extern struct tm *mprLocaltime(time_t* now, struct tm *tmp); +extern int mprRfcTime(char *buf, int size, const struct tm *timeptr); + +// +// General other xPlatform routines +// +extern char *mprGetBaseName(char *name); +extern int mprGetDirName(char *buf, int bufsize, char *path); +extern void mprMakeArgv(char *prog, char *cmd, char ***argv, int *argc); +extern int mprMakeDir(char *path); +extern char *mprInetNtoa(char *buf, int size, const struct in_addr in); +extern void mprSleep(int msec); +extern struct hostent* mprGetHostByName(char *name); +extern void mprFreeGetHostByName(struct hostent* hostp); +extern int mprGetRandomBytes(uchar *buf, int size, int block); + +#if BLD_FEATURE_MULTITHREAD +extern char *mprGetCurrentThreadName(); +extern void mprLock(); +extern void mprUnlock(); +#else +#if __cplusplus +inline void mprLock() {}; +inline void mprUnlock() {}; +#else +#define mprLock() +#define mprUnlock() +#endif +#endif + +extern bool mprGetDebugMode(); +extern void mprSetDebugMode(bool on); + +extern int mprGetOsError(); +extern char *mprGetErrorMsg(int errCode); +extern char *mprMakeTempFileName(char *buf, int bufsize, char *tempDir); +extern void mprNextFds(char *msg); +extern char *mprGetFullPathName(char *buf, int buflen, char *path); + +extern void mprError(char *file, int line, int flags, char *fmt, ...); + + +#if BLD_FEATURE_LOG || !__cplusplus +extern void mprLog(int level, char *fmt, ...); +#endif + +typedef void (*MprMemProc)(int askSize, int totalPoolMem, int limit); + +extern void *mprCalloc(uint numElem, uint size); +extern int mprCreateMemHeap(char *userBuf, int initialSize, int limit); +extern void mprFree(void *ptr); +extern void *mprMalloc(uint size); +extern void mprMemClose(); +#if BLD_FEATURE_MALLOC_HOOK +extern void mprHookMalloc(); +#endif +extern void mprMemStop(); +extern void mprPrintMemStats(); +extern void *mprRealloc(void *ptr, uint size); +extern void mprRequestMemStats(bool on); +extern void mprSetMemHandler(MprMemProc cback); +extern char *mprStrdup(const char *str); + +#if WIN +extern int mprReadRegistry(char *key, char *val, char **buf, int max); +#endif + + +#if __cplusplus +} // extern "C" +#endif + +#endif // _h_MPR + +// +// 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 +// diff --git a/source4/web_server/ejs/mprOs.h b/source4/web_server/ejs/mprOs.h new file mode 100644 index 0000000000..5a88f4f8af --- /dev/null +++ b/source4/web_server/ejs/mprOs.h @@ -0,0 +1,627 @@ +/// +/// @file mprOs.h +/// @brief Include O/S headers and smooth out per-O/S differences +// @copy default +// +// Copyright (c) Mbedthis Software LLC, 2003-2005. 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 +//////////////////////////////// Documentation ///////////////////////////////// +/// +/// This header is part of the Mbedthis Portable Runtime and aims to include +/// all necessary O/S headers and to unify the constants and declarations +/// required by Mbedthis products. It can be included by C or C++ programs. +/// +//////////////////////////////////////////////////////////////////////////////// + +#error foo + +blah blah; + +#ifndef _h_MPR_OS_HDRS +#define _h_MPR_OS_HDRS 1 + +#include "web_server/ejs/config.h" + +////////////////////////////////// CPU Families //////////////////////////////// +// +// Porters, add your CPU families here and update configure code. +// +#define MPR_CPU_UNKNOWN 0 +#define MPR_CPU_IX86 1 +#define MPR_CPU_PPC 2 +#define MPR_CPU_SPARC 3 +#define MPR_CPU_XSCALE 4 +#define MPR_CPU_ARM 5 +#define MPR_CPU_MIPS 6 +#define MPR_CPU_68K 7 +#define MPR_CPU_SIMNT 8 // VxWorks NT simulator +#define MPR_CPU_SIMSPARC 9 // VxWorks sparc simulator + +////////////////////////////////// O/S Includes //////////////////////////////// + +#if LINUX || SOLARIS + #include <sys/types.h> + #include <time.h> + #include <arpa/inet.h> + #include <ctype.h> + #include <dlfcn.h> + #include <fcntl.h> + #include <grp.h> + #include <errno.h> + #include <libgen.h> + #include <limits.h> + #include <netdb.h> + #include <net/if.h> + #include <netinet/in.h> + #include <netinet/tcp.h> + #include <netinet/ip.h> + #include <pthread.h> + #include <pwd.h> + #include <resolv.h> + #include <signal.h> + #include <stdarg.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <syslog.h> + #include <sys/ioctl.h> + #include <sys/stat.h> + #include <sys/param.h> + #include <sys/resource.h> + #include <sys/sem.h> + #include <sys/shm.h> + #include <sys/socket.h> + #include <sys/select.h> + #include <sys/time.h> + #include <sys/times.h> + #include <sys/utsname.h> + #include <sys/wait.h> + #include <unistd.h> + +#if LINUX + #include <stdint.h> +#endif + +#if SOLARIS + #include <netinet/in_systm.h> +#endif + +#if BLD_FEATURE_FLOATING_POINT + #define __USE_ISOC99 1 + #include <math.h> + #include <values.h> +#endif + +#endif // LINUX || SOLARIS + +#if VXWORKS + #include <vxWorks.h> + #include <envLib.h> + #include <sys/types.h> + #include <time.h> + #include <arpa/inet.h> + #include <ctype.h> + #include <fcntl.h> + #include <errno.h> + #include <limits.h> + #include <loadLib.h> + #include <netdb.h> + #include <net/if.h> + #include <netinet/tcp.h> + #include <netinet/in.h> + #include <netinet/ip.h> + #include <signal.h> + #include <stdarg.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <sysSymTbl.h> + #include <sys/fcntlcom.h> + #include <sys/ioctl.h> + #include <sys/stat.h> + #include <sys/socket.h> + #include <sys/times.h> + #include <sys/wait.h> + #include <unistd.h> + #include <unldLib.h> + + #if BLD_FEATURE_FLOATING_POINT + #include <float.h> + #define __USE_ISOC99 1 + #include <math.h> + #endif + + #include <sockLib.h> + #include <inetLib.h> + #include <ioLib.h> + #include <pipeDrv.h> + #include <hostLib.h> + #include <netdb.h> + #include <tickLib.h> + #include <taskHookLib.h> + +#endif // VXWORKS + +#if MACOSX + #include <time.h> + #include <arpa/inet.h> + #include <ctype.h> + #include <fcntl.h> + #include <grp.h> + #include <errno.h> + #include <libgen.h> + #include <limits.h> + #include <mach-o/dyld.h> + #include <netdb.h> + #include <net/if.h> + #include <netinet/in_systm.h> + #include <netinet/in.h> + #include <netinet/tcp.h> + #include <netinet/ip.h> + #include <pthread.h> + #include <pwd.h> + #include <resolv.h> + #include <signal.h> + #include <stdarg.h> + #include <stdio.h> + #include <stdlib.h> + #include <stdint.h> + #include <string.h> + #include <syslog.h> + #include <sys/ioctl.h> + #include <sys/types.h> + #include <sys/stat.h> + #include <sys/param.h> + #include <sys/resource.h> + #include <sys/sem.h> + #include <sys/shm.h> + #include <sys/socket.h> + #include <sys/select.h> + #include <sys/time.h> + #include <sys/times.h> + #include <sys/types.h> + #include <sys/utsname.h> + #include <sys/wait.h> + #include <unistd.h> +#endif // MACOSX + +#if WIN + #include <ctype.h> + #include <conio.h> + #include <direct.h> + #include <errno.h> + #include <fcntl.h> + #include <io.h> + #include <limits.h> + #include <malloc.h> + #include <process.h> + #include <sys/stat.h> + #include <sys/types.h> + #include <stddef.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <stdarg.h> + #include <time.h> + #define WIN32_LEAN_AND_MEAN + #include <winsock2.h> + #include <windows.h> + #include <winbase.h> + #if BLD_FEATURE_FLOATING_POINT + #include <float.h> + #endif + #include <shlobj.h> + #include <shellapi.h> + #include <wincrypt.h> +#endif // WIN + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// General Defines /////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#define MAXINT INT_MAX +#define BITS(type) (BITSPERBYTE * (int) sizeof(type)) + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +// +// Set FD_SETSIZE to the maximum number of files (sockets) that you want to +// support. It is used in select.cpp. +// +// #ifdef FD_SETSIZE +// #undef FD_SETSIZE +// #endif +// #define FD_SETSIZE 128 +// + +typedef char *MprStr; // Used for dynamic strings + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Linux Defines //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if LINUX + typedef unsigned char uchar; + +#if BLD_FEATURE_INT64 + __extension__ typedef long long int int64; + __extension__ typedef unsigned long long int uint64; + #define INT64(x) (x##LL) +#endif + + #define closesocket(x) close(x) + #define MPR_BINARY "" + #define MPR_TEXT "" + #define O_BINARY 0 + #define O_TEXT 0 + #define SOCKET_ERROR -1 + #define MPR_DLL_EXT ".so" + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT MAXFLOAT +#endif + + #if BLD_FEATURE_MALLOC + // + // PORTERS: You will need add assembler code for your architecture here + // only if you want to use the fast malloc (BLD_FEATURE_MALLOC) + // + #if UNUSED + #define MPR_GET_RETURN(ip) __builtin_return_address(0) + #else + #if BLD_HOST_CPU_ARCH == MPR_CPU_IX86 + #define MPR_GET_RETURN(ip) \ + asm("movl 4(%%ebp),%%eax ; movl %%eax,%0" : \ + "=g" (ip) : \ + : "eax") + #endif + #endif // UNUSED + #endif // BLD_FEATURE_MALLOC + +#if FUTURE +// #define mprGetHiResTime(x) __asm__ __volatile__ ("rdtsc" : "=A" (x)) +// extern char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen); + + // + // Atomic functions + // + typedef struct { volatile int counter; } mprAtomic_t; + + #if BLD_FEATURE_MULTITHREAD + #define LOCK "lock ; " + #else + #define LOCK "" + #endif + + static __inline__ void mprAtomicInc(mprAtomic_t* v) { + __asm__ __volatile__( + LOCK "incl %0" + :"=m" (v->counter) + :"m" (v->counter)); + } + + static __inline__ void mprAtomicDec(mprAtomic_t* v) { + __asm__ __volatile__( + LOCK "decl %0" + :"=m" (v->counter) + :"m" (v->counter)); + } +#endif // FUTURE + +#endif // LINUX + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// VxWorks Defines /////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if VXWORKS + + typedef unsigned char uchar; + typedef unsigned int uint; + typedef unsigned long ulong; + + #define HAVE_SOCKLEN_T + typedef int socklen_t; + +#if BLD_FEATURE_INT64 + typedef long long int int64; + typedef unsigned long long int uint64; + #define INT64(x) (x##LL) +#endif + + #define closesocket(x) close(x) + #define getpid() taskIdSelf() + #define MPR_BINARY "" + #define MPR_TEXT "" + #define O_BINARY 0 + #define O_TEXT 0 + #define SOCKET_ERROR -1 + #define MPR_DLL_EXT ".so" + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT FLT_MAX +#endif + + #undef R_OK + #define R_OK 4 + #undef W_OK + #define W_OK 2 + #undef X_OK + #define X_OK 1 + #undef F_OK + #define F_OK 0 + + #define MSG_NOSIGNAL 0 + + extern int access(char *path, int mode); + extern int sysClkRateGet(); + + #if BLD_FEATURE_MALLOC + // + // PORTERS: You will need add assembler code for your architecture here + // only if you want to use the fast malloc (BLD_FEATURE_MALLOC) + // + #if UNUSED + #define MPR_GET_RETURN(ip) __builtin_return_address(0) + #else + #if BLD_HOST_CPU_ARCH == MPR_CPU_IX86 + #define MPR_GET_RETURN(ip) \ + asm("movl 4(%%ebp),%%eax ; movl %%eax,%0" : \ + "=g" (ip) : \ + : "eax") + #endif + #endif // UNUSED + #endif // BLD_FEATURE_MALLOC +#endif // VXWORKS + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// MacOsx Defines /////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +#if MACOSX + typedef unsigned long ulong; + typedef unsigned char uchar; + +#if BLD_FEATURE_INT64 + __extension__ typedef long long int int64; + __extension__ typedef unsigned long long int uint64; + #define INT64(x) (x##LL) +#endif + #define closesocket(x) close(x) + #define MPR_BINARY "" + #define MPR_TEXT "" + #define O_BINARY 0 + #define O_TEXT 0 + #define SOCKET_ERROR -1 + #define MPR_DLL_EXT ".dylib" + #define MSG_NOSIGNAL 0 + #define __WALL 0x40000000 + #define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT MAXFLOAT +#endif + + #if MPR_FEATURE_MALLOC + // + // PORTERS: You will need add assembler code for your architecture here + // only if you want to use the fast malloc (MPR_FEATURE_MALLOC) + // + #define MPR_GET_RETURN(ip) __builtin_return_address + #endif + +#if FUTURE +// #define mprGetHiResTime(x) __asm__ __volatile__ ("rdtsc" : "=A" (x)) +// extern char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen); + + // + // Atomic functions + // + typedef struct { volatile int counter; } mprAtomic_t; + + #if MPR_FEATURE_MULTITHREAD + #define LOCK "lock ; " + #else + #define LOCK "" + #endif + + static __inline__ void mprAtomicInc(mprAtomic_t* v) { + __asm__ __volatile__( + LOCK "incl %0" + :"=m" (v->counter) + :"m" (v->counter)); + } + + static __inline__ void mprAtomicDec(mprAtomic_t* v) { + __asm__ __volatile__( + LOCK "decl %0" + :"=m" (v->counter) + :"m" (v->counter)); + } +#endif +#endif // MACOSX + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// Windows Defines /////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if WIN + typedef unsigned char uchar; + typedef unsigned int uint; + typedef unsigned long ulong; + typedef unsigned short ushort; + +#if BLD_FEATURE_INT64 + typedef __int64 int64; + typedef unsigned __int64 uint64; + #define INT64(x) (x##i64) +#endif + + typedef int uid_t; + typedef void *handle; + typedef char *caddr_t; + typedef long pid_t; + typedef int gid_t; + typedef ushort mode_t; + typedef void *siginfo_t; + + #define HAVE_SOCKLEN_T + typedef int socklen_t; + + #undef R_OK + #define R_OK 4 + #undef W_OK + #define W_OK 2 + #undef X_OK + #define X_OK 1 + #undef F_OK + #define F_OK 0 + + #ifndef EADDRINUSE + #define EADDRINUSE 46 + #endif + #ifndef EWOULDBLOCK + #define EWOULDBLOCK EAGAIN + #endif + #ifndef ENETDOWN + #define ENETDOWN 43 + #endif + #ifndef ECONNRESET + #define ECONNRESET 44 + #endif + #ifndef ECONNREFUSED + #define ECONNREFUSED 45 + #endif + + #define MSG_NOSIGNAL 0 + #define MPR_BINARY "b" + #define MPR_TEXT "t" + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT DBL_MAX +#endif + +#ifndef FILE_FLAG_FIRST_PIPE_INSTANCE +#define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 +#endif + + #define access _access + #define close _close + #define fileno _fileno + #define fstat _fstat + #define getpid _getpid + #define open _open + #define putenv _putenv + #define read _read + #define stat _stat + #define umask _umask + #define unlink _unlink + #define write _write + #define strdup _strdup + #define lseek _lseek + + #define mkdir(a,b) _mkdir(a) + #define rmdir(a) _rmdir(a) + + #if BLD_FEATURE_MALLOC + // + // PORTERS: You will need add assembler code for your architecture here + // only if you want to use the fast malloc (BLD_FEATURE_MALLOC) + // + #if MPR_CPU_IX86 + #define MPR_GET_RETURN(ip) \ + __asm { mov eax, 4[ebp] } \ + __asm { mov ip, eax } + #endif + #endif + + #define MPR_DLL_EXT ".dll" + + extern void srand48(long); + extern long lrand48(void); + extern long ulimit(int, ...); + extern long nap(long); + extern uint sleep(unsigned int secs); + extern uid_t getuid(void); + extern uid_t geteuid(void); + +#endif // WIN + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Solaris Defines //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#if SOLARIS + typedef unsigned char uchar; + +#if BLD_FEATURE_INT64 + typedef long long int int64; + typedef unsigned long long int uint64; + #define INT64(x) (x##LL) +#endif + + #define closesocket(x) close(x) + #define MPR_BINARY "" + #define MPR_TEXT "" + #define O_BINARY 0 + #define O_TEXT 0 + #define SOCKET_ERROR -1 + #define MPR_DLL_EXT ".so" + #define MSG_NOSIGNAL 0 + #define INADDR_NONE ((in_addr_t) 0xffffffff) + #define __WALL 0 + #define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT MAXFLOAT +#endif + +#endif // SOLARIS + +//////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +} +#endif + +#endif // _h_MPR_OS_HDRS + +// +// 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 +// diff --git a/source4/web_server/ejs/var.c b/source4/web_server/ejs/var.c new file mode 100644 index 0000000000..53b66d8b1b --- /dev/null +++ b/source4/web_server/ejs/var.c @@ -0,0 +1,2161 @@ +/* + * @file var.c + * @brief MPR Universal Variable Type + * @overview + * + * @copy default.m + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved. + * Copyright (c) Michael O'Brien, 1994-1995. 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 + */ + +/******************************* Documentation ********************************/ + +/* + * This module is NOT multithreaded. + * + * Properties are variables that are stored in an object type variable. + * Properties can be primitive data types, other objects or functions. + * Properties are indexed by a character name. + */ + +/********************************** Includes **********************************/ + +#include "web_server/ejs/var.h" + +/*********************************** Locals ***********************************/ +#if VAR_DEBUG + +static MprProperties objectList; /* Dummy head of objects list */ +static int objectCount = -1; /* Count of objects */ + +#endif +/***************************** Forward Declarations ***************************/ + +static int adjustRefCount(MprProperties *pp, int adj); +static int adjustVarRefCount(MprVar *vp, int adj); +static MprVar *allocProperty(const char *propertyName); +static void copyVarCore(MprVar *dest, MprVar *src, int copyDepth); +static MprProperties + *createProperties(const char *name, int hashSize); +static bool freeVar(MprVar *vp, int force); +static bool freeVarStorage(MprVar *vp, int force); +static MprVar *getObjChain(MprProperties *pp, const char *property); +static int hash(MprProperties *pp, const char *property); +static bool releaseProperties(MprProperties *pp, int force); + +/*********************************** Code *************************************/ +/* + * Destroy a variable and all referenced variables. Release any referenced + * object regardless of whether other users still have references. Be VERY + * careful using this routine. + * + * Return TRUE if the underlying data is freed. Objects may not be freed if + * there are other users of the object. + */ + +bool mprDestroyAllVars(MprVar *vp) +{ + mprAssert(vp); + + if (vp->trigger) { + if ((vp->trigger)(MPR_VAR_DELETE, vp->parentProperties, vp, 0, 0) + == MPR_TRIGGER_ABORT) { + return 0; + } + } + + /* + * Free the actual value. If this var refers to an object, we will + * recurse through all the properties freeing all vars. + */ + return freeVar(vp, 1); +} + +/******************************************************************************/ +/* + * Destroy a variable. Release any referenced object (destroy if no other + * users are referencing). + * + * Return TRUE if the underlying data is freed. Objects may not be freed if + * there are other users of the object. + */ + +bool mprDestroyVar(MprVar *vp) +{ + mprAssert(vp); + + if (vp->trigger) { + if ((vp->trigger)(MPR_VAR_DELETE, vp->parentProperties, vp, 0, 0) + == MPR_TRIGGER_ABORT) { + return 0; + } + } + + /* + * Free the actual value. If this var refers to an object, we will + * recurse through all the properties freeing all that have no other + * references. + */ + return freeVar(vp, 0); +} + +/******************************************************************************/ +/* + * Free the value in a variable for primitive types. Release objects. + * + * Return TRUE if the underlying data is freed. Objects may not be freed if + * there are other users of the object. + */ + +static bool freeVar(MprVar *vp, int force) +{ + bool freed; + + mprAssert(vp); + + freed = freeVarStorage(vp, force); + + mprFree(vp->name); + mprFree(vp->fullName); + + if (vp->allocatedVar) { + mprFree(vp); + } else { + vp->name = 0; + vp->fullName = 0; + vp->type = MPR_TYPE_UNDEFINED; + } + return freed; +} + +/******************************************************************************/ +/* + * Free the value in a variable for primitive types. Release objects. + * + * Return TRUE if the underlying data is freed. Objects may not be freed if + * there are other users of the object. + */ + +static bool freeVarStorage(MprVar *vp, int force) +{ + MprArray *argList; + bool freed; + int i; + + freed = 1; + mprAssert(vp); + + switch (vp->type) { + default: + break; + + case MPR_TYPE_STRING: + if (vp->allocatedData && vp->string != 0) { + mprFree(vp->string); + vp->string = 0; + vp->allocatedData = 0; + } + break; + + case MPR_TYPE_OBJECT: +#if VAR_DEBUG + /* + * Recurse through all properties and release / delete. Release the + * properties hash table. + */ + if (vp->properties->refCount > 1) { + mprLog(7, "freeVar: ACT \"%s\", 0x%x, ref %d, force %d\n", + vp->name, vp->properties, vp->properties->refCount, force); + } else { + mprLog(7, "freeVar: DEL \"%s\", 0x%x, ref %d, force %d\n", + vp->name, vp->properties, vp->properties->refCount, force); + } +#endif + if (vp->allocatedData) { + freed = releaseProperties(vp->properties, force); + } + vp->properties = 0; + break; + + case MPR_TYPE_FUNCTION: + if (vp->allocatedData) { + argList = vp->function.args; + for (i = 0; i < argList->max; i++) { + if (argList->handles[i] != 0) { + mprFree(argList->handles[i]); + } + } + mprDestroyArray(argList); + vp->function.args = 0; + mprFree(vp->function.body); + vp->function.body = 0; + } + break; + } + + vp->type = MPR_TYPE_UNDEFINED; + return freed; +} + +/******************************************************************************/ +/* + * Adjust the object reference count and return the currrent count of + * users. + */ + +static int adjustVarRefCount(MprVar *vp, int adj) +{ + mprAssert(vp); + + if (vp->type != MPR_TYPE_OBJECT) { + mprAssert(vp->type == MPR_TYPE_OBJECT); + return 0; + } + return adjustRefCount(vp->properties, adj); +} + +/******************************************************************************/ +/* + * Get the object reference count + */ + +int mprGetVarRefCount(MprVar *vp) +{ + mprAssert(vp); + + if (vp->type != MPR_TYPE_OBJECT) { + mprAssert(vp->type == MPR_TYPE_OBJECT); + return 0; + } + return adjustRefCount(vp->properties, 0); +} + +/******************************************************************************/ +/* + * Update the variable's name + */ + +void mprSetVarName(MprVar *vp, char *name) +{ + mprAssert(vp); + + mprFree(vp->name); + vp->name = mprStrdup(name); +} + +/******************************************************************************/ +/* + * Append to the variable's full name + */ + +void mprSetVarFullName(MprVar *vp, char *name) +{ +#if VAR_DEBUG + mprAssert(vp); + + mprFree(vp->fullName); + vp->fullName = mprStrdup(name); + if (vp->type == MPR_TYPE_OBJECT) { + if (strcmp(vp->properties->name, "this") == 0) { + mprStrcpy(vp->properties->name, sizeof(vp->properties->name), name); + } + } +#endif +} + +/******************************************************************************/ +/* + * Make a var impervious to recursive forced deletes. + */ + +void mprSetVarDeleteProtect(MprVar *vp, int deleteProtect) +{ + mprAssert(vp); + + if (vp->type == MPR_TYPE_OBJECT && vp->properties) { + vp->properties->deleteProtect = deleteProtect; + } +} + +/******************************************************************************/ +/* + * Make a variable readonly. Can still be deleted. + */ + +void mprSetVarReadonly(MprVar *vp, int readonly) +{ + mprAssert(vp); + + vp->readonly = readonly; +} + +/******************************************************************************/ + +MprVarTrigger mprAddVarTrigger(MprVar *vp, MprVarTrigger fn) +{ + MprVarTrigger oldTrigger; + + mprAssert(vp); + mprAssert(fn); + + oldTrigger = vp->trigger; + vp->trigger = fn; + return oldTrigger; +} + +/******************************************************************************/ + +MprType mprGetVarType(MprVar *vp) +{ + mprAssert(vp); + + return vp->type; +} + +/******************************************************************************/ +/********************************** Properties ********************************/ +/******************************************************************************/ +/* + * Create a property in an object with a defined value. If the property + * already exists in the object, then just write its value. + */ + +MprVar *mprCreateProperty(MprVar *obj, const char *propertyName, MprVar *newValue) +{ + MprVar *prop, *last; + int bucketIndex; + + mprAssert(obj); + mprAssert(propertyName && *propertyName); + + if (obj->type != MPR_TYPE_OBJECT) { + mprAssert(obj->type == MPR_TYPE_OBJECT); + return 0; + } + + /* + * See if property already exists and locate the bucket to hold the + * property reference. + */ + last = 0; + bucketIndex = hash(obj->properties, propertyName); + prop = obj->properties->buckets[bucketIndex]; + + /* + * Find the property in the hash chain if it exists + */ + for (last = 0; prop; last = prop, prop = prop->forw) { + if (prop->name[0] == propertyName[0] && + strcmp(prop->name, propertyName) == 0) { + break; + } + } + + if (prop) { + // FUTURE -- remove. Just for debug. + mprAssert(prop == 0); + mprLog(0, "Attempting to create property %s in object %s\n", + propertyName, obj->name); + return 0; + } + + if (obj->trigger) { + if ((obj->trigger)(MPR_VAR_CREATE_PROPERTY, obj->properties, prop, + newValue, 0) == MPR_TRIGGER_ABORT) { + return 0; + } + } + + /* + * Create a new property + */ + prop = allocProperty(propertyName); + if (prop == 0) { + mprAssert(prop); + return 0; + } + + copyVarCore(prop, newValue, MPR_SHALLOW_COPY); + + prop->bucketIndex = bucketIndex; + if (last) { + last->forw = prop; + } else { + obj->properties->buckets[bucketIndex] = prop; + } + prop->parentProperties = obj->properties; + + /* + * Update the item counts + */ + obj->properties->numItems++; + if (! mprVarIsFunction(prop->type)) { + obj->properties->numDataItems++; + } + + return prop; +} + +/******************************************************************************/ +/* + * Create a property in an object with a defined value. If the property + * already exists in the object, then just write its value. Same as + * mprCreateProperty except that the new value is passed by value rather than + * by pointer. + */ + +MprVar *mprCreatePropertyValue(MprVar *obj, const char *propertyName, MprVar newValue) +{ + return mprCreateProperty(obj, propertyName, &newValue); +} + +/******************************************************************************/ +/* + * Create a new property + */ + +static MprVar *allocProperty(const char *propertyName) +{ + MprVar *prop; + + prop = (MprVar*) mprMalloc(sizeof(MprVar)); + if (prop == 0) { + mprAssert(prop); + return 0; + } + memset(prop, 0, sizeof(MprVar)); + prop->allocatedVar = 1; + prop->name = mprStrdup(propertyName); + prop->forw = (MprVar*) 0; + + return prop; +} + +/******************************************************************************/ +/* + * Update a property in an object with a defined value. Create the property + * if it doesn not already exist. + */ + +MprVar *mprSetProperty(MprVar *obj, const char *propertyName, MprVar *newValue) +{ + MprVar *prop, triggerValue; + int rc; + + mprAssert(obj); + mprAssert(propertyName && *propertyName); + mprAssert(obj->type == MPR_TYPE_OBJECT); + + if (obj->type != MPR_TYPE_OBJECT) { + mprAssert(0); + return 0; + } + + prop = mprGetProperty(obj, propertyName, 0); + if (prop == 0) { + return mprCreateProperty(obj, propertyName, newValue); + } + + if (obj->trigger) { + /* + * Call the trigger before the update and pass it the new value. + */ + triggerValue = *newValue; + triggerValue.allocatedVar = 0; + triggerValue.allocatedData = 0; + rc = (obj->trigger)(MPR_VAR_WRITE, obj->properties, obj, + &triggerValue, 0); + if (rc == MPR_TRIGGER_ABORT) { + return 0; + + } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { + /* + * Trigger must copy to triggerValue a variable that is not + * a structure copy of the existing data. + */ + copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY); + mprDestroyVar(&triggerValue); + return prop; + } + } + copyVarCore(prop, newValue, MPR_SHALLOW_COPY); + return prop; +} + +/******************************************************************************/ +/* + * Update a property in an object with a defined value. Create the property + * if it does not already exist. Same as mprSetProperty except that the + * new value is passed by value rather than by pointer. + */ + +MprVar *mprSetPropertyValue(MprVar *obj, const char *propertyName, MprVar newValue) +{ + return mprSetProperty(obj, propertyName, &newValue); +} + +/******************************************************************************/ +/* + * Delete a property from this object + */ + +int mprDeleteProperty(MprVar *obj, const char *property) +{ + MprVar *prop, *last; + char *cp; + int bucketIndex; + + mprAssert(obj); + mprAssert(property && *property); + mprAssert(obj->type == MPR_TYPE_OBJECT); + + if (obj->type != MPR_TYPE_OBJECT) { + mprAssert(obj->type == MPR_TYPE_OBJECT); + return 0; + } + + last = 0; + bucketIndex = hash(obj->properties, property); + if ((prop = obj->properties->buckets[bucketIndex]) != 0) { + for ( ; prop; prop = prop->forw) { + cp = prop->name; + if (cp[0] == property[0] && strcmp(cp, property) == 0) { + break; + } + last = prop; + } + } + if (prop == (MprVar*) 0) { + mprAssert(prop); + return MPR_ERR_NOT_FOUND; + } + if (prop->readonly) { + mprAssert(! prop->readonly); + return MPR_ERR_READ_ONLY; + } + + if (obj->trigger) { + if ((obj->trigger)(MPR_VAR_DELETE_PROPERTY, obj->properties, prop, 0, 0) + == MPR_TRIGGER_ABORT) { + return MPR_ERR_ABORTED; + } + } + + if (last) { + last->forw = prop->forw; + } else { + obj->properties->buckets[bucketIndex] = prop->forw; + } + + obj->properties->numItems--; + if (! mprVarIsFunction(prop->type)) { + obj->properties->numDataItems--; + } + + mprDestroyVar(prop); + + return 0; +} + +/******************************************************************************/ +/* + * Find a property in an object and return a pointer to it. If a value arg + * is supplied, then copy the data into the var. + */ + +MprVar *mprGetProperty(MprVar *obj, const char *property, MprVar *value) +{ + MprVar *prop, triggerValue; + int rc; + + if (obj == 0 || obj->type != MPR_TYPE_OBJECT || property == 0 || + *property == '\0') { + if (value) { + value->type = MPR_TYPE_UNDEFINED; + } + return 0; + } + + for (prop = getObjChain(obj->properties, property); prop; + prop = prop->forw) { + if (prop->name[0] == property[0] && strcmp(prop->name, property) == 0) { + break; + } + } + if (prop == 0) { + if (value) { + value->type = MPR_TYPE_UNDEFINED; + } + return 0; + } + if (value) { + if (prop->trigger) { + triggerValue = *prop; + triggerValue.allocatedVar = 0; + triggerValue.allocatedData = 0; + /* + * Pass the trigger the current read value and may receive + * a new value. + */ + rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, + &triggerValue, 0); + if (rc == MPR_TRIGGER_ABORT) { + if (value) { + value->type = MPR_TYPE_UNDEFINED; + } + return 0; + + } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { + copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY); + mprDestroyVar(&triggerValue); + } + } + /* + * Clone. No copy. + */ + *value = *prop; + } + return prop; +} + +/******************************************************************************/ +/* + * Read a properties value. This returns the property's value. It does not + * copy object/string data but returns a pointer directly into the variable. + * The caller does not and should not call mprDestroy on the returned value. + * If value is null, just read the property and run triggers. + */ + +int mprReadProperty(MprVar *prop, MprVar *value) +{ + MprVar triggerValue; + int rc; + + mprAssert(prop); + + if (prop->trigger) { + triggerValue = *prop; + triggerValue.allocatedVar = 0; + triggerValue.allocatedData = 0; + rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, + &triggerValue, 0); + + if (rc == MPR_TRIGGER_ABORT) { + return MPR_ERR_ABORTED; + + } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { + copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY); + mprDestroyVar(&triggerValue); + return 0; + } + } + if (value) { + *value = *prop; + + /* + * Just so that if the user calls mprDestroyVar on value, it will do no + * harm. + */ + value->allocatedVar = 0; + value->allocatedData = 0; + } + return 0; +} + +/******************************************************************************/ +/* + * Read a properties value. This returns a copy of the property variable. + * However, if the property is an object or string, it returns a copy of the + * reference to the underlying data. If copyDepth is set to MPR_DEEP_COPY, + * then the underlying objects and strings data will be copied as well. If + * copyDepth is set to MPR_SHALLOW_COPY, then only strings will be copied. If + * it is set to MPR_NO_COPY, then no data will be copied. In all cases, the + * user must call mprDestroyVar to free resources. This routine will run any + * registered triggers which may modify the value the user receives (without + * updating the properties real value). + * + * WARNING: the args are reversed to most other APIs. This conforms to the + * strcpy(dest, src) standard instead. + */ + +int mprCopyProperty(MprVar *dest, MprVar *prop, int copyDepth) +{ + MprVar triggerValue; + int rc; + + mprAssert(prop); + mprAssert(dest); + + if (prop->trigger) { + triggerValue = *prop; + triggerValue.allocatedVar = 0; + triggerValue.allocatedData = 0; + rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, + &triggerValue, copyDepth); + + if (rc == MPR_TRIGGER_ABORT) { + return MPR_ERR_ABORTED; + + } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { + copyVarCore(dest, &triggerValue, MPR_SHALLOW_COPY); + mprDestroyVar(&triggerValue); + return 0; + } + } + mprCopyVar(dest, prop, copyDepth); + return 0; +} + +/******************************************************************************/ +/* + * Write a new value into an existing property in an object. + */ + +int mprWriteProperty(MprVar *vp, MprVar *value) +{ + MprVar triggerValue; + int rc; + + mprAssert(vp); + mprAssert(value); + + if (vp->readonly) { + return MPR_ERR_READ_ONLY; + } + + if (vp->trigger) { + triggerValue = *value; + + rc = (vp->trigger)(MPR_VAR_WRITE, vp->parentProperties, vp, + &triggerValue, 0); + + if (rc == MPR_TRIGGER_ABORT) { + return MPR_ERR_ABORTED; + + } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) { + copyVarCore(vp, &triggerValue, MPR_SHALLOW_COPY); + mprDestroyVar(&triggerValue); + return 0; + } + /* Fall through */ + } + + copyVarCore(vp, value, MPR_SHALLOW_COPY); + return 0; +} + +/******************************************************************************/ +/* + * Write a new value into an existing property in an object. + */ + +int mprWritePropertyValue(MprVar *vp, MprVar value) +{ + mprAssert(vp); + + return mprWriteProperty(vp, &value); +} + +/******************************************************************************/ +/* + * Get the count of properties. + */ + +int mprGetPropertyCount(MprVar *vp, int includeFlags) +{ + mprAssert(vp); + + if (vp->type != MPR_TYPE_OBJECT) { + return 0; + } + if (includeFlags == MPR_ENUM_DATA) { + return vp->properties->numDataItems; + } else { + return vp->properties->numItems; + } +} + +/******************************************************************************/ +/* + * Get the first property in an object. Used for walking all properties in an + * object. + */ + +MprVar *mprGetFirstProperty(MprVar *obj, int includeFlags) +{ + MprVar *prop; + int i; + + mprAssert(obj); + mprAssert(obj->type == MPR_TYPE_OBJECT); + + if (obj->type != MPR_TYPE_OBJECT) { + mprAssert(obj->type == MPR_TYPE_OBJECT); + return 0; + } + + for (i = 0; i < (int) obj->properties->hashSize; i++) { + for (prop = obj->properties->buckets[i]; prop; prop = prop->forw) { + if (prop) { + if (mprVarIsFunction(prop->type)) { + if (!(includeFlags & MPR_ENUM_FUNCTIONS)) { + continue; + } + } else { + if (!(includeFlags & MPR_ENUM_DATA)) { + continue; + } + } + return prop; + } + break; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Get the next property in sequence. + */ + +MprVar *mprGetNextProperty(MprVar *obj, MprVar *last, int includeFlags) +{ + MprProperties *properties; + int i; + + mprAssert(obj); + mprAssert(obj->type == MPR_TYPE_OBJECT); + + if (obj->type != MPR_TYPE_OBJECT) { + mprAssert(obj->type == MPR_TYPE_OBJECT); + return 0; + } + properties = obj->properties; + + if (last->forw) { + return last->forw; + } + + for (i = last->bucketIndex + 1; i < (int) properties->hashSize; i++) { + for (last = properties->buckets[i]; last; last = last->forw) { + if (mprVarIsFunction(last->type)) { + if (!(includeFlags & MPR_ENUM_FUNCTIONS)) { + continue; + } + } else { + if (!(includeFlags & MPR_ENUM_DATA)) { + continue; + } + } + return last; + } + } + return 0; +} + +/******************************************************************************/ +/************************** Internal Support Routines *************************/ +/******************************************************************************/ +/* + * Create an hash table to hold and index properties. Properties are just + * variables which may contain primitive data types, functions or other + * objects. The hash table is the essence of an object. HashSize specifies + * the size of the hash table to use and should be a prime number. + */ + +static MprProperties *createProperties(const char *name, int hashSize) +{ + MprProperties *pp; + + if (hashSize < 7) { + hashSize = 7; + } + if ((pp = (MprProperties*) mprMalloc(sizeof(MprProperties))) == NULL) { + mprAssert(0); + return 0; + } + mprAssert(pp); + memset(pp, 0, sizeof(MprProperties)); + + pp->numItems = 0; + pp->numDataItems = 0; + pp->hashSize = hashSize; + pp->buckets = (MprVar**) mprMalloc(pp->hashSize * sizeof(MprVar*)); + mprAssert(pp->buckets); + memset(pp->buckets, 0, pp->hashSize * sizeof(MprVar*)); + pp->refCount = 1; + +#if VAR_DEBUG + if (objectCount == -1) { + objectCount = 0; + objectList.next = objectList.prev = &objectList; + } + + mprStrcpy(pp->name, sizeof(pp->name), name); + pp->next = &objectList; + pp->prev = objectList.prev; + objectList.prev->next = pp; + objectList.prev = pp; + objectCount++; +#endif + return pp; +} + +/******************************************************************************/ +/* + * Release an object's properties hash table. If this is the last person + * using it, free it. Return TRUE if the object is released. + */ + +static bool releaseProperties(MprProperties *obj, int force) +{ + MprProperties *pp; + MprVar *prop, *forw; + int i; + + mprAssert(obj); + mprAssert(obj->refCount > 0); + +#if VAR_DEBUG + /* + * Debug sanity check + */ + mprAssert(obj->refCount < 20); +#endif + + if (--obj->refCount > 0 && !force) { + return 0; + } + +#if VAR_DEBUG + mprAssert(obj->prev); + mprAssert(obj->next); + mprAssert(obj->next->prev); + mprAssert(obj->prev->next); + obj->next->prev = obj->prev; + obj->prev->next = obj->next; + objectCount--; +#endif + + for (i = 0; i < (int) obj->hashSize; i++) { + for (prop = obj->buckets[i]; prop; prop = forw) { + forw = prop->forw; + if (prop->type == MPR_TYPE_OBJECT) { + + if (prop->properties == obj) { + /* Self reference */ + continue; + } + pp = prop->properties; + if (pp->visited) { + continue; + } + + pp->visited = 1; + if (! freeVar(prop, pp->deleteProtect ? 0 : force)) { + pp->visited = 0; + } + + } else { + freeVar(prop, force); + } + } + } + + mprFree((void*) obj->buckets); + mprFree((void*) obj); + + return 1; +} + +/******************************************************************************/ +/* + * Adjust the reference count + */ + +static int adjustRefCount(MprProperties *pp, int adj) +{ + mprAssert(pp); + + /* + * Debug sanity check + */ + mprAssert(pp->refCount < 20); + + return pp->refCount += adj; +} + +/******************************************************************************/ +#if VAR_DEBUG +/* + * Print objects held + */ + +void mprPrintObjects(char *msg) +{ + MprProperties *pp, *np; + MprVar *prop, *forw; + char *buf; + int i; + + mprLog(7, "%s: Object Store. %d objects.\n", msg, objectCount); + pp = objectList.next; + while (pp != &objectList) { + mprLog(7, "%s: 0x%x, refCount %d, properties %d\n", + pp->name, pp, pp->refCount, pp->numItems); + for (i = 0; i < (int) pp->hashSize; i++) { + for (prop = pp->buckets[i]; prop; prop = forw) { + forw = prop->forw; + if (prop->properties == pp) { + /* Self reference */ + continue; + } + mprVarToString(&buf, MPR_MAX_STRING, 0, prop); + if (prop->type == MPR_TYPE_OBJECT) { + np = objectList.next; + while (np != &objectList) { + if (prop->properties == np) { + break; + } + np = np->next; + } + if (prop->properties == np) { + mprLog(7, " %s: OBJECT 0x%x, <%s>\n", + prop->name, prop->properties, prop->fullName); + } else { + mprLog(7, " %s: OBJECT NOT FOUND, %s <%s>\n", + prop->name, buf, prop->fullName); + } + } else { + mprLog(7, " %s: <%s> = %s\n", prop->name, + prop->fullName, buf); + } + mprFree(buf); + } + } + pp = pp->next; + } +} + +/******************************************************************************/ + +void mprPrintObjRefCount(MprVar *vp) +{ + mprLog(7, "OBJECT 0x%x, refCount %d\n", vp->properties, + vp->properties->refCount); +} + +#endif +/******************************************************************************/ +/* + * Get the bucket chain containing a property. + */ + +static MprVar *getObjChain(MprProperties *obj, const char *property) +{ + mprAssert(obj); + + return obj->buckets[hash(obj, property)]; +} + +/******************************************************************************/ +/* + * Fast hash. The history of this algorithm is part of lost computer science + * folk lore. + */ + +static int hash(MprProperties *pp, const char *property) +{ + uint sum; + + mprAssert(pp); + mprAssert(property); + + sum = 0; + while (*property) { + sum += (sum * 33) + *property++; + } + + return sum % pp->hashSize; +} + +/******************************************************************************/ +/*********************************** Constructors *****************************/ +/******************************************************************************/ +/* + * Initialize an undefined value. + */ + +MprVar mprCreateUndefinedVar() +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_UNDEFINED; + return v; +} + +/******************************************************************************/ +/* + * Initialize an null value. + */ + +MprVar mprCreateNullVar() +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_NULL; + return v; +} + +/******************************************************************************/ + +MprVar mprCreateBoolVar(bool value) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_BOOL; + v.boolean = value; + return v; +} + +/******************************************************************************/ +/* + * Initialize a C function. + */ + +MprVar mprCreateCFunctionVar(MprCFunction fn, void *thisPtr, int flags) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_CFUNCTION; + v.cFunction.fn = fn; + v.cFunction.thisPtr = thisPtr; + v.flags = flags; + + return v; +} + +/******************************************************************************/ +/* + * Initialize a C function. + */ + +MprVar mprCreateStringCFunctionVar(MprStringCFunction fn, void *thisPtr, int flags) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_STRING_CFUNCTION; + v.cFunctionWithStrings.fn = fn; + v.cFunctionWithStrings.thisPtr = thisPtr; + v.flags = flags; + + return v; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Initialize a floating value. + */ + +MprVar mprCreateFloatVar(double value) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_FLOAT; + v.floating = value; + return v; +} + +#endif +/******************************************************************************/ +/* + * Initialize an integer value. + */ + +MprVar mprCreateIntegerVar(int value) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_INT; + v.integer = value; + return v; +} + +/******************************************************************************/ +#if BLD_FEATURE_INT64 +/* + * Initialize a 64-bit integer value. + */ + +MprVar mprCreateInteger64Var(int64 value) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_INT64; + v.integer64 = value; + return v; +} + +#endif /* BLD_FEATURE_INT64 */ +/******************************************************************************/ +/* + * Initialize an number variable. Type is defined by configure. + */ + +MprVar mprCreateNumberVar(MprNum value) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = BLD_FEATURE_NUM_TYPE_ID; +#if BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64 + v.integer64 = value; +#elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT + v.float = value; +#else + v.integer = value; +#endif + return v; +} + +/******************************************************************************/ +/* + * Initialize a (bare) JavaScript function. args and body can be null. + */ + +MprVar mprCreateFunctionVar(char *args, char *body, int flags) +{ + MprVar v; + char *cp, *arg, *last; + int aid; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_FUNCTION; + v.flags = flags; + + v.function.args = mprCreateArray(); + + if (args) { + args = mprStrdup(args); + arg = mprStrTok(args, ",", &last); + while (arg) { + while (isspace((int) *arg)) + arg++; + for (cp = &arg[strlen(arg) - 1]; cp > arg; cp--) { + if (!isspace((int) *cp)) { + break; + } + } + cp[1] = '\0'; + + aid = mprAddToArray(v.function.args, mprStrdup(arg)); + arg = mprStrTok(0, ",", &last); + } + mprFree(args); + } + + if (body) { + v.function.body = mprStrdup(body); + } + v.allocatedData = 1; + return v; +} + +/******************************************************************************/ +/* + * Initialize an object variable. Return type == MPR_TYPE_UNDEFINED if the + * memory allocation for the properties table failed. + */ + +MprVar mprCreateObjVar(const char *name, int hashSize) +{ + MprVar v; + + mprAssert(name && *name); + + memset(&v, 0x0, sizeof(MprVar)); + v.type = MPR_TYPE_OBJECT; + if (hashSize <= 0) { + hashSize = MPR_DEFAULT_HASH_SIZE; + } + v.properties = createProperties(name, hashSize); + if (v.properties == 0) { + /* Indicate failed memory allocation */ + v.type = MPR_TYPE_UNDEFINED; + } + v.allocatedData = 1; + v.name = mprStrdup(name); + mprLog(7, "mprCreateObjVar %s, 0x%x\n", name, v.properties); + return v; +} + +/******************************************************************************/ +/* + * Initialize a string value. + */ + +MprVar mprCreateStringVar(const char *value, bool allocate) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_STRING; + if (value == 0) { + v.string = ""; + } else if (allocate) { + v.string = mprStrdup(value); + v.allocatedData = 1; + } else { + v.string = value; + } + return v; +} + +/******************************************************************************/ +/* + * Copy an objects core value (only). This preserves the destination object's + * name. This implements copy by reference for objects and copy by value for + * strings and other types. Caller must free dest prior to calling. + */ + +static void copyVarCore(MprVar *dest, MprVar *src, int copyDepth) +{ + MprVarTrigger saveTrigger; + MprVar *srcProp, *destProp, *last; + char **srcArgs; + int i; + + mprAssert(dest); + mprAssert(src); + + if (dest == src) { + return; + } + + /* + * FUTURE: we should allow read-only triggers where the value is never + * stored in the object. Currently, triggers override the readonly + * status. + */ + + if (dest->type != MPR_TYPE_UNDEFINED && dest->readonly && !dest->trigger) { + mprAssert(0); + return; + } + + if (dest->type != MPR_TYPE_UNDEFINED) { + saveTrigger = dest->trigger; + freeVarStorage(dest, 0); + } else { + saveTrigger = 0; + } + + switch (src->type) { + default: + case MPR_TYPE_UNDEFINED: + case MPR_TYPE_NULL: + break; + + case MPR_TYPE_BOOL: + dest->boolean = src->boolean; + break; + + case MPR_TYPE_STRING_CFUNCTION: + dest->cFunctionWithStrings = src->cFunctionWithStrings; + break; + + case MPR_TYPE_CFUNCTION: + dest->cFunction = src->cFunction; + break; + +#if BLD_FEATURE_FLOATING_POINT + case MPR_TYPE_FLOAT: + dest->floating = src->floating; + break; +#endif + + case MPR_TYPE_INT: + dest->integer = src->integer; + break; + +#if BLD_FEATURE_INT64 + case MPR_TYPE_INT64: + dest->integer64 = src->integer64; + break; +#endif + + case MPR_TYPE_OBJECT: + if (copyDepth == MPR_DEEP_COPY) { + + dest->properties = createProperties(src->name, + src->properties->hashSize); + dest->allocatedData = 1; + + for (i = 0; i < (int) src->properties->hashSize; i++) { + last = 0; + for (srcProp = src->properties->buckets[i]; srcProp; + srcProp = srcProp->forw) { + if (srcProp->visited) { + continue; + } + destProp = allocProperty(srcProp->name); + if (destProp == 0) { + mprAssert(destProp); + return; + } + + destProp->bucketIndex = i; + if (last) { + last->forw = destProp; + } else { + dest->properties->buckets[i] = destProp; + } + destProp->parentProperties = dest->properties; + + /* + * Recursively copy the object + */ + srcProp->visited = 1; + copyVarCore(destProp, srcProp, copyDepth); + srcProp->visited = 0; + last = srcProp; + } + } + dest->properties->numItems = src->properties->numItems; + dest->properties->numDataItems = src->properties->numDataItems; + dest->allocatedData = 1; + + } else if (copyDepth == MPR_SHALLOW_COPY) { + dest->properties = src->properties; + adjustVarRefCount(src, 1); + dest->allocatedData = 1; + + } else { + dest->properties = src->properties; + dest->allocatedData = 0; + } + break; + + case MPR_TYPE_FUNCTION: + if (copyDepth != MPR_NO_COPY) { + dest->function.args = mprCreateArray(); + srcArgs = (char**) src->function.args->handles; + for (i = 0; i < src->function.args->max; i++) { + if (srcArgs[i]) { + mprAddToArray(dest->function.args, mprStrdup(srcArgs[i])); + } + } + dest->function.body = mprStrdup(src->function.body); + dest->allocatedData = 1; + } else { + dest->function.args = src->function.args; + dest->function.body = src->function.body; + dest->allocatedData = 0; + } + break; + + case MPR_TYPE_STRING: + if (src->string && copyDepth != MPR_NO_COPY) { + dest->string = mprStrdup(src->string); + dest->allocatedData = 1; + } else { + dest->string = src->string; + dest->allocatedData = 0; + } + break; + } + + dest->type = src->type; + dest->flags = src->flags; + dest->trigger = saveTrigger; + + /* + * Just for safety + */ + dest->spare = 0; +} + +/******************************************************************************/ +/* + * Copy an entire object including name. + */ + +void mprCopyVar(MprVar *dest, MprVar *src, int copyDepth) +{ + mprAssert(dest); + mprAssert(src); + + copyVarCore(dest, src, copyDepth); + + mprFree(dest->name); + dest->name = mprStrdup(src->name); + +#if VAR_DEBUG + if (src->type == MPR_TYPE_OBJECT) { + + mprFree(dest->fullName); + dest->fullName = mprStrdup(src->fullName); + + mprLog(7, "mprCopyVar: object \"%s\", FDQ \"%s\" 0x%x, refCount %d\n", + dest->name, dest->fullName, dest->properties, + dest->properties->refCount); + } +#endif +} + +/******************************************************************************/ +/* + * Copy an entire object including name. + */ + +void mprCopyVarValue(MprVar *dest, MprVar src, int copyDepth) +{ + mprAssert(dest); + + mprCopyVar(dest, &src, copyDepth); +} + +/******************************************************************************/ +/* + * Copy an object. This implements copy by reference for objects and copy by + * value for strings and other types. Caller must free dest prior to calling. + */ + +MprVar *mprDupVar(MprVar *src, int copyDepth) +{ + MprVar *dest; + + mprAssert(src); + + dest = (MprVar*) mprMalloc(sizeof(MprVar)); + memset(dest, 0, sizeof(MprVar)); + + mprCopyVar(dest, src, copyDepth); + return dest; +} + +/******************************************************************************/ +/* + * Convert a value to a text based representation of its value + * FUTURE -- conver this to use the format string in all cases. Allow + * arbitrary format strings. + */ + +void mprVarToString(char** out, int size, char *fmt, MprVar *obj) +{ + char *src; + + mprAssert(out); + + *out = NULL; + + if (obj->trigger) { + mprReadProperty(obj, 0); + } + + switch (obj->type) { + case MPR_TYPE_UNDEFINED: + // FUTURE -- spec says convert to "undefined" + *out = mprStrdup(""); + break; + + case MPR_TYPE_NULL: + *out = mprStrdup("null"); + break; + + case MPR_TYPE_BOOL: + if (obj->boolean) { + *out = mprStrdup("true"); + } else { + *out = mprStrdup("false"); + } + break; + +#if BLD_FEATURE_FLOATING_POINT + case MPR_TYPE_FLOAT: + if (fmt == NULL || *fmt == '\0') { + mprAllocSprintf(out, size, "%f", obj->floating); + } else { + mprAllocSprintf(out, size, fmt, obj->floating); + } + break; +#endif + + case MPR_TYPE_INT: + if (fmt == NULL || *fmt == '\0') { + mprAllocSprintf(out, size, "%d", obj->integer); + } else { + mprAllocSprintf(out, size, fmt, obj->integer); + } + break; + +#if BLD_FEATURE_INT64 + case MPR_TYPE_INT64: + if (fmt == NULL || *fmt == '\0') { +#if BLD_GOAHEAD_WEBSERVER + mprAllocSprintf(out, size, "%d", (int) obj->integer64); +#else + mprAllocSprintf(out, size, "%Ld", obj->integer64); +#endif + } else { + mprAllocSprintf(out, size, fmt, obj->integer64); + } + break; +#endif + + case MPR_TYPE_CFUNCTION: + mprAllocSprintf(out, size, "[C Function]"); + break; + + case MPR_TYPE_STRING_CFUNCTION: + mprAllocSprintf(out, size, "[C StringFunction]"); + break; + + case MPR_TYPE_FUNCTION: + mprAllocSprintf(out, size, "[JavaScript Function]"); + break; + + case MPR_TYPE_OBJECT: + // FUTURE -- really want: [object class: name] + mprAllocSprintf(out, size, "[object %s]", obj->name); + break; + + case MPR_TYPE_STRING: + src = obj->string; + + mprAssert(src); + if (fmt && *fmt) { + mprAllocSprintf(out, size, fmt, src); + + } else if (src == NULL) { + *out = mprStrdup("null"); + + } else { + *out = mprStrdup(src); + } + break; + + default: + mprAssert(0); + } +} + +/******************************************************************************/ +/* + * Parse a string based on formatting instructions and intelligently + * create a variable. + */ + +MprVar mprParseVar(char *buf, MprType preferredType) +{ + MprType type; + char *cp; + + mprAssert(buf); + + if (preferredType == MPR_TYPE_UNDEFINED) { + if (*buf == '-') { + type = MPR_NUM_VAR; + + } else if (!isdigit((int) *buf)) { + if (strcmp(buf, "true") == 0 || strcmp(buf, "false") == 0) { + type = MPR_TYPE_BOOL; + } else { + type = MPR_TYPE_STRING; + } + + } else if (isdigit((int) *buf)) { + type = MPR_NUM_VAR; + cp = buf; + if (*cp && tolower(cp[1]) == 'x') { + cp = &cp[2]; + } + for (cp = buf; *cp; cp++) { + if (! isdigit((int) *cp)) { + break; + } + } + + if (*cp != '\0') { +#if BLD_FEATURE_FLOATING_POINT + if (*cp == '.' || tolower(*cp) == 'e') { + type = MPR_TYPE_FLOAT; + } else +#endif + { + type = MPR_NUM_VAR; + } + } + } + } else { + type = preferredType; + } + + switch (type) { + case MPR_TYPE_OBJECT: + case MPR_TYPE_UNDEFINED: + case MPR_TYPE_NULL: + default: + break; + + case MPR_TYPE_BOOL: + return mprCreateBoolVar(buf[0] == 't' ? 1 : 0); + + case MPR_TYPE_INT: + return mprCreateIntegerVar(mprParseInteger(buf)); + +#if BLD_FEATURE_INT64 + case MPR_TYPE_INT64: + return mprCreateInteger64Var(mprParseInteger64(buf)); +#endif + + case MPR_TYPE_STRING: + if (strcmp(buf, "null") == 0) { + return mprCreateNullVar(); + } else if (strcmp(buf, "undefined") == 0) { + return mprCreateUndefinedVar(); + } + + return mprCreateStringVar(buf, 1); + +#if BLD_FEATURE_FLOATING_POINT + case MPR_TYPE_FLOAT: + return mprCreateFloatVar(atof(buf)); +#endif + + } + return mprCreateUndefinedVar(); +} + +/******************************************************************************/ +/* + * Convert the variable to a boolean. Only for primitive types. + */ + +bool mprVarToBool(MprVar *vp) +{ + mprAssert(vp); + + switch (vp->type) { + case MPR_TYPE_UNDEFINED: + case MPR_TYPE_NULL: + case MPR_TYPE_STRING_CFUNCTION: + case MPR_TYPE_CFUNCTION: + case MPR_TYPE_FUNCTION: + case MPR_TYPE_OBJECT: + return 0; + + case MPR_TYPE_BOOL: + return vp->boolean; + +#if BLD_FEATURE_FLOATING_POINT + case MPR_TYPE_FLOAT: + return (vp->floating != 0 && !mprIsNan(vp->floating)); +#endif + + case MPR_TYPE_INT: + return (vp->integer != 0); + +#if BLD_FEATURE_INT64 + case MPR_TYPE_INT64: + return (vp->integer64 != 0); +#endif + + case MPR_TYPE_STRING: + mprAssert(vp->string); + return (vp->string[0] != '\0'); + } + + /* Not reached */ + return 0; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Convert the variable to a floating point number. Only for primitive types. + */ + +double mprVarToFloat(MprVar *vp) +{ + mprAssert(vp); + + switch (vp->type) { + case MPR_TYPE_UNDEFINED: + case MPR_TYPE_NULL: + case MPR_TYPE_STRING_CFUNCTION: + case MPR_TYPE_CFUNCTION: + case MPR_TYPE_FUNCTION: + case MPR_TYPE_OBJECT: + return 0; + + case MPR_TYPE_BOOL: + return (vp->boolean) ? 1.0 : 0.0; + + case MPR_TYPE_FLOAT: + return vp->floating; + + case MPR_TYPE_INT: + return (double) vp->integer; + +#if BLD_FEATURE_INT64 + case MPR_TYPE_INT64: + return (double) vp->integer64; +#endif + + case MPR_TYPE_STRING: + mprAssert(vp->string); + return atof(vp->string); + } + + /* Not reached */ + return 0; +} + +#endif +/******************************************************************************/ +/* + * Convert the variable to a number type. Only works for primitive types. + */ + +MprNum mprVarToNumber(MprVar *vp) +{ +#if BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64 + return mprVarToInteger64(vp); +#elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT + return mprVarToFloat(vp); +#else + return mprVarToInteger(vp); +#endif +} + +/******************************************************************************/ +/* + * Convert the variable to a number type. Only works for primitive types. + */ + +MprNum mprParseNumber(char *s) +{ +#if BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64 + return mprParseInteger64(s); +#elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT + return mprParseFloat(s); +#else + return mprParseInteger(s); +#endif +} + +/******************************************************************************/ +#if BLD_FEATURE_INT64 +/* + * Convert the variable to an Integer64 type. Only works for primitive types. + */ + +int64 mprVarToInteger64(MprVar *vp) +{ + mprAssert(vp); + + switch (vp->type) { + case MPR_TYPE_UNDEFINED: + case MPR_TYPE_NULL: + case MPR_TYPE_STRING_CFUNCTION: + case MPR_TYPE_CFUNCTION: + case MPR_TYPE_FUNCTION: + case MPR_TYPE_OBJECT: + return 0; + + case MPR_TYPE_BOOL: + return (vp->boolean) ? 1 : 0; + +#if BLD_FEATURE_FLOATING_POINT + case MPR_TYPE_FLOAT: + if (mprIsNan(vp->floating)) { + return 0; + } + return (int64) vp->floating; +#endif + + case MPR_TYPE_INT: + return vp->integer; + + case MPR_TYPE_INT64: + return vp->integer64; + + case MPR_TYPE_STRING: + return mprParseInteger64(vp->string); + } + + /* Not reached */ + return 0; +} + +/******************************************************************************/ +/* + * Convert the string buffer to an Integer64. + */ + +int64 mprParseInteger64(char *str) +{ + char *cp; + int64 num64; + int radix, c, negative; + + mprAssert(str); + + cp = str; + num64 = 0; + negative = 0; + + if (*cp == '-') { + cp++; + negative = 1; + } + + /* + * Parse a number. Observe hex and octal prefixes (0x, 0) + */ + if (*cp != '0') { + /* + * Normal numbers (Radix 10) + */ + while (isdigit((int) *cp)) { + num64 = (*cp - '0') + (num64 * 10); + cp++; + } + } else { + cp++; + if (tolower(*cp) == 'x') { + cp++; + radix = 16; + while (*cp) { + c = tolower(*cp); + if (isdigit(c)) { + num64 = (c - '0') + (num64 * radix); + } else if (c >= 'a' && c <= 'f') { + num64 = (c - 'a') + (num64 * radix); + } else { + break; + } + cp++; + } + + } else{ + radix = 8; + while (*cp) { + c = tolower(*cp); + if (isdigit(c) && c < '8') { + num64 = (c - '0') + (num64 * radix); + } else { + break; + } + cp++; + } + } + } + + if (negative) { + return 0 - num64; + } + return num64; +} + +#endif /* BLD_FEATURE_INT64 */ +/******************************************************************************/ +/* + * Convert the variable to an Integer type. Only works for primitive types. + */ + +int mprVarToInteger(MprVar *vp) +{ + mprAssert(vp); + + switch (vp->type) { + case MPR_TYPE_UNDEFINED: + case MPR_TYPE_NULL: + case MPR_TYPE_STRING_CFUNCTION: + case MPR_TYPE_CFUNCTION: + case MPR_TYPE_FUNCTION: + case MPR_TYPE_OBJECT: + return 0; + + case MPR_TYPE_BOOL: + return (vp->boolean) ? 1 : 0; + +#if BLD_FEATURE_FLOATING_POINT + case MPR_TYPE_FLOAT: + if (mprIsNan(vp->floating)) { + return 0; + } + return (int) vp->floating; +#endif + + case MPR_TYPE_INT: + return vp->integer; + +#if BLD_FEATURE_INT64 + case MPR_TYPE_INT64: + return (int) vp->integer64; +#endif + + case MPR_TYPE_STRING: + return mprParseInteger(vp->string); + } + + /* Not reached */ + return 0; +} + +/******************************************************************************/ +/* + * Convert the string buffer to an Integer. + */ + +int mprParseInteger(char *str) +{ + char *cp; + int num; + int radix, c, negative; + + mprAssert(str); + + cp = str; + num = 0; + negative = 0; + + if (*cp == '-') { + cp++; + negative = 1; + } + + /* + * Parse a number. Observe hex and octal prefixes (0x, 0) + */ + if (*cp != '0') { + /* + * Normal numbers (Radix 10) + */ + while (isdigit((int) *cp)) { + num = (*cp - '0') + (num * 10); + cp++; + } + } else { + cp++; + if (tolower(*cp) == 'x') { + cp++; + radix = 16; + while (*cp) { + c = tolower(*cp); + if (isdigit(c)) { + num = (c - '0') + (num * radix); + } else if (c >= 'a' && c <= 'f') { + num = (c - 'a') + (num * radix); + } else { + break; + } + cp++; + } + + } else{ + radix = 8; + while (*cp) { + c = tolower(*cp); + if (isdigit(c) && c < '8') { + num = (c - '0') + (num * radix); + } else { + break; + } + cp++; + } + } + } + + if (negative) { + return 0 - num; + } + return num; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Convert the string buffer to an Floating. + */ + +double mprParseFloat(char *str) +{ + return atof(str); +} + +/******************************************************************************/ + +bool mprIsNan(double f) +{ +#if WIN + return _isnan(f); +#elif VXWORKS + // FUTURE + return (0); +#else + return (f == FP_NAN); +#endif +} +/******************************************************************************/ + +bool mprIsInfinite(double f) +{ +#if WIN + return !_finite(f); +#elif VXWORKS + // FUTURE + return (0); +#else + return (f == FP_INFINITE); +#endif +} + +#endif // BLD_FEATURE_FLOATING_POINT +/******************************************************************************/ + +/* + * 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 + */ diff --git a/source4/web_server/ejs/var.h b/source4/web_server/ejs/var.h new file mode 100644 index 0000000000..2e8fdf6b58 --- /dev/null +++ b/source4/web_server/ejs/var.h @@ -0,0 +1,482 @@ +/* + * @file var.h + * @brief MPR Universal Variable Type + * @copy default.m + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved. + * Copyright (c) Michael O'Brien, 1994-1995. 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 + */ + +/******************************* Documentation ********************************/ +/* + * Variables can efficiently store primitive types and can hold references to + * objects. Objects can store properties which are themselves variables. + * Properties can be primitive data types, other objects or functions. + * Properties are indexed by a character name. A variable may store one of + * the following types: + * + * string, integer, integer-64bit, C function, C function with string args, + * Javascript function, Floating point number, boolean value, Undefined + * value and the Null value. + * + * Variables have names while objects may be referenced by multiple variables. + * Objects use reference counting for garbage collection. + * + * This module is not thread safe for performance and compactness. It relies + * on upper modules to provide thread synchronization as required. The API + * provides primitives to get variable/object references or to get copies of + * variables which will help minimize required lock times. + */ + +#ifndef _h_MPR_VAR +#define _h_MPR_VAR 1 + +/********************************* Includes ***********************************/ + +#include "web_server/ejs/miniMpr.h" + +/********************************** Defines ***********************************/ + +/* + * Define VAR_DEBUG if you want to track objects. However, this code is not + * thread safe and you need to run the server single threaded. + * + * #define VAR_DEBUG 1 + */ + +#ifdef __cplusplus +extern "C" { +#endif +/* + * Forward declare types + */ +struct MprProperties; +struct MprVar; + +/* + * Possible variable types. Don't use enum because we need to be able to + * do compile time conditional compilation on BLD_FEATURE_NUM_TYPE_ID. + */ +typedef int MprType; +#define MPR_TYPE_UNDEFINED 0 ///< Undefined. No value has been set. +#define MPR_TYPE_NULL 1 ///< Value defined to be null. +#define MPR_TYPE_BOOL 2 ///< Boolean type. +#define MPR_TYPE_CFUNCTION 3 ///< C function or C++ method +#define MPR_TYPE_FLOAT 4 ///< Floating point number +#define MPR_TYPE_INT 5 ///< Integer number +#define MPR_TYPE_INT64 6 ///< 64-bit Integer number +#define MPR_TYPE_OBJECT 7 ///< Object reference +#define MPR_TYPE_FUNCTION 8 ///< JavaScript function +#define MPR_TYPE_STRING 9 ///< String (immutable) +#define MPR_TYPE_STRING_CFUNCTION 10 ///< C/C++ function with string args + +/* + * Create a type for the default number type + * Config.h will define the default number type. For example: + * + * BLD_FEATURE_NUM_TYPE=int + * BLD_FEATURE_NUM_TYPE_ID=MPR_TYPE_INT + */ + +/** + * Set to the type used for MPR numeric variables. Will equate to int, int64 + * or double. + */ +typedef BLD_FEATURE_NUM_TYPE MprNum; + +/** + * Set to the MPR_TYPE used for MPR numeric variables. Will equate to + * MPR_TYPE_INT, MPR_TYPE_INT64 or MPR_TYPE_FLOAT. + */ +#define MPR_NUM_VAR BLD_FEATURE_NUM_TYPE_ID +#define MPR_TYPE_NUM BLD_FEATURE_NUM_TYPE_ID + +/* + * Return TRUE if a variable is a function type + */ +#define mprVarIsFunction(type) \ + (type == MPR_TYPE_FUNCTION || type == MPR_TYPE_STRING_CFUNCTION || \ + type == MPR_TYPE_CFUNCTION) + +/* + * Return TRUE if a variable is a numeric type + */ +#define mprVarIsNumber(type) \ + (type == MPR_TYPE_INT || type == MPR_TYPE_INT64 || type == MPR_TYPE_FLOAT) + +/* + * Return TRUE if a variable is a boolean + */ +#define mprVarIsBoolean(type) \ + (type == MPR_TYPE_BOOL) +#define mprVarIsString(type) \ + (type == MPR_TYPE_STRING) +#define mprVarIsObject(type) \ + (type == MPR_TYPE_OBJECT) +#define mprVarIsFloating(type) \ + (type == MPR_TYPE_FLOAT) +#define mprVarIsUndefined(var) \ + ((var)->type == MPR_TYPE_UNDEFINED) +#define mprVarIsNull(var) \ + ((var)->type == MPR_TYPE_NULL) +#define mprVarIsValid(var) \ + (((var)->type != MPR_TYPE_NULL) && ((var)->type != MPR_TYPE_UNDEFINED)) + +#define MPR_VAR_MAX_RECURSE 5 /* Max object loops */ + +#if BLD_FEATURE_SQUEEZE +#define MPR_MAX_VAR 64 /* Max var full name */ +#else +#define MPR_MAX_VAR 512 +#endif + +#ifndef __NO_PACK +#pragma pack(2) +#endif /* _NO_PACK */ + +/* + * Function signatures + */ +typedef int MprVarHandle; +typedef int (*MprCFunction)(MprVarHandle userHandle, int argc, + struct MprVar **argv); +typedef int (*MprStringCFunction)(MprVarHandle userHandle, int argc, + char **argv); + +/* + * Triggers + */ +typedef enum { + MPR_VAR_WRITE, /* This property is being updated */ + MPR_VAR_READ, /* This property is being read */ + MPR_VAR_CREATE_PROPERTY, /* A property is being created */ + MPR_VAR_DELETE_PROPERTY, /* A property is being deleted */ + MPR_VAR_DELETE /* This object is being deleted */ +} MprVarTriggerOp; + +/* + * Trigger function return codes. + */ +typedef enum { + MPR_TRIGGER_ABORT, /* Abort the current operation */ + MPR_TRIGGER_USE_NEW_VALUE, /* Proceed and use the newValue */ + MPR_TRIGGER_PROCEED /* Proceed with the operation */ +} MprVarTriggerStatus; + +/* + * The MprVarTrigger arguments have the following meaning: + * + * op The operation being performed. See MprVarTriggerOp. + * parentProperties Pointer to the MprProperties structure. + * vp Pointer to the property that registered the trigger. + * newValue New value (see below for more details). + * copyDepth Specify what data items to copy. + * + * For VAR_READ, newVar is set to a temporary variable that the trigger + * function may assign a value to be returned instead of the actual + * property value. + * For VAR_WRITE, newValue holds the new value. The old existing value may be + * accessed via vp. + * For DELETE_PROPERTY, vp is the property being deleted. newValue is null. + * For ADD_PROPERTY, vp is set to the property being added and newValue holds + * the new value. + */ +typedef MprVarTriggerStatus (*MprVarTrigger)(MprVarTriggerOp op, + struct MprProperties *parentProperties, struct MprVar *vp, + struct MprVar *newValue, int copyDepth); + +/* + * mprCreateFunctionVar flags + */ +/** Use the alternate handle on function callbacks */ +#define MPR_VAR_ALT_HANDLE 0x1 + +/** Use the script handle on function callbacks */ +#define MPR_VAR_SCRIPT_HANDLE 0x2 + +/* + * Useful define for the copyDepth argument + */ +/** Don't copy any data. Copy only the variable name */ +#define MPR_NO_COPY 0 + +/** Copy strings. Increment object reference counts. */ +#define MPR_SHALLOW_COPY 1 + +/** Copy strings and do complete object copies. */ +#define MPR_DEEP_COPY 2 + +/* + * GetFirst / GetNext flags + */ +/** Step into data properties. */ +#define MPR_ENUM_DATA 0x1 + +/** Step into functions properties. */ +#define MPR_ENUM_FUNCTIONS 0x2 + +/* + * Collection type to hold properties in an object + */ +typedef struct MprProperties { /* Collection of properties */ +#if VAR_DEBUG + struct MprProperties *next; /* Linked list */ + struct MprProperties *prev; /* Linked list */ + char name[32]; /* Debug name */ +#endif + struct MprVar **buckets; /* Hash chains */ + int numItems; /* Total count of items */ + int numDataItems; /* Enumerable data items */ + uint hashSize : 8; /* Size of the hash table */ + uint refCount : 8; /* References to this property*/ + uint deleteProtect : 8; /* Don't recursively delete */ + uint visited : 8; /* Node has been processed */ +} MprProperties; + +/* + * Universal Variable Type + */ +typedef struct MprVar { + MprStr name; /* Property name */ + MprStr fullName; /* Full object name */ + MprProperties *properties; /* Pointer to properties */ + + /* + * Packed bit field + */ + MprType type : 8; /* Selector into union */ + uint bucketIndex : 8; /* Copy of bucket index */ + + uint flags : 5; /* Type specific flags */ + uint allocatedData : 1; /* Data needs freeing */ + uint readonly : 1; /* Unmodifiable */ + uint deleteProtect : 1; /* Don't recursively delete */ + + uint visited : 1; /* Node has been processed */ + uint allocatedVar : 1; /* Var needs freeing */ + uint spare : 6; /* Unused */ + + struct MprVar *forw; /* Hash table linkage */ + MprVarTrigger trigger; /* Trigger function */ + +#if UNUSED && KEEP + struct MprVar *baseClass; /* Pointer to class object */ +#endif + MprProperties *parentProperties; /* Pointer to parent object */ + + /* + * Union of primitive types. When debugging on Linux, don't use unions + * as the gdb debugger can't display them. + */ +#if !BLD_DEBUG && !LINUX && !VXWORKS + union { +#endif + int boolean; /* Use int for speed */ +#if BLD_FEATURE_FLOATING_POINT + double floating; +#endif + int integer; +#if BLD_FEATURE_INT64 + int64 integer64; +#endif + struct { /* Javascript functions */ + MprArray *args; /* Null terminated */ + char *body; + } function; + struct { /* Function with MprVar args */ + MprCFunction fn; + void *thisPtr; + } cFunction; + struct { /* Function with string args */ + MprStringCFunction fn; + void *thisPtr; + } cFunctionWithStrings; + MprStr string; /* Allocated string */ +#if !BLD_DEBUG && !LINUX && !VXWORKS + }; +#endif +} MprVar; + +/* + * Define a field macro so code an use numbers in a "generic" fashion. + */ +#if MPR_NUM_VAR == MPR_TYPE_INT || DOXYGEN +//* Default numeric type */ +#define mprNumber integer +#endif +#if MPR_NUM_VAR == MPR_TYPE_INT64 +//* Default numeric type */ +#define mprNumber integer64 +#endif +#if MPR_NUM_VAR == MPR_TYPE_FLOAT +//* Default numeric type */ +#define mprNumber floating +#endif + +typedef BLD_FEATURE_NUM_TYPE MprNumber; + + +#ifndef __NO_PACK +#pragma pack() +#endif /* __NO_PACK */ + +/********************************* Prototypes *********************************/ +/* + * Variable constructors and destructors + */ +extern MprVar mprCreateObjVar(const char *name, int hashSize); +extern MprVar mprCreateBoolVar(bool value); +extern MprVar mprCreateCFunctionVar(MprCFunction fn, void *thisPtr, + int flags); +#if BLD_FEATURE_FLOATING_POINT +extern MprVar mprCreateFloatVar(double value); +#endif +extern MprVar mprCreateIntegerVar(int value); +#if BLD_FEATURE_INT64 +extern MprVar mprCreateInteger64Var(int64 value); +#endif +extern MprVar mprCreateFunctionVar(char *args, char *body, int flags); +extern MprVar mprCreateNullVar(void); +extern MprVar mprCreateNumberVar(MprNumber value); +extern MprVar mprCreateStringCFunctionVar(MprStringCFunction fn, + void *thisPtr, int flags); +extern MprVar mprCreateStringVar(const char *value, bool allocate); +extern MprVar mprCreateUndefinedVar(void); +extern bool mprDestroyVar(MprVar *vp); +extern bool mprDestroyAllVars(MprVar* vp); +extern MprType mprGetVarType(MprVar *vp); + +/* + * Copy + */ +extern void mprCopyVar(MprVar *dest, MprVar *src, int copyDepth); +extern void mprCopyVarValue(MprVar *dest, MprVar src, int copyDepth); +extern MprVar *mprDupVar(MprVar *src, int copyDepth); + +/* + * Manage vars + */ +extern MprVarTrigger + mprAddVarTrigger(MprVar *vp, MprVarTrigger fn); +extern int mprGetVarRefCount(MprVar *vp); +extern void mprSetVarDeleteProtect(MprVar *vp, int deleteProtect); +extern void mprSetVarFullName(MprVar *vp, char *name); +extern void mprSetVarReadonly(MprVar *vp, int readonly); +extern void mprSetVarName(MprVar *vp, char *name); + +/* + * Create properties and return a reference to the property. + */ +extern MprVar *mprCreateProperty(MprVar *obj, const char *property, + MprVar *newValue); +extern MprVar *mprCreatePropertyValue(MprVar *obj, const char *property, + MprVar newValue); +extern int mprDeleteProperty(MprVar *obj, const char *property); + +/* + * Get/Set properties. Set will update/create. + */ +extern MprVar *mprGetProperty(MprVar *obj, const char *property, MprVar *value); +extern MprVar *mprSetProperty(MprVar *obj, const char *property, MprVar *value); +extern MprVar *mprSetPropertyValue(MprVar *obj, const char *property, MprVar value); + +/* + * Directly read/write property values (the property must already exist) + * For mprCopyProperty, mprDestroyVar must always called on the var. + */ +extern int mprReadProperty(MprVar *prop, MprVar *value); +extern int mprWriteProperty(MprVar *prop, MprVar *newValue); +extern int mprWritePropertyValue(MprVar *prop, MprVar newValue); + +/* + * Copy a property. NOTE: reverse of most other args: (dest, src) + */ +extern int mprCopyProperty(MprVar *dest, MprVar *prop, int copyDepth); + +/* + * Enumerate properties + */ +extern MprVar *mprGetFirstProperty(MprVar *obj, int includeFlags); +extern MprVar *mprGetNextProperty(MprVar *obj, MprVar *currentProperty, + int includeFlags); + +/* + * Query properties characteristics + */ +extern int mprGetPropertyCount(MprVar *obj, int includeFlags); + +/* + * Conversion routines + */ +extern MprVar mprParseVar(char *str, MprType prefType); +extern MprNum mprVarToNumber(MprVar *vp); +extern int mprVarToInteger(MprVar *vp); +#if BLD_FEATURE_INT64 +extern int64 mprVarToInteger64(MprVar *vp); +#endif +extern bool mprVarToBool(MprVar *vp); +#if BLD_FEATURE_FLOATING_POINT +extern double mprVarToFloat(MprVar *vp); +#endif +extern void mprVarToString(char** buf, int size, char *fmt, MprVar *vp); + +/* + * Parsing and utility routines + */ +extern MprNum mprParseNumber(char *str); +extern int mprParseInteger(char *str); + +#if BLD_FEATURE_INT64 +extern int64 mprParseInteger64(char *str); +#endif + +#if BLD_FEATURE_FLOATING_POINT +extern double mprParseFloat(char *str); +extern bool mprIsInfinite(double f); +extern bool mprIsNan(double f); +#endif + +#if VAR_DEBUG +extern void mprPrintObjects(char *msg); +extern void mprPrintObjRefCount(MprVar *vp); +#endif + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ +#endif /* _h_MPR_VAR */ + +/* + * 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 + */ diff --git a/source4/web_server/esp/esp.c b/source4/web_server/esp/esp.c new file mode 100644 index 0000000000..26c37385dd --- /dev/null +++ b/source4/web_server/esp/esp.c @@ -0,0 +1,1042 @@ +/* + * @file esp.c + * @brief Embedded Server Pages (ESP) core processing. + * @overview The ESP handler provides an efficient way to generate + * dynamic pages using server-side Javascript. This code provides + * core processing, and should be called by an associated web + * server URL handler. + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 "esp.h" + +#if BLD_FEATURE_ESP_MODULE + +/*********************************** Locals ***********************************/ +/* + * Master ESP control interface with the web server + */ + +static Esp *esp; + +/***************************** Forward Declarations ***************************/ + +static int buildScript(EspRequest *ep, char **jsBuf, char *input, char + **errMsg); + +/************************************ Code ************************************/ +/* + * Called at server initialization + */ + +int espOpen(Esp *control) +{ + mprAssert(control); + +#if BLD_FEATURE_MULTITHREAD + ejsOpen(control->lock, control->unlock, control->lockData); +#else + ejsOpen(0, 0, 0); +#endif + + /* + * Register the standard procedures + */ + espRegisterProcs(); + + /* + * Just for brain dead systems that don't zero global memory + */ + esp = control; + return 0; +} + +/******************************************************************************/ +/* + * Called at server termination + */ + +void espClose() +{ + ejsClose(); +} + +/******************************************************************************/ +/* + * Create for new ESP request. Assumed that this is called after all the + * HTTP headers have been read but before POST data has been read. It is + * expected that any session cookies have been read and that "variables" + * contains references to all the environment objects including "session". + * requestHandle is the web server request handle. + */ + +EspRequest *espCreateRequest(EspHandle webServerRequestHandle, char *uri, + MprVar *variables) +{ + EspRequest *ep; + MprVar *global; +#if BLD_FEATURE_LEGACY_API + MprVar *np; + char keyBuf[ESP_MAX_HEADER]; + int i; +#endif + + mprAssert(variables); + + ep = mprMalloc(sizeof(EspRequest)); + if (ep == 0) { + return 0; + } + memset(ep, 0, sizeof(EspRequest)); + ep->requestHandle = webServerRequestHandle; + ep->esp = esp; + ep->uri = mprStrdup(uri); + ep->docPath = 0; + ep->variables = variables; + + /* + * The handle passed to ejsOpenEngine is passed to every C function + * called by JavaScript. + */ + ep->eid = ejsOpenEngine((EjsHandle) ep, (EjsHandle) webServerRequestHandle); + if (ep->eid < 0) { + mprFree(ep); + return 0; + } + + /* + * All these copies and SetProperties will only copy references + * They will increments the object ref counts. + */ + mprCopyVar(&variables[ESP_GLOBAL_OBJ], ejsGetGlobalObject(ep->eid), + MPR_SHALLOW_COPY); + mprCopyVar(&variables[ESP_LOCAL_OBJ], ejsGetLocalObject(ep->eid), + MPR_SHALLOW_COPY); + + global = &variables[ESP_GLOBAL_OBJ]; + mprCreateProperty(global, "application", &variables[ESP_APPLICATION_OBJ]); + mprCreateProperty(global, "cookies", &variables[ESP_COOKIES_OBJ]); + mprCreateProperty(global, "files", &variables[ESP_FILES_OBJ]); + mprCreateProperty(global, "form", &variables[ESP_FORM_OBJ]); + mprCreateProperty(global, "headers", &variables[ESP_HEADERS_OBJ]); + mprCreateProperty(global, "request", &variables[ESP_REQUEST_OBJ]); + + // + // FUTURE -- could server be shared across all requests for a given host + // and be made read-only. + // + mprCreateProperty(global, "server", &variables[ESP_SERVER_OBJ]); + +#if BLD_FEATURE_SESSION + mprCreateProperty(global, "session", &variables[ESP_SESSION_OBJ]); +#endif + +#if BLD_FEATURE_LEGACY_API + /* + * DEPRECATED: 2.0 + * Define variables as globals. headers[] are prefixed with "HTTP_". + * NOTE: MaRequest::setVar does not copy into globals, whereas espSetVar + * does if legacy_api is defined. So variables pre-defined by MaRequest + * must be copied here into globals[].
+ *
+ * NOTE: if a variable is in session[] and in form[], the form[] will
+ * override being later in the variables[] list. Use mprSetProperty
+ * instead of mprCreateProperty to cover for this case. + */ + for (i = 0; i < ESP_OBJ_MAX; i++) { + if (i == ESP_GLOBAL_OBJ || i == ESP_LOCAL_OBJ) { + continue; + } + if (variables[i].type != MPR_TYPE_OBJECT) { + continue; + } + np = mprGetFirstProperty(&variables[i], MPR_ENUM_DATA); + while (np) { + if (i == ESP_HEADERS_OBJ) { + mprSprintf(keyBuf, sizeof(keyBuf) - 1, "HTTP_%s", np->name); + mprSetProperty(global, keyBuf, np); + } else { + mprSetProperty(global, np->name, np); + } + np = mprGetNextProperty(&variables[i], np, MPR_ENUM_DATA); + } + } +#endif + return ep; +} + +/******************************************************************************/ + +void espDestroyRequest(EspRequest *ep) +{ + mprAssert(ep); + mprAssert(ep->eid >= 0); + + mprFree(ep->uri); + mprFree(ep->docPath); + ejsCloseEngine(ep->eid); + mprFree(ep); +} + +/******************************************************************************/ +/* + * The callback function will be called: + * + * (fn)(EjsId eid, EspRequest *ep, argc, argv); + * + * Callers can get their web server handle by calling: + * + * rq = (requiredCast) espGetHandle(ep); + */ + +void espDefineCFunction(EspRequest *ep, char *functionName, EspCFunction fn, + void *thisPtr) +{ + mprAssert(functionName && *functionName); + mprAssert(fn); + + if (ep) { + ejsDefineCFunction(ep->eid, functionName, (MprCFunction) fn, + thisPtr, 0); + } else { + ejsDefineCFunction(-1, functionName, (MprCFunction) fn, thisPtr, 0); + } +} + +/******************************************************************************/ + +void espDefineStringCFunction(EspRequest *ep, const char *functionName, + EspStringCFunction fn, void *thisPtr) +{ + mprAssert(functionName && *functionName); + mprAssert(fn); + + if (ep) { + ejsDefineStringCFunction(ep->eid, functionName, (MprStringCFunction) fn, + thisPtr, 0); + } else { + ejsDefineStringCFunction(-1, functionName, (MprStringCFunction) fn, + thisPtr, 0); + } +} + +/******************************************************************************/ + +void *espGetRequestHandle(EspRequest *ep) +{ + return ep->requestHandle; +} + +/******************************************************************************/ + +EjsId espGetScriptHandle(EspRequest *ep) +{ + return ep->eid; +} + +/******************************************************************************/ + +char *espGetStringVar(EspRequest *ep, EspEnvType oType, char *var, + char *defaultValue) +{ + MprVar value; + + if (espGetVar(ep, oType, var, &value) < 0 || + value.type != MPR_TYPE_STRING) { + return defaultValue; + } + return value.string; +} + +/******************************************************************************/ + +int espGetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar *value) +{ + MprVar *vp; + + mprAssert(ep); + mprAssert(var); + + vp = mprGetProperty(&ep->variables[oType], var, 0); + if (vp == 0) { + return -1; + } + *value = *vp; + return 0; +} + +/******************************************************************************/ +/* + * Process the ESP page. docBuf holds the page already. We expect that + * ep->variables holds all the pertinent environment variables. + */ + +int espProcessRequest(EspRequest *ep, const char *docPath, char *docBuf, + char **errMsg) +{ + char *jsBuf; + + mprAssert(ep); + + ep->docPath = mprStrdup(docPath); + + jsBuf = 0; + if (buildScript(ep, &jsBuf, docBuf, errMsg) < 0) { + return MPR_ERR_CANT_COMPLETE; + } + + if (jsBuf) { + mprLog(7, "esp: script is:\n%s\n", jsBuf); + + /* + * Now evaluate the entire escript + * MOB could cache the script + */ + if (ejsEvalScript(ep->eid, jsBuf, 0, errMsg) < 0) { + return MPR_ERR_ABORTED; + } + + mprFree(jsBuf); + } + return 0; +} + +/******************************************************************************/ + +void espRedirect(EspRequest *ep, int code, char *url) +{ + mprAssert(ep); + mprAssert(url); + + ep->esp->redirect(ep->requestHandle, code, url); +} + +/******************************************************************************/ + +void espError(EspRequest *ep, const char *fmt, ...) +{ + va_list args; + char *buf; + + mprAssert(ep); + mprAssert(fmt); + + va_start(args, fmt); + mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args); + ejsSetErrorMsg(ep->eid, buf); + mprFree(buf); + va_end(args); +} + +/******************************************************************************/ + +void espSetHeader(EspRequest *ep, char *header, bool allowMultiple) +{ + mprAssert(ep); + + ep->esp->setHeader(ep->requestHandle, header, allowMultiple); +} + +/******************************************************************************/ +/* + * Caller does not need to destroy the var + */ + +MprVar *espGetResult(EspRequest *ep) +{ + mprAssert(ep); + + return ejsGetReturnValue(ep->eid); +} + +/******************************************************************************/ + +void espSetReturn(EspRequest *ep, MprVar value) +{ + mprAssert(ep); + + ejsSetReturnValue(ep->eid, value); +} + +/******************************************************************************/ + +void espSetReturnString(EspRequest *ep, char *str) +{ + mprAssert(ep); + + ejsSetReturnValue(ep->eid, mprCreateStringVar(str, 0)); +} + +/******************************************************************************/ + +void espSetResponseCode(EspRequest *ep, int code) +{ + mprAssert(ep); + + ep->esp->setResponseCode(ep->requestHandle, code); +} + +/******************************************************************************/ + +void espSetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar value) +{ + mprCreatePropertyValue(&ep->variables[oType], var, value); +} + +/******************************************************************************/ + +void espSetStringVar(EspRequest *ep, EspEnvType oType, + const char *var, const char *value) +{ + /* + * Will create or update if already existing + */ + mprCreatePropertyValue(&ep->variables[oType], var, + mprCreateStringVar(value, 0)); +} + +/******************************************************************************/ + +int espUnsetVar(EspRequest *ep, EspEnvType oType, char *var) +{ + return mprDeleteProperty(&ep->variables[oType], var); +} + +/******************************************************************************/ + +int espWrite(EspRequest *ep, char *buf, int size) +{ + mprAssert(ep); + mprAssert(buf); + mprAssert(size >= 0); + + return ep->esp->writeBlock(ep->requestHandle, buf, size); +} + +/******************************************************************************/ + +int espWriteString(EspRequest *ep, char *buf) +{ + mprAssert(ep); + mprAssert(buf); + + return ep->esp->writeBlock(ep->requestHandle, buf, strlen(buf)); +} + +/******************************************************************************/ + +int espWriteFmt(EspRequest *ep, char *fmt, ...) +{ + va_list args; + char *buf; + int rc, len; + + mprAssert(ep); + mprAssert(fmt); + + va_start(args, fmt); + len = mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args); + rc = ep->esp->writeBlock(ep->requestHandle, buf, len); + mprFree(buf); + va_end(args); + return rc; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* + * Get a javascript identifier. Must allow x.y['abc'] or x.y["abc"]. + * Must be careful about quoting and only allow quotes inside []. + */ + +static int getIdentifier(EspParse *parse) +{ + int atQuote, prevC, c; + + mprAssert(parse); + + atQuote = 0; + prevC = 0; + c = *parse->inp++; + + while (isalnum(c) || c == '_' || c == '.' || c == '[' || + c == ']' || c == '\'' || c == '\"') { + if (c == '\'' || c == '\"') { + if (c == atQuote) { + atQuote = 0; + } else if (prevC == '[') { + atQuote = c; + } else { + break; + } + } + if (parse->tokp >= parse->endp) { + parse->token = (char*) mprRealloc(parse->token, + parse->tokLen + ESP_TOK_INCR); + if (parse->token == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + parse->token[parse->tokLen] = '\0'; + parse->tokLen += ESP_TOK_INCR; + parse->endp = &parse->token[parse->tokLen - 1]; + } + *parse->tokp++ = c; + prevC = c; + c = *parse->inp++; + } + + parse->inp--; + *parse->tokp = '\0'; + + return 0; +} + +/******************************************************************************/ +/* + * Get the next ESP input token. input points to the next input token. + * parse->token will hold the parsed token. The function returns the token id. + */ + +static int getEspToken(int state, EspParse *parse) +{ + char *cp; + int tid, done, c, quoted; + + tid = ESP_TOK_LITERAL; + parse->tokp = parse->token; + parse->tokp[0] = '\0'; + quoted = 0; + + c = *parse->inp++; + for (done = 0; !done; c = *parse->inp++) { + + /* + * Get room for more characters in the token buffer + */ + if (parse->tokp >= parse->endp) { + parse->token = (char*) mprRealloc(parse->token, + parse->tokLen + ESP_TOK_INCR); + if (parse->token == 0) { + return ESP_TOK_ERR; + } + parse->token[parse->tokLen] = '\0';
+ parse->tokp = &parse->token[parse->tokLen - 1]; + parse->tokLen += ESP_TOK_INCR; + parse->endp = &parse->token[parse->tokLen - 1]; + } + + switch (c) { + case 0: + if (*parse->token) { + done++; + parse->inp--; + break; + } + return ESP_TOK_EOF; + + default: + if (c == '\"' && state != ESP_STATE_IN_ESP_TAG) { + *parse->tokp++ = '\\'; + } + *parse->tokp++ = c; + quoted = 0; + break; + + case '\\': + quoted = 1; + *parse->tokp++ = c; + break; + + case '@': + if (*parse->inp == '@' && state != ESP_STATE_IN_ESP_TAG) { + if (quoted) { + parse->tokp--; + quoted = 0; + } else { + if (*parse->token) { + parse->inp--; + } else { + parse->inp++; + tid = ESP_TOK_ATAT; + if (getIdentifier(parse) < 0) { + return ESP_TOK_ERR; + } + } + done++; + break; + } + } + *parse->tokp++ = c; + break; + + case '<': + if (*parse->inp == '%' && state != ESP_STATE_IN_ESP_TAG) { + if (quoted) { + parse->tokp--; + quoted = 0; + *parse->tokp++ = c; + break; + } + if (*parse->token) { + parse->inp--; + done++; + break; + } + parse->inp++; + while (isspace((int) *parse->inp)) { + parse->inp++; + } + if (*parse->inp == '=') { + parse->inp++; + while (isspace((int) *parse->inp)) { + parse->inp++; + } + tid = ESP_TOK_EQUALS; + if (getIdentifier(parse) < 0) { + return ESP_TOK_ERR; + } + done++; + break; + } + if (*parse->inp == 'i' && + strncmp(parse->inp, "include", 7) == 0 && + isspace((int) parse->inp[7])) { + tid = ESP_TOK_INCLUDE; + parse->inp += 7; + while (isspace((int) *parse->inp)) { + parse->inp++; + } + while (*parse->inp && !isspace((int) *parse->inp) && + *parse->inp != '%' && parse->tokp < parse->endp) { + *parse->tokp++ = *parse->inp++; + } + *parse->tokp = '\0'; + if (parse->token[0] == '"') { + parse->tokp = parse->token; + for (cp = &parse->token[1]; *cp; ) { + *parse->tokp++ = *cp++; + } + if (cp[-1] == '"') { + parse->tokp--; + } + *parse->tokp = '\0'; + } + + } else { + tid = ESP_TOK_START_ESP; + } + done++; + break; + } + *parse->tokp++ = c; + break; + + case '%': + if (*parse->inp == '>' && state == ESP_STATE_IN_ESP_TAG) { + if (quoted) { + parse->tokp--; + quoted = 0; + } else { + if (*parse->token) { + parse->inp--; + } else { + tid = ESP_TOK_END_ESP; + parse->inp++; + } + done++; + break; + } + } + *parse->tokp++ = c; + break; + } + } + + *parse->tokp = '\0'; + parse->inp--; + return tid; +} + +/******************************************************************************/ +/* + * Convert an ESP page into a JavaScript. We also expand include files. + */ + +static int buildScript(EspRequest *ep, char **jsBuf, char *input, char **errMsg) +{ + EspParse parse; + char path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME], incPath[MPR_MAX_FNAME]; + char *incBuf, *incText; + int state, tid, len, rc, maxScriptSize, incSize; + + mprAssert(ep); + mprAssert(jsBuf); + mprAssert(input); + + rc = 0; + len = 0; + state = ESP_STATE_BEGIN; + if (errMsg) { + *errMsg = 0; + } + + memset(&parse, 0, sizeof(parse)); + parse.token = (char*) mprMalloc(ESP_TOK_INCR); + if (parse.token == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + parse.token[0] = '\0'; + parse.tokLen = ESP_TOK_INCR; + parse.endp = &parse.token[parse.tokLen - 1]; + parse.tokp = parse.token; + parse.inBuf = input; + parse.inp = parse.inBuf; + + maxScriptSize = esp->maxScriptSize; + + tid = getEspToken(state, &parse); + while (tid != ESP_TOK_EOF && len >= 0) { + + switch (tid) { + default: + case ESP_TOK_ERR: + mprFree(parse.token); + return MPR_ERR_BAD_SYNTAX; + + case ESP_TOK_LITERAL: + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, + "write(\"", parse.token, "\");\n", 0); + break; + + case ESP_TOK_ATAT: + /* + * Trick to get undefined variables to evaluate to "". + * Catenate with "" to cause toString to run. + */ + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, + "write(\"\" + ", parse.token, ");\n", 0); + break; + + case ESP_TOK_EQUALS: + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, + "write(\"\" + ", parse.token, ");\n", 0); + state = ESP_STATE_IN_ESP_TAG; + break; + + case ESP_TOK_START_ESP: + state = ESP_STATE_IN_ESP_TAG; + tid = getEspToken(state, &parse); + while (tid != ESP_TOK_EOF && tid != ESP_TOK_EOF && + tid != ESP_TOK_END_ESP && len >= 0) { + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, + parse.token, 0); + tid = getEspToken(state, &parse); + } + state = ESP_STATE_BEGIN; + break; + + case ESP_TOK_END_ESP: + state = ESP_STATE_BEGIN; + break; + + case ESP_TOK_INCLUDE:
+ if (parse.token[0] == '/') {
+ mprStrcpy(incPath, sizeof(incPath), parse.token);
+ } else {
+ mprGetDirName(dir, sizeof(dir), ep->uri);
+ mprSprintf(incPath, sizeof(incPath), "%s/%s",
+ dir, parse.token);
+ } + if (esp->mapToStorage(ep->requestHandle, path, sizeof(path), + incPath, 0) < 0) { + mprAllocSprintf(errMsg, MPR_MAX_STRING, + "Can't find include file: %s", path); + rc = MPR_ERR_CANT_OPEN; + break; + } + if (esp->readFile(ep->requestHandle, &incText, &incSize, path) < 0){ + mprAllocSprintf(errMsg, MPR_MAX_STRING, + "Can't read include file: %s", path); + rc = MPR_ERR_CANT_READ; + break; + } + incText[incSize] = '\0'; + + /* + * Recurse and process the include script + */ + incBuf = 0; + if ((rc = buildScript(ep, &incBuf, incText, errMsg)) < 0) { + mprFree(incText);
+ mprFree(parse.token);
+ return rc; + }
+
+ len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, incBuf, 0); + mprFree(incText); + mprFree(incBuf); + state = ESP_STATE_IN_ESP_TAG; + break; + } + tid = getEspToken(state, &parse); + } + mprFree(parse.token);
+ if (len < 0) {
+ mprAllocSprintf(errMsg, MPR_MAX_STRING,
+ "Script token is too big in %s.\nConfigured maximum is %d.",
+ path, maxScriptSize);
+ return MPR_ERR_WONT_FIT;
+ } + return rc; +} + +/******************************************************************************/ +/******************************* Wrapped Routines *****************************/ +/******************************************************************************/ + +int espCopyVar(EspRequest *ep, char *var, MprVar *value, int copyDepth) +{ + return ejsCopyVar(ep->eid, var, value, copyDepth); +} + +/******************************************************************************/ + +MprVar espCreateObjVar(char *name, int hashSize) +{ + return ejsCreateObj(name, hashSize); +} + +/******************************************************************************/ + +MprVar espCreateArrayVar(char *name, int size) +{ + return ejsCreateArray(name, size); +} + +/******************************************************************************/ + +bool espDestroyVar(MprVar *obj) +{ + return ejsDestroyVar(obj); +} + +/******************************************************************************/ + +MprVar *espCreateProperty(MprVar *obj, char *property, MprVar *newValue) +{ + return mprCreateProperty(obj, property, newValue); +} + +/******************************************************************************/ + +MprVar *espCreatePropertyValue(MprVar *obj, char *property, MprVar newValue) +{ + return mprCreatePropertyValue(obj, property, newValue); +} + +/******************************************************************************/ + +void espDefineFunction(EspRequest *ep, char *functionName, char *args, char *body) +{ + ejsDefineFunction(ep->eid, functionName, args, body); +} + +/******************************************************************************/ + +int espDeleteProperty(MprVar *obj, char *property) +{ + return mprDeleteProperty(obj, property); +} + +/******************************************************************************/ + +int espDeleteVar(EspRequest *ep, char *var) +{ + return ejsDeleteVar(ep->eid, var); +} + +/******************************************************************************/ +int espEvalFile(EspRequest *ep, char *path, MprVar *result, char **emsg) +{ + return ejsEvalFile(ep->eid, path, result, emsg); +} + +/******************************************************************************/ + +int espEvalScript(EspRequest *ep, char *script, MprVar *result, char **emsg) +{ + return ejsEvalScript(ep->eid, script, result, emsg); +} + +/******************************************************************************/ + +int espGetPropertyCount(MprVar *obj, int includeFlags) +{ + if (obj->type != MPR_TYPE_OBJECT) { + return MPR_ERR_BAD_STATE; + } + return mprGetPropertyCount(obj, includeFlags); +} + +/******************************************************************************/ + +MprVar *espGetFirstProperty(MprVar *obj, int includeFlags) +{ + return mprGetFirstProperty(obj, includeFlags); +} + +/******************************************************************************/ + +MprVar *espGetGlobalObject(EspRequest *ep) +{ + return ejsGetGlobalObject(ep->eid); +} + +/******************************************************************************/ + +MprVar *espGetLocalObject(EspRequest *ep) +{ + return ejsGetLocalObject(ep->eid); +} + +/******************************************************************************/ + +MprVar *espGetNextProperty(MprVar *obj, MprVar *currentProperty, + int includeFlags) +{ + return mprGetNextProperty(obj, currentProperty, includeFlags); +} + +/******************************************************************************/ + +MprVar *espGetProperty(MprVar *obj, char *property, MprVar *value) +{ + return mprGetProperty(obj, property, value); +} + +/******************************************************************************/ + +void *espGetThisPtr(EspRequest *ep) +{ + return ejsGetThisPtr(ep->eid); +} + +/******************************************************************************/ +#if UNUSED + +int espReadProperty(MprVar *dest, MprVar *prop) +{ + mprAssert(prop); + mprAssert(dest); + + *dest = *prop; + return 0; +} + +#endif +/******************************************************************************/ + +int espReadVar(EspRequest *ep, char *var, MprVar *value) +{ + return ejsReadVar(ep->eid, var, value); +} + +/******************************************************************************/ + +int espRunFunction(EspRequest *ep, MprVar *obj, char *functionName, + MprArray *args) +{ + return ejsRunFunction(ep->eid, obj, functionName, args); +} + +/******************************************************************************/ + +MprVar *espSetProperty(MprVar *obj, char *property, MprVar *newValue) +{ + return mprSetProperty(obj, property, newValue); +} + +/******************************************************************************/ + +MprVar *espSetPropertyValue(MprVar *obj, char *property, MprVar newValue) +{ + return mprSetPropertyValue(obj, property, newValue); +} + +/******************************************************************************/ + +int espWriteVar(EspRequest *ep, char *var, MprVar *value) +{ + return ejsWriteVar(ep->eid, var, value); +} + +/******************************************************************************/ + +int espWriteVarValue(EspRequest *ep, char *var, MprVar value) +{ + return ejsWriteVarValue(ep->eid, var, value); +} + +/******************************************************************************/ +#if UNUSED + +int espWriteProperty(MprVar *prop, MprVar *newValue) +{ + return mprWriteProperty(prop, newValue); +} + +/******************************************************************************/ + +int espWritePropertyValue(MprVar *prop, MprVar newValue) +{ + return mprWritePropertyValue(prop, newValue); +} + +#endif +/******************************************************************************/ + +#else /* !BLD_FEATURE_ESP_MODULE */ +void espDummy() {} + +/******************************************************************************/ +#endif /* BLD_FEATURE_ESP_MODULE */ + +/* + * 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 + */ diff --git a/source4/web_server/esp/esp.h b/source4/web_server/esp/esp.h new file mode 100644 index 0000000000..33ab9d7ac9 --- /dev/null +++ b/source4/web_server/esp/esp.h @@ -0,0 +1,279 @@ +/** + * @file esp.h + * @brief Header for Embedded Server Pages (ESP) + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 **********************************/ + +#ifndef _h_ESP_h +#define _h_ESP_h 1 + +#include "web_server/ejs/ejs.h" +#include "web_server/esp/espEnv.h" +#include "web_server/ejs/var.h" +#include "web_server/ejs/miniMpr.h" + +/*********************************** Defines **********************************/ + +#define ESP_STRING_ARGS MPR_TYPE_STRING_ARGS + +#if BLD_FEATURE_SQUEEZE +#define ESP_TOK_INCR 1024 +#define ESP_MAX_HEADER 1024 +#else +#define ESP_TOK_INCR 4096 +#define ESP_MAX_HEADER 4096 +#endif + +/* + * ESP lexical analyser tokens + */ +#define ESP_TOK_ERR -1 /* Any input error */ +#define ESP_TOK_EOF 0 /* End of file */ +#define ESP_TOK_START_ESP 1 /* <% */ +#define ESP_TOK_END_ESP 2 /* %> */ +#define ESP_TOK_ATAT 3 /* @@var */ +#define ESP_TOK_LITERAL 4 /* literal HTML */ +#define ESP_TOK_INCLUDE 5 /* include file.esp */ +#define ESP_TOK_EQUALS 6 /* = var */ + +/* + * ESP parser states + */ +#define ESP_STATE_BEGIN 1 /* Starting state */ +#define ESP_STATE_IN_ESP_TAG 2 /* Inside a <% %> group */ + +/*********************************** Types ************************************/ + +typedef void* EspHandle; /* Opaque Web server handle type */ + +/* + * Per request control block + */ +typedef struct EspRequest { + MprStr docPath; /* Physical path for ESP page */ + EjsId eid; /* EJS instance handle */ + struct Esp *esp; /* Pointer to ESP control block */ + EspHandle requestHandle; /* Per request web server handle */ + MprStr uri; /* Request URI */ + MprVar *variables; /* Pointer to variables */ +} EspRequest; + +/* + * Master ESP control block. This defines the function callbacks for a + * web server handler to implement. ESP will call these functions as + * required. + */ +typedef struct Esp { + int maxScriptSize; + void (*createSession)(EspHandle handle, int timeout); + void (*destroySession)(EspHandle handle); + char *(*getSessionId)(EspHandle handle); + int (*mapToStorage)(EspHandle handle, char *path, int len, char *uri, + int flags); + int (*readFile)(EspHandle handle, char **buf, int *len, char *path); + void (*redirect)(EspHandle handle, int code, char *url); + void (*setCookie)(EspHandle handle, char *name, char *value, + int lifetime, char *path, bool secure); + void (*setHeader)(EspHandle handle, const char *value, bool allowMultiple); + void (*setResponseCode)(EspHandle handle, int code); + int (*writeBlock)(EspHandle handle, char *buf, int size); + int (*writeFmt)(EspHandle handle, char *fmt, ...); +#if BLD_FEATURE_MULTITHREAD + void (*lock)(void *lockData); + void (*unlock)(void *lockData); + void *lockData; +#endif +} Esp; + + +/* + * ESP parse context + */ +typedef struct { + char *inBuf; /* Input data to parse */ + char *inp; /* Next character for input */ + char *endp; /* End of storage (allow for null) */ + char *tokp; /* Pointer to current parsed token */ + char *token; /* Storage buffer for token */ + int tokLen; /* Length of buffer */ +} EspParse; + + +/******************************** Private APIs ********************************/ + +extern void espRegisterProcs(void); + +/******************************** Published API *******************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Function callback signatures + */ +typedef int (*EspCFunction)(EspRequest *ep, int argc, + struct MprVar **argv); +typedef int (*EspStringCFunction)(EspRequest *ep, int argc, + char **argv); + +/* + * APIs for those hosting the ESP module + */ +extern int espOpen(Esp *control); +extern void espClose(void); +extern EspRequest *espCreateRequest(EspHandle webServerRequestHandle, + char *uri, MprVar *envObj); +extern void espDestroyRequest(EspRequest *ep); +extern int espProcessRequest(EspRequest *ep, const char *docPath, + char *docBuf, char **errMsg); + +/* + * Method invocation + */ +extern void espDefineCFunction(EspRequest *ep, char *functionName, + EspCFunction fn, void *thisPtr); +extern void espDefineFunction(EspRequest *ep, char *functionName, + char *args, char *body); +extern void espDefineStringCFunction(EspRequest *ep, + const char *functionName, EspStringCFunction fn, + void *thisPtr); +extern int espRunFunction(EspRequest *ep, MprVar *obj, + char *functionName, MprArray *args); +extern void espSetResponseCode(EspRequest *ep, int code); +extern void espSetReturn(EspRequest *ep, MprVar value); +extern void *espGetThisPtr(EspRequest *ep); + +/* + * Utility routines to use in C methods + */ +extern void espError(EspRequest *ep, const char *fmt, ...); +extern int espEvalFile(EspRequest *ep, char *path, MprVar *result, + char **emsg); +extern int espEvalScript(EspRequest *ep, char *script, MprVar *result, + char **emsg); +extern MprVar *espGetLocalObject(EspRequest *ep); +extern MprVar *espGetGlobalObject(EspRequest *ep); +extern EspHandle espGetRequestHandle(EspRequest *ep); +extern MprVar *espGetResult(EspRequest *ep); +extern EjsId espGetScriptHandle(EspRequest *ep); +extern void espRedirect(EspRequest *ep, int code, char *url); +extern void espSetHeader(EspRequest *ep, char *header, + bool allowMultiple); +extern void espSetReturnString(EspRequest *ep, char *str); +extern int espWrite(EspRequest *ep, char *buf, int size); +extern int espWriteString(EspRequest *ep, char *buf); +extern int espWriteFmt(EspRequest *ep, char *fmt, ...); + +/* + * ESP array[] variable access (set will update/create) + */ +extern int espGetVar(EspRequest *ep, EspEnvType oType, char *var, + MprVar *value); +extern char *espGetStringVar(EspRequest *ep, EspEnvType oType, + char *var, char *defaultValue); +extern void espSetVar(EspRequest *ep, EspEnvType oType, char *var, + MprVar value); +extern void espSetStringVar(EspRequest *ep, EspEnvType oType, + const char *var, const char *value); +extern int espUnsetVar(EspRequest *ep, EspEnvType oType, char *var); + +/* + * Object creation and management + */ +extern MprVar espCreateObjVar(char *name, int hashSize); +extern MprVar espCreateArrayVar(char *name, int size); +extern bool espDestroyVar(MprVar *var); +extern MprVar *espCreateProperty(MprVar *obj, char *property, + MprVar *newValue); +extern MprVar *espCreatePropertyValue(MprVar *obj, char *property, + MprVar newValue); +extern int espDeleteProperty(MprVar *obj, char *property); + +/* + * JavaScript variable management. Set will create/update a property. + * All return a property reference. GetProperty will optionally return the + * property in value. + */ +extern MprVar *espGetProperty(MprVar *obj, char *property, + MprVar *value); +extern MprVar *espSetProperty(MprVar *obj, char *property, + MprVar *newValue); +extern MprVar *espSetPropertyValue(MprVar *obj, char *property, + MprVar newValue); + +#if 0 +/* + * Low-level direct read and write of properties. + * FUTURE: -- Read is not (dest, src). MUST WARN IN DOC ABOUT COPY/READ + * Will still cause triggers to run. + */ +extern int espReadProperty(MprVar *dest, MprVar *prop); +extern int espWriteProperty(MprVar *prop, MprVar *newValue); +extern int espWritePropertyValue(MprVar *prop, MprVar newValue); +#endif + + +/* + * Access JavaScript variables by their full name. Can use "." or "[]". For + * example: "global.request['REQUEST_URI']" + * For Read/write, the variables must exist. + */ +extern int espCopyVar(EspRequest *ep, char *var, MprVar *value, + int copyDepth); +extern int espDeleteVar(EspRequest *ep, char *var); +extern int espReadVar(EspRequest *ep, char *var, MprVar *value); +extern int espWriteVar(EspRequest *ep, char *var, MprVar *value); +extern int espWriteVarValue(EspRequest *ep, char *var, MprVar value); + +/* + * Object property enumeration + */ +extern MprVar *espGetFirstProperty(MprVar *obj, int includeFlags); +extern MprVar *espGetNextProperty(MprVar *obj, MprVar *currentProperty, + int includeFlags); +extern int espGetPropertyCount(MprVar *obj, int includeFlags); + +#ifdef __cplusplus +} +#endif +/******************************************************************************/ +#endif /* _h_ESP_h */ + +/* + * 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 + */ diff --git a/source4/web_server/esp/espEnv.h b/source4/web_server/esp/espEnv.h new file mode 100644 index 0000000000..a3c9d9f5c7 --- /dev/null +++ b/source4/web_server/esp/espEnv.h @@ -0,0 +1,128 @@ +/* + * @file espEnv.h + * @brief ESP Environment Variables + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 + */ + +/******************************************************************************/ + +#ifndef _h_ESP_ENV_h +#define _h_ESP_ENV_h 1 + +/* + * @brief Scripting environment variable array types + */ +typedef enum EspEnvType { + ESP_UNDEFINED_OBJ = -1, + + /** + * Elements for server[]: + * DOCUMENT_ROOT GATEWAY_INTERFACE SERVER_ADDR SERVER_PORT SERVER_NAME + * SERVER_PROTOCOL SERVER_SOFTWARE SERVER_URL UPLOAD_DIR + * FUTURE: SERVER_ADMIN + * FUTURE: this could be shared across all hosts and be made read-only. + */ + ESP_SERVER_OBJ = 0, /*! server[] data */ + + /** + * Elements for session[]: are user defined + */ + ESP_SESSION_OBJ = 1, /*! session[] data */ + + /** + * Elements for request[]: + * AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE QUERY_STRING PATH_INFO + * PATH_TRANSLATED REMOTE_ADDR REMOTE_HOST REMOTE_USER REQUEST_METHOD + * REQUEST_URI SCRIPT_FILENAME SCRIPT_NAME + * FUTURE: FILEPATH_INFO REDIRECT_URL SELF REMOTE_PORT AUTH_USER + * AUTH_GROUP AUTH_ACL + */ + ESP_REQUEST_OBJ = 2, /*! request[] data */ + + /** + * Elements for headers[]: + * HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_CONNECTION HTTP_HOST + * HTTP_REFERER HTTP_USER_AGENT and any other custom headers + */ + ESP_HEADERS_OBJ = 3, /*! header [] data */ + + /** + * Elements for cookies[]: are defined by the HTTP request + */ + ESP_COOKIES_OBJ = 4, /*! cookies[] data */ + + /** + * Elements for files[]: are defined by the HTTP request + * CLIENT_FILENAME CONTENT_TYPE FILENAME SIZE + */ + ESP_FILES_OBJ = 5, /*! files[] data */ + + /** + * Elements for form[]: are defined by the HTTP request + */ + ESP_FORM_OBJ = 6, /*! form[] data */ + + /** + * Elements for application[]: are user defined + */ + ESP_APPLICATION_OBJ = 7, /*! application[] data */ + + /** + * Elements for global[]: are defined by ESP/EJS + */ + ESP_GLOBAL_OBJ = 8, /*! global [] data */ + + /* + * Elements for local[]: are defined by ESP/EJS + */ + ESP_LOCAL_OBJ = 9, /*! local [] data */ +} EspEnvType; + +#define ESP_OBJ_MAX 10 /* Total objects */ + +#if BLD_SQUEEZE +#define ESP_HASH_SIZE 19 /* Size of hash tables */ +#else +#define ESP_HASH_SIZE 37 +#endif + +/******************************************************************************/ +#endif /* _h_ESP_ENV_h */ + +/* + * 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 + */ diff --git a/source4/web_server/esp/espProcs.c b/source4/web_server/esp/espProcs.c new file mode 100644 index 0000000000..a8da800213 --- /dev/null +++ b/source4/web_server/esp/espProcs.c @@ -0,0 +1,230 @@ +/* + * @file espProcs.c + * @brief Embedded Server Pages (ESP) Procedures. + * @overview These ESP procedures can be used in ESP pages for common tasks. + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. 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 "esp.h" + +/************************************ Code ************************************/ +#if BLD_FEATURE_ESP_MODULE +#if BLD_FEATURE_SESSION +/* + * destroySession + */ + +static int destroySessionProc(EspRequest *ep, int argc, char **argv) +{ + ep->esp->destroySession(ep->requestHandle); + return 0; +} + +#endif /* BLD_FEATURE_SESSION */ + +/******************************************************************************/ +/* + * include + * + * This includes javascript libraries. For example: + * + * <% include("file", ...); %> + * + * Don't confuse with ESP includes: + * + * <% include file.esp %> + * + * Filenames are relative to the base document including the file. + * FUTURE -- move back to EJS. Only here now because we need ep->readFile. + */ + +static int includeProc(EspRequest *ep, int argc, char **argv) +{ + Esp *esp; + char path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME]; + char *emsg, *buf; + int size, i; + + esp = ep->esp; + mprAssert(argv); + for (i = 0; i < argc; i++) { + mprGetDirName(dir, sizeof(dir), ep->docPath); + mprSprintf(path, sizeof(path), "%s/%s", dir, argv[i]); + + if (esp->readFile(ep->requestHandle, &buf, &size, path) < 0) { + espError(ep, "Can't read include file: %s", path); + return MPR_ERR_CANT_ACCESS; + } + buf[size] = '\0'; + + if (ejsEvalScript(espGetScriptHandle(ep), buf, 0, &emsg) < 0) { + espError(ep, "Cant evaluate script"); + mprFree(buf); + return -1; + } + mprFree(buf); + } + return 0; +} + +/******************************************************************************/ +/* + * redirect + * + * This implemements <% redirect(url, code); %> command. The redirection + * code is optional. + */ + +static int redirectProc(EspRequest *ep, int argc, char **argv) +{ + char *url; + int code; + + if (argc < 1) { + espError(ep, "Bad args"); + return MPR_ERR_BAD_ARGS; + } + url = argv[0]; + if (argc == 2) { + code = atoi(argv[1]); + } else { + code = 302; + } + espRedirect(ep, code, url); + return 0; +} + +/******************************************************************************/ +#if BLD_FEATURE_SESSION +/* + * useSession + */ + +static int useSessionProc(EspRequest *ep, int argc, char **argv) +{ + int timeout; + + if (argc > 1) { + espError(ep, "Bad args"); + return MPR_ERR_BAD_ARGS; + + } else if (argc == 1) { + timeout = atoi(argv[0]); + } else { + timeout = 0; + } + + ep->esp->createSession(ep->requestHandle, timeout); + espSetReturnString(ep, ep->esp->getSessionId(ep->requestHandle)); + return 0; +} + +#endif /* BLD_FEATURE_SESSION */ +/******************************************************************************/ +/* + * setHeader + * + * This implemements <% setHeader("key: value", allowMultiple); %> command. + */ + +static int setHeaderProc(EspRequest *ep, int argc, char **argv) +{ + mprAssert(argv); + if (argc != 2) { + espError(ep, "Bad args"); + return MPR_ERR_BAD_ARGS; + } + ep->esp->setHeader(ep->requestHandle, argv[0], atoi(argv[1])); + return 0; +} + +/******************************************************************************/ +/* + * write + * + * This implemements <% write("text"); %> command. + */ + +static int writeProc(EspRequest *ep, int argc, char **argv) +{ + char *s; + int i, len; + + mprAssert(argv); + for (i = 0; i < argc; i++) { + s = argv[i]; + len = strlen(s); + if (len > 0) { + if (espWrite(ep, s, len) != len) { + espError(ep, "Can't write to client"); + return -1; + } + } + } + return 0; +} + +/******************************************************************************/ + +void espRegisterProcs() +{ + espDefineStringCFunction(0, "write", writeProc, 0); + espDefineStringCFunction(0, "setHeader", setHeaderProc, 0); + espDefineStringCFunction(0, "redirect", redirectProc, 0); + espDefineStringCFunction(0, "include", includeProc, 0); + +#if BLD_FEATURE_SESSION + /* + * Create and use are synonomous + */ + espDefineStringCFunction(0, "useSession", useSessionProc, 0); + espDefineStringCFunction(0, "createSession", useSessionProc, 0); + espDefineStringCFunction(0, "destroySession", destroySessionProc, 0); +#endif +} + +/******************************************************************************/ + +#else +void mprEspControlsDummy() {} + +#endif /* BLD_FEATURE_ESP_MODULE */ + +/* + * 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 + */ diff --git a/source4/web_server/http.c b/source4/web_server/http.c new file mode 100644 index 0000000000..ad7eaa8ead --- /dev/null +++ b/source4/web_server/http.c @@ -0,0 +1,627 @@ +/* + Unix SMB/CIFS implementation. + + http handling code + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; 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. + + 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "web_server/web_server.h" +#include "smbd/service_stream.h" +#include "lib/events/events.h" +#include "system/filesys.h" +#include "system/iconv.h" +#include "system/time.h" +#include "web_server/esp/esp.h" + +/* state of the esp subsystem */ +struct esp_state { + struct websrv_context *web; + struct MprVar variables[ESP_OBJ_MAX]; + struct EspRequest *req; +}; + +/* destroy a esp session */ +static int esp_destructor(void *ptr) +{ + struct esp_state *esp = talloc_get_type(ptr, struct esp_state); + if (esp->req) { + espDestroyRequest(esp->req); + } + espClose(); + mprFreeAll(); + return 0; +} + +/* + output the http headers +*/ +static void http_output_headers(struct websrv_context *web) +{ + int i; + char *s; + DATA_BLOB b; + const char *response_string = "Unknown Code"; + const struct { + unsigned code; + const char *response_string; + } codes[] = { + { 200, "OK" }, + { 301, "Moved" }, + { 302, "Found" }, + { 303, "Method" }, + { 304, "Not Modified" }, + { 400, "Bad request" }, + { 401, "Unauthorized" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 500, "Internal Server Error" }, + { 501, "Not implemented" } + }; + for (i=0;i<ARRAY_SIZE(codes);i++) { + if (codes[i].code == web->output.response_code) { + response_string = codes[i].response_string; + } + } + + if (web->output.headers == NULL) return; + s = talloc_asprintf(web, "HTTP/1.0 %u %s\r\n", + web->output.response_code, response_string); + if (s == NULL) return; + for (i=0;web->output.headers[i];i++) { + s = talloc_asprintf_append(s, "%s\r\n", web->output.headers[i]); + } + s = talloc_asprintf_append(s, "\r\n"); + if (s == NULL) return; + + b = web->output.content; + web->output.content.data = s; + web->output.content.length = strlen(s); + data_blob_append(web, &web->output.content, b.data, b.length); + data_blob_free(&b); +} + +/* + called when esp wants to output something +*/ +static int http_writeBlock(EspHandle handle, char *buf, int size) +{ + struct websrv_context *web = talloc_get_type(handle, struct websrv_context); + NTSTATUS status; + status = data_blob_append(web, &web->output.content, buf, size); + if (!NT_STATUS_IS_OK(status)) return -1; + return size; +} + + +/* + set a http header +*/ +static void http_setHeader(EspHandle handle, const char *value, bool allowMultiple) +{ + struct websrv_context *web = talloc_get_type(handle, struct websrv_context); + char *p = strchr(value, ':'); + + if (p && !allowMultiple && web->output.headers) { + int i; + for (i=0;web->output.headers[i];i++) { + if (strncmp(web->output.headers[i], value, (p+1)-value) == 0) { + web->output.headers[i] = talloc_strdup(web, value); + return; + } + } + } + + web->output.headers = str_list_add(web->output.headers, value); +} + +/* + set a http response code +*/ +static void http_setResponseCode(EspHandle handle, int code) +{ + struct websrv_context *web = talloc_get_type(handle, struct websrv_context); + web->output.response_code = code; +} + +/* + redirect to another web page + */ +static void http_redirect(EspHandle handle, int code, char *url) +{ + struct websrv_context *web = talloc_get_type(handle, struct websrv_context); + const char *host = web->input.host; + + /* form the full url, unless it already looks like a url */ + if (strchr(url, ':') == NULL) { + if (host == NULL) { + host = talloc_asprintf(web, "%s:%u", + socket_get_my_addr(web->conn->socket, web), + socket_get_my_port(web->conn->socket)); + } + if (host == NULL) goto internal_error; + if (url[0] != '/') { + char *p = strrchr(web->input.url, '/'); + if (p == web->input.url) { + url = talloc_asprintf(web, "http://%s/%s", host, url); + } else { + int dirlen = p - web->input.url; + url = talloc_asprintf(web, "http://%s%*.*s/%s", + host, + dirlen, dirlen, web->input.url, + url); + } + if (url == NULL) goto internal_error; + } + } + + http_setHeader(handle, talloc_asprintf(web, "Location: %s", url), 0); + + /* make sure we give a valid redirect code */ + if (code >= 300 && code < 400) { + http_setResponseCode(handle, code); + } else { + http_setResponseCode(handle, 302); + } + return; + +internal_error: + http_error(web, 500, "Internal server error"); +} + + +/* callbacks for esp processing */ +static const struct Esp esp_control = { + .maxScriptSize = 60000, + .writeBlock = http_writeBlock, + .setHeader = http_setHeader, + .redirect = http_redirect, + .setResponseCode = http_setResponseCode +}; + + +/* + setup for a raw http level error +*/ +void http_error(struct websrv_context *web, int code, const char *info) +{ + char *s; + s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %u</TITLE></HEAD><BODY><H1>Error %u</H1>%s<p></BODY></HTML>\r\n\r\n", + code, code, info); + if (s == NULL) { + stream_terminate_connection(web->conn, "http_error: out of memory"); + return; + } + http_writeBlock(web, s, strlen(s)); + http_setResponseCode(web, code); + http_output_headers(web); + EVENT_FD_NOT_READABLE(web->conn->event.fde); + EVENT_FD_WRITEABLE(web->conn->event.fde); +} + +/* + map a unix error code to a http error +*/ +void http_error_unix(struct websrv_context *web, const char *info) +{ + int code = 500; + switch (errno) { + case ENOENT: + case EISDIR: + code = 404; + break; + case EACCES: + code = 403; + break; + } + http_error(web, code, info); +} + +/* + return the local path for a URL +*/ +static const char *http_local_path(struct websrv_context *web, const char *url) +{ + int i; + char *path; + + /* check that the url is OK */ + if (url[0] != '/') return NULL; + + for (i=0;url[i];i++) { + if ((!isalnum(url[i]) && !strchr("./", url[i])) || + (url[i] == '.' && strchr("/.", url[i+1]))) { + return NULL; + } + } + + path = talloc_asprintf(web, "%s/%s", lp_swat_directory(), url+1); + if (path == NULL) return NULL; + + if (directory_exist(path)) { + path = talloc_asprintf_append(path, "/index.html"); + } + return path; +} + + +/* + a simple file request +*/ +static void http_simple_request(struct websrv_context *web) +{ + const char *url = web->input.url; + const char *path; + struct stat st; + + path = http_local_path(web, url); + if (path == NULL) goto invalid; + + /* looks ok */ + web->output.fd = open(path, O_RDONLY); + if (web->output.fd == -1) { + http_error_unix(web, url); + return; + } + + if (fstat(web->output.fd, &st) != 0 || !S_ISREG(st.st_mode)) { + close(web->output.fd); + goto invalid; + } + + http_output_headers(web); + EVENT_FD_WRITEABLE(web->conn->event.fde); + return; + +invalid: + http_error(web, 400, "Malformed URL"); +} + +/* + setup the standard ESP arrays +*/ +static void http_setup_arrays(struct esp_state *esp) +{ + struct websrv_context *web = esp->web; + struct EspRequest *req = esp->req; + char *p; + + espSetStringVar(req, ESP_REQUEST_OBJ, "CONTENT_LENGTH", + talloc_asprintf(esp, "%u", web->input.content_length)); + if (web->input.query_string) { + espSetStringVar(req, ESP_REQUEST_OBJ, "QUERY_STRING", + web->input.query_string); + } + espSetStringVar(req, ESP_REQUEST_OBJ, "REQUEST_METHOD", + web->input.post_request?"POST":"GET"); + espSetStringVar(req, ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url); + p = strrchr(web->input.url, '/'); + espSetStringVar(req, ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1); + + if (web->input.referer) { + espSetStringVar(req, ESP_HEADERS_OBJ, "HTT_REFERER", web->input.referer); + } + if (web->input.user_agent) { + espSetStringVar(req, ESP_HEADERS_OBJ, "USER_AGENT", web->input.user_agent); + } + + espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_ADDR", + socket_get_my_addr(web->conn->socket, esp)); + espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_PORT", + talloc_asprintf(esp, "%u", socket_get_my_port(web->conn->socket))); + espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_PROTOCOL", "http"); +} + + + + + +/* + process a esp request +*/ +static void esp_request(struct esp_state *esp) +{ + struct websrv_context *web = esp->web; + const char *url = web->input.url; + char *buf; + const char *path; + struct stat st; + int fd, res; + char *emsg = NULL; + + http_setup_arrays(esp); + + path = http_local_path(web, url); + if (path == NULL) goto invalid; + + espSetStringVar(esp->req, ESP_REQUEST_OBJ, "SCRIPT_FILENAME", path); + + /* looks ok */ + fd = open(path, O_RDONLY); + if (fd == -1) { + http_error_unix(web, url); + return; + } + + if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) { + close(fd); + goto invalid; + } + + buf = talloc_size(esp, st.st_size+1); + if (buf == NULL) goto invalid; + + if (read(fd, buf, st.st_size) != st.st_size) { + goto invalid; + } + buf[st.st_size] = 0; + close(fd); + + res = espProcessRequest(esp->req, path, buf, &emsg); + if (res != 0 && emsg) { + http_writeBlock(esp, emsg, strlen(emsg)); + } + http_output_headers(web); + EVENT_FD_WRITEABLE(web->conn->event.fde); + return; + +invalid: + http_error(web, 400, "Malformed URL"); +} + + +/* + handling of + and % escapes in http variables +*/ +static const char *http_unescape(TALLOC_CTX *mem_ctx, const char *p) +{ + char *s0 = talloc_strdup(mem_ctx, p); + char *s = s0; + if (s == NULL) return NULL; + + while (*s) { + unsigned v; + if (*s == '+') *s = ' '; + if (*s == '%' && sscanf(s+1, "%02x", &v) == 1) { + *s = (char)v; + memmove(s+1, s+3, strlen(s+3)+1); + } + s++; + } + + return s0; +} + +/* + set a form or GET variable +*/ +static void esp_putvar(struct esp_state *esp, const char *var, const char *value) +{ + espSetStringVar(esp->req, ESP_FORM_OBJ, + http_unescape(esp, var), + http_unescape(esp, value)); +} + + +/* + parse the variables in a POST style request +*/ +static NTSTATUS http_parse_post(struct esp_state *esp) +{ + DATA_BLOB b = esp->web->input.partial; + + while (b.length) { + char *p, *line; + size_t len; + + p = memchr(b.data, '&', b.length); + if (p == NULL) { + len = b.length; + } else { + len = p - (char *)b.data; + } + line = talloc_strndup(esp, b.data, len); + NT_STATUS_HAVE_NO_MEMORY(line); + + p = strchr(line,'='); + if (p) { + *p = 0; + esp_putvar(esp, line, p+1); + } + talloc_free(line); + b.length -= len; + b.data += len; + if (b.length > 0) { + b.length--; + b.data++; + } + } + + return NT_STATUS_OK; +} + +/* + parse the variables in a GET style request +*/ +static NTSTATUS http_parse_get(struct esp_state *esp) +{ + struct websrv_context *web = esp->web; + char *p, *s, *tok; + char *pp; + + p = strchr(web->input.url, '?'); + web->input.query_string = p+1; + *p = 0; + + s = talloc_strdup(esp, esp->web->input.query_string); + NT_STATUS_HAVE_NO_MEMORY(s); + + for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) { + p = strchr(tok,'='); + if (p) { + *p = 0; + esp_putvar(esp, tok, p+1); + } + } + return NT_STATUS_OK; +} + +/* + setup some standard variables +*/ +static void http_setup_vars(struct esp_state *esp) +{ + int i; + + for (i = 0; i < ESP_OBJ_MAX; i++) { + esp->variables[i] = mprCreateUndefinedVar(); + } + esp->variables[ESP_HEADERS_OBJ] = mprCreateObjVar("headers", ESP_HASH_SIZE); + esp->variables[ESP_FORM_OBJ] = mprCreateObjVar("form", ESP_HASH_SIZE); + esp->variables[ESP_APPLICATION_OBJ] = mprCreateObjVar("application", ESP_HASH_SIZE); + esp->variables[ESP_COOKIES_OBJ] = mprCreateObjVar("cookies", ESP_HASH_SIZE); + esp->variables[ESP_FILES_OBJ] = mprCreateObjVar("files", ESP_HASH_SIZE); + esp->variables[ESP_REQUEST_OBJ] = mprCreateObjVar("request", ESP_HASH_SIZE); + esp->variables[ESP_SERVER_OBJ] = mprCreateObjVar("server", ESP_HASH_SIZE); + esp->variables[ESP_SESSION_OBJ] = mprCreateObjVar("session", ESP_HASH_SIZE); +} + +/* + process a complete http request +*/ +void http_process_input(struct websrv_context *web) +{ + NTSTATUS status; + struct esp_state *esp; + char *p; + int i; + const char *file_type = NULL; + const struct { + const char *extension; + const char *mime_type; + } mime_types[] = { + {"gif", "image/gif"}, + {"png", "image/png"}, + {"jpg", "image/jpeg"}, + {"txt", "text/plain"} + }; + + esp = talloc_zero(web, struct esp_state); + if (esp == NULL) goto internal_error; + + esp->web = web; + + mprSetCtx(esp); + + talloc_set_destructor(esp, esp_destructor); + + if (espOpen(&esp_control) != 0) goto internal_error; + + http_setup_vars(esp); + + esp->req = espCreateRequest(web, web->input.url, esp->variables); + if (esp->req == NULL) goto internal_error; + + if (web->input.url == NULL) { + http_error(web, 400, "You must specify a GET or POST request"); + return; + } + + if (web->input.post_request) { + status = http_parse_post(esp); + if (!NT_STATUS_IS_OK(status)) { + http_error(web, 400, "Malformed POST data"); + return; + } + } else if (strchr(web->input.url, '?')) { + status = http_parse_get(esp); + if (!NT_STATUS_IS_OK(status)) { + http_error(web, 400, "Malformed GET data"); + return; + } + } + + /* process all html files as ESP */ + p = strrchr(web->input.url, '.'); + for (i=0;p && i<ARRAY_SIZE(mime_types);i++) { + if (strcmp(mime_types[i].extension, p+1) == 0) { + file_type = mime_types[i].mime_type; + } + } + if (file_type == NULL) { + file_type = "text/html"; + } + + /* setup basic headers */ + http_setResponseCode(web, 200); + http_setHeader(web, talloc_asprintf(esp, "Date: %s", + http_timestring(esp, time(NULL))), 0); + http_setHeader(web, "Server: Samba", 0); + http_setHeader(web, "Connection: close", 0); + http_setHeader(web, talloc_asprintf(esp, "Content-Type: %s", file_type), 0); + + if (strcmp(file_type, "text/html") == 0) { + esp_request(esp); + } else { + http_simple_request(web); + } + talloc_free(esp); + return; + +internal_error: + talloc_free(esp); + http_error(web, 500, "Internal server error"); +} + + +/* + parse one line of header input +*/ +NTSTATUS http_parse_header(struct websrv_context *web, const char *line) +{ + if (line[0] == 0) { + web->input.end_of_headers = True; + } else if (strncasecmp(line,"GET ", 4)==0) { + web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t")); + } else if (strncasecmp(line,"POST ", 5)==0) { + web->input.post_request = True; + web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t")); + } else if (strchr(line, ':') == NULL) { + http_error(web, 400, "This server only accepts GET and POST requests"); + return NT_STATUS_INVALID_PARAMETER; + } else if (strncasecmp(line,"Content-Length: ", 16)==0) { + web->input.content_length = strtoul(&line[16], NULL, 10); + } else { +#define PULL_HEADER(v, s) do { \ + if (strncmp(line, s, strlen(s)) == 0) { \ + web->input.v = talloc_strdup(web, &line[strlen(s)]); \ + return NT_STATUS_OK; \ + } \ +} while (0) + PULL_HEADER(content_type, "Content-Type: "); + PULL_HEADER(user_agent, "User-Agent: "); + PULL_HEADER(referer, "Referer: "); + PULL_HEADER(host, "Host: "); + PULL_HEADER(accept_encoding, "Accept-Encoding: "); + } + + /* ignore all other headers for now */ + return NT_STATUS_OK; +} + + diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c new file mode 100644 index 0000000000..9d161bdd8a --- /dev/null +++ b/source4/web_server/web_server.c @@ -0,0 +1,252 @@ +/* + Unix SMB/CIFS implementation. + + web server startup + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; 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. + + 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "smbd/service_task.h" +#include "smbd/service_stream.h" +#include "web_server/web_server.h" +#include "lib/events/events.h" +#include "system/filesys.h" + +/* don't allow connections to hang around forever */ +#define HTTP_TIMEOUT 30 + +/* + destroy a web connection +*/ +static int websrv_destructor(void *ptr) +{ + struct websrv_context *web = talloc_get_type(ptr, struct websrv_context); + if (web->output.fd != -1) { + close(web->output.fd); + } + return 0; +} + +/* + called when a connection times out. This prevents a stuck connection + from hanging around forever +*/ +static void websrv_timeout(struct event_context *event_context, + struct timed_event *te, + struct timeval t, void *private) +{ + struct websrv_context *web = talloc_get_type(private, struct websrv_context); + stream_terminate_connection(web->conn, "websrv_context: timeout"); +} + +/* + called when a web connection becomes readable +*/ +static void websrv_recv(struct stream_connection *conn, uint16_t flags) +{ + struct websrv_context *web = talloc_get_type(conn->private, + struct websrv_context); + NTSTATUS status; + uint8_t buf[1024]; + size_t nread; + uint8_t *p; + DATA_BLOB b; + + /* not the most efficient http parser ever, but good enough for us */ + status = socket_recv(conn->socket, buf, sizeof(buf), &nread, 0); + if (NT_STATUS_IS_ERR(status)) goto failed; + if (!NT_STATUS_IS_OK(status)) return; + + status = data_blob_append(web, &web->input.partial, buf, nread); + if (!NT_STATUS_IS_OK(status)) goto failed; + + /* parse any lines that are available */ + b = web->input.partial; + while (!web->input.end_of_headers && + (p=memchr(b.data, '\n', b.length))) { + const char *line = b.data; + *p = 0; + if (p != b.data && p[-1] == '\r') { + p[-1] = 0; + } + status = http_parse_header(web, line); + if (!NT_STATUS_IS_OK(status)) return; + b.length -= (p - b.data) + 1; + b.data = p+1; + } + + /* keep any remaining bytes in web->input.partial */ + if (b.length == 0) { + b.data = NULL; + } + b = data_blob_talloc(web, b.data, b.length); + data_blob_free(&web->input.partial); + web->input.partial = b; + + /* we finish when we have both the full headers (terminated by + a blank line) and any post data, as indicated by the + content_length */ + if (web->input.end_of_headers && + web->input.partial.length == web->input.content_length) { + EVENT_FD_NOT_READABLE(web->conn->event.fde); + http_process_input(web); + } + return; + +failed: + stream_terminate_connection(conn, "websrv_recv: failed\n"); +} + + +/* + called when a web connection becomes writable +*/ +static void websrv_send(struct stream_connection *conn, uint16_t flags) +{ + struct websrv_context *web = talloc_get_type(conn->private, + struct websrv_context); + NTSTATUS status; + size_t nsent; + DATA_BLOB b; + + b = web->output.content; + b.data += web->output.nsent; + b.length -= web->output.nsent; + + status = socket_send(conn->socket, &b, &nsent, 0); + if (NT_STATUS_IS_ERR(status)) { + stream_terminate_connection(web->conn, "socket_send: failed"); + return; + } + if (!NT_STATUS_IS_OK(status)) { + return; + } + + web->output.nsent += nsent; + + /* possibly read some more raw data from a file */ + if (web->output.content.length == web->output.nsent && + web->output.fd != -1) { + uint8_t buf[2048]; + ssize_t nread; + + data_blob_free(&web->output.content); + web->output.nsent = 0; + + nread = read(web->output.fd, buf, sizeof(buf)); + if (nread == 0) { + close(web->output.fd); + web->output.fd = -1; + } + if (nread == -1 && errno == EINTR) { + return; + } + web->output.content = data_blob_talloc(web, buf, nread); + } + + if (web->output.content.length == web->output.nsent) { + stream_terminate_connection(web->conn, NULL); + } +} + +/* + establish a new connection to the web server +*/ +static void websrv_accept(struct stream_connection *conn) +{ + struct websrv_context *web; + + web = talloc_zero(conn, struct websrv_context); + if (web == NULL) goto failed; + + web->conn = conn; + conn->private = web; + web->output.fd = -1; + talloc_set_destructor(web, websrv_destructor); + + event_add_timed(conn->event.ctx, web, + timeval_current_ofs(HTTP_TIMEOUT, 0), + websrv_timeout, web); + return; + +failed: + talloc_free(conn); +} + + +static const struct stream_server_ops web_stream_ops = { + .name = "web", + .accept_connection = websrv_accept, + .recv_handler = websrv_recv, + .send_handler = websrv_send, +}; + +/* + startup the web server task +*/ +static void websrv_task_init(struct task_server *task) +{ + NTSTATUS status; + uint16_t port = lp_swat_port(); + const struct model_ops *model_ops; + + /* run the web server as a single process */ + model_ops = process_model_byname("single"); + if (!model_ops) goto failed; + + if (lp_interfaces() && lp_bind_interfaces_only()) { + int num_interfaces = iface_count(); + int i; + for(i = 0; i < num_interfaces; i++) { + const char *address = iface_n_ip(i); + status = stream_setup_socket(task->event_ctx, model_ops, + &web_stream_ops, + "ipv4", address, + &port, task); + if (!NT_STATUS_IS_OK(status)) goto failed; + } + } else { + status = stream_setup_socket(task->event_ctx, model_ops, + &web_stream_ops, + "ipv4", lp_socket_address(), + &port, task); + if (!NT_STATUS_IS_OK(status)) goto failed; + } + + return; + +failed: + task_terminate(task, "Failed to startup web server task"); +} + + +/* + called on startup of the web server service It's job is to start + listening on all configured sockets +*/ +static NTSTATUS websrv_init(struct event_context *event_context, + const struct model_ops *model_ops) +{ + return task_server_startup(event_context, model_ops, websrv_task_init); +} + +/* called at smbd startup - register ourselves as a server service */ +NTSTATUS server_service_web_init(void) +{ + return register_server_service("web", websrv_init); +} diff --git a/source4/web_server/web_server.h b/source4/web_server/web_server.h new file mode 100644 index 0000000000..0202c91ef7 --- /dev/null +++ b/source4/web_server/web_server.h @@ -0,0 +1,49 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; 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. + + 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "request.h" +#include "smbd/process_model.h" + +/* + context of one open web connection +*/ +struct websrv_context { + struct stream_connection *conn; + struct { + DATA_BLOB partial; + BOOL end_of_headers; + char *url; + unsigned content_length; + BOOL post_request; + const char *content_type; + const char *query_string; + const char *user_agent; + const char *referer; + const char *host; + const char *accept_encoding; + } input; + struct { + DATA_BLOB content; + int fd; + unsigned nsent; + int response_code; + const char **headers; + } output; +}; |