r6981: first version of the builtin web server for Samba4
authorAndrew Tridgell <tridge@samba.org>
Thu, 26 May 2005 01:06:32 +0000 (01:06 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:17:01 +0000 (13:17 -0500)
This includes an embedded server side scripting system called 'esp'
(see http://www.appwebserver.org/products/esp/esp.html) and javascript
based scripting language called 'esj' (see
http://www.appwebserver.org/products/ejs/ejs.html)

The justification for including this scripting language is that it
should make it much easier to write a high quality web interface for
Samba4. The scripting language can call into any Samba4 library code
(so for example it will be able to make ldb and loadparm calls), plus
it provides easy support for forms, cookies, sessions etc.

There is still quite a bit more work to do on the web server, but
there is enough here now for people to look at and comment. I will be
committing some sample web pages that test esp functionality shortly.

25 files changed:
source/build/smb_build/main.pm
source/include/structs.h
source/param/loadparm.c
source/smbd/config.mk
source/web_server/config.mk [new file with mode: 0644]
source/web_server/ejs/config.h [new file with mode: 0644]
source/web_server/ejs/ejs.c [new file with mode: 0644]
source/web_server/ejs/ejs.h [new file with mode: 0644]
source/web_server/ejs/ejsInternal.h [new file with mode: 0644]
source/web_server/ejs/ejsLex.c [new file with mode: 0644]
source/web_server/ejs/ejsParser.c [new file with mode: 0644]
source/web_server/ejs/ejsProcs.c [new file with mode: 0644]
source/web_server/ejs/miniMpr.c [new file with mode: 0644]
source/web_server/ejs/miniMpr.h [new file with mode: 0644]
source/web_server/ejs/mpr.h [new file with mode: 0644]
source/web_server/ejs/mprOs.h [new file with mode: 0644]
source/web_server/ejs/var.c [new file with mode: 0644]
source/web_server/ejs/var.h [new file with mode: 0644]
source/web_server/esp/esp.c [new file with mode: 0644]
source/web_server/esp/esp.h [new file with mode: 0644]
source/web_server/esp/espEnv.h [new file with mode: 0644]
source/web_server/esp/espProcs.c [new file with mode: 0644]
source/web_server/http.c [new file with mode: 0644]
source/web_server/web_server.c [new file with mode: 0644]
source/web_server/web_server.h [new file with mode: 0644]

index c13fa2cb2346321652e62ccf212223a82e2072df..75e2a62d3ce7b79758e991477e6db6d8ade49c1a 100644 (file)
@@ -41,6 +41,7 @@ sub smb_build_main($)
                "smb_server/config.mk",
                "rpc_server/config.mk",
                "ldap_server/config.mk",
+               "web_server/config.mk",
                "winbind/config.mk",
                "nbt_server/config.mk",
                "cldap_server/config.mk",
index 4124b19e80fb24969fc8dada99f8c69c49c04400..b34dbff663b0c8b23bb76eb67e8c9628a197e875 100644 (file)
@@ -211,3 +211,4 @@ struct wrepl_pull_names;
 struct arcfour_state;
 
 union libnet_SamDump;
+struct websrv_context;
index 649c28098ce7b6fea2112cc122ffca2e97e0634f..956c42881bc90982114f0e2a2ee042dbf121a0c1 100644 (file)
@@ -211,6 +211,7 @@ typedef struct
        BOOL bWinbindUseDefaultDomain;
        char *szIDMapBackend;
        char *szGuestaccount;
+       char *swat_directory;
        int max_mux;
        int max_xmit;
        int pwordlevel;
@@ -238,6 +239,7 @@ typedef struct
        int nbt_port;
        int dgram_port;
        int cldap_port;
+       int swat_port;
        char *socket_options;
        BOOL bDNSproxy;
        BOOL bWINSsupport;
@@ -621,6 +623,8 @@ static struct parm_struct parm_table[] = {
        {"nbt port", P_INTEGER, P_GLOBAL, &Globals.nbt_port, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
        {"dgram port", P_INTEGER, P_GLOBAL, &Globals.dgram_port, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
        {"cldap port", P_INTEGER, P_GLOBAL, &Globals.cldap_port, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"swat port", P_INTEGER, P_GLOBAL, &Globals.swat_port, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"swat directory", P_STRING, P_GLOBAL, &Globals.swat_directory, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
        {"large readwrite", P_BOOL, P_GLOBAL, &Globals.bLargeReadwrite, NULL, NULL, FLAG_DEVELOPER},
        {"max protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_DEVELOPER},
        {"min protocol", P_ENUM, P_GLOBAL, &Globals.minprotocol, NULL, enum_protocol, FLAG_DEVELOPER},
@@ -938,7 +942,7 @@ static void init_globals(void)
        do_parameter("max connections", "-1");
 
        do_parameter("dcerpc endpoint servers", "epmapper srvsvc wkssvc rpcecho samr netlogon lsarpc spoolss drsuapi winreg dssetup");
-       do_parameter("server services", "smb rpc nbt ldap cldap");
+       do_parameter("server services", "smb rpc nbt ldap cldap web");
        do_parameter("auth methods", "anonymous sam_ignoredomain");
        do_parameter("smb passwd file", dyn_SMB_PASSWD_FILE);
        do_parameter("private dir", dyn_PRIVATE_DIR);
@@ -1057,6 +1061,8 @@ static void init_globals(void)
        do_parameter("nbt port", "137");
        do_parameter("dgram port", "138");
        do_parameter("cldap port", "389");
+       do_parameter("swat port", "901");
+       do_parameter_var("swat directory", "%s%s", dyn_LIBDIR, "/swat");
 
        do_parameter("nt status support", "True");
 
@@ -1160,7 +1166,9 @@ FN_GLOBAL_LIST(lp_smb_ports, &Globals.smb_ports)
 FN_GLOBAL_INTEGER(lp_nbt_port, &Globals.nbt_port)
 FN_GLOBAL_INTEGER(lp_dgram_port, &Globals.dgram_port)
 FN_GLOBAL_INTEGER(lp_cldap_port, &Globals.cldap_port)
+FN_GLOBAL_INTEGER(lp_swat_port, &Globals.swat_port)
 FN_GLOBAL_STRING(lp_dos_charset, &Globals.dos_charset)
+FN_GLOBAL_STRING(lp_swat_directory, &Globals.swat_directory)
 FN_GLOBAL_STRING(lp_unix_charset, &Globals.unix_charset)
 FN_GLOBAL_STRING(lp_display_charset, &Globals.display_charset)
 FN_GLOBAL_STRING(lp_logfile, &Globals.szLogFile)
index 081e8d14950c1ea6b2e3113d503c952994edc65b..3da36077e1209539c59d73cdc49b2e23478fb38c 100644 (file)
@@ -60,6 +60,16 @@ REQUIRED_SUBSYSTEMS = \
 # End MODULE server_service_cldapd
 ################################################
 
+################################################
+# Start MODULE server_service_web
+[MODULE::server_service_web]
+INIT_FUNCTION = server_service_web_init
+SUBSYSTEM = SERVER_SERVICE
+REQUIRED_SUBSYSTEMS = \
+               WEB
+# End MODULE server_service_web
+################################################
+
 #######################
 # Start SUBSYSTEM SERVICE
 [SUBSYSTEM::SERVER_SERVICE]
diff --git a/source/web_server/config.mk b/source/web_server/config.mk
new file mode 100644 (file)
index 0000000..34d8aff
--- /dev/null
@@ -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/source/web_server/ejs/config.h b/source/web_server/ejs/config.h
new file mode 100644 (file)
index 0000000..ec35089
--- /dev/null
@@ -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/source/web_server/ejs/ejs.c b/source/web_server/ejs/ejs.c
new file mode 100644 (file)
index 0000000..2d85ad1
--- /dev/null
@@ -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/source/web_server/ejs/ejs.h b/source/web_server/ejs/ejs.h
new file mode 100644 (file)
index 0000000..c1d087c
--- /dev/null
@@ -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/source/web_server/ejs/ejsInternal.h b/source/web_server/ejs/ejsInternal.h
new file mode 100644 (file)
index 0000000..fe79afb
--- /dev/null
@@ -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/source/web_server/ejs/ejsLex.c b/source/web_server/ejs/ejsLex.c
new file mode 100644 (file)
index 0000000..24e48d5
--- /dev/null
@@ -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/source/web_server/ejs/ejsParser.c b/source/web_server/ejs/ejsParser.c
new file mode 100644 (file)
index 0000000..a7d27fb
--- /dev/null
@@ -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/source/web_server/ejs/ejsProcs.c b/source/web_server/ejs/ejsProcs.c
new file mode 100644 (file)
index 0000000..db9e506
--- /dev/null
@@ -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/source/web_server/ejs/miniMpr.c b/source/web_server/ejs/miniMpr.c
new file mode 100644 (file)
index 0000000..38241e5
--- /dev/null
@@ -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/source/web_server/ejs/miniMpr.h b/source/web_server/ejs/miniMpr.h
new file mode 100644 (file)
index 0000000..28a990c
--- /dev/null
@@ -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/source/web_server/ejs/mpr.h b/source/web_server/ejs/mpr.h
new file mode 100644 (file)
index 0000000..848518b
--- /dev/null
@@ -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, 
+