From adbb1612c12d03fa94e4ee23fbc2fa96c09d9dcd Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 13 Jul 2005 00:06:38 +0000 Subject: 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) --- source4/lib/appweb/ejs/config.h | 141 ++ source4/lib/appweb/ejs/config.mk | 13 + source4/lib/appweb/ejs/ejs.h | 133 ++ source4/lib/appweb/ejs/ejsInternal.h | 293 +++++ source4/lib/appweb/ejs/ejsLex.c | 913 +++++++++++++ source4/lib/appweb/ejs/ejsLib.c | 1061 +++++++++++++++ source4/lib/appweb/ejs/ejsParser.c | 2378 ++++++++++++++++++++++++++++++++++ source4/lib/appweb/ejs/ejsProcs.c | 703 ++++++++++ source4/lib/appweb/ejs/miniMpr.c | 512 ++++++++ source4/lib/appweb/ejs/miniMpr.h | 292 +++++ source4/lib/appweb/ejs/var.c | 2197 +++++++++++++++++++++++++++++++ source4/lib/appweb/ejs/var.h | 496 +++++++ 12 files changed, 9132 insertions(+) create mode 100644 source4/lib/appweb/ejs/config.h create mode 100644 source4/lib/appweb/ejs/config.mk create mode 100644 source4/lib/appweb/ejs/ejs.h create mode 100644 source4/lib/appweb/ejs/ejsInternal.h create mode 100644 source4/lib/appweb/ejs/ejsLex.c create mode 100644 source4/lib/appweb/ejs/ejsLib.c create mode 100644 source4/lib/appweb/ejs/ejsParser.c create mode 100644 source4/lib/appweb/ejs/ejsProcs.c create mode 100644 source4/lib/appweb/ejs/miniMpr.c create mode 100644 source4/lib/appweb/ejs/miniMpr.h create mode 100644 source4/lib/appweb/ejs/var.c create mode 100644 source4/lib/appweb/ejs/var.h (limited to 'source4/lib/appweb/ejs') 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 (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 + #include + #include + #include + #include + #include + #include + +#if !WIN + #include +#endif + +#if CE + #include + #include "CE/wincompat.h" +#endif + +#if LYNX + #include +#endif + +#if QNX4 + #include +#endif + #include + +/********************************** 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 + */ -- cgit From a102f52db89f8a31327a3a9fafa1e5a1699ca3cd Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 13 Jul 2005 00:12:22 +0000 Subject: r8400: separate out the mpr code, as it is in the upstream appweb sources (This used to be commit 0e30b6e4cc9e70975d3179cfeddc4bfcc8c8fbb7) --- source4/lib/appweb/ejs/ejs.h | 4 +- source4/lib/appweb/ejs/miniMpr.c | 512 --------- source4/lib/appweb/ejs/miniMpr.h | 292 ----- source4/lib/appweb/ejs/var.c | 2197 -------------------------------------- source4/lib/appweb/ejs/var.h | 496 --------- 5 files changed, 2 insertions(+), 3499 deletions(-) delete mode 100644 source4/lib/appweb/ejs/miniMpr.c delete mode 100644 source4/lib/appweb/ejs/miniMpr.h delete mode 100644 source4/lib/appweb/ejs/var.c delete mode 100644 source4/lib/appweb/ejs/var.h (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejs.h b/source4/lib/appweb/ejs/ejs.h index f1d2bb4c6e..5efdb47192 100644 --- a/source4/lib/appweb/ejs/ejs.h +++ b/source4/lib/appweb/ejs/ejs.h @@ -44,8 +44,8 @@ #ifndef _h_EJS #define _h_EJS 1 -#include "miniMpr.h" -#include "var.h" +#include "lib/appweb/mpr/miniMpr.h" +#include "lib/appweb/mpr/var.h" #ifdef __cplusplus extern "C" { diff --git a/source4/lib/appweb/ejs/miniMpr.c b/source4/lib/appweb/ejs/miniMpr.c deleted file mode 100644 index 7dda4e7bd7..0000000000 --- a/source4/lib/appweb/ejs/miniMpr.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * @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 deleted file mode 100644 index d431ebdc1b..0000000000 --- a/source4/lib/appweb/ejs/miniMpr.h +++ /dev/null @@ -1,292 +0,0 @@ -/* - * @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 - #include - #include - #include - #include - #include - #include - -#if !WIN - #include -#endif - -#if CE - #include - #include "CE/wincompat.h" -#endif - -#if LYNX - #include -#endif - -#if QNX4 - #include -#endif - #include - -/********************************** 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 deleted file mode 100644 index 9d2afe5306..0000000000 --- a/source4/lib/appweb/ejs/var.c +++ /dev/null @@ -1,2197 +0,0 @@ -/* - * @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 deleted file mode 100644 index 8ed13a4995..0000000000 --- a/source4/lib/appweb/ejs/var.h +++ /dev/null @@ -1,496 +0,0 @@ -/* - * @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 - */ -- cgit From 61b16caed8b0ae4ffbbbabfc007a6e3c19b39227 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 14 Jul 2005 13:17:14 +0000 Subject: r8466: it is not portable to assert() a va_list (it breaks on alpha for example) (This used to be commit 2a64ee4227840de4aebe2ebe1887dfd167831738) --- source4/lib/appweb/ejs/ejsLib.c | 1 - 1 file changed, 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c index caae5b6495..b3e61ec375 100644 --- a/source4/lib/appweb/ejs/ejsLib.c +++ b/source4/lib/appweb/ejs/ejsLib.c @@ -477,7 +477,6 @@ static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args) char *errbuf, *msgbuf; mprAssert(ep); - mprAssert(args); msgbuf = NULL; mprAllocVsprintf(&msgbuf, MPR_MAX_STRING, fmt, args); -- cgit From a0bc4da1a307753e3b28a11863f50d66b0894190 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 15 Jul 2005 07:18:23 +0000 Subject: r8483: switched our generated ejs rpc code over to the new OO interface. This means we don't pollute the name space, and also makes for faster startup times as we only create variables for the pipes that we use, not all pipes (This used to be commit 57d7a585e8162d21c7152952aa0cc7471968784f) --- source4/lib/appweb/ejs/ejsProcs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsProcs.c b/source4/lib/appweb/ejs/ejsProcs.c index c01f411161..adef6e898a 100644 --- a/source4/lib/appweb/ejs/ejsProcs.c +++ b/source4/lib/appweb/ejs/ejsProcs.c @@ -629,6 +629,7 @@ int ejsDefineStandardProperties(MprVar *obj) mprCreatePropertyValue(obj, "undefined", mprCreateUndefinedVar()); mprCreatePropertyValue(obj, "true", mprCreateBoolVar(1)); mprCreatePropertyValue(obj, "false", mprCreateBoolVar(0)); + mprCreatePropertyValue(obj, "NULL", mprCreatePtrVar(NULL)); #if BLD_FEATURE_LEGACY_API /* -- cgit From 2d92eca39316a9edb17a1ffee0087f7365453471 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 19 Jul 2005 00:40:52 +0000 Subject: r8562: small merge with upstream (This used to be commit 6c9bf162afddda4747ef08b56598aaf0747a4d27) --- source4/lib/appweb/ejs/ejsParser.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c index 772ed574c5..452e7fde8f 100644 --- a/source4/lib/appweb/ejs/ejsParser.c +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -1280,6 +1280,10 @@ static int parseId(Ejs *ep, int state, int flags, char **id, char **fullName, tid = ejsLexGetToken(ep, state); if (tid == EJS_TOK_LPAREN) { + if (ep->currentProperty == 0 && (flags & EJS_FLAGS_EXE)) { + ejsError(ep, "Function name not defined \"%s\"\n", *id); + return -1; + } ejsLexPutbackToken(ep, EJS_TOK_FUNCTION_NAME, ep->token); return state; } @@ -1975,11 +1979,6 @@ static int evalFunction(Ejs *ep, MprVar *obj, int flags) 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. */ @@ -2179,7 +2178,6 @@ int ejsRunFunction(int eid, MprVar *obj, const char *functionName, MprVar *ejsFindObj(Ejs *ep, int state, const char *property, int flags) { - MprVar *vp; MprVar *obj; mprAssert(ep); @@ -2193,10 +2191,7 @@ MprVar *ejsFindObj(Ejs *ep, int state, const char *property, int flags) } 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)) { + if (mprGetProperty(ep->local, property, 0)) { obj = ep->local; } else { obj = ep->global; -- cgit From cd65f29fc68eb51d0ca7e3c598f875d2c9eafc35 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 19 Jul 2005 11:52:50 +0000 Subject: r8599: null terminate the argv list in string C functions (This used to be commit cabf638a31ab5f9b0e62e085d844d615c597bc2f) --- source4/lib/appweb/ejs/ejsParser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c index 452e7fde8f..e7c44f4d6c 100644 --- a/source4/lib/appweb/ejs/ejsParser.c +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -2003,10 +2003,11 @@ static int evalFunction(Ejs *ep, MprVar *obj, int flags) case MPR_TYPE_STRING_CFUNCTION: if (actualArgs->used > 0) { - argBuf = mprMalloc(actualArgs->used * sizeof(char*)); + argBuf = mprMalloc((1+actualArgs->used) * sizeof(char*)); for (i = 0; i < actualArgs->used; i++) { mprVarToString(&argBuf[i], MPR_MAX_STRING, 0, argValues[i]); } + argBuf[i] = NULL; } else { argBuf = 0; } -- cgit From 6f48b3a5cbe0235e60834f0b96a34290797d3bf4 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 20 Jul 2005 05:13:01 +0000 Subject: r8630: give a much nicer backtrace on assert() failures in ejs I will submit this upstream (This used to be commit a2266f6927d953459bd4b02fbca0a9c95c18494e) --- source4/lib/appweb/ejs/ejsInternal.h | 2 ++ source4/lib/appweb/ejs/ejsLex.c | 7 +++++-- source4/lib/appweb/ejs/ejsLib.c | 36 ++++++++++++++++++------------------ 3 files changed, 25 insertions(+), 20 deletions(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsInternal.h b/source4/lib/appweb/ejs/ejsInternal.h index 3bf99d88b9..8b66dafdca 100644 --- a/source4/lib/appweb/ejs/ejsInternal.h +++ b/source4/lib/appweb/ejs/ejsInternal.h @@ -192,6 +192,8 @@ typedef struct ejEval { char *tokEndp; /* Pointer past end of token */ char *tokServp; /* Pointer to next token char */ int tokSize; /* Size of token buffer */ + struct ejEval *next; /* used for backtraces */ + const char *procName; /* gives name in backtrace */ } EjsInput; /* diff --git a/source4/lib/appweb/ejs/ejsLex.c b/source4/lib/appweb/ejs/ejsLex.c index a5f15c2979..b4617a638e 100644 --- a/source4/lib/appweb/ejs/ejsLex.c +++ b/source4/lib/appweb/ejs/ejsLex.c @@ -60,11 +60,13 @@ int ejsLexOpenScript(Ejs *ep, char *script) mprAssert(ep); mprAssert(script); - if ((ep->input = mprMalloc(sizeof(EjsInput))) == NULL) { + if ((ip = mprMalloc(sizeof(EjsInput))) == NULL) { return -1; } - ip = ep->input; memset(ip, 0, sizeof(*ip)); + ip->next = ep->input; + ep->input = ip; + ip->procName = ep->proc?ep->proc->procName:NULL; /* * Create the parse token buffer and script buffer @@ -102,6 +104,7 @@ void ejsLexCloseScript(Ejs *ep) ip = ep->input; mprAssert(ip); + ep->input = ip->next; for (i = 0; i < EJS_TOKEN_STACK; i++) { mprFree(ip->putBack[i].token); diff --git a/source4/lib/appweb/ejs/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c index b3e61ec375..eed6d67fb4 100644 --- a/source4/lib/appweb/ejs/ejsLib.c +++ b/source4/lib/appweb/ejs/ejsLib.c @@ -385,7 +385,6 @@ int ejsEvalBlock(EjsId eid, char *script, MprVar *vp, char **emsg) int ejsEvalScript(EjsId eid, char *script, MprVar *vp, char **emsg) { Ejs *ep; - EjsInput *oldBlock; int state; void *endlessLoopTest; int loopCounter; @@ -408,7 +407,6 @@ int ejsEvalScript(EjsId eid, char *script, MprVar *vp, char **emsg) /* * Allocate a new evaluation block, and save the old one */ - oldBlock = ep->input; ejsLexOpenScript(ep, script); /* @@ -447,11 +445,6 @@ int ejsEvalScript(EjsId eid, char *script, MprVar *vp, char **emsg) *emsg = mprStrdup(ep->error); } - /* - * Restore the old evaluation block - */ - ep->input = oldBlock; - if (state == EJS_STATE_ERR) { return -1; } @@ -475,24 +468,31 @@ static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args) { EjsInput *ip; char *errbuf, *msgbuf; + int frame = 0; mprAssert(ep); 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; + ip = ep->input; + mprAllocSprintf(&errbuf, MPR_MAX_STRING, "%s\nBacktrace:\n", msgbuf); + + /* form a backtrace */ + while (ip) { + char *msg2, *ebuf2; + mprAllocSprintf(&msg2, MPR_MAX_STRING, + "\t[%2d] %20s:%-4d -> %s\n", + frame++, ip->procName?ip->procName:"", ip->lineNumber, ip->line); + ebuf2 = mprRealloc(errbuf, strlen(errbuf) + strlen(msg2) + 1); + if (ebuf2 == NULL) break; + errbuf = ebuf2; + memcpy(errbuf+strlen(errbuf), msg2, strlen(msg2)+1); + mprFree(msg2); + ip = ip->next; } + mprFree(ep->error); + ep->error = errbuf; mprFree(msgbuf); } -- cgit From 3473f6b74b4545d09234d198468b0229c621b1cb Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 20 Jul 2005 05:40:24 +0000 Subject: r8631: give an error on incorrect argument count (This used to be commit ed09e19794d9625553bc48892a2e08b84d7bdcf4) --- source4/lib/appweb/ejs/ejsParser.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c index e7c44f4d6c..1f110e7ac4 100644 --- a/source4/lib/appweb/ejs/ejsParser.c +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -2059,12 +2059,11 @@ static int evalFunction(Ejs *ep, MprVar *obj, int flags) 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); + 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 -- cgit From 5e9988708f30ce093ab0f48ebb02787c9e418331 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 20 Jul 2005 11:55:20 +0000 Subject: r8651: fixed a boolean expression bug (submitted upstream) (This used to be commit bd4c388cd2653823e012f1cd8c14832ee032eb11) --- source4/lib/appweb/ejs/ejsParser.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c index 1f110e7ac4..dadd3eac11 100644 --- a/source4/lib/appweb/ejs/ejsParser.c +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -1641,6 +1641,9 @@ static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) case EJS_EXPR_NOTEQ: lval = lhs->type != rhs->type; break; + case EJS_EXPR_BOOL_COMP: + lval = ! mprVarToBool(rhs); + break; default: lval = 0; } -- cgit From d55b75d8336326a2f8aaad2144e53d8b821a3b21 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 22 Jul 2005 01:56:51 +0000 Subject: r8696: fixed ejs to more strictly follow the va_list rules. Might fix that pesky panic on x86_64 (This used to be commit 80177b29f4b32996d41a87df041c4c1bfb6876c2) --- source4/lib/appweb/ejs/ejsParser.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c index dadd3eac11..5b8b84f7b3 100644 --- a/source4/lib/appweb/ejs/ejsParser.c +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -230,7 +230,7 @@ static int parseStmt(Ejs *ep, int state, int flags) #if BLD_DEBUG fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, fullNameLen, - 0, ".", 0); + 0, ".", NULL); #endif ep->currentProperty = vp; @@ -266,7 +266,7 @@ static int parseStmt(Ejs *ep, int state, int flags) * If not executing yet, id may not be known */ fullNameLen = mprReallocStrcat(&fullName, MPR_MAX_VAR, - fullNameLen, 0, "[", id, "]", 0); + fullNameLen, 0, "[", id, "]", NULL); } #endif @@ -1258,7 +1258,7 @@ static int parseId(Ejs *ep, int state, int flags, char **id, char **fullName, *id = mprStrdup(ep->token); #if BLD_DEBUG *fullNameLen = mprReallocStrcat(fullName, MPR_MAX_VAR, *fullNameLen, - 0, *id, 0); + 0, *id, NULL); #endif if (ep->currentObj == 0) { ep->currentObj = ejsFindObj(ep, state, *id, flags); -- cgit From f13fbd7e6fcdf8fe6de2d9be2fd453fe22fc5e16 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 7 Aug 2005 06:16:32 +0000 Subject: r9173: catch ep->local being NULL (This used to be commit 9f75bc3ca061abbfeb6ac9fc60834b8c1f6e2c4d) --- source4/lib/appweb/ejs/ejsLib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c index eed6d67fb4..b24b2b013c 100644 --- a/source4/lib/appweb/ejs/ejsLib.c +++ b/source4/lib/appweb/ejs/ejsLib.c @@ -211,7 +211,9 @@ void ejsCloseEngine(EjsId eid) mprDestroyVar(&ep->result); mprDestroyVar(&ep->tokenNumber); - mprDeleteProperty(ep->local, "local"); + if (ep->local) { + mprDeleteProperty(ep->local, "local"); + } mprDeleteProperty(ep->global, "this"); mprDeleteProperty(ep->global, "global"); -- cgit From a663d2110678b6c059737eecf7929cd5e5d46160 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 7 Aug 2005 15:45:59 +0000 Subject: r9183: more workarounds for the global variables in ejs. I will discuss getting rid of these with the mbedthis people. (This used to be commit a5b9e74a5c23e724ae4ee222e6f128133b175494) --- source4/lib/appweb/ejs/ejs.h | 3 +++ source4/lib/appweb/ejs/ejsLib.c | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejs.h b/source4/lib/appweb/ejs/ejs.h index 5efdb47192..c7b0c54d8e 100644 --- a/source4/lib/appweb/ejs/ejs.h +++ b/source4/lib/appweb/ejs/ejs.h @@ -71,6 +71,9 @@ extern void ejsClose(void); extern EjsId ejsOpenEngine(EjsHandle primaryHandle, EjsHandle altHandle); extern void ejsCloseEngine(EjsId eid); +void *ejs_save_state(void); +void ejs_restore_state(void *ptr); + /* * Evaluation functions */ diff --git a/source4/lib/appweb/ejs/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c index b24b2b013c..f6c004592b 100644 --- a/source4/lib/appweb/ejs/ejsLib.c +++ b/source4/lib/appweb/ejs/ejsLib.c @@ -58,6 +58,33 @@ static void *lockData; #define ejsUnlock() #endif + +/* + save/restore global ejs state - used to cope with simultaneous ejs requests + this is a workaround for the use of global variables in ejs +*/ +struct ejs_state_ctx { + struct MprVar master; + struct MprArray *ejsList; +}; + +void *ejs_save_state(void) +{ + struct ejs_state_ctx *ctx = talloc(talloc_autofree_context(), struct ejs_state_ctx); + ctx->master = master; + ctx->ejsList = ejsList; + return ctx; +} + +void ejs_restore_state(void *ptr) +{ + struct ejs_state_ctx *ctx = talloc_get_type(ptr, struct ejs_state_ctx); + master = ctx->master; + ejsList = ctx->ejsList; + talloc_free(ctx); +} + + /****************************** Forward Declarations **************************/ static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen); -- cgit From 1774019fc27869f01808ad8b0f8f85d03492d4b0 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 13 Aug 2005 03:16:03 +0000 Subject: r9299: fixed the evaluation of pointer expressions that evaluate to boolean (This used to be commit 17679676d4629ac81116ae1b102f1a311b5d4eed) --- source4/lib/appweb/ejs/ejsParser.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c index 5b8b84f7b3..871907dd10 100644 --- a/source4/lib/appweb/ejs/ejsParser.c +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -48,6 +48,7 @@ static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs); 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 evalPtrExpr(Ejs *ep, void *l, int rel, void *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); @@ -1670,8 +1671,8 @@ static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) return 0; case MPR_TYPE_PTR: - mprCopyVarValue(&ep->result, mprCreateBoolVar(lhs->ptr == rhs->ptr), 0); - return 0; + rc = evalPtrExpr(ep, lhs->ptr, rel, rhs->ptr); + break; case MPR_TYPE_BOOL: rc = evalBoolExpr(ep, lhs->boolean, rel, rhs->boolean); @@ -1806,6 +1807,28 @@ static int evalBoolExpr(Ejs *ep, bool l, int rel, bool r) return 0; } +static int evalPtrExpr(Ejs *ep, void *l, int rel, void *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 == NULL) ? 1 : 0; + break; + default: + ejsError(ep, "Bad operator %d", rel); + return -1; + } + mprCopyVarValue(&ep->result, mprCreateBoolVar(lval), 0); + return 0; +} + /******************************************************************************/ /* * Expressions with numeric operands -- cgit From 56aadf82af5e3c5feeeb0dc5a5c14fc6967b4336 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 16 Aug 2005 00:01:25 +0000 Subject: r9311: Missing null pointer check in ejsLib Found by coverity (This used to be commit ae7b073af3d690275dbfa11c8406963702c57d3b) --- source4/lib/appweb/ejs/ejsLib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c index f6c004592b..af5ce52731 100644 --- a/source4/lib/appweb/ejs/ejsLib.c +++ b/source4/lib/appweb/ejs/ejsLib.c @@ -327,7 +327,8 @@ int ejsEvalFile(EjsId eid, char *path, MprVar *result, char **emsg) * Error return */ error: - *emsg = mprStrdup(ep->error); + if(emsg) + *emsg = mprStrdup(ep->error); return -1; } -- cgit From 8f9478b09d39d8c13871684a6af3d3e1629a0e84 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 23 Aug 2005 05:21:04 +0000 Subject: r9504: use some low level ejs hackery to give much better exception error messages in both the web server and smbscript. We can now give backtraces for all internal asserts, not just high level errors (This used to be commit 84c756b25ccb2bd75360bdb9b7b7643975d1f3b3) --- source4/lib/appweb/ejs/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/config.h b/source4/lib/appweb/ejs/config.h index 320318a0b2..de8a1096c9 100644 --- a/source4/lib/appweb/ejs/config.h +++ b/source4/lib/appweb/ejs/config.h @@ -8,7 +8,7 @@ #define BLD_APPWEB_CONFIG "normal.conf" #define BLD_APPWEB 0 #define BLD_COMPANY "Mbedthis" -#define BLD_DEBUG 1 +#define BLD_DEBUG 0 #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" -- cgit From 2dc45bd4841a0b7ea640d9a41e381f4601809262 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 25 Aug 2005 00:57:21 +0000 Subject: r9600: fixed the intermittent failures we were getting with ejs in the build farm. (This used to be commit b23bffcba62df954c7fb439c78b962fbd262cc5e) --- source4/lib/appweb/ejs/ejsParser.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c index 871907dd10..da7b544c90 100644 --- a/source4/lib/appweb/ejs/ejsParser.c +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -1485,6 +1485,23 @@ static int evalCond(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) return 0; } + +/* + return true if this string is a valid number +*/ +static int string_is_number(const char *s) +{ + char *endptr = NULL; + if (s == NULL || *s == 0) { + return 0; + } + strtod(s, &endptr); + if (endptr != NULL && *endptr == 0) { + return 1; + } + return 0; +} + /******************************************************************************/ /* * Evaluate an operation. Returns with the result in ep->result. Returns -1 @@ -1533,6 +1550,24 @@ static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) /* Nothing more can be done */ } + /* undefined and null are special, in that they don't get promoted when + comparing */ + if (rel == EJS_EXPR_EQ || rel == EJS_EXPR_NOTEQ) { + if (lhs->type == MPR_TYPE_UNDEFINED || rhs->type == MPR_TYPE_UNDEFINED) { + return evalBoolExpr(ep, + lhs->type == MPR_TYPE_UNDEFINED, + rel, + rhs->type == MPR_TYPE_UNDEFINED); + } + + if (lhs->type == MPR_TYPE_NULL || rhs->type == MPR_TYPE_NULL) { + return evalBoolExpr(ep, + lhs->type == MPR_TYPE_NULL, + rel, + rhs->type == MPR_TYPE_NULL); + } + } + /* * From here on, lhs and rhs may contain allocated data (strings), so * we must always destroy before overwriting. @@ -1556,7 +1591,7 @@ static int evalExpr(Ejs *ep, MprVar *lhs, int rel, MprVar *rhs) */ if (lhs->type != rhs->type) { if (lhs->type == MPR_TYPE_STRING) { - if (isdigit((int) lhs->string[0])) { + if (string_is_number(lhs->string)) { num = mprVarToNumber(lhs); lhs->allocatedVar = 0; mprDestroyVar(lhs); -- cgit From 6812c73534001d2dd05a9a74358d2b6d0029f1a7 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Tue, 20 Sep 2005 11:59:03 +0000 Subject: r10348: Add scons scripts for remaining subsystems. Most subsystems build now, but final linking still fails (as does generating files asn1, et, idl and proto files) (This used to be commit 4f0d7f75b99c7f4388d8acb0838577d86baf68b5) --- source4/lib/appweb/ejs/config.mk | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 source4/lib/appweb/ejs/config.mk (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/config.mk b/source4/lib/appweb/ejs/config.mk deleted file mode 100644 index f2c0e62f1e..0000000000 --- a/source4/lib/appweb/ejs/config.mk +++ /dev/null @@ -1,13 +0,0 @@ -####################### -# 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 -####################### -- cgit From 5f6a3213e254a9ef178fc756b72badd5823811b2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 15 Oct 2005 09:28:56 +0000 Subject: r11084: - allow hex numbers with 'a'...'f' digits to be parsed - parse hex numbers correct tridge: how could we submit this to the upstream appweb library? metze (This used to be commit 70cde83c134f2d8bb2f6c0649b7f87a8846e63a4) --- source4/lib/appweb/ejs/ejsLex.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsLex.c b/source4/lib/appweb/ejs/ejsLex.c index b4617a638e..b9a363cfc9 100644 --- a/source4/lib/appweb/ejs/ejsLex.c +++ b/source4/lib/appweb/ejs/ejsLex.c @@ -633,12 +633,19 @@ static int getLexicalToken(Ejs *ep, int state) break; } if (tolower(c) == 'x') { - if (tokenAddChar(ep, c) < 0) { - return EJS_TOK_ERR; - } - if ((c = inputGetc(ep)) < 0) { - break; - } + do { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + } while (isdigit(c) || (tolower(c) >= 'a' && tolower(c) <= 'f')); + + mprDestroyVar(&ep->tokenNumber); + ep->tokenNumber = mprParseVar(ep->token, type); + inputPutback(ep, c); + return EJS_TOK_NUMBER; } if (! isdigit(c)) { #if BLD_FEATURE_FLOATING_POINT -- cgit From 9aa3c44f70e1102be64c503da43718b2c187dde3 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 2 Nov 2005 00:59:01 +0000 Subject: r11456: fixed a ejs parser bug for delete() statements (This used to be commit b8694c58f528d9da66cd623076282caece39d8a7) --- source4/lib/appweb/ejs/ejsParser.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsParser.c b/source4/lib/appweb/ejs/ejsParser.c index da7b544c90..da922a5728 100644 --- a/source4/lib/appweb/ejs/ejsParser.c +++ b/source4/lib/appweb/ejs/ejsParser.c @@ -312,7 +312,9 @@ static int parseStmt(Ejs *ep, int state, int flags) flags | EJS_FLAGS_DELETE) != EJS_STATE_EXPR_DONE) { goto error; } - mprDeleteProperty(ep->currentObj, ep->currentProperty->name); + if (flags & EJS_FLAGS_EXE) { + mprDeleteProperty(ep->currentObj, ep->currentProperty->name); + } done++; break; -- cgit From cf0f4ec073b694e43daeeddf9aab51cd3e51fd2b Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sun, 30 Apr 2006 13:54:03 +0000 Subject: r15358: Fix some compiler warnings / type safety. Found by tcc (This used to be commit 12ba42de5886f9f4f9b1698476557e0c217d06f3) --- source4/lib/appweb/ejs/ejsLib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c index af5ce52731..0dfc2e0ed2 100644 --- a/source4/lib/appweb/ejs/ejsLib.c +++ b/source4/lib/appweb/ejs/ejsLib.c @@ -64,8 +64,8 @@ static void *lockData; this is a workaround for the use of global variables in ejs */ struct ejs_state_ctx { - struct MprVar master; - struct MprArray *ejsList; + MprVar master; + MprArray *ejsList; }; void *ejs_save_state(void) -- cgit From 4b74445676cb601e16e96d70224604d9bd6248c2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 2 Jun 2006 08:44:31 +0000 Subject: r16002: fix the build on solaris Patch from Bjoern Jacke, thanks! metze (This used to be commit 1250355909533e033f30e6a960c58cfc9ec760ba) --- source4/lib/appweb/ejs/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/config.h b/source4/lib/appweb/ejs/config.h index de8a1096c9..feec3c9e5b 100644 --- a/source4/lib/appweb/ejs/config.h +++ b/source4/lib/appweb/ejs/config.h @@ -58,7 +58,7 @@ #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 int64_t #define BLD_FEATURE_NUM_TYPE_ID MPR_TYPE_INT64 #define BLD_FEATURE_ROMFS 0 #define BLD_FEATURE_RUN_AS_SERVICE 0 -- cgit From deca12d7be900dbf86065867840ae5500a398ae2 Mon Sep 17 00:00:00 2001 From: Derrell Lipman Date: Mon, 2 Oct 2006 02:38:43 +0000 Subject: r19021: match the prototype for a trigger function (This used to be commit f2f191891582af7dcb6d38c14e4ef7afc361299a) --- source4/lib/appweb/ejs/ejsProcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsProcs.c b/source4/lib/appweb/ejs/ejsProcs.c index adef6e898a..43fff4a40b 100644 --- a/source4/lib/appweb/ejs/ejsProcs.c +++ b/source4/lib/appweb/ejs/ejsProcs.c @@ -59,7 +59,7 @@ static int valueOfProc(EjsHandle eid, int argc, MprVar **argv); */ static MprVarTriggerStatus lengthTrigger(MprVarTriggerOp op, MprProperties *parentProperties, MprVar *prop, MprVar *newValue, - bool copyRef); + int copyRef); /******************************************************************************/ /* @@ -340,7 +340,7 @@ static int valueOfProc(EjsHandle eid, int argc, MprVar **argv) static MprVarTriggerStatus lengthTrigger(MprVarTriggerOp op, MprProperties *parentProperties, MprVar *prop, MprVar *newValue, - bool copyRef) + int copyRef) { switch (op) { case MPR_VAR_READ: -- cgit From 43470b5ec3d451aa75acf2cda40cf2dcc019efab Mon Sep 17 00:00:00 2001 From: Derrell Lipman Date: Sun, 31 Dec 2006 20:05:29 +0000 Subject: r20444: WEB Application framework / SWAT. We're now at the stage where the web application framework should build and install automatically. Derrell (This used to be commit 0201baef46c1701007e0a4cdd95edee287939318) --- source4/lib/appweb/ejs/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/config.h b/source4/lib/appweb/ejs/config.h index feec3c9e5b..8c06d28147 100644 --- a/source4/lib/appweb/ejs/config.h +++ b/source4/lib/appweb/ejs/config.h @@ -1,5 +1,5 @@ #define BLD_PRODUCT "Samba4" -#define BLD_NAME "Samba4 SWAT" +#define BLD_NAME "Samba4 WEB Applications" #define BLD_VERSION "4" #define BLD_NUMBER "1" #define BLD_TYPE "DEBUG" -- cgit From 98b57d5eb61094a9c88e2f7d90d3e21b7e74e9d8 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sat, 8 Sep 2007 16:46:30 +0000 Subject: r25035: Fix some more warnings, use service pointer rather than service number in more places. (This used to be commit df9cebcb97e20564359097148665bd519f31bc6f) --- source4/lib/appweb/ejs/ejsLib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/lib/appweb/ejs') diff --git a/source4/lib/appweb/ejs/ejsLib.c b/source4/lib/appweb/ejs/ejsLib.c index 0dfc2e0ed2..67d0a4e760 100644 --- a/source4/lib/appweb/ejs/ejsLib.c +++ b/source4/lib/appweb/ejs/ejsLib.c @@ -154,7 +154,7 @@ EjsId ejsOpenEngine(EjsHandle primaryHandle, EjsHandle altHandle) MprVar *np; Ejs *ep; - ep = mprMalloc(sizeof(Ejs)); + ep = (Ejs *)mprMalloc(sizeof(Ejs)); if (ep == 0) { return (EjsId) -1; } -- cgit