diff options
author | Andrew Tridgell <tridge@samba.org> | 2005-07-13 00:06:38 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:22:39 -0500 |
commit | adbb1612c12d03fa94e4ee23fbc2fa96c09d9dcd (patch) | |
tree | d59f4ab0c87ea7f7fd45f1abbf494cac8e85ac34 /source4/lib/appweb | |
parent | e5700ea5607c366ff8c4fbf272749efae886bbab (diff) | |
download | samba-adbb1612c12d03fa94e4ee23fbc2fa96c09d9dcd.tar.gz samba-adbb1612c12d03fa94e4ee23fbc2fa96c09d9dcd.tar.bz2 samba-adbb1612c12d03fa94e4ee23fbc2fa96c09d9dcd.zip |
r8399: move the ejs and esp code closer to the directory layout used by the
upstream sources. This makes it much easier to keep it up to date.
I will separate out the mpr code into lib/appweb/mpr next
(This used to be commit 52db7a052baeb0f11361ed69b71cb790039e3cc9)
Diffstat (limited to 'source4/lib/appweb')
-rw-r--r-- | source4/lib/appweb/config.mk | 24 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/config.h | 141 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/config.mk | 13 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/ejs.h | 133 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/ejsInternal.h | 293 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/ejsLex.c | 913 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/ejsLib.c | 1061 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/ejsParser.c | 2378 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/ejsProcs.c | 703 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/miniMpr.c | 512 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/miniMpr.h | 292 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/var.c | 2197 | ||||
-rw-r--r-- | source4/lib/appweb/ejs/var.h | 496 | ||||
-rw-r--r-- | source4/lib/appweb/esp/esp.c | 1042 | ||||
-rw-r--r-- | source4/lib/appweb/esp/esp.h | 279 | ||||
-rw-r--r-- | source4/lib/appweb/esp/espEnv.h | 128 | ||||
-rw-r--r-- | source4/lib/appweb/esp/espProcs.c | 246 |
17 files changed, 10851 insertions, 0 deletions
diff --git a/source4/lib/appweb/config.mk b/source4/lib/appweb/config.mk new file mode 100644 index 0000000000..7d1eca3437 --- /dev/null +++ b/source4/lib/appweb/config.mk @@ -0,0 +1,24 @@ +####################### +# Start SUBSYSTEM EJS +[SUBSYSTEM::EJS] +ADD_OBJ_FILES = \ + lib/appweb/ejs/ejsLib.o \ + lib/appweb/ejs/ejsLex.o \ + lib/appweb/ejs/ejsParser.o \ + lib/appweb/ejs/ejsProcs.o \ + lib/appweb/ejs/miniMpr.o \ + lib/appweb/ejs/var.o +NOPROTO=YES +# End SUBSYSTEM EJS +####################### + +####################### +# Start SUBSYSTEM ESP +[SUBSYSTEM::ESP] +ADD_OBJ_FILES = \ + lib/appweb/esp/esp.o \ + lib/appweb/esp/espProcs.o +REQUIRED_SUBSYSTEMS = EJS +NOPROTO=YES +# End SUBSYSTEM ESP +####################### diff --git a/source4/lib/appweb/ejs/config.h b/source4/lib/appweb/ejs/config.h new file mode 100644 index 0000000000..320318a0b2 --- /dev/null +++ b/source4/lib/appweb/ejs/config.h @@ -0,0 +1,141 @@ +#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 1 +#define BLD_FEATURE_IF_MODIFIED 0 +#define BLD_FEATURE_INT64 1 +#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 int64 +#define BLD_FEATURE_NUM_TYPE_ID MPR_TYPE_INT64 +#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" + +#ifndef MAX_FLOAT +#define MAX_FLOAT 3.40282347e+38F +#endif diff --git a/source4/lib/appweb/ejs/config.mk b/source4/lib/appweb/ejs/config.mk new file mode 100644 index 0000000000..f2c0e62f1e --- /dev/null +++ b/source4/lib/appweb/ejs/config.mk @@ -0,0 +1,13 @@ +####################### +# Start SUBSYSTEM EJS +[SUBSYSTEM::EJS] +ADD_OBJ_FILES = \ + lib/ejs/ejsLib.o \ + lib/ejs/ejsLex.o \ + lib/ejs/ejsParser.o \ + lib/ejs/ejsProcs.o \ + lib/ejs/miniMpr.o \ + lib/ejs/var.o +NOPROTO=YES +# End SUBSYSTEM EJS +####################### diff --git a/source4/lib/appweb/ejs/ejs.h b/source4/lib/appweb/ejs/ejs.h new file mode 100644 index 0000000000..f1d2bb4c6e --- /dev/null +++ b/source4/lib/appweb/ejs/ejs.h @@ -0,0 +1,133 @@ +/* + * @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 "miniMpr.h" +#include "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, const char *functionName, + char *args, char *body); +extern void ejsDefineCFunction(EjsId eid, const 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, ...) + PRINTF_ATTRIBUTE(2,3); +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/lib/appweb/ejs/ejsInternal.h b/source4/lib/appweb/ejs/ejsInternal.h new file mode 100644 index 0000000000..3bf99d88b9 --- /dev/null +++ b/source4/lib/appweb/ejs/ejsInternal.h @@ -0,0 +1,293 @@ +/* + * @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 "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, ...) PRINTF_ATTRIBUTE(2,3); + +#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/lib/appweb/ejs/ejsLex.c b/source4/lib/appweb/ejs/ejsLex.c new file mode 100644 index 0000000000..a5f15c2979 --- /dev/null +++ b/source4/lib/appweb/ejs/ejsLex.c @@ -0,0 +1,913 @@ +/* + * @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 "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) { + if (style == '/') { + return EJS_TOK_EOF; + } + 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' || tolower(c) == 'f') { + type = MPR_TYPE_FLOAT; + } + } while (isdigit(c) || c == '.' || tolower(c) == 'e' || tolower(c) == 'f' || + ((type == MPR_TYPE_FLOAT) && (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/lib/appweb/ejs/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c new file mode 100644 index 0000000000..caae5b6495 --- /dev/null +++ b/source4/lib/appweb/ejs/ejsLib.c @@ -0,0 +1,1061 @@ +/* + * @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 "ejsInternal.h" + +#if BLD_FEATURE_EJS + +/********************************** Local Data ********************************/ + +/* + * These fields must be locked before any access when multithreaded + */ +static MprVar master; /* Master object */ +static MprArray *ejsList; /* List of ej handles */ + +#if BLD_FEATURE_MULTITHREAD +static EjsLock lock; +static EjsUnlock unlock; +static void *lockData; +#define ejsLock() if (lock) { (lock)(lockData); } else +#define ejsUnlock() if (unlock) { (unlock)(lockData); } else +#else +#define ejsLock() +#define ejsUnlock() +#endif + +/****************************** Forward Declarations **************************/ + +static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen); + +/************************************* Code ***********************************/ +/* + * Initialize the EJ subsystem + */ + +int ejsOpen(EjsLock lockFn, EjsUnlock unlockFn, void *data) +{ + MprVar *np; + +#if BLD_FEATURE_MULTITHREAD + if (lockFn) { + lock = lockFn; + unlock = unlockFn; + lockData = data; + } +#endif + ejsLock(); + + /* + * Master is the top level object (above global). It is used to clone its + * contents into the global scope for each. This is never visible to the + * user, so don't use ejsCreateObj(). + */ + master = mprCreateObjVar("master", EJS_SMALL_OBJ_HASH_SIZE); + if (master.type == MPR_TYPE_UNDEFINED) { + ejsUnlock(); + return MPR_ERR_CANT_ALLOCATE; + } + + ejsList = mprCreateArray(); + ejsDefineStandardProperties(&master); + + /* + * Make these objects immutable + */ + np = mprGetFirstProperty(&master, MPR_ENUM_FUNCTIONS | MPR_ENUM_DATA); + while (np) { + mprSetVarReadonly(np, 1); + np = mprGetNextProperty(&master, np, MPR_ENUM_FUNCTIONS | + MPR_ENUM_DATA); + } + ejsUnlock(); + return 0; +} + +/******************************************************************************/ + +void ejsClose() +{ + ejsLock(); + mprDestroyArray(ejsList); + mprDestroyVar(&master); + ejsUnlock(); +} + +/******************************************************************************/ +/* + * Create and initialize an EJS engine + */ + +EjsId ejsOpenEngine(EjsHandle primaryHandle, EjsHandle altHandle) +{ + MprVar *np; + Ejs *ep; + + ep = mprMalloc(sizeof(Ejs)); + if (ep == 0) { + return (EjsId) -1; + } + memset(ep, 0, sizeof(Ejs)); + + ejsLock(); + ep->eid = (EjsId) mprAddToArray(ejsList, ep); + ejsUnlock(); + + /* + * Create array of local variable frames + */ + ep->frames = mprCreateArray(); + if (ep->frames == 0) { + ejsCloseEngine(ep->eid); + return (EjsId) -1; + } + ep->primaryHandle = primaryHandle; + ep->altHandle = altHandle; + + /* + * Create first frame: global variables + */ + ep->global = (MprVar*) mprMalloc(sizeof(MprVar)); + *ep->global = ejsCreateObj("global", EJS_OBJ_HASH_SIZE); + if (ep->global->type == MPR_TYPE_UNDEFINED) { + ejsCloseEngine(ep->eid); + return (EjsId) -1; + } + mprAddToArray(ep->frames, ep->global); + + /* + * Create first local variable frame + */ + ep->local = (MprVar*) mprMalloc(sizeof(MprVar)); + *ep->local = ejsCreateObj("local", EJS_OBJ_HASH_SIZE); + if (ep->local->type == MPR_TYPE_UNDEFINED) { + ejsCloseEngine(ep->eid); + return (EjsId) -1; + } + mprAddToArray(ep->frames, ep->local); + + /* + * Clone all master variables into the global frame. This does a + * reference copy. + * + * ejsDefineStandardProperties(ep->global); + */ + np = mprGetFirstProperty(&master, MPR_ENUM_FUNCTIONS | MPR_ENUM_DATA); + while (np) { + mprCreateProperty(ep->global, np->name, np); + np = mprGetNextProperty(&master, np, MPR_ENUM_FUNCTIONS | + MPR_ENUM_DATA); + } + + mprCreateProperty(ep->global, "global", ep->global); + mprCreateProperty(ep->global, "this", ep->global); + mprCreateProperty(ep->local, "local", ep->local); + + return ep->eid; +} + +/******************************************************************************/ +/* + * Close an EJS instance + */ + +void ejsCloseEngine(EjsId eid) +{ + Ejs *ep; + MprVar *vp; + void **handles; + int i; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return; + } + + mprFree(ep->error); + mprDestroyVar(&ep->result); + mprDestroyVar(&ep->tokenNumber); + + mprDeleteProperty(ep->local, "local"); + mprDeleteProperty(ep->global, "this"); + mprDeleteProperty(ep->global, "global"); + + handles = ep->frames->handles; + for (i = 0; i < ep->frames->max; i++) { + vp = handles[i]; + if (vp) { +#if BLD_DEBUG + if (vp->type == MPR_TYPE_OBJECT && vp->properties->refCount > 1) { + mprLog(7, "ejsCloseEngine: %s has ref count %d\n", + vp->name, vp->properties->refCount); + } +#endif + mprDestroyVar(vp); + mprFree(vp); + mprRemoveFromArray(ep->frames, i); + } + } + mprDestroyArray(ep->frames); + + ejsLock(); + mprRemoveFromArray(ejsList, (int) ep->eid); + ejsUnlock(); + + mprFree(ep); +} + +/******************************************************************************/ +/* + * Evaluate an EJS script file + */ + +int ejsEvalFile(EjsId eid, char *path, MprVar *result, char **emsg) +{ + struct stat sbuf; + Ejs *ep; + char *script; + int rc, fd; + + mprAssert(path && *path); + + if (emsg) { + *emsg = NULL; + } + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + goto error; + } + + if ((fd = open(path, O_RDONLY | O_BINARY, 0666)) < 0) { + ejsError(ep, "Can't open %s\n", path); + goto error; + } + + if (stat(path, &sbuf) < 0) { + close(fd); + ejsError(ep, "Cant stat %s", path); + goto error; + } + + if ((script = (char*) mprMalloc(sbuf.st_size + 1)) == NULL) { + close(fd); + ejsError(ep, "Cant malloc %d", (int) sbuf.st_size); + goto error; + } + + if (read(fd, script, sbuf.st_size) != (int) sbuf.st_size) { + close(fd); + mprFree(script); + ejsError(ep, "Error reading %s", path); + goto error; + } + + script[sbuf.st_size] = '\0'; + close(fd); + + rc = ejsEvalBlock(eid, script, result, emsg); + mprFree(script); + + return rc; + +/* + * Error return + */ +error: + *emsg = mprStrdup(ep->error); + return -1; +} + +/******************************************************************************/ +/* + * Create a new variable scope block. This pushes the old local frame down + * the stack and creates a new local variables frame. + */ + +int ejsOpenBlock(EjsId eid) +{ + Ejs *ep; + + if((ep = ejsPtr(eid)) == NULL) { + return -1; + } + + ep->local = (MprVar*) mprMalloc(sizeof(MprVar)); + *ep->local = ejsCreateObj("localBlock", EJS_OBJ_HASH_SIZE); + + mprCreateProperty(ep->local, "local", ep->local); + + return mprAddToArray(ep->frames, ep->local); +} + +/******************************************************************************/ +/* + * Close a variable scope block opened via ejsOpenBlock. Pop back the old + * local variables frame. + */ + +int ejsCloseBlock(EjsId eid, int fid) +{ + Ejs *ep; + + if((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return -1; + } + + /* + * Must remove self-references before destroying "local" + */ + mprDeleteProperty(ep->local, "local"); + + mprDestroyVar(ep->local); + mprFree(ep->local); + + mprRemoveFromArray(ep->frames, fid); + ep->local = (MprVar*) ep->frames->handles[ep->frames->used - 1]; + + return 0; +} + +/******************************************************************************/ +/* + * Create a new variable scope block and evaluate a script. All frames + * created during this context will be automatically deleted when complete. + * vp and emsg are optional. i.e. created local variables will be discarded + * when this routine returns. + */ + +int ejsEvalBlock(EjsId eid, char *script, MprVar *vp, char **emsg) +{ + int rc, fid; + + mprAssert(script); + + fid = ejsOpenBlock(eid); + rc = ejsEvalScript(eid, script, vp, emsg); + ejsCloseBlock(eid, fid); + + return rc; +} + +/******************************************************************************/ +/* + * Parse and evaluate a EJS. Return the result in *vp. The result is "owned" + * by EJ and the caller must not free it. Returns -1 on errors and zero + * for success. On errors, emsg will be set to the reason. The caller must + * free emsg. + */ + +int ejsEvalScript(EjsId eid, char *script, MprVar *vp, char **emsg) +{ + Ejs *ep; + EjsInput *oldBlock; + int state; + void *endlessLoopTest; + int loopCounter; + + if (emsg) { + *emsg = NULL; + } + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return -1; + } + + mprDestroyVar(&ep->result); + + if (script == 0) { + return 0; + } + + /* + * Allocate a new evaluation block, and save the old one + */ + oldBlock = ep->input; + ejsLexOpenScript(ep, script); + + /* + * Do the actual parsing and evaluation + */ + loopCounter = 0; + endlessLoopTest = NULL; + ep->exitStatus = 0; + + do { + state = ejsParse(ep, EJS_STATE_BEGIN, EJS_FLAGS_EXE); + + if (state == EJS_STATE_RET) { + state = EJS_STATE_EOF; + } + /* + * Stuck parser and endless recursion protection. + */ + if (endlessLoopTest == ep->input->scriptServp) { + if (loopCounter++ > 10) { + state = EJS_STATE_ERR; + ejsError(ep, "Syntax error"); + } + } else { + endlessLoopTest = ep->input->scriptServp; + loopCounter = 0; + } + } while (state != EJS_STATE_EOF && state != EJS_STATE_ERR); + + ejsLexCloseScript(ep); + + /* + * Return any error string to the user + */ + if (state == EJS_STATE_ERR && emsg) { + *emsg = mprStrdup(ep->error); + } + + /* + * Restore the old evaluation block + */ + ep->input = oldBlock; + + if (state == EJS_STATE_ERR) { + return -1; + } + + if (vp) { + *vp = ep->result; + } + + return ep->exitStatus; +} + +/******************************************************************************/ +/* + * Core error handling + */ + +static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args) + PRINTF_ATTRIBUTE(2, 0); + +static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args) +{ + EjsInput *ip; + char *errbuf, *msgbuf; + + mprAssert(ep); + mprAssert(args); + + msgbuf = NULL; + mprAllocVsprintf(&msgbuf, MPR_MAX_STRING, fmt, args); + + if (ep) { + ip = ep->input; + if (ip) { + mprAllocSprintf(&errbuf, MPR_MAX_STRING, + "%s\nError on line %d. Offending line: %s\n\n", + msgbuf, ip->lineNumber, ip->line); + } else { + mprAllocSprintf(&errbuf, MPR_MAX_STRING, "%s\n", msgbuf); + } + mprFree(ep->error); + ep->error = errbuf; + } + mprFree(msgbuf); +} + +/******************************************************************************/ +/* + * Internal use function to set the error message + */ + +void ejsError(Ejs* ep, const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ejsErrorCore(ep, fmt, args); + va_end(args); +} + +/******************************************************************************/ +/* + * Public routine to set the error message + */ + +void ejsSetErrorMsg(EjsId eid, const char* fmt, ...) +{ + va_list args; + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return; + } + va_start(args, fmt); + ejsErrorCore(ep, fmt, args); + va_end(args); +} + +/******************************************************************************/ +/* + * Get the current line number + */ + +int ejsGetLineNumber(EjsId eid) +{ + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return -1; + } + return ep->input->lineNumber; +} + +/******************************************************************************/ +/* + * Return the local object + */ + +MprVar *ejsGetLocalObject(EjsId eid) +{ + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return 0; + } + return ep->local; +} + +/******************************************************************************/ +/* + * Return the global object + */ + +MprVar *ejsGetGlobalObject(EjsId eid) +{ + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return 0; + } + return ep->global; +} + +/******************************************************************************/ +/* + * Copy the value of an object property. Return value is in "value". + * If deepCopy is true, copy all object/strings. Otherwise, object reference + * counts are incremented. Callers must always call mprDestroyVar on the + * return value to prevent leaks. + * + * Returns: -1 on errors or if the variable is not found. + */ + +int ejsCopyVar(EjsId eid, const char *var, MprVar *value, bool deepCopy) +{ + Ejs *ep; + MprVar *vp; + + mprAssert(var && *var); + mprAssert(value); + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return -1; + } + + if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) { + return -1; + } + + return mprCopyProperty(value, vp, deepCopy); +} + +/******************************************************************************/ +/* + * Return the value of an object property. Return value is in "value". + * Objects and strings are not copied and reference counts are not modified. + * Callers should NOT call mprDestroyVar. Returns: -1 on errors or if the + * variable is not found. + */ + +int ejsReadVar(EjsId eid, const char *var, MprVar *value) +{ + Ejs *ep; + MprVar *vp; + + mprAssert(var && *var); + mprAssert(value); + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return -1; + } + + if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) { + return -1; + } + + return mprReadProperty(vp, value); +} + +/******************************************************************************/ +/* + * Set a variable that may be an arbitrarily complex object or array reference. + * Will always define in the top most variable frame. + */ + +int ejsWriteVar(EjsId eid, const char *var, MprVar *value) +{ + Ejs *ep; + MprVar *vp; + + mprAssert(var && *var); + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return -1; + } + + if (ejsGetVarCore(ep, var, 0, &vp, EJS_FLAGS_CREATE) < 0) { + return -1; + } + mprAssert(vp); + + /* + * Only copy the value. Don't overwrite the object's name + */ + mprWriteProperty(vp, value); + + return 0; +} + +/******************************************************************************/ +/* + * Set a variable that may be an arbitrarily complex object or array reference. + * Will always define in the top most variable frame. + */ + +int ejsWriteVarValue(EjsId eid, const char *var, MprVar value) +{ + return ejsWriteVar(eid, var, &value); +} + +/******************************************************************************/ +/* + * Delete a variable + */ + +int ejsDeleteVar(EjsId eid, const char *var) +{ + Ejs *ep; + MprVar *vp; + MprVar *obj; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return -1; + } + if (ejsGetVarCore(ep, var, &obj, &vp, 0) < 0) { + return -1; + } + mprDeleteProperty(obj, vp->name); + return 0; +} + +/******************************************************************************/ +/* + * Set the expression return value + */ + +void ejsSetReturnValue(EjsId eid, MprVar value) +{ + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return; + } + mprCopyVar(&ep->result, &value, MPR_SHALLOW_COPY); +} + +/******************************************************************************/ +/* + * Set the expression return value to a string value + */ + +void ejsSetReturnString(EjsId eid, const char *str) +{ + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return; + } + mprCopyVarValue(&ep->result, mprCreateStringVar(str, 0), MPR_SHALLOW_COPY); +} + +/******************************************************************************/ +/* + * Get the expression return value + */ + +MprVar *ejsGetReturnValue(EjsId eid) +{ + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return 0; + } + return &ep->result; +} + +/******************************************************************************/ +/* + * Define a C function. If eid < 0, then update the master object with this + * function. NOTE: in this case, functionName must be simple without any "." or + * "[]" elements. If eid >= 0, add to the specified script engine. In this + * case, functionName can be an arbitrary object reference and can contain "." + * or "[]". + */ + +void ejsDefineCFunction(EjsId eid, const char *functionName, MprCFunction fn, + void *thisPtr, int flags) +{ + if (eid < 0) { + ejsLock(); + mprCreatePropertyValue(&master, functionName, + mprCreateCFunctionVar(fn, thisPtr, flags)); + ejsUnlock(); + } else { + ejsWriteVarValue(eid, functionName, + mprCreateCFunctionVar(fn, thisPtr, flags)); + } +} + +/******************************************************************************/ +/* + * Define a C function with String arguments + */ + +void ejsDefineStringCFunction(EjsId eid, const char *functionName, + MprStringCFunction fn, void *thisPtr, int flags) +{ + if (eid < 0) { + ejsLock(); + mprCreatePropertyValue(&master, functionName, + mprCreateStringCFunctionVar(fn, thisPtr, flags)); + ejsUnlock(); + } else { + ejsWriteVarValue(eid, functionName, + mprCreateStringCFunctionVar(fn, thisPtr, flags)); + } +} + +/******************************************************************************/ +/* + * Define a JavaScript function. Args should be comma separated. + * Body should not contain braces. + */ + +void ejsDefineFunction(EjsId eid, const char *functionName, char *args, + char *body) +{ + MprVar v; + + v = mprCreateFunctionVar(args, body, 0); + if (eid < 0) { + ejsLock(); + mprCreateProperty(&master, functionName, &v); + ejsUnlock(); + } else { + ejsWriteVar(eid, functionName, &v); + } + mprDestroyVar(&v); +} + +/******************************************************************************/ + +void *ejsGetThisPtr(EjsId eid) +{ + Ejs *ep; + + if ((ep = ejsPtr(eid)) == NULL) { + mprAssert(ep); + return 0; + } + return ep->thisPtr; +} + +/******************************************************************************/ +/* + * Find a variable given a variable name and return the parent object and + * the variable itself, the variable . This routine supports variable names + * that may be objects or arrays but may NOT have expressions in the array + * indicies. Returns -1 on errors or if the variable is not found. + */ + +int ejsGetVarCore(Ejs *ep, const char *vname, 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, vname, flags); + currentVar = 0; + propertyName = 0; + + next = varName = mprStrdup(vname); + + 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/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c new file mode 100644 index 0000000000..772ed574c5 --- /dev/null +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -0,0 +1,2378 @@ +/* + * @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 "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; + } + + /* + * Register the function name early to allow for recursive + * function calls (see note in ECMA standard, page 71) + */ + if (!(flags & EJS_FLAGS_ASSIGNMENT)) { + currentObj = ejsFindObj(ep, 0, procName, flags); + vp = mprSetProperty(currentObj, procName, &v); + } + + /* + * 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) { + 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); + lhs->allocatedVar = 0; + mprDestroyVar(lhs); + *lhs = mprCreateNumberVar(num); + /* Examine further below */ + + } else { + /* + * Convert the RHS to a string + */ + mprVarToString(&str, MPR_MAX_STRING, 0, rhs); + rhs->allocatedVar = 0; + 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_PTR: + mprCopyVarValue(&ep->result, mprCreateBoolVar(lhs->ptr == rhs->ptr), 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; + + if (prototype == NULL) { + ejsError(ep, "Function name not defined '%s'\n", proc->procName); + return -1; + } + + /* + * 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]); + } + for (i = 0; i < actualArgs->used; 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; + strcpy(&buf[oldLen], value); + + } 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/lib/appweb/ejs/ejsProcs.c b/source4/lib/appweb/ejs/ejsProcs.c new file mode 100644 index 0000000000..c01f411161 --- /dev/null +++ b/source4/lib/appweb/ejs/ejsProcs.c @@ -0,0 +1,703 @@ +/* + * @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 "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 XX_UNUSED_XX + 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 && mprVarIsNumber(argv[0]->type)) { + /* + * 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 { + /* + * x = new Array(element0, element1, ..., elementN): + */ + max = argc; + for (i = 0; i < max; i++) { + mprItoa(i, idx, sizeof(idx)); + mprCreateProperty(obj, idx, argv[i]); + } + } + + 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: + case MPR_TYPE_PTR: + 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, "%s", argv[0]); + + } else if (argc == 2) { + mprLog(atoi(argv[0]), "%s", 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/lib/appweb/ejs/miniMpr.c b/source4/lib/appweb/ejs/miniMpr.c new file mode 100644 index 0000000000..7dda4e7bd7 --- /dev/null +++ b/source4/lib/appweb/ejs/miniMpr.c @@ -0,0 +1,512 @@ +/* + * @file miniMpr.cpp + * @brief Mini Mbedthis Portable Runtime (MPR) + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved. + * + * This software is distributed under commercial and open source licenses. + * You may use the GPL open source license described below or you may acquire + * a commercial license from Mbedthis Software. You agree to be fully bound + * by the terms of either license. Consult the LICENSE.TXT distributed with + * this software for full details. + * + * This software is open source; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See the GNU General Public License for more + * details at: http://www.mbedthis.com/downloads/gplLicense.html + * + * This program is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This GPL license does NOT permit incorporating this software into + * proprietary programs. If you are unable to comply with the GPL, you must + * acquire a commercial license to use this software. Commercial licenses + * for this software and support services are available from Mbedthis + * Software at http://www.mbedthis.com + * + * @end + */ + +#include "lib/appweb/ejs/miniMpr.h" + +/************************************ Code ************************************/ +#if !BLD_APPWEB +#if !BLD_GOAHEAD_WEBSERVER + +static void *mpr_ctx; + +/* set the memory context to be used for all ejs variables */ +void mprSetCtx(TALLOC_CTX *ctx) +{ + mpr_ctx = ctx; +} + +/* return the memory context being used for all ejs variables */ +void *mprMemCtx(void) +{ + return mpr_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) +{ + char *buf; + mprAllocSprintf(&buf, MPR_MAX_STRING, "esp exception - ASSERT at %s:%d, %s\n", + file, line, cond); + ejs_exception(buf); +} + +#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/lib/appweb/ejs/miniMpr.h b/source4/lib/appweb/ejs/miniMpr.h new file mode 100644 index 0000000000..d431ebdc1b --- /dev/null +++ b/source4/lib/appweb/ejs/miniMpr.h @@ -0,0 +1,292 @@ +/* + * @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 + */ +#ifndef _INCLUDES_H + #include "includes.h" +#endif + +/* 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 + #include <math.h> + +/********************************** 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 +#ifndef uint + #define uint unsigned +#endif + __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) PRINTF_ATTRIBUTE(3,0); +extern int mprAllocSprintf(char **msgbuf, int maxSize, const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +extern char *mprItoa(int num, char *buf, int width); +extern void mprLog(int level, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +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 mprSetCtx(void *ctx); +extern void *mprMemCtx(void); + +#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/lib/appweb/ejs/var.c b/source4/lib/appweb/ejs/var.c new file mode 100644 index 0000000000..9d2afe5306 --- /dev/null +++ b/source4/lib/appweb/ejs/var.c @@ -0,0 +1,2197 @@ +/* + * @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 "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 && + 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; +} + +/******************************************************************************/ +/* + * Initialize an opaque pointer. + */ + +MprVar mprCreatePtrVar(void *ptr) +{ + MprVar v; + + memset(&v, 0x0, sizeof(v)); + v.type = MPR_TYPE_PTR; + v.ptr = ptr; + + 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%p\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 = (char*) 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_PTR: + dest->ptr = src->ptr; + 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_PTR: + mprAllocSprintf(out, size, "[Opaque Pointer %p]", obj->ptr); + 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 = MPR_TYPE_UNDEFINED; + 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((unsigned char)cp[1]) == 'x') { + cp = &cp[2]; + } + for (cp = buf; *cp; cp++) { + if (! isdigit((unsigned char) *cp)) { + break; + } + } + + if (*cp != '\0') { +#if BLD_FEATURE_FLOATING_POINT + if (*cp == '.' || tolower((unsigned char)*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: + case MPR_TYPE_PTR: + 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(const 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_PTR: + return (vp->ptr != NULL); + + 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(const 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: + case MPR_TYPE_PTR: + 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(const 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(const 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: + case MPR_TYPE_PTR: + 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((unsigned char)*cp) == 'x') { + cp++; + radix = 16; + while (*cp) { + c = tolower((unsigned char)*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((unsigned char)*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(const 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: + case MPR_TYPE_PTR: + 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((unsigned char)*cp) == 'x') { + cp++; + radix = 16; + while (*cp) { + c = tolower((unsigned char)*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((unsigned char)*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/lib/appweb/ejs/var.h b/source4/lib/appweb/ejs/var.h new file mode 100644 index 0000000000..8ed13a4995 --- /dev/null +++ b/source4/lib/appweb/ejs/var.h @@ -0,0 +1,496 @@ +/* + * @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 "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 */ +#define MPR_TYPE_PTR 11 /* Opaque pointer */ + +/* + * 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 mprVarIsPtr(type) \ + (type == MPR_TYPE_PTR) +#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 */ + /* FUTURE - Better way of doing this */ + int numDataItems; /* Enumerable data items */ + uint hashSize : 8; /* Size of the hash table */ + /* FUTURE -- increase size of refCount */ + uint refCount : 8; /* References to this property*/ + /* FUTURE - make these flags */ + uint deleteProtect : 8; /* Don't recursively delete */ + uint visited : 8; /* Node has been processed */ +} MprProperties; + +/* + * Universal Variable Type + */ +typedef struct MprVar { + /* FUTURE - remove name to outside reference */ + MprStr name; /* Property name */ + /* FUTURE - remove */ + MprStr fullName; /* Full object name */ + /* FUTURE - make part of the union */ + 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 */ + void *ptr; /* Opaque pointer */ +#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 MprVar mprCreatePtrVar(void *ptr); +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(const MprVar *vp); +extern int mprVarToInteger(const MprVar *vp); +#if BLD_FEATURE_INT64 +extern int64 mprVarToInteger64(const MprVar *vp); +#endif +extern bool mprVarToBool(const MprVar *vp); +#if BLD_FEATURE_FLOATING_POINT +extern double mprVarToFloat(const 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/lib/appweb/esp/esp.c b/source4/lib/appweb/esp/esp.c new file mode 100644 index 0000000000..ef20557f93 --- /dev/null +++ b/source4/lib/appweb/esp/esp.c @@ -0,0 +1,1042 @@ +/* + * @file esp.c + * @brief Embedded Server Pages (ESP) core processing. + * @overview The ESP handler provides an efficient way to generate + * dynamic pages using server-side Javascript. This code provides + * core processing, and should be called by an associated web + * server URL handler. + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved. + * + * This software is distributed under commercial and open source licenses. + * You may use the GPL open source license described below or you may acquire + * a commercial license from Mbedthis Software. You agree to be fully bound + * by the terms of either license. Consult the LICENSE.TXT distributed with + * this software for full details. + * + * This software is open source; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See the GNU General Public License for more + * details at: http://www.mbedthis.com/downloads/gplLicense.html + * + * This program is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This GPL license does NOT permit incorporating this software into + * proprietary programs. If you are unable to comply with the GPL, you must + * acquire a commercial license to use this software. Commercial licenses + * for this software and support services are available from Mbedthis + * Software at http://www.mbedthis.com + * + * @end + */ +/********************************** Includes **********************************/ + +#include "esp.h" + +#if BLD_FEATURE_ESP_MODULE + +/*********************************** Locals ***********************************/ +/* + * Master ESP control interface with the web server + */ + +static const Esp *esp; + +/***************************** Forward Declarations ***************************/ + +static int buildScript(EspRequest *ep, char **jsBuf, char *input, char + **errMsg); + +/************************************ Code ************************************/ +/* + * Called at server initialization + */ + +int espOpen(const Esp *control) +{ + mprAssert(control); + +#if BLD_FEATURE_MULTITHREAD + ejsOpen(control->lock, control->unlock, control->lockData); +#else + ejsOpen(0, 0, 0); +#endif + + /* + * Register the standard procedures + */ + espRegisterProcs(); + + /* + * Just for brain dead systems that don't zero global memory + */ + esp = control; + return 0; +} + +/******************************************************************************/ +/* + * Called at server termination + */ + +void espClose() +{ + ejsClose(); +} + +/******************************************************************************/ +/* + * Create for new ESP request. Assumed that this is called after all the + * HTTP headers have been read but before POST data has been read. It is + * expected that any session cookies have been read and that "variables" + * contains references to all the environment objects including "session". + * requestHandle is the web server request handle. + */ + +EspRequest *espCreateRequest(EspHandle webServerRequestHandle, char *uri, + MprVar *variables) +{ + EspRequest *ep; + MprVar *global; +#if BLD_FEATURE_LEGACY_API + MprVar *np; + char keyBuf[ESP_MAX_HEADER]; + int i; +#endif + + mprAssert(variables); + + ep = mprMalloc(sizeof(EspRequest)); + if (ep == 0) { + return 0; + } + memset(ep, 0, sizeof(EspRequest)); + ep->requestHandle = webServerRequestHandle; + ep->esp = esp; + ep->uri = mprStrdup(uri); + ep->docPath = 0; + ep->variables = variables; + + /* + * The handle passed to ejsOpenEngine is passed to every C function + * called by JavaScript. + */ + ep->eid = ejsOpenEngine((EjsHandle) ep, (EjsHandle) webServerRequestHandle); + if (ep->eid < 0) { + mprFree(ep); + return 0; + } + + /* + * All these copies and SetProperties will only copy references + * They will increments the object ref counts. + */ + mprCopyVar(&variables[ESP_GLOBAL_OBJ], ejsGetGlobalObject(ep->eid), + MPR_SHALLOW_COPY); + mprCopyVar(&variables[ESP_LOCAL_OBJ], ejsGetLocalObject(ep->eid), + MPR_SHALLOW_COPY); + + global = &variables[ESP_GLOBAL_OBJ]; + mprCreateProperty(global, "application", &variables[ESP_APPLICATION_OBJ]); + mprCreateProperty(global, "cookies", &variables[ESP_COOKIES_OBJ]); + mprCreateProperty(global, "files", &variables[ESP_FILES_OBJ]); + mprCreateProperty(global, "form", &variables[ESP_FORM_OBJ]); + mprCreateProperty(global, "headers", &variables[ESP_HEADERS_OBJ]); + mprCreateProperty(global, "request", &variables[ESP_REQUEST_OBJ]); + + /* + * FUTURE -- could server be shared across all requests for a given host + * and be made read-only. + */ + mprCreateProperty(global, "server", &variables[ESP_SERVER_OBJ]); + +#if BLD_FEATURE_SESSION + mprCreateProperty(global, "session", &variables[ESP_SESSION_OBJ]); +#endif + +#if BLD_FEATURE_LEGACY_API + /* + * DEPRECATED: 2.0 + * Define variables as globals. headers[] are prefixed with "HTTP_". + * NOTE: MaRequest::setVar does not copy into globals, whereas espSetVar + * does if legacy_api is defined. So variables pre-defined by MaRequest + * must be copied here into globals[].
+ * + * NOTE: if a variable is in session[] and in form[], the form[] will + * override being later in the variables[] list. Use mprSetProperty + * instead of mprCreateProperty to cover for this case. + */ + for (i = 0; i < ESP_OBJ_MAX; i++) { + if (i == ESP_GLOBAL_OBJ || i == ESP_LOCAL_OBJ) { + continue; + } + if (variables[i].type != MPR_TYPE_OBJECT) { + continue; + } + np = mprGetFirstProperty(&variables[i], MPR_ENUM_DATA); + while (np) { + if (i == ESP_HEADERS_OBJ) { + mprSprintf(keyBuf, sizeof(keyBuf) - 1, "HTTP_%s", np->name); + mprSetProperty(global, keyBuf, np); + } else { + mprSetProperty(global, np->name, np); + } + np = mprGetNextProperty(&variables[i], np, MPR_ENUM_DATA); + } + } +#endif + return ep; +} + +/******************************************************************************/ + +void espDestroyRequest(EspRequest *ep) +{ + mprAssert(ep); + mprAssert(ep->eid >= 0); + + mprFree(ep->uri); + mprFree(ep->docPath); + ejsCloseEngine(ep->eid); + mprFree(ep); +} + +/******************************************************************************/ +/* + * The callback function will be called: + * + * (fn)(EjsId eid, EspRequest *ep, argc, argv); + * + * Callers can get their web server handle by calling: + * + * rq = (requiredCast) espGetHandle(ep); + */ + +void espDefineCFunction(EspRequest *ep, const 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, const char *str) +{ + mprAssert(ep); + + ejsSetReturnValue(ep->eid, mprCreateStringVar(str, 0)); +} + +/******************************************************************************/ + +void espSetResponseCode(EspRequest *ep, int code) +{ + mprAssert(ep); + + ep->esp->setResponseCode(ep->requestHandle, code); +} + +/******************************************************************************/ + +void espSetVar(EspRequest *ep, EspEnvType oType, char *var, MprVar value) +{ + mprCreatePropertyValue(&ep->variables[oType], var, value); +} + +/******************************************************************************/ + +void espSetStringVar(EspRequest *ep, EspEnvType oType, + const char *var, const char *value) +{ + /* + * Will create or update if already existing + */ + mprCreatePropertyValue(&ep->variables[oType], var, + mprCreateStringVar(value, 0)); +} + +/******************************************************************************/ + +int espUnsetVar(EspRequest *ep, EspEnvType oType, char *var) +{ + return mprDeleteProperty(&ep->variables[oType], var); +} + +/******************************************************************************/ + +int espWrite(EspRequest *ep, char *buf, int size) +{ + mprAssert(ep); + mprAssert(buf); + mprAssert(size >= 0); + + return ep->esp->writeBlock(ep->requestHandle, buf, size); +} + +/******************************************************************************/ + +int espWriteString(EspRequest *ep, char *buf) +{ + mprAssert(ep); + mprAssert(buf); + + return ep->esp->writeBlock(ep->requestHandle, buf, strlen(buf)); +} + +/******************************************************************************/ + +int espWriteFmt(EspRequest *ep, char *fmt, ...) +{ + va_list args; + char *buf; + int rc, len; + + mprAssert(ep); + mprAssert(fmt); + + va_start(args, fmt); + len = mprAllocVsprintf(&buf, MPR_MAX_HEAP_SIZE, fmt, args); + rc = ep->esp->writeBlock(ep->requestHandle, buf, len); + mprFree(buf); + va_end(args); + return rc; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* + * Get a javascript identifier. Must allow x.y['abc'] or x.y["abc"]. + * Must be careful about quoting and only allow quotes inside []. + */ + +static int getIdentifier(EspParse *parse) +{ + int atQuote, prevC, c; + + mprAssert(parse); + + atQuote = 0; + prevC = 0; + c = *parse->inp++; + + while (isalnum(c) || c == '_' || c == '.' || c == '[' || + c == ']' || c == '\'' || c == '\"') { + if (c == '\'' || c == '\"') { + if (c == atQuote) { + atQuote = 0; + } else if (prevC == '[') { + atQuote = c; + } else { + break; + } + } + if (parse->tokp >= parse->endp) { + parse->token = (char*) mprRealloc(parse->token, + parse->tokLen + ESP_TOK_INCR); + if (parse->token == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + parse->token[parse->tokLen] = '\0'; + parse->tokLen += ESP_TOK_INCR; + parse->endp = &parse->token[parse->tokLen - 1]; + } + *parse->tokp++ = c; + prevC = c; + c = *parse->inp++; + } + + parse->inp--; + *parse->tokp = '\0'; + + return 0; +} + +/******************************************************************************/ +/* + * Get the next ESP input token. input points to the next input token. + * parse->token will hold the parsed token. The function returns the token id. + */ + +static int getEspToken(int state, EspParse *parse) +{ + char *cp; + int tid, done, c, quoted; + + tid = ESP_TOK_LITERAL; + parse->tokp = parse->token; + parse->tokp[0] = '\0'; + quoted = 0; + + c = *parse->inp++; + for (done = 0; !done; c = *parse->inp++) { + + /* + * Get room for more characters in the token buffer + */ + if (parse->tokp >= parse->endp) { + parse->token = (char*) mprRealloc(parse->token, + parse->tokLen + ESP_TOK_INCR); + if (parse->token == 0) { + return ESP_TOK_ERR; + } + parse->token[parse->tokLen] = '\0';
+ parse->tokp = &parse->token[parse->tokLen - 1]; + parse->tokLen += ESP_TOK_INCR; + parse->endp = &parse->token[parse->tokLen - 1]; + } + + switch (c) { + case 0: + if (*parse->token) { + done++; + parse->inp--; + break; + } + return ESP_TOK_EOF; + + default: + if (c == '\"' && state != ESP_STATE_IN_ESP_TAG) { + *parse->tokp++ = '\\'; + } + *parse->tokp++ = c; + quoted = 0; + break; + + case '\\': + quoted = 1; + *parse->tokp++ = c; + break; + + case '@': + if (*parse->inp == '@' && state != ESP_STATE_IN_ESP_TAG) { + if (quoted) { + parse->tokp--; + quoted = 0; + } else { + if (*parse->token) { + parse->inp--; + } else { + parse->inp++; + tid = ESP_TOK_ATAT; + if (getIdentifier(parse) < 0) { + return ESP_TOK_ERR; + } + } + done++; + break; + } + } + *parse->tokp++ = c; + break; + + case '<': + if (*parse->inp == '%' && state != ESP_STATE_IN_ESP_TAG) { + if (quoted) { + parse->tokp--; + quoted = 0; + *parse->tokp++ = c; + break; + } + if (*parse->token) { + parse->inp--; + done++; + break; + } + parse->inp++; + while (isspace((int) *parse->inp)) { + parse->inp++; + } + if (*parse->inp == '=') { + parse->inp++; + while (isspace((int) *parse->inp)) { + parse->inp++; + } + tid = ESP_TOK_EQUALS; + if (getIdentifier(parse) < 0) { + return ESP_TOK_ERR; + } + done++; + break; + } + if (*parse->inp == 'i' && + strncmp(parse->inp, "include", 7) == 0 && + isspace((int) parse->inp[7])) { + tid = ESP_TOK_INCLUDE; + parse->inp += 7; + while (isspace((int) *parse->inp)) { + parse->inp++; + } + while (*parse->inp && !isspace((int) *parse->inp) && + *parse->inp != '%' && parse->tokp < parse->endp) { + *parse->tokp++ = *parse->inp++; + } + *parse->tokp = '\0'; + if (parse->token[0] == '"') { + parse->tokp = parse->token; + for (cp = &parse->token[1]; *cp; ) { + *parse->tokp++ = *cp++; + } + if (cp[-1] == '"') { + parse->tokp--; + } + *parse->tokp = '\0'; + } + + } else { + tid = ESP_TOK_START_ESP; + } + done++; + break; + } + *parse->tokp++ = c; + break; + + case '%': + if (*parse->inp == '>' && state == ESP_STATE_IN_ESP_TAG) { + if (quoted) { + parse->tokp--; + quoted = 0; + } else { + if (*parse->token) { + parse->inp--; + } else { + tid = ESP_TOK_END_ESP; + parse->inp++; + } + done++; + break; + } + } + *parse->tokp++ = c; + break; + } + } + + *parse->tokp = '\0'; + parse->inp--; + return tid; +} + +/******************************************************************************/ +/* + * Convert an ESP page into a JavaScript. We also expand include files. + */ + +static int buildScript(EspRequest *ep, char **jsBuf, char *input, char **errMsg) +{ + EspParse parse; + char path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME], incPath[MPR_MAX_FNAME]; + char *incBuf, *incText; + int state, tid, len, rc, maxScriptSize, incSize; + + mprAssert(ep); + mprAssert(jsBuf); + mprAssert(input); + + rc = 0; + len = 0; + state = ESP_STATE_BEGIN; + if (errMsg) { + *errMsg = 0; + } + + memset(&parse, 0, sizeof(parse)); + parse.token = (char*) mprMalloc(ESP_TOK_INCR); + if (parse.token == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + parse.token[0] = '\0'; + parse.tokLen = ESP_TOK_INCR; + parse.endp = &parse.token[parse.tokLen - 1]; + parse.tokp = parse.token; + parse.inBuf = input; + parse.inp = parse.inBuf; + + maxScriptSize = esp->maxScriptSize; + + tid = getEspToken(state, &parse); + while (tid != ESP_TOK_EOF && len >= 0) { + + switch (tid) { + default: + case ESP_TOK_ERR: + mprFree(parse.token); + return MPR_ERR_BAD_SYNTAX; + + case ESP_TOK_LITERAL: + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, + "write(\"", parse.token, "\");\n", 0); + break; + + case ESP_TOK_ATAT: + /* + * Trick to get undefined variables to evaluate to "". + * Catenate with "" to cause toString to run. + */ + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, + "write(\"\" + ", parse.token, ");\n", 0); + break; + + case ESP_TOK_EQUALS: + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, + "write(\"\" + ", parse.token, ");\n", 0); + state = ESP_STATE_IN_ESP_TAG; + break; + + case ESP_TOK_START_ESP: + state = ESP_STATE_IN_ESP_TAG; + tid = getEspToken(state, &parse); + while (tid != ESP_TOK_EOF && tid != ESP_TOK_EOF && + tid != ESP_TOK_END_ESP && len >= 0) { + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, + parse.token, 0); + tid = getEspToken(state, &parse); + } + state = ESP_STATE_BEGIN; + break; + + case ESP_TOK_END_ESP: + state = ESP_STATE_BEGIN; + break; + + case ESP_TOK_INCLUDE: + if (parse.token[0] == '/') { + mprStrcpy(incPath, sizeof(incPath), parse.token); + } else { + mprGetDirName(dir, sizeof(dir), ep->uri); + mprSprintf(incPath, sizeof(incPath), "%s/%s", + dir, parse.token); + } + if (esp->mapToStorage(ep->requestHandle, path, sizeof(path), + incPath, 0) < 0) { + mprAllocSprintf(errMsg, MPR_MAX_STRING, + "Can't find include file: %s", path); + rc = MPR_ERR_CANT_OPEN; + break; + } + if (esp->readFile(ep->requestHandle, &incText, &incSize, path) < 0){ + mprAllocSprintf(errMsg, MPR_MAX_STRING, + "Can't read include file: %s", path); + rc = MPR_ERR_CANT_READ; + break; + } + incText[incSize] = '\0'; + + /* + * Recurse and process the include script + */ + incBuf = 0; + if ((rc = buildScript(ep, &incBuf, incText, errMsg)) < 0) { + mprFree(incText); + mprFree(parse.token); + return rc; + } + + len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, incBuf, 0); + mprFree(incText); + mprFree(incBuf); + state = ESP_STATE_IN_ESP_TAG; + break; + } + tid = getEspToken(state, &parse); + } + mprFree(parse.token); + if (len < 0) { + mprAllocSprintf(errMsg, MPR_MAX_STRING, + "Script token is too big in %s.\nConfigured maximum is %d.", + path, maxScriptSize); + return MPR_ERR_WONT_FIT; + } + return rc; +} + +/******************************************************************************/ +/******************************* Wrapped Routines *****************************/ +/******************************************************************************/ + +int espCopyVar(EspRequest *ep, char *var, MprVar *value, int copyDepth) +{ + return ejsCopyVar(ep->eid, var, value, copyDepth); +} + +/******************************************************************************/ + +MprVar espCreateObjVar(char *name, int hashSize) +{ + return ejsCreateObj(name, hashSize); +} + +/******************************************************************************/ + +MprVar espCreateArrayVar(char *name, int size) +{ + return ejsCreateArray(name, size); +} + +/******************************************************************************/ + +bool espDestroyVar(MprVar *obj) +{ + return ejsDestroyVar(obj); +} + +/******************************************************************************/ + +MprVar *espCreateProperty(MprVar *obj, char *property, MprVar *newValue) +{ + return mprCreateProperty(obj, property, newValue); +} + +/******************************************************************************/ + +MprVar *espCreatePropertyValue(MprVar *obj, char *property, MprVar newValue) +{ + return mprCreatePropertyValue(obj, property, newValue); +} + +/******************************************************************************/ + +void espDefineFunction(EspRequest *ep, const 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 XX_UNUSED_XX + +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 XX_UNUSED_XX + +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/lib/appweb/esp/esp.h b/source4/lib/appweb/esp/esp.h new file mode 100644 index 0000000000..ee86e8d345 --- /dev/null +++ b/source4/lib/appweb/esp/esp.h @@ -0,0 +1,279 @@ +/** + * @file esp.h + * @brief Header for Embedded Server Pages (ESP) + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved. + * + * This software is distributed under commercial and open source licenses. + * You may use the GPL open source license described below or you may acquire + * a commercial license from Mbedthis Software. You agree to be fully bound + * by the terms of either license. Consult the LICENSE.TXT distributed with + * this software for full details. + * + * This software is open source; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See the GNU General Public License for more + * details at: http://www.mbedthis.com/downloads/gplLicense.html + * + * This program is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This GPL license does NOT permit incorporating this software into + * proprietary programs. If you are unable to comply with the GPL, you must + * acquire a commercial license to use this software. Commercial licenses + * for this software and support services are available from Mbedthis + * Software at http://www.mbedthis.com + * + * @end + */ +/********************************** Includes **********************************/ + +#ifndef _h_ESP_h +#define _h_ESP_h 1 + +#include "lib/appweb/ejs/ejs.h" +#include "lib/appweb/esp/espEnv.h" +#include "lib/appweb/ejs/var.h" +#include "lib/appweb/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 */ + const 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); + const char *(*getSessionId)(EspHandle handle); + int (*mapToStorage)(EspHandle handle, char *path, int len, const char *uri, + int flags); + int (*readFile)(EspHandle handle, char **buf, int *len, const char *path); + void (*redirect)(EspHandle handle, int code, char *url); + void (*setCookie)(EspHandle handle, const char *name, const char *value, + int lifetime, const 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(const 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, const char *functionName, + EspCFunction fn, void *thisPtr); +extern void espDefineFunction(EspRequest *ep, const 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, ...) PRINTF_ATTRIBUTE(2,3); +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, const 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, ...) PRINTF_ATTRIBUTE(2,3); + +/* + * 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/lib/appweb/esp/espEnv.h b/source4/lib/appweb/esp/espEnv.h new file mode 100644 index 0000000000..a3c9d9f5c7 --- /dev/null +++ b/source4/lib/appweb/esp/espEnv.h @@ -0,0 +1,128 @@ +/* + * @file espEnv.h + * @brief ESP Environment Variables + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved. + * + * This software is distributed under commercial and open source licenses. + * You may use the GPL open source license described below or you may acquire + * a commercial license from Mbedthis Software. You agree to be fully bound + * by the terms of either license. Consult the LICENSE.TXT distributed with + * this software for full details. + * + * This software is open source; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See the GNU General Public License for more + * details at: http://www.mbedthis.com/downloads/gplLicense.html + * + * This program is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This GPL license does NOT permit incorporating this software into + * proprietary programs. If you are unable to comply with the GPL, you must + * acquire a commercial license to use this software. Commercial licenses + * for this software and support services are available from Mbedthis + * Software at http://www.mbedthis.com + * + * @end + */ + +/******************************************************************************/ + +#ifndef _h_ESP_ENV_h +#define _h_ESP_ENV_h 1 + +/* + * @brief Scripting environment variable array types + */ +typedef enum EspEnvType { + ESP_UNDEFINED_OBJ = -1, + + /** + * Elements for server[]: + * DOCUMENT_ROOT GATEWAY_INTERFACE SERVER_ADDR SERVER_PORT SERVER_NAME + * SERVER_PROTOCOL SERVER_SOFTWARE SERVER_URL UPLOAD_DIR + * FUTURE: SERVER_ADMIN + * FUTURE: this could be shared across all hosts and be made read-only. + */ + ESP_SERVER_OBJ = 0, /*! server[] data */ + + /** + * Elements for session[]: are user defined + */ + ESP_SESSION_OBJ = 1, /*! session[] data */ + + /** + * Elements for request[]: + * AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE QUERY_STRING PATH_INFO + * PATH_TRANSLATED REMOTE_ADDR REMOTE_HOST REMOTE_USER REQUEST_METHOD + * REQUEST_URI SCRIPT_FILENAME SCRIPT_NAME + * FUTURE: FILEPATH_INFO REDIRECT_URL SELF REMOTE_PORT AUTH_USER + * AUTH_GROUP AUTH_ACL + */ + ESP_REQUEST_OBJ = 2, /*! request[] data */ + + /** + * Elements for headers[]: + * HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_CONNECTION HTTP_HOST + * HTTP_REFERER HTTP_USER_AGENT and any other custom headers + */ + ESP_HEADERS_OBJ = 3, /*! header [] data */ + + /** + * Elements for cookies[]: are defined by the HTTP request + */ + ESP_COOKIES_OBJ = 4, /*! cookies[] data */ + + /** + * Elements for files[]: are defined by the HTTP request + * CLIENT_FILENAME CONTENT_TYPE FILENAME SIZE + */ + ESP_FILES_OBJ = 5, /*! files[] data */ + + /** + * Elements for form[]: are defined by the HTTP request + */ + ESP_FORM_OBJ = 6, /*! form[] data */ + + /** + * Elements for application[]: are user defined + */ + ESP_APPLICATION_OBJ = 7, /*! application[] data */ + + /** + * Elements for global[]: are defined by ESP/EJS + */ + ESP_GLOBAL_OBJ = 8, /*! global [] data */ + + /* + * Elements for local[]: are defined by ESP/EJS + */ + ESP_LOCAL_OBJ = 9, /*! local [] data */ +} EspEnvType; + +#define ESP_OBJ_MAX 10 /* Total objects */ + +#if BLD_SQUEEZE +#define ESP_HASH_SIZE 19 /* Size of hash tables */ +#else +#define ESP_HASH_SIZE 37 +#endif + +/******************************************************************************/ +#endif /* _h_ESP_ENV_h */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim:tw=78 + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/source4/lib/appweb/esp/espProcs.c b/source4/lib/appweb/esp/espProcs.c new file mode 100644 index 0000000000..28b69a8a6f --- /dev/null +++ b/source4/lib/appweb/esp/espProcs.c @@ -0,0 +1,246 @@ +/* + * @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) +{ + const Esp *esp; + char path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME]; + char *emsg=NULL, *buf; + int size, i; + + esp = ep->esp; + mprAssert(argv); + for (i = 0; i < argc; i++) { + const char *extension; + + if (argv[i][0] != '/') { + mprGetDirName(dir, sizeof(dir), ep->docPath); + mprSprintf(path, sizeof(path), "%s/%s", dir, argv[i]); + } else { + mprSprintf(path, sizeof(path), "%s", 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'; + + extension = strrchr(argv[i], '.'); + /* this makes handling include files in esp scripts much more convenient */ + if (extension && strcasecmp(extension, ".esp") == 0) { + if (espProcessRequest(ep, path, buf, &emsg) != 0) { + espError(ep, "Cant evaluate script - %s", emsg?emsg:""); + mprFree(buf); + return -1; + } + } else { + if (ejsEvalScript(espGetScriptHandle(ep), buf, 0, &emsg) < 0) { + espError(ep, "Cant evaluate script - %s", emsg?emsg:""); + 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 + */ |