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.
(This used to be commit 26f0ba92c0c565ac9e4cb5a079d795d4262497dd)

25 files changed:
source4/build/smb_build/main.pm
source4/include/structs.h
source4/param/loadparm.c
source4/smbd/config.mk
source4/web_server/config.mk [new file with mode: 0644]
source4/web_server/ejs/config.h [new file with mode: 0644]
source4/web_server/ejs/ejs.c [new file with mode: 0644]
source4/web_server/ejs/ejs.h [new file with mode: 0644]
source4/web_server/ejs/ejsInternal.h [new file with mode: 0644]
source4/web_server/ejs/ejsLex.c [new file with mode: 0644]
source4/web_server/ejs/ejsParser.c [new file with mode: 0644]
source4/web_server/ejs/ejsProcs.c [new file with mode: 0644]
source4/web_server/ejs/miniMpr.c [new file with mode: 0644]
source4/web_server/ejs/miniMpr.h [new file with mode: 0644]
source4/web_server/ejs/mpr.h [new file with mode: 0644]
source4/web_server/ejs/mprOs.h [new file with mode: 0644]
source4/web_server/ejs/var.c [new file with mode: 0644]
source4/web_server/ejs/var.h [new file with mode: 0644]
source4/web_server/esp/esp.c [new file with mode: 0644]
source4/web_server/esp/esp.h [new file with mode: 0644]
source4/web_server/esp/espEnv.h [new file with mode: 0644]
source4/web_server/esp/espProcs.c [new file with mode: 0644]
source4/web_server/http.c [new file with mode: 0644]
source4/web_server/web_server.c [new file with mode: 0644]
source4/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/source4/web_server/config.mk b/source4/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/source4/web_server/ejs/config.h b/source4/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/source4/web_server/ejs/ejs.c b/source4/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/source4/web_server/ejs/ejs.h b/source4/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/source4/web_server/ejs/ejsInternal.h b/source4/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/source4/web_server/ejs/ejsLex.c b/source4/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/source4/web_server/ejs/ejsParser.c b/source4/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/source4/web_server/ejs/ejsProcs.c b/source4/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/source4/web_server/ejs/miniMpr.c b/source4/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/source4/web_server/ejs/miniMpr.h b/source4/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/source4/web_server/ejs/mpr.h b/source4/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, 
+                                               char *msg);
+       void                    enableTimeStamps(bool on);
+       void                    logConfig();
+       void                    rotate();
+       void                    shuttingDown();
+       int                             setLogSpec(char *path, int maxSize);
+       int                             start();
+       void                    stop();
+       void                    writeTimeStamp();
+};
+
+
+class MprLogToWindow : public MprLogListener {
+  private:
+  public:
+                                       MprLogToWindow();               ///< Constructor
+                                       ~MprLogToWindow();              ///< Destructor
+       void                    logEvent(char *module, int flags, int level, char *thread, 
+                                               char *msg);
+};
+
+
+//
+//     Overall logging service
+//
+class MprLogService {
+  private:
+       MprList                 listeners;                      // Users registered for output
+       uint                    defaultLevel;           // Default level for modules
+       MprLogModule    *defaultModule;         // Default log module for this log
+       MprList                 moduleList;                     // List of modules to trace 
+       char                    *moduleSpecs;           // Module level spec string
+       char                    *logSpec;                       // Saved logging spec string
+       bool                    logging;                        // Logging is enabled
+#if BLD_FEATURE_MULTITHREAD
+       MprMutex                *mutex;                         // Mutex lock 
+#endif
+
+  public:
+                                       MprLogService();
+                                       ~MprLogService(void);
+       void                    addListener(MprLogListener *lp);
+       void                    insertModule(MprLogModule *module);
+       void                    error(char *file, int line, int flags, char *fmt, 
+                                               va_list args);
+       MprLogModule    *getDefaultModule() { return defaultModule; }; 
+       int                             getDefaultLevel() { return defaultLevel; };
+       char                    *getLogSpec() { return logSpec; };
+       char                    *getModuleSpecs();
+       bool                    isLogging();
+       void                    removeListener(MprLogListener *lp);
+       void                    removeModule(MprLogModule *module);
+       void                    setDefaultLevel(int l);
+       void                    setDefaultModule(MprLogModule *m) { defaultModule = m; };
+       int                             setLogSpec(char *fileSpec);
+       void                    shuttingDown();
+       void                    start();
+       void                    stop();
+       void                    traceCore(int level, int flags, MprLogModule *module, 
+                                               char *fmt, va_list ap);
+       void                    writeHeader();
+       void                    writeLogStamp();
+
+#if BLD_FEATURE_MULTITHREAD
+       void                    lock() { 
+                                               if (mutex) {
+                                                       mutex->lock(); 
+                                               }
+                                       };
+       void                    unlock() { 
+                                               if (mutex) {
+                                                       mutex->unlock(); 
+                                               }
+                                       };
+#else
+       inline void             lock() {};
+       inline void             unlock() {};
+#endif
+
+  private:
+       void                    output(MprLogModule *module, int flags, int level, 
+                                               char *msg);
+       void                    breakpoint(char *file, int line);
+};
+
+extern void            mprLog(int level, MprLogModule *module, char *fmt, ...);
+extern void            mprLog(int level, int flags, MprLogModule *module, 
+                                       char *fmt, ...);
+extern void            mprLog(char *fmt, ...);
+
+#else // !BLD_FEATURE_LOG
+//
+//     If logging is not enabled, we inline these functions to nothing
+//
+class MprLogModule { 
+       void *x; 
+  public:
+       MprLogModule(char *name) {}
+};
+inline void            mprLog(int level, MprLogModule *module, char *fmt, ...) {};
+inline void            mprLog(char *fmt, ...) {}
+extern "C" void                mprLog(int level, char *fmt, ...);
+#endif
+
+///////////////////////////////// MprLogModule /////////////////////////////////
+#if BLD_FEATURE_LOG
+//
+//     Class to describe a trace log module
+//
+
+class MprLogModule : public MprLink {
+  private:
+       MprStr                  name;
+       int                             level;                          // Current trace output level 
+       bool                    enabled;
+
+  public:
+                                       MprLogModule(char *name);
+                                       ~MprLogModule();
+       void                    innerMprLogModule(char *name);
+       int                             getLevel(void) { return level; };
+       void                    disable() { enabled = 0; };
+       void                    enable() { enabled = 1; };
+       int                             getEnabled() { return enabled; };
+       char                    *getName() { return name; };
+       void                    setLevel(int l) { level = l; };
+};
+
+#endif // BLD_FEATURE_LOG
+//////////////////////////////////// MprBuf ////////////////////////////////////
+
+typedef int                    (*MprBufProc)(MprBuf* bp, void *arg);
+
+class MprBuf {
+  private:
+       uchar                   *buf;                           // Actual buffer for data 
+       uchar                   *endbuf;                        // Pointer one past the end of buffer 
+       uchar                   *start;                         // Pointer to next data char 
+       uchar                   *end;                           // Pointer one past the last data char 
+       int                             buflen;                         // Current size of buffer 
+       int                             maxsize;                        // Max size the buffer can ever grow 
+       int                             growBy;                         // Next growth increment to use 
+       MprBufProc              refillProc;                     // Function to refill the buffer
+       void                    *refillArg;                     // Arg to refill proc
+
+  public:
+                                       MprBuf();
+                                       MprBuf(int initialSize, int maxsize = -1);
+                                       ~MprBuf();
+       inline void             addNull() {
+                                               *((char*) end) = (char) '\0';
+                                       };
+       void                    adjustStart(int size);
+       void                    adjustEnd(int size);
+       void                    copyDown();
+       inline void             flush() {
+                                               start = buf;
+                                               end = buf;
+                                       };
+       inline int              get() {
+                                               if (start == end) {
+                                                       return -1;
+                                               }
+                                               int     c = (uchar) *start++;
+                                               if (start >= endbuf) {
+                                                       start = buf;
+                                               }
+                                               return c;
+                                       };
+       int                             get(uchar *buf, int len);
+       inline char             *getBuf() { return (char*) buf; };
+       inline char             *getEnd() { return (char*) end; };
+       inline char             *getEndBuf() { return (char*) endbuf; };
+       inline int              getLength() {
+                                               return ((start > end) ? 
+                                                       (buflen + (end - start)) : (end - start));
+                                       };
+       inline int              getLinearData() {
+                                               return min((endbuf - start), getLength());
+                                       }
+       inline int              getLinearSpace() {
+                                               int len = getLength();
+                                               int space = buflen - len - 1;
+                                               return min((endbuf - end), space);
+                                       }
+       inline int              getSpace() {
+                                               return buflen - getLength() - 1;
+                                       };
+       inline char             *getStart() { return (char*) start; };
+       inline int              getSize() { return buflen; };
+       int                             insert(char c);
+       inline int              look() {
+                                               if (start == end) {
+                                                       return -1;
+                                               }
+                                               return* start;
+                                       }
+       int                             lookLast();
+       int                             put(char c);
+       int                             put(char *str);
+       int                             putInt(int i);
+       int                             putFmt(char *fmt, ...);
+       int                             put(uchar *buf, int len);
+       inline int              refill() { 
+                                               return (refillProc) ? 
+                                                       (refillProc)(this, refillArg) : 0; 
+                                       };
+       inline void             resetIfEmpty() {
+                                               if (getLength() == 0) {
+                                                       flush();
+                                               }
+                                       };
+       void                    setBuf(uchar *userBuf, int size);
+       void                    setBuf(int initialSize, int maxSize);
+       void                    setRefillProc(MprBufProc fn, void *arg) { 
+                                               refillProc = fn; 
+                                               refillArg = arg; 
+                                       };
+       uchar                   *takeBuffer();
+  private:
+       int                             grow();
+};
+
+//////////////////////////////// MprCmdService /////////////////////////////////
+#if BLD_FEATURE_CGI_MODULE
+//
+//     Flags for MprCmd
+//
+#define MPR_CMD_BACKGROUND     0x1                     // Continue running if MPR exits 
+#define MPR_CMD_REAP_MAX       24
+
+//
+//     Cmd service control
+//
+
+class MprCmdService {
+  private:
+       MprList                 cmdList;                        // List of commands
+       MprMutex                *mutex;                         // Multi-thread sync
+       ulong                   completedCmds[MPR_CMD_REAP_MAX];
+       int                             exitStatus[MPR_CMD_REAP_MAX];
+       MprTimer                *timer;                         // Timer to poll for child completion
+
+  public:
+                                       MprCmdService();
+                                       ~MprCmdService();
+       void                    cmdWatcher();
+       void                    cmdWatcherTimer(MprTimer *tp);
+       void                    insertCmd(MprCmd* rp);
+       void                    removeCmd(MprCmd* rp);
+       int                             start();
+       void                    startWatcher();
+       int                             stop();
+
+#if LINUX || SOLARIS || VXWORKS
+       void                    processStatus(int pid, int status);
+       void                    initSignals();
+#endif
+
+#if BLD_FEATURE_MULTITHREAD
+       void                    lock() { mutex->lock(); };
+       void                    unlock() { mutex->unlock(); };
+#else
+       inline void             lock() {};
+       inline void             unlock() {};
+#endif
+};
+
+//////////////////////////////////// MprCmd ////////////////////////////////////
+
+//
+//     Cmd procs must return the number of bytes read or -1 for errors.
+//
+typedef void   (*MprCmdProc)(MprCmd* rp, void *data);
+
+#define MPR_CMD_EOF                    -1                      // EOF return value
+
+//
+//     Flags
+//
+#define MPR_CMD_DETACHED       0x1
+#define MPR_CMD_NEW_SESSION    0x2
+#define MPR_CMD_CHDIR          0x4
+#define MPR_CMD_WAIT           0x8
+#define MPR_CMD_SHOW           0x10
+#define MPR_CMD_USER_FLAGS     0x1f
+
+#define MPR_CMD_COMPLETE       0x40
+#define MPR_CMD_DISPOSED       0x80
+#define MPR_CMD_STDIO_MADE     0x100
+
+//
+//     Indicies for clientFd and serverFd
+//
+#define MPR_CMD_IN                     0                       // Stdin for the server
+#define MPR_CMD_OUT                    1                       // Stdout for the server
+#define MPR_CMD_MAX_FD         2
+
+#define MPR_CMD_WATCHER_NAP            100             // Timeout for watcher while polling
+#define MPR_CMD_WATCHER_TIMEOUT        5000    // Timeout for child to start
+
+class MprCmdFiles {
+  public:
+       MprStr                  name[MPR_CMD_MAX_FD];
+       int                             fd[MPR_CMD_MAX_FD];
+       int                             clientFd[MPR_CMD_MAX_FD];
+  public:
+                                       MprCmdFiles();
+                                       ~MprCmdFiles();
+};
+
+//
+//     MprCmd class
+//
+class MprCmd : public MprLink {
+  private:
+       MprStr                  cwd;                            // Current working dir for the process
+       void                    *data;                          // Callback user data
+       int                             exitStatus;                     // Command exit status
+       int                             flags;                          // Control flags (userFlags not here)
+       MprCmdFiles             files;                          // Stdin, stdout for the command
+       int                             inUse;                          // Used by dispose()
+       MprLogModule    *log;
+       MprCmdProc              cmdDoneProc;            // Handler for client completion
+
+#if VXWORKS
+       MprSelectHandler        
+                                       *handler;
+       int                             waitFd;                         // Pipe to await child exit
+#endif
+       ulong                   process;                        // Id/handle of the created process
+
+#if BLD_FEATURE_MULTITHREAD
+#if VXWORKS
+       MprCond                 *startCond;
+       MprCond                 *exitCond;
+#endif
+       MprMutex                *mutex;
+#endif
+
+  public:
+                                       MprCmd();
+                                       ~MprCmd();
+       void                    closeWriteFd();
+       int                             dispose();
+       int                             getExitCode(int *code = 0);
+       int                             getWriteFd();
+       int                             getReadFd();
+       void                    invokeCallback(MprMutex *callerMutex);
+       bool                    isRunning();
+       int                             makeStdio();
+       void                    setCwd(char *dir);
+       int                             setInuse();
+       int                             start(char *cmd, int flags);
+       int                             start(char *cmd, char **argv, char **envp, 
+                                               MprCmdProc completionProc, void *data, int flags);
+       void                    stop();
+
+       //
+       //      Internal API
+       //
+       ulong                   getProcess() { return process; };
+       void                    reset();
+       void                    resetFiles();
+       void                    setExitStatus(int status);
+
+  private:
+       int                             waitForChild(int timeout);
+
+#if BLD_FEATURE_MULTITHREAD
+       void                    lock() { mutex->lock(); };
+       void                    unlock() { mutex->unlock(); };
+#else
+       inline void             lock() {};
+       inline void             unlock() {};
+#endif
+
+       friend class    MprCmdService;
+};
+
+#endif // BLD_FEATURE_CGI_MODULE
+////////////////////////////// MprSelectService ////////////////////////////////
+//
+//     Standard select bit mask options
+//
+#define MPR_READABLE                   0x2
+#define MPR_WRITEABLE                  0x4
+#define MPR_EXCEPTION                  0x8
+
+typedef void           (*MprSelectProc)(void *data, int mask, int isMprPoolThread);
+
+class MprSelectService {
+  private:
+       struct sockaddr_in 
+                                       breakAddress;           // Breakout port socket address
+       int                             breakSock;                      // MprSocket to wakeup select service
+       int                             breakPort;                      // Port to talk to the select service
+       int                             breakRetries;           // Retry attempts to open breakout port
+       MprList                 list;                           // List of select handlers
+       int                             flags;                          // State flags
+       int                             maskGeneration;         // Generation number for mask changes
+       int                             listGeneration;         // Generation number for list changes
+       MprLogModule    *log;
+       int                             rebuildMasks;           // Complete select mask rebuild required
+       int                             delayedFds[FD_SETSIZE];
+       int                             maxDelayedFd;
+#if WIN
+       int                             sockMessage;            // MprSocket message for AsyncSelect
+       HWND                    hwnd;                           // Window handle to use for AsyncSelect
+#endif
+
+#if BLD_FEATURE_MULTITHREAD
+       MprCond                 *cond;                          // Wait for select to breakout
+       MprMutex                *mutex;                         // General multi-thread synchronization
+#endif
+
+  public:
+       //
+       //      Published API
+       //
+                                       MprSelectService();
+                                       ~MprSelectService();
+       int                             getFds(fd_set *readInterest, fd_set *writeInterest, 
+                                               fd_set *exceptInterest, int *maxFd, int *lastGet);
+       void                    serviceIO(int readyFds, fd_set *readFds, fd_set *writeFds, 
+                                               fd_set *exceptFds);
+       void                    serviceIO(int sockFd, int winMask);
+
+       //
+       //      Unpublished API
+       //
+       int                             insertHandler(MprSelectHandler *sp);
+       void                    awaken(int wait = 0);
+       void                    delayedClose(int fd);
+       bool                    getAsyncSelectMode();
+       int                             getFlags() { return flags; };
+       int                             modifyHandler(MprSelectHandler *sp, bool wakeUp);
+       void                    removeHandler(MprSelectHandler *sp);
+       MprLogModule    *getLog() { return log; };
+       int                             openBreakoutPort();
+       void                    setPort(int n);
+       int                             start();
+       int                             stop();
+
+#if BLD_FEATURE_MULTITHREAD
+       void                    lock() { mutex->lock(); };
+       void                    unlock() { mutex->unlock(); };
+#else
+       inline void             lock() {};
+       inline void             unlock() {};
+#endif
+
+#if WIN
+       HWND                    getHwnd() { return hwnd; };
+       int                             getMessage() { return sockMessage; };
+       void                    setHwnd(HWND h) { hwnd = (HWND) h; };
+       void                    setMessage(int m) { sockMessage = m; };
+       void                    setAsyncSelectMode(bool asyncSelect);
+#endif
+};
+
+////////////////////////////// MprSelectHandler ////////////////////////////////
+//
+//     Flags
+//
+#define MPR_SELECT_DISPOSED                    0x1
+#define MPR_SELECT_RUNNING                     0x2
+#define MPR_SELECT_CLOSEFD                     0x4
+#define MPR_SELECT_CLIENT_CLOSED       0x8     // Client disconnection received
+
+class MprSelectHandler : public MprLink {
+  private:
+       int                             desiredMask;            // Mask of desired events 
+       int                             disableMask;            // Mask of disabled events 
+       int                             fd;                                     // O/S File descriptor (sp->sock) 
+       int                             flags;                          // Control flags
+       void                    *handlerData;           // Argument to pass to proc. 
+       int                             inUse;                          // Used by dispose()
+       MprLogModule    *log;
+       int                             presentMask;            // Mask of events that have been seen 
+       int                             priority;                       // Priority if events handled by threads
+
+#if BLD_FEATURE_MULTITHREAD
+       MprCond                 *stoppingCond;          // Synchronization when stopping
+#endif
+
+  public:
+       MprSelectService *selectService;        // Select service pointer
+       MprSelectProc   proc;                           // Select handler procedure 
+
+  public:
+                                       MprSelectHandler(int fd, int mask, MprSelectProc proc, 
+                                               void *data, int priority);
+       bool                    dispose();
+       void                    disableEvents(bool wakeUp);
+       void                    enableEvents(bool wakeUp);
+       int                             getFd() { return fd; };
+       int                             getFlags() { return flags; };
+       void                    runHandler();
+       void                    selectProc(MprTask *tp);
+       void                    setInterest(int mask);
+       void                    setWinInterest();
+       void                    setProc(MprSelectProc newProc, int mask);
+       void                    setCloseOnDispose();
+       int                             stop(int timeout);
+
+  private:
+                                       ~MprSelectHandler();
+
+       friend class    MprSelectService;
+};
+
+//////////////////////////////// MprInterface //////////////////////////////////
+
+class MprInterface : public MprLink {
+  public:
+       MprStr                  name;                           // Interface name
+       MprStr                  ipAddr;
+       MprStr                  broadcast;
+       MprStr                  mask;
+  public:
+                                       MprInterface(char *name, char *ipAddr, char *bcast, 
+                                               char *mask);
+                                       ~MprInterface();
+};
+
+////////////////////////////// MprSocketService ////////////////////////////////
+
+typedef void           (*MprSocketIoProc)(void *data, MprSocket *sp, int mask, 
+                                               int isMprPoolThread);
+typedef void           (*MprSocketAcceptProc)(void *data, MprSocket *sp, char *ip, 
+                                               int port, MprSocket *lp, int isMprPoolThread);
+//
+//     Mpr socket service class
+//
+class MprSocketService {
+  private:
+       MprList                 socketList;                     // List of all sockets
+       MprList                 ipList;                         // List of ip addresses 
+       MprLogModule    *log;
+
+#if BLD_FEATURE_MULTITHREAD
+       MprMutex                *mutex;
+#endif
+
+  public:
+                                       MprSocketService();
+                                       ~MprSocketService();
+#if BLD_FEATURE_LOG
+       MprLogModule    *getLogModule() { return log; };
+#endif
+       MprList                 *getInterfaceList();
+       void                    insertMprSocket(MprSocket *sp);
+       void                    removeMprSocket(MprSocket *sp);
+       int                             start();
+       int                             stop();
+
+#if BLD_FEATURE_MULTITHREAD
+       void                    lock() { mutex->lock(); };
+       void                    unlock() { mutex->unlock(); };
+#else
+       inline void             lock() {};
+       inline void             unlock() {};
+#endif
+
+  private:
+       int                             getInterfaces();
+};
+
+////////////////////////////////// MprSocket ///////////////////////////////////
+//
+//     close flags
+//
+#define MPR_SHUTDOWN_READ              0
+#define MPR_SHUTDOWN_WRITE             1
+#define MPR_SHUTDOWN_BOTH              2
+
+//
+//     Flags
+//
+#define MPR_SOCKET_BLOCK               0x1                     // Use blocking I/O 
+#define MPR_SOCKET_BROADCAST   0x2                     // Broadcast mode 
+#define MPR_SOCKET_CLOSED              0x4                     // MprSocket has been closed 
+#define MPR_SOCKET_CONNECTING  0x8                     // MprSocket has been closed 
+#define MPR_SOCKET_DATAGRAM            0x10            // Use datagrams 
+#define MPR_SOCKET_EOF                 0x20            // Seen end of file 
+#define MPR_SOCKET_LISTENER            0x40            // MprSocket is server listener 
+#define MPR_SOCKET_NOREUSE             0x80            // Dont set SO_REUSEADDR option  
+#define MPR_SOCKET_NODELAY             0x100           // Disable Nagle algorithm 
+#define MPR_SOCKET_DISPOSED            0x200           // Delete requested
+
+#define MPR_SOCKET_READABLE            0x2
+#define MPR_SOCKET_WRITABLE            0x4
+#define MPR_SOCKET_EXCEPTION   0x8
+
+class MprSocket : public MprLink 
+{
+  private:
+       MprSocketAcceptProc 
+                                       acceptCallback;         // Accept callback 
+       void                    *acceptData;            // User accept callback data 
+       int                             currentEvents;          // Mask of ready events (FD_x) 
+       int                             error;                          // Last error 
+       MprSelectHandler *handler;                      // Select handler
+       int                             handlerMask;            // Handler events of interest 
+       int                             handlerPriority;        // Handler priority 
+       int                             interestEvents;         // Mask of events to watch for 
+       MprSocketIoProc ioCallback;                     // User I/O callback 
+       void                    *ioData;                        // User io callback data 
+       void                    *ioData2;                       // Secondary user io callback data 
+       int                             inUse;                          // In use counter. Used by dispose()
+       MprStr                  ipAddr;                         // Host IP address
+       MprLogModule    *log;                           // Pointer to MprSocketService module
+       int                             port;                           // Port to listen on 
+       int                             selectEvents;           // Events being selected 
+
+#if BLD_FEATURE_MULTITHREAD
+       MprMutex                *mutex;                         // Multi-thread sync
+#endif
+
+  protected:
+       int                             sock;                           // Actual socket handle 
+       int                             flags;                          // Current state flags 
+       bool                    secure;                         // MprSocket is using SSL
+
+  public:
+                                       MprSocket();
+       void                    forcedClose();
+       void                    acceptProc(int isMprPoolThread);
+       bool                    getEof();
+       int                             getError();
+       int                             getFlags();
+       char                    *getIpAddr() { return ipAddr; };
+       int                             getPort();
+       int                             getFd();
+       bool                    getBlockingMode();
+       void                    getAcceptCallback(MprSocketAcceptProc *fn, void **data);
+       void                    getCallback(MprSocketIoProc *fn, void **data, 
+                                               void **data2, int *mask);
+       bool                    isSecure() { return secure; };
+       int                             openServer(char *ipAddr, int portNum, 
+                                               MprSocketAcceptProc acceptFn, void *data, int flags);
+       int                             openClient(char *hostName, int portNum, int flags);
+       void                    setBlockingMode(bool on);
+       int                             setBufSize(int sendSize, int recvSize);
+       //      FUTURE -- rename: handler vs callback
+       void                    setCallback(MprSocketIoProc fn, void *data, 
+                                               void *data2, int mask, int pri = MPR_NORMAL_PRIORITY);
+       int                             write(char *s);
+
+       virtual                 ~MprSocket();
+       virtual void    close(int how);
+       virtual bool    dispose();
+       virtual void    ioProc(int mask, int isMprPoolThread);
+       virtual MprSocket       
+                                       *newSocket();
+       virtual int             read(char *buf, int len);
+       virtual int             write(char *buf, int len);
+
+#if BLD_FEATURE_MULTITHREAD
+       void                    lock() { mutex->lock(); };
+       void                    unlock() { mutex->unlock(); };
+#else
+       inline void             lock() {};
+       inline void             unlock() {};
+#endif
+
+  private:
+       void                    setMask(int handlerMask);
+       void                    setNoDelay(bool on);
+
+       friend class    MprSocketService;
+};
+
+//////////////////////////////// MprPoolService ////////////////////////////////
+
+#if BLD_DEBUG
+class MprPoolStats {
+  public:
+       int                             maxThreads;                     // Configured max number of threads
+       int                             minThreads;                     // Configured minimum
+       int                             numThreads;                     // Configured minimum
+       int                             maxUse;                         // Max used 
+       int                             pruneHighWater;         // Peak thread use in last minute
+       int                             idleThreads;            // Current idle
+       int                             busyThreads;            // Current busy
+};
+#endif
+
+//
+//     A task queue consists of a list of tasks and optional list of threads
+//
+typedef void           (*MprTaskProc)(void *data, MprTask *tp);
+
+//
+//     Class for the overall thread pool service
+//
+class MprPoolService {
+  protected:                                                   // Allow MprPoolThreads to access
+       MprStr                  name;                           // Name of pool
+       int                             nextTaskNum;            // Unique next task number
+       MprList                 runningTasks;           // List of executing tasks
+       int                             stackSize;                      // Stack size for worker threads 
+       MprList                 tasks;                          // Prioritized list of pending tasks
+
+#if BLD_FEATURE_MULTITHREAD
+       MprList                 busyThreads;            // List of threads to service tasks
+       MprList                 idleThreads;            // List of threads to service tasks
+       int                             maxThreads;                     // Max # threads in pool 
+       int                             maxUseThreads;          // Max threads ever used
+       int                             minThreads;                     // Max # threads in pool 
+       MprMutex                *mutex;                         // Per task synchronization
+       int                             nextThreadNum;          // Unique next thread number
+       int                             numThreads;                     // Current number of threads in pool
+       int                             pruneHighWater;         // Peak thread use in last minute
+       MprTimer                *pruneTimer;            // Timer for excess threads pruner
+       MprMutex                *incMutex;                      // Per task synchronization
+#endif
+
+  public:
+       MprLogModule    *log;
+
+  public:
+                                       MprPoolService(char *name);
+                                       ~MprPoolService();
+       int                             assignNextTask(MprPoolThread *pt);
+       void                    dequeueTask(MprTask *tp);
+#if BLD_DEBUG
+       void                    getStats(MprPoolStats *ps);
+#endif
+       void                    insertTask(MprTask *np, MprTask *tp);
+       void                    prune();
+       void                    queueTask(MprTask *tp);
+       void                    queueRunningTask(MprTask *tp);
+       void                    removeTask(MprTask *tp);
+       int                             runTasks();
+       void                    setStackSize(int n);
+       int                             start();
+       int                             stop(int timeout);
+
+#if BLD_FEATURE_MULTITHREAD
+       void                    dispatchTasks();
+       int                             getMinPoolThreads() { return minThreads; };
+       int                             getNumPoolThreads() { 
+                                               return idleThreads.getNumItems() +
+                                                               busyThreads.getNumItems();
+                                       };
+       int                             getMaxPoolThreads() { return maxThreads; };
+       int                             getNumIdleThreads() { return idleThreads.getNumItems(); };
+       void                    lock();
+       int                             getNextThreadNum();
+       void                    removeThread(MprPoolThread *pt);
+       void                    setMinPoolThreads(int n);
+       void                    setMaxPoolThreads(int n);
+       void                    unlock();
+#else
+       inline void             lock() {};
+       inline void             unlock() {};
+       int                             getMaxPoolThreads() { return 0; };
+#endif
+
+       friend class    MprPoolThread;
+       friend class    MprTask;
+};
+
+///////////////////////////////// MprPoolThread ////////////////////////////////
+#if BLD_FEATURE_MULTITHREAD
+//
+//     Flags
+//
+#define MPR_POOL_THREAD_SLEEPING       0x1
+
+//
+//     Class for each thread in the thread pool
+//
+class MprPoolThread : public MprLink {
+  private:
+       MprPoolService  *pool;                          // Which thread pool do we swim in
+       MprTask                 *currentTask;           // Current task being run
+       int                             flags;                          
+
+#if BLD_FEATURE_MULTITHREAD
+       MprThread               *thread;                        // Associated thread
+       MprCond                 *idleCond;                      // Used to wait for work
+#endif
+
+  public:
+                                       MprPoolThread(MprPoolService *pool, int stackSize);
+                                       ~MprPoolThread();
+       MprPoolService  *getMprPoolService() { return pool; };
+       MprThread               *getThread() { return thread; };
+       MprTask                 *getCurrentTask() { return currentTask; };
+       void                    makeIdle();
+       void                    start();
+       void                    setTask(MprTask *tp);
+       void                    threadMain();
+       void                    wakeup();
+};
+
+#endif
+/////////////////////////////////// MprTask ////////////////////////////////////
+//
+//     Flags
+//
+#define MPR_TASK_DISPOSED      0x1
+#define MPR_TASK_RUNNING       0x2
+
+//
+//     Class for each task (unit of work) 
+//
+class MprTask : public MprLink {
+  public:
+       void                    *data;                          // Task data 
+       int                             flags;                          // Control flags
+       int                             inUse;                          // In use counter. Used by dispose()
+       MprPoolService  *pool;                          // Managing pool
+       int                             priority;                       // Priority of event 
+       MprTaskProc             proc;                           // Procedure to service this event. 
+
+#if BLD_FEATURE_MULTITHREAD
+       MprPoolThread   *pt;                            // Pool thread servicing this task
+       MprCond                 *stoppingCond;          // Synchronization for timer->dispose()
+#endif
+
+  public:
+                                       MprTask(MprTaskProc proc, void *data, 
+                                               int priority = MPR_NORMAL_PRIORITY);
+                                       MprTask(MprPoolService *pool, MprTaskProc proc, 
+                                               void *data, int priority = MPR_NORMAL_PRIORITY);
+       bool                    dispose();
+       void                    start();
+       int                             stop(int timeout);
+
+#if BLD_FEATURE_MULTITHREAD
+       MprPoolThread   *getThread() { return pt; }; 
+#endif
+
+  private:
+                                       ~MprTask();
+       friend class    MprPoolThread;
+       friend class    MprPoolService;
+};
+
+/////////////////////////////// MprThreadService ///////////////////////////////
+#if BLD_FEATURE_MULTITHREAD
+// 
+//     Threading primitives
+//
+typedef void           (*MprThreadProc)(void *arg, MprThread *tp);
+
+class MprThreadService {
+  private:
+       MprList                 threads;                        // List of all threads
+       MprThread               *mainThread;            // Main application Mpr thread id 
+       MprMutex                *mutex;                         // Multi-thread sync
+
+  public:
+                                       MprThreadService();
+                                       ~MprThreadService();
+       MprThread               *getCurrentThread();
+       void                    insertThread(MprThread *tp);
+       void                    removeThread(MprThread *tp);
+       int                             start();
+       int                             stop(int timeout);
+
+       inline void     lock() { 
+                                               if (mutex) {
+                                                       mutex->lock(); 
+                                               }
+                                       };
+       inline void             unlock() { 
+                                               if (mutex) {
+                                                       mutex->unlock(); 
+                                               }
+                                       };
+};
+
+/////////////////////////////////// MprThread //////////////////////////////////
+
+class MprThread : public MprLink {
+  private:
+       #if WIN
+               int                     osThreadId;                     // O/S thread id
+               handle          threadHandle;           // Threads OS handle 
+       #endif
+       #if LINUX || MACOSX || SOLARIS
+               pthread_t       osThreadId;                     // O/S thread id 
+       #endif
+       #if VXWORKS
+               int                     osThreadId;                     // O/S thread id (same as pid)
+       #endif
+       void                    *data;                          // Data argument
+       MprThreadProc   entry;                          // Users thread entry point
+       MprStr                  name;                           // Name of thead for trace
+       MprMutex                *mutex;                         // Multi-thread synchronization
+       int                             pid;                            // Owning process id
+       int                             priority;                       // Current priority 
+       int                             stackSize;                      // Only VxWorks implements
+
+  public:
+                                       MprThread(int pri, char *name);
+                                       //      FUTURE -- move pri to last and default it.
+                                       MprThread(MprThreadProc proc, int pri, void *data, 
+                                               char *name, int stackSize = 0);
+                                       ~MprThread();
+       int                             getId() { return (int) osThreadId; };
+       char                    *getName() { return name; };
+       int                             getPriority() { return priority; };
+       void                    lock() { mutex->lock(); };
+       void                    setId(int id);
+       void                    setPriority(int priority);
+       void                    setStackSize(int size);
+       int                             start();
+       void                    threadProc();
+       void                    unlock() { mutex->unlock(); };
+  private:
+       int                             mapMprPriorityToOs(int mprPriority);
+       int                             mapOsPriorityToMpr(int nativePriority);
+};
+
+extern MprThread       *mprGetCurrentThread();
+extern int                     mprGetMaxPoolThreads();
+
+#endif // BLD_FEATURE_MULTITHREAD
+/////////////////////////////// MprTimerService ////////////////////////////////
+
+#define MPR_TIMER_TOLERANCE            2               // Used in timer calculations
+
+//
+//     Timer service. One per MPR 
+//
+class MprTimerService {
+  private:
+       int                             lastIdleTime;           // Last return value from getIdleTime()
+       int                             lastRanTimers;          // Last call to runTimers()
+       MprLogModule    *log;                           // Log module to identify trace
+       MprList                 timerList;                      // List of all timers
+
+#if BLD_FEATURE_MULTITHREAD
+       MprMutex                *mutex;                         // Multi-thread sync
+#endif
+
+  public:
+                                       MprTimerService();
+                                       ~MprTimerService();
+       void                    callTimer(MprTimer *tp);
+       int                             getIdleTime();
+       int                             runTimers();
+       int                             stop();
+       int                             start();
+
+#if BLD_FEATURE_MULTITHREAD
+       MprMutex                *getMutex() { return mutex; };
+       inline void             lock() { mutex->lock(); };
+       inline void             unlock() { mutex->unlock(); };
+#else
+       inline void             lock() {};
+       inline void             unlock() {};
+#endif
+
+  protected:
+       void                    updateSelect(MprTimer *tp);
+
+       friend class    MprTimer;
+};
+
+//////////////////////////////////// MprTimer //////////////////////////////////
+//
+//     MprTimer callback function prototype
+//
+typedef void           (*MprTimerProc)(void *data, MprTimer *tp);
+
+//
+//     MprTimer flags
+//
+#define MPR_TIMER_DISPOSED             0x1
+#define MPR_TIMER_RUNNING              0x2
+#define MPR_TIMER_TASK                 0x4
+#define MPR_TIMER_AUTO_RESCHED 0x8
+
+class MprTimer : public MprLink {
+  private:
+       void                    *data;                          // Argument to pass to callback
+       int                             flags;
+       int                             inUse;                          // In use counter. Used by dispose()
+       int                             period;                         // Reschedule period 
+       MprTimerProc    proc;                           // Callback procedure
+       MprTime                 time;                           // When timer is due to next run
+       MprTimerService *timerService;
+
+#if BLD_FEATURE_MULTITHREAD
+       MprCond                 *stoppingCond;          // Synchronization when stopping
+#endif
+
+  public:
+                                       MprTimer(int msec, MprTimerProc routine, void *arg, 
+                                               int userFlags = 0);
+       bool                    dispose();
+       int                             getPeriod() { return period; };
+       MprTimerService *getMprTimerService() { return timerService; };
+       void                    reschedule();
+       void                    reschedule(int msec);
+       int                             stop(int timeout);
+
+private:
+                                       ~MprTimer();
+       friend class    MprTimerService;
+};
+
+//////////////////////////////// MprWinService /////////////////////////////////
+#if BLD_FEATURE_RUN_AS_SERVICE && WIN
+
+class MprWinService {
+  private:
+       MprStr                  svcName;
+  public:
+                                       MprWinService(char *name);
+                                       ~MprWinService();
+       int                             install(char *displayName, char *cmd);
+       int                             registerService(HANDLE threadHandle, HANDLE waitEvent);
+       int                             remove(int code);
+       int                             startDispatcher(LPSERVICE_MAIN_FUNCTION svcMain);
+       int                             start();
+       int                             stop(int code);
+       void                    updateStatus(int status, int exitCode);
+};
+
+#endif
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////// Other C++ Stuff ////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+#if BLD_DEBUG && UNUSED
+#if LINUX || MACOSX
+#if MPR_CPU_IX86
+       inline int64 mprGetHiResTime() {
+               int64   now;
+               __asm__ __volatile__ ("rdtsc" : "=A" (now));
+               return now;
+       }
+#endif
+#endif
+#if WIN
+       inline int64 mprGetHiResTime() {
+               int64   now;
+               QueryPerformanceCounter((LARGE_INTEGER*) &now);
+               return now;
+       }
+#endif
+       
+////////////////////////////////////////////////////////////////////////////////
+
+inline int64 mprGetElapsedTime() 
+{
+       static int64    lastMark = 0;
+       int64                   now, elapsed;
+       
+       now = mprGetHiResTime();
+       if (now > lastMark) {
+               elapsed = now - lastMark;
+               lastMark = now;
+       } else {
+               elapsed = lastMark - now + 1;
+               lastMark = now;
+       }
+       return elapsed;
+};
+
+#endif // BLD_DEBUG && UNUSED
+#endif // __cplusplus
+
+////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////// C Prototypes //////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ *     Generic array type
+ */
+typedef struct MprArray {
+       int             max;                                    /**< Size of the handles array. */
+       int             used;                                   /**< Count of used entries in handles. */
+       void    **handles;
+} MprArray;
+
+#if BLD_FEATURE_SQUEEZE
+#define MPR_ARRAY_INCR         8
+#else
+#define MPR_ARRAY_INCR         16
+#endif
+
+//
+//     Array
+//
+extern MprArray        *mprCreateArray();
+extern void    mprDestroyArray(MprArray *array);
+extern int             mprAddToArray(MprArray *array, void *item);
+extern int             mprRemoveFromArray(MprArray *array, int index);
+
+//
+//     Printf replacements
+//
+#define        MPR_STDOUT      1
+#define        MPR_STDERR      2
+
+extern int             mprPrintf(char *fmt, ...);
+extern int             mprStaticPrintf(char *fmt, ...);
+extern int             mprFprintf(int fd, char *fmt, ...);
+extern int             mprAllocSprintf(char **s, int n, char *fmt, ...);
+extern int             mprAllocVsprintf(char **s, int n, char *fmt, va_list arg);
+extern int             mprSprintf(char *s, int n, char *fmt, ...);
+extern int             mprVsprintf(char *s, int n, char *fmt, va_list arg);
+extern char            *mprItoa(int value, char *userBuf, int width);
+extern int             mprAtoi(char *str, int radix);
+
+//
+//     Safe string routines
+//
+extern int             mprStrcpy(char *dest, int destMax, const char *src);
+extern int             mprMemcpy(char *dest, int destMax, const char *src, 
+                                               int nbytes);
+extern int             mprAllocStrcpy(char **dest, int max, const char *src);
+extern int             mprReallocStrcpy(char **dest, int max, const char *src);
+extern int             mprAllocMemcpy(char **dest, int destMax, const char *src, 
+                                               int nbytes);
+extern int             mprStrcat(char *dest, int max, const char *delim, 
+                                               const char *src, ...);
+extern int             mprAllocStrcat(char **dest, int max, const char *delim, 
+                                               const char *src, ...);
+extern int             mprReallocStrcat(char **dest, int max, int existingLen,
+                                               const char *delim, const char *src, ...);
+extern int             mprStrlen(char *src, int max);
+
+//     FUTURE (rename to Strcmpi, Strncmpi)
+extern int             mprStrCmpAnyCase(char *s1, char *s2);
+extern int             mprStrCmpAnyCaseCount(char *s1, char *s2, int len);
+extern char            *mprStrLower(char *string);
+extern char            *mprStrUpper(char *string);
+extern char            *mprStrTrim(char *string, char c);
+
+//
+//     Reentrant string and time routines
+//
+extern char            *mprStrTok(char *str, const char *sep, char **last);
+extern char            *mprGetWordTok(char *word, int wordLen, char *str, 
+                                               const char *delim, char **tok);
+extern int             mprCtime(const time_t* timer, char *buf, int bufsize);
+extern int             mprGetTime(MprTime *tp);
+extern int             mprAsctime(const struct tm *timeptr, char *buf, int bufsiz);
+extern struct tm *mprGmtime(time_t* now, struct tm *tmp);
+extern struct tm *mprLocaltime(time_t* now, struct tm *tmp);
+extern int             mprRfcTime(char *buf, int size, const struct tm *timeptr);
+
+//
+//     General other xPlatform routines
+//
+extern char            *mprGetBaseName(char *name);
+extern int             mprGetDirName(char *buf, int bufsize, char *path);
+extern void            mprMakeArgv(char *prog, char *cmd, char ***argv, int *argc);
+extern int             mprMakeDir(char *path);
+extern char    *mprInetNtoa(char *buf, int size, const struct in_addr in);
+extern void            mprSleep(int msec);
+extern struct hostent* mprGetHostByName(char *name);
+extern void            mprFreeGetHostByName(struct hostent* hostp);
+extern int             mprGetRandomBytes(uchar *buf, int size, int block);
+
+#if BLD_FEATURE_MULTITHREAD
+extern char            *mprGetCurrentThreadName();
+extern void            mprLock();
+extern void            mprUnlock();
+#else
+#if __cplusplus
+inline void            mprLock() {};
+inline void            mprUnlock() {};
+#else
+#define mprLock()
+#define mprUnlock()
+#endif
+#endif
+
+extern bool            mprGetDebugMode();
+extern void            mprSetDebugMode(bool on);
+
+extern int             mprGetOsError();
+extern char            *mprGetErrorMsg(int errCode);
+extern char            *mprMakeTempFileName(char *buf, int bufsize, char *tempDir);
+extern void            mprNextFds(char *msg);
+extern char            *mprGetFullPathName(char *buf, int buflen, char *path);
+
+extern void            mprError(char *file, int line, int flags, char *fmt, ...);
+
+
+#if BLD_FEATURE_LOG || !__cplusplus
+extern void            mprLog(int level, char *fmt, ...);
+#endif
+
+typedef void   (*MprMemProc)(int askSize, int totalPoolMem, int limit);
+
+extern void            *mprCalloc(uint numElem, uint size);
+extern int             mprCreateMemHeap(char *userBuf, int initialSize, int limit);
+extern void            mprFree(void *ptr);
+extern void            *mprMalloc(uint size);
+extern void            mprMemClose();
+#if BLD_FEATURE_MALLOC_HOOK
+extern void            mprHookMalloc();
+#endif
+extern void            mprMemStop();
+extern void            mprPrintMemStats();
+extern void            *mprRealloc(void *ptr, uint size);
+extern void            mprRequestMemStats(bool on);
+extern void            mprSetMemHandler(MprMemProc cback);
+extern char            *mprStrdup(const char *str);
+
+#if WIN
+extern int             mprReadRegistry(char *key, char *val, char **buf, int max);
+#endif
+
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#endif // _h_MPR 
+
+//
+// Local variables:
+// tab-width: 4
+// c-basic-offset: 4
+// End:
+// vim:tw=78
+// vim600: sw=4 ts=4 fdm=marker
+// vim<600: sw=4 ts=4
+//
diff --git a/source4/web_server/ejs/mprOs.h b/source4/web_server/ejs/mprOs.h
new file mode 100644 (file)
index 0000000..5a88f4f
--- /dev/null
@@ -0,0 +1,627 @@
+///
+///    @file   mprOs.h
+/// @brief     Include O/S headers and smooth out per-O/S differences
+//     @copy   default
+//     
+//     Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
+//     
+//     This software is distributed under commercial and open source licenses.
+//     You may use the GPL open source license described below or you may acquire 
+//     a commercial license from Mbedthis Software. You agree to be fully bound 
+//     by the terms of either license. Consult the LICENSE.TXT distributed with 
+//     this software for full details.
+//     
+//     This software is open source; you can redistribute it and/or modify it 
+//     under the terms of the GNU General Public License as published by the 
+//     Free Software Foundation; either version 2 of the License, or (at your 
+//     option) any later version. See the GNU General Public License for more 
+//     details at: http://www.mbedthis.com/downloads/gplLicense.html
+//     
+//     This program is distributed WITHOUT ANY WARRANTY; without even the 
+//     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+//     
+//     This GPL license does NOT permit incorporating this software into 
+//     proprietary programs. If you are unable to comply with the GPL, you must
+//     acquire a commercial license to use this software. Commercial licenses 
+//     for this software and support services are available from Mbedthis 
+//     Software at http://www.mbedthis.com 
+//     
+//     @end
+//////////////////////////////// Documentation /////////////////////////////////
+///
+///    This header is part of the Mbedthis Portable Runtime and aims to include
+///    all necessary O/S headers and to unify the constants and declarations 
+///    required by Mbedthis products. It can be included by C or C++ programs.
+///
+////////////////////////////////////////////////////////////////////////////////
+
+#error foo
+
+blah blah;
+
+#ifndef _h_MPR_OS_HDRS
+#define _h_MPR_OS_HDRS 1
+
+#include       "web_server/ejs/config.h"
+
+////////////////////////////////// CPU Families ////////////////////////////////
+//
+//     Porters, add your CPU families here and update configure code. 
+//
+#define MPR_CPU_UNKNOWN                0
+#define MPR_CPU_IX86           1
+#define MPR_CPU_PPC            2
+#define MPR_CPU_SPARC          3
+#define MPR_CPU_XSCALE                 4
+#define MPR_CPU_ARM            5
+#define MPR_CPU_MIPS           6
+#define MPR_CPU_68K            7
+#define MPR_CPU_SIMNT          8                       //      VxWorks NT simulator
+#define MPR_CPU_SIMSPARC       9                       //      VxWorks sparc simulator
+
+////////////////////////////////// O/S Includes ////////////////////////////////
+
+#if LINUX || SOLARIS
+       #include        <sys/types.h>
+       #include        <time.h>
+       #include        <arpa/inet.h>
+       #include        <ctype.h>
+       #include        <dlfcn.h>
+       #include        <fcntl.h>
+       #include        <grp.h> 
+       #include        <errno.h>
+       #include        <libgen.h>
+       #include        <limits.h>
+       #include        <netdb.h>
+       #include        <net/if.h>
+       #include        <netinet/in.h>
+       #include        <netinet/tcp.h>
+       #include        <netinet/ip.h>
+       #include        <pthread.h> 
+       #include        <pwd.h> 
+       #include        <resolv.h>
+       #include        <signal.h>
+       #include        <stdarg.h>
+       #include        <stdio.h>
+       #include        <stdlib.h>
+       #include        <string.h>
+       #include        <syslog.h>
+       #include        <sys/ioctl.h>
+       #include        <sys/stat.h>
+       #include        <sys/param.h>
+       #include        <sys/resource.h>
+       #include        <sys/sem.h>
+       #include        <sys/shm.h>
+       #include        <sys/socket.h>
+       #include        <sys/select.h>
+       #include        <sys/time.h>
+       #include        <sys/times.h>
+       #include        <sys/utsname.h>
+       #include        <sys/wait.h>
+       #include        <unistd.h>
+
+#if LINUX
+       #include        <stdint.h>
+#endif
+
+#if SOLARIS
+       #include        <netinet/in_systm.h>
+#endif
+
+#if BLD_FEATURE_FLOATING_POINT
+       #define __USE_ISOC99 1
+       #include        <math.h>
+       #include        <values.h>
+#endif
+
+#endif // LINUX || SOLARIS
+
+#if VXWORKS
+       #include        <vxWorks.h>
+       #include        <envLib.h>
+       #include        <sys/types.h>
+       #include        <time.h>
+       #include        <arpa/inet.h>
+       #include        <ctype.h>
+       #include        <fcntl.h>
+       #include        <errno.h>
+       #include        <limits.h>
+       #include        <loadLib.h>
+       #include        <netdb.h>
+       #include        <net/if.h>
+       #include        <netinet/tcp.h>
+       #include        <netinet/in.h>
+       #include        <netinet/ip.h>
+       #include        <signal.h>
+       #include        <stdarg.h>
+       #include        <stdio.h>
+       #include        <stdlib.h>
+       #include        <string.h>
+       #include        <sysSymTbl.h>
+       #include        <sys/fcntlcom.h>
+       #include        <sys/ioctl.h>
+       #include        <sys/stat.h>
+       #include        <sys/socket.h>
+       #include        <sys/times.h>
+       #include        <sys/wait.h>
+       #include        <unistd.h>
+       #include        <unldLib.h>
+
+       #if BLD_FEATURE_FLOATING_POINT
+       #include        <float.h>
+       #define __USE_ISOC99 1
+       #include        <math.h>
+       #endif
+
+       #include        <sockLib.h>
+       #include        <inetLib.h>
+       #include        <ioLib.h>
+       #include        <pipeDrv.h>
+       #include        <hostLib.h>
+       #include        <netdb.h>
+       #include        <tickLib.h>
+       #include        <taskHookLib.h>
+
+#endif // VXWORKS
+
+#if MACOSX
+       #include        <time.h>
+       #include        <arpa/inet.h>
+       #include        <ctype.h>
+       #include        <fcntl.h>
+       #include        <grp.h> 
+       #include        <errno.h>
+       #include        <libgen.h>
+       #include        <limits.h>
+       #include        <mach-o/dyld.h>
+       #include        <netdb.h>
+       #include        <net/if.h>
+       #include        <netinet/in_systm.h>
+       #include        <netinet/in.h>
+       #include        <netinet/tcp.h>
+       #include        <netinet/ip.h>
+       #include        <pthread.h> 
+       #include        <pwd.h> 
+       #include        <resolv.h>
+       #include        <signal.h>
+       #include        <stdarg.h>
+       #include        <stdio.h>
+       #include        <stdlib.h>
+       #include        <stdint.h>
+       #include        <string.h>
+       #include        <syslog.h>
+       #include        <sys/ioctl.h>
+       #include        <sys/types.h>
+       #include        <sys/stat.h>
+       #include        <sys/param.h>
+       #include        <sys/resource.h>
+       #include        <sys/sem.h>
+       #include        <sys/shm.h>
+       #include        <sys/socket.h>
+       #include        <sys/select.h>
+       #include        <sys/time.h>
+       #include        <sys/times.h>
+       #include        <sys/types.h>
+       #include        <sys/utsname.h>
+       #include        <sys/wait.h>
+       #include        <unistd.h>
+#endif // MACOSX
+
+#if WIN
+       #include        <ctype.h>
+       #include        <conio.h>
+       #include        <direct.h>
+       #include        <errno.h>
+       #include        <fcntl.h>
+       #include        <io.h>
+       #include        <limits.h>
+       #include        <malloc.h>
+       #include        <process.h>
+       #include        <sys/stat.h>
+       #include        <sys/types.h>
+       #include        <stddef.h>
+       #include        <stdio.h>
+       #include        <stdlib.h>
+       #include        <string.h>
+       #include        <stdarg.h>
+       #include        <time.h>
+       #define WIN32_LEAN_AND_MEAN
+       #include        <winsock2.h>
+       #include        <windows.h>
+       #include        <winbase.h>
+       #if BLD_FEATURE_FLOATING_POINT
+       #include        <float.h>
+       #endif
+       #include        <shlobj.h>
+       #include        <shellapi.h>
+       #include        <wincrypt.h>
+#endif // WIN 
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// General Defines ///////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+#define        MAXINT                  INT_MAX
+#define BITS(type)             (BITSPERBYTE * (int) sizeof(type))
+
+#ifndef max
+#define max(a,b)  (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b)  (((a) < (b)) ? (a) : (b))
+#endif
+
+//
+//     Set FD_SETSIZE to the maximum number of files (sockets) that you want to
+//     support. It is used in select.cpp.
+//
+//     #ifdef FD_SETSIZE
+//             #undef FD_SETSIZE
+//     #endif
+//     #define FD_SETSIZE              128
+//
+
+typedef char   *MprStr;                                        // Used for dynamic strings
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// Linux Defines ////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+#if LINUX
+       typedef unsigned char uchar;
+
+#if BLD_FEATURE_INT64
+       __extension__ typedef long long int int64;
+       __extension__ typedef unsigned long long int uint64;
+       #define INT64(x) (x##LL)
+#endif
+
+       #define closesocket(x)  close(x)
+       #define MPR_BINARY              ""
+       #define MPR_TEXT                ""
+       #define O_BINARY                0
+       #define O_TEXT                  0
+       #define SOCKET_ERROR    -1
+       #define MPR_DLL_EXT             ".so"
+
+#if BLD_FEATURE_FLOATING_POINT
+       #define MAX_FLOAT               MAXFLOAT
+#endif
+
+       #if BLD_FEATURE_MALLOC
+               //
+               //      PORTERS: You will need add assembler code for your architecture here
+               //      only if you want to use the fast malloc (BLD_FEATURE_MALLOC)
+               //
+               #if UNUSED
+                       #define MPR_GET_RETURN(ip)      __builtin_return_address(0)
+               #else
+                       #if BLD_HOST_CPU_ARCH == MPR_CPU_IX86
+                               #define MPR_GET_RETURN(ip)      \
+                                       asm("movl 4(%%ebp),%%eax ; movl %%eax,%0" : \
+                                               "=g" (ip) : \
+                                               : "eax")
+                       #endif
+               #endif // UNUSED
+       #endif // BLD_FEATURE_MALLOC
+
+#if FUTURE
+//     #define mprGetHiResTime(x) __asm__ __volatile__ ("rdtsc" : "=A" (x))
+//     extern char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen);
+
+       //
+       //      Atomic functions
+       //
+       typedef struct { volatile int counter; } mprAtomic_t;
+
+       #if BLD_FEATURE_MULTITHREAD
+       #define LOCK "lock ; "
+       #else
+       #define LOCK ""
+       #endif
+
+       static __inline__ void mprAtomicInc(mprAtomic_t* v) {
+               __asm__ __volatile__(
+                       LOCK "incl %0"
+                       :"=m" (v->counter)
+                       :"m" (v->counter));
+       }
+
+       static __inline__ void mprAtomicDec(mprAtomic_t* v) {
+               __asm__ __volatile__(
+                       LOCK "decl %0"
+                       :"=m" (v->counter)
+                       :"m" (v->counter));
+       }
+#endif // FUTURE
+
+#endif         // LINUX 
+
+////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// VxWorks Defines ///////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+#if VXWORKS
+
+       typedef unsigned char uchar;
+       typedef unsigned int uint;
+       typedef unsigned long ulong;
+
+       #define HAVE_SOCKLEN_T
+       typedef int     socklen_t;
+
+#if BLD_FEATURE_INT64
+       typedef long long int int64;
+       typedef unsigned long long int uint64;
+       #define INT64(x) (x##LL)
+#endif
+
+       #define closesocket(x)  close(x)
+       #define getpid()                taskIdSelf()
+       #define MPR_BINARY              ""
+       #define MPR_TEXT                ""
+       #define O_BINARY                0
+       #define O_TEXT                  0
+       #define SOCKET_ERROR    -1
+       #define MPR_DLL_EXT             ".so"
+
+#if BLD_FEATURE_FLOATING_POINT
+       #define MAX_FLOAT               FLT_MAX
+#endif
+
+       #undef R_OK
+       #define R_OK    4
+       #undef W_OK
+       #define W_OK    2
+       #undef X_OK
+       #define X_OK    1
+       #undef F_OK
+       #define F_OK    0
+
+       #define MSG_NOSIGNAL 0
+       
+       extern int access(char *path, int mode);
+       extern int sysClkRateGet();
+
+       #if BLD_FEATURE_MALLOC
+               //
+               //      PORTERS: You will need add assembler code for your architecture here
+               //      only if you want to use the fast malloc (BLD_FEATURE_MALLOC)
+               //
+               #if UNUSED
+                       #define MPR_GET_RETURN(ip)      __builtin_return_address(0)
+               #else
+                       #if BLD_HOST_CPU_ARCH == MPR_CPU_IX86
+                               #define MPR_GET_RETURN(ip)      \
+                                       asm("movl 4(%%ebp),%%eax ; movl %%eax,%0" : \
+                                               "=g" (ip) : \
+                                               : "eax")
+                       #endif
+               #endif // UNUSED
+       #endif // BLD_FEATURE_MALLOC
+#endif         // VXWORKS 
+
+////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// MacOsx Defines ///////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+#if MACOSX
+       typedef unsigned long ulong;
+       typedef unsigned char uchar;
+
+#if BLD_FEATURE_INT64
+       __extension__ typedef long long int int64;
+       __extension__ typedef unsigned long long int uint64;
+       #define INT64(x) (x##LL)
+#endif
+       #define closesocket(x)  close(x)
+       #define MPR_BINARY              ""
+       #define MPR_TEXT                ""
+       #define O_BINARY                0
+       #define O_TEXT                  0
+       #define SOCKET_ERROR    -1
+       #define MPR_DLL_EXT             ".dylib"
+       #define MSG_NOSIGNAL    0
+       #define __WALL          0x40000000
+       #define PTHREAD_MUTEX_RECURSIVE_NP  PTHREAD_MUTEX_RECURSIVE
+
+#if BLD_FEATURE_FLOATING_POINT
+       #define MAX_FLOAT               MAXFLOAT
+#endif
+
+       #if MPR_FEATURE_MALLOC
+       //
+       //      PORTERS: You will need add assembler code for your architecture here
+       //      only if you want to use the fast malloc (MPR_FEATURE_MALLOC)
+       //
+       #define MPR_GET_RETURN(ip)      __builtin_return_address
+       #endif
+
+#if FUTURE
+//     #define mprGetHiResTime(x) __asm__ __volatile__ ("rdtsc" : "=A" (x))
+//     extern char *inet_ntoa_r(const struct in_addr in, char *buffer, int buflen);
+
+       //
+       //      Atomic functions
+       //
+       typedef struct { volatile int counter; } mprAtomic_t;
+
+       #if MPR_FEATURE_MULTITHREAD
+       #define LOCK "lock ; "
+       #else
+       #define LOCK ""
+       #endif
+
+       static __inline__ void mprAtomicInc(mprAtomic_t* v) {
+               __asm__ __volatile__(
+                       LOCK "incl %0"
+                       :"=m" (v->counter)
+                       :"m" (v->counter));
+       }
+
+       static __inline__ void mprAtomicDec(mprAtomic_t* v) {
+               __asm__ __volatile__(
+                       LOCK "decl %0"
+                       :"=m" (v->counter)
+                       :"m" (v->counter));
+       }
+#endif
+#endif // MACOSX
+
+////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// Windows Defines ///////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+#if WIN
+       typedef unsigned char uchar;
+       typedef unsigned int uint;
+       typedef unsigned long ulong;
+       typedef unsigned short ushort;
+
+#if BLD_FEATURE_INT64
+       typedef __int64 int64;
+       typedef unsigned __int64 uint64;
+       #define INT64(x) (x##i64)
+#endif
+
+       typedef int     uid_t;
+       typedef void    *handle;
+       typedef char    *caddr_t;
+       typedef long    pid_t;
+       typedef int             gid_t;
+       typedef ushort  mode_t;
+       typedef void    *siginfo_t;
+
+       #define HAVE_SOCKLEN_T
+       typedef int     socklen_t;
+
+       #undef R_OK
+       #define R_OK    4
+       #undef W_OK
+       #define W_OK    2
+       #undef X_OK
+       #define X_OK    1
+       #undef F_OK
+       #define F_OK    0
+       
+       #ifndef EADDRINUSE
+       #define EADDRINUSE              46
+       #endif
+       #ifndef EWOULDBLOCK
+       #define EWOULDBLOCK             EAGAIN
+       #endif
+       #ifndef ENETDOWN
+       #define ENETDOWN                43
+       #endif
+       #ifndef ECONNRESET
+       #define ECONNRESET              44
+       #endif
+       #ifndef ECONNREFUSED
+       #define ECONNREFUSED    45
+       #endif
+
+       #define MSG_NOSIGNAL    0
+       #define MPR_BINARY              "b"
+       #define MPR_TEXT                "t"
+
+#if BLD_FEATURE_FLOATING_POINT
+       #define MAX_FLOAT               DBL_MAX
+#endif
+
+#ifndef FILE_FLAG_FIRST_PIPE_INSTANCE
+#define FILE_FLAG_FIRST_PIPE_INSTANCE   0x00080000
+#endif
+
+       #define access  _access
+       #define close   _close
+       #define fileno  _fileno
+       #define fstat   _fstat
+       #define getpid  _getpid
+       #define open    _open
+       #define putenv  _putenv
+       #define read    _read
+       #define stat    _stat
+       #define umask   _umask
+       #define unlink  _unlink
+       #define write   _write
+       #define strdup  _strdup
+       #define lseek   _lseek
+
+       #define mkdir(a,b)      _mkdir(a)
+       #define rmdir(a)        _rmdir(a)
+
+       #if BLD_FEATURE_MALLOC
+       //
+       //      PORTERS: You will need add assembler code for your architecture here
+       //      only if you want to use the fast malloc (BLD_FEATURE_MALLOC)
+       //
+       #if MPR_CPU_IX86
+       #define MPR_GET_RETURN(ip) \
+               __asm { mov     eax, 4[ebp] } \
+               __asm { mov ip, eax     }
+       #endif
+       #endif
+
+       #define MPR_DLL_EXT             ".dll"
+
+       extern void             srand48(long);
+       extern long             lrand48(void);
+       extern long     ulimit(int, ...);
+       extern long     nap(long);
+       extern uint     sleep(unsigned int secs);
+       extern uid_t    getuid(void);
+       extern uid_t    geteuid(void);
+
+#endif // WIN 
+
+////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Solaris Defines ////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+#if SOLARIS
+       typedef unsigned char uchar;
+
+#if BLD_FEATURE_INT64
+       typedef long long int int64;
+       typedef unsigned long long int uint64;
+       #define INT64(x) (x##LL)
+#endif
+
+       #define closesocket(x)  close(x)
+       #define MPR_BINARY              ""
+       #define MPR_TEXT                ""
+       #define O_BINARY                0
+       #define O_TEXT                  0
+       #define SOCKET_ERROR    -1
+       #define MPR_DLL_EXT             ".so"
+       #define MSG_NOSIGNAL    0
+       #define INADDR_NONE             ((in_addr_t) 0xffffffff)
+       #define __WALL  0
+       #define PTHREAD_MUTEX_RECURSIVE_NP  PTHREAD_MUTEX_RECURSIVE
+
+#if BLD_FEATURE_FLOATING_POINT
+       #define MAX_FLOAT               MAXFLOAT
+#endif
+
+#endif // SOLARIS 
+
+////////////////////////////////////////////////////////////////////////////////
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _h_MPR_OS_HDRS 
+
+//
+// Local variables:
+// tab-width: 4
+// c-basic-offset: 4
+// End:
+// vim:tw=78
+// vim600: sw=4 ts=4 fdm=marker
+// vim<600: sw=4 ts=4
+//
diff --git a/source4/web_server/ejs/var.c b/source4/web_server/ejs/var.c
new file mode 100644 (file)
index 0000000..53b66d8
--- /dev/null
@@ -0,0 +1,2161 @@
+/*
+ *     @file   var.c
+ *     @brief  MPR Universal Variable Type
+ *     @overview
+ *
+ *     @copy   default.m
+ *     
+ *     Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
+ *     Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved.
+ *     
+ *     This software is distributed under commercial and open source licenses.
+ *     You may use the GPL open source license described below or you may acquire 
+ *     a commercial license from Mbedthis Software. You agree to be fully bound 
+ *     by the terms of either license. Consult the LICENSE.TXT distributed with 
+ *     this software for full details.
+ *     
+ *     This software is open source; you can redistribute it and/or modify it 
+ *     under the terms of the GNU General Public License as published by the 
+ *     Free Software Foundation; either version 2 of the License, or (at your 
+ *     option) any later version. See the GNU General Public License for more 
+ *     details at: http://www.mbedthis.com/downloads/gplLicense.html
+ *     
+ *     This program is distributed WITHOUT ANY WARRANTY; without even the 
+ *     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ *     
+ *     This GPL license does NOT permit incorporating this software into 
+ *     proprietary programs. If you are unable to comply with the GPL, you must
+ *     acquire a commercial license to use this software. Commercial licenses 
+ *     for this software and support services are available from Mbedthis 
+ *     Software at http://www.mbedthis.com 
+ *     
+ *     @end
+ */
+
+/******************************* Documentation ********************************/
+
+/*
+ *     This module is NOT multithreaded. 
+ *
+ *     Properties are variables that are stored in an object type variable.
+ *     Properties can be primitive data types, other objects or functions.
+ *     Properties are indexed by a character name.
+ */
+
+/********************************** Includes **********************************/
+
+#include       "web_server/ejs/var.h"
+
+/*********************************** Locals ***********************************/
+#if VAR_DEBUG
+
+static MprProperties   objectList;                     /* Dummy head of objects list */
+static int                             objectCount = -1;       /* Count of objects */
+
+#endif
+/***************************** Forward Declarations ***************************/
+
+static int             adjustRefCount(MprProperties *pp, int adj);
+static int             adjustVarRefCount(MprVar *vp, int adj);
+static MprVar  *allocProperty(const char *propertyName);
+static void    copyVarCore(MprVar *dest, MprVar *src, int copyDepth);
+static MprProperties 
+                               *createProperties(const char *name, int hashSize);
+static bool    freeVar(MprVar *vp, int force);
+static bool    freeVarStorage(MprVar *vp, int force);
+static MprVar  *getObjChain(MprProperties *pp, const char *property);
+static int             hash(MprProperties *pp, const char *property);
+static bool            releaseProperties(MprProperties *pp, int force);
+
+/*********************************** Code *************************************/
+/*
+ *     Destroy a variable and all referenced variables. Release any referenced 
+ *     object regardless of whether other users still have references. Be VERY
+ *     careful using this routine. 
+ *
+ *     Return TRUE if the underlying data is freed. Objects may not be freed if 
+ *     there are other users of the object.
+ */
+
+bool mprDestroyAllVars(MprVar *vp)
+{
+       mprAssert(vp);
+
+       if (vp->trigger) {
+               if ((vp->trigger)(MPR_VAR_DELETE, vp->parentProperties, vp, 0, 0) 
+                               == MPR_TRIGGER_ABORT) {
+                       return 0;
+               }
+       }
+
+       /*
+        *      Free the actual value. If this var refers to an object, we will 
+        *      recurse through all the properties freeing all vars.
+        */
+       return freeVar(vp, 1);
+}
+
+/******************************************************************************/
+/*
+ *     Destroy a variable. Release any referenced object (destroy if no other
+ *     users are referencing).
+ *
+ *     Return TRUE if the underlying data is freed. Objects may not be freed if 
+ *     there are other users of the object.
+ */
+
+bool mprDestroyVar(MprVar *vp)
+{
+       mprAssert(vp);
+
+       if (vp->trigger) {
+               if ((vp->trigger)(MPR_VAR_DELETE, vp->parentProperties, vp, 0, 0) 
+                               == MPR_TRIGGER_ABORT) {
+                       return 0;
+               }
+       }
+
+       /*
+        *      Free the actual value. If this var refers to an object, we will 
+        *      recurse through all the properties freeing all that have no other
+        *      references.
+        */
+       return freeVar(vp, 0);
+}
+
+/******************************************************************************/
+/*
+ *     Free the value in a variable for primitive types. Release objects.
+ *
+ *     Return TRUE if the underlying data is freed. Objects may not be freed if 
+ *     there are other users of the object.
+ */
+
+static bool freeVar(MprVar *vp, int force)
+{
+       bool    freed;
+
+       mprAssert(vp);
+
+       freed = freeVarStorage(vp, force);
+
+       mprFree(vp->name);
+       mprFree(vp->fullName);
+
+       if (vp->allocatedVar) {
+               mprFree(vp);
+       } else {
+               vp->name = 0;
+               vp->fullName = 0;
+               vp->type = MPR_TYPE_UNDEFINED;
+       }
+       return freed;
+}
+
+/******************************************************************************/
+/*
+ *     Free the value in a variable for primitive types. Release objects.
+ *
+ *     Return TRUE if the underlying data is freed. Objects may not be freed if 
+ *     there are other users of the object.
+ */
+
+static bool freeVarStorage(MprVar *vp, int force)
+{
+       MprArray        *argList;
+       bool            freed;
+       int                     i;
+
+       freed = 1;
+       mprAssert(vp);
+
+       switch (vp->type) {
+       default:
+               break;
+
+       case MPR_TYPE_STRING:
+               if (vp->allocatedData && vp->string != 0) {
+                       mprFree(vp->string);
+                       vp->string = 0;
+                       vp->allocatedData = 0;
+               }
+               break;
+
+       case MPR_TYPE_OBJECT:
+#if VAR_DEBUG
+               /*
+                *      Recurse through all properties and release / delete. Release the 
+                *      properties hash table.
+                */
+               if (vp->properties->refCount > 1) {
+                       mprLog(7, "freeVar: ACT \"%s\", 0x%x, ref %d, force %d\n", 
+                               vp->name, vp->properties, vp->properties->refCount, force);
+               } else {
+                       mprLog(7, "freeVar: DEL \"%s\", 0x%x, ref %d, force %d\n", 
+                               vp->name, vp->properties, vp->properties->refCount, force);
+               }
+#endif
+               if (vp->allocatedData) {
+                       freed = releaseProperties(vp->properties, force);
+               }
+               vp->properties = 0;
+               break;
+
+       case MPR_TYPE_FUNCTION:
+               if (vp->allocatedData) {
+                       argList = vp->function.args;
+                       for (i = 0; i < argList->max; i++) {
+                               if (argList->handles[i] != 0) {
+                                       mprFree(argList->handles[i]);
+                               }
+                       }
+                       mprDestroyArray(argList);
+                       vp->function.args = 0;
+                       mprFree(vp->function.body);
+                       vp->function.body = 0;
+               }
+               break;
+       }
+
+       vp->type = MPR_TYPE_UNDEFINED;
+       return freed;
+}
+
+/******************************************************************************/
+/*
+ *     Adjust the object reference count and return the currrent count of 
+ *     users.
+ */
+
+static int adjustVarRefCount(MprVar *vp, int adj)
+{
+       mprAssert(vp);
+
+       if (vp->type != MPR_TYPE_OBJECT) {
+               mprAssert(vp->type == MPR_TYPE_OBJECT);
+               return 0;
+       }
+       return adjustRefCount(vp->properties, adj);
+}
+
+/******************************************************************************/
+/*
+ *     Get the object reference count 
+ */
+
+int mprGetVarRefCount(MprVar *vp)
+{
+       mprAssert(vp);
+
+       if (vp->type != MPR_TYPE_OBJECT) {
+               mprAssert(vp->type == MPR_TYPE_OBJECT);
+               return 0;
+       }
+       return adjustRefCount(vp->properties, 0);
+}
+
+/******************************************************************************/
+/*
+ *     Update the variable's name
+ */
+
+void mprSetVarName(MprVar *vp, char *name)
+{
+       mprAssert(vp);
+
+       mprFree(vp->name);
+       vp->name = mprStrdup(name);
+}
+
+/******************************************************************************/
+/*
+ *     Append to the variable's full name
+ */
+
+void mprSetVarFullName(MprVar *vp, char *name)
+{
+#if VAR_DEBUG
+       mprAssert(vp);
+
+       mprFree(vp->fullName);
+       vp->fullName = mprStrdup(name);
+       if (vp->type == MPR_TYPE_OBJECT) {
+               if (strcmp(vp->properties->name, "this") == 0) {
+                       mprStrcpy(vp->properties->name, sizeof(vp->properties->name), name);
+               }
+       }
+#endif
+}
+
+/******************************************************************************/
+/*
+ *     Make a var impervious to recursive forced deletes. 
+ */
+
+void mprSetVarDeleteProtect(MprVar *vp, int deleteProtect)
+{
+       mprAssert(vp);
+
+       if (vp->type == MPR_TYPE_OBJECT && vp->properties) {
+               vp->properties->deleteProtect = deleteProtect;
+       }
+}
+
+/******************************************************************************/
+/*
+ *     Make a variable readonly. Can still be deleted.
+ */
+
+void mprSetVarReadonly(MprVar *vp, int readonly)
+{
+       mprAssert(vp);
+
+       vp->readonly = readonly;
+}
+
+/******************************************************************************/
+
+MprVarTrigger mprAddVarTrigger(MprVar *vp, MprVarTrigger fn)
+{
+       MprVarTrigger oldTrigger;
+
+       mprAssert(vp);
+       mprAssert(fn);
+
+       oldTrigger = vp->trigger;
+       vp->trigger = fn;
+       return oldTrigger;
+}
+
+/******************************************************************************/
+
+MprType mprGetVarType(MprVar *vp)
+{
+       mprAssert(vp);
+
+       return vp->type;
+}
+
+/******************************************************************************/
+/********************************** Properties ********************************/
+/******************************************************************************/
+/*
+ *     Create a property in an object with a defined value. If the property 
+ *     already exists in the object, then just write its value.
+ */
+
+MprVar *mprCreateProperty(MprVar *obj, const char *propertyName, MprVar *newValue)
+{
+       MprVar  *prop, *last;
+       int             bucketIndex;
+
+       mprAssert(obj);
+       mprAssert(propertyName && *propertyName);
+
+       if (obj->type != MPR_TYPE_OBJECT) {
+               mprAssert(obj->type == MPR_TYPE_OBJECT);
+               return 0;
+       }
+
+       /*
+        *      See if property already exists and locate the bucket to hold the
+        *      property reference.
+        */
+       last = 0;
+       bucketIndex = hash(obj->properties, propertyName);
+       prop = obj->properties->buckets[bucketIndex];
+
+       /*
+        *      Find the property in the hash chain if it exists
+        */
+       for (last = 0; prop; last = prop, prop = prop->forw) {
+               if (prop->name[0] == propertyName[0] && 
+                               strcmp(prop->name, propertyName) == 0) {
+                       break;
+               }
+       }
+
+       if (prop) {
+               //      FUTURE -- remove. Just for debug.
+               mprAssert(prop == 0);
+               mprLog(0, "Attempting to create property %s in object %s\n",
+                       propertyName, obj->name);
+               return 0;
+       }
+
+       if (obj->trigger) {
+               if ((obj->trigger)(MPR_VAR_CREATE_PROPERTY, obj->properties, prop, 
+                               newValue, 0) == MPR_TRIGGER_ABORT) {
+                       return 0;
+               }
+       }
+
+       /*
+        *      Create a new property
+        */
+       prop = allocProperty(propertyName);
+       if (prop == 0) {
+               mprAssert(prop);
+               return 0;
+       }
+
+       copyVarCore(prop, newValue, MPR_SHALLOW_COPY);
+
+       prop->bucketIndex = bucketIndex;
+       if (last) {
+               last->forw = prop;
+       } else {
+               obj->properties->buckets[bucketIndex] = prop;
+       }
+       prop->parentProperties = obj->properties;
+
+       /*
+        *      Update the item counts 
+        */
+       obj->properties->numItems++;
+       if (! mprVarIsFunction(prop->type)) {
+               obj->properties->numDataItems++;
+       }
+
+       return prop;
+}
+
+/******************************************************************************/
+/*
+ *     Create a property in an object with a defined value. If the property 
+ *     already exists in the object, then just write its value. Same as 
+ *     mprCreateProperty except that the new value is passed by value rather than
+ *     by pointer.
+ */
+
+MprVar *mprCreatePropertyValue(MprVar *obj, const char *propertyName, MprVar newValue)
+{
+       return mprCreateProperty(obj, propertyName, &newValue);
+}
+
+/******************************************************************************/
+/*
+ *     Create a new property
+ */
+
+static MprVar *allocProperty(const char *propertyName)
+{
+       MprVar          *prop;
+
+       prop = (MprVar*) mprMalloc(sizeof(MprVar));
+       if (prop == 0) {
+               mprAssert(prop);
+               return 0;
+       }
+       memset(prop, 0, sizeof(MprVar));
+       prop->allocatedVar = 1;
+       prop->name = mprStrdup(propertyName);
+       prop->forw = (MprVar*) 0;
+
+       return prop;
+}
+
+/******************************************************************************/
+/*
+ *     Update a property in an object with a defined value. Create the property
+ *     if it doesn not already exist.
+ */
+
+MprVar *mprSetProperty(MprVar *obj, const char *propertyName, MprVar *newValue)
+{
+       MprVar  *prop, triggerValue;
+       int             rc;
+
+       mprAssert(obj);
+       mprAssert(propertyName && *propertyName);
+       mprAssert(obj->type == MPR_TYPE_OBJECT);
+
+       if (obj->type != MPR_TYPE_OBJECT) {
+               mprAssert(0);
+               return 0;
+       }
+
+       prop = mprGetProperty(obj, propertyName, 0);
+       if (prop == 0) {
+               return mprCreateProperty(obj, propertyName, newValue);
+       }
+
+       if (obj->trigger) {
+               /* 
+                *      Call the trigger before the update and pass it the new value.
+                */
+               triggerValue = *newValue;
+               triggerValue.allocatedVar = 0;
+               triggerValue.allocatedData = 0;
+               rc = (obj->trigger)(MPR_VAR_WRITE, obj->properties, obj, 
+                               &triggerValue, 0);
+               if (rc == MPR_TRIGGER_ABORT) {
+                       return 0;
+
+               } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) {
+                       /*
+                        *      Trigger must copy to triggerValue a variable that is not
+                        *      a structure copy of the existing data.
+                        */
+                       copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY);
+                       mprDestroyVar(&triggerValue);
+                       return prop;
+               }
+       }
+       copyVarCore(prop, newValue, MPR_SHALLOW_COPY);
+       return prop;
+}
+
+/******************************************************************************/
+/*
+ *     Update a property in an object with a defined value. Create the property
+ *     if it does not already exist. Same as mprSetProperty except that the 
+ *     new value is passed by value rather than by pointer.
+ */
+
+MprVar *mprSetPropertyValue(MprVar *obj, const char *propertyName, MprVar newValue)
+{
+       return mprSetProperty(obj, propertyName, &newValue);
+}
+
+/******************************************************************************/
+/*
+ *     Delete a property from this object
+ */
+
+int mprDeleteProperty(MprVar *obj, const char *property)
+{
+       MprVar          *prop, *last;
+       char            *cp;
+       int                     bucketIndex;
+
+       mprAssert(obj);
+       mprAssert(property && *property);
+       mprAssert(obj->type == MPR_TYPE_OBJECT);
+
+       if (obj->type != MPR_TYPE_OBJECT) {
+               mprAssert(obj->type == MPR_TYPE_OBJECT);
+               return 0;
+       }
+
+       last = 0;
+       bucketIndex = hash(obj->properties, property);
+       if ((prop = obj->properties->buckets[bucketIndex]) != 0) {
+               for ( ; prop; prop = prop->forw) {
+                       cp = prop->name;
+                       if (cp[0] == property[0] && strcmp(cp, property) == 0) {
+                               break;
+                       }
+                       last = prop;
+               }
+       }
+       if (prop == (MprVar*) 0) {
+               mprAssert(prop);
+               return MPR_ERR_NOT_FOUND;
+       }
+       if (prop->readonly) {
+               mprAssert(! prop->readonly);
+               return MPR_ERR_READ_ONLY;
+       }
+
+       if (obj->trigger) {
+               if ((obj->trigger)(MPR_VAR_DELETE_PROPERTY, obj->properties, prop, 0, 0)
+                               == MPR_TRIGGER_ABORT) {
+                       return MPR_ERR_ABORTED;
+               }
+       }
+
+       if (last) {
+               last->forw = prop->forw;
+       } else {
+               obj->properties->buckets[bucketIndex] = prop->forw;
+       }
+
+       obj->properties->numItems--;
+       if (! mprVarIsFunction(prop->type)) {
+               obj->properties->numDataItems--;
+       }
+
+       mprDestroyVar(prop);
+
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Find a property in an object and return a pointer to it. If a value arg
+ *     is supplied, then copy the data into the var. 
+ */
+
+MprVar *mprGetProperty(MprVar *obj, const char *property, MprVar *value)
+{
+       MprVar  *prop, triggerValue;
+       int             rc;
+
+       if (obj == 0 || obj->type != MPR_TYPE_OBJECT || property == 0 || 
+                       *property == '\0') {
+               if (value) {
+                       value->type = MPR_TYPE_UNDEFINED;
+               }
+               return 0;
+       }
+
+       for (prop = getObjChain(obj->properties, property); prop; 
+                       prop = prop->forw) {
+               if (prop->name[0] == property[0] && strcmp(prop->name, property) == 0) {
+                       break;
+               }
+       }
+       if (prop == 0) {
+               if (value) {
+                       value->type = MPR_TYPE_UNDEFINED;
+               }
+               return 0;
+       }
+       if (value) {
+               if (prop->trigger) {
+                       triggerValue = *prop;
+                       triggerValue.allocatedVar = 0;
+                       triggerValue.allocatedData = 0;
+                       /*
+                        *      Pass the trigger the current read value and may receive
+                        *      a new value.
+                        */ 
+                       rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, 
+                               &triggerValue, 0);
+                       if (rc == MPR_TRIGGER_ABORT) {
+                               if (value) {
+                                       value->type = MPR_TYPE_UNDEFINED;
+                               }
+                               return 0;
+
+                       } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) {
+                               copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY);
+                               mprDestroyVar(&triggerValue);
+                       }
+               }
+               /*
+                *      Clone. No copy.
+                */
+               *value = *prop;
+       }
+       return prop;
+}
+
+/******************************************************************************/
+/*
+ *     Read a properties value. This returns the property's value. It does not
+ *     copy object/string data but returns a pointer directly into the variable.
+ *     The caller does not and should not call mprDestroy on the returned value.
+ *     If value is null, just read the property and run triggers.
+ */
+
+int mprReadProperty(MprVar *prop, MprVar *value)
+{
+       MprVar  triggerValue;
+       int             rc;
+
+       mprAssert(prop);
+
+       if (prop->trigger) {
+               triggerValue = *prop;
+               triggerValue.allocatedVar = 0;
+               triggerValue.allocatedData = 0;
+               rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, 
+                       &triggerValue, 0);
+
+               if (rc == MPR_TRIGGER_ABORT) {
+                       return MPR_ERR_ABORTED;
+
+               } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) {
+                       copyVarCore(prop, &triggerValue, MPR_SHALLOW_COPY);
+                       mprDestroyVar(&triggerValue);
+                       return 0;
+               }
+       }
+       if (value) {
+               *value = *prop;
+
+               /*
+                *      Just so that if the user calls mprDestroyVar on value, it will do no
+                *      harm.
+                */
+               value->allocatedVar = 0;
+               value->allocatedData = 0;
+       }
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Read a properties value. This returns a copy of the property variable. 
+ *     However, if the property is an object or string, it returns a copy of the
+ *     reference to the underlying data. If copyDepth is set to MPR_DEEP_COPY,
+ *     then the underlying objects and strings data will be copied as well. If 
+ *     copyDepth is set to MPR_SHALLOW_COPY, then only strings will be copied. If
+ *     it is set to MPR_NO_COPY, then no data will be copied. In all cases, the 
+ *     user must call mprDestroyVar to free resources. This routine will run any 
+ *     registered triggers which may modify the value the user receives (without 
+ *     updating the properties real value).
+ *
+ *     WARNING: the args are reversed to most other APIs. This conforms to the
+ *     strcpy(dest, src) standard instead.
+ */
+
+int mprCopyProperty(MprVar *dest, MprVar *prop, int copyDepth)
+{
+       MprVar  triggerValue;
+       int             rc;
+
+       mprAssert(prop);
+       mprAssert(dest);
+
+       if (prop->trigger) {
+               triggerValue = *prop;
+               triggerValue.allocatedVar = 0;
+               triggerValue.allocatedData = 0;
+               rc = (prop->trigger)(MPR_VAR_READ, prop->parentProperties, prop, 
+                       &triggerValue, copyDepth);
+
+               if (rc == MPR_TRIGGER_ABORT) {
+                       return MPR_ERR_ABORTED;
+
+               } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) {
+                       copyVarCore(dest, &triggerValue, MPR_SHALLOW_COPY);
+                       mprDestroyVar(&triggerValue);
+                       return 0;
+               }
+       }
+       mprCopyVar(dest, prop, copyDepth);
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Write a new value into an existing property in an object.
+ */
+
+int mprWriteProperty(MprVar *vp, MprVar *value)
+{
+       MprVar  triggerValue;
+       int             rc;
+
+       mprAssert(vp);
+       mprAssert(value);
+
+       if (vp->readonly) {
+               return MPR_ERR_READ_ONLY;
+       }
+
+       if (vp->trigger) {
+               triggerValue = *value;
+
+               rc = (vp->trigger)(MPR_VAR_WRITE, vp->parentProperties, vp, 
+                       &triggerValue, 0);
+
+               if (rc == MPR_TRIGGER_ABORT) {
+                       return MPR_ERR_ABORTED;
+
+               } else if (rc == MPR_TRIGGER_USE_NEW_VALUE) {
+                       copyVarCore(vp, &triggerValue, MPR_SHALLOW_COPY);
+                       mprDestroyVar(&triggerValue);
+                       return 0;
+               }
+               /* Fall through */
+       }
+
+       copyVarCore(vp, value, MPR_SHALLOW_COPY);
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Write a new value into an existing property in an object.
+ */
+
+int mprWritePropertyValue(MprVar *vp, MprVar value)
+{
+       mprAssert(vp);
+
+       return mprWriteProperty(vp, &value);
+}
+
+/******************************************************************************/
+/*
+ *     Get the count of properties.
+ */
+
+int mprGetPropertyCount(MprVar *vp, int includeFlags)
+{
+       mprAssert(vp);
+
+       if (vp->type != MPR_TYPE_OBJECT) {
+               return 0;
+       }
+       if (includeFlags == MPR_ENUM_DATA) {
+               return vp->properties->numDataItems;
+       } else {
+               return vp->properties->numItems;
+       }
+}
+
+/******************************************************************************/
+/*
+ *     Get the first property in an object. Used for walking all properties in an
+ *     object.
+ */
+
+MprVar *mprGetFirstProperty(MprVar *obj, int includeFlags)
+{
+       MprVar          *prop;
+       int                     i;
+
+       mprAssert(obj);
+       mprAssert(obj->type == MPR_TYPE_OBJECT);
+
+       if (obj->type != MPR_TYPE_OBJECT) {
+               mprAssert(obj->type == MPR_TYPE_OBJECT);
+               return 0;
+       }
+
+       for (i = 0; i < (int) obj->properties->hashSize; i++) {
+               for (prop = obj->properties->buckets[i]; prop; prop = prop->forw) {
+                       if (prop) {
+                               if (mprVarIsFunction(prop->type)) {
+                                       if (!(includeFlags & MPR_ENUM_FUNCTIONS)) {
+                                               continue;
+                                       }
+                               } else {
+                                       if (!(includeFlags & MPR_ENUM_DATA)) {
+                                               continue;
+                                       }
+                               }
+                               return prop;
+                       }
+                       break;
+               }
+       }
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Get the next property in sequence.
+ */
+
+MprVar *mprGetNextProperty(MprVar *obj, MprVar *last, int includeFlags)
+{
+       MprProperties   *properties;
+       int                             i;
+
+       mprAssert(obj);
+       mprAssert(obj->type == MPR_TYPE_OBJECT);
+
+       if (obj->type != MPR_TYPE_OBJECT) {
+               mprAssert(obj->type == MPR_TYPE_OBJECT);
+               return 0;
+       }
+       properties = obj->properties;
+
+       if (last->forw) {
+               return last->forw;
+       }
+
+       for (i = last->bucketIndex + 1; i < (int) properties->hashSize; i++) {
+               for (last = properties->buckets[i]; last; last = last->forw) {
+                       if (mprVarIsFunction(last->type)) {
+                               if (!(includeFlags & MPR_ENUM_FUNCTIONS)) {
+                                       continue;
+                               }
+                       } else {
+                               if (!(includeFlags & MPR_ENUM_DATA)) {
+                                       continue;
+                               }
+                       }
+                       return last;
+               }
+       }
+       return 0;
+}
+
+/******************************************************************************/
+/************************** Internal Support Routines *************************/
+/******************************************************************************/
+/*
+ *     Create an hash table to hold and index properties. Properties are just 
+ *     variables which may contain primitive data types, functions or other
+ *     objects. The hash table is the essence of an object. HashSize specifies 
+ *     the size of the hash table to use and should be a prime number.
+ */
+
+static MprProperties *createProperties(const char *name, int hashSize)
+{
+       MprProperties   *pp;
+
+       if (hashSize < 7) {
+               hashSize = 7;
+       }
+       if ((pp = (MprProperties*) mprMalloc(sizeof(MprProperties))) == NULL) {
+               mprAssert(0);
+               return 0;
+       }
+       mprAssert(pp);
+       memset(pp, 0, sizeof(MprProperties));
+
+       pp->numItems = 0;
+       pp->numDataItems = 0;
+       pp->hashSize = hashSize;
+       pp->buckets = (MprVar**) mprMalloc(pp->hashSize * sizeof(MprVar*));
+       mprAssert(pp->buckets);
+       memset(pp->buckets, 0, pp->hashSize * sizeof(MprVar*));
+       pp->refCount = 1;
+
+#if VAR_DEBUG
+       if (objectCount == -1) {
+               objectCount = 0;
+               objectList.next = objectList.prev = &objectList;
+       }
+
+       mprStrcpy(pp->name, sizeof(pp->name), name);
+       pp->next = &objectList;
+       pp->prev = objectList.prev;
+       objectList.prev->next = pp;
+       objectList.prev = pp;
+       objectCount++;
+#endif
+       return pp;
+}
+
+/******************************************************************************/
+/*
+ *     Release an object's properties hash table. If this is the last person 
+ *     using it, free it. Return TRUE if the object is released.
+ */
+
+static bool releaseProperties(MprProperties *obj, int force)
+{
+       MprProperties   *pp;
+       MprVar                  *prop, *forw;
+       int                             i;
+
+       mprAssert(obj);
+       mprAssert(obj->refCount > 0);
+
+#if VAR_DEBUG
+       /*
+        *      Debug sanity check
+        */
+       mprAssert(obj->refCount < 20);
+#endif
+
+       if (--obj->refCount > 0 && !force) {
+               return 0;
+       }
+
+#if VAR_DEBUG
+       mprAssert(obj->prev);
+       mprAssert(obj->next);
+       mprAssert(obj->next->prev);
+       mprAssert(obj->prev->next);
+       obj->next->prev = obj->prev;
+       obj->prev->next = obj->next;
+       objectCount--;
+#endif
+
+       for (i = 0; i < (int) obj->hashSize; i++) {
+               for (prop = obj->buckets[i]; prop; prop = forw) {
+                       forw = prop->forw;
+                       if (prop->type == MPR_TYPE_OBJECT) {
+
+                               if (prop->properties == obj) {
+                                       /* Self reference */
+                                       continue;
+                               }
+                               pp = prop->properties;
+                               if (pp->visited) {
+                                       continue;
+                               }
+
+                               pp->visited = 1;
+                               if (! freeVar(prop, pp->deleteProtect ? 0 : force)) {
+                                       pp->visited = 0;
+                               }
+
+                       } else {
+                               freeVar(prop, force);
+                       }
+               }
+       }
+
+       mprFree((void*) obj->buckets);
+       mprFree((void*) obj);
+
+       return 1;
+}
+
+/******************************************************************************/
+/*
+ *     Adjust the reference count
+ */
+
+static int adjustRefCount(MprProperties *pp, int adj)
+{
+       mprAssert(pp);
+
+       /*
+        *      Debug sanity check
+        */
+       mprAssert(pp->refCount < 20);
+
+       return pp->refCount += adj;
+}
+
+/******************************************************************************/
+#if VAR_DEBUG
+/*
+ *     Print objects held
+ */
+
+void mprPrintObjects(char *msg)
+{
+       MprProperties   *pp, *np;
+       MprVar                  *prop, *forw;
+       char                    *buf;
+       int                             i;
+
+       mprLog(7, "%s: Object Store. %d objects.\n", msg, objectCount);
+       pp = objectList.next;
+       while (pp != &objectList) {
+               mprLog(7, "%s: 0x%x, refCount %d, properties %d\n", 
+                       pp->name, pp, pp->refCount, pp->numItems);
+               for (i = 0; i < (int) pp->hashSize; i++) {
+                       for (prop = pp->buckets[i]; prop; prop = forw) {
+                               forw = prop->forw;
+                               if (prop->properties == pp) {
+                                       /* Self reference */
+                                       continue;
+                               }
+                               mprVarToString(&buf, MPR_MAX_STRING, 0, prop);
+                               if (prop->type == MPR_TYPE_OBJECT) {
+                                       np = objectList.next;
+                                       while (np != &objectList) {
+                                               if (prop->properties == np) {
+                                                       break;
+                                               }
+                                               np = np->next;
+                                       }
+                                       if (prop->properties == np) {
+                                               mprLog(7, "    %s: OBJECT 0x%x, <%s>\n", 
+                                                       prop->name, prop->properties, prop->fullName);
+                                       } else {
+                                               mprLog(7, "    %s: OBJECT NOT FOUND, %s <%s>\n", 
+                                                       prop->name, buf, prop->fullName);
+                                       }
+                               } else {
+                                       mprLog(7, "    %s: <%s> = %s\n", prop->name, 
+                                               prop->fullName, buf);
+                               }
+                               mprFree(buf);
+                       }
+               }
+               pp = pp->next;
+       }
+}
+
+/******************************************************************************/
+
+void mprPrintObjRefCount(MprVar *vp)
+{
+       mprLog(7, "OBJECT 0x%x, refCount %d\n", vp->properties,
+               vp->properties->refCount);
+}
+
+#endif
+/******************************************************************************/
+/*
+ *     Get the bucket chain containing a property.
+ */
+
+static MprVar *getObjChain(MprProperties *obj, const char *property)
+{
+       mprAssert(obj);
+
+       return obj->buckets[hash(obj, property)];
+}
+
+/******************************************************************************/
+/*
+ *     Fast hash. The history of this algorithm is part of lost computer science 
+ *     folk lore.
+ */
+
+static int hash(MprProperties *pp, const char *property)
+{
+       uint    sum;
+
+       mprAssert(pp);
+       mprAssert(property);
+
+       sum = 0;
+       while (*property) {
+               sum += (sum * 33) + *property++;
+       }
+
+       return sum % pp->hashSize;
+}
+
+/******************************************************************************/
+/*********************************** Constructors *****************************/
+/******************************************************************************/
+/*
+ *     Initialize an undefined value.
+ */
+
+MprVar mprCreateUndefinedVar()
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_UNDEFINED;
+       return v;
+}
+
+/******************************************************************************/
+/*
+ *     Initialize an null value.
+ */
+
+MprVar mprCreateNullVar()
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_NULL;
+       return v;
+}
+
+/******************************************************************************/
+
+MprVar mprCreateBoolVar(bool value)
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_BOOL;
+       v.boolean = value;
+       return v;
+}
+
+/******************************************************************************/
+/*
+ *     Initialize a C function.
+ */
+
+MprVar mprCreateCFunctionVar(MprCFunction fn, void *thisPtr, int flags)
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_CFUNCTION;
+       v.cFunction.fn = fn;
+       v.cFunction.thisPtr = thisPtr;
+       v.flags = flags;
+
+       return v;
+}
+
+/******************************************************************************/
+/*
+ *     Initialize a C function.
+ */
+
+MprVar mprCreateStringCFunctionVar(MprStringCFunction fn, void *thisPtr, int flags)
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_STRING_CFUNCTION;
+       v.cFunctionWithStrings.fn = fn;
+       v.cFunctionWithStrings.thisPtr = thisPtr;
+       v.flags = flags;
+
+       return v;
+}
+
+/******************************************************************************/
+#if BLD_FEATURE_FLOATING_POINT
+/*
+ *     Initialize a floating value.
+ */
+
+MprVar mprCreateFloatVar(double value)
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_FLOAT;
+       v.floating = value;
+       return v;
+}
+
+#endif
+/******************************************************************************/
+/*
+ *     Initialize an integer value.
+ */
+
+MprVar mprCreateIntegerVar(int value)
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_INT;
+       v.integer = value;
+       return v;
+}
+
+/******************************************************************************/
+#if BLD_FEATURE_INT64
+/*
+ *     Initialize a 64-bit integer value.
+ */
+
+MprVar mprCreateInteger64Var(int64 value)
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_INT64;
+       v.integer64 = value;
+       return v;
+}
+
+#endif /* BLD_FEATURE_INT64 */
+/******************************************************************************/
+/*
+ *     Initialize an number variable. Type is defined by configure.
+ */
+
+MprVar mprCreateNumberVar(MprNum value)
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = BLD_FEATURE_NUM_TYPE_ID;
+#if   BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64
+       v.integer64 = value;
+#elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT
+       v.float = value;
+#else
+       v.integer = value;
+#endif
+       return v;
+}
+
+/******************************************************************************/
+/*
+ *     Initialize a (bare) JavaScript function. args and body can be null.
+ */
+
+MprVar mprCreateFunctionVar(char *args, char *body, int flags)
+{
+       MprVar          v;
+       char            *cp, *arg, *last;
+       int                     aid;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_FUNCTION;
+       v.flags = flags;
+
+       v.function.args = mprCreateArray();
+
+       if (args) {
+               args = mprStrdup(args);
+               arg = mprStrTok(args, ",", &last);
+               while (arg) {
+                       while (isspace((int) *arg))
+                               arg++;
+                       for (cp = &arg[strlen(arg) - 1]; cp > arg; cp--) {
+                               if (!isspace((int) *cp)) {
+                                       break;
+                               }
+                       }
+                       cp[1] = '\0';
+                                       
+                       aid = mprAddToArray(v.function.args, mprStrdup(arg));
+                       arg = mprStrTok(0, ",", &last);
+               }
+               mprFree(args);
+       }
+
+       if (body) {
+               v.function.body = mprStrdup(body);
+       }
+       v.allocatedData = 1;
+       return v;
+}
+
+/******************************************************************************/
+/*
+ *     Initialize an object variable. Return type == MPR_TYPE_UNDEFINED if the
+ *     memory allocation for the properties table failed.
+ */
+
+MprVar mprCreateObjVar(const char *name, int hashSize)
+{
+       MprVar  v;
+
+       mprAssert(name && *name);
+
+       memset(&v, 0x0, sizeof(MprVar));
+       v.type = MPR_TYPE_OBJECT;
+       if (hashSize <= 0) {
+               hashSize = MPR_DEFAULT_HASH_SIZE;
+       }
+       v.properties = createProperties(name, hashSize);
+       if (v.properties == 0) {
+               /* Indicate failed memory allocation */
+               v.type = MPR_TYPE_UNDEFINED;
+       }
+       v.allocatedData = 1;
+       v.name = mprStrdup(name);
+       mprLog(7, "mprCreateObjVar %s, 0x%x\n", name, v.properties);
+       return v;
+}
+
+/******************************************************************************/
+/*
+ *     Initialize a string value.
+ */
+
+MprVar mprCreateStringVar(const char *value, bool allocate)
+{
+       MprVar  v;
+
+       memset(&v, 0x0, sizeof(v));
+       v.type = MPR_TYPE_STRING;
+       if (value == 0) {
+               v.string = "";
+       } else if (allocate) {
+               v.string = mprStrdup(value);
+               v.allocatedData = 1;
+       } else {
+               v.string = value;
+       }
+       return v;
+}
+
+/******************************************************************************/
+/*
+ *     Copy an objects core value (only). This preserves the destination object's 
+ *     name. This implements copy by reference for objects and copy by value for 
+ *     strings and other types. Caller must free dest prior to calling.
+ */
+
+static void copyVarCore(MprVar *dest, MprVar *src, int copyDepth)
+{
+       MprVarTrigger   saveTrigger;
+       MprVar                  *srcProp, *destProp, *last;
+       char                    **srcArgs;
+       int                             i;
+
+       mprAssert(dest);
+       mprAssert(src);
+
+       if (dest == src) {
+               return;
+       }
+
+       /*
+        *      FUTURE: we should allow read-only triggers where the value is never
+        *      stored in the object. Currently, triggers override the readonly
+        *      status.
+        */
+
+       if (dest->type != MPR_TYPE_UNDEFINED && dest->readonly && !dest->trigger) {
+               mprAssert(0);
+               return;
+       }
+
+       if (dest->type != MPR_TYPE_UNDEFINED) {
+               saveTrigger = dest->trigger;
+               freeVarStorage(dest, 0);
+       } else {
+               saveTrigger = 0;
+       }
+
+       switch (src->type) {
+       default:
+       case MPR_TYPE_UNDEFINED:
+       case MPR_TYPE_NULL:
+               break;
+
+       case MPR_TYPE_BOOL:
+               dest->boolean = src->boolean;
+               break;
+
+       case MPR_TYPE_STRING_CFUNCTION:
+               dest->cFunctionWithStrings = src->cFunctionWithStrings;
+               break;
+
+       case MPR_TYPE_CFUNCTION:
+               dest->cFunction = src->cFunction;
+               break;
+
+#if BLD_FEATURE_FLOATING_POINT
+       case MPR_TYPE_FLOAT:
+               dest->floating = src->floating;
+               break;
+#endif
+
+       case MPR_TYPE_INT:
+               dest->integer = src->integer;
+               break;
+
+#if BLD_FEATURE_INT64
+       case MPR_TYPE_INT64:
+               dest->integer64 = src->integer64;
+               break;
+#endif
+
+       case MPR_TYPE_OBJECT:
+               if (copyDepth == MPR_DEEP_COPY) {
+
+                       dest->properties = createProperties(src->name, 
+                               src->properties->hashSize);
+                       dest->allocatedData = 1;
+
+                       for (i = 0; i < (int) src->properties->hashSize; i++) {
+                               last = 0;
+                               for (srcProp = src->properties->buckets[i]; srcProp; 
+                                               srcProp = srcProp->forw) {
+                                       if (srcProp->visited) {
+                                               continue;
+                                       }
+                                       destProp = allocProperty(srcProp->name);
+                                       if (destProp == 0) {
+                                               mprAssert(destProp);
+                                               return;
+                                       }
+
+                                       destProp->bucketIndex = i;
+                                       if (last) {
+                                               last->forw = destProp;
+                                       } else {
+                                               dest->properties->buckets[i] = destProp;
+                                       }
+                                       destProp->parentProperties = dest->properties;
+
+                                       /*
+                                        *      Recursively copy the object
+                                        */
+                                       srcProp->visited = 1;
+                                       copyVarCore(destProp, srcProp, copyDepth);
+                                       srcProp->visited = 0;
+                                       last = srcProp;
+                               }
+                       }
+                       dest->properties->numItems = src->properties->numItems;
+                       dest->properties->numDataItems = src->properties->numDataItems;
+                       dest->allocatedData = 1;
+
+               } else if (copyDepth == MPR_SHALLOW_COPY) {
+                       dest->properties = src->properties;
+                       adjustVarRefCount(src, 1);
+                       dest->allocatedData = 1;
+
+               } else {
+                       dest->properties = src->properties;
+                       dest->allocatedData = 0;
+               }
+               break;
+
+       case MPR_TYPE_FUNCTION:
+               if (copyDepth != MPR_NO_COPY) {
+                       dest->function.args = mprCreateArray();
+                       srcArgs = (char**) src->function.args->handles;
+                       for (i = 0; i < src->function.args->max; i++) {
+                               if (srcArgs[i]) {
+                                       mprAddToArray(dest->function.args, mprStrdup(srcArgs[i]));
+                               }
+                       }
+                       dest->function.body = mprStrdup(src->function.body);
+                       dest->allocatedData = 1;
+               } else {
+                       dest->function.args = src->function.args;
+                       dest->function.body = src->function.body;
+                       dest->allocatedData = 0;
+               }
+               break;
+
+       case MPR_TYPE_STRING:
+               if (src->string && copyDepth != MPR_NO_COPY) {
+                       dest->string = mprStrdup(src->string);
+                       dest->allocatedData = 1;
+               } else {
+                       dest->string = src->string;
+                       dest->allocatedData = 0;
+               }
+               break;
+       }
+
+       dest->type = src->type;
+       dest->flags = src->flags;
+       dest->trigger = saveTrigger;
+
+       /*
+        *      Just for safety
+        */
+       dest->spare = 0;
+}
+
+/******************************************************************************/
+/*
+ *     Copy an entire object including name.
+ */
+
+void mprCopyVar(MprVar *dest, MprVar *src, int copyDepth)
+{
+       mprAssert(dest);
+       mprAssert(src);
+
+       copyVarCore(dest, src, copyDepth);
+
+       mprFree(dest->name);
+       dest->name = mprStrdup(src->name);
+
+#if VAR_DEBUG
+       if (src->type == MPR_TYPE_OBJECT) {
+
+               mprFree(dest->fullName);
+               dest->fullName = mprStrdup(src->fullName);
+
+               mprLog(7, "mprCopyVar: object \"%s\", FDQ \"%s\" 0x%x, refCount %d\n", 
+                       dest->name, dest->fullName, dest->properties, 
+                       dest->properties->refCount);
+       }
+#endif
+}
+
+/******************************************************************************/
+/*
+ *     Copy an entire object including name.
+ */
+
+void mprCopyVarValue(MprVar *dest, MprVar src, int copyDepth)
+{
+       mprAssert(dest);
+
+       mprCopyVar(dest, &src, copyDepth); 
+}
+
+/******************************************************************************/
+/*
+ *     Copy an object. This implements copy by reference for objects and copy by
+ *     value for strings and other types. Caller must free dest prior to calling.
+ */
+
+MprVar *mprDupVar(MprVar *src, int copyDepth)
+{
+       MprVar  *dest;
+
+       mprAssert(src);
+
+       dest = (MprVar*) mprMalloc(sizeof(MprVar));
+       memset(dest, 0, sizeof(MprVar));
+
+       mprCopyVar(dest, src, copyDepth);
+       return dest;
+}
+
+/******************************************************************************/
+/*
+ *     Convert a value to a text based representation of its value
+ *     FUTURE -- conver this to use the format string in all cases. Allow
+ *     arbitrary format strings.
+ */
+
+void mprVarToString(char** out, int size, char *fmt, MprVar *obj)
+{
+       char    *src;
+
+       mprAssert(out);
+
+       *out = NULL;
+
+       if (obj->trigger) {
+               mprReadProperty(obj, 0);
+       }
+
+       switch (obj->type) {
+       case MPR_TYPE_UNDEFINED:
+               //      FUTURE -- spec says convert to "undefined"
+               *out = mprStrdup("");
+               break;
+
+       case MPR_TYPE_NULL:
+               *out = mprStrdup("null");
+               break;
+
+       case MPR_TYPE_BOOL:
+               if (obj->boolean) {
+                       *out = mprStrdup("true");
+               } else {
+                       *out = mprStrdup("false");
+               }
+               break;
+
+#if BLD_FEATURE_FLOATING_POINT
+       case MPR_TYPE_FLOAT:
+               if (fmt == NULL || *fmt == '\0') {
+                       mprAllocSprintf(out, size, "%f", obj->floating);
+               } else {
+                       mprAllocSprintf(out, size, fmt, obj->floating);
+               }
+               break;
+#endif
+
+       case MPR_TYPE_INT:
+               if (fmt == NULL || *fmt == '\0') {
+                       mprAllocSprintf(out, size, "%d", obj->integer);
+               } else {
+                       mprAllocSprintf(out, size, fmt, obj->integer);
+               }
+               break;
+
+#if BLD_FEATURE_INT64
+       case MPR_TYPE_INT64:
+               if (fmt == NULL || *fmt == '\0') {
+#if BLD_GOAHEAD_WEBSERVER
+                       mprAllocSprintf(out, size, "%d", (int) obj->integer64);
+#else
+                       mprAllocSprintf(out, size, "%Ld", obj->integer64);
+#endif
+               } else {
+                       mprAllocSprintf(out, size, fmt, obj->integer64);
+               }
+               break;
+#endif
+
+       case MPR_TYPE_CFUNCTION:
+               mprAllocSprintf(out, size, "[C Function]");
+               break;
+
+       case MPR_TYPE_STRING_CFUNCTION:
+               mprAllocSprintf(out, size, "[C StringFunction]");
+               break;
+
+       case MPR_TYPE_FUNCTION:
+               mprAllocSprintf(out, size, "[JavaScript Function]");
+               break;
+
+       case MPR_TYPE_OBJECT:
+               //      FUTURE -- really want: [object class: name] 
+               mprAllocSprintf(out, size, "[object %s]", obj->name);
+               break;
+
+       case MPR_TYPE_STRING:
+               src = obj->string;
+
+               mprAssert(src);
+               if (fmt && *fmt) {
+                       mprAllocSprintf(out, size, fmt, src);
+
+               } else if (src == NULL) {
+                       *out = mprStrdup("null");
+
+               } else {
+                       *out = mprStrdup(src);
+               }
+               break;
+
+       default:
+               mprAssert(0);
+       }
+}
+
+/******************************************************************************/
+/*
+ *     Parse a string based on formatting instructions and intelligently 
+ *     create a variable.
+ */
+
+MprVar mprParseVar(char *buf, MprType preferredType)
+{
+       MprType         type;
+       char            *cp;
+
+       mprAssert(buf);
+
+       if (preferredType == MPR_TYPE_UNDEFINED) {
+               if (*buf == '-') {
+                       type = MPR_NUM_VAR;
+
+               } else if (!isdigit((int) *buf)) {
+                       if (strcmp(buf, "true") == 0 || strcmp(buf, "false") == 0) {
+                               type = MPR_TYPE_BOOL;
+                       } else {
+                               type = MPR_TYPE_STRING;
+                       }
+
+               } else if (isdigit((int) *buf)) {
+                       type = MPR_NUM_VAR;
+                       cp = buf;
+                       if (*cp && tolower(cp[1]) == 'x') {
+                               cp = &cp[2];
+                       }
+                       for (cp = buf; *cp; cp++) {
+                               if (! isdigit((int) *cp)) {
+                                       break;
+                               }
+                       }
+
+                       if (*cp != '\0') {
+#if BLD_FEATURE_FLOATING_POINT
+                               if (*cp == '.' || tolower(*cp) == 'e') {
+                                       type = MPR_TYPE_FLOAT;
+                               } else
+#endif
+                               {
+                                       type = MPR_NUM_VAR;
+                               }
+                       }
+               }
+       } else {
+               type = preferredType;
+       }
+
+       switch (type) {
+       case MPR_TYPE_OBJECT:
+       case MPR_TYPE_UNDEFINED:
+       case MPR_TYPE_NULL:
+       default:
+               break;
+
+       case MPR_TYPE_BOOL:
+               return mprCreateBoolVar(buf[0] == 't' ? 1 : 0);
+
+       case MPR_TYPE_INT:
+               return mprCreateIntegerVar(mprParseInteger(buf));
+
+#if BLD_FEATURE_INT64
+       case MPR_TYPE_INT64:
+               return mprCreateInteger64Var(mprParseInteger64(buf));
+#endif
+
+       case MPR_TYPE_STRING:
+               if (strcmp(buf, "null") == 0) {
+                       return mprCreateNullVar();
+               } else if (strcmp(buf, "undefined") == 0) {
+                       return mprCreateUndefinedVar();
+               } 
+                       
+               return mprCreateStringVar(buf, 1);
+
+#if BLD_FEATURE_FLOATING_POINT
+       case MPR_TYPE_FLOAT:
+               return mprCreateFloatVar(atof(buf));
+#endif
+
+       }
+       return mprCreateUndefinedVar();
+}
+
+/******************************************************************************/
+/*
+ *     Convert the variable to a boolean. Only for primitive types.
+ */
+
+bool mprVarToBool(MprVar *vp)
+{
+       mprAssert(vp);
+
+       switch (vp->type) {
+       case MPR_TYPE_UNDEFINED:
+       case MPR_TYPE_NULL:
+       case MPR_TYPE_STRING_CFUNCTION:
+       case MPR_TYPE_CFUNCTION:
+       case MPR_TYPE_FUNCTION:
+       case MPR_TYPE_OBJECT:
+               return 0;
+
+       case MPR_TYPE_BOOL:
+               return vp->boolean;
+
+#if BLD_FEATURE_FLOATING_POINT
+       case MPR_TYPE_FLOAT:
+               return (vp->floating != 0 && !mprIsNan(vp->floating));
+#endif
+
+       case MPR_TYPE_INT:
+               return (vp->integer != 0);
+
+#if BLD_FEATURE_INT64
+       case MPR_TYPE_INT64:
+               return (vp->integer64 != 0);
+#endif
+
+       case MPR_TYPE_STRING:
+               mprAssert(vp->string);
+               return (vp->string[0] != '\0');
+       }
+
+       /* Not reached */
+       return 0;
+}
+
+/******************************************************************************/
+#if BLD_FEATURE_FLOATING_POINT
+/*
+ *     Convert the variable to a floating point number. Only for primitive types.
+ */
+
+double mprVarToFloat(MprVar *vp)
+{
+       mprAssert(vp);
+
+       switch (vp->type) {
+       case MPR_TYPE_UNDEFINED:
+       case MPR_TYPE_NULL:
+       case MPR_TYPE_STRING_CFUNCTION:
+       case MPR_TYPE_CFUNCTION:
+       case MPR_TYPE_FUNCTION:
+       case MPR_TYPE_OBJECT:
+               return 0;
+
+       case MPR_TYPE_BOOL:
+               return (vp->boolean) ? 1.0 : 0.0;
+
+       case MPR_TYPE_FLOAT:
+               return vp->floating;
+
+       case MPR_TYPE_INT:
+               return (double) vp->integer;
+
+#if BLD_FEATURE_INT64
+       case MPR_TYPE_INT64:
+               return (double) vp->integer64;
+#endif
+
+       case MPR_TYPE_STRING:
+               mprAssert(vp->string);
+               return atof(vp->string);
+       }
+
+       /* Not reached */
+       return 0;
+}
+
+#endif
+/******************************************************************************/
+/*
+ *     Convert the variable to a number type. Only works for primitive types.
+ */
+
+MprNum mprVarToNumber(MprVar *vp)
+{
+#if BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64
+       return mprVarToInteger64(vp);
+#elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT
+       return mprVarToFloat(vp);
+#else 
+       return mprVarToInteger(vp);
+#endif
+}
+
+/******************************************************************************/
+/*
+ *     Convert the variable to a number type. Only works for primitive types.
+ */
+
+MprNum mprParseNumber(char *s)
+{
+#if BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_INT64
+       return mprParseInteger64(s);
+#elif BLD_FEATURE_NUM_TYPE_ID == MPR_TYPE_FLOAT
+       return mprParseFloat(s);
+#else 
+       return mprParseInteger(s);
+#endif
+}
+
+/******************************************************************************/
+#if BLD_FEATURE_INT64
+/*
+ *     Convert the variable to an Integer64 type. Only works for primitive types.
+ */
+
+int64 mprVarToInteger64(MprVar *vp)
+{
+       mprAssert(vp);
+
+       switch (vp->type) {
+       case MPR_TYPE_UNDEFINED:
+       case MPR_TYPE_NULL:
+       case MPR_TYPE_STRING_CFUNCTION:
+       case MPR_TYPE_CFUNCTION:
+       case MPR_TYPE_FUNCTION:
+       case MPR_TYPE_OBJECT:
+               return 0;
+
+       case MPR_TYPE_BOOL:
+               return (vp->boolean) ? 1 : 0;
+
+#if BLD_FEATURE_FLOATING_POINT
+       case MPR_TYPE_FLOAT:
+               if (mprIsNan(vp->floating)) {
+                       return 0;
+               }
+               return (int64) vp->floating;
+#endif
+
+       case MPR_TYPE_INT:
+               return vp->integer;
+
+       case MPR_TYPE_INT64:
+               return vp->integer64;
+
+       case MPR_TYPE_STRING:
+               return mprParseInteger64(vp->string);
+       }
+
+       /* Not reached */
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Convert the string buffer to an Integer64.
+ */
+
+int64 mprParseInteger64(char *str)
+{
+       char    *cp;
+       int64   num64;
+       int             radix, c, negative;
+
+       mprAssert(str);
+
+       cp = str;
+       num64 = 0;
+       negative = 0;
+
+       if (*cp == '-') {
+               cp++;
+               negative = 1;
+       }
+
+       /*
+        *      Parse a number. Observe hex and octal prefixes (0x, 0)
+        */
+       if (*cp != '0') {
+               /* 
+                *      Normal numbers (Radix 10)
+                */
+               while (isdigit((int) *cp)) {
+                       num64 = (*cp - '0') + (num64 * 10);
+                       cp++;
+               }
+       } else {
+               cp++;
+               if (tolower(*cp) == 'x') {
+                       cp++;
+                       radix = 16;
+                       while (*cp) {
+                               c = tolower(*cp);
+                               if (isdigit(c)) {
+                                       num64 = (c - '0') + (num64 * radix);
+                               } else if (c >= 'a' && c <= 'f') {
+                                       num64 = (c - 'a') + (num64 * radix);
+                               } else {
+                                       break;
+                               }
+                               cp++;
+                       }
+
+               } else{
+                       radix = 8;
+                       while (*cp) {
+                               c = tolower(*cp);
+                               if (isdigit(c) && c < '8') {
+                                       num64 = (c - '0') + (num64 * radix);
+                               } else {
+                                       break;
+                               }
+                               cp++;
+                       }
+               }
+       }
+
+       if (negative) {
+               return 0 - num64;
+       }
+       return num64;
+}
+
+#endif /* BLD_FEATURE_INT64 */
+/******************************************************************************/
+/*
+ *     Convert the variable to an Integer type. Only works for primitive types.
+ */
+
+int mprVarToInteger(MprVar *vp)
+{
+       mprAssert(vp);
+
+       switch (vp->type) {
+       case MPR_TYPE_UNDEFINED:
+       case MPR_TYPE_NULL:
+       case MPR_TYPE_STRING_CFUNCTION:
+       case MPR_TYPE_CFUNCTION:
+       case MPR_TYPE_FUNCTION:
+       case MPR_TYPE_OBJECT:
+               return 0;
+
+       case MPR_TYPE_BOOL:
+               return (vp->boolean) ? 1 : 0;
+
+#if BLD_FEATURE_FLOATING_POINT
+       case MPR_TYPE_FLOAT:
+               if (mprIsNan(vp->floating)) {
+                       return 0;
+               }
+               return (int) vp->floating;
+#endif
+
+       case MPR_TYPE_INT:
+               return vp->integer;
+
+#if BLD_FEATURE_INT64
+       case MPR_TYPE_INT64:
+               return (int) vp->integer64;
+#endif
+
+       case MPR_TYPE_STRING:
+               return mprParseInteger(vp->string);
+       }
+
+       /* Not reached */
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Convert the string buffer to an Integer.
+ */
+
+int mprParseInteger(char *str)
+{
+       char    *cp;
+       int             num;
+       int             radix, c, negative;
+
+       mprAssert(str);
+
+       cp = str;
+       num = 0;
+       negative = 0;
+
+       if (*cp == '-') {
+               cp++;
+               negative = 1;
+       }
+
+       /*
+        *      Parse a number. Observe hex and octal prefixes (0x, 0)
+        */
+       if (*cp != '0') {
+               /* 
+                *      Normal numbers (Radix 10)
+                */
+               while (isdigit((int) *cp)) {
+                       num = (*cp - '0') + (num * 10);
+                       cp++;
+               }
+       } else {
+               cp++;
+               if (tolower(*cp) == 'x') {
+                       cp++;
+                       radix = 16;
+                       while (*cp) {
+                               c = tolower(*cp);
+                               if (isdigit(c)) {
+                                       num = (c - '0') + (num * radix);
+                               } else if (c >= 'a' && c <= 'f') {
+                                       num = (c - 'a') + (num * radix);
+                               } else {
+                                       break;
+                               }
+                               cp++;
+                       }
+
+               } else{
+                       radix = 8;
+                       while (*cp) {
+                               c = tolower(*cp);
+                               if (isdigit(c) && c < '8') {
+                                       num = (c - '0') + (num * radix);
+                               } else {
+                                       break;
+                               }
+                               cp++;
+                       }
+               }
+       }
+
+       if (negative) {
+               return 0 - num;
+       }
+       return num;
+}
+
+/******************************************************************************/
+#if BLD_FEATURE_FLOATING_POINT
+/*
+ *     Convert the string buffer to an Floating.
+ */
+
+double mprParseFloat(char *str)
+{
+       return atof(str);
+}
+
+/******************************************************************************/
+
+bool mprIsNan(double f)
+{
+#if WIN
+       return _isnan(f);
+#elif VXWORKS
+       //      FUTURE
+       return (0);
+#else
+       return (f == FP_NAN);
+#endif
+}
+/******************************************************************************/
+
+bool mprIsInfinite(double f)
+{
+#if WIN
+       return !_finite(f);
+#elif VXWORKS
+       //      FUTURE
+       return (0);
+#else
+       return (f == FP_INFINITE);
+#endif
+}
+
+#endif // BLD_FEATURE_FLOATING_POINT
+/******************************************************************************/
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim:tw=78
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/source4/web_server/ejs/var.h b/source4/web_server/ejs/var.h
new file mode 100644 (file)
index 0000000..2e8fdf6
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ *     @file   var.h
+ *     @brief  MPR Universal Variable Type
+ *     @copy   default.m
+ *     
+ *     Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
+ *     Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved.
+ *     
+ *     This software is distributed under commercial and open source licenses.
+ *     You may use the GPL open source license described below or you may acquire 
+ *     a commercial license from Mbedthis Software. You agree to be fully bound 
+ *     by the terms of either license. Consult the LICENSE.TXT distributed with 
+ *     this software for full details.
+ *     
+ *     This software is open source; you can redistribute it and/or modify it 
+ *     under the terms of the GNU General Public License as published by the 
+ *     Free Software Foundation; either version 2 of the License, or (at your 
+ *     option) any later version. See the GNU General Public License for more 
+ *     details at: http://www.mbedthis.com/downloads/gplLicense.html
+ *     
+ *     This program is distributed WITHOUT ANY WARRANTY; without even the 
+ *     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ *     
+ *     This GPL license does NOT permit incorporating this software into 
+ *     proprietary programs. If you are unable to comply with the GPL, you must
+ *     acquire a commercial license to use this software. Commercial licenses 
+ *     for this software and support services are available from Mbedthis 
+ *     Software at http://www.mbedthis.com 
+ *     
+ *     @end
+ */
+
+/******************************* Documentation ********************************/
+/*
+ *     Variables can efficiently store primitive types and can hold references to
+ *     objects. Objects can store properties which are themselves variables.
+ *     Properties can be primitive data types, other objects or functions. 
+ *     Properties are indexed by a character name. A variable may store one of 
+ *     the following types: 
+ *
+ *             string, integer, integer-64bit, C function, C function with string args,
+ *              Javascript function, Floating point number, boolean value, Undefined 
+ *             value and the Null value. 
+ *
+ *     Variables have names while objects may be referenced by multiple variables.
+ *     Objects use reference counting for garbage collection.
+ *
+ *     This module is not thread safe for performance and compactness. It relies
+ *     on upper modules to provide thread synchronization as required. The API
+ *     provides primitives to get variable/object references or to get copies of 
+ *     variables which will help minimize required lock times.
+ */
+
+#ifndef _h_MPR_VAR
+#define _h_MPR_VAR 1
+
+/********************************* Includes ***********************************/
+
+#include       "web_server/ejs/miniMpr.h"
+
+/********************************** Defines ***********************************/
+
+/*
+ *     Define VAR_DEBUG if you want to track objects. However, this code is not
+ *     thread safe and you need to run the server single threaded.
+ *
+ *             #define VAR_DEBUG 1
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ *     Forward declare types
+ */
+struct MprProperties;
+struct MprVar;
+
+/*
+ *     Possible variable types. Don't use enum because we need to be able to
+ *     do compile time conditional compilation on BLD_FEATURE_NUM_TYPE_ID.
+ */
+typedef int MprType;
+#define MPR_TYPE_UNDEFINED                     0       ///< Undefined. No value has been set.
+#define MPR_TYPE_NULL                          1       ///< Value defined to be null.
+#define MPR_TYPE_BOOL                          2       ///< Boolean type.
+#define MPR_TYPE_CFUNCTION                     3       ///< C function or C++ method
+#define MPR_TYPE_FLOAT                                 4       ///< Floating point number
+#define MPR_TYPE_INT                           5       ///< Integer number
+#define MPR_TYPE_INT64                                 6       ///< 64-bit Integer number
+#define MPR_TYPE_OBJECT                        7       ///< Object reference
+#define MPR_TYPE_FUNCTION                      8       ///< JavaScript function
+#define MPR_TYPE_STRING                        9       ///< String (immutable)
+#define MPR_TYPE_STRING_CFUNCTION      10      ///< C/C++ function with string args
+
+/*
+ *     Create a type for the default number type
+ *     Config.h will define the default number type. For example:
+ *
+ *             BLD_FEATURE_NUM_TYPE=int
+ *             BLD_FEATURE_NUM_TYPE_ID=MPR_TYPE_INT
+ */
+
+/**
+ *     Set to the type used for MPR numeric variables. Will equate to int, int64 
+ *     or double. 
+ */
+typedef BLD_FEATURE_NUM_TYPE MprNum;
+
+/**
+ *     Set to the MPR_TYPE used for MPR numeric variables. Will equate to 
+ *     MPR_TYPE_INT, MPR_TYPE_INT64 or MPR_TYPE_FLOAT.
+ */
+#define MPR_NUM_VAR BLD_FEATURE_NUM_TYPE_ID
+#define MPR_TYPE_NUM BLD_FEATURE_NUM_TYPE_ID
+
+/*
+ *     Return TRUE if a variable is a function type
+ */
+#define mprVarIsFunction(type) \
+       (type == MPR_TYPE_FUNCTION || type == MPR_TYPE_STRING_CFUNCTION || \
+        type == MPR_TYPE_CFUNCTION)
+
+/*
+ *     Return TRUE if a variable is a numeric type
+ */
+#define mprVarIsNumber(type) \
+       (type == MPR_TYPE_INT || type == MPR_TYPE_INT64 || type == MPR_TYPE_FLOAT)
+
+/*
+ *     Return TRUE if a variable is a boolean
+ */
+#define mprVarIsBoolean(type) \
+       (type == MPR_TYPE_BOOL)
+#define mprVarIsString(type) \
+       (type == MPR_TYPE_STRING)
+#define mprVarIsObject(type) \
+       (type == MPR_TYPE_OBJECT)
+#define mprVarIsFloating(type) \
+       (type == MPR_TYPE_FLOAT)
+#define mprVarIsUndefined(var) \
+       ((var)->type == MPR_TYPE_UNDEFINED)
+#define mprVarIsNull(var) \
+       ((var)->type == MPR_TYPE_NULL)
+#define mprVarIsValid(var) \
+       (((var)->type != MPR_TYPE_NULL) && ((var)->type != MPR_TYPE_UNDEFINED))
+
+#define MPR_VAR_MAX_RECURSE            5                               /* Max object loops */
+
+#if BLD_FEATURE_SQUEEZE
+#define MPR_MAX_VAR                            64                              /* Max var full name */
+#else
+#define MPR_MAX_VAR                            512
+#endif
+
+#ifndef __NO_PACK
+#pragma pack(2)
+#endif /* _NO_PACK */
+
+/*
+ *     Function signatures
+ */
+typedef int    MprVarHandle;
+typedef int (*MprCFunction)(MprVarHandle userHandle, int argc, 
+       struct MprVar **argv);
+typedef int (*MprStringCFunction)(MprVarHandle userHandle, int argc, 
+       char **argv);
+
+/*
+ *     Triggers
+ */
+typedef enum {
+       MPR_VAR_WRITE,                                          /* This property is being updated */
+       MPR_VAR_READ,                                           /* This property is being read */
+       MPR_VAR_CREATE_PROPERTY,                        /* A property is being created */
+       MPR_VAR_DELETE_PROPERTY,                        /* A property is being deleted */
+       MPR_VAR_DELETE                                          /* This object is being deleted */
+} MprVarTriggerOp;
+
+/*
+ *     Trigger function return codes.
+ */
+typedef enum {
+       MPR_TRIGGER_ABORT,                                      /* Abort the current operation */
+       MPR_TRIGGER_USE_NEW_VALUE,                      /* Proceed and use the newValue */
+       MPR_TRIGGER_PROCEED                                     /* Proceed with the operation */
+} MprVarTriggerStatus;
+
+/*
+ *     The MprVarTrigger arguments have the following meaning:
+ *
+ *             op                                      The operation being performed. See MprVarTriggerOp.
+ *             parentProperties        Pointer to the MprProperties structure.
+ *             vp                                      Pointer to the property that registered the trigger.
+ *             newValue                        New value (see below for more details).
+ *             copyDepth                       Specify what data items to copy.
+ *
+ *     For VAR_READ, newVar is set to a temporary variable that the trigger 
+ *             function may assign a value to be returned instead of the actual 
+ *             property value. 
+ *     For VAR_WRITE, newValue holds the new value. The old existing value may be
+ *             accessed via vp.
+ *     For DELETE_PROPERTY, vp is the property being deleted. newValue is null.
+ *     For ADD_PROPERTY, vp is set to the property being added and newValue holds 
+ *             the new value.
+ */
+typedef MprVarTriggerStatus (*MprVarTrigger)(MprVarTriggerOp op, 
+       struct MprProperties *parentProperties, struct MprVar *vp, 
+       struct MprVar *newValue, int copyDepth);
+
+/*
+ *     mprCreateFunctionVar flags
+ */
+/** Use the alternate handle on function callbacks */
+#define MPR_VAR_ALT_HANDLE             0x1
+
+/** Use the script handle on function callbacks */
+#define MPR_VAR_SCRIPT_HANDLE  0x2
+
+/*
+ *     Useful define for the copyDepth argument
+ */
+/** Don't copy any data. Copy only the variable name */
+#define MPR_NO_COPY                    0
+
+/** Copy strings. Increment object reference counts. */
+#define MPR_SHALLOW_COPY       1
+
+/** Copy strings and do complete object copies. */
+#define MPR_DEEP_COPY          2
+
+/*
+ *     GetFirst / GetNext flags
+ */
+/** Step into data properties. */
+#define MPR_ENUM_DATA          0x1
+
+/** Step into functions properties. */
+#define MPR_ENUM_FUNCTIONS     0x2
+
+/*
+ *     Collection type to hold properties in an object
+ */
+typedef struct MprProperties {                                 /* Collection of properties */
+#if VAR_DEBUG
+       struct MprProperties *next;                                     /* Linked list */
+       struct MprProperties *prev;                                     /* Linked list */
+       char                            name[32];                               /* Debug name */
+#endif
+       struct MprVar           **buckets;                              /* Hash chains */
+       int                                     numItems;                               /* Total count of items */
+       int                                     numDataItems;                   /* Enumerable data items */
+       uint                            hashSize                : 8;    /* Size of the hash table */
+       uint                            refCount                : 8;    /* References to this property*/
+       uint                            deleteProtect   : 8;    /* Don't recursively delete */
+       uint                            visited                 : 8;    /* Node has been processed */
+} MprProperties;
+
+/*
+ *     Universal Variable Type
+ */
+typedef struct MprVar {
+       MprStr                          name;                                   /* Property name */
+       MprStr                          fullName;                               /* Full object name */
+       MprProperties           *properties;                    /* Pointer to properties */
+
+       /*
+        *      Packed bit field
+        */
+       MprType                         type                    : 8;    /* Selector into union */
+       uint                            bucketIndex             : 8;    /* Copy of bucket index */
+
+       uint                            flags                   : 5;    /* Type specific flags */
+       uint                            allocatedData   : 1;    /* Data needs freeing */
+       uint                            readonly                : 1;    /* Unmodifiable */
+       uint                            deleteProtect   : 1;    /* Don't recursively delete */
+
+       uint                            visited                 : 1;    /* Node has been processed */
+       uint                            allocatedVar    : 1;    /* Var needs freeing */
+       uint                            spare                   : 6;    /* Unused */
+
+       struct MprVar           *forw;                                  /* Hash table linkage */
+       MprVarTrigger           trigger;                                /* Trigger function */
+
+#if UNUSED && KEEP
+       struct MprVar           *baseClass;                             /* Pointer to class object */
+#endif
+       MprProperties           *parentProperties;              /* Pointer to parent object */
+
+       /*
+        *      Union of primitive types. When debugging on Linux, don't use unions 
+        *      as the gdb debugger can't display them.
+        */
+#if !BLD_DEBUG && !LINUX && !VXWORKS
+       union {
+#endif
+               int                             boolean;                                /* Use int for speed */
+#if BLD_FEATURE_FLOATING_POINT
+               double                  floating;
+#endif
+               int                             integer;
+#if BLD_FEATURE_INT64
+               int64                   integer64;
+#endif
+               struct {                                                                /* Javascript functions */
+                       MprArray        *args;                                  /* Null terminated */
+                       char            *body;
+               } function;
+               struct {                                                                /* Function with MprVar args */
+                       MprCFunction fn;
+                       void            *thisPtr;
+               } cFunction;
+               struct {                                                                /* Function with string args */
+                       MprStringCFunction fn;
+                       void            *thisPtr;
+               } cFunctionWithStrings;
+               MprStr                  string;                                 /* Allocated string */
+#if !BLD_DEBUG && !LINUX && !VXWORKS
+       };
+#endif
+} MprVar;
+
+/*
+ *     Define a field macro so code an use numbers in a "generic" fashion.
+ */
+#if MPR_NUM_VAR == MPR_TYPE_INT || DOXYGEN
+//*    Default numeric type */
+#define mprNumber integer
+#endif
+#if MPR_NUM_VAR == MPR_TYPE_INT64
+//*    Default numeric type */
+#define mprNumber integer64
+#endif
+#if MPR_NUM_VAR == MPR_TYPE_FLOAT
+//*    Default numeric type */
+#define mprNumber floating
+#endif
+
+typedef BLD_FEATURE_NUM_TYPE MprNumber;
+
+
+#ifndef __NO_PACK
+#pragma pack()
+#endif /* __NO_PACK */
+
+/********************************* Prototypes *********************************/
+/*
+ *     Variable constructors and destructors
+ */
+extern MprVar  mprCreateObjVar(const char *name, int hashSize);
+extern MprVar  mprCreateBoolVar(bool value);
+extern MprVar  mprCreateCFunctionVar(MprCFunction fn, void *thisPtr, 
+                                       int flags);
+#if BLD_FEATURE_FLOATING_POINT
+extern MprVar  mprCreateFloatVar(double value);
+#endif
+extern MprVar  mprCreateIntegerVar(int value);
+#if BLD_FEATURE_INT64
+extern MprVar  mprCreateInteger64Var(int64 value);
+#endif
+extern MprVar  mprCreateFunctionVar(char *args, char *body, int flags);
+extern MprVar  mprCreateNullVar(void);
+extern MprVar  mprCreateNumberVar(MprNumber value);
+extern MprVar  mprCreateStringCFunctionVar(MprStringCFunction fn, 
+                                       void *thisPtr, int flags);
+extern MprVar  mprCreateStringVar(const char *value, bool allocate);
+extern MprVar  mprCreateUndefinedVar(void);
+extern bool    mprDestroyVar(MprVar *vp);
+extern bool    mprDestroyAllVars(MprVar* vp);
+extern MprType mprGetVarType(MprVar *vp);
+
+/*
+ *     Copy
+ */
+extern void            mprCopyVar(MprVar *dest, MprVar *src, int copyDepth);
+extern void            mprCopyVarValue(MprVar *dest, MprVar src, int copyDepth);
+extern MprVar  *mprDupVar(MprVar *src, int copyDepth);
+
+/*
+ *     Manage vars
+ */
+extern MprVarTrigger 
+                               mprAddVarTrigger(MprVar *vp, MprVarTrigger fn);
+extern int             mprGetVarRefCount(MprVar *vp);
+extern void    mprSetVarDeleteProtect(MprVar *vp, int deleteProtect);
+extern void    mprSetVarFullName(MprVar *vp, char *name);
+extern void    mprSetVarReadonly(MprVar *vp, int readonly);
+extern void    mprSetVarName(MprVar *vp, char *name);
+
+/*
+ *     Create properties and return a reference to the property.
+ */
+extern MprVar  *mprCreateProperty(MprVar *obj, const char *property, 
+                                       MprVar *newValue);
+extern MprVar  *mprCreatePropertyValue(MprVar *obj, const char *property, 
+                                       MprVar newValue);
+extern int             mprDeleteProperty(MprVar *obj, const char *property);
+
+/*
+ *     Get/Set properties. Set will update/create.
+ */
+extern MprVar  *mprGetProperty(MprVar *obj, const char *property, MprVar *value);
+extern MprVar  *mprSetProperty(MprVar *obj, const char *property, MprVar *value);
+extern MprVar  *mprSetPropertyValue(MprVar *obj, const char *property, MprVar value);
+
+/*
+ *     Directly read/write property values (the property must already exist)
+ *     For mprCopyProperty, mprDestroyVar must always called on the var.
+ */
+extern int             mprReadProperty(MprVar *prop, MprVar *value);
+extern int             mprWriteProperty(MprVar *prop, MprVar *newValue);
+extern int             mprWritePropertyValue(MprVar *prop, MprVar newValue);
+
+/*
+ *     Copy a property. NOTE: reverse of most other args: (dest, src)
+ */
+extern int             mprCopyProperty(MprVar *dest, MprVar *prop, int copyDepth);
+
+/*
+ *     Enumerate properties
+ */
+extern MprVar  *mprGetFirstProperty(MprVar *obj, int includeFlags);
+extern MprVar  *mprGetNextProperty(MprVar *obj, MprVar *currentProperty, 
+                                       int includeFlags);
+
+/*
+ *     Query properties characteristics
+ */
+extern int             mprGetPropertyCount(MprVar *obj, int includeFlags);
+
+/*
+ *     Conversion routines
+ */
+extern MprVar  mprParseVar(char *str, MprType prefType);
+extern MprNum  mprVarToNumber(MprVar *vp);
+extern int             mprVarToInteger(MprVar *vp);
+#if BLD_FEATURE_INT64
+extern int64   mprVarToInteger64(MprVar *vp);
+#endif
+extern bool    mprVarToBool(MprVar *vp);
+#if BLD_FEATURE_FLOATING_POINT
+extern double  mprVarToFloat(MprVar *vp);
+#endif
+extern void    mprVarToString(char** buf, int size, char *fmt, MprVar *vp);
+
+/*
+ *     Parsing and utility routines
+ */
+extern MprNum  mprParseNumber(char *str);
+extern int             mprParseInteger(char *str);
+
+#if BLD_FEATURE_INT64
+extern int64   mprParseInteger64(char *str);
+#endif
+
+#if BLD_FEATURE_FLOATING_POINT
+extern double  mprParseFloat(char *str);
+extern bool    mprIsInfinite(double f);
+extern bool    mprIsNan(double f);
+#endif
+
+#if VAR_DEBUG
+extern void    mprPrintObjects(char *msg);
+extern void    mprPrintObjRefCount(MprVar *vp);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/*****************************************************************************/
+#endif /* _h_MPR_VAR */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim:tw=78
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/source4/web_server/esp/esp.c b/source4/web_server/esp/esp.c
new file mode 100644 (file)
index 0000000..26c3738
--- /dev/null
@@ -0,0 +1,1042 @@
+/*
+ *     @file   esp.c
+ *     @brief  Embedded Server Pages (ESP) core processing.
+ *     @overview The ESP handler provides an efficient way to generate 
+ *             dynamic pages using server-side Javascript. This code provides 
+ *             core processing, and should be called by an associated web 
+ *             server URL handler.
+ */
+/********************************* Copyright **********************************/
+/*
+ *     @copy   default
+ *     
+ *     Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
+ *     
+ *     This software is distributed under commercial and open source licenses.
+ *     You may use the GPL open source license described below or you may acquire 
+ *     a commercial license from Mbedthis Software. You agree to be fully bound 
+ *     by the terms of either license. Consult the LICENSE.TXT distributed with 
+ *     this software for full details.
+ *     
+ *     This software is open source; you can redistribute it and/or modify it 
+ *     under the terms of the GNU General Public License as published by the 
+ *     Free Software Foundation; either version 2 of the License, or (at your 
+ *     option) any later version. See the GNU General Public License for more 
+ *     details at: http://www.mbedthis.com/downloads/gplLicense.html
+ *     
+ *     This program is distributed WITHOUT ANY WARRANTY; without even the 
+ *     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ *     
+ *     This GPL license does NOT permit incorporating this software into 
+ *     proprietary programs. If you are unable to comply with the GPL, you must
+ *     acquire a commercial license to use this software. Commercial licenses 
+ *     for this software and support services are available from Mbedthis 
+ *     Software at http://www.mbedthis.com 
+ *     
+ *     @end
+ */
+/********************************** Includes **********************************/
+
+#include       "esp.h"
+
+#if BLD_FEATURE_ESP_MODULE
+
+/*********************************** Locals ***********************************/
+/*
+ *     Master ESP control interface with the web server
+ */
+
+static Esp *esp;
+
+/***************************** Forward Declarations ***************************/
+
+static int     buildScript(EspRequest *ep, char **jsBuf, char *input, char
+                               **errMsg);
+
+/************************************ Code ************************************/
+/*
+ *     Called at server initialization
+ */
+
+int espOpen(Esp *control)
+{
+       mprAssert(control);
+
+#if BLD_FEATURE_MULTITHREAD
+       ejsOpen(control->lock, control->unlock, control->lockData);
+#else
+       ejsOpen(0, 0, 0);
+#endif
+
+       /*
+        *      Register the standard procedures
+        */
+       espRegisterProcs();
+
+       /*
+        *      Just for brain dead systems that don't zero global memory
+        */
+       esp = control;
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Called at server termination
+ */
+
+void espClose()
+{
+       ejsClose();
+}
+
+/******************************************************************************/
+/*
+ *     Create for new ESP request. Assumed that this is called after all the 
+ *     HTTP headers have been read but before POST data has been read. It is 
+ *     expected that any session cookies have been read and that "variables" 
+ *     contains references to all the environment objects including "session". 
+ *     requestHandle is the web server request handle.
+ */
+
+EspRequest *espCreateRequest(EspHandle webServerRequestHandle, char *uri, 
+       MprVar *variables)
+{
+       EspRequest      *ep;
+       MprVar          *global;
+#if BLD_FEATURE_LEGACY_API
+       MprVar          *np;
+       char            keyBuf[ESP_MAX_HEADER];
+       int                     i;
+#endif
+
+       mprAssert(variables);
+
+       ep = mprMalloc(sizeof(EspRequest));
+       if (ep == 0) {
+               return 0;
+       }
+       memset(ep, 0, sizeof(EspRequest));
+       ep->requestHandle = webServerRequestHandle;
+       ep->esp = esp;
+       ep->uri = mprStrdup(uri);
+       ep->docPath = 0;
+       ep->variables = variables;
+       
+       /*
+        *      The handle passed to ejsOpenEngine is passed to every C function 
+        *      called by JavaScript.
+        */
+       ep->eid = ejsOpenEngine((EjsHandle) ep, (EjsHandle) webServerRequestHandle);
+       if (ep->eid < 0) {
+               mprFree(ep);
+               return 0;
+       }
+
+       /*
+        *      All these copies and SetProperties will only copy references 
+        *      They will increments the object ref counts.
+        */
+       mprCopyVar(&variables[ESP_GLOBAL_OBJ], ejsGetGlobalObject(ep->eid), 
+               MPR_SHALLOW_COPY);
+       mprCopyVar(&variables[ESP_LOCAL_OBJ], ejsGetLocalObject(ep->eid), 
+               MPR_SHALLOW_COPY);
+
+       global = &variables[ESP_GLOBAL_OBJ];
+       mprCreateProperty(global, "application", &variables[ESP_APPLICATION_OBJ]);
+       mprCreateProperty(global, "cookies", &variables[ESP_COOKIES_OBJ]);
+       mprCreateProperty(global, "files", &variables[ESP_FILES_OBJ]);
+       mprCreateProperty(global, "form", &variables[ESP_FORM_OBJ]);
+       mprCreateProperty(global, "headers", &variables[ESP_HEADERS_OBJ]);
+       mprCreateProperty(global, "request", &variables[ESP_REQUEST_OBJ]);
+
+       //
+       //      FUTURE -- could server be shared across all requests for a given host
+       //      and be made read-only.
+       //
+       mprCreateProperty(global, "server", &variables[ESP_SERVER_OBJ]);
+
+#if BLD_FEATURE_SESSION
+       mprCreateProperty(global, "session", &variables[ESP_SESSION_OBJ]);
+#endif
+
+#if BLD_FEATURE_LEGACY_API
+       /*
+        *      DEPRECATED: 2.0
+        *      Define variables as globals. headers[] are prefixed with "HTTP_".
+        *      NOTE: MaRequest::setVar does not copy into globals, whereas espSetVar
+        *      does if legacy_api is defined. So variables pre-defined by MaRequest 
+        *      must be copied here into globals[].\r
+        *\r
+        *      NOTE: if a variable is in session[] and in form[], the form[] will\r
+        *      override being later in the variables[] list. Use mprSetProperty \r
+        *      instead of mprCreateProperty to cover for this case.
+        */
+       for (i = 0; i < ESP_OBJ_MAX; i++) {
+               if (i == ESP_GLOBAL_OBJ || i == ESP_LOCAL_OBJ) {
+                       continue;
+               }
+               if (variables[i].type != MPR_TYPE_OBJECT) {
+                       continue;
+               }
+               np = mprGetFirstProperty(&variables[i], MPR_ENUM_DATA);
+               while (np) {
+                       if (i == ESP_HEADERS_OBJ) {
+                               mprSprintf(keyBuf, sizeof(keyBuf) - 1, "HTTP_%s", np->name);
+                               mprSetProperty(global, keyBuf, np);
+                       } else {
+                               mprSetProperty(global, np->name, np);
+                       }
+                       np = mprGetNextProperty(&variables[i], np, MPR_ENUM_DATA);
+               }
+       }
+#endif
+       return ep;
+}
+
+/******************************************************************************/
+void espDestroyRequest(EspRequest *ep)
+{
+       mprAssert(ep);
+       mprAssert(ep->eid >= 0);
+
+       mprFree(ep->uri);
+       mprFree(ep->docPath);
+       ejsCloseEngine(ep->eid);
+       mprFree(ep);
+}
+
+/******************************************************************************/
+/*
+ *     The callback function will be called:
+ *
+ *             (fn)(EjsId eid, EspRequest *ep, argc, argv);
+ *
+ *     Callers can get their web server handle by calling:
+ *
+ *             rq = (requiredCast) espGetHandle(ep);
+ */
+
+void espDefineCFunction(EspRequest *ep, char *functionName, EspCFunction fn, 
+       void *thisPtr)
+{
+       mprAssert(functionName && *functionName);
+       mprAssert(fn);
+
+       if (ep) {
+               ejsDefineCFunction(ep->eid, functionName, (MprCFunction) fn, 
+                       thisPtr, 0);
+       } else {
+               ejsDefineCFunction(-1, functionName, (MprCFunction) fn, thisPtr, 0);
+       }
+}
+
+/******************************************************************************/
+
+void espDefineStringCFunction(EspRequest *ep, const char *functionName, 
+       EspStringCFunction fn, void *thisPtr)
+{
+       mprAssert(functionName && *functionName);
+       mprAssert(fn);
+
+       if (ep) {
+               ejsDefineStringCFunction(ep->eid, functionName, (MprStringCFunction) fn,
+                       thisPtr, 0);
+       } else {
+               ejsDefineStringCFunction(-1, functionName, (MprStringCFunction) fn, 
+                       thisPtr, 0);
+       }
+}
+
+/******************************************************************************/
+
+void *espGetRequestHandle(EspRequest *ep)
+{
+       return ep->requestHandle;
+}
+
+/******************************************************************************/
+
+EjsId espGetScriptHandle(EspRequest *ep)
+{
+       return ep->eid;
+}
+
+/******************************************************************************/
+
+char *espGetStringVar(EspRequest *ep, EspEnvType oType, char *var, 
+       char *defaultValue)
+{
+       MprVar  value;
+
+       if (espGetVar(ep, oType, var, &value) < 0 || 
+                       value.type != MPR_TYPE_STRING) {
+               return defaultValue;
+       } 
+       return value.string;
+}
+
+/******************************************************************************/
+
+int espGetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar *value)
+{
+       MprVar          *vp;
+
+       mprAssert(ep);
+       mprAssert(var);
+
+       vp = mprGetProperty(&ep->variables[oType], var, 0);
+       if (vp == 0) {
+               return -1;
+       }
+       *value = *vp;
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Process the ESP page. docBuf holds the page already. We expect that
+ *     ep->variables holds all the pertinent environment variables.
+ */
+
+int espProcessRequest(EspRequest *ep, const char *docPath, char *docBuf, 
+                                         char **errMsg)
+{
+       char    *jsBuf;
+
+       mprAssert(ep);
+
+       ep->docPath = mprStrdup(docPath);
+
+       jsBuf = 0;
+       if (buildScript(ep, &jsBuf, docBuf, errMsg) < 0) {
+               return MPR_ERR_CANT_COMPLETE;
+       }
+
+       if (jsBuf) {
+               mprLog(7, "esp: script is:\n%s\n", jsBuf);
+
+               /*
+                *      Now evaluate the entire escript
+                *      MOB could cache the script 
+                */
+               if (ejsEvalScript(ep->eid, jsBuf, 0, errMsg) < 0) {
+                       return MPR_ERR_ABORTED;
+               }
+
+               mprFree(jsBuf);
+       }
+       return 0;
+}
+
+/******************************************************************************/
+
+void espRedirect(EspRequest *ep, int code, char *url)
+{
+       mprAssert(ep);
+       mprAssert(url);
+
+       ep->esp->redirect(ep->requestHandle, code, url);
+}
+
+/******************************************************************************/
+
+void espError(EspRequest *ep, const char *fmt, ...)
+{
+       va_list         args;
+       char            *buf;
+
+       mprAssert(ep);
+       mprAssert(fmt);
+
+       va_start(args, fmt);
+       mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args);
+       ejsSetErrorMsg(ep->eid, buf);
+       mprFree(buf);
+       va_end(args);
+}
+
+/******************************************************************************/
+
+void espSetHeader(EspRequest *ep, char *header, bool allowMultiple)
+{
+       mprAssert(ep);
+
+       ep->esp->setHeader(ep->requestHandle, header, allowMultiple);
+}
+
+/******************************************************************************/
+/*
+ *     Caller does not need to destroy the var
+ */
+
+MprVar *espGetResult(EspRequest *ep)
+{
+       mprAssert(ep);
+
+       return ejsGetReturnValue(ep->eid);
+}
+
+/******************************************************************************/
+
+void espSetReturn(EspRequest *ep, MprVar value)
+{
+       mprAssert(ep);
+
+       ejsSetReturnValue(ep->eid, value);
+}
+
+/******************************************************************************/
+
+void espSetReturnString(EspRequest *ep, char *str)
+{
+       mprAssert(ep);
+
+       ejsSetReturnValue(ep->eid, mprCreateStringVar(str, 0));
+}
+
+/******************************************************************************/
+
+void espSetResponseCode(EspRequest *ep, int code)
+{
+       mprAssert(ep);
+
+       ep->esp->setResponseCode(ep->requestHandle, code);
+}
+
+/******************************************************************************/
+
+void espSetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar value)
+{
+       mprCreatePropertyValue(&ep->variables[oType], var, value);
+}
+
+/******************************************************************************/
+
+void espSetStringVar(EspRequest *ep, EspEnvType oType, 
+                                        const char *var, const char *value)
+{
+       /*
+        *      Will create or update if already existing
+        */
+       mprCreatePropertyValue(&ep->variables[oType], var, 
+               mprCreateStringVar(value, 0));
+}
+
+/******************************************************************************/
+
+int espUnsetVar(EspRequest *ep, EspEnvType oType, char *var)
+{
+       return mprDeleteProperty(&ep->variables[oType], var);
+}
+
+/******************************************************************************/
+
+int espWrite(EspRequest *ep, char *buf, int size)
+{
+       mprAssert(ep);
+       mprAssert(buf);
+       mprAssert(size >= 0);
+
+       return ep->esp->writeBlock(ep->requestHandle, buf, size);
+}
+
+/******************************************************************************/
+
+int espWriteString(EspRequest *ep, char *buf)
+{
+       mprAssert(ep);
+       mprAssert(buf);
+
+       return ep->esp->writeBlock(ep->requestHandle, buf, strlen(buf));
+}
+
+/******************************************************************************/
+
+int espWriteFmt(EspRequest *ep, char *fmt, ...)
+{
+       va_list         args;
+       char            *buf;
+       int                     rc, len;
+
+       mprAssert(ep);
+       mprAssert(fmt);
+
+       va_start(args, fmt);
+       len = mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args);
+       rc = ep->esp->writeBlock(ep->requestHandle, buf, len);
+       mprFree(buf);
+       va_end(args);
+       return rc;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/*
+ *     Get a javascript identifier. Must allow x.y['abc'] or x.y["abc"].
+ *     Must be careful about quoting and only allow quotes inside []. 
+ */
+
+static int getIdentifier(EspParse *parse)
+{
+       int             atQuote, prevC, c;
+
+       mprAssert(parse);
+
+       atQuote = 0;
+       prevC = 0;
+       c = *parse->inp++;
+
+       while (isalnum(c) || c == '_' || c == '.' || c == '[' || 
+                       c == ']' || c == '\'' || c == '\"') {
+               if (c == '\'' || c == '\"') {
+                       if (c == atQuote) {
+                               atQuote = 0;
+                       } else if (prevC == '[') {
+                               atQuote = c;
+                       } else {
+                               break;
+                       }
+               }
+               if (parse->tokp >= parse->endp) {
+                       parse->token = (char*) mprRealloc(parse->token, 
+                               parse->tokLen + ESP_TOK_INCR);
+                       if (parse->token == 0) {
+                               return MPR_ERR_CANT_ALLOCATE;
+                       }
+                       parse->token[parse->tokLen] = '\0';
+                       parse->tokLen += ESP_TOK_INCR;
+                       parse->endp = &parse->token[parse->tokLen - 1];
+               }
+               *parse->tokp++ = c;
+               prevC = c;
+               c = *parse->inp++;
+       }
+
+       parse->inp--;
+       *parse->tokp = '\0';
+
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     Get the next ESP input token. input points to the next input token.
+ *     parse->token will hold the parsed token. The function returns the token id.
+ */
+
+static int getEspToken(int state, EspParse *parse)
+{
+       char    *cp;
+       int             tid, done, c, quoted;
+
+       tid = ESP_TOK_LITERAL;
+       parse->tokp = parse->token;
+       parse->tokp[0] = '\0';
+       quoted = 0;
+
+       c = *parse->inp++;
+       for (done = 0; !done; c = *parse->inp++) {
+
+               /*
+                *      Get room for more characters in the token buffer
+                */
+               if (parse->tokp >= parse->endp) {
+                       parse->token = (char*) mprRealloc(parse->token, 
+                               parse->tokLen + ESP_TOK_INCR);
+                       if (parse->token == 0) {
+                               return ESP_TOK_ERR;
+                       }
+                       parse->token[parse->tokLen] = '\0';\r
+                       parse->tokp = &parse->token[parse->tokLen - 1];
+                       parse->tokLen += ESP_TOK_INCR;
+                       parse->endp = &parse->token[parse->tokLen - 1];
+               }
+
+               switch (c) {
+               case 0:
+                       if (*parse->token) {
+                               done++;
+                               parse->inp--;
+                               break;
+                       }
+                       return ESP_TOK_EOF;
+
+               default:
+                       if (c == '\"' && state != ESP_STATE_IN_ESP_TAG) {
+                               *parse->tokp++ = '\\';
+                       }
+                       *parse->tokp++ = c;
+                       quoted = 0;
+                       break;
+
+               case '\\':
+                       quoted = 1;
+                       *parse->tokp++ = c;
+                       break;
+
+               case '@':
+                       if (*parse->inp == '@' && state != ESP_STATE_IN_ESP_TAG) {
+                               if (quoted) {
+                                       parse->tokp--;
+                                       quoted = 0;
+                               } else {
+                                       if (*parse->token) {
+                                               parse->inp--;
+                                       } else {
+                                               parse->inp++;
+                                               tid = ESP_TOK_ATAT;
+                                               if (getIdentifier(parse) < 0) {
+                                                       return ESP_TOK_ERR;
+                                               }
+                                       }
+                                       done++;
+                                       break;
+                               }
+                       }
+                       *parse->tokp++ = c;
+                       break;
+
+               case '<':
+                       if (*parse->inp == '%' && state != ESP_STATE_IN_ESP_TAG) {
+                               if (quoted) {
+                                       parse->tokp--;
+                                       quoted = 0;
+                                       *parse->tokp++ = c;
+                                       break;
+                               }
+                               if (*parse->token) {
+                                       parse->inp--;
+                                       done++;
+                                       break;
+                               }
+                               parse->inp++;
+                               while (isspace((int) *parse->inp)) {
+                                       parse->inp++;
+                               }
+                               if (*parse->inp == '=') {
+                                       parse->inp++;
+                                       while (isspace((int) *parse->inp)) {
+                                               parse->inp++;
+                                       }
+                                       tid = ESP_TOK_EQUALS;
+                                       if (getIdentifier(parse) < 0) {
+                                               return ESP_TOK_ERR;
+                                       }
+                                       done++;
+                                       break;
+                               }
+                               if (*parse->inp == 'i' && 
+                                               strncmp(parse->inp, "include", 7) == 0 &&
+                                               isspace((int) parse->inp[7])) {
+                                       tid = ESP_TOK_INCLUDE;
+                                       parse->inp += 7;
+                                       while (isspace((int) *parse->inp)) {
+                                               parse->inp++;
+                                       }
+                                       while (*parse->inp && !isspace((int) *parse->inp) && 
+                                                       *parse->inp != '%' && parse->tokp < parse->endp) {
+                                               *parse->tokp++ = *parse->inp++;
+                                       }
+                                       *parse->tokp = '\0';
+                                       if (parse->token[0] == '"') {
+                                               parse->tokp = parse->token;
+                                               for (cp = &parse->token[1]; *cp; ) {
+                                                       *parse->tokp++ = *cp++;
+                                               }
+                                               if (cp[-1] == '"') {
+                                                       parse->tokp--;
+                                               }
+                                               *parse->tokp = '\0';
+                                       }
+                                       
+                               } else {
+                                       tid = ESP_TOK_START_ESP;
+                               }
+                               done++;
+                               break;
+                       }
+                       *parse->tokp++ = c;
+                       break;
+
+               case '%':
+                       if (*parse->inp == '>' && state == ESP_STATE_IN_ESP_TAG) {
+                               if (quoted) {
+                                       parse->tokp--;
+                                       quoted = 0;
+                               } else {
+                                       if (*parse->token) {
+                                               parse->inp--;
+                                       } else {
+                                               tid = ESP_TOK_END_ESP;
+                                               parse->inp++;
+                                       }
+                                       done++;
+                                       break;
+                               }
+                       }
+                       *parse->tokp++ = c;
+                       break;
+               }
+       }
+
+       *parse->tokp = '\0';
+       parse->inp--;
+       return tid;
+}
+
+/******************************************************************************/
+/*
+ *     Convert an ESP page into a JavaScript. We also expand include files.
+ */
+
+static int buildScript(EspRequest *ep, char **jsBuf, char *input, char **errMsg)
+{
+       EspParse        parse;
+       char            path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME], incPath[MPR_MAX_FNAME];
+       char            *incBuf, *incText;
+       int                     state, tid, len, rc, maxScriptSize, incSize;
+
+       mprAssert(ep);
+       mprAssert(jsBuf);
+       mprAssert(input);
+
+       rc = 0;
+       len = 0;
+       state = ESP_STATE_BEGIN;
+       if (errMsg) {
+               *errMsg = 0;
+       }
+
+       memset(&parse, 0, sizeof(parse));
+       parse.token = (char*) mprMalloc(ESP_TOK_INCR);
+       if (parse.token == 0) {
+               return MPR_ERR_CANT_ALLOCATE;
+       }
+       parse.token[0] = '\0';
+       parse.tokLen = ESP_TOK_INCR;
+       parse.endp = &parse.token[parse.tokLen - 1];
+       parse.tokp = parse.token;
+       parse.inBuf = input;
+       parse.inp = parse.inBuf;
+
+       maxScriptSize = esp->maxScriptSize;
+
+       tid = getEspToken(state, &parse);
+       while (tid != ESP_TOK_EOF && len >= 0) {
+
+               switch (tid) {
+               default:
+               case ESP_TOK_ERR:
+                       mprFree(parse.token);
+                       return MPR_ERR_BAD_SYNTAX;
+                       
+               case ESP_TOK_LITERAL:
+                       len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, 
+                               "write(\"", parse.token, "\");\n", 0);
+                       break;
+
+               case ESP_TOK_ATAT:
+                       /*
+                        *      Trick to get undefined variables to evaluate to "".
+                        *      Catenate with "" to cause toString to run. 
+                        */
+                       len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, 
+                               "write(\"\" + ", parse.token, ");\n", 0);
+                       break;
+
+               case ESP_TOK_EQUALS:
+                       len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, 
+                               "write(\"\" + ", parse.token, ");\n", 0);
+                       state = ESP_STATE_IN_ESP_TAG;
+                       break;
+
+               case ESP_TOK_START_ESP:
+                       state = ESP_STATE_IN_ESP_TAG;
+                       tid = getEspToken(state, &parse);
+                       while (tid != ESP_TOK_EOF && tid != ESP_TOK_EOF && 
+                                       tid != ESP_TOK_END_ESP && len >= 0) {
+                               len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, 
+                                       parse.token, 0);
+                               tid = getEspToken(state, &parse);
+                       }
+                       state = ESP_STATE_BEGIN;
+                       break;
+
+               case ESP_TOK_END_ESP:
+                       state = ESP_STATE_BEGIN;
+                       break;
+
+               case ESP_TOK_INCLUDE:\r
+                       if (parse.token[0] == '/') {\r
+                               mprStrcpy(incPath, sizeof(incPath), parse.token);\r
+                       } else {\r
+                               mprGetDirName(dir, sizeof(dir), ep->uri);\r
+                               mprSprintf(incPath, sizeof(incPath), "%s/%s", \r
+                                       dir, parse.token);\r
+                       }
+                       if (esp->mapToStorage(ep->requestHandle, path, sizeof(path),
+                                       incPath, 0) < 0) {
+                               mprAllocSprintf(errMsg, MPR_MAX_STRING, 
+                                       "Can't find include file: %s", path);
+                               rc = MPR_ERR_CANT_OPEN;
+                               break;
+                       }
+                       if (esp->readFile(ep->requestHandle, &incText, &incSize, path) < 0){
+                               mprAllocSprintf(errMsg, MPR_MAX_STRING, 
+                                       "Can't read include file: %s", path);
+                               rc = MPR_ERR_CANT_READ;
+                               break;
+                       }
+                       incText[incSize] = '\0';
+
+                       /*
+                        *      Recurse and process the include script
+                        */
+                       incBuf = 0;
+                       if ((rc = buildScript(ep, &incBuf, incText, errMsg)) < 0) {
+                               mprFree(incText);\r
+                               mprFree(parse.token);\r
+                               return rc;
+                       }\r
+\r
+                       len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, incBuf, 0);
+                       mprFree(incText);
+                       mprFree(incBuf);
+                       state = ESP_STATE_IN_ESP_TAG;
+                       break;
+               }
+               tid = getEspToken(state, &parse);
+       }
+       mprFree(parse.token);\r
+       if (len < 0) {\r
+               mprAllocSprintf(errMsg, MPR_MAX_STRING, \r
+                       "Script token is too big in %s.\nConfigured maximum is %d.", \r
+                       path, maxScriptSize);\r
+               return MPR_ERR_WONT_FIT;\r
+       }
+       return rc;
+}
+
+/******************************************************************************/
+/******************************* Wrapped Routines *****************************/
+/******************************************************************************/
+
+int espCopyVar(EspRequest *ep, char *var, MprVar *value, int copyDepth)
+{
+       return ejsCopyVar(ep->eid, var, value, copyDepth);
+}
+
+/******************************************************************************/
+
+MprVar espCreateObjVar(char *name, int hashSize)
+{
+       return ejsCreateObj(name, hashSize);
+}
+
+/******************************************************************************/
+
+MprVar espCreateArrayVar(char *name, int size)
+{
+       return ejsCreateArray(name, size);
+}
+
+/******************************************************************************/
+
+bool espDestroyVar(MprVar *obj)
+{
+       return ejsDestroyVar(obj);
+}
+
+/******************************************************************************/
+
+MprVar *espCreateProperty(MprVar *obj, char *property, MprVar *newValue)
+{
+       return mprCreateProperty(obj, property, newValue);
+}
+
+/******************************************************************************/
+
+MprVar *espCreatePropertyValue(MprVar *obj, char *property, MprVar newValue)
+{
+       return mprCreatePropertyValue(obj, property, newValue);
+}
+
+/******************************************************************************/
+
+void espDefineFunction(EspRequest *ep, char *functionName, char *args, char *body)
+{
+       ejsDefineFunction(ep->eid, functionName, args, body);
+}
+
+/******************************************************************************/
+
+int espDeleteProperty(MprVar *obj, char *property)
+{
+       return mprDeleteProperty(obj, property);
+}
+
+/******************************************************************************/
+
+int espDeleteVar(EspRequest *ep, char *var)
+{
+       return ejsDeleteVar(ep->eid, var);
+}
+
+/******************************************************************************/
+int espEvalFile(EspRequest *ep, char *path, MprVar *result, char **emsg)
+{
+       return ejsEvalFile(ep->eid, path, result, emsg);
+}
+
+/******************************************************************************/
+
+int espEvalScript(EspRequest *ep, char *script, MprVar *result, char **emsg)
+{
+       return ejsEvalScript(ep->eid, script, result, emsg);
+}
+
+/******************************************************************************/
+
+int espGetPropertyCount(MprVar *obj, int includeFlags)
+{
+       if (obj->type != MPR_TYPE_OBJECT) {
+               return MPR_ERR_BAD_STATE;
+       }
+       return mprGetPropertyCount(obj, includeFlags);
+}
+
+/******************************************************************************/
+
+MprVar *espGetFirstProperty(MprVar *obj, int includeFlags)
+{
+       return mprGetFirstProperty(obj, includeFlags);
+}
+
+/******************************************************************************/
+
+MprVar *espGetGlobalObject(EspRequest *ep)
+{
+       return ejsGetGlobalObject(ep->eid);
+}
+
+/******************************************************************************/
+
+MprVar *espGetLocalObject(EspRequest *ep)
+{
+       return ejsGetLocalObject(ep->eid);
+}
+
+/******************************************************************************/
+
+MprVar *espGetNextProperty(MprVar *obj, MprVar *currentProperty, 
+       int includeFlags)
+{
+       return mprGetNextProperty(obj, currentProperty, includeFlags);
+}
+
+/******************************************************************************/
+
+MprVar *espGetProperty(MprVar *obj, char *property, MprVar *value)
+{
+       return mprGetProperty(obj, property, value);
+}
+
+/******************************************************************************/
+
+void *espGetThisPtr(EspRequest *ep)
+{
+       return ejsGetThisPtr(ep->eid);
+}
+
+/******************************************************************************/
+#if UNUSED
+int espReadProperty(MprVar *dest, MprVar *prop)
+{
+       mprAssert(prop);
+       mprAssert(dest);
+
+       *dest = *prop;
+       return 0;
+}
+
+#endif
+/******************************************************************************/
+
+int espReadVar(EspRequest *ep, char *var, MprVar *value)
+{
+       return ejsReadVar(ep->eid, var, value);
+}
+
+/******************************************************************************/
+
+int espRunFunction(EspRequest *ep, MprVar *obj, char *functionName, 
+       MprArray *args)
+{
+       return ejsRunFunction(ep->eid, obj, functionName, args);
+}
+
+/******************************************************************************/
+
+MprVar *espSetProperty(MprVar *obj, char *property, MprVar *newValue)
+{
+       return mprSetProperty(obj, property, newValue);
+}
+
+/******************************************************************************/
+
+MprVar *espSetPropertyValue(MprVar *obj, char *property, MprVar newValue)
+{
+       return mprSetPropertyValue(obj, property, newValue);
+}
+
+/******************************************************************************/
+
+int espWriteVar(EspRequest *ep, char *var, MprVar *value)
+{
+       return ejsWriteVar(ep->eid, var, value);
+}
+
+/******************************************************************************/
+
+int espWriteVarValue(EspRequest *ep, char *var, MprVar value)
+{
+       return ejsWriteVarValue(ep->eid, var, value);
+}
+
+/******************************************************************************/
+#if UNUSED
+
+int espWriteProperty(MprVar *prop, MprVar *newValue)
+{
+       return mprWriteProperty(prop, newValue);
+}
+
+/******************************************************************************/
+
+int espWritePropertyValue(MprVar *prop, MprVar newValue)
+{
+       return mprWritePropertyValue(prop, newValue);
+}
+
+#endif
+/******************************************************************************/
+
+#else  /* !BLD_FEATURE_ESP_MODULE */
+void espDummy() {}
+
+/******************************************************************************/
+#endif /* BLD_FEATURE_ESP_MODULE */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim:tw=78
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/source4/web_server/esp/esp.h b/source4/web_server/esp/esp.h
new file mode 100644 (file)
index 0000000..33ab9d7
--- /dev/null
@@ -0,0 +1,279 @@
+/**
+ *     @file   esp.h
+ *     @brief  Header for Embedded Server Pages (ESP)
+ */
+/********************************* Copyright **********************************/
+/*
+ *     @copy   default
+ *     
+ *     Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
+ *     
+ *     This software is distributed under commercial and open source licenses.
+ *     You may use the GPL open source license described below or you may acquire 
+ *     a commercial license from Mbedthis Software. You agree to be fully bound 
+ *     by the terms of either license. Consult the LICENSE.TXT distributed with 
+ *     this software for full details.
+ *     
+ *     This software is open source; you can redistribute it and/or modify it 
+ *     under the terms of the GNU General Public License as published by the 
+ *     Free Software Foundation; either version 2 of the License, or (at your 
+ *     option) any later version. See the GNU General Public License for more 
+ *     details at: http://www.mbedthis.com/downloads/gplLicense.html
+ *     
+ *     This program is distributed WITHOUT ANY WARRANTY; without even the 
+ *     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ *     
+ *     This GPL license does NOT permit incorporating this software into 
+ *     proprietary programs. If you are unable to comply with the GPL, you must
+ *     acquire a commercial license to use this software. Commercial licenses 
+ *     for this software and support services are available from Mbedthis 
+ *     Software at http://www.mbedthis.com 
+ *     
+ *     @end
+ */
+/********************************** Includes **********************************/
+
+#ifndef _h_ESP_h
+#define _h_ESP_h 1
+
+#include       "web_server/ejs/ejs.h"
+#include       "web_server/esp/espEnv.h"
+#include       "web_server/ejs/var.h"
+#include       "web_server/ejs/miniMpr.h"
+
+/*********************************** Defines **********************************/
+
+#define ESP_STRING_ARGS                MPR_TYPE_STRING_ARGS
+
+#if BLD_FEATURE_SQUEEZE
+#define ESP_TOK_INCR                   1024
+#define ESP_MAX_HEADER                         1024
+#else
+#define ESP_TOK_INCR                   4096
+#define ESP_MAX_HEADER                         4096
+#endif
+
+/*
+ *     ESP lexical analyser tokens
+ */
+#define ESP_TOK_ERR                            -1                      /* Any input error */
+#define ESP_TOK_EOF                            0                       /* End of file */
+#define ESP_TOK_START_ESP              1                       /* <% */
+#define ESP_TOK_END_ESP                        2                       /* %> */
+#define ESP_TOK_ATAT                   3                       /* @@var */
+#define ESP_TOK_LITERAL                        4                       /* literal HTML */
+#define ESP_TOK_INCLUDE                        5                       /* include file.esp */
+#define ESP_TOK_EQUALS                 6                       /* = var */
+
+/*
+ *     ESP parser states
+ */
+#define ESP_STATE_BEGIN                        1                       /* Starting state */
+#define ESP_STATE_IN_ESP_TAG   2                       /* Inside a <% %> group */
+
+/*********************************** Types ************************************/
+
+typedef void* EspHandle;                                       /* Opaque Web server handle type */
+
+/*
+ *     Per request control block
+ */
+typedef struct EspRequest {
+       MprStr          docPath;                                        /* Physical path for ESP page */        
+       EjsId           eid;                                            /* EJS instance handle */
+       struct Esp      *esp;                                           /* Pointer to ESP control block */
+       EspHandle       requestHandle;                          /* Per request web server handle */
+       MprStr          uri;                                            /* Request URI */               
+       MprVar          *variables;                                     /* Pointer to variables */
+} EspRequest;
+
+/*
+ *     Master ESP control block. This defines the function callbacks for a 
+ *     web server handler to implement. ESP will call these functions as
+ *     required.
+ */
+typedef struct Esp {
+       int             maxScriptSize;
+       void    (*createSession)(EspHandle handle, int timeout);
+       void    (*destroySession)(EspHandle handle);
+       char    *(*getSessionId)(EspHandle handle);
+       int             (*mapToStorage)(EspHandle handle, char *path, int len, char *uri,
+                               int flags);
+       int             (*readFile)(EspHandle handle, char **buf, int *len, char *path);
+       void    (*redirect)(EspHandle handle, int code, char *url);
+       void    (*setCookie)(EspHandle handle, char *name, char *value, 
+                               int lifetime, char *path, bool secure);
+       void    (*setHeader)(EspHandle handle, const char *value, bool allowMultiple);
+       void    (*setResponseCode)(EspHandle handle, int code);
+       int             (*writeBlock)(EspHandle handle, char *buf, int size);
+       int             (*writeFmt)(EspHandle handle, char *fmt, ...);
+#if BLD_FEATURE_MULTITHREAD
+       void    (*lock)(void *lockData);
+       void    (*unlock)(void *lockData);
+       void    *lockData;
+#endif
+} Esp;
+
+
+/*
+ *     ESP parse context
+ */
+typedef struct {
+       char    *inBuf;                                 /* Input data to parse */
+       char    *inp;                                   /* Next character for input */
+       char    *endp;                                  /* End of storage (allow for null) */
+       char    *tokp;                                  /* Pointer to current parsed token */
+       char    *token;                                 /* Storage buffer for token */
+       int             tokLen;                                 /* Length of buffer */
+} EspParse;
+
+
+/******************************** Private APIs ********************************/
+
+extern void                    espRegisterProcs(void);
+
+/******************************** Published API *******************************/
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/*
+ *     Function callback signatures
+ */
+typedef int            (*EspCFunction)(EspRequest *ep, int argc, 
+                                               struct MprVar **argv);
+typedef int            (*EspStringCFunction)(EspRequest *ep, int argc, 
+                                               char **argv);
+
+/*
+ *     APIs for those hosting the ESP module
+ */
+extern int                     espOpen(Esp *control);
+extern void                    espClose(void);
+extern EspRequest      *espCreateRequest(EspHandle webServerRequestHandle, 
+                                               char *uri, MprVar *envObj);
+extern void                    espDestroyRequest(EspRequest *ep);
+extern int                     espProcessRequest(EspRequest *ep, const char *docPath, 
+                                               char *docBuf, char **errMsg);
+
+/*
+ *     Method invocation
+ */
+extern void                    espDefineCFunction(EspRequest *ep, char *functionName, 
+                                               EspCFunction fn, void *thisPtr);
+extern void            espDefineFunction(EspRequest *ep, char *functionName, 
+                                               char *args, char *body);
+extern void                    espDefineStringCFunction(EspRequest *ep, 
+                                               const char *functionName, EspStringCFunction fn, 
+                                               void *thisPtr);
+extern int                     espRunFunction(EspRequest *ep, MprVar *obj, 
+                                               char *functionName, MprArray *args);
+extern void                    espSetResponseCode(EspRequest *ep, int code);
+extern void                    espSetReturn(EspRequest *ep, MprVar value);
+extern void            *espGetThisPtr(EspRequest *ep);
+
+/*
+ *     Utility routines to use in C methods
+ */
+extern void                    espError(EspRequest *ep, const char *fmt, ...);
+extern int                     espEvalFile(EspRequest *ep, char *path, MprVar *result, 
+                                               char **emsg);
+extern int                     espEvalScript(EspRequest *ep, char *script, MprVar *result, 
+                                               char **emsg);
+extern MprVar          *espGetLocalObject(EspRequest *ep);
+extern MprVar          *espGetGlobalObject(EspRequest *ep);
+extern EspHandle       espGetRequestHandle(EspRequest *ep);
+extern MprVar          *espGetResult(EspRequest *ep);
+extern EjsId           espGetScriptHandle(EspRequest *ep);
+extern void                    espRedirect(EspRequest *ep, int code, char *url);
+extern void                    espSetHeader(EspRequest *ep, char *header, 
+                                               bool allowMultiple);
+extern void                    espSetReturnString(EspRequest *ep, char *str);
+extern int                     espWrite(EspRequest *ep, char *buf, int size);
+extern int                     espWriteString(EspRequest *ep, char *buf);
+extern int                     espWriteFmt(EspRequest *ep, char *fmt, ...);
+
+/*
+ *     ESP array[] variable access (set will update/create)
+ */
+extern int                     espGetVar(EspRequest *ep, EspEnvType oType, char *var,
+                                               MprVar *value);
+extern char                    *espGetStringVar(EspRequest *ep, EspEnvType oType, 
+                                               char *var, char *defaultValue);
+extern void                    espSetVar(EspRequest *ep, EspEnvType oType, char *var, 
+                                               MprVar value);
+extern void                    espSetStringVar(EspRequest *ep, EspEnvType oType, 
+                                               const char *var, const char *value);
+extern int                     espUnsetVar(EspRequest *ep, EspEnvType oType, char *var);
+
+/*
+ *     Object creation and management
+ */
+extern MprVar          espCreateObjVar(char *name, int hashSize);
+extern MprVar          espCreateArrayVar(char *name, int size);
+extern bool                    espDestroyVar(MprVar *var);
+extern MprVar          *espCreateProperty(MprVar *obj, char *property, 
+                                               MprVar *newValue);
+extern MprVar          *espCreatePropertyValue(MprVar *obj, char *property, 
+                                               MprVar newValue);
+extern int                     espDeleteProperty(MprVar *obj, char *property);
+
+/*
+ *     JavaScript variable management. Set will create/update a property.
+ *     All return a property reference. GetProperty will optionally return the
+ *     property in value.
+ */
+extern MprVar          *espGetProperty(MprVar *obj, char *property, 
+                                               MprVar *value);
+extern MprVar          *espSetProperty(MprVar *obj, char *property, 
+                                               MprVar *newValue);
+extern MprVar          *espSetPropertyValue(MprVar *obj, char *property, 
+                                               MprVar newValue);
+
+#if 0
+/*
+ *     Low-level direct read and write of properties. 
+ *     FUTURE:  -- Read is not (dest, src). MUST WARN IN DOC ABOUT COPY/READ
+ *     Will still cause triggers to run.
+ */
+extern int                     espReadProperty(MprVar *dest, MprVar *prop);
+extern int                     espWriteProperty(MprVar *prop, MprVar *newValue);
+extern int                     espWritePropertyValue(MprVar *prop, MprVar newValue);
+#endif
+
+
+/* 
+ *     Access JavaScript variables by their full name. Can use "." or "[]". For
+ *     example: "global.request['REQUEST_URI']"
+ *     For Read/write, the variables must exist.
+ */
+extern int                     espCopyVar(EspRequest *ep, char *var, MprVar *value, 
+                                               int copyDepth);
+extern int                     espDeleteVar(EspRequest *ep, char *var);
+extern int                     espReadVar(EspRequest *ep, char *var, MprVar *value);
+extern int                     espWriteVar(EspRequest *ep, char *var, MprVar *value);
+extern int                     espWriteVarValue(EspRequest *ep, char *var, MprVar value);
+
+/*
+ *     Object property enumeration
+ */
+extern MprVar          *espGetFirstProperty(MprVar *obj, int includeFlags);
+extern MprVar          *espGetNextProperty(MprVar *obj, MprVar *currentProperty,
+                                               int includeFlags);
+extern int                     espGetPropertyCount(MprVar *obj, int includeFlags);
+
+#ifdef  __cplusplus
+}
+#endif
+/******************************************************************************/
+#endif /* _h_ESP_h */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim:tw=78
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/source4/web_server/esp/espEnv.h b/source4/web_server/esp/espEnv.h
new file mode 100644 (file)
index 0000000..a3c9d9f
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ *     @file   espEnv.h
+ *     @brief  ESP Environment Variables
+ */
+/********************************* Copyright **********************************/
+/*
+ *     @copy   default
+ *     
+ *     Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
+ *     
+ *     This software is distributed under commercial and open source licenses.
+ *     You may use the GPL open source license described below or you may acquire 
+ *     a commercial license from Mbedthis Software. You agree to be fully bound 
+ *     by the terms of either license. Consult the LICENSE.TXT distributed with 
+ *     this software for full details.
+ *     
+ *     This software is open source; you can redistribute it and/or modify it 
+ *     under the terms of the GNU General Public License as published by the 
+ *     Free Software Foundation; either version 2 of the License, or (at your 
+ *     option) any later version. See the GNU General Public License for more 
+ *     details at: http://www.mbedthis.com/downloads/gplLicense.html
+ *     
+ *     This program is distributed WITHOUT ANY WARRANTY; without even the 
+ *     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ *     
+ *     This GPL license does NOT permit incorporating this software into 
+ *     proprietary programs. If you are unable to comply with the GPL, you must
+ *     acquire a commercial license to use this software. Commercial licenses 
+ *     for this software and support services are available from Mbedthis 
+ *     Software at http://www.mbedthis.com 
+ *     
+ *     @end
+ */
+
+/******************************************************************************/
+
+#ifndef _h_ESP_ENV_h
+#define _h_ESP_ENV_h 1
+
+/*
+ *     @brief Scripting environment variable array types
+ */
+typedef enum EspEnvType {
+       ESP_UNDEFINED_OBJ = -1,
+
+       /**     
+        *      Elements for server[]: 
+        *              DOCUMENT_ROOT GATEWAY_INTERFACE SERVER_ADDR SERVER_PORT SERVER_NAME 
+        *              SERVER_PROTOCOL SERVER_SOFTWARE SERVER_URL UPLOAD_DIR
+        *              FUTURE: SERVER_ADMIN 
+        *              FUTURE: this could be shared across all hosts and be made read-only.
+        */
+       ESP_SERVER_OBJ = 0,                                             /*! server[] data */
+
+       /**
+        *      Elements for session[]: are user defined
+        */
+       ESP_SESSION_OBJ = 1,                                    /*! session[] data */
+
+       /**     
+        *      Elements for request[]: 
+        *              AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE QUERY_STRING PATH_INFO 
+        *              PATH_TRANSLATED REMOTE_ADDR REMOTE_HOST REMOTE_USER REQUEST_METHOD 
+        *              REQUEST_URI SCRIPT_FILENAME SCRIPT_NAME 
+        *              FUTURE: FILEPATH_INFO REDIRECT_URL SELF REMOTE_PORT AUTH_USER 
+        *              AUTH_GROUP AUTH_ACL 
+        */
+       ESP_REQUEST_OBJ = 2,                                    /*! request[] data */
+
+       /**     
+        *      Elements for headers[]: 
+        *              HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_CONNECTION HTTP_HOST 
+        *              HTTP_REFERER HTTP_USER_AGENT and any other custom headers
+        */
+       ESP_HEADERS_OBJ = 3,                                    /*! header [] data */
+
+       /**
+        *      Elements for cookies[]: are defined by the HTTP request
+        */
+       ESP_COOKIES_OBJ = 4,                                    /*! cookies[] data */
+       
+       /**
+        *      Elements for files[]: are defined by the HTTP request
+        *              CLIENT_FILENAME CONTENT_TYPE FILENAME SIZE
+        */
+       ESP_FILES_OBJ = 5,                                              /*! files[] data */
+
+       /**
+        *      Elements for form[]: are defined by the HTTP request
+        */
+       ESP_FORM_OBJ = 6,                                               /*! form[] data */
+
+       /**
+        *      Elements for application[]: are user defined
+        */
+       ESP_APPLICATION_OBJ = 7,                                /*! application[] data */
+
+       /**
+        *      Elements for global[]: are defined by ESP/EJS
+        */
+       ESP_GLOBAL_OBJ = 8,                                             /*! global [] data */
+
+       /*
+        *      Elements for local[]: are defined by ESP/EJS
+        */
+       ESP_LOCAL_OBJ = 9,                                              /*! local [] data */
+} EspEnvType;
+
+#define ESP_OBJ_MAX    10                                      /* Total objects */
+
+#if BLD_SQUEEZE
+#define ESP_HASH_SIZE  19                                      /* Size of hash tables */
+#else
+#define ESP_HASH_SIZE  37
+#endif
+
+/******************************************************************************/
+#endif /* _h_ESP_ENV_h */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim:tw=78
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/source4/web_server/esp/espProcs.c b/source4/web_server/esp/espProcs.c
new file mode 100644 (file)
index 0000000..a8da800
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *     @file   espProcs.c
+ *     @brief  Embedded Server Pages (ESP) Procedures.
+ *     @overview These ESP procedures can be used in ESP pages for common tasks.
+ */
+/********************************* Copyright **********************************/
+/*
+ *     @copy   default
+ *     
+ *     Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
+ *     
+ *     This software is distributed under commercial and open source licenses.
+ *     You may use the GPL open source license described below or you may acquire 
+ *     a commercial license from Mbedthis Software. You agree to be fully bound 
+ *     by the terms of either license. Consult the LICENSE.TXT distributed with 
+ *     this software for full details.
+ *     
+ *     This software is open source; you can redistribute it and/or modify it 
+ *     under the terms of the GNU General Public License as published by the 
+ *     Free Software Foundation; either version 2 of the License, or (at your 
+ *     option) any later version. See the GNU General Public License for more 
+ *     details at: http://www.mbedthis.com/downloads/gplLicense.html
+ *     
+ *     This program is distributed WITHOUT ANY WARRANTY; without even the 
+ *     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ *     
+ *     This GPL license does NOT permit incorporating this software into 
+ *     proprietary programs. If you are unable to comply with the GPL, you must
+ *     acquire a commercial license to use this software. Commercial licenses 
+ *     for this software and support services are available from Mbedthis 
+ *     Software at http://www.mbedthis.com 
+ *     
+ *     @end
+ */
+/********************************** Includes **********************************/
+
+#include       "esp.h"
+
+/************************************ Code ************************************/
+#if BLD_FEATURE_ESP_MODULE
+#if BLD_FEATURE_SESSION
+/*
+ *     destroySession
+ */
+
+static int destroySessionProc(EspRequest *ep, int argc, char **argv)
+{
+       ep->esp->destroySession(ep->requestHandle);
+       return 0;
+}
+
+#endif /* BLD_FEATURE_SESSION */
+
+/******************************************************************************/
+/*
+ *     include
+ *
+ *     This includes javascript libraries. For example:
+ *
+ *             <% include("file", ...); %> 
+ *
+ *     Don't confuse with ESP includes:
+ *
+ *             <% include file.esp %>
+ *
+ *     Filenames are relative to the base document including the file.
+ *     FUTURE -- move back to EJS. Only here now because we need ep->readFile.
+ */ 
+
+static int includeProc(EspRequest *ep, int argc, char **argv)
+{
+       Esp             *esp;
+       char    path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME];
+       char    *emsg, *buf;
+       int             size, i;
+
+       esp = ep->esp;
+       mprAssert(argv);
+       for (i = 0; i < argc; i++) {
+               mprGetDirName(dir, sizeof(dir), ep->docPath);
+               mprSprintf(path, sizeof(path), "%s/%s", dir, argv[i]);
+               
+               if (esp->readFile(ep->requestHandle, &buf, &size, path) < 0) {
+                       espError(ep, "Can't read include file: %s", path);
+                       return MPR_ERR_CANT_ACCESS;
+               }
+               buf[size] = '\0';
+
+               if (ejsEvalScript(espGetScriptHandle(ep), buf, 0, &emsg) < 0) {
+                       espError(ep, "Cant evaluate script");
+                       mprFree(buf);
+                       return -1;
+               }
+               mprFree(buf);
+       }
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     redirect
+ *
+ *     This implemements <% redirect(url, code); %> command. The redirection 
+ *     code is optional.
+ */ 
+
+static int redirectProc(EspRequest *ep, int argc, char **argv)
+{
+       char    *url;
+       int             code;
+
+       if (argc < 1) {
+               espError(ep, "Bad args");
+               return MPR_ERR_BAD_ARGS;
+       }
+       url = argv[0];
+       if (argc == 2) {
+               code = atoi(argv[1]);
+       } else {
+               code = 302;
+       }
+       espRedirect(ep, code, url);
+       return 0;
+}
+
+/******************************************************************************/
+#if BLD_FEATURE_SESSION
+/*
+ *     useSession
+ */
+
+static int useSessionProc(EspRequest *ep, int argc, char **argv)
+{
+       int                     timeout;
+
+       if (argc > 1) {
+               espError(ep, "Bad args");
+               return MPR_ERR_BAD_ARGS;
+
+       } else if (argc == 1) {
+               timeout = atoi(argv[0]);
+       } else {
+               timeout = 0;
+       }
+       
+       ep->esp->createSession(ep->requestHandle, timeout);
+       espSetReturnString(ep, ep->esp->getSessionId(ep->requestHandle));
+       return 0;
+}
+
+#endif /* BLD_FEATURE_SESSION */
+/******************************************************************************/
+/*
+ *     setHeader
+ *
+ *     This implemements <% setHeader("key: value", allowMultiple); %> command.
+ */ 
+
+static int setHeaderProc(EspRequest *ep, int argc, char **argv)
+{
+       mprAssert(argv);
+       if (argc != 2) {
+               espError(ep, "Bad args");
+               return MPR_ERR_BAD_ARGS;
+       }
+       ep->esp->setHeader(ep->requestHandle, argv[0], atoi(argv[1]));
+       return 0;
+}
+
+/******************************************************************************/
+/*
+ *     write
+ *
+ *     This implemements <% write("text"); %> command.
+ */ 
+
+static int writeProc(EspRequest *ep, int argc, char **argv)
+{
+       char    *s;
+       int             i, len;
+
+       mprAssert(argv);
+       for (i = 0; i < argc; i++) {
+               s = argv[i];
+               len = strlen(s);
+               if (len > 0) {
+                       if (espWrite(ep, s, len) != len) {
+                               espError(ep, "Can't write to client");
+                               return -1;
+                       }
+               }
+       }
+       return 0;
+}
+
+/******************************************************************************/
+
+void espRegisterProcs()
+{
+       espDefineStringCFunction(0, "write", writeProc, 0);
+       espDefineStringCFunction(0, "setHeader", setHeaderProc, 0);
+       espDefineStringCFunction(0, "redirect", redirectProc, 0);
+       espDefineStringCFunction(0, "include", includeProc, 0);
+
+#if BLD_FEATURE_SESSION
+       /*
+        *      Create and use are synonomous
+        */
+       espDefineStringCFunction(0, "useSession", useSessionProc, 0);
+       espDefineStringCFunction(0, "createSession", useSessionProc, 0);
+       espDefineStringCFunction(0, "destroySession", destroySessionProc, 0);
+#endif
+}
+
+/******************************************************************************/
+
+#else
+void mprEspControlsDummy() {}
+
+#endif /* BLD_FEATURE_ESP_MODULE */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim:tw=78
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/source4/web_server/http.c b/source4/web_server/http.c
new file mode 100644 (file)
index 0000000..ad7eaa8
--- /dev/null
@@ -0,0 +1,627 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   http handling code
+
+   Copyright (C) Andrew Tridgell 2005
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "web_server/web_server.h"
+#include "smbd/service_stream.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "system/iconv.h"
+#include "system/time.h"
+#include "web_server/esp/esp.h"
+
+/* state of the esp subsystem */
+struct esp_state {
+       struct websrv_context *web;
+       struct MprVar variables[ESP_OBJ_MAX];
+       struct EspRequest *req;
+};
+
+/* destroy a esp session */
+static int esp_destructor(void *ptr)
+{
+       struct esp_state *esp = talloc_get_type(ptr, struct esp_state);
+       if (esp->req) {
+               espDestroyRequest(esp->req);
+       }
+       espClose();
+       mprFreeAll();
+       return 0;
+}
+
+/*
+  output the http headers
+*/
+static void http_output_headers(struct websrv_context *web)
+{
+       int i;
+       char *s;
+       DATA_BLOB b;
+       const char *response_string = "Unknown Code";
+       const struct {
+               unsigned code;
+               const char *response_string;
+       } codes[] = {
+               { 200, "OK" },
+               { 301, "Moved" },
+               { 302, "Found" },
+               { 303, "Method" },
+               { 304, "Not Modified" },
+               { 400, "Bad request" },
+               { 401, "Unauthorized" },
+               { 403, "Forbidden" },
+               { 404, "Not Found" },
+               { 500, "Internal Server Error" },
+               { 501, "Not implemented" }
+       };
+       for (i=0;i<ARRAY_SIZE(codes);i++) {
+               if (codes[i].code == web->output.response_code) {
+                       response_string = codes[i].response_string;
+               }
+       }
+
+       if (web->output.headers == NULL) return;
+       s = talloc_asprintf(web, "HTTP/1.0 %u %s\r\n", 
+                           web->output.response_code, response_string);
+       if (s == NULL) return;
+       for (i=0;web->output.headers[i];i++) {
+               s = talloc_asprintf_append(s, "%s\r\n", web->output.headers[i]);
+       }
+       s = talloc_asprintf_append(s, "\r\n");
+       if (s == NULL) return;
+
+       b = web->output.content;
+       web->output.content.data = s;
+       web->output.content.length = strlen(s);
+       data_blob_append(web, &web->output.content, b.data, b.length);
+       data_blob_free(&b);
+}
+
+/*
+  called when esp wants to output something
+*/
+static int http_writeBlock(EspHandle handle, char *buf, int size)
+{
+       struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
+       NTSTATUS status;
+       status = data_blob_append(web, &web->output.content, buf, size);
+       if (!NT_STATUS_IS_OK(status)) return -1;
+       return size;
+}
+
+
+/*
+  set a http header
+*/
+static void http_setHeader(EspHandle handle, const char *value, bool allowMultiple)
+{
+       struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
+       char *p = strchr(value, ':');
+
+       if (p && !allowMultiple && web->output.headers) {
+               int i;
+               for (i=0;web->output.headers[i];i++) {
+                       if (strncmp(web->output.headers[i], value, (p+1)-value) == 0) {
+                               web->output.headers[i] = talloc_strdup(web, value);
+                               return;
+                       }
+               }
+       }
+
+       web->output.headers = str_list_add(web->output.headers, value);
+}
+
+/*
+  set a http response code
+*/
+static void http_setResponseCode(EspHandle handle, int code)
+{
+       struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
+       web->output.response_code = code;
+}
+
+/*
+  redirect to another web page
+ */
+static void http_redirect(EspHandle handle, int code, char *url)
+{
+       struct websrv_context *web = talloc_get_type(handle, struct websrv_context);
+       const char *host = web->input.host;
+       
+       /* form the full url, unless it already looks like a url */
+       if (strchr(url, ':') == NULL) {
+               if (host == NULL) {
+                       host = talloc_asprintf(web, "%s:%u",
+                                              socket_get_my_addr(web->conn->socket, web),
+                                              socket_get_my_port(web->conn->socket));
+               }
+               if (host == NULL) goto internal_error;
+               if (url[0] != '/') {
+                       char *p = strrchr(web->input.url, '/');
+                       if (p == web->input.url) {
+                               url = talloc_asprintf(web, "http://%s/%s", host, url);
+                       } else {
+                               int dirlen = p - web->input.url;
+                               url = talloc_asprintf(web, "http://%s%*.*s/%s",
+                                                     host, 
+                                                     dirlen, dirlen, web->input.url,
+                                                     url);
+                       }
+                       if (url == NULL) goto internal_error;
+               }
+       }
+
+       http_setHeader(handle, talloc_asprintf(web, "Location: %s", url), 0);
+
+       /* make sure we give a valid redirect code */
+       if (code >= 300 && code < 400) {
+               http_setResponseCode(handle, code);
+       } else {
+               http_setResponseCode(handle, 302);
+       }
+       return;
+
+internal_error:
+       http_error(web, 500, "Internal server error");
+}
+
+
+/* callbacks for esp processing */
+static const struct Esp esp_control = {
+       .maxScriptSize   = 60000,
+       .writeBlock      = http_writeBlock,
+       .setHeader       = http_setHeader,
+       .redirect        = http_redirect,
+       .setResponseCode = http_setResponseCode
+};
+
+
+/*
+  setup for a raw http level error
+*/
+void http_error(struct websrv_context *web, int code, const char *info)
+{
+       char *s;
+       s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %u</TITLE></HEAD><BODY><H1>Error %u</H1>%s<p></BODY></HTML>\r\n\r\n", 
+                           code, code, info);
+       if (s == NULL) {
+               stream_terminate_connection(web->conn, "http_error: out of memory");
+               return;
+       }
+       http_writeBlock(web, s, strlen(s));
+       http_setResponseCode(web, code);
+       http_output_headers(web);
+       EVENT_FD_NOT_READABLE(web->conn->event.fde);
+       EVENT_FD_WRITEABLE(web->conn->event.fde);
+}
+
+/*
+  map a unix error code to a http error
+*/
+void http_error_unix(struct websrv_context *web, const char *info)
+{
+       int code = 500;
+       switch (errno) {
+       case ENOENT:
+       case EISDIR:
+               code = 404;
+               break;
+       case EACCES:
+               code = 403;
+               break;
+       }
+       http_error(web, code, info);
+}
+
+/*
+  return the local path for a URL
+*/
+static const char *http_local_path(struct websrv_context *web, const char *url)
+{
+       int i;
+       char *path;
+
+       /* check that the url is OK */
+       if (url[0] != '/') return NULL;
+
+       for (i=0;url[i];i++) {
+               if ((!isalnum(url[i]) && !strchr("./", url[i])) ||
+                   (url[i] == '.' && strchr("/.", url[i+1]))) {
+                       return NULL;
+               }
+       }
+
+       path = talloc_asprintf(web, "%s/%s", lp_swat_directory(), url+1);
+       if (path == NULL) return NULL;
+
+       if (directory_exist(path)) {
+               path = talloc_asprintf_append(path, "/index.html");
+       }
+       return path;
+}
+
+
+/*
+  a simple file request
+*/
+static void http_simple_request(struct websrv_context *web)
+{
+       const char *url = web->input.url;
+       const char *path;
+       struct stat st;
+
+       path = http_local_path(web, url);
+       if (path == NULL) goto invalid;
+
+       /* looks ok */
+       web->output.fd = open(path, O_RDONLY);
+       if (web->output.fd == -1) {
+               http_error_unix(web, url);
+               return;
+       }
+
+       if (fstat(web->output.fd, &st) != 0 || !S_ISREG(st.st_mode)) {
+               close(web->output.fd);
+               goto invalid;
+       }
+
+       http_output_headers(web);
+       EVENT_FD_WRITEABLE(web->conn->event.fde);
+       return;
+
+invalid:
+       http_error(web, 400, "Malformed URL");
+}
+
+/*
+  setup the standard ESP arrays
+*/
+static void http_setup_arrays(struct esp_state *esp)
+{
+       struct websrv_context *web = esp->web;
+       struct EspRequest *req = esp->req;
+       char *p;
+
+       espSetStringVar(req, ESP_REQUEST_OBJ, "CONTENT_LENGTH", 
+                       talloc_asprintf(esp, "%u", web->input.content_length));
+       if (web->input.query_string) {
+               espSetStringVar(req, ESP_REQUEST_OBJ, "QUERY_STRING", 
+                               web->input.query_string);
+       }
+       espSetStringVar(req, ESP_REQUEST_OBJ, "REQUEST_METHOD", 
+                       web->input.post_request?"POST":"GET");
+       espSetStringVar(req, ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url);
+       p = strrchr(web->input.url, '/');
+       espSetStringVar(req, ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1);
+
+       if (web->input.referer) {
+               espSetStringVar(req, ESP_HEADERS_OBJ, "HTT_REFERER", web->input.referer);
+       }
+       if (web->input.user_agent) {
+               espSetStringVar(req, ESP_HEADERS_OBJ, "USER_AGENT", web->input.user_agent);
+       }
+
+       espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_ADDR", 
+                       socket_get_my_addr(web->conn->socket, esp));
+       espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_PORT", 
+                       talloc_asprintf(esp, "%u", socket_get_my_port(web->conn->socket)));
+       espSetStringVar(req, ESP_SERVER_OBJ, "SERVER_PROTOCOL", "http");
+}
+
+
+
+
+
+/*
+  process a esp request
+*/
+static void esp_request(struct esp_state *esp)
+{
+       struct websrv_context *web = esp->web;
+       const char *url = web->input.url;
+       char *buf;
+       const char *path;
+       struct stat st;
+       int fd, res;
+       char *emsg = NULL;
+
+       http_setup_arrays(esp);
+
+       path = http_local_path(web, url);
+       if (path == NULL) goto invalid;
+
+       espSetStringVar(esp->req, ESP_REQUEST_OBJ, "SCRIPT_FILENAME", path);
+
+       /* looks ok */
+       fd = open(path, O_RDONLY);
+       if (fd == -1) {
+               http_error_unix(web, url);
+               return;
+       }
+
+       if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) {
+               close(fd);
+               goto invalid;
+       }
+
+       buf = talloc_size(esp, st.st_size+1);
+       if (buf == NULL) goto invalid;
+
+       if (read(fd, buf, st.st_size) != st.st_size) {
+               goto invalid;
+       }
+       buf[st.st_size] = 0;
+       close(fd);
+
+       res = espProcessRequest(esp->req, path, buf, &emsg);
+       if (res != 0 && emsg) {
+               http_writeBlock(esp, emsg, strlen(emsg));
+       }
+       http_output_headers(web);
+       EVENT_FD_WRITEABLE(web->conn->event.fde);
+       return;
+
+invalid:
+       http_error(web, 400, "Malformed URL");
+}
+
+
+/* 
+   handling of + and % escapes in http variables 
+*/
+static const char *http_unescape(TALLOC_CTX *mem_ctx, const char *p)
+{
+       char *s0 = talloc_strdup(mem_ctx, p);
+       char *s = s0;
+       if (s == NULL) return NULL;
+
+       while (*s) {
+               unsigned v;
+               if (*s == '+') *s = ' ';
+               if (*s == '%' && sscanf(s+1, "%02x", &v) == 1) {
+                       *s = (char)v;
+                       memmove(s+1, s+3, strlen(s+3)+1);
+               }
+               s++;
+       }
+
+       return s0;
+}
+
+/*
+  set a form or GET variable
+*/
+static void esp_putvar(struct esp_state *esp, const char *var, const char *value)
+{
+       espSetStringVar(esp->req, ESP_FORM_OBJ, 
+                       http_unescape(esp, var),
+                       http_unescape(esp, value));
+}
+
+
+/*
+  parse the variables in a POST style request
+*/
+static NTSTATUS http_parse_post(struct esp_state *esp)
+{
+       DATA_BLOB b = esp->web->input.partial;
+
+       while (b.length) {
+               char *p, *line;
+               size_t len;
+
+               p = memchr(b.data, '&', b.length);
+               if (p == NULL) {
+                       len = b.length;
+               } else {
+                       len = p - (char *)b.data;
+               }
+               line = talloc_strndup(esp, b.data, len);
+               NT_STATUS_HAVE_NO_MEMORY(line);
+                                    
+               p = strchr(line,'=');
+               if (p) {
+                       *p = 0;
+                       esp_putvar(esp, line, p+1);
+               }
+               talloc_free(line);
+               b.length -= len;
+               b.data += len;
+               if (b.length > 0) {
+                       b.length--;
+                       b.data++;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  parse the variables in a GET style request
+*/
+static NTSTATUS http_parse_get(struct esp_state *esp)
+{
+       struct websrv_context *web = esp->web;
+       char *p, *s, *tok;
+       char *pp;
+
+       p = strchr(web->input.url, '?');
+       web->input.query_string = p+1;
+       *p = 0;
+
+       s = talloc_strdup(esp, esp->web->input.query_string);
+       NT_STATUS_HAVE_NO_MEMORY(s);
+
+       for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) {
+               p = strchr(tok,'=');
+               if (p) {
+                       *p = 0;
+                       esp_putvar(esp, tok, p+1);
+               }
+       }
+       return NT_STATUS_OK;
+}
+
+/*
+  setup some standard variables
+*/
+static void http_setup_vars(struct esp_state *esp)
+{
+       int i;
+
+       for (i = 0; i < ESP_OBJ_MAX; i++) {
+               esp->variables[i] = mprCreateUndefinedVar();
+       }
+       esp->variables[ESP_HEADERS_OBJ]     = mprCreateObjVar("headers", ESP_HASH_SIZE);
+       esp->variables[ESP_FORM_OBJ]        = mprCreateObjVar("form", ESP_HASH_SIZE);
+       esp->variables[ESP_APPLICATION_OBJ] = mprCreateObjVar("application", ESP_HASH_SIZE);
+       esp->variables[ESP_COOKIES_OBJ]     = mprCreateObjVar("cookies", ESP_HASH_SIZE);
+       esp->variables[ESP_FILES_OBJ]       = mprCreateObjVar("files", ESP_HASH_SIZE);
+       esp->variables[ESP_REQUEST_OBJ]     = mprCreateObjVar("request", ESP_HASH_SIZE);
+       esp->variables[ESP_SERVER_OBJ]      = mprCreateObjVar("server", ESP_HASH_SIZE);
+       esp->variables[ESP_SESSION_OBJ]     = mprCreateObjVar("session", ESP_HASH_SIZE);
+}
+
+/*
+  process a complete http request
+*/
+void http_process_input(struct websrv_context *web)
+{
+       NTSTATUS status;
+       struct esp_state *esp;
+       char *p;
+       int i;
+       const char *file_type = NULL;
+       const struct {
+               const char *extension;
+               const char *mime_type;
+       } mime_types[] = {
+               {"gif",  "image/gif"},
+               {"png",  "image/png"},
+               {"jpg",  "image/jpeg"},
+               {"txt",  "text/plain"}
+       };
+
+       esp = talloc_zero(web, struct esp_state);
+       if (esp == NULL) goto internal_error;
+
+       esp->web = web;
+
+       mprSetCtx(esp);
+
+       talloc_set_destructor(esp, esp_destructor);
+
+       if (espOpen(&esp_control) != 0) goto internal_error;
+
+       http_setup_vars(esp);
+       
+       esp->req = espCreateRequest(web, web->input.url, esp->variables);
+       if (esp->req == NULL) goto internal_error;
+
+       if (web->input.url == NULL) {
+               http_error(web, 400, "You must specify a GET or POST request");
+               return;
+       }
+
+       if (web->input.post_request) {
+               status = http_parse_post(esp);
+               if (!NT_STATUS_IS_OK(status)) {
+                       http_error(web, 400, "Malformed POST data");
+                       return;
+               }
+       } else if (strchr(web->input.url, '?')) {
+               status = http_parse_get(esp);
+               if (!NT_STATUS_IS_OK(status)) {
+                       http_error(web, 400, "Malformed GET data");
+                       return;
+               }
+       }
+
+       /* process all html files as ESP */
+       p = strrchr(web->input.url, '.');
+       for (i=0;p && i<ARRAY_SIZE(mime_types);i++) {
+               if (strcmp(mime_types[i].extension, p+1) == 0) {
+                       file_type = mime_types[i].mime_type;
+               }
+       }
+       if (file_type == NULL) {
+               file_type = "text/html";
+       }
+
+       /* setup basic headers */
+       http_setResponseCode(web, 200);
+       http_setHeader(web, talloc_asprintf(esp, "Date: %s", 
+                                           http_timestring(esp, time(NULL))), 0);
+       http_setHeader(web, "Server: Samba", 0);
+       http_setHeader(web, "Connection: close", 0);
+       http_setHeader(web, talloc_asprintf(esp, "Content-Type: %s", file_type), 0);
+
+       if (strcmp(file_type, "text/html") == 0) {
+               esp_request(esp);
+       } else {
+               http_simple_request(web);
+       }
+       talloc_free(esp);
+       return;
+       
+internal_error:
+       talloc_free(esp);
+       http_error(web, 500, "Internal server error");
+}
+
+
+/*
+  parse one line of header input
+*/
+NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
+{
+       if (line[0] == 0) {
+               web->input.end_of_headers = True;
+       } else if (strncasecmp(line,"GET ", 4)==0) {
+               web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
+       } else if (strncasecmp(line,"POST ", 5)==0) {
+               web->input.post_request = True;
+               web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
+       } else if (strchr(line, ':') == NULL) {
+               http_error(web, 400, "This server only accepts GET and POST requests");
+               return NT_STATUS_INVALID_PARAMETER;
+       } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
+               web->input.content_length = strtoul(&line[16], NULL, 10);
+       } else {
+#define PULL_HEADER(v, s) do { \
+       if (strncmp(line, s, strlen(s)) == 0) { \
+               web->input.v = talloc_strdup(web, &line[strlen(s)]); \
+               return NT_STATUS_OK; \
+       } \
+} while (0)
+               PULL_HEADER(content_type, "Content-Type: ");
+               PULL_HEADER(user_agent, "User-Agent: ");
+               PULL_HEADER(referer, "Referer: ");
+               PULL_HEADER(host, "Host: ");
+               PULL_HEADER(accept_encoding, "Accept-Encoding: ");
+       }
+
+       /* ignore all other headers for now */
+       return NT_STATUS_OK;
+}
+
+
diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c
new file mode 100644 (file)
index 0000000..9d161bd
--- /dev/null
@@ -0,0 +1,252 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   web server startup
+
+   Copyright (C) Andrew Tridgell 2005
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "web_server/web_server.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+
+/* don't allow connections to hang around forever */
+#define HTTP_TIMEOUT 30
+
+/*
+  destroy a web connection
+*/
+static int websrv_destructor(void *ptr)
+{
+       struct websrv_context *web = talloc_get_type(ptr, struct websrv_context);
+       if (web->output.fd != -1) {
+               close(web->output.fd);
+       }
+       return 0;
+}
+
+/*
+  called when a connection times out. This prevents a stuck connection
+  from hanging around forever
+*/
+static void websrv_timeout(struct event_context *event_context, 
+                          struct timed_event *te, 
+                          struct timeval t, void *private)
+{
+       struct websrv_context *web = talloc_get_type(private, struct websrv_context);
+       stream_terminate_connection(web->conn, "websrv_context: timeout");
+}
+
+/*
+  called when a web connection becomes readable
+*/
+static void websrv_recv(struct stream_connection *conn, uint16_t flags)
+{
+       struct websrv_context *web = talloc_get_type(conn->private, 
+                                                    struct websrv_context);
+       NTSTATUS status;
+       uint8_t buf[1024];
+       size_t nread;
+       uint8_t *p;
+       DATA_BLOB b;
+
+       /* not the most efficient http parser ever, but good enough for us */
+       status = socket_recv(conn->socket, buf, sizeof(buf), &nread, 0);
+       if (NT_STATUS_IS_ERR(status)) goto failed;
+       if (!NT_STATUS_IS_OK(status)) return;
+
+       status = data_blob_append(web, &web->input.partial, buf, nread);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+
+       /* parse any lines that are available */
+       b = web->input.partial;
+       while (!web->input.end_of_headers &&
+              (p=memchr(b.data, '\n', b.length))) {
+               const char *line = b.data;
+               *p = 0;
+               if (p != b.data && p[-1] == '\r') {
+                       p[-1] = 0;
+               }
+               status = http_parse_header(web, line);
+               if (!NT_STATUS_IS_OK(status)) return;
+               b.length -= (p - b.data) + 1;
+               b.data = p+1;
+       }
+
+       /* keep any remaining bytes in web->input.partial */
+       if (b.length == 0) {
+               b.data = NULL;
+       }
+       b = data_blob_talloc(web, b.data, b.length);
+       data_blob_free(&web->input.partial);
+       web->input.partial = b;
+
+       /* we finish when we have both the full headers (terminated by
+          a blank line) and any post data, as indicated by the
+          content_length */
+       if (web->input.end_of_headers &&
+           web->input.partial.length == web->input.content_length) {
+               EVENT_FD_NOT_READABLE(web->conn->event.fde);
+               http_process_input(web);
+       }
+       return;
+
+failed:
+       stream_terminate_connection(conn, "websrv_recv: failed\n");
+}
+
+
+/*
+  called when a web connection becomes writable
+*/
+static void websrv_send(struct stream_connection *conn, uint16_t flags)
+{
+       struct websrv_context *web = talloc_get_type(conn->private, 
+                                                    struct websrv_context);
+       NTSTATUS status;
+       size_t nsent;
+       DATA_BLOB b;
+
+       b = web->output.content;
+       b.data += web->output.nsent;
+       b.length -= web->output.nsent;
+
+       status = socket_send(conn->socket, &b, &nsent, 0);
+       if (NT_STATUS_IS_ERR(status)) {
+               stream_terminate_connection(web->conn, "socket_send: failed");
+               return;
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               return;
+       }
+
+       web->output.nsent += nsent;
+
+       /* possibly read some more raw data from a file */
+       if (web->output.content.length == web->output.nsent && 
+           web->output.fd != -1) {
+               uint8_t buf[2048];
+               ssize_t nread;
+
+               data_blob_free(&web->output.content);
+               web->output.nsent = 0;
+
+               nread = read(web->output.fd, buf, sizeof(buf));
+               if (nread == 0) {
+                       close(web->output.fd);
+                       web->output.fd = -1;
+               }
+               if (nread == -1 && errno == EINTR) {
+                       return;
+               }
+               web->output.content = data_blob_talloc(web, buf, nread);
+       }
+
+       if (web->output.content.length == web->output.nsent) {
+               stream_terminate_connection(web->conn, NULL);
+       }
+}
+
+/*
+  establish a new connection to the web server
+*/
+static void websrv_accept(struct stream_connection *conn)
+{
+       struct websrv_context *web;
+
+       web = talloc_zero(conn, struct websrv_context);
+       if (web == NULL) goto failed;
+
+       web->conn = conn;
+       conn->private = web;
+       web->output.fd = -1;
+       talloc_set_destructor(web, websrv_destructor);
+
+       event_add_timed(conn->event.ctx, web, 
+                       timeval_current_ofs(HTTP_TIMEOUT, 0),
+                       websrv_timeout, web);
+       return;
+
+failed:
+       talloc_free(conn);
+}
+
+
+static const struct stream_server_ops web_stream_ops = {
+       .name                   = "web",
+       .accept_connection      = websrv_accept,
+       .recv_handler           = websrv_recv,
+       .send_handler           = websrv_send,
+};
+
+/*
+  startup the web server task
+*/
+static void websrv_task_init(struct task_server *task)
+{
+       NTSTATUS status;
+       uint16_t port = lp_swat_port();
+       const struct model_ops *model_ops;
+
+       /* run the web server as a single process */
+       model_ops = process_model_byname("single");
+       if (!model_ops) goto failed;
+
+       if (lp_interfaces() && lp_bind_interfaces_only()) {
+               int num_interfaces = iface_count();
+               int i;
+               for(i = 0; i < num_interfaces; i++) {
+                       const char *address = iface_n_ip(i);
+                       status = stream_setup_socket(task->event_ctx, model_ops, 
+                                                    &web_stream_ops, 
+                                                    "ipv4", address, 
+                                                    &port, task);
+                       if (!NT_STATUS_IS_OK(status)) goto failed;
+               }
+       } else {
+               status = stream_setup_socket(task->event_ctx, model_ops, 
+                                            &web_stream_ops, 
+                                            "ipv4", lp_socket_address(), 
+                                            &port, task);
+               if (!NT_STATUS_IS_OK(status)) goto failed;
+       }
+
+       return;
+
+failed:
+       task_terminate(task, "Failed to startup web server task");
+}
+
+
+/*
+  called on startup of the web server service It's job is to start
+  listening on all configured sockets
+*/
+static NTSTATUS websrv_init(struct event_context *event_context, 
+                           const struct model_ops *model_ops)
+{      
+       return task_server_startup(event_context, model_ops, websrv_task_init);
+}
+
+/* called at smbd startup - register ourselves as a server service */
+NTSTATUS server_service_web_init(void)
+{
+       return register_server_service("web", websrv_init);
+}
diff --git a/source4/web_server/web_server.h b/source4/web_server/web_server.h
new file mode 100644 (file)
index 0000000..0202c91
--- /dev/null
@@ -0,0 +1,49 @@
+/* 
+   Unix SMB/CIFS implementation.
+   
+   Copyright (C) Andrew Tridgell              2005
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "request.h"
+#include "smbd/process_model.h"
+
+/*
+  context of one open web connection
+*/
+struct websrv_context {
+       struct stream_connection *conn;
+       struct {
+               DATA_BLOB partial;
+               BOOL end_of_headers;
+               char *url;
+               unsigned content_length;
+               BOOL post_request;
+               const char *content_type;
+               const char *query_string;
+               const char *user_agent;
+               const char *referer;
+               const char *host;
+               const char *accept_encoding;
+       } input;
+       struct {
+               DATA_BLOB content;
+               int fd;
+               unsigned nsent;
+               int response_code;
+               const char **headers;
+       } output;
+};