summaryrefslogtreecommitdiff
path: root/source4/web_server
diff options
context:
space:
mode:
Diffstat (limited to 'source4/web_server')
-rw-r--r--source4/web_server/config.mk39
-rw-r--r--source4/web_server/ejs/config.h147
-rw-r--r--source4/web_server/ejs/ejs.c1058
-rw-r--r--source4/web_server/ejs/ejs.h131
-rw-r--r--source4/web_server/ejs/ejsInternal.h292
-rw-r--r--source4/web_server/ejs/ejsLex.c910
-rw-r--r--source4/web_server/ejs/ejsParser.c2358
-rw-r--r--source4/web_server/ejs/ejsProcs.c705
-rw-r--r--source4/web_server/ejs/miniMpr.c512
-rw-r--r--source4/web_server/ejs/miniMpr.h287
-rw-r--r--source4/web_server/ejs/mpr.h2259
-rw-r--r--source4/web_server/ejs/mprOs.h627
-rw-r--r--source4/web_server/ejs/var.c2161
-rw-r--r--source4/web_server/ejs/var.h482
-rw-r--r--source4/web_server/esp/esp.c1042
-rw-r--r--source4/web_server/esp/esp.h279
-rw-r--r--source4/web_server/esp/espEnv.h128
-rw-r--r--source4/web_server/esp/espProcs.c230
-rw-r--r--source4/web_server/http.c627
-rw-r--r--source4/web_server/web_server.c252
-rw-r--r--source4/web_server/web_server.h49
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;
+};