diff options
Diffstat (limited to 'source4/lib')
80 files changed, 33339 insertions, 0 deletions
diff --git a/source4/lib/appweb/ejs-2.0/.bashrc b/source4/lib/appweb/ejs-2.0/.bashrc new file mode 100644 index 0000000000..c05ee0e6e8 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/.bashrc @@ -0,0 +1,153 @@ +# +# .bashrc -- Login shell startup script for windows using Mbedthis winTools +# +# Copyright (c) Mbedthis Software, 2003-2005. All Rights Reserved. +# + +TERM=ansi +# +# Set the desired .NET Framework +# +# FRAMEWORK=v1.0.3705 +FRAMEWORK=v1.1.4322 +# FRAMEWORK=v2.0.40607 + +# +# Set the desired Microsoft C Compiler version +# +# PREFERRED_CC=VS2005 +# PREFERRED_CC=VS2003 +# PREFERRED_CC=VS.NET +PREFERRED_CC=VS6 + +# +# Set to 1 if VXWORKS support is required +# +# VXWORKS=1 + +HOME=`pwd` +if [ ! -x winTools -o ! -x winTools/cygpath.exe ] +then + echo "Can't find build tools. Install build tools in $HOME/winTools" +fi + +ROOT=`winTools/cygpath -u $HOMEDRIVE` +: ${ROOT:=C:/} +APPWEB_PATH="${HOME}/bin/DEBUG:${HOME}/bin/RELEASE:${HOME}/bin:${HOME}/winTools" +CDPATH=".:${HOME}:${HOME}/http:${HOME}/http/modules:${HOME}/packages" +PS1="$ " + +export CDPATH INCLUDE LIB LIBPATH PATH PS1 TERM + +echo -e "\n\n###################################################" +echo "Mbedthis AppWeb, Cygwin build tools." +echo "Using compiler: $PREFERRED_CC, .NET framework: $FRAMEWORK" +echo -e "###################################################" + +################################################################################ + +# +# Setup for Visual Studio and SDK +# +if [ $PREFERRED_CC == "VS2005" ] +then + # + # Visual Studio .NET 2005 defines. + # + CYNET="${ROOT}/Program Files/Microsoft Visual Studio 8" + DOSNET="C:/Program Files/Microsoft Visual Studio 8" + PATH="$APPWEB_PATH:$CYNET/Common7/IDE:$CYNET/VC/BIN:$CYNET/VC/VCPackages:$CYNET/Common7/Tools:$CYNET/Common7/Tools/bin:$CYNET/SDK/v2.0/bin:`cygpath -W`/Microsoft.NET/Framework/v2.0.40607:$CYNET/SDK/v2.0/bin:$PATH" + INCLUDE="$DOSNET/VC/ATLMFC/INCLUDE;$DOSNET/VC/INCLUDE;$DOSNET/VC/PlatformSDK/include;$DOSNET/SDK/v2.0/include;$INCLUDE" + LIB="$DOSNET/VC/ATLMFC/LIB;$DOSNET/VC/LIB;$DOSNET/VC/PlatformSDK/lib;$DOSNET/SDK/v2.0/lib;$LIB" + LIBPATH=c:/WINDOWS/Microsoft.NET/Framework/$FRAMEWORK +fi + +if [ $PREFERRED_CC == "VS2003" ] +then + # + # Visual Studio .NET 2003 defines. + # + CYNET="${ROOT}/Program Files/Microsoft Visual Studio .NET 2003" + DOSNET="C:/Program Files/Microsoft Visual Studio .NET 2003" + PATH="$APPWEB_PATH:$CYNET/Common7/IDE:$CYNET/VC7/BIN:$CYNET/Common7/Tools:$CYNET/Common7/Tools/bin/prerelease:$CYNET/Common7/Tools/bin:$CYNET/FrameworkSDK/bin:${ROOT}/WINDOWS/Microsoft.NET/Framework/$FRAMEWORK:$CYNET/SDK/v1.1/bin:$PATH" + INCLUDE="$DOSNET/VC7/ATLMFC/INCLUDE;$DOSNET/VC7/INCLUDE;$DOSNET/VC7/PlatformSDK/include/prerelease;$DOSNET/VC7/PlatformSDK/include;$DOSNET/FrameworkSDK/include;$INCLUDE" + LIB="$DOSNET/VC7/ATLMFC/LIB;$DOSNET/VC7/LIB;$DOSNET/VC7/PlatformSDK/lib/prerelease;$DOSNET/VC7/PlatformSDK/lib;$DOSNET/FrameworkSDK/lib;$LIB" +fi + + +if [ $PREFERRED_CC == "VS.NET" ] +then + # + # Visual Studio .NET defines. + # + CYNET="${ROOT}/Program Files/Microsoft Visual Studio .NET" + DOSNET="C:/Program Files/Microsoft Visual Studio .NET" + PATH="$APPWEB_PATH:$CYNET/Common7/IDE:$CYNET/VC7/BIN:$CYNET/Common7/Tools:$CYNET/Common7/Tools/bin/prerelease:$CYNET/Common7/Tools/bin:$CYNET/FrameworkSDK/bin:${ROOT}/WINDOWS/Microsoft.NET/Framework/$FRAMEWORK:$CYNET/SDK/v1.0/bin:$PATH" + INCLUDE="$DOSNET/VC7/ATLMFC/INCLUDE;$DOSNET/VC7/INCLUDE;$DOSNET/VC7/PlatformSDK/include/prerelease;$DOSNET/VC7/PlatformSDK/include;$DOSNET/FrameworkSDK/include;$INCLUDE" + LIB="$DOSNET/VC7/ATLMFC/LIB;$DOSNET/VC7/LIB;$DOSNET/VC7/PlatformSDK/lib/prerelease;$DOSNET/VC7/PlatformSDK/lib;$DOSNET/FrameworkSDK/lib;$LIB" +fi + + +if [ $PREFERRED_CC == "VS6" ] +then + # Visual Studio 6 defines. + # + CYNET="${ROOT}/Program Files/Microsoft Visual Studio" + DOSNET="C:/Program Files/Microsoft Visual Studio" + PATH="$APPWEB_PATH:$CYNET/Common/MSDev98/bin:$CYNET/VC98/BIN:$CYNET/Common/IDE:$CYNET/Common/Tools/WinNT:$CYNET/Common/Tools:$PATH" + INCLUDE="$DOSNET/VC98/ATLMFC/INCLUDE;$DOSNET/VC98/INCLUDE;$DOSNET/VC98/MFC/INCLUDE;$INCLUDE" + LIB="$DOSNET/VC98/LIB;$DOSNET/VC98/MFC/LIB;$LIB" +fi + +if [ $VXWORKS ] +then + # + # Required by VxWorks + # + WIND_BASE=C:/tornado + WIND_HOST_TYPE=x86-win32 + WIND_REGISTRY=coalsack + WIND_LMHOST=coalsack + BLD_VX_HOST=i386-wrs-vxworks + VX_TOOLS=`cygpath $WIND_BASE`/host/$WIND_HOST_TYPE + export WIND_BASE WIND_HOST_TYPE WIND_REGISTRY WIND_LMHOST BLD_VX_HOST + + # + # Use cygwin make and tools by preference + # + PATH="$APPWEB_PATH:$VX_TOOLS/bin:$PATH" +fi + +# +# Make required directories for CYGWIN +# +if [ ! -x /bin/bash.exe ] +then + DIR=`cygpath -w "$HOME/winTools"` + echo -e "\nCreating /bin" + echo Mounting \"${DIR}\" as /bin + mount -f -b "$DIR" /bin +fi + +if [ ! -x /tmp ] +then + mkdir -p tmp + DIR=`cygpath -w "$HOME/tmp"` + echo -e "\nCreating /tmp" + echo Mounting \"${DIR}\" as /tmp + mount -f -b "$DIR" /tmp +fi +echo + +################################################################################ +# +# Do a bit of validation +# +type cl 2>/dev/null >/dev/null +if [ $? -ne 0 ] +then + echo "Can't find compiler: cl. Check WIN/bashrc settings for PATH" +fi +set -o vi + +################################################################################ diff --git a/source4/lib/appweb/ejs-2.0/.exrc b/source4/lib/appweb/ejs-2.0/.exrc new file mode 100644 index 0000000000..dd37846529 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/.exrc @@ -0,0 +1 @@ +set ts=4 sw=4 diff --git a/source4/lib/appweb/ejs-2.0/.ignore b/source4/lib/appweb/ejs-2.0/.ignore new file mode 100644 index 0000000000..866f06c6c1 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/.ignore @@ -0,0 +1,57 @@ +*.class +*.dll +*.exe +*.exp +*.jar +*.lib +*.lnk +*.map +*.new +*.old +*.pdb +*.res +*.sln +*.suo +*.sym +*.tmp +*.sav +.ICEauthority +.bash_history +.changes +.cvsignore +.esd_auth +.fonts.cache-1 +.viminfo +make.dep +thumbs.db +vc70.pdb +*.ncb +*.opt +*.idb +*.pch +*.dep +*.aps +all +tmp +make.vars +sh.vars +config.make +config.h +config.sh +configure +make.os +.firstBuild +.viminfo +.ssh +_viminfo +buildConfig.h +buildConfig.make +buildConfig.sh +.ccache +appWeb.kdev* +subversion +.vimrc +.subversion +.ignore +njs +msvc diff --git a/source4/lib/appweb/ejs-2.0/.loginrc b/source4/lib/appweb/ejs-2.0/.loginrc new file mode 100644 index 0000000000..90b76a2ad0 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/.loginrc @@ -0,0 +1,218 @@ +# +# .loginrc -- Michael's login shell startup script (used only for Windows) +# +# NOTE: this should not be distributed with releases. +# +# Copyright (c) Mbedthis Software, 2003-2005. All Rights Reserved. +# +HOME=`pwd` + +TERM=ansi +CYROOT=/cygdrive/c +JDK="/cygdrive/c/program files/java/jdk1.5.0_07/bin" +H=$CYROOT +R=usr/svn/appWeb/main +CDPATH=".:${H}/usr/svn/appWeb:${H}/${R}/http:${H}/${R}/http/modules:${H}/${R}:${H}/usr/svn/appWeb/releases:${H}/usr/svn/packages:${H}/usr/svn/bling/player/trunk:${H}/usr/svn/bling/player/trunk/src:${H}/usr/svn/bling/player/trunk/appdir:${H}/usr/svn:${H}:${H}/usr/svn:${H}/usr" +APPWEB_PATH="${H}/${R}/bin/DEBUG:${H}/${R}/bin/RELEASE:${H}/${R}/bin:${H}/usr/bin" +PATH="${H}/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:$PATH:${H}/tcl/bin:${JDK}" + +CLASSPATH="c:/usr/svn/j/ggperf" +PS1='`cygpath -m $PWD`> ' +SVN_EDITOR=C:/cygwin/bin/vim.exe + +umask 022 + +export TERM CDPATH INCLUDE LIB LIBPATH PATH PS1 SVN_EDITOR MSCL CLASSPATH + +################################################################################ +# +# Set the dev environment PATH and other critical environment variables +# + +# export BLD_ALTERNATE_CONFIG=WIN/buildConfig + +# +# Desired .NET Framework +# +# FRAMEWORK=v1.0.3705 +# FRAMEWORK=v1.1.4322 +FRAMEWORK=v2.0.50727 + +# +# Desired C Compiler +# +# MSCL=VS2005 +# MSCL=VS2003 +# MSCL=VS.NET +MSCL=VS6 +VXWORKS=1 + +echo "Using compiler: $MSCL, .NET framework: $FRAMEWORK" + +# +# Setup for Visual Studio and SDK +# +if [ $MSCL == "VS2005" ] +then + # + # Visual Studio .NET 2005 defines. + # + CYNET="$H/Program Files/Microsoft Visual Studio 8" + DOSNET="C:/Program Files/Microsoft Visual Studio 8" + PATH="$APPWEB_PATH:$CYNET/Common7/IDE:$CYNET/VC/BIN:$CYNET/VC/VCPackages:$CYNET/Common7/Tools:$CYNET/Common7/Tools/bin:$CYNET/SDK/v2.0/bin:$CYROOT/WINDOWS/Microsoft.NET/Framework/v2.0.40607:$CYNET/SDK/v2.0/bin:$PATH" + INCLUDE="$DOSNET/VC/ATLMFC/INCLUDE;$DOSNET/VC/INCLUDE;$DOSNET/VC/PlatformSDK/include;$DOSNET/SDK/v2.0/include;$INCLUDE" + LIB="$DOSNET/VC/ATLMFC/LIB;$DOSNET/VC/LIB;$DOSNET/VC/PlatformSDK/lib;$DOSNET/SDK/v2.0/lib;$LIB" + LIBPATH=c:/WINDOWS/Microsoft.NET/Framework/$FRAMEWORK + + # MOB -- old + # PATH="$APPWEB_PATH:$CYNET/Common7/IDE:$CYNET/VC/BIN:$CYNET/Common7/Tools:$CYNET/Common7/Tools/bin/prerelease:$CYNET/Common7/Tools/bin:$CYNET/FrameworkSDK/bin:$H/WINDOWS/Microsoft.NET/Framework/$FRAMEWORK:$PATH" + # INCLUDE="$DOSNET/VC/ATLMFC/INCLUDE;$DOSNET/VC/INCLUDE;$DOSNET/VC/PlatformSDK/include/prerelease;$DOSNET/VC/PlatformSDK/include;$DOSNET/FrameworkSDK/include;$INCLUDE" + # LIB="$DOSNET/VC/ATLMFC/LIB;$DOSNET/VC/LIB;$DOSNET/VC/PlatformSDK/lib/prerelease;$DOSNET/VC/PlatformSDK/lib;$DOSNET/FrameworkSDK/lib;$LIB" +fi + +if [ $MSCL == "VS2003" ] +then + # + # Visual Studio .NET 2003 defines. + # + CYNET="$H/Program Files/Microsoft Visual Studio .NET 2003" + DOSNET="C:/Program Files/Microsoft Visual Studio .NET 2003" + PATH="$APPWEB_PATH:$CYNET/Common7/IDE:$CYNET/VC7/BIN:$CYNET/Common7/Tools:$CYNET/Common7/Tools/bin/prerelease:$CYNET/Common7/Tools/bin:$CYNET/FrameworkSDK/bin:$H/WINDOWS/Microsoft.NET/Framework/$FRAMEWORK:$CYNET/SDK/v1.1/bin:$PATH" + INCLUDE="$DOSNET/VC7/ATLMFC/INCLUDE;$DOSNET/VC7/INCLUDE;$DOSNET/VC7/PlatformSDK/include/prerelease;$DOSNET/VC7/PlatformSDK/include;$DOSNET/FrameworkSDK/include;$INCLUDE" + LIB="$DOSNET/VC7/ATLMFC/LIB;$DOSNET/VC7/LIB;$DOSNET/VC7/PlatformSDK/lib/prerelease;$DOSNET/VC7/PlatformSDK/lib;$DOSNET/FrameworkSDK/lib;$LIB" +fi + + +if [ $MSCL == "VS.NET" ] +then + # + # Visual Studio .NET defines. + # + CYNET="$H/Program Files/Microsoft Visual Studio .NET" + DOSNET="C:/Program Files/Microsoft Visual Studio .NET" + PATH="$APPWEB_PATH:$CYNET/Common7/IDE:$CYNET/VC7/BIN:$CYNET/Common7/Tools:$CYNET/Common7/Tools/bin/prerelease:$CYNET/Common7/Tools/bin:$CYNET/FrameworkSDK/bin:$H/WINDOWS/Microsoft.NET/Framework/$FRAMEWORK:$CYNET/SDK/v1.0/bin:$PATH" + INCLUDE="$DOSNET/VC7/ATLMFC/INCLUDE;$DOSNET/VC7/INCLUDE;$DOSNET/VC7/PlatformSDK/include/prerelease;$DOSNET/VC7/PlatformSDK/include;$DOSNET/FrameworkSDK/include;$INCLUDE" + LIB="$DOSNET/VC7/ATLMFC/LIB;$DOSNET/VC7/LIB;$DOSNET/VC7/PlatformSDK/lib/prerelease;$DOSNET/VC7/PlatformSDK/lib;$DOSNET/FrameworkSDK/lib;$LIB" +fi + + +if [ $MSCL == "VS6" ] +then + # Visual Studio 6 defines. + # + CYNET="$H/Program Files/Microsoft Visual Studio" + DOSNET="C:/Program Files/Microsoft Visual Studio" + PATH="$APPWEB_PATH:$CYNET/Common/MSDev98/bin:$CYNET/VC98/BIN:$CYNET/Common/IDE:$CYNET/Common/Tools/WinNT:$CYNET/Common/Tools:$PATH" + # OLD PATH="$APPWEB_PATH:$CYNET/Common/IDE:$CYNET/VC98/BIN:$CYNET/Common/MSDev98/bin:$CYNET/Common/Tools:$CYNET/Common/Tools/bin/prerelease:$CYNET/Common/Tools/bin:$CYNET/FrameworkSDK/bin:$H/WINDOWS/Microsoft.NET/Framework/$FRAMEWORK:$PATH" + INCLUDE="$DOSNET/VC98/ATLMFC/INCLUDE;$DOSNET/VC98/INCLUDE;$DOSNET/VC98/MFC/INCLUDE;$INCLUDE" + LIB="$DOSNET/VC98/LIB;$DOSNET/VC98/MFC/LIB;$LIB" +fi + +if [ $VXWORKS ] +then + # + # Required by VxWorks + # + WIND_BASE=C:/tornado + WIND_HOST_TYPE=x86-win32 + WIND_REGISTRY=coalsack + WIND_LMHOST=coalsack + BLD_VX_HOST=i386-wrs-vxworks + export WIND_BASE WIND_HOST_TYPE WIND_REGISTRY WIND_LMHOST BLD_VX_HOST + + VX_TOOLS=`cygpath $WIND_BASE`/host/$WIND_HOST_TYPE + # + # Use cygwin make and tools by preference + # + PATH="$APPWEB_PATH:$PATH:$VX_TOOLS/bin" +fi + +# +# Make required directories for CYGWIN +# +if [ ! -x /bin/bash.exe ] +then + DIR=`cygpath -w "$HOME/winTools"` + echo -e "\nCreating /bin" + echo Mounting \"${DIR}\" as /bin + mount -f -b "$DIR" /bin +fi + +if [ ! -x /tmp ] +then + mkdir -p tmp + DIR=`cygpath -w "$HOME/tmp"` + echo -e "\nCreating /tmp" + echo Mounting \"${DIR}\" as /tmp + mount -f -b "$DIR" /tmp +fi +echo + + +################################################################################ +# +# Do a bit of validation (MOB -- extend) +# +type cl 2>/dev/null >/dev/null +if [ $? -ne 0 ] +then + echo "Can't find compiler: cl. Check WIN/bashrc settings for PATH" +fi + +################################################################################ +# +# Some convenient functions +# +pvi () { + + pattern=$1 + shift + files=$* + if [ -z "${files}" ] + then + files='*.c *.cpp *.h Makefile *.html *.aspx *.cs' + fi + vi -c "/${pattern}" $(grep -l "${pattern}" ${files}) +} + +################################################################################ + +g() { + pattern=$1 + shift + files=$* + if [ -z "${files}" ] + then + files=`echo *.c *.cpp *.h Makefile *.html *.aspx *.cs` + fi + eval grep "${pattern}" ${files} +} + +################################################################################ + +usedvi() { + pvi $1 $HOME/mr/*.c $HOME/mr/*.h $HOME/mr/WIN/*.c $HOME/lib/*/*.c +} + +################################################################################ + +alias ls='ls -CF $*' +alias lc='ls -CF $*' +alias lr='ls -R $*' +alias xwin='startxwin.sh' +alias htmlview='"C:/Program Files/Internet Explorer/iexplore.exe" $*' +set -o vi + +if [ `uname -o` = "Cygwin" ] +then + alias vim='"C:/Program Files/vim/vim64/vim.exe" $*' + alias gvim='"C:/Program Files/vim/vim64/gvim.exe" $*' +fi + +brew() { + "C:/Program Files/BREW SDK v2.1.3/Bin/BREW_Emulator.exe" +} + +js() { + cscript /nologo $* +} diff --git a/source4/lib/appweb/ejs-2.0/ejs/.ignore b/source4/lib/appweb/ejs-2.0/ejs/.ignore new file mode 100644 index 0000000000..47f4ac63b2 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/.ignore @@ -0,0 +1,2 @@ +ejs +future diff --git a/source4/lib/appweb/ejs-2.0/ejs/Makefile b/source4/lib/appweb/ejs-2.0/ejs/Makefile new file mode 100644 index 0000000000..ea6be8c401 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/Makefile @@ -0,0 +1,61 @@ +# +# Makefile for Embedded Javascript (EJS) +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +# +# Ejs may be linked into shared handlers so we must build the objects both +# shared and static if --shared was specified to configure. +# +COMPILE := *.c +EXPORT_OBJECTS := yes +PRE_DIRS := classes system db +MAKE_IFLAGS := -I../mpr -I../exml + +include make.dep + +ifeq ($(BLD_PRODUCT),ejs) +POST_DIRS := package +endif + +ifeq ($(BLD_FEATURE_TEST),1) +POST_DIRS += test +endif + +ifeq ($(BLD_FEATURE_EJS_DB),1) +LIBS += sqlite +endif + +TARGETS += $(BLD_BIN_DIR)/libejs$(BLD_LIB) +TARGETS += $(BLD_BIN_DIR)/ejs$(BLD_EXE) + +ifeq ($(BLD_FEATURE_EJS),1) +compileExtra: $(TARGETS) +endif + +$(BLD_BIN_DIR)/libejs$(BLD_LIB): files \ + $(shell BLD_OBJ=$(BLD_OBJ) \; BLD_OBJ_DIR=$(BLD_OBJ_DIR) \; \ + eval echo `cat files`) + @bld --library $(BLD_BIN_DIR)/libejs \ + --objectsDir $(BLD_OBJ_DIR) --objectList files \ + --libs "exml mpr $(LIBS)" + +$(BLD_BIN_DIR)/ejs$(BLD_EXE): $(BLD_BIN_DIR)/libejs$(BLD_LIB) \ + $(BLD_BIN_DIR)/libmpr$(BLD_LIB) \ + $(BLD_BIN_DIR)/libejs$(BLD_LIB) $(FILES) + @bld --executable $(BLD_BIN_DIR)/ejs$(BLD_EXE) \ + --rpath "$(BLD_PREFIX)/bin" \ + --preferStatic --smartLibs "ejs exml mpr $(LIBS)" \ + --objectsDir $(BLD_OBJ_DIR) \ + --objects "$(BLD_OBJ_DIR)/ejsCmd$(BLD_OBJ)" + +cleanExtra: + @echo "rm -f $(TARGETS)" | $(BLDOUT) + @rm -f $(TARGETS) + @rm -f $(BLD_BIN_DIR)/libejs.* + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/.ignore b/source4/lib/appweb/ejs-2.0/ejs/classes/.ignore new file mode 100644 index 0000000000..fb5a29031e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/.ignore @@ -0,0 +1 @@ +.updated diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/Makefile b/source4/lib/appweb/ejs-2.0/ejs/classes/Makefile new file mode 100644 index 0000000000..ce12bb3829 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/Makefile @@ -0,0 +1,21 @@ +# +# Makefile to build the EJS Classes +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I.. -I../../mpr -I../../exml + +include make.dep + +compileExtra: .updated + +.updated: $(FILES) + @touch .updated + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/classes/ejsArray.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsArray.c new file mode 100644 index 0000000000..feb64b1aa8 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsArray.c @@ -0,0 +1,167 @@ +/* + * @file ejsArray.c + * @brief Array class + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS + +/************************************ Code ************************************/ + +int ejsDefineArrayClass(Ejs *ep) +{ + if (ejsDefineClass(ep, "Array", "Object", ejsArrayConstructor) == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ +/* + * Routine to create the base array type + */ + +EjsVar *ejsCreateArrayInternal(EJS_LOC_DEC(ep, loc), int size) +{ + EjsProperty *pp; + EjsVar *obj, *vp; + + /* MOB -- need to supply hash size -- max(size, 503)); */ + + obj = ejsCreateSimpleObjInternal(EJS_LOC_PASS(ep, loc), "Array"); + if (obj == 0) { + mprAssert(0); + return obj; + } + obj->isArray = 1; + + /* MOB -- call constructor here and replace this code */ + + pp = ejsSetPropertyToInteger(ep, obj, "length", size); + ejsMakePropertyEnumerable(pp, 0); + + vp = ejsGetVarPtr(pp); + vp->isArrayLength = 1; + + return obj; +} + +/******************************************************************************/ + +EjsVar *ejsAddArrayElt(Ejs *ep, EjsVar *op, EjsVar *element, + EjsCopyDepth copyDepth) +{ + EjsProperty *pp; + EjsVar *vp; + char idx[16]; + int length; + + mprAssert(op->isArray); + + length = ejsGetPropertyAsInteger(ep, op, "length"); + + mprItoa(idx, sizeof(idx), length); + pp = ejsCreateProperty(ep, op, idx); + vp = ejsGetVarPtr(pp); + + ejsWriteVar(ep, vp, element, copyDepth); + + ejsSetPropertyToInteger(ep, op, "length", length + 1); + + return vp; +} + +/******************************************************************************/ +/* + * Constructor + */ + +int ejsArrayConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsProperty *pp; + EjsVar *vp; + char idx[16]; + int i, max; + + thisObj->isArray = 1; + max = 0; + + if (argc > 0) { + if (argc == 1 && ejsVarIsNumber(argv[0])) { + /* + * x = new Array(size); + */ + max = (int) ejsVarToInteger(argv[0]); + + } else { + /* + * x = new Array(element0, element1, ..., elementN): + */ + max = argc; + for (i = 0; i < max; i++) { + mprItoa(idx, sizeof(idx), i); + pp = ejsCreateSimpleProperty(ep, thisObj, idx); + vp = ejsGetVarPtr(pp); + ejsWriteVar(ep, vp, argv[i], EJS_SHALLOW_COPY); + } + } + } + + pp = ejsCreateSimpleProperty(ep, thisObj, "length"); + ejsMakePropertyEnumerable(pp, 0); + vp = ejsGetVarPtr(pp); + ejsWriteVarAsInteger(ep, vp, max); + vp->isArrayLength = 1; + + return 0; +} + +/******************************************************************************/ + +#else +void ejsArrayDummy() {} + +/******************************************************************************/ +#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-2.0/ejs/classes/ejsDate.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsDate.c new file mode 100755 index 0000000000..096316a822 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsDate.c @@ -0,0 +1,197 @@ +/* + * @file ejsStndClasses.c + * @brief EJS support methods + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS && 0 + +/******************************************************************************/ +/* + * Date constructor + + * + * Date(); + * Date(milliseconds); + * Date(dateString); + * Date(year, month, date); + * Date(year, month, date, hour, minute, second); + */ + +int ejsDateConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + return 0; +} + +/******************************************************************************/ + +static int load(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *fileName; + XmlState *parser; + Exml *xp; + MprFile *file; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ep, EJS_ARG_ERROR, "Bad args. Usage: load(fileName);"); + return -1; + } + fileName = argv[0]->string; + + /* FUTURE -- not romable + Need rom code in MPR not MprServices + */ + file = mprOpen(ep, fileName, O_RDONLY, 0664); + if (file == 0) { + ejsError(ep, EJS_IO_ERROR, "Can't open: %s", fileName); + return -1; + } + + xp = initParser(ep, thisObj, fileName); + parser = exmlGetParseArg(xp); + + exmlSetInputStream(xp, readFileData, (void*) file); + + if (exmlParse(xp) < 0) { + if (! ejsGotException(ep)) { + ejsError(ep, EJS_IO_ERROR, "Can't parse XML file: %s\nDetails %s", + fileName, exmlGetErrorMsg(xp)); + } + termParser(xp); + mprClose(file); + return -1; + } + + ejsSetReturnValue(ep, parser->nodeStack[0].obj); + + termParser(xp); + mprClose(file); + + return 0; +} + +/******************************************************************************/ + +int ejsDefineDateClass(Ejs *ep) +{ + EjsVar *dateClass; + + dateClass = ejsDefineClass(ep, "Date", "Object", ejsDateConstructor); + if (dateClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + ejsDefineCMethod(ep, dateClass, "getDate", xxxProc, EJS_NO_LOCAL); + + /* Returns "Friday" or 4 ? */ + ejsDefineCMethod(ep, dateClass, "getDay", xxxProc, EJS_NO_LOCAL); + + ejsDefineCMethod(ep, dateClass, "getMonth", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getFullYear", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getYear", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getHours", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getMinutes", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getSeconds", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getMilliseconds", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getTime", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "getTimeZoneOffset", xxxProc, EJS_NO_LOCAL); + + ejsDefineCMethod(ep, dateClass, "parse", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setDate", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setMonth", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setFullYear", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setYear", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setMinutes", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setSeconds", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setMilliseconds", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "setTime", xxxProc, EJS_NO_LOCAL); + + ejsDefineCMethod(ep, dateClass, "toString", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "toGMTString", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "toUTCString", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "toLocaleString", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "UTC", xxxProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, dateClass, "valueOf", xxxProc, EJS_NO_LOCAL); + /* + UTC: getUTCDate, getUTCDay, getUTCMonth, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCSeconds, getUTCMilliseconds + setUTCDate, setUTCDay, setUTCMonth, setUTCFullYear, setUTCHours, + setUTCMinutes, setUTCSeconds, setUTCMilliseconds + */ + + return ejsObjHasErrors(dateClass) ? MPR_ERR_CANT_INITIALIZE : 0; +} + +/******************************************************************************/ +/* + Time is since 1970/01/01 GMT + + Normal: Fri Feb 10 2006 05:06:44 GMT-0800 (Pacific Standard Time) + UTC: Sat, 11 Feb 2006 05:06:44 GMT + + // Using without New + + println(Date()); + + var myDate = new Date(); + myDate.setFullYear(2010, 0, 14); + + var today = new Date(); + + if (myDate > today) { + } else { + } + + + X=Date() should be equivalent to X=(new Date()).toString() + + */ +/******************************************************************************/ + +#else +void ejsStndClassesDummy() {} + +/******************************************************************************/ +#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-2.0/ejs/classes/ejsError.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsError.c new file mode 100755 index 0000000000..99445afc7c --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsError.c @@ -0,0 +1,140 @@ +/* + * @file ejsError.c + * @brief Error class + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS + +/************************************ Code ************************************/ +/* + * Parse the args and return the message. Convert non-string args using + * .toString. + */ + +static char *getMessage(Ejs *ep, int argc, EjsVar **argv) +{ + if (argc == 0) { + return ""; + + } else if (argc == 1) { + if (! ejsVarIsString(argv[0])) { + if (ejsRunMethod(ep, argv[0], "toString", 0) < 0) { + return 0; + } + return ep->result->string; + + } else { + return argv[0]->string; + } + + } else { + /* Don't call ejsError here or it will go recursive. */ + return 0; + } +} + + +/******************************************************************************/ +/* + * Error Constructor and also used for constructor for sub classes. + * + * Usage: new Error([message]) + */ + +int ejsErrorCons(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *msg, *stack; + + msg = getMessage(ep, argc, argv); + if (msg == 0) { + return -1; + } + + ejsSetPropertyToString(ep, thisObj, "name", ejsGetBaseClassName(thisObj)); + ejsSetPropertyToString(ep, thisObj, "message", msg); + + ejsSetPropertyToUndefined(ep, thisObj, "stack"); + + stack = ejsFormatStack(ep); + if (stack) { + ejsSetPropertyToString(ep, thisObj, "stack", stack); + mprFree(stack); + } + + if (ejsObjHasErrors(thisObj)) { + return -1; + } + + return 0; +} + +/******************************************************************************/ + +int ejsDefineErrorClasses(Ejs *ep) +{ + if (ejsDefineClass(ep, "Error", "Object", ejsErrorCons) == 0 || + ejsDefineClass(ep, "AssertError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "EvalError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "InternalError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "IOError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "MemoryError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "RangeError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "ReferenceError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "SyntaxError", "Error", ejsErrorCons) == 0 || + ejsDefineClass(ep, "TypeError", "Error", ejsErrorCons) == 0) { + + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ + +#else +void ejsErrorDummy() {} + +/******************************************************************************/ +#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-2.0/ejs/classes/ejsObject.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsObject.c new file mode 100644 index 0000000000..4f2e23beb2 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsObject.c @@ -0,0 +1,588 @@ +/* + * @file ejsObject.c + * @brief Object class + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS + +/****************************** Forward Declarations **************************/ +/* + * Support routines + */ + +static void formatVar(Ejs *ep, MprBuf *bp, EjsVar *vp); + +/******************************************************************************/ +/* + * Routine to create an object of the desired class. Class name may + * contain "." + * + * The created object will be a stand-alone class NOT entered into the + * properties of any other object. Callers must do this if required. ClassName + * may contain "." and is interpreted relative to "obj" if supplied. + * + * Note: this does not call the constructors for the various objects and base + * classes. + */ + +EjsVar *ejsCreateSimpleObjInternal(EJS_LOC_DEC(ep, loc), const char *className) +{ + EjsVar *baseClass; + + if (className && *className) { + baseClass = ejsGetClass(ep, 0, className); + if (baseClass == 0) { + mprError(ep, MPR_LOC, "Can't find base class %s", className); + return 0; + } + } else { + baseClass = 0; + } + + return ejsCreateSimpleObjUsingClassInt(EJS_LOC_PASS(ep, loc), + baseClass); +} + +/******************************************************************************/ +/* + * Create an object based upon the specified base class object. It will be a + * stand-alone class not entered into the properties of any other object. + * Callers must do this if required. + * + * Note: this does not call the constructors for the various objects and base + * classes. + */ + +EjsVar *ejsCreateSimpleObjUsingClassInt(EJS_LOC_DEC(ep, loc), + EjsVar *baseClass) +{ + EjsVar *vp; + + mprAssert(baseClass); + + if (baseClass == 0) { + mprError(ep, MPR_LOC, "Missing base class\n"); + return 0; + } + + vp = ejsCreateObjVarInternal(EJS_LOC_PASS(ep, loc)); + if (vp == 0) { + return vp; + } + + ejsSetBaseClass(vp, baseClass); + + /* + * This makes all internal method accesses faster + * NOTE: this code is duplicated in ejsCreateSimpleClass + */ + mprAssert(vp->objectState); + vp->objectState->methods = baseClass->objectState->methods; + + return vp; +} + +/******************************************************************************/ + +void ejsSetMethods(Ejs *ep, EjsVar *op) +{ + op->objectState->methods = ep->global->objectState->methods; +} + +/******************************************************************************/ +/******************************** Internal Methods ****************************/ +/******************************************************************************/ + +static EjsVar *createObjProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsGetVarPtr(ejsCreateSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static int deleteObjProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsDeleteProperty(ep, obj, property); +} + +/******************************************************************************/ + +static EjsVar *getObjProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsGetVarPtr(ejsGetSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ +/* + * Set the value of a property. Create if it does not exist + */ + +static EjsVar *setObjProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value) +{ + EjsProperty *pp; + EjsVar *vp; + + pp = ejsCreateSimpleProperty(ep, obj, property); + if (pp == 0) { + mprAssert(pp); + return 0; + } + vp = ejsGetVarPtr(pp); + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) < 0) { + mprAssert(0); + return 0; + } + return ejsGetVarPtr(pp); +} + +/******************************************************************************/ +/*********************************** Constructors *****************************/ +/******************************************************************************/ +#if UNUSED +/* + * Object constructor. We don't use this for speed. Think very carefully if + * you add an object constructor. + */ + +int ejsObjectConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + return 0; +} + +#endif +/******************************************************************************/ +/******************************** Visible Methods *****************************/ +/******************************************************************************/ + +static int cloneMethod(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int copyDepth; + + copyDepth = EJS_DEEP_COPY; + + if (argc == 1 && ejsVarToBoolean(argv[0])) { + copyDepth = EJS_RECURSIVE_DEEP_COPY; + } + + ejsWriteVar(ep, ep->result, thisObj, copyDepth); + + return 0; +} + +/******************************************************************************/ + +static int toStringMethod(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprBuf *bp; + int saveMaxDepth, saveDepth, saveFlags; + + saveMaxDepth = ep->maxDepth; + + if (argc >= 1) { + ep->maxDepth = ejsVarToInteger(argv[0]); + } else if (ep->maxDepth == 0) { + ep->maxDepth = MAXINT; + } + + saveFlags = ep->flags; + if (argc >= 2) { + if (ejsVarToBoolean(argv[1])) { + ep->flags |= EJS_FLAGS_ENUM_HIDDEN; + } + } + if (argc == 3) { + if (ejsVarToBoolean(argv[2])) { + ep->flags |= EJS_FLAGS_ENUM_BASE; + } + } + + bp = mprCreateBuf(ep, 0, 0); + + saveDepth = ep->depth; + + formatVar(ep, bp, thisObj); + + ep->depth = saveDepth; + ep->maxDepth = saveMaxDepth; + + mprAddNullToBuf(bp); + + ejsWriteVarAsString(ep, ep->result, mprGetBufStart(bp)); + mprFree(bp); + + ep->flags = saveFlags; + + return 0; +} + +/******************************************************************************/ + +static int valueOfMethod(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 0) { + mprAssert(0); + return -1; + } + + switch (thisObj->type) { + default: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_OBJECT: + case EJS_TYPE_METHOD: + case EJS_TYPE_STRING_CMETHOD: + ejsWriteVar(ep, ep->result, thisObj, EJS_SHALLOW_COPY); + break; + + case EJS_TYPE_STRING: + ejsWriteVarAsInteger(ep, ep->result, atoi(thisObj->string)); + break; + + case EJS_TYPE_BOOL: + case EJS_TYPE_INT: +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: +#endif +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: +#endif + ejsWriteVar(ep, ep->result, thisObj, EJS_SHALLOW_COPY); + break; + } + return 0; +} + +/******************************************************************************/ + +static int hashGetAccessor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ejs, (int) thisObj->objectState); + return 0; +} + +/******************************************************************************/ + +static int classGetAccessor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (thisObj->objectState == 0 || thisObj->objectState->baseClass == 0) { + ejsSetReturnValueToString(ejs, "object"); + } else { + ejsSetReturnValueToString(ejs, + thisObj->objectState->baseClass->objectState->className); + } + return 0; +} + +/******************************************************************************/ +/* + * Format an object. Called recursively to format properties and contained + * objects. + */ + +static void formatVar(Ejs *ep, MprBuf *bp, EjsVar *vp) +{ + EjsProperty *pp, *first; + EjsVar *propVar, *baseClass; + char *buf, *value; + int i; + + if (vp->type == EJS_TYPE_OBJECT) { + if (!vp->objectState->visited) { + + mprPutStringToBuf(bp, vp->isArray ? "[\n" : "{\n"); + + ep->depth++; + vp->objectState->visited = 1; + + if (ep->depth <= ep->maxDepth) { + first = ejsGetFirstProperty(vp, EJS_ENUM_ALL); + + if (ep->flags & EJS_FLAGS_ENUM_BASE) { + baseClass = vp->objectState->baseClass; + if (baseClass) { + for (i = 0; i < ep->depth; i++) { + mprPutStringToBuf(bp, " "); + } + mprPutStringToBuf(bp, baseClass->objectState->objName); + mprPutStringToBuf(bp, ": /* Base Class */ "); + if (baseClass->objectState == vp->objectState) { + value = "this"; + } else if (ejsRunMethodCmd(ep, baseClass, "toString", + "%d", ep->maxDepth) < 0) { + value = "[object Object]"; + } else { + mprAssert(ejsVarIsString(ep->result)); + value = ep->result->string; + } + mprPutStringToBuf(bp, value); + if (first) { + mprPutStringToBuf(bp, ",\n"); + } + } + } + + pp = first; + while (pp) { + if (! pp->dontEnumerate || + ep->flags & EJS_FLAGS_ENUM_HIDDEN) { + for (i = 0; i < ep->depth; i++) { + mprPutStringToBuf(bp, " "); + } + + if (! vp->isArray) { + mprPutStringToBuf(bp, pp->name); + mprPutStringToBuf(bp, ": "); + } + + propVar = ejsGetVarPtr(pp); + if (propVar->type == EJS_TYPE_OBJECT) { + if (pp->var.objectState == vp->objectState) { + value = "this"; + } else if (ejsRunMethodCmd(ep, propVar, + "toString", "%d", ep->maxDepth) < 0) { + value = "[object Object]"; + } else { + mprAssert(ejsVarIsString(ep->result)); + value = ep->result->string; + } + mprPutStringToBuf(bp, value); + + } else { + formatVar(ep, bp, &pp->var); + } + + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + if (pp) { + mprPutStringToBuf(bp, ",\n"); + } + } else { + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + } + } + vp->objectState->visited = 0; + + mprPutCharToBuf(bp, '\n'); + + ep->depth--; + for (i = 0; i < ep->depth; i++) { + mprPutStringToBuf(bp, " "); + } + mprPutCharToBuf(bp, vp->isArray ? ']' : '}'); + } + + } else if (vp->type == EJS_TYPE_METHOD) { + + mprPutStringToBuf(bp, "function ("); + for (i = 0; i < vp->method.args->length; i++) { + mprPutStringToBuf(bp, vp->method.args->items[i]); + if ((i + 1) < vp->method.args->length) { + mprPutStringToBuf(bp, ", "); + } + } + mprPutStringToBuf(bp, ") {"); + mprPutStringToBuf(bp, vp->method.body); + for (i = 0; i < ep->depth; i++) { + mprPutStringToBuf(bp, " "); + } + mprPutStringToBuf(bp, "}"); + + } else { + + if (vp->type == EJS_TYPE_STRING) { + mprPutCharToBuf(bp, '\"'); + } + + /* + * We don't use ejsVarToString for arrays, objects and strings. + * This is because ejsVarToString does not call "obj.toString" + * and it is not required for strings. + * MOB - rc + */ + buf = ejsVarToString(ep, vp); + mprPutStringToBuf(bp, buf); + + if (vp->type == EJS_TYPE_STRING) { + mprPutCharToBuf(bp, '\"'); + } + } +} + +/******************************************************************************/ +/* + * mixin code. Blends code at the "thisObj" level. + */ + +static int mixinMethod(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsProperty *pp; + char *buf; + int fid, i, rc; + + mprAssert(argv); + + /* + * Create a variable scope block set to the current object + */ + rc = 0; + fid = ejsSetBlock(ep, thisObj); + + for (i = 0; i < argc; i++) { + + if (ejsVarIsString(argv[i])) { + rc = ejsEvalScript(ep, argv[i]->string, 0); + + } else if (ejsVarIsObject(argv[i])) { + + /* MOB -- OPT. When we have proper scope chains, we should just + refer to the module and not copy */ + pp = ejsGetFirstProperty(argv[i], EJS_ENUM_ALL); + while (pp) { + ejsSetProperty(ep, thisObj, pp->name, ejsGetVarPtr(pp)); + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + + } else { + /* MOB - rc */ + buf = ejsVarToString(ep, argv[i]); + rc = ejsEvalScript(ep, buf, 0); + + } + if (rc < 0) { + ejsCloseBlock(ep, fid); + return -1; + } + } + ejsCloseBlock(ep, fid); + return 0; +} + +/******************************************************************************/ +/* + * Create the object class + */ + +int ejsDefineObjectClass(Ejs *ep) +{ + EjsMethods *methods; + EjsProperty *objectProp, *protoProp; + EjsVar *op, *globalClass; + + /* + * Must specially hand-craft the object class as it is the base class + * of all objects. + */ + op = ejsCreateObjVar(ep); + if (op == 0) { + return MPR_ERR_CANT_CREATE; + } + ejsSetClassName(ep, op, "Object"); + + /* + * Don't use a constructor for objects for speed + */ + ejsMakeClassNoConstructor(op); + + /* + * MOB -- should mark properties as public / private and class or instance. + */ + ejsDefineCMethod(ep, op, "clone", cloneMethod, EJS_NO_LOCAL); + ejsDefineCMethod(ep, op, "toString", toStringMethod, EJS_NO_LOCAL); + ejsDefineCMethod(ep, op, "valueOf", valueOfMethod, EJS_NO_LOCAL); + ejsDefineCMethod(ep, op, "mixin", mixinMethod, EJS_NO_LOCAL); + + ejsDefineCAccessors(ep, op, "hash", hashGetAccessor, 0, EJS_NO_LOCAL); + ejsDefineCAccessors(ep, op, "baseClass", classGetAccessor, 0, EJS_NO_LOCAL); + + /* + * MOB -- make this an accessor + */ + protoProp = ejsSetProperty(ep, op, "prototype", op); + if (protoProp == 0) { + ejsFreeVar(ep, op); + return MPR_ERR_CANT_CREATE; + } + + /* + * Setup the internal methods. Most classes will never override these. + * The XML class will. We rely on talloc to free internal. Use "ep" as + * the parent as we need "methods" to live while the interpreter lives. + */ + methods = mprAllocTypeZeroed(ep, EjsMethods); + op->objectState->methods = methods; + + methods->createProperty = createObjProperty; + methods->deleteProperty = deleteObjProperty; + methods->getProperty = getObjProperty; + methods->setProperty = setObjProperty; + + objectProp = ejsSetPropertyAndFree(ep, ep->global, "Object", op); + + /* + * Change the global class to use Object's methods + */ + globalClass = ep->service->globalClass; + globalClass->objectState->methods = methods; + globalClass->objectState->baseClass = ejsGetVarPtr(protoProp); + + ep->objectClass = ejsGetVarPtr(objectProp); + + if (ejsObjHasErrors(ejsGetVarPtr(objectProp))) { + ejsFreeVar(ep, op); + return MPR_ERR_CANT_CREATE; + } + return 0; +} + +/******************************************************************************/ + +#else +void ejsObjectDummy() {} + +/******************************************************************************/ +#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-2.0/ejs/classes/ejsStndClasses.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsStndClasses.c new file mode 100644 index 0000000000..fd6cda7813 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsStndClasses.c @@ -0,0 +1,144 @@ +/* + * @file ejsStndClasses.c + * @brief EJS support methods + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS + +/******************************************************************************/ +/******************************* Function Class *******************************/ +/******************************************************************************/ + +int ejsDefineFunctionClass(Ejs *ep) +{ + if (ejsDefineClass(ep, "Function", "Object", ejsFunctionConstructor) == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ +/* + * Function constructor + */ + +int ejsFunctionConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsArgError(ep, "Usage: Function(\"function (arg) { script };\");"); + } + + rc = ejsEvalScript(ep, argv[0]->string, 0); + + /* + * Note: this will convert the object into a method. It will cease to be + * an object. + */ + if (rc == 0 && ejsVarIsMethod(ep->result)) { + /* + * Must make thisObj collectable. + */ + ejsMakeObjPermanent(thisObj, 0); + ejsMakeObjLive(thisObj, 1); + mprAssert(ejsObjIsCollectable(thisObj)); + ejsWriteVar(ep, thisObj, ep->result, EJS_SHALLOW_COPY); + } + return rc; +} + +/******************************************************************************/ +/******************************* Boolean Class ********************************/ +/******************************************************************************/ + +int ejsDefineBooleanClass(Ejs *ep) +{ + if (ejsDefineClass(ep, "Boolean", "Object", ejsBooleanConstructor) == 0){ + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ +/* + * Boolean constructor + */ + +int ejsBooleanConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + return 0; +} + +/******************************************************************************/ +/******************************** Number Class ********************************/ +/******************************************************************************/ + +int ejsDefineNumberClass(Ejs *ep) +{ + if (ejsDefineClass(ep, "Number", "Object", ejsNumberConstructor) == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + return 0; +} + +/******************************************************************************/ +/* + * Number constructor + */ + +int ejsNumberConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + return 0; +} + +/******************************************************************************/ + +#else +void ejsStndClassesDummy() {} + +/******************************************************************************/ +#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-2.0/ejs/classes/ejsString.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsString.c new file mode 100644 index 0000000000..2339650361 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsString.c @@ -0,0 +1,381 @@ +/* + * @file ejsString.c + * @brief EJScript string class + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS +/******************************************************************************/ +/*********************************** Constructors *****************************/ +/******************************************************************************/ +/* + * String constructor. + */ + +int ejsStringConstructor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *str; + + if (argc == 0) { + ejsSetReturnValueToString(ejs, ""); + + } else if (argc == 1) { + /* MOB -- rc */ + str = ejsVarToString(ejs, argv[0]); + ejsSetReturnValueToString(ejs, str); + + } else { + ejsArgError(ejs, "usage: String([var])"); + return -1; + } + + return 0; +} + +/******************************************************************************/ +/******************************** Visible Methods *****************************/ +/******************************************************************************/ +/* + * Return a string containing the character at a given index + * + * String string.charAt(Number) + */ + +static int charAt(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsNum num; + char buf[2]; + + if (argc != 1) { + ejsArgError(ejs, "usage: charAt(integer)"); + return -1; + } + + num = ejsVarToNumber(argv[0]); + if (num < 0 || num >= thisObj->length) { + ejsError(ejs, EJS_RANGE_ERROR, "Bad index"); + return -1; + } + + mprAssert(ejsVarIsString(thisObj)); + + buf[0] = argv[0]->string[num]; + buf[1] = '\0'; + ejsSetReturnValueToString(ejs, buf); + + return 0; +} + +/******************************************************************************/ +/* + * Return an integer containing the character at a given index + * + * Number string.charCodeAt(Number) + */ + +static EjsNum charCodeAt(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsNum num; + + if (argc != 1) { + ejsArgError(ejs, "usage: charCodeAt(integer)"); + return -1; + } + + num = ejsVarToNumber(argv[0]); + if (num < 0 || num >= thisObj->length) { + ejsError(ejs, EJS_RANGE_ERROR, "Bad index"); + return -1; + } + ejsSetReturnValueToNumber(ejs, (EjsNum) argv[0]->string[num]); + + return 0; +} + +/******************************************************************************/ +/* + * Catenate + * + * String string.catenate(var, ...) + */ + +static int concat(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int i; + + if (argc == 0) { + ejsArgError(ejs, "usage: concat(String, ...)"); + return -1; + } + + mprAssert(ejsVarIsString(thisObj)); + + for (i = 0; i < argc; i++) { + if (ejsStrcat(ejs, thisObj, argv[i]) < 0) { + ejsMemoryError(ejs); + return -1; + } + } + ejsSetReturnValue(ejs, thisObj); + return 0; +} + +/******************************************************************************/ +/* + * Return the position of the first occurance of a substring + * + * Number string.indexOf(String subString [, Number start]) + */ + +static int indexOf(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *pat, *s1, *s2, *origin; + int start, i; + + if (argc == 0 || argc > 2) { + ejsArgError(ejs, "usage: indexOf(String [, Number])"); + return -1; + } + + pat = ejsVarToString(ejs, argv[0]); + + if (argc == 2) { + start = ejsVarToNumber(argv[1]); + if (start > thisObj->length) { + start = thisObj->length; + } + } else { + start = 0; + } + + i = start; + for (origin = &thisObj->string[i]; i < thisObj->length; i++, origin++) { + s1 = origin; + for (s2 = pat; *s1 && *s2; s1++, s2++) { + if (*s1 != *s2) { + break; + } + } + if (*s2 == '\0') { + ejsSetReturnValueToNumber(ejs, (EjsNum) (origin - thisObj->string)); + } + } + + ejsSetReturnValueToNumber(ejs, (EjsNum) -1); + return 0; +} + +/******************************************************************************/ +/* + * Return the position of the last occurance of a substring + * + * Number string.lastIndexOf(String subString [, Number start]) + */ + +static int lastIndexOf(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *pat, *s1, *s2, *origin; + int start; + + if (argc == 0 || argc > 2) { + ejsArgError(ejs, "usage: indexOf(String [, Number])"); + return -1; + } + + pat = ejsVarToString(ejs, argv[0]); + + if (argc == 2) { + start = ejsVarToNumber(argv[1]); + if (start > thisObj->length) { + start = thisObj->length; + } + } else { + start = 0; + } + + origin = &thisObj->string[thisObj->length - 1]; + for (; origin >= &thisObj->string[start]; origin--) { + + s1 = origin; + for (s2 = pat; *s1 && *s2; s1++, s2++) { + if (*s1 != *s2) { + break; + } + } + if (*s2 == '\0') { + ejsSetReturnValueToNumber(ejs, (EjsNum) (origin - thisObj->string)); + } + } + + ejsSetReturnValueToNumber(ejs, (EjsNum) -1); + return 0; +} + +/******************************************************************************/ +/* + * Return a substring + * + * Number string.slice(Number start, Number end) + */ + +static int slice(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsNum start, end; + + if (argc != 2) { + ejsArgError(ejs, "usage: slice(Number, Number)"); + return -1; + } + + start = ejsVarToNumber(argv[0]); + end = ejsVarToNumber(argv[1]); + if (start < 0 || start >= thisObj->length) { + ejsError(ejs, EJS_RANGE_ERROR, "Bad start index"); + return-1; + } + if (end < 0 || end >= thisObj->length) { + ejsError(ejs, EJS_RANGE_ERROR, "Bad end index"); + return -1; + } + + mprAssert(ejsVarIsString(thisObj)); + + ejsSetReturnValueToBinaryString(ejs, (uchar*) &thisObj->string[start], + end - start); + + return 0; +} + +/******************************************************************************/ +/* + * Split a string + * + * Number string.split(String delimiter [, Number limit]) + */ + +static int split(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *array, *vp; + char *delim, *last, *cp; + int len, limit, alloc; + + if (argc == 0 || argc > 2) { + ejsArgError(ejs, "usage: split(String [, Number])"); + return -1; + } + + delim = ejsVarToStringEx(ejs, argv[0], &alloc); + + limit = ejsVarToNumber(argv[1]); + + array = ejsCreateArray(ejs, 0); + + len = strlen(delim); + + last = thisObj->string; + for (cp = last; *cp; cp++) { + if (*cp == *delim && strncmp(cp, delim, len) == 0) { + if (cp > last) { + vp = ejsCreateBinaryStringVar(ejs, (uchar*) last, (cp - last)); + ejsAddArrayElt(ejs, array, vp, EJS_SHALLOW_COPY); + ejsFreeVar(ejs, vp); + } + } + } + + ejsSetReturnValue(ejs, array); + ejsFreeVar(ejs, array); + + if (alloc) { + mprFree(delim); + } + + return 0; +} + +/******************************************************************************/ +/* + * Create the object class + */ + +int ejsDefineStringClass(Ejs *ejs) +{ + EjsVar *sc; + + sc = ejsDefineClass(ejs, "String", "Object", ejsStringConstructor); + if (sc == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + ejsDefineCMethod(ejs, sc, "charAt", charAt, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "charCodeAt", charCodeAt, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "concat", concat, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "indexOf", indexOf, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "lastIndexOf", lastIndexOf, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "slice", slice, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "split", split, EJS_NO_LOCAL); +#if UNUSED + ejsDefineCMethod(ejs, sc, "match", match, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "replace", replace, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "search", search, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "substring", substring, EJS_NO_LOCAL); + // MOB bad name + ejsDefineCMethod(ejs, sc, "substr", substr, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "toLowerCase", toLowerCase, EJS_NO_LOCAL); + ejsDefineCMethod(ejs, sc, "toUpperCase", toUpperCase, EJS_NO_LOCAL); + + // Static method + ejsDefineCMethod(ejs, sc, "fromCharCode", fromCharCode, 0, EJS_NO_LOCAL); +#endif + + if (ejsObjHasErrors(sc)) { + ejsFreeVar(ejs, sc); + return MPR_ERR_CANT_CREATE; + } + return 0; +} + +/******************************************************************************/ +#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-2.0/ejs/classes/ejsXml.c b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsXml.c new file mode 100644 index 0000000000..a2ef8d1390 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/classes/ejsXml.c @@ -0,0 +1,1327 @@ +/* + * @file ejsXml.c + * @brief E4X XML support + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/************************************ Doc *************************************/ +/* + * Javascript class definition + * + * class XML { + * public XML(); + * public XML(string xmlString); // "<tag... " + * public XML(string file); // "file" + * + * public void load(string file); + * public void save(string file); + * public Array children(); + * public Array attributes(); + * } + * + [[Internal Properties / Methods]] + - prototype - Ptr to class prototype (base class) + - class - Type of class + Object.prototype.toString + - Value - + - Get(name) - Returns the value + - Put(name, value) - Sets the value + - HasProperty(name) - Bool if property exists + - Delete(name) - Delete property + - DefaultValue(hint) - Return default primitive (not obj) value + toString, if result is obj, then call valueOf + if hint is number, then call valueOf, then toString + - Construct(arg list) - Constructor + - Call(arg list) - Function call + - HasInstance(value) - ?? + - Scope - Frame scope chain + - Match(string, index) - Regexp match + + - Example: + XML attribute @name + @* + * + var node = new XML("<order/>"); + Operators: + var prices = order..price; + var urgentItems = order.item(@level == "rush"); + var itemAttrs = order.item[0].@*; # @ for attributes + XML Literals + order.customer.address = + <address>..... + <zip>{zipCode}</zip> Where {var} is a JS var + <tag attribute={prefix}> ... Also for attributes + </address> + Omit namespaces + Example: + var html = <html/>; + html.head.title = "My title"; + head.body@bgcolor = "#e4e4e4"; +*/ + +/********************************** Includes **********************************/ + +#include "ejs.h" +#include "exml.h" + +/************************************ Data ************************************/ +#if BLD_FEATURE_EJS_E4X + +/* + * Per tag state + */ +typedef struct XmlTagState { + EjsVar *obj; + EjsVar *attributes; + EjsVar *comments; +} XmlTagState; + +/* + * Parser state + */ +typedef struct XmlState { + Ejs *ep; + EjsVar *xmlClass; + EjsVar *xmlListClass; + XmlTagState nodeStack[E4X_MAX_NODE_DEPTH]; + int topOfStack; + long inputSize; + long inputPos; + const char *inputBuf; + const char *fileName; +} XmlState; + +/****************************** Forward Declarations **************************/ +/* + * XML methods + */ +static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int name(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int load(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int save(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int toString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int valueOf(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); + +/* MOB -- temp */ +static int getList(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int setText(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); + +#if FUTURE +static int length(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int toXmlString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); + +static int appendChild(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int attributes(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int child(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int children(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int comments(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int decendants(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int elements(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int insertChildAfter(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int insertChildBefore(Ejs *ep, EjsVar *thisObj, int argc, + EjsVar **argv); +static int replace(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int setName(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv); +#endif + +/* + * Internal methods + */ +static EjsVar *createXmlProperty(Ejs *ep, EjsVar *obj, const char *property); +static int deleteXmlProperty(Ejs *ep, EjsVar *obj, const char *property); +static EjsVar *getXmlProperty(Ejs *ep, EjsVar *obj, const char *property); +static EjsVar *setXmlProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value); +static int loadXmlString(Ejs *ep, EjsVar *thisObj, const char *xmlString); + +/* + * XMLList methods + */ +static EjsVar *createXmlListProperty(Ejs *ep, EjsVar *obj, + const char *property); +static int deleteXmlListProperty(Ejs *ep, EjsVar *obj, + const char *property); +static EjsVar *getXmlListProperty(Ejs *ep, EjsVar *obj, const char *property); +static EjsVar *setXmlListProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value); + +/* + * Misc + */ +static int readFileData(Exml *xp, void *data, char *buf, int size); +static int readStringData(Exml *xp, void *data, char *buf, int size); +static int parserHandler(Exml *xp, int state, const char *tagName, + const char *attName, const char *value); +static void termParser(Exml *xp); +static Exml *initParser(Ejs *ep, EjsVar *thisObj, const char *fileName); +static int getNumElements(EjsVar *obj); +static int getText(MprBuf *buf, EjsVar *obj); +static int xmlToString(Ejs *ep, MprBuf *buf, EjsVar *obj, int indentLevel); +static void indent(MprBuf *bp, int level); +static char *cleanTagName(char *name); + +/******************************************************************************/ +/* + * Define the E4X classes (XML, XMLList) + */ + +int ejsDefineXmlClasses(Ejs *ep) +{ + EjsMethods *methods; + EjsVar *xmlClass, *xmlListClass; + + /* + * Create the XML class + */ + xmlClass = ejsDefineClass(ep, "XML", "Object", ejsXmlConstructor); + if (xmlClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the XML class methods + */ + ejsDefineCMethod(ep, xmlClass, "text", text, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "name", name, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "load", load, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "save", save, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "toString", toString, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "valueOf", valueOf, EJS_NO_LOCAL); + +/* MOB -- temporary only */ + ejsDefineCMethod(ep, xmlClass, "getList", getList, EJS_NO_LOCAL); + ejsDefineCMethod(ep, xmlClass, "setText", setText, EJS_NO_LOCAL); + + /* + * Setup the XML internal methods. + */ + methods = mprAllocTypeZeroed(ep, EjsMethods); + xmlClass->objectState->methods = methods; + + methods->createProperty = createXmlProperty; + methods->deleteProperty = deleteXmlProperty; + methods->getProperty = getXmlProperty; + methods->setProperty = setXmlProperty; + + /* + * Create the XMLList class + */ + xmlListClass = ejsDefineClass(ep, "XMLList", "Array", + ejsXmlListConstructor); + + /* + * Define the XMLList class methods + */ + + /* + * Setup the XML internal methods. + */ + methods = mprAllocTypeZeroed(ep, EjsMethods); + xmlListClass->objectState->methods = methods; + + methods->createProperty = createXmlListProperty; + methods->deleteProperty = deleteXmlListProperty; + methods->getProperty = getXmlListProperty; + methods->setProperty = setXmlListProperty; + + /* MOB -- need to complete xmlListClass */ + + return (ejsObjHasErrors(xmlClass) || ejsObjHasErrors(xmlListClass)) + ? MPR_ERR_CANT_INITIALIZE : 0; + return 0; +} + +/******************************************************************************/ +/* + * Routine to create an XML object using a default constructor + */ + +EjsVar *ejsCreateXml(Ejs *ep) +{ + EjsVar *op; + + op = ejsCreateSimpleObj(ep, "XML"); + if (op == 0) { + mprAssert(op); + return op; + } + ejsSetVarName(ep, op, "xmlNode"); + + /* + * Invoke class constructors manually (for speed and space) + */ + if (ejsXmlConstructor(ep, op, 0, 0) < 0) { + mprFree(op); + mprAssert(0); + return 0; + } + return op; +} + +/******************************************************************************/ +/* + * XML constructor + */ + +int ejsXmlConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *vp; + const char *str; + + ejsSetVarFlags(thisObj, EJS_XML_FLAGS_ELEMENT); + + if (argc == 1) { + vp = argv[0]; + + if (ejsVarIsObject(vp)) { + /* Convert DOM to XML. Not implemented */; + + } else if (ejsVarIsString(vp)) { + str = vp->string; + if (str == 0) { + return 0; + } + if (*str == '<') { + /* XML Literal */ + return loadXmlString(ep, thisObj, str); + + } else { + /* Load from file */ + return load(ep, thisObj, argc, argv); + } + } else { + ejsError(ep, EJS_TYPE_ERROR, "Bad type passed to XML constructor"); + return -1; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Routine to create an XMLList object + */ + +EjsVar *ejsCreateXmlList(Ejs *ep) +{ + EjsVar *op; + + /* Sanity limit for size of hash table */ + + op = ejsCreateSimpleObj(ep, "XMLList"); + if (op == 0) { + mprAssert(0); + return op; + } + if (ejsArrayConstructor(ep, op, 0, 0) < 0 || + ejsXmlConstructor(ep, op, 0, 0) < 0) { + mprFree(op); + mprAssert(0); + return 0; + } + return op; +} + +/******************************************************************************/ +/* + * XMLList constructor + */ + +int ejsXmlListConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + // ejsSetVarFlags(vp, EJS_XML_FLAGS_ELEMENT); + return 0; +} + +/******************************************************************************/ +/******************************** Internal Methods ****************************/ +/******************************************************************************/ + +static EjsVar *createXmlProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsGetVarPtr(ejsCreateSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static int deleteXmlProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsDeleteProperty(ep, obj, property); +} + +/******************************************************************************/ +/* MOB -- need ep as an arg */ +static EjsVar *getXmlProperty(Ejs *ep, EjsVar *obj, const char *property) +{ +#if NEW + EjsVar *lp; + + lp = ejsCreateXmlList(ep); + if (isdigit(*property)) { + /* MOB -- where do we store these. Do we need them ? */ + lp->targetObject = obj + lp->targetProperty = property + return getXmlListProperty(lp, property); + } + + /* What about a simple elment. Should it not return the text */ + + if (*property == '@') { + ap = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (ap) { + vp = ejsGetVarPtr(ap); + /* MOB -- are attributes unique ? */ + if (vp->flags & EJS_XML_FLAGS_ATTRIBUTE && + strcmp(property, ap->name) == 0) { + ejsAppendXml(lp, vp); + } + ap = ejsGetNexttProperty(ap, EJS_ENUM_ALL); + } + } else { + while (ap) { + vp = ejsGetVarPtr(ap); + /* MOB -- are attributes unique ? */ + if (vp->flags & EJS_XML_FLAGS_ELEMENT && + strcmp(property, ap->name) == 0) { + ejsAppendXml(lp, vp); + } + ap = ejsGetNexttProperty(ap, EJS_ENUM_ALL); + } + } + return l; + + // Must always return XML or XMLList event for comments and attributes +#endif + return ejsGetVarPtr(ejsGetSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static EjsVar *setXmlProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value) +{ + EjsProperty *pp; + EjsVar *vp; + + pp = ejsCreateSimpleProperty(ep, obj, property); + if (pp == 0) { + /* Should never happen */ + mprAssert(pp); + return 0; + } + vp = ejsGetVarPtr(pp); + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) < 0) { + return 0; + } + return ejsGetVarPtr(pp); +} + +/******************************************************************************/ +/* + NEW + +static EjsVar *setXmlProperty(Ejs *ep, EjsVar *op, const char *property, + EjsVar *value) +{ + + if ((value->objectState->baseClass != XML && + value->objectState->baseClass != XMLList) || + value->string[0] != '<') { + ejsVarToString(luevalue.toString(); + ejsRunMethod(ep, value, "toString", 0); + value = ejsDupVar(ep->result); + + } else { + value = ejsDupVar(value); + } + + if (isdigit(*property)) { + // ERROR -- reserved for future versions + return 0; + } + + if (*property == '@') { + if (op->objectState->baseClass == XMLList) { + if (op->obj.LENGTH_PROPERTY == 0) { + c = ""; + } else { + // Catenate all result of toString on all elts in list + } + } else { + c = c.toString(); + } + // Replace existing attribute of same name or insert + return; + } + for (i = op->obj.LENGTH - 1; i >= 0; i--) { + // Delete item of same name + } + if (not Found) { + Append new Xml object + - set [[name]], [[class]] == "element" + } + + mprFree(value); +} + + */ +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ + +static int load(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *fileName; + XmlState *parser; + Exml *xp; + MprFile *file; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ep, EJS_ARG_ERROR, "Bad args. Usage: load(fileName);"); + return -1; + } + fileName = argv[0]->string; + + /* MOB -- not romable + Need rom code in MPR not MprServices + */ + file = mprOpen(ep, fileName, O_RDONLY, 0664); + if (file == 0) { + ejsError(ep, EJS_IO_ERROR, "Can't open: %s", fileName); + return -1; + } + + /* MOB -- should we empty thisObj of all existing properties ? */ + + xp = initParser(ep, thisObj, fileName); + parser = exmlGetParseArg(xp); + + exmlSetInputStream(xp, readFileData, (void*) file); + + if (exmlParse(xp) < 0) { + if (! ejsGotException(ep)) { + ejsError(ep, EJS_IO_ERROR, "Can't parse XML file: %s\nDetails %s", + fileName, exmlGetErrorMsg(xp)); + } + termParser(xp); + mprClose(file); + return -1; + } + + ejsSetReturnValue(ep, parser->nodeStack[0].obj); + + termParser(xp); + mprClose(file); + + return 0; +} + +/******************************************************************************/ + +static int loadXmlString(Ejs *ep, EjsVar *thisObj, const char *xmlString) +{ + XmlState *parser; + Exml *xp; + + xp = initParser(ep, thisObj, "string"); + parser = exmlGetParseArg(xp); + + parser->inputBuf = xmlString; + parser->inputSize = strlen(xmlString); + + exmlSetInputStream(xp, readStringData, (void*) 0); + + if (exmlParse(xp) < 0) { + if (! ejsGotException(ep)) { + ejsError(ep, EJS_IO_ERROR, "Can't parse XML string\nError %s", + exmlGetErrorMsg(xp)); + } + termParser(xp); + return -1; + } + + ejsSetReturnValue(ep, parser->nodeStack[0].obj); + + termParser(xp); + + return 0; +} + +/******************************************************************************/ + +static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetSimpleProperty(ep, thisObj, E4X_TEXT_PROPERTY)); + if (vp == 0) { + ejsSetReturnValueToString(ep, ""); + return 0; + } + ejsSetReturnValue(ep, vp); + return 0; +} + +/******************************************************************************/ +/* + * Return the tag name + */ + +static int name(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetSimpleProperty(ep, thisObj, E4X_TAG_NAME_PROPERTY)); + if (vp == 0) { + ejsSetReturnValueToString(ep, ""); + return 0; + } + ejsSetReturnValue(ep, vp); +#if UNDEFINED + char *name; + /* MOB -- not ideal as we can't guarantee thisObj is a property */ + name = ejsGetPropertyPtr(thisObj)->name; + if (name == 0) { + name = ""; + } + ejsSetReturnValueToString(ep, name); +#endif + return 0; +} + +/******************************************************************************/ +/* MOB -- temporary only */ + +static int setText(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "usage: setText(string)"); + } + + ejsSetProperty(ep, thisObj, E4X_TEXT_PROPERTY, argv[0]); + ejsSetReturnValue(ep, argv[0]); + return 0; +} + +/******************************************************************************/ + +static Exml *initParser(Ejs *ep, EjsVar *thisObj, const char *fileName) +{ + XmlState *parser; + Exml *xp; + + xp = exmlOpen(ep, 512, E4X_BUF_MAX); + mprAssert(xp); + + /* + * Create the parser stack + */ + parser = mprAllocTypeZeroed(ep, XmlState); + parser->ep = ep; + parser->nodeStack[0].obj = thisObj; + parser->xmlClass = ejsGetClass(ep, 0, "XML"); + parser->xmlListClass = ejsGetClass(ep, 0, "XMLList"); + parser->fileName = fileName; + + exmlSetParseArg(xp, parser); + exmlSetParserHandler(xp, parserHandler); + + return xp; +} + +/******************************************************************************/ + +static void termParser(Exml *xp) +{ + mprFree(exmlGetParseArg(xp)); + exmlClose(xp); +} + +/******************************************************************************/ +/* + * XML parsing callback. Called for each elt and attribute/value pair. + * For speed, we handcraft the object model here rather than calling + * putXmlProperty. + * + * "<!-- txt -->" parseHandler(efd, , EXML_COMMENT); + * "<elt" parseHandler(efd, , EXML_NEW_ELT); + * "...att=value" parseHandler(efd, , EXML_NEW_ATT); + * "<elt ...>" parseHandler(efd, , EXML_ELT_DEFINED); + * "<elt/>" parseHandler(efd, , EXML_SOLO_ELT_DEFINED); + * "<elt> ...<" parseHandler(efd, , EXML_ELT_DATA); + * "...</elt>" parseHandler(efd, , EXML_END_ELT); + * + * Note: we recurse on every new nested elt. + */ + +static int parserHandler(Exml *xp, int state, const char *tagName, + const char *attName, const char *value) +{ + XmlState *parser; + XmlTagState *tos; + EjsVar *currentNode, *vp, *tagNode, *parent, *vpx; + EjsProperty *pp; + Ejs *ep; + char *name; + + parser = (XmlState*) xp->parseArg; + ep = parser->ep; + tos = &parser->nodeStack[parser->topOfStack]; + currentNode = tos->obj; + + mprAssert(state >= 0); + mprAssert(tagName && *tagName); + + switch (state) { + case EXML_PI: + /* + * By using a property name with a leading space, we can store + * non-user-visible processing instructions as regular properties. + */ + pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, E4X_PI_PROPERTY); + ejsMakePropertyEnumerable(pp, 1); + vp = ejsGetVarPtr(pp); + ejsWriteVarAsString(ep, vp, value); + ejsSetVarFlags(vp, EJS_XML_FLAGS_PI); + break; + + case EXML_COMMENT: + /* + * By using a property name with a leading space, we can store + * non- user-visible comments as regular properties. + */ + pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, + E4X_COMMENT_PROPERTY); + ejsMakePropertyEnumerable(pp, 1); + vp = ejsGetVarPtr(pp); + ejsWriteVarAsString(ep, vp, value); + ejsSetVarFlags(vp, EJS_XML_FLAGS_COMMENT); + break; + + case EXML_NEW_ELT: + if (parser->topOfStack > E4X_MAX_NODE_DEPTH) { + ejsError(ep, EJS_IO_ERROR, + "XML nodes nested too deeply in %s at line %d", + parser->fileName, exmlGetLineNumber(xp)); + return MPR_ERR_BAD_SYNTAX; + } + + name = mprStrdup(xp, tagName); + if (name == 0) { + return MPR_ERR_MEMORY; + } + + if (cleanTagName(name) < 0) { + ejsError(ep, EJS_TYPE_ERROR, "Bad XML tag name in %s at %d", + parser->fileName, exmlGetLineNumber(xp)); + mprFree(name); + return MPR_ERR_BAD_SYNTAX; + } + + pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, name); + ejsMakePropertyEnumerable(pp, 1); + + tagNode = ejsGetVarPtr(pp); + + /* MOB -- OPT */ + vpx = ejsCreateXml(ep); + vp = ejsWriteVar(ep, tagNode, vpx, EJS_SHALLOW_COPY); + ejsMakeObjLive(vp, 1); + ejsFreeVar(ep, vpx); + + /* MOB -- return code */ + pp = ejsSetPropertyToString(ep, vp, E4X_TAG_NAME_PROPERTY, name); + ejsMakePropertyEnumerable(pp, 0); + + ejsSetVarFlags(vp, EJS_XML_FLAGS_ELEMENT); + ejsMakePropertyEnumerable(ejsGetPropertyPtr(vp), 1); + + tos = &parser->nodeStack[++(parser->topOfStack)]; + currentNode = tos->obj = vp; + tos->attributes = 0; + tos->comments = 0; + mprFree(name); + break; + + case EXML_NEW_ATT: + if (mprAllocSprintf(MPR_LOC_ARGS(xp), &name, 0, "@%s", attName) < 0) { + return MPR_ERR_MEMORY; + } + pp = ejsCreateProperty(ep, currentNode, name); + ejsMakePropertyEnumerable(pp, 1); + + vp = ejsGetVarPtr(pp); + ejsWriteVarAsString(ep, vp, value); + ejsSetVarFlags(vp, EJS_XML_FLAGS_ATTRIBUTE); + mprFree(name); + break; + + case EXML_SOLO_ELT_DEFINED: + parser->topOfStack--; + mprAssert(parser->topOfStack >= 0); + tos = &parser->nodeStack[parser->topOfStack]; + break; + + case EXML_ELT_DEFINED: + if (parser->topOfStack > 0) { + parent = parser->nodeStack[parser->topOfStack - 1].obj; + ejsSetProperty(ep, currentNode, E4X_PARENT_PROPERTY, parent); + } + break; + + case EXML_ELT_DATA: + case EXML_CDATA: + pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, + E4X_TEXT_PROPERTY); + ejsMakePropertyEnumerable(pp, 1); + vp = ejsGetVarPtr(pp); + ejsWriteVarAsString(ep, vp, value); + ejsSetVarFlags(vp, EJS_XML_FLAGS_TEXT); + break; + + case EXML_END_ELT: + /* + * This is the closing element in a pair "<x>...</x>". + * Pop the stack frame off the elt stack + */ + parser->topOfStack--; + mprAssert(parser->topOfStack >= 0); + tos = &parser->nodeStack[parser->topOfStack]; + break; + + default: + ejsError(ep, EJS_IO_ERROR, "XML error in %s at %d\nDetails %s", + parser->fileName, exmlGetLineNumber(xp), exmlGetErrorMsg(xp)); + mprAssert(0); + return MPR_ERR_BAD_SYNTAX; + } + return 0; +} + +/******************************************************************************/ + +static char *cleanTagName(char *name) +{ + char *cp; + + for (cp = name; *cp; cp++) { + if (*cp == ':') { + *cp = '_'; + } else if (!isalnum(*cp) && *cp != '_' && *cp != '$' && *cp != '@') { + return 0; + } + } + return name; +} + +/******************************************************************************/ + +static int readFileData(Exml *xp, void *data, char *buf, int size) +{ + mprAssert(xp); + mprAssert(data); + mprAssert(buf); + mprAssert(size > 0); + + return mprRead((MprFile*) data, buf, size); +} + +/******************************************************************************/ + +static int readStringData(Exml *xp, void *data, char *buf, int size) +{ + XmlState *parser; + int rc, len; + + mprAssert(xp); + mprAssert(buf); + mprAssert(size > 0); + + parser = (XmlState*) xp->parseArg; + + if (parser->inputPos < parser->inputSize) { + len = min(size, (parser->inputSize - parser->inputPos)); + rc = mprMemcpy(buf, size, &parser->inputBuf[parser->inputPos], len); + parser->inputPos += len; + return rc; + } + return 0; +} + +/******************************************************************************/ + +static int save(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *fileName; + MprBuf *buf; + MprFile *file; + int bytes, len; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ep, EJS_ARG_ERROR, "Bad args. Usage: save(fileName);"); + return -1; + } + fileName = argv[0]->string; + + /* MOB -- not romable + Need rom code in MPR not MprServices + */ + + /* + * Convert to a string + */ + buf = mprCreateBuf(ep, E4X_BUF_SIZE, E4X_BUF_MAX); + if (xmlToString(ep, buf, thisObj, -1) < 0) { + mprFree(buf); + return -1; + } + + file = mprOpen(ep, fileName, + O_CREAT | O_TRUNC | O_WRONLY | O_TEXT, 0664); + if (file == 0) { + ejsError(ep, EJS_IO_ERROR, "Can't open: %s, %d", fileName, + mprGetOsError()); + return -1; + } + + len = mprGetBufLength(buf); + bytes = mprWrite(file, buf->start, len); + if (bytes != len) { + ejsError(ep, EJS_IO_ERROR, "Can't write to: %s", fileName); + mprClose(file); + return -1; + } + mprWrite(file, "\n", 1); + mprFree(buf); + + mprClose(file); + + return 0; +} + +/******************************************************************************/ + +static int toString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprBuf *buf; + + buf = mprCreateBuf(ep, E4X_BUF_SIZE, E4X_BUF_MAX); + + if (xmlToString(ep, buf, thisObj, -1) < 0) { + mprFree(buf); + return -1; + } + ejsWriteVarAsString(ep, ep->result, (char*) buf->start); + + mprFree(buf); + + return 0; +} + +/******************************************************************************/ +/* MOB -- need to support XMLList */ + +static int xmlToString(Ejs *ep, MprBuf *buf, EjsVar *obj, int indentLevel) +{ + EjsProperty *pp; + EjsVar *vp; + char *varBuf; + int endTag, sawElements; + + if (indentLevel < 0) { + mprPutStringToBuf(buf, "<?xml version=\"1.0\"?>"); + } + + switch (obj->type) { + case EJS_TYPE_STRING: + if (obj->flags & EJS_XML_FLAGS_ATTRIBUTE) { + mprPutFmtStringToBuf(buf, " %s=\"%s\"", + &ejsGetPropertyPtr(obj)->name[1], obj->string); + /* No new line */ + + } else if (obj->flags & EJS_XML_FLAGS_COMMENT) { + mprPutCharToBuf(buf, '\n'); + indent(buf, indentLevel); + mprPutFmtStringToBuf(buf, "<!-- %s -->", obj->string); + + } else if (obj->flags & EJS_XML_FLAGS_TEXT) { + mprPutStringToBuf(buf, obj->string); + + } else { +// indent(buf, indentLevel); + mprPutStringToBuf(buf, obj->string); +// mprPutCharToBuf(buf, '\n'); + } + break; + + default: + /* Primitive types come here */ + indent(buf, indentLevel); + /* MOB -- rc */ + varBuf = ejsVarToString(ep, obj); + mprPutStringToBuf(buf, varBuf); + break; + + case EJS_TYPE_OBJECT: + if (obj->objectState->baseClass == ejsGetClass(ep, 0, "XML")) { + if (!obj->objectState->visited) { + obj->objectState->visited = 1; + + /* MOB -- opt. Flags would be quicker */ + if (strcmp(ejsGetPropertyPtr(obj)->name, + E4X_PARENT_PROPERTY) == 0) { + return 0; + } + /* + * MOB -- short term fix for tags with no body but with + * attributes + */ + if (getNumElements(obj) == 0 && 0) { + /* + * XML element is simple with no elements, so return just + * the text. + */ + if (getText(buf, obj) < 0) { + ejsError(ep, EJS_IO_ERROR, + "XML is to big to convert to a string"); + obj->objectState->visited = 0; + return -1; + } + + } else if (obj->flags & (EJS_XML_FLAGS_ELEMENT)) { + /* + * XML object is complex (has elements) so return full XML + * content. + */ + mprPutCharToBuf(buf, '\n'); + indent(buf, indentLevel); + + /* + * When called from toString, obj is not a property + */ + if (indentLevel >= 0) { + mprPutFmtStringToBuf(buf, "<%s", + ejsGetPropertyPtr(obj)->name); + endTag = 0; + + } else { + endTag = 1; + } + + sawElements = 0; + pp = ejsGetFirstProperty(obj, 0); + while (pp) { + vp = ejsGetVarPtr(pp); + + if (! (vp->flags & EJS_XML_FLAGS_ATTRIBUTE)) { + if (endTag == 0) { + endTag++; + mprPutStringToBuf(buf, ">"); + } + } + if (vp->flags & EJS_XML_FLAGS_ELEMENT) { + if (strcmp(ejsGetPropertyPtr(vp)->name, + E4X_PARENT_PROPERTY) != 0) { + sawElements++; + } + } + + if (xmlToString(ep, buf, ejsGetVarPtr(pp), + indentLevel + 1) < 0){ + return -1; + } + + pp = ejsGetNextProperty(pp, 0); + } + if (indentLevel >= 0) { + if (sawElements) { + mprPutCharToBuf(buf, '\n'); + indent(buf, indentLevel); + } + mprPutFmtStringToBuf(buf, "</%s>", + ejsGetPropertyPtr(obj)->name); + } + } + obj->objectState->visited = 0; + } + return 0; + } + + if (obj->objectState->baseClass == ejsGetClass(ep, 0, "XMLList")) { + indent(buf, indentLevel); + /* MOB -- TBD */ + return 0; + } + + /* + * All other objects. Allow other objects to override toString + */ + if (ejsRunMethod(ep, obj->objectState->baseClass, "toString", + 0) < 0) { + return -1; + } + if (ejsVarIsString(ep->result)) { + indent(buf, indentLevel); + mprPutStringToBuf(buf, obj->string); + } + break; + } + return 0; +} + +/******************************************************************************/ + +static void indent(MprBuf *bp, int level) +{ + int i; + + for (i = 0; i < level; i++) { + mprPutCharToBuf(bp, '\t'); + } +} + +/******************************************************************************/ + +static int valueOf(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 0) { + mprAssert(0); + return -1; + } + + switch (thisObj->type) { + default: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_OBJECT: + case EJS_TYPE_METHOD: + case EJS_TYPE_STRING_CMETHOD: + ejsWriteVar(ep, ep->result, thisObj, EJS_SHALLOW_COPY); + break; + + case EJS_TYPE_STRING: + ejsWriteVarAsInteger(ep, ep->result, atoi(thisObj->string)); + break; + + case EJS_TYPE_BOOL: + case EJS_TYPE_INT: +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: +#endif +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: +#endif + ejsWriteVar(ep, ep->result, thisObj, EJS_SHALLOW_COPY); + break; + } + return 0; +} + +/******************************************************************************/ + +static int getList(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *nodeName; + EjsProperty *pp; + EjsVar *list, *vp; + + if (argc != 1) { + nodeName = 0; + } else { + nodeName = argv[0]->string; + } + + list = ejsCreateArray(ep, 0); + + pp = ejsGetFirstProperty(thisObj, EJS_ENUM_ALL); + while (pp) { + vp = ejsGetVarPtr(pp); + if (vp->type == EJS_TYPE_OBJECT) { + if (strcmp(ejsGetPropertyPtr(vp)->name, E4X_PARENT_PROPERTY) != 0) { + if (vp->flags & EJS_XML_FLAGS_ELEMENT && + (nodeName == 0 || strcmp(nodeName, pp->name) == 0)) { + ejsAddArrayElt(ep, list, vp, EJS_SHALLOW_COPY); + } + } + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + + ejsSetReturnValueAndFree(ep, list); + return 0; +} + +/******************************************************************************/ + +static int getNumElements(EjsVar *obj) +{ + EjsProperty *pp; + int count; + + count = 0; + pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (pp) { + if (ejsGetVarPtr(pp)->flags & EJS_XML_FLAGS_ELEMENT) { + count++; + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + return count; +} + +/******************************************************************************/ +/* MOB - This needs to be a public method */ + +static int getText(MprBuf *buf, EjsVar *obj) +{ + EjsProperty *pp; + EjsVar *vp; + + pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (pp) { + vp = ejsGetVarPtr(pp); + if (vp->flags & EJS_XML_FLAGS_TEXT) { + /* MOB -- should test for overflow */ + mprPutStringToBuf(buf, vp->string); + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + return 0; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************** Internal Methods ****************************/ +/******************************************************************************/ + +static EjsVar *createXmlListProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsGetVarPtr(ejsCreateProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static int deleteXmlListProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + return ejsDeleteProperty(ep, obj, property); +} + +/******************************************************************************/ + +static EjsVar *getXmlListProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + // Must always return XML or XMLList event for comments and attributes + return ejsGetVarPtr(ejsGetSimpleProperty(ep, obj, property)); +} + +/******************************************************************************/ + +static EjsVar *setXmlListProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value) +{ + EjsProperty *pp; + EjsVar *vp; + + pp = ejsGetSimpleProperty(ep, obj, property); + if (pp == 0) { + mprAssert(pp); + return 0; + } + vp = ejsGetVarPtr(pp); + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) < 0){ + mprAssert(0); + return 0; + } + return ejsGetVarPtr(pp); +} + +/******************************************************************************/ +/* + NEW + +static EjsVar *putXmlListProperty(EjsVar *op, const char *property, + EjsVar *value) +{ + + if ((value->objectState->baseClass != XML && + value->objectState->baseClass != XMLList) || + value->string[0] != '<') { + c = value.toString(); + } else { + value = ejsDupVar(value); + ?? + } + if (isdigit(*property)) { + // ERROR + return 0; + } + if (*property == '@') { + if (op->objectState->baseClass == XMLList) { + if (op->obj.LENGTH_PROPERTY == 0) { + c = ""; + } else { + // Catenate all result of toString on all elts in list + } + } else { + c = c.toString(); + } + // Replace existing attribute of same name or insert + return; + } + for (i = op->obj.LENGTH - 1; i >= 0; i--) { + // Delete item of same name + } + if (not Found) { + Append new Xml object + - set [[name]], [[class]] == "element" + } +} + + */ + +/******************************************************************************/ +#else +void ejs4xDummy() {} + +/******************************************************************************/ +#endif /* BLD_FEATURE_EJS_E4X */ + +/* + * 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-2.0/ejs/ejs.c b/source4/lib/appweb/ejs-2.0/ejs/ejs.c new file mode 100644 index 0000000000..0fcc6f0545 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejs.c @@ -0,0 +1,1378 @@ +/* + * @file ejs.c + * @brief Embedded JavaScript (EJS) + * @overview Main module interface logic. + * @remarks The initialization code must be run single-threaded. Includes: + * ejsOpen, ejsClose. + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "ejs.h" + +#if BLD_FEATURE_EJS + +/************************************* Code ***********************************/ +/* + * Initialize the EJS subsystem + */ + +EjsService *ejsOpenService(MprCtx ctx) +{ + EjsService *service; + Ejs *interp; + + service = mprAllocTypeZeroed(ctx, EjsService); + if (service == 0) { + mprError(ctx, MPR_LOC, "Can't allocate service memory"); + return 0; + } + + interp = ejsCreateInterp(service, 0, 0, 0, 1); + if (interp == 0) { + mprError(ctx, MPR_LOC, "Can't create master interpreter"); + mprFree(service); + return 0; + } + service->master = interp; + + /* + * Restore the default GC settings for the master interpreter. + * ejsCreateInterp will have initialized them. + */ + ejsGCInit(interp, EJS_DEFAULT_OBJ_INC, EJS_DEFAULT_PROP_INC, + EJS_DEFAULT_VAR_INC, EJS_DEFAULT_STR_INC); + + /* + * Save the default interpreter and global class for all to access + * MOB -- don't store these. Store the service + */ + mprSetKeyValue(interp, "ejsMaster", interp); + mprSetKeyValue(interp, "ejsGlobalClass", interp->global); + + /* + * Once the Object class is created, this routine will also make the + * Global class a subclass of Object. + */ + if (ejsDefineObjectClass(interp) < 0) { + mprError(ctx, MPR_LOC, "Can't define EJS object class"); + mprFree(service); + return 0; + } + + /* + * Create all the standard classes + */ + if (ejsDefineStandardClasses(interp) < 0) { + mprError(ctx, MPR_LOC, "Can't define EJS standard classes"); + mprFree(service); + return 0; + } + + if (ejsDefineSystemClasses(interp) < 0) { + mprError(ctx, MPR_LOC, "Can't define EJS system classes"); + mprFree(service); + return 0; + } + + if (ejsCreateObjectModel(interp) < 0) { + mprError(ctx, MPR_LOC, "Can't create EJS object model"); + mprFree(service); + return 0; + } + +#if UNUSED && BLD_FEATURE_ALLOC_STATS +{ + EjsVar v; + mprLog(ctx, 0, "Obj %d, Var %d, Prop %d\n", sizeof(EjsObj), sizeof(EjsVar), + sizeof(EjsProperty)); + mprLog(ctx, 0, "GCLink %d\n", sizeof(EjsGCLink)); + mprLog(ctx, 0, "objectState %d\n", (uint) &v.objectState - (uint) &v); +} +#endif + + return service; +} + +/******************************************************************************/ +/* + * Close down the EJS Service + */ + +void ejsCloseService(EjsService *sp, bool doStats) +{ + Ejs *ep; + + mprAssert(sp); + + ep = sp->master; + mprAssert(ep); + + ejsTermSystemClasses(ep); + + if (ep) { + ejsFreeVar(ep, sp->globalClass); + +#if BLD_FEATURE_ALLOC_STATS + if (doStats) { + mprLog(sp, 0, "GC Statistics for the Global Interpreter"); + } +#endif + ejsDestroyInterp(ep, doStats); + } + + mprRemoveKeyValue(sp, "ejsMaster"); + mprRemoveKeyValue(sp, "ejsGlobalClass"); + + mprFree(sp); +} + +/******************************************************************************/ + +Ejs *ejsGetMasterInterp(EjsService *sp) +{ + return sp->master; +} + +/******************************************************************************/ +#if BLD_FEATURE_MULTITHREAD + +int ejsSetServiceLocks(EjsService *sp, EjsLockFn lock, EjsUnlockFn unlock, + void *data) +{ + mprAssert(sp); + + sp->lock = lock; + sp->unlock = unlock; + sp->lockData = data; + return 0; +} + +#endif +/******************************************************************************/ +/* + * Create and initialize an EJS interpreter. Interpreters have a global object + * that has the service global class set as a base class. This way, it + * inherits all the desired global properties, methods and classes. + * + * The primary and alternate handles are provided to C methods depending on + * the flags provided when the C methods are defined. The global variable + * (optionally) defines a predefined global variable space. + */ + +Ejs *ejsCreateInterp(EjsService *sp, void *primaryHandle, void *altHandle, + EjsVar *global, bool useOwnSlab) +{ + EjsProperty *pp; + EjsVar *baseClass; + Ejs *ep; + + ep = mprAllocTypeZeroed(sp, Ejs); + if (ep == 0) { + mprAssert(0); + return ep; + } + + ep->stkPtr = &ep->stack[EJS_MAX_STACK]; + + ep->service = sp; + ep->primaryHandle = primaryHandle; + ep->altHandle = altHandle; + + if (sp->master) { + ep->objectClass = sp->master->objectClass; + } + + if (useOwnSlab) { + ep->slabs = (EjsSlab*) mprAllocZeroed(ep, sizeof(EjsSlab) * + EJS_SLAB_MAX); + ep->slabAllocContext = ep; + + } else { + ep->slabs = sp->master->slabs; + ep->slabAllocContext = sp->master; + ep->flags |= EJS_FLAGS_SHARED_SLAB; + } + + ep->frames = mprCreateItemArray(ep, EJS_INC_FRAMES, EJS_MAX_FRAMES); + if (ep->frames == 0) { + mprFree(ep); + return 0; + } + + ejsGCInit(ep, EJS_OBJ_INC, EJS_PROP_INC, EJS_VAR_INC, EJS_STR_INC); + + if (sp->globalClass == 0) { + /* + * Only do this for the Global interpreter. Create a global class + * (prototype) object. This is base class from which all global + * spaces will inherit. + */ + sp->globalClass = ejsCreateObjVar(ep); + if (sp->globalClass == 0) { + mprFree(ep); + return 0; + } + ejsSetClassName(ep, sp->globalClass, "Global"); + global = sp->globalClass; + } + + if (global) { + /* + * The default interpreter uses the Global class as its global + * space. + */ + ep->global = ejsDupVar(ep, global, EJS_SHALLOW_COPY); + if (ep->global == 0) { + mprFree(ep); + return 0; + } + if (ep->global->objectState != sp->globalClass->objectState) { + ejsSetBaseClass(ep->global, sp->globalClass); + } + + } else { + /* + * Use the global class as our global so we can find the object class + */ + baseClass = ejsGetClass(ep, sp->globalClass, "Object"); + if (baseClass) { + ep->global = ejsCreateSimpleObjUsingClass(ep, baseClass); + if (ep->global == 0) { + mprFree(ep); + return 0; + } + + /* + * Override the base class and set to the master Global class + */ + ejsSetBaseClass(ep->global, sp->globalClass); + + } else { + ep->global = ejsCreateObjVar(ep); + } + } + + /* + * The "global" variable points to the global space + */ + pp = ejsSetProperty(ep, ep->global, "global", ep->global); + if (pp == 0) { + mprFree(ep); + return 0; + } + ejsMakePropertyEnumerable(pp, 0); + + /* + * The "Global" variable points to the Global class + */ + pp = ejsSetProperty(ep, ep->global, "Global", sp->globalClass); + if (pp == 0) { + mprFree(ep); + return 0; + } + ejsMakePropertyEnumerable(pp, 0); + + ep->local = ejsDupVar(ep, ep->global, EJS_SHALLOW_COPY); + if (ep->frames == 0 || ep->global == 0 || ep->local == 0) { + mprFree(ep); + return 0; + } + ejsSetVarName(ep, ep->local, "topLevelLocal"); + + if (mprAddItem(ep->frames, ep->global) < 0 || + mprAddItem(ep->frames, ep->local) < 0) { + mprFree(ep); + return 0; + } + + ep->result = ejsCreateUndefinedVar(ep); + if (ep->result == 0) { + mprFree(ep); + return 0; + } + + return ep; +} + +/******************************************************************************/ +/* + * Close an EJS interpreter + */ + +void ejsDestroyInterp(Ejs *ep, bool doStats) +{ + ejsCleanInterp(ep, doStats); + + mprFree(ep); +} + +/******************************************************************************/ +/* + * Clean an EJS interpreter of all allocated variables, but DONT destroy. + * We use this rather than DestroyInterp so we delay freeing the Ejs struct + * until after the service is closed. + */ + +void ejsCleanInterp(Ejs *ep, bool doStats) +{ + int i; + + if (ep->global) { + ejsDeleteProperty(ep, ep->local, "global"); + ejsDeleteProperty(ep, ep->global, "global"); + ep->global = 0; + } + if (ep->local) { + ejsFreeVar(ep, ep->local); + ep->local = 0; + } + if (ep->global) { + ejsFreeVar(ep, ep->global); + ep->global = 0; + } + if (ep->result) { + ejsFreeVar(ep, ep->result); + ep->result = 0; + } + if (ep->castAlloc && ep->castTemp) { + mprFree(ep->castTemp); + ep->castTemp = 0; + } + if (ep->frames) { + for (i = ep->frames->length - 1; i >= 0; i--) { + mprRemoveItemByIndex(ep->frames, i); + } + mprFree(ep->frames); + ep->frames = 0; + } + + if (doStats) { + +#if BLD_FEATURE_ALLOC_STATS + mprLog(ep, 0, " "); + mprLog(ep, 0, "GC Statistics for Interpreter (0x%X)", (uint) ep); +#endif + + /* + * Cleanup before printing the alloc report + */ + ejsSetGCDebugLevel(ep, 3); + ejsCollectGarbage(ep, -1); + +#if BLD_DEBUG + /* + * If we are the master, dump objects + */ + if (ep->service->master == ep) { + ejsDumpObjects(ep); + } +#endif + +#if BLD_FEATURE_ALLOC_STATS + /* + * Print an alloc report. 1 == do leak report + */ + ejsPrintAllocReport(ep, 1); +#endif + + } else { + /* + * Must collect garbage here incase sharing interpreters with the + * master. If we don't, the mprFree later in DestroyInterp will free + * all memory and when the master does GC --> crash. + */ + ejsCollectGarbage(ep, -1); + } +} + +/******************************************************************************/ +/* + * Evaluate an EJS script file. This will evaluate the script at the current + * context. Ie. if inside a function, declarations will be local. + */ + +int ejsEvalFile(Ejs *ep, const char *path, EjsVar *result) +{ + MprFile *file; + MprFileInfo info; + char *script; + char *saveFileName; + int rc; + + mprAssert(path && *path); + + if ((file = mprOpen(ep, path, O_RDONLY | O_BINARY, 0666)) == 0) { + ejsError(ep, EJS_IO_ERROR, "Can't open %s", path); + return -1; + } + + if (mprGetFileInfo(ep, path, &info) < 0) { + ejsError(ep, EJS_IO_ERROR, "Can't get file info for %s", path); + goto error; + } + + if ((script = (char*) mprAlloc(ep, info.size + 1)) == NULL) { + ejsError(ep, "MemoryError", "Cant malloc %d", (int) info.size); + goto error; + } + + if (mprRead(file, script, info.size) != (int) info.size) { + mprFree(script); + ejsError(ep, EJS_IO_ERROR, "Error reading %s", path); + goto error; + } + mprClose(file); + script[info.size] = '\0'; + + saveFileName = ep->fileName; + ep->fileName = mprStrdup(ep, path); + + rc = ejsEvalScript(ep, script, result); + mprFree(script); + + mprFree(ep->fileName); + ep->fileName = saveFileName; + + return rc; + +/* + * Error return + */ +error: + mprClose(file); + 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(Ejs *ep) +{ + EjsProperty *pp; + int fid; + + ep->local = ejsCreateSimpleObj(ep, "Object"); + ejsSetVarName(ep, ep->local, "local"); + + if (ep->local == 0) { + ejsMemoryError(ep); + return -1; + } + + if (ep->frames->length > EJS_MAX_FRAMES && !ep->gotException) { + ejsError(ep, EJS_RANGE_ERROR, "Recursion too deep: Max depth %d", + EJS_MAX_FRAMES); + return -1; + } + + /* + * Must add to frames before ejsSetProperty which will make the object live + */ + fid = mprAddItem(ep->frames, ep->local); + if (fid < 0) { + ejsMemoryError(ep); + return -1; + } + + /* Self reference */ + pp = ejsSetProperty(ep, ep->local, "local", ep->local); + ejsMakePropertyEnumerable(pp, 0); + + return fid; +} + +/******************************************************************************/ +/* + * Set a new variable scope block. This pushes the old local frame down + * the stack and creates a new local variables frame. + */ + +int ejsSetBlock(Ejs *ep, EjsVar *local) +{ + ep->local = ejsDupVar(ep, local, EJS_SHALLOW_COPY); + ejsMakeObjPermanent(ep->local, 1); + return mprAddItem(ep->frames, ep->local); +} + +/******************************************************************************/ +/* + * Close a variable scope block opened via ejsOpenBlock. Pop back the old + * local variables frame. + */ + +int ejsCloseBlock(Ejs *ep, int fid) +{ + mprAssert(ep->local >= 0); + mprAssert(fid >= 0); + + mprAssert(ep->local == (EjsVar*) mprGetItem(ep->frames, fid)); + + if (ep->local) { + /* Allow GC */ + ejsMakeObjPermanent(ep->local, 0); + ejsFreeVar(ep, ep->local); + } + + mprRemoveItemByIndex(ep->frames, fid); + + ep->local = (EjsVar*) mprGetItem(ep->frames, + mprGetItemCount(ep->frames) - 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 is optional. i.e. created local variables will be discarded + * when this routine returns. + */ + +int ejsEvalBlock(Ejs *ep, char *script, EjsVar *vp) +{ + int rc, fid; + + mprAssert(script); + + fid = ejsOpenBlock(ep); + if (fid < 0) { + return fid; + } + + rc = ejsEvalScript(ep, script, vp); + + ejsCloseBlock(ep, fid); + + return rc; +} + +/******************************************************************************/ +/* + * Parse and evaluate a EJS. The script is evaluated at the current context. + * 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. + */ + +int ejsEvalScript(Ejs *ep, const char *script, EjsVar *vp) +{ + int state; + + ejsClearVar(ep, ep->result); + ep->gotException = 0; + + if (script == 0) { + return 0; + } + + /* + * Allocate a new evaluation block, and save the old one + */ + if (ejsLexOpenScript(ep, script) < 0) { + return MPR_ERR_MEMORY; + } + + /* + * Do the actual parsing and evaluation + */ + ep->scriptStatus = 0; + + do { + state = ejsParse(ep, EJS_STATE_BEGIN, EJS_FLAGS_EXE); + + if (state == EJS_STATE_RET) { + state = EJS_STATE_EOF; + } + } while (state != EJS_STATE_EOF && state != EJS_STATE_ERR); + + ejsLexCloseScript(ep); + + if (state == EJS_STATE_ERR) { + return -1; + } + + if (vp) { + /* Caller must not free. */ + *vp = *ep->result; + } + + return ep->scriptStatus; +} + +/******************************************************************************/ + +void ejsSetFileName(Ejs *ep, const char *fileName) +{ + mprFree(ep->fileName); + ep->fileName = mprStrdup(ep, fileName); +} + +/******************************************************************************/ +/* + * Format the stack backtrace + */ + +char *ejsFormatStack(Ejs* ep) +{ + EjsInput *ip; + char *errbuf; + int frame, len; + + mprAssert(ep); + + ip = ep->input; + + errbuf = 0; + + len = 0; + frame = 0; + while (ip && frame < EJS_MAX_BACKTRACE) { + char *traceLine, *newErrbuf, *line; + for (line = ip->line; *line && isspace(*line); line++) { + ; + } + mprAllocSprintf(MPR_LOC_ARGS(ep), &traceLine, MPR_MAX_STRING, + " [%02d] %s, %s, line %d -> %s\n", + frame++, + ip->fileName ? ip->fileName : "script", + ip->procName ? ip->procName: "global", + ip->lineNumber, line); + if (traceLine == 0) { + break; + } + newErrbuf = mprRealloc(ep, errbuf, len + strlen(traceLine) + 1); + if (newErrbuf == NULL) { + break; + } + errbuf = newErrbuf; + memcpy(&errbuf[len], traceLine, strlen(traceLine) + 1); + len += strlen(traceLine); + mprFree(traceLine); + ip = ip->next; + } + return errbuf; +} + +/******************************************************************************/ +/* + * Internal use method to set the error message + * + * Error, ArgError, AssertError, IOError, MemoryError, RangeError, + * ReferenceError, SyntaxError, TypeError, MemoryError + */ + +void ejsError(Ejs* ep, const char *errorType, const char* fmt, ...) +{ + va_list fmtArgs; + EjsVar *error; + char *msg, *stack; + + va_start(fmtArgs, fmt); + mprAllocVsprintf(MPR_LOC_ARGS(ep), &msg, MPR_MAX_STRING, fmt, fmtArgs); + va_end(fmtArgs); + + /* + * Create a new Error exception object. If bad error type, default to + * "Error" + */ + if (ejsGetClass(ep, 0, errorType) == 0) { + errorType = "Error"; + } + ep->gotException = 1; + + error = ejsCreateObj(ep, 0, errorType, msg); + if (error == 0) { + return; + } + mprFree(msg); + + stack = ejsFormatStack(ep); + ejsSetPropertyToString(ep, error, "stack", stack); + mprFree(stack); + + ejsWriteVar(ep, ep->result, error, EJS_SHALLOW_COPY); + ejsFreeVar(ep, error); +} + +/******************************************************************************/ + +void ejsSyntaxError(Ejs *ep, const char *msg) +{ + if (msg == 0) { + msg = " "; + } + ejsError(ep, EJS_SYNTAX_ERROR, msg); +} + +/******************************************************************************/ + +void ejsMemoryError(Ejs *ep) +{ + ejsError(ep, EJS_MEMORY_ERROR, "Memory allocation error"); +} + +/******************************************************************************/ + +void ejsArgError(Ejs *ep, const char *msg) +{ + mprAssert(msg && *msg); + + ejsError(ep, EJS_ARG_ERROR, msg); +} + +/******************************************************************************/ + +void ejsInternalError(Ejs *ep, const char *msg) +{ + mprAssert(msg && *msg); + + ejsError(ep, EJS_INTERNAL_ERROR, msg); +} + +/******************************************************************************/ +/* + * Public routine to set the error message. Caller MUST NOT free. + */ + +char *ejsGetErrorMsg(Ejs *ep) +{ + EjsVar *error; + const char *message, *stack, *name; + char *buf; + + error = ep->result; + + if (! ejsVarIsObject(error)) { + name = message = stack = 0; + } else { + name = ejsGetPropertyAsString(ep, error, "name"); + message = ejsGetPropertyAsString(ep, error, "message"); + stack = ejsGetPropertyAsString(ep, error, "stack"); + } + if (name == 0 || message == 0) { + buf = mprStrdup(ep, "Unspecified execution error\n"); + } else { + mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, + "%s Exception: %s\nStack:\n%s\n", + name, message, stack ? stack : " " ); + } + mprFree(ep->errorMsg); + ep->errorMsg = buf; + return buf; +} + +/******************************************************************************/ +/* + * Get the current line number + */ + +int ejsGetLineNumber(Ejs *ep) +{ + if (ep->input == 0) { + return -1; + } + return ep->input->lineNumber; +} + +/******************************************************************************/ +/* + * Return the local object + */ + +EjsVar *ejsGetLocalObj(Ejs *ep) +{ + return ep->local; +} + +/******************************************************************************/ +/* + * Return the global object + */ + +EjsVar *ejsGetGlobalObj(Ejs *ep) +{ + return ep->global; +} + +/******************************************************************************/ +/* + * Set the expression return value + */ + +void ejsSetReturnValue(Ejs *ep, EjsVar *vp) +{ + mprAssert(ep); + mprAssert(vp); + + if (vp == 0) { + return; + } + ejsWriteVar(ep, ep->result, vp, EJS_SHALLOW_COPY); +} + +/******************************************************************************/ +/* + * Set the expression return value and free the arg. + */ + +void ejsSetReturnValueAndFree(Ejs *ep, EjsVar *vp) +{ + mprAssert(ep); + mprAssert(vp); + + ejsWriteVar(ep, ep->result, vp, EJS_SHALLOW_COPY); + ejsFreeVar(ep, vp); +} + +/******************************************************************************/ +/* + * Set the expression return value to a string value. + */ + +void ejsSetReturnValueToString(Ejs *ep, const char *value) +{ + mprAssert(ep); + mprAssert(value); + + ejsWriteVarAsString(ep, ep->result, value); +} + +/******************************************************************************/ +/* + * Set the expression return value to a binary string value. + */ + +void ejsSetReturnValueToBinaryString(Ejs *ep, const uchar *value, int len) +{ + mprAssert(ep); + mprAssert(value); + + ejsWriteVarAsBinaryString(ep, ep->result, value, len); +} + +/******************************************************************************/ +/* + * Set the expression return value to a integer value. + */ + +void ejsSetReturnValueToInteger(Ejs *ep, int value) +{ + mprAssert(ep); + + ejsWriteVarAsInteger(ep, ep->result, value); +} + +/******************************************************************************/ +/* + * Set the expression return value to an EjsNum value. + */ + +void ejsSetReturnValueToNumber(Ejs *ep, EjsNum value) +{ + mprAssert(ep); + + ejsWriteVarAsNumber(ep, ep->result, value); +} + +/******************************************************************************/ +/* + * Set the expression return value to a boolean value. + */ + +void ejsSetReturnValueToBoolean(Ejs *ep, int value) +{ + mprAssert(ep); + + ejsWriteVarAsBoolean(ep, ep->result, value); +} + +/******************************************************************************/ +/* + * Set the expression return value to a boolean value. + */ + +void ejsSetReturnValueToUndefined(Ejs *ep) +{ + mprAssert(ep); + + ejsWriteVarAsUndefined(ep, ep->result); +} + +/******************************************************************************/ +/* + * Get the expression return value + */ + +EjsVar *ejsGetReturnValue(Ejs *ep) +{ + mprAssert(ep); + + return ep->result; +} + +/******************************************************************************/ + +void *ejsGetUserData(Ejs *ep) +{ + mprAssert(ep); + + return ep->userData; +} + +/******************************************************************************/ +/* + * Get a variable given a full variable spec possibly containing "." or "[]". + */ + +EjsVar *ejsGetVar(Ejs *ep, const char *fullName) +{ + mprAssert(ep); + mprAssert(fullName && *fullName); + + return ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 0); +} + +/******************************************************************************/ +/* + * Get a string var given a full variable spec possibly containing "." or "[]". + */ + +const char *ejsGetStr(Ejs *ep, const char *fullName, const char *defaultValue) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 0); + if (vp == 0 || !ejsVarIsString(vp)) { + return defaultValue; + } + /* MOB -- what about VarToStr */ + return vp->string; +} + +/******************************************************************************/ +/* + * Get an int var given a full variable spec possibly containing "." or "[]". + */ + +int ejsGetInt(Ejs *ep, const char *fullName, int defaultValue) +{ + EjsVar *vp; + + mprAssert(ep); + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 0); + if (vp == 0 || !ejsVarIsInteger(vp)) { + return defaultValue; + } + /* MOB -- should use VarToInt */ + return vp->integer; +} + +/******************************************************************************/ +/* + * Get an bool var given a full variable spec possibly containing "." or "[]". + */ + +int ejsGetBool(Ejs *ep, const char *fullName, int defaultValue) +{ + EjsVar *vp; + + mprAssert(ep); + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 0); + if (vp == 0 || !ejsVarIsBoolean(vp)) { + return defaultValue; + } + /* MOB -- should use VarToBool */ + return vp->boolean; +} + +/******************************************************************************/ +/* + * Set a variable that may be an arbitrarily complex object or array reference. + * Will always define in the top most variable frame. + */ + +int ejsSetVar(Ejs *ep, const char *fullName, const EjsVar *value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) == 0) { + return MPR_ERR_CANT_WRITE; + } + + 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 ejsSetStr(Ejs *ep, const char *fullName, const char *value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + if (ejsWriteVarAsString(ep, vp, value) == 0) { + return MPR_ERR_CANT_WRITE; + } + + 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 ejsSetInt(Ejs *ep, const char *fullName, int value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + /* Can't fail */ + ejsWriteVarAsInteger(ep, 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 ejsSetBool(Ejs *ep, const char *fullName, bool value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + /* Can't fail */ + ejsWriteVarAsBoolean(ep, 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. Free the value passed in. + */ + +int ejsSetVarAndFree(Ejs *ep, const char *fullName, EjsVar *value) +{ + EjsVar *vp; + + mprAssert(fullName && *fullName); + + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, fullName, 1); + if (vp == 0) { + return MPR_ERR_CANT_CREATE; + } + + if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) == 0) { + ejsFreeVar(ep, value); + return MPR_ERR_CANT_WRITE; + } + + ejsFreeVar(ep, value); + return 0; +} + +/******************************************************************************/ +/* + * Delete a variable + */ + +int ejsDeleteVar(Ejs *ep, const char *fullName) +{ + EjsVar *vp; + EjsVar *obj; + char *propertyName; + + vp = ejsFindProperty(ep, &obj, &propertyName, ep->global, ep->local, + fullName, 0); + if (vp == 0) { + return -1; + } + + mprAssert(propertyName); + mprAssert(propertyName); + + return ejsDeleteProperty(ep, obj, propertyName); +} + +/******************************************************************************/ +/* + * 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) { + * // Insufficient args + * return -1; + * } + */ + +int ejsParseArgs(int argc, char **argv, const char *fmt, ...) +{ + va_list vargs; + const char *cp; + char **sp, *s; + int *bp, *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, int*); + 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; +} + +/******************************************************************************/ +/* + * Define the standard classes + */ + +int ejsDefineStandardClasses(Ejs *master) +{ + if (ejsDefineArrayClass(master) != 0 || + ejsDefineBooleanClass(master) != 0 || + ejsDefineErrorClasses(master) != 0 || + ejsDefineFunctionClass(master) != 0 || + ejsDefineNumberClass(master) != 0 || +#if FUTURE + ejsDefineDateClass(master) != 0 || +#endif +#if BLD_FEATURE_EJS_E4X + ejsDefineXmlClasses(master) != 0 || +#endif +#if BLD_FEATURE_EJS_DB && NOT_HERE + ejsDefineDbClasses(master) != 0 || +#endif + ejsDefineStringClass(master) != 0) { + return MPR_ERR_MEMORY; + } + return 0; +} + +/******************************************************************************/ +/* + * Define the EJS System Object Model + */ + +int ejsDefineSystemClasses(Ejs *master) +{ + if (ejsDefineSystemClass(master) != 0 || + ejsDefineAppClass(master) != 0 || + ejsDefineMemoryClass(master) != 0 || + ejsDefineLogClass(master) != 0 || + ejsDefineDebugClass(master) != 0 || + ejsDefineGCClass(master) != 0 || + ejsDefineFileSystemClass(master) != 0 || +#if BREW + ejsDefineFileClass(master) != 0 || + ejsDefineHTTPClass(master) != 0 || +#endif + ejsDefineGlobalProperties(master) != 0) { + return MPR_ERR_MEMORY; + } + return 0; +} + +/******************************************************************************/ +/* + * Terminate the system object model and classes + */ + +int ejsTermSystemClasses(Ejs *master) +{ +#if BREW + ejsTermHTTPClass(master); +#endif + return 0; +} + +/******************************************************************************/ +/* + * Define the EJS object model + */ + +int ejsCreateObjectModel(Ejs *ejs) +{ + EjsProperty *pp; + + pp = ejsSetPropertyToNewObj(ejs, ejs->global, "system", "System", 0); + if (pp == 0) { + return MPR_ERR_MEMORY; + } + + if (ejsSetPropertyToNewObj(ejs, ejsGetVarPtr(pp), "app", "System.App", + 0) == 0) { + return MPR_ERR_MEMORY; + } + return 0; +} + +/******************************************************************************/ + +void ejsTrace(Ejs *ep, const char *fmt, ...) +{ + va_list args; + char buf[MPR_MAX_LOG_STRING]; + int len; + + va_start(args, fmt); + len = mprVsprintf(buf, sizeof(buf) - 1, fmt, args); + va_end(args); + + mprLog(ep, 0, buf, len); + + va_end(args); +} + +/******************************************************************************/ + +bool ejsGotException(Ejs *ep) +{ + return (bool) ep->gotException; +} + +/******************************************************************************/ + +void ejsSetPrimaryHandle(Ejs *ep, void *primaryHandle) +{ + mprAssert(ep); + + ep->primaryHandle = primaryHandle; +} + +/******************************************************************************/ + +void ejsSetAlternateHandle(Ejs *ep, void *alternateHandle) +{ + mprAssert(ep); + + ep->altHandle = alternateHandle; +} + +/******************************************************************************/ + +#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-2.0/ejs/ejs.h b/source4/lib/appweb/ejs-2.0/ejs/ejs.h new file mode 100644 index 0000000000..a926446524 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejs.h @@ -0,0 +1,849 @@ +/* + * ejs.h - EJScript Language (ECMAScript) header. + */ + +/********************************* Copyright **********************************/ +/* + * @copy default.g + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + * Copyright (c) Michael O'Brien, 1994-1995. 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 "mpr.h" +#include "ejsVar.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************* Prototypes *********************************/ +/* + * Constants + */ +#if BLD_FEATURE_SQUEEZE + #define EJS_GC_WORK_QUOTA 160 /* Allocations required before + garbage colllection */ + + #define EJS_PARSE_INCR 256 /* Growth factor */ + #define EJS_MAX_RECURSE 25 /* Sanity for maximum recursion */ + #define EJS_SMALL_OBJ_HASH_SIZE 11 /* Small object hash size */ + #define EJS_LIST_INCR 8 /* Growth increment for lists */ + #define EJS_MAX_BACKTRACE 10 /* Recursion limit for assert */ + +#else + #define EJS_GC_WORK_QUOTA 500 + + #define EJS_PARSE_INCR 1024 + #define EJS_MAX_RECURSE 100 + #define EJS_SMALL_OBJ_HASH_SIZE 11 + #define EJS_LIST_INCR 16 + #define EJS_MAX_BACKTRACE 25 + +#endif + +/* + * Allocation increments for the default interpreter + */ +#define EJS_DEFAULT_VAR_INC 8 /* Var allocation increment */ +#define EJS_DEFAULT_PROP_INC 96 /* Property allocation increment */ +#define EJS_DEFAULT_OBJ_INC 24 /* Object allocation increment */ +#define EJS_DEFAULT_STR_INC 64 /* Object allocation increment */ + +#define EJS_MIN_TIME_FOR_GC 300 /**< Need 1/3 sec for GC */ +#define EJS_GC_MIN_WORK_QUOTA 50 /**< Min to stop thrashing */ + +/* + * Allocation increments for all non-default interpreters + */ +#define EJS_VAR_INC 32 +#define EJS_PROP_INC 64 +#define EJS_OBJ_INC 64 +#define EJS_STR_INC 64 + +#define EJS_INC_FRAMES 8 /* Frame stack increment */ +#define EJS_MAX_FRAMES 64 /* Max frame 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_METHOD_NAME 11 /* methodName( */ +#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 */ +#define EJS_TOK_CLASS 29 /* class */ +#define EJS_TOK_EXTENDS 30 /* extends */ +#define EJS_TOK_PUBLIC 31 /* public */ +#define EJS_TOK_PRIVATE 32 /* private */ +#define EJS_TOK_PROTECTED 33 /* private */ +#define EJS_TOK_TRY 34 /* try */ +#define EJS_TOK_CATCH 35 /* catch */ +#define EJS_TOK_FINALLY 36 /* finally */ +#define EJS_TOK_THROW 37 /* throw */ +#define EJS_TOK_COLON 38 /* : */ +#define EJS_TOK_GET 39 /* get */ +#define EJS_TOK_SET 40 /* set */ +#define EJS_TOK_MODULE 41 /* module */ +#define EJS_TOK_EACH 42 /* each */ + +/* + * 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 /* ! */ + +/** + * EJ Parsing States. Error and Return are be negative. + */ +#define EJS_STATE_ERR -1 /**< Error state */ +#define EJS_STATE_RET -2 /**< Return statement */ +#define EJS_STATE_EOF -3 /**< 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 /* Method 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_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_NEW 0x80 /* In a new stmt() */ +#define EJS_FLAGS_EXIT 0x100 /* Must exit */ +#define EJS_FLAGS_LHS 0x200 /* Left-hand-side of assignment */ +#define EJS_FLAGS_FORIN 0x400 /* In "for (v in ..." */ +#define EJS_FLAGS_CLASS_DEC 0x800 /* "class name [extends] name " */ +#define EJS_FLAGS_TRY 0x2000 /* In a try {} block */ +#define EJS_FLAGS_CATCH 0x4000 /* "catch (variable)" */ +#define EJS_FLAGS_DONT_GC 0x8000 /* Don't garbage collect */ +#define EJS_FLAGS_NO_ARGS 0x10000 /* Accessors don't use args */ +#define EJS_FLAGS_ENUM_HIDDEN 0x20000 /* Enumerate hidden fields */ +#define EJS_FLAGS_ENUM_BASE 0x40000 /* Enumerate base classes */ +#define EJS_FLAGS_TRACE_ARGS 0x80000 /* Support for printv */ +#define EJS_FLAGS_SHARED_SLAB 0x100000/* Using a shared slab */ + +/* + * Exceptions + */ +#define EJS_ARG_ERROR "ArgError" /**< Method argument error */ +#define EJS_ASSERT_ERROR "AssertError" /**< Assertion error */ +#define EJS_EVAL_ERROR "EvalError" /**< General evalation error */ +#define EJS_INTERNAL_ERROR "InternalError" /**< Internal error */ +#define EJS_IO_ERROR "IOError" /**< IO or data error */ +#define EJS_MEMORY_ERROR "MemoryError" /**< Memory allocation error */ +#define EJS_RANGE_ERROR "RangeError" /**< Data out of range (div by 0) */ +#define EJS_REFERENCE_ERROR "ReferenceError"/**< Object or property reference */ +#define EJS_SYNTAX_ERROR "SyntaxError" /**< Javascript syntax error */ +#define EJS_TYPE_ERROR "TypeError" /**< Wrong type supplied */ + +/* + * E4X + */ +#if BLD_FEATURE_EJS_E4X +#if BLD_FEATURE_SQUEEZE +#define E4X_BUF_SIZE 512 /* Initial buffer size for tokens */ +#define E4X_BUF_MAX (32 * 1024) /* Max size for tokens */ +#define E4X_MAX_NODE_DEPTH 24 /* Max nesting of tags */ +#else +#define E4X_BUF_SIZE 4096 +#define E4X_BUF_MAX (128 * 1024) +#define E4X_MAX_NODE_DEPTH 128 +#endif + +#define E4X_MAX_ELT_SIZE (E4X_BUF_MAX-1) +#define E4X_TEXT_PROPERTY "-txt" +#define E4X_TAG_NAME_PROPERTY "-tag" +#define E4X_COMMENT_PROPERTY "-com" +#define E4X_ATTRIBUTES_PROPERTY "-att" +#define E4X_PI_PROPERTY "-pi" +#define E4X_PARENT_PROPERTY "-parent" +#endif + +#if BLD_FEATURE_MULTITHREAD +/** + * Multithreaded lock function + */ +typedef void (*EjsLockFn)(void *lockData); +/** + * Multithreaded unlock function + */ +typedef void (*EjsUnlockFn)(void *lockData); +#endif + +/* + * Token limits + */ +#define EJS_MAX_LINE 128 /* Maximum input line buffer */ +#define EJS_MAX_TOKEN 640 /* Max input parse token */ +#define EJS_TOKEN_STACK 3 /* Put back token stack */ + +/* + * Putback token + */ + +typedef struct EjsToken { + char tokbuf[EJS_MAX_TOKEN]; + int tid; /* Token ID */ +} EjsToken; + +/* + * EJ evaluation block structure + */ +typedef struct EjsInput { + EjsToken putBack[EJS_TOKEN_STACK]; /* Put back token stack */ + int putBackIndex; /* Top of stack index */ + char line[EJS_MAX_LINE]; /* Current line */ + char *fileName; /* File or script name */ + int lineLength; /* Current line length */ + int lineNumber; /* Parse line number */ + int lineColumn; /* Column in line */ + struct EjsInput *next; /* Used for backtraces */ + const char *procName; /* Gives name in backtrace */ + const char *script; /* Input script for parsing */ + char *scriptServp; /* Next token in the script */ + int scriptSize; /* Length of script */ + char tokbuf[EJS_MAX_TOKEN]; /* Current token */ + int tid; /* Token ID */ + char *tokEndp; /* Pointer past end of token */ + char *tokServp; /* Pointer to next token char */ + struct EjsInput *nextInput; /* Free list of input structs */ +} EjsInput; + +/* + * Method call structure + */ +typedef struct EjsProc { + MprArray *args; /* Args for method */ + EjsVar *fn; /* Method definition */ + char *procName; /* Method name */ +} EjsProc; + + +/** + * @overview EJScript Service structure + * @description The EJScript service manages the overall language runtime. It + * is the factory that creates interpreter instances via ejsCreateInterp. + * The EJScript service creates a master interpreter that holds the + * standard language classes and properties. When user interpreters are + * created, they reference (without copying) the master interpreter to + * gain access to the standard classes and types. + * @stability Prototype. + * @library libejs. + * @see ejsOpenService, ejsCloseService, ejsCreateInterp, ejsDestoryInterp + */ +typedef struct EjsService { + EjsVar *globalClass; /* Global class */ + struct Ejs *master; /* Master Interp inherited by all */ +#if BLD_FEATURE_MULTITHREAD + EjsLockFn lock; + EjsUnlockFn unlock; + void *lockData; +#endif +} EjsService; + + +/* + * Memory statistics + */ +typedef struct EjsMemStats { + uint maxMem; + uint usedMem; +} EjsMemStats; + + +/* + * Garbage collection block alignment + */ +#define EJS_ALLOC_ALIGN(ptr) \ + (((ptr) + sizeof(void*) - 1) & ~(sizeof(void*) - 1)) + +/* + * Default GC tune factors + */ +#define EJS_GC_START_THRESHOLD (32 * 1024) + +/* + * The Garbage collector is a generational collector. It ages blocks and + * optimizes the mark / sweep algorithm to focus on new and recent blocks + */ +typedef enum EjsGeneration { + EJS_GEN_NEW = 0, + EJS_GEN_RECENT_1 = 1, + EJS_GEN_RECENT_2 = 2, + EJS_GEN_OLD = 3, + EJS_GEN_PERMANENT = 4, + EJS_GEN_MAX = 5, +} EjsGeneration; + +/* + * Garbage collector control + */ +typedef struct EjsGC { + bool enable; + bool enableDemandCollect; + bool enableIdleCollect; + /* + * maxMemory should be set to be 95% of the real max memory limit + */ + uint maxMemory; /* Above this, Throw Memory exception. */ + int workQuota; /* Quota of work before GC */ + int workDone; /* Count of allocations */ + int degraded; /* Have exceeded maxMemory */ + + /* + * Debug Levels 0-N (increases verbosity) + * 1 -- Sweep and collection count + * 2 -- Trace objects deleted + * 3 -- Trace objects marked + * 4 -- Print alloc report when needing a demand allocation + * + */ + int debugLevel; /* In debug mode */ + int collecting; /* Running garbage collection */ + uint collectionCount; /* Number of times GC ran */ +#if BLD_DEBUG + int gcIndent; /* Indent formatting */ + int objectsInUse; /* Objects currently reachable */ + int propertiesInUse; /* Properties currently reachable */ +#endif +} EjsGC; + +/* + * Slab memory allocation + */ +typedef struct EjsSlab { + uint allocIncrement; /* Growth increment in slab */ + uint size; /* Size of allocations */ + EjsGCLink freeList; /* Free list (only next ptr is used) */ + EjsObj *lastRecentBlock; /* Saved for GC age generations phase */ + EjsGCLink allocList[EJS_GEN_MAX]; /* Allocated block list */ + +#if BLD_FEATURE_ALLOC_STATS + uint totalAlloc; /* Total count of allocation calls */ + uint freeCount; /* Number of blocks on the slab freelist */ + uint allocCount; /* Number of allocated blocks */ + uint peakAllocated; /* Peak allocated */ + uint peakFree; /* Peak on the free list */ + uint totalReclaimed; /* Total blocks reclaimed on sweeps */ + uint totalSweeps; /* Total sweeps */ +#endif +} EjsSlab; + + +/** + * @overview EJ interpreter control structure. + * @description EJ allocates one control structure per active interpreter. + * The \ref ejsCreateInterp routine creates the Ejs structure and returns + * a reference to be used in subsequent EJ API calls. + * @stability Prototype. + * @library libejs. + * @see ejsCreateInterp, ejsDestroyInterp, ejsOpenService + */ +struct Ejs { + void *altHandle; /* Alternate callback handle */ + bool castAlloc; /* True if castTemp is allocated */ + char *castTemp; /* Temporary string for casting */ + char *currentClass; /* Current class name */ + EjsVar *currentObj; /* Ptr to current object */ + EjsVar *thisObject; /* Ptr to current "this" */ + EjsProperty *currentProperty; /* Ptr to current property */ + EjsGC gc; /* Garbage collector control */ + char *errorMsg; /* Error message */ + char *fileName; /* File or script name */ + int lineNumber; /* File line number */ + int scriptStatus; /* Status to exit() */ + int flags; /* Flags */ + MprArray *frames; /* List of variable frames */ + EjsVar *global; /* Global object */ + EjsVar *objectClass; /* Object class */ + int gotException; /* Exception thrown */ + EjsInput *input; /* Input evaluation block */ + int depth; /* Recursion depth */ + EjsVar *local; /* Local object */ + int maxDepth; /* Maximum depth for formatting */ + void *primaryHandle; /* primary callback handle */ + EjsProc *proc; /* Current method */ + int recurseCount; /* Recursion counter */ + EjsVar *result; /* Variable result */ + int tid; /* Current token id */ + char *token; /* Pointer to token string */ + EjsVar tokenNumber; /* Parsed number */ + EjsService *service; /* Service object */ + void *userData; /* Method user data */ + + EjsSlab *slabs; /* Memory allocation slabs */ + MprCtx slabAllocContext; /* Allocation context */ + EjsInput *inputList; /* Free list of input structs */ + +#if BLD_FEATURE_MULTITHREAD + EjsLockFn lock; /* Lock method */ + EjsUnlockFn unlock; /* Unlock method */ + void *lockData; /* Lock data argument */ +#endif +#define EJS_MAX_STACK (10 * 1024) + char stack[EJS_MAX_STACK]; /* Local variable stack */ + char *stkPtr; /* Local variable stack ptr */ + void *inputMarker; /* Recurse protection */ +}; + + +typedef struct EjsModule +{ + int dummy; +} EjsModule; + + +/* + * Method callback when using Alternate handles. GaCompat uses these and + * passes the web server request structure via the altHandle. + */ +typedef void *EjsHandle; +typedef int (*EjsAltCMethod)(Ejs *ejs, EjsHandle altHandle, + EjsVar *thisObj, int argc, EjsVar **argv); +typedef int (*EjsAltStringCMethod)(Ejs *ejs, EjsHandle altHandle, + EjsVar *thisObj, int argc, char **argv); + + +/* + * API Constants + */ +#define EJS_USE_OWN_SLAB 1 + +/******************************** Internal API ********************************/ +/* + * Ejs Lex + */ +extern int ejsLexOpenScript(Ejs *ejs, const char *script); +extern void ejsLexCloseScript(Ejs *ejs); +extern int ejsInitInputState(EjsInput *ip); +extern void ejsLexSaveInputState(Ejs *ejs, EjsInput* state); +extern void ejsLexFreeInputState(Ejs *ejs, EjsInput* state); +extern void ejsLexRestoreInputState(Ejs *ejs, EjsInput* state); +extern int ejsLexGetToken(Ejs *ejs, int state); +extern void ejsLexPutbackToken(Ejs *ejs, int tid, char *string); + +/* + * Parsing + */ +extern int ejsParse(Ejs *ejs, int state, int flags); +extern int ejsGetFlags(Ejs *ejs); + +/* + * Create variable scope blocks + */ +extern int ejsOpenBlock(Ejs *ejs); +extern int ejsSetBlock(Ejs *ejs, EjsVar *local); +extern int ejsCloseBlock(Ejs *ejs, int vid); +extern int ejsEvalBlock(Ejs *ejs, char *script, EjsVar *vp); +extern void ejsSetFileName(Ejs *ejs, const char *fileName); + +/* + * Class definitions + */ +extern EjsVar *ejsCreateSimpleClass(Ejs *ejs, EjsVar *baseClass, + const char *className); +extern int ejsDefineObjectClass(Ejs *ejs); +extern int ejsDefineArrayClass(Ejs *ejs); +extern int ejsDefineBooleanClass(Ejs *ejs); +extern int ejsDefineErrorClasses(Ejs *ejs); +extern int ejsDefineFileClass(Ejs *ejs); +extern int ejsDefineFileSystemClass(Ejs *ejs); +extern int ejsDefineHTTPClass(Ejs *ejs); +extern int ejsDefineFunctionClass(Ejs *ejs); +extern int ejsDefineNumberClass(Ejs *ejs); +extern int ejsDefineStringClass(Ejs *ejs); +extern int ejsDefineDateClass(Ejs *ejs); +extern int ejsDefineStandardClasses(Ejs *ejs); + +#if BLD_FEATURE_EJS_E4X +extern int ejsDefineXmlClasses(Ejs *ejs); +extern EjsVar *ejsCreateXml(Ejs *ejs); +#endif + +#if BLD_FEATURE_EJS_DB +extern int ejsDefineDbClasses(Ejs *ejs); +#endif + +/* + * System class definitions + */ +extern int ejsDefineSystemClasses(Ejs *ejs); +extern int ejsDefineSystemClass(Ejs *ejs); +extern int ejsDefineAppClass(Ejs *ejs); +extern int ejsDefineDebugClass(Ejs *ejs); +extern int ejsDefineLogClass(Ejs *ejs); +extern int ejsDefineMemoryClass(Ejs *ejs); +extern int ejsDefineGCClass(Ejs *ejs); +extern int ejsDefineGlobalProperties(Ejs *ejs); + +extern int ejsTermSystemClasses(Ejs *ejs); +extern void ejsTermHTTPClass(Ejs *ejs); + +extern int ejsCreateObjectModel(Ejs *ejs); + +/* + * Class constructors + */ +extern int ejsArrayConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsXmlConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsXmlListConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsBooleanConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **agv); +extern int ejsFunctionConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **agv); +extern int ejsNumberConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsStringConstructor(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv); +extern int ejsDateConstructor(Ejs *ejs, EjsVar *thisObj, + int argc, EjsVar **argv); + +/* + * Garbage collection + */ +extern void ejsGCInit(Ejs *ejs, int objInc, int propInc, int varInc, + int strInc); +extern int ejsIsTimeForGC(Ejs *ep, int timeTillNextEvent); + +extern bool ejsSetGCDebugLevel(Ejs *ep, int debugLevel); +extern void ejsSweepAll(Ejs *ep); + +extern EjsObj *ejsAllocObj(EJS_LOC_DEC(ejs, loc)); +extern EjsProperty *ejsAllocProperty(EJS_LOC_DEC(ejs, loc)); +extern EjsVar *ejsAllocVar(EJS_LOC_DEC(ejs, loc)); +extern void ejsFree(Ejs *ejs, void *ptr, int slabIndex); + +extern int ejsCollectGarbage(Ejs *ejs, int slabIndex); +extern int ejsIncrementalCollectGarbage(Ejs *ejs); + +#if BLD_DEBUG +extern void ejsDumpObjects(Ejs *ejs); +#endif + +#if BLD_FEATURE_ALLOC_STATS +extern void ejsPrintAllocReport(Ejs *ejs, bool printLeakReport); +#endif + +extern void ejsCleanInterp(Ejs *ejs, bool doStats); +extern void ejsSetInternalMethods(Ejs *ejs, EjsVar *op); +extern void ejsSetPrimaryHandle(Ejs *ep, void *primaryHandle); +extern void ejsSetAlternateHandle(Ejs *ep, void *alternateHandle); +extern void *ejsGetUserData(Ejs *ejs); + +/* + * Could possibly make these routines public + */ + +extern int ejsSetGCMaxMemory(Ejs *ep, uint maxMemory); +extern uint ejsGetUsedMemory(Ejs *ejs); +extern uint ejsGetAllocatedMemory(Ejs *ejs); +extern uint ejsGetAvailableMemory(Ejs *ejs); +extern char *ejsFormatStack(Ejs* ep);; + +/********************************* Prototypes *********************************/ +#if BLD_FEATURE_MULTITHREAD +extern int ejsSetServiceLocks(EjsService *sp, EjsLockFn lock, + EjsUnlockFn unlock, void *data); +#endif + +/* + * Ejs service and interpreter management + */ +extern EjsService *ejsOpenService(MprCtx ctx); +extern void ejsCloseService(EjsService *sp, bool doStats); + +extern Ejs *ejsCreateInterp(EjsService *sp, void *primaryHandle, + void *altHandle, EjsVar *global, bool useOwnSlab); +extern void ejsDestroyInterp(Ejs *ejs, bool doStats); + +extern Ejs *ejsGetMasterInterp(EjsService *sp); +extern EjsVar *ejsGetGlobalClass(Ejs *ejs); + +/* + * Module support + */ +extern EjsModule *ejsCreateModule(const char *name, const char *version, + int (*start)(EjsModule*), int (*stop)(EjsModule*)); + +/* + * Native Objects + */ + +void ejsSetNativeData(EjsVar *obj, void *data); +void ejsSetNativeHelpers(Ejs *ejs, EjsVar *nativeClass, + int (*createInstance)(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv), + void (*disposeInstance)(Ejs *ejs, EjsVar *thisObj), + bool (*hasProperty)(Ejs *ejs, EjsVar *thisObj, const char *prop), + int (*deleteProperty)(Ejs *ejs, EjsVar *thisObj, const char *prop), + int (*getProperty)(Ejs *ejs, EjsVar *thisObj, const char *prop, + EjsVar *dest), + int (*setProperty)(Ejs *ejs, EjsVar *thisObj, const char *prop, + EjsVar *value), + int (*doOperator)(Ejs *ejs, EjsVar *thisObj, EjsOp *op, EjsVar + *result, EjsVar *lhs, EjsVar *rhs, int *code) + ); + +/* + * Evaluation methods + */ +extern int ejsEvalFile(Ejs *ejs, const char *path, EjsVar *result); +extern int ejsEvalScript(Ejs *ejs, const char *script, EjsVar *result); +extern int ejsRunMethod(Ejs *ejs, EjsVar *obj, + const char *methodName, MprArray *args); +extern int ejsRunMethodCmd(Ejs *ejs, EjsVar *obj, + const char *methodName, const char *cmdFmt, ...); +extern EjsVar *ejsGetReturnValue(Ejs *ejs); + +extern EjsVar *ejsGetLocalObj(Ejs *ejs); +extern EjsVar *ejsGetGlobalObj(Ejs *ejs); + +/* + * Define a class in the specified interpreter. If used with the default + * interpeter, then the class is defined for all interpreters. + */ +extern EjsVar *ejsDefineClass(Ejs *ejs, const char *className, + const char *extends, EjsCMethod constructor); +extern EjsVar *ejsGetClass(Ejs *ejs, EjsVar *parentClass, + const char *className); + +extern const char *ejsGetClassName(EjsVar *obj); +extern const char *ejsGetBaseClassName(EjsVar *obj); + +extern bool ejsIsSubClass(EjsVar *target, EjsVar *baseClass); +extern EjsVar *ejsGetBaseClass(EjsVar *obj); +extern void ejsSetBaseClass(EjsVar *obj, EjsVar *baseClass); + + +#define ejsCreateSimpleObj(ejs, className) \ + ejsCreateSimpleObjInternal(EJS_LOC_ARGS(ejs), className) +extern EjsVar *ejsCreateSimpleObjInternal(EJS_LOC_DEC(ejs, loc), + const char *className); + +#define ejsCreateSimpleObjUsingClass(ejs, baseClass) \ + ejsCreateSimpleObjUsingClassInt(EJS_LOC_ARGS(ejs), \ + baseClass) +extern EjsVar *ejsCreateSimpleObjUsingClassInt(EJS_LOC_DEC(ejs, loc), + EjsVar *baseClass); + +/* + * This will create an object and call all required constructors + */ +extern EjsVar *ejsCreateObj(Ejs *ejs, EjsVar *obj, + const char *className, const char *constructorArgs); + +#define ejsCreateObjUsingArgv(ejs, obj, className, args) \ + ejsCreateObjUsingArgvInternal(EJS_LOC_ARGS(ejs), obj, \ + className, args) +extern EjsVar *ejsCreateObjUsingArgvInternal(EJS_LOC_DEC(ejs, loc), + EjsVar *obj, const char *className, MprArray *args); + +#define ejsCreateArray(ejs, size) \ + ejsCreateArrayInternal(EJS_LOC_ARGS(ejs), size) +extern EjsVar *ejsCreateArrayInternal(EJS_LOC_DEC(ejs, loc), + int size); + +/* + * Array methods. MOB -- need other array methods + */ +/* MOB -- spell out element */ +extern EjsVar *ejsAddArrayElt(Ejs *ejs, EjsVar *op, EjsVar *element, + EjsCopyDepth copyDepth); +/* + * Required: Array methods + * + array = obj.getMethods(); + array = obj.getProperties(); + + array.property.isPublic(); + array.property.isPrivate(); + array.property.isMethod(); + array.property.isEnumerable(); + array.property.isReadOnly(); + array.property.allowsNonUnique(); + array.property.getParent(); +*/ + +/* MOB -- should we have an API that takes a EjsCopyDepth */ +extern void ejsSetReturnValue(Ejs *ejs, EjsVar *vp); +extern void ejsSetReturnValueAndFree(Ejs *ejs, EjsVar *vp); +extern void ejsSetReturnValueToBoolean(Ejs *ejs, bool value); +extern void ejsSetReturnValueToBinaryString(Ejs *ejs, + const uchar *value, int len); +extern void ejsSetReturnValueToInteger(Ejs *ejs, int value); +extern void ejsSetReturnValueToNumber(Ejs *ejs, EjsNum value); +extern void ejsSetReturnValueToString(Ejs *ejs, const char *value); +extern void ejsSetReturnValueToUndefined(Ejs *ejs); + +/* + * Variable access and control. The fullName arg can contain "[]" and "." + */ +extern bool ejsGetBool(Ejs *ejs, const char *fullName, bool defaultValue); +extern int ejsGetInt(Ejs *ejs, const char *fullName, int defaultValue); +extern const char *ejsGetStr(Ejs *ejs, const char *fullName, + const char *defaultValue); +extern EjsVar *ejsGetVar(Ejs *ejs, const char *fullName); + +extern int ejsSetBool(Ejs *ejs, const char *fullName, bool value); +extern int ejsSetInt(Ejs *ejs, const char *fullName, int value); +extern int ejsSetStr(Ejs *ejs, const char *fullName, const char *value); +extern int ejsSetVar(Ejs *ejs, const char *fullName, const EjsVar *value); +extern int ejsSetVarAndFree(Ejs *ejs, const char *fullName, EjsVar *value); + +extern int ejsDeleteVar(Ejs *ejs, const char *fullName); + +/* + * Error handling + */ +extern void ejsError(Ejs *ejs, const char *errorType, const char *fmt, + ...) PRINTF_ATTRIBUTE(3,4); +/* MOB -- this should take no arguments */ +extern void ejsArgError(Ejs *ejs, const char *msg); +extern void ejsInternalError(Ejs *ejs, const char *msg); +extern void ejsMemoryError(Ejs *ejs); +extern void ejsSyntaxError(Ejs *ejs, const char *msg); + +/* + * Utility methods + */ +extern int ejsParseArgs(int argc, char **argv, const char *fmt, ...); + +extern void ejsExit(Ejs *ejs, int status); +extern bool ejsIsExiting(Ejs *ejs); +extern void ejsClearExiting(Ejs *ejs); + +extern bool ejsGotException(Ejs *ejs); + +/* MOB -- rename Method to Function */ +extern void ejsFreeMethodArgs(Ejs *ep, MprArray *args); +extern int ejsStrcat(Ejs *ep, EjsVar *dest, EjsVar *src); + +/* + * Debugging routines + */ +extern char *ejsGetErrorMsg(Ejs *ejs); +extern int ejsGetLineNumber(Ejs *ejs); +extern void ejsTrace(Ejs *ejs, const char *fmt, ...); + +/* + * Multithreaded lock routines + */ +#if BLD_FEATURE_MULTITHREAD +#define ejsLock(sp) if (sp->lock) { (sp->lock)(sp->lockData); } else +#define ejsUnlock(sp) if (sp->unlock) { (sp->unlock)(sp->lockData); } else +#else +#define ejsLock(sp) +#define ejsUnlock(sp) +#endif + +#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-2.0/ejs/ejsClass.c b/source4/lib/appweb/ejs-2.0/ejs/ejsClass.c new file mode 100644 index 0000000000..58609adf3f --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsClass.c @@ -0,0 +1,273 @@ +/* + * @file ejsClass.c + * @brief EJS class support + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************* Includes ***********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS + +/************************************ Code ************************************/ +/* + * Internal API + * + * Routine to create a simple class object. This routine will create a + * stand-alone class object. Callers must insert this into the relevant + * "global" object for name resolution. From these class objects, instance + * objects may be created via the javascript "new" command. + * + * Users should use ejsDefineClass + */ + +EjsVar *ejsCreateSimpleClass(Ejs *ep, EjsVar *baseClass, const char *className) +{ + EjsProperty *pp; + EjsVar *classObj; + + /* + * Create an instance of an Object to act as the static class object + */ + classObj = ejsCreateSimpleObjUsingClass(ep, baseClass); + if (classObj == 0) { + mprAssert(classObj); + return 0; + } + ejsSetClassName(ep, classObj, className); + + /* + * Set the propotype property to point to this class. + * Note: this is a self reference so the alive bit will not be turned on. + */ + pp = ejsSetProperty(ep, classObj, "prototype", classObj); + ejsMakePropertyEnumerable(pp, 0); + + return classObj; +} + +/******************************************************************************/ +/* + * Define a class in the given interpreter. If parentClass is specified, the + * class is defined in the parent. Otherwise, the class will be defined + * locally/globally. ClassName and extends are full variable specs + * (may contain ".") + */ + +EjsVar *ejsDefineClass(Ejs *ep, const char *className, const char *extends, + EjsCMethod constructor) +{ + EjsVar *parentClass, *classObj, *baseClass, *vp; + char *name; + char *cp; + + /* + * If the className is a qualified name (with "."), then get the + * parent class name. + */ + name = mprStrdup(ep, className); + cp = strrchr(name, '.'); + if (cp != 0) { + *cp++ = '\0'; + className = cp; + parentClass = ejsFindProperty(ep, 0, 0, ep->global, ep->local, name, 0); + if (parentClass == 0 || parentClass->type != EJS_TYPE_OBJECT) { + mprError(ep, MPR_LOC, "Can't find class's parent class %s", name); + mprFree(name); + return 0; + } + + } else { + /* + * Simple class name without a "." so create the class locally + * if a local scope exists, otherwise globally. + */ + parentClass = (ep->local) ? ep->local : ep->global; + } + + if (parentClass == 0) { + mprError(ep, MPR_LOC, "Can't find parent class"); + mprFree(name); + return 0; + } + + /* OPT should use function that doesn't parse [] . */ + baseClass = ejsGetClass(ep, 0, extends); + if (baseClass == 0) { + mprAssert(baseClass); + mprFree(name); + return 0; + } + + classObj = ejsCreateSimpleClass(ep, baseClass, className); + if (classObj == 0) { + mprAssert(classObj); + mprFree(name); + return 0; + } + + if (constructor) { + ejsDefineCMethod(ep, classObj, className, constructor, 0); + } + + ejsSetPropertyAndFree(ep, parentClass, className, classObj); + + vp = ejsGetPropertyAsVar(ep, parentClass, className); + mprFree(name); + + return vp; +} + +/******************************************************************************/ +/* + * Find a class and return the property defining the class. ClassName may + * contain "." and is interpreted relative to obj. Obj is typically some + * parent object, ep->local or ep->global. If obj is null, then the global + * space is used. + */ + +EjsVar *ejsGetClass(Ejs *ep, EjsVar *obj, const char *className) +{ + EjsVar *vp; + + mprAssert(ep); + + /* + * Search first for a constructor of the name of class + * global may not be defined yet. + */ + if (obj) { + vp = ejsFindProperty(ep, 0, 0, obj, 0, className, 0); + + } else { + mprAssert(ep->global); + vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, className, 0); + } + if (vp == 0 || vp->type != EJS_TYPE_OBJECT) { + return 0; + } + + /* + * Return a reference to the prototype (self) reference. This + * ensures that even if "obj" is deleted, this reference will remain + * usable. + */ + return ejsGetPropertyAsVar(ep, vp, "prototype"); +} + +/******************************************************************************/ +/* + * Return the class name of a class or object + */ + +const char *ejsGetClassName(EjsVar *vp) +{ + EjsObj *obj; + + mprAssert(vp); + mprAssert(vp->type == EJS_TYPE_OBJECT); + mprAssert(vp->objectState->baseClass); + + if (vp == 0 || !ejsVarIsObject(vp)) { + return 0; + } + obj = vp->objectState; + + return obj->className; +} + +/******************************************************************************/ +/* + * Return the class name of an objects underlying class + * If called on an object, it returns the base class. + * If called on a class, it returns the base class for the class. + */ + +const char *ejsGetBaseClassName(EjsVar *vp) +{ + EjsObj *obj; + + mprAssert(vp); + mprAssert(vp->type == EJS_TYPE_OBJECT); + mprAssert(vp->objectState->baseClass); + + if (vp == 0 || !ejsVarIsObject(vp)) { + return 0; + } + obj = vp->objectState; + if (obj->baseClass == 0) { + return 0; + } + mprAssert(obj->baseClass->objectState); + + return obj->baseClass->objectState->className; +} + +/******************************************************************************/ + +EjsVar *ejsGetBaseClass(EjsVar *vp) +{ + if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) { + mprAssert(0); + return 0; + } + return vp->objectState->baseClass; +} + +/******************************************************************************/ + +void ejsSetBaseClass(EjsVar *vp, EjsVar *baseClass) +{ + if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) { + mprAssert(0); + return; + } + vp->objectState->baseClass = baseClass; +} + +/******************************************************************************/ + +#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-2.0/ejs/ejsCmd.c b/source4/lib/appweb/ejs-2.0/ejs/ejsCmd.c new file mode 100644 index 0000000000..b5279c949a --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsCmd.c @@ -0,0 +1,468 @@ +/* + * @file ejsCmd.c + * @brief Embedded JavaScript (EJS) command line program. + * @overview + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS && !BREW + +/************************************ Defines *********************************/ + +#define EJS_MAX_CMD_LINE (16 * 1024) +#define EJS_MAX_SCRIPT (4 * 1024 * 1024) +#define EJS_MAX_RESULT_SIZE (4 * 1024 * 1024) +#define EJS_PROMPT "ejs> " + +/****************************** Forward Declarations **************************/ + +static int parseFile(EjsService *ejsService, Ejs *ejs, const char *fileName, + const char *testName, MprFile *testLogFile); +static int ifConsole(); + +static int interactiveUse(MprApp *app, Ejs *ejs, FILE *input, + char *fileName); +static char *readCmd(MprApp *app, FILE *input); + +static int memoryFailure(MprApp *app, uint size, uint total, bool granted); + +static int isConsole = 0; +static int traceCmds = 0; +static int stats = 0; +static int verbose = 0; + +/************************************ Main ************************************/ + +int main(int argc, char *argv[]) +{ + MprApp *app; + const char *programName; + MprFile *testLogFile; + EjsService *ejsService; + Ejs *ejs; + char *commandLine; + const char *testName; + char *argp, *cmd, *testLog; + int i, rc, nextArg, err, len, firstArg, iterations, debugLevel; + + app = mprInit(memoryFailure); + + isConsole = ifConsole(); + programName = mprGetBaseName(argv[0]); + debugLevel = 0; + + ejsService = ejsOpenService(app); + if (ejsService == 0) { + mprError(app, MPR_LOC, "Can't initialize the EJS service."); + return -1; + } + + err = 0; + iterations = 1; + stats = 0; + testLog = getenv("TEST_LOG"); + testLogFile = 0; + testName = 0; + + for (nextArg = 1; nextArg < argc; nextArg++) { + argp = argv[nextArg]; + if (*argp != '-') { + break; + } + if (strcmp(argp, "--debug") == 0) { + if (nextArg >= argc) { + err++; + } else { + debugLevel = atoi(argv[++nextArg]); + } + + } else if (strcmp(argp, "--stats") == 0) { + stats++; + + } else if (strcmp(argp, "--trace") == 0) { + traceCmds++; + + } else if (strcmp(argp, "--iterations") == 0) { + if (nextArg >= argc) { + err++; + } else { + iterations = atoi(argv[++nextArg]); + } + + } else if (strcmp(argp, "--log") == 0) { + /* Get file to log test results to when using ejs as a test shell */ + if (nextArg >= argc) { + err++; + } else { + testLog = argv[++nextArg]; + } + + } else if (strcmp(argp, "--testName") == 0) { + if (nextArg >= argc) { + err++; + } else { + testName = argv[++nextArg]; + } + + } else if (strcmp(argp, "-v") == 0) { + verbose++; + + } else if (strcmp(argp, "-vv") == 0) { + verbose += 2; + + } else if (strcmp(argp, "--verbose") == 0) { + verbose += 2; + + } else { + err++; + break; + } + if (err) { + mprErrorPrintf(app, + "Usage: %s [options] files... or\n" + " %s < file or\n" + " %s or\n" + " Switches:\n" + " --iterations num # Number of iterations to eval file\n" + " --stats # Output stats on exit\n" + " --testName name # Set the test name", + programName, programName, programName); + return -1; + } + } + + if (testName) { + i = 0; + commandLine = 0; + len = mprAllocStrcat(MPR_LOC_ARGS(app), &commandLine, 0, " ", + mprGetBaseName(argv[i++]), 0); + for (; i < argc; i++) { + len = mprReallocStrcat(MPR_LOC_ARGS(app), &commandLine, 0, len, + " ", argv[i], 0); + } + mprPrintf(app, " %s\n", commandLine); + } + if (testLog) { + testLogFile = mprOpen(app, testLog, + O_CREAT | O_APPEND | O_WRONLY | O_TEXT, 0664); + if (testLogFile == 0) { + mprError(app, MPR_LOC, "Can't open %s", testLog); + return MPR_ERR_CANT_OPEN; + } + mprFprintf(testLogFile, "\n %s\n", commandLine); + } + + ejs = ejsCreateInterp(ejsService, 0, 0, 0, 0); + if (ejs == 0) { + mprError(app, MPR_LOC, "Can't create EJS interpreter"); + ejsCloseService(ejsService, stats); + if (testLogFile) { + mprClose(testLogFile); + } + mprTerm(app, stats); + exit(-1); + } + + if (debugLevel > 0) { + ejsSetGCDebugLevel(ejs, debugLevel); + } + + rc = 0; + + if (nextArg < argc) { + /* + * Process files supplied on the command line + */ + firstArg = nextArg; + for (i = 0; i < iterations; i++) { + for (nextArg = firstArg; nextArg < argc; nextArg++) { + rc = parseFile(ejsService, ejs, argv[nextArg], testName, + testLogFile); + if (rc < 0) { + return rc; + } + } + } + if (testName) { + if (verbose == 1) { + mprPrintf(app, "\n"); + } + if (verbose <= 1) { + mprPrintf(app, " # PASSED all tests for \"%s\"\n", testName); + } + } + + } else if (! isConsole) { + /* + * Read a script from stdin + */ + cmd = readCmd(app, stdin); + + ejsSetFileName(ejs, "stdin"); + + rc = ejsEvalScript(ejs, cmd, 0); + if (rc < 0) { + mprPrintf(app, "ejs: Error: %s\n", ejsGetErrorMsg(ejs)); + } + mprFree(cmd); + + } else { + /* + * Interactive use. Read commands from the command line. + */ + rc = interactiveUse(app, ejs, stdin, "stdin"); + } + + /* + * Cleanup. Do stats if required. + */ + if (ejs) { + ejsCleanInterp(ejs, 0); + ejsCleanInterp(ejs->service->master, 0); + ejsDestroyInterp(ejs, 0); + } + + ejsCloseService(ejsService, stats); + + if (testLogFile) { + mprClose(testLogFile); + } + + mprTerm(app, stats); + return rc; +} + +/******************************************************************************/ + +static int parseFile(EjsService *ejsService, Ejs *ejs, const char *fileName, + const char *testName, MprFile *testLogFile) +{ + int rc; + + if (testName && verbose == 1) { + mprPrintf(ejs, "."); + } + if (verbose > 1) { + mprPrintf(ejs, "File: %s\n", fileName); + } + + rc = ejsEvalFile(ejs, fileName, 0); + + if (testName) { + char fileBuf[MPR_MAX_FNAME], *cp; + mprStrcpy(fileBuf, sizeof(fileBuf), fileName); + if ((cp = strstr(fileBuf, ".ejs")) != 0) { + *cp = '\0'; + } + if (rc == 0) { + if (verbose > 1) { + mprPrintf(ejs, " # PASSED test \"%s.%s\"\n", testName, + fileBuf); + } + if (testLogFile) { + mprFprintf(testLogFile, " # PASSED test \"%s.%s\"\n", + testName, fileBuf); + } + + } else { + + mprPrintf(ejs, "FAILED test \"%s.%s\"\nDetails: %s\n", + testName, fileBuf, ejsGetErrorMsg(ejs)); + + if (testLogFile) { + mprFprintf(testLogFile, + "FAILED test \"%s.%s\"\nDetails: %s\n", + testName, fileBuf, ejsGetErrorMsg(ejs)); + } + } + } else if (rc < 0) { + mprPrintf(ejs, "ejs: %sIn file \"%s\"\n", + ejsGetErrorMsg(ejs), fileName); + } + return rc; +} + +/******************************************************************************/ + +static char *readCmd(MprApp *app, FILE *input) +{ + char line[EJS_MAX_CMD_LINE]; + char *cmd; + int len, cmdLen; + + cmd = 0; + cmdLen = 0; + + line[sizeof(line) - 1] = '\0'; + + while (1) { + + if (fgets(line, sizeof(line) - 1, input) == NULL) { + break; + } + + len = strlen(line); + + if (line[len - 1] == '\\') { + line[len - 1] = '\0'; + } + cmdLen = mprReallocStrcat(MPR_LOC_ARGS(app), &cmd, EJS_MAX_SCRIPT, + cmdLen, 0, line, 0); + } + return cmd; +} + +/******************************************************************************/ + +static int interactiveUse(MprApp *app, Ejs *ejs, FILE *input, char *fileName) +{ + EjsVar result; + char line[EJS_MAX_CMD_LINE]; + char *cmd, *buf; + int len, cmdLen, rc; + + cmd = 0; + cmdLen = 0; + + line[sizeof(line) - 1] = '\0'; + + ejsSetFileName(ejs, "console"); + + while (! ejsIsExiting(ejs)) { + + if (isConsole) { + write(1, EJS_PROMPT, strlen(EJS_PROMPT)); + } + + if (fgets(line, sizeof(line) - 1, input) == NULL) { + break; + } + + len = strlen(line); + while (len > 0 && + (line[len - 1] == '\n' || line[len - 1] == '\r')) { + len--; + line[len] = '\0'; + } + + if (line[len - 1] == '\\') { + line[len - 1] = '\0'; + cmdLen = mprReallocStrcat(MPR_LOC_ARGS(app), &cmd, EJS_MAX_SCRIPT, + cmdLen, 0, line, 0); + + } else { + + cmdLen = mprReallocStrcat(MPR_LOC_ARGS(app), &cmd, EJS_MAX_SCRIPT, + cmdLen, 0, line, 0); + + + if (traceCmds) { + mprPrintf(ejs, "# %s\n", cmd); + } + + if (cmd[0] == 0x4 || cmd[0] == 0x26 || strcmp(cmd, "quit") == 0) { + ejsExit(ejs, 0); + + } else if ((rc = ejsEvalScript(ejs, cmd, &result)) < 0) { + + mprPrintf(app, "ejs: Error: %s\n", ejsGetErrorMsg(ejs)); + + if (! isConsole) { + return rc; + } + + } else { + if (isConsole || traceCmds) { + buf = ejsVarToString(ejs, &result); + mprPrintf(ejs, "%s\n", buf); + } + } + mprFree(cmd); + cmd = 0; + cmdLen = 0; + } + } + return 0; +} + +/******************************************************************************/ + +static int ifConsole() +{ +#if WIN + INPUT_RECORD irec[1]; + int records = 0; + + if (PeekConsoleInput(GetStdHandle(STD_INPUT_HANDLE), irec, 1, + &records) != 0) { + return 1; + } +#else + return isatty(0); +#endif + return 0; +} + +/******************************************************************************/ + +static int memoryFailure(MprApp *app, uint size, uint total, bool granted) +{ + if (!granted) { + mprPrintf(app, "Can't allocate memory block of size %d\n", size); + mprPrintf(app, "Total memory used %d\n", total); + exit(255); + } + mprPrintf(app, "Memory request for %d bytes exceeds memory red-line\n", + size); + mprPrintf(app, "Total memory used %d\n", total); + return 0; +} + +/******************************************************************************/ + +#else +void ejsCmdLineDummy() {} + +/******************************************************************************/ +#endif /* BLD_FEATURE_EJS */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c b/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c new file mode 100755 index 0000000000..264da05721 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsGarbage.c @@ -0,0 +1,1214 @@ +/* + * @file ejsGarbage.c + * @brief EJS Garbage collector. + * @overview This implements a generational mark and sweep collection scheme. + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "ejs.h" + +#if BLD_FEATURE_EJS + +/****************************** Forward Declarations **************************/ + +static void mark(Ejs *ep); +static void markObjByVar(Ejs *ep, EjsVar *op); +static void markObj(EjsObj *obj); +static void markPerm(Ejs *ep, uint gen); +static int sweep(Ejs *ep, uint gen); +static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex); +static void ejsGracefulDegrade(Ejs *ep); +static void resetMarks(Ejs *ep, EjsSlab *slab); + +#if FUTURE +static void ageGenerations(Ejs *ep); +#endif + +#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) +uint breakAddr; +#endif + +/************************************* Code ***********************************/ + +void ejsGCInit(Ejs *ep, int objInc, int propInc, int varInc, int strInc) +{ + EjsSlab *slab; + + if (ep->service && ep->service->globalClass) { + ep->service->globalClass->objectState->gcMarked = 1; + } + + slab = &ep->slabs[EJS_SLAB_OBJ]; + slab->allocIncrement = objInc; + slab->size = EJS_ALLOC_ALIGN(sizeof(EjsObj)); + + slab = &ep->slabs[EJS_SLAB_PROPERTY]; + slab->allocIncrement = propInc; + slab->size = EJS_ALLOC_ALIGN(sizeof(EjsProperty)); + + slab = &ep->slabs[EJS_SLAB_VAR]; + slab->allocIncrement = varInc; + slab->size = EJS_ALLOC_ALIGN(sizeof(EjsVar)); + + /* + * Initialize GC. + * Enable GC both idle and demand collections. + * Set no limits and garbage collect if the slabs are + * empty and we have used more than the THRESHOLD of ram. + */ + ep->gc.debugLevel = 0; + ep->gc.enable = 1; + ep->gc.enableIdleCollect = 1; + ep->gc.enableDemandCollect = 1; + ep->gc.workQuota = EJS_GC_WORK_QUOTA; + ep->gc.maxMemory = 0; +} + + +/******************************************************************************/ +#if BLD_FEATURE_ALLOC_STATS + +void ejsPrintAllocReport(Ejs *ep, bool printLeakReport) +{ + EjsSlab *slab; + char *name; + int slabIndex, isObj; + + for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) { + slab = &ep->slabs[slabIndex]; + if (slabIndex == EJS_SLAB_VAR) { + name = "var"; + } else if (slabIndex == EJS_SLAB_PROPERTY) { + name = "prop"; + } else { + name = "obj"; + } + mprLog(ep, 0, " "); + mprLog(ep, 0, " GC \"%s\" local slab", name); + mprLog(ep, 0, " Total blocks %14d", + slab->allocCount + slab->freeCount); + mprLog(ep, 0, " Block size %14d", slab->size); + mprLog(ep, 0, " Slab RAM allocated %14d", + (slab->allocCount + slab->freeCount) * slab->size); + mprLog(ep, 0, " Slab RAM in use %14d", + slab->allocCount * slab->size); + mprLog(ep, 0, " Blocks in use %14d", slab->allocCount); + mprLog(ep, 0, " Free blocks %14d", slab->freeCount); + mprLog(ep, 0, " Peak allocated %14d", slab->peakAllocated); + mprLog(ep, 0, " Peak free %14d", slab->peakFree); + mprLog(ep, 0, " Total allocations %14d", slab->totalAlloc); + mprLog(ep, 0, " Total blocks reclaimed %14d", slab->totalReclaimed); + mprLog(ep, 0, " Total sweeps %14d", slab->totalSweeps); + mprLog(ep, 0, " Allocation inc %14d", slab->allocIncrement); + } + + mprLog(ep, 0, " "); + mprLog(ep, 0, " Total EJS memory in use %10d", ejsGetUsedMemory(ep)); + mprLog(ep, 0, " Total EJS memory allocated %10d", + ejsGetAllocatedMemory(ep)); + + if (printLeakReport) { + mprLog(ep, 0, " "); + for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) { + int size; + + slab = &ep->slabs[slabIndex]; + + isObj = 0; + mprLog(ep, 0, " "); + if (slabIndex == EJS_SLAB_VAR) { + name = "var"; + size = sizeof(EjsVar); + } else if (slabIndex == EJS_SLAB_PROPERTY) { + name = "prop"; + size = sizeof(EjsProperty); + } else { + name = "obj"; + size = sizeof(EjsObj); + isObj++; + } +#if BLD_FEATURE_ALLOC_LEAK_TRACK +{ + EjsGCLink *lp; + EjsObj *obj; + int count; + + mprLog(ep, 0, "EJS Leak Report for \"%s\"", name); + count = 0; + + for (lp = slab->allocList[0].next; lp; lp = lp->next) { + mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, size); + if (isObj) { + obj = (EjsObj*) lp; + mprLog(ep, 0, " %-20s %10d %s %s", + lp->allocatedBy, size, + obj->permanent ? "permanent" : "", + obj->alive ? "alive" : "" + ); + } else { + mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, + size); + } + count++; + } + mprLog(ep, 0, " Total blocks %14d", count); +} +#endif + } + mprLog(ep, 0, " "); + } +} + +#endif +/******************************************************************************/ +/* + * Slab allocator + */ + +static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex) +{ + EjsSlab *slab; + EjsGCLink *block; + EjsGC *gc; + uint allocatedMemory; + int i; + + mprStackCheck(ep); + + if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) { + mprAssert(0); + return 0; + } + + /* + * See if the slab has some free blocks + */ + slab = &ep->slabs[slabIndex]; + if ((block = slab->freeList.next) == 0) { + + allocatedMemory = ejsGetAllocatedMemory(ep); + gc = &ep->gc; + + /* + * No blocks available. If demand collection is enabled, try + * to garbage collect first. We collect if we have done a good + * work quota or we are over the max memory limit. + */ + if (slabIndex != EJS_SLAB_VAR && + ep->gc.enable && ep->gc.enableDemandCollect) { + if ((ep->gc.workDone > ep->gc.workQuota) || + (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory)) { + +#if DEBUG_USE_ONLY + if (ep->gc.debugLevel > 0) { + mprLog(ep, 0, "Need GC, EJS RAM %d, MPR RAM %d\n", + allocatedMemory, mprGetAllocatedMemory(ep)); + if (ep->gc.debugLevel > 4) { + ejsPrintAllocReport(ep, 0); + } + } +#endif + if (ejsCollectGarbage(ep, slabIndex) == 0) { + block = slab->freeList.next; + } + } + } + + if (block == 0) { + if (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory) { + /* + * We are above the max memory limit. We will fail this + * memory allocation, but allow subsequent allocations to + * permit error recovery. We gracefully degrade by setting + * slab chunk sizes to 1. This minimizes real memory + * consumption. This allows us to create + * an exception block to be created by upper layers. + */ + if (! gc->degraded) { + ejsGracefulDegrade(ep); + return 0; + } + } + + /* + * Still non available, so allocate more memory for a set of blocks + * OPT -- should bypass mprAlloc. Need mprMalloc. + */ + block = mprAlloc(ep->slabAllocContext, + slab->size * slab->allocIncrement); + if (block == 0) { + /* + * Now we're in trouble. We should really never get here + * as the graceful degrade will have signaled a memory + * allocation failure. + */ + mprAssert(block != 0); + return 0; + } + + /* + * Chain all the blocks together onto the slab free list + */ + for (i = slab->allocIncrement - 1; i >= 0; i--) { + block->next = slab->freeList.next; +#if BLD_DEBUG + block->magic = EJS_MAGIC_FREE; +#endif + slab->freeList.next = block; + block = (EjsGCLink*) ((char*) block + slab->size); + } + + block = slab->freeList.next; + +#if BLD_FEATURE_ALLOC_STATS + slab->freeCount += slab->allocIncrement; + if (slab->freeCount > slab->peakFree) { + slab->peakFree = slab->freeCount; + } +#endif + } + } + + /* + * We use block to point to the user data in the block. We only + * store the magic number (if debug). No other data is stored in the + * user block. + */ +#if BLD_DEBUG + mprAssert(block->magic == EJS_MAGIC_FREE); +#endif + + /* + * Remove from the free list + */ + slab->freeList.next = block->next; + + /* + * Zero block + */ + memset(block, 0, slab->size); + +#if BLD_DEBUG + block->magic = EJS_MAGIC; +#endif + +#if BLD_FEATURE_ALLOC_STATS + slab->totalAlloc++; + if (++slab->allocCount > slab->peakAllocated) { + slab->peakAllocated = slab->allocCount; + } + slab->freeCount--; +#endif + +#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) + if ((uint) block == breakAddr) { + mprBreakpoint(MPR_LOC, "Watched Block"); + } +#endif + return block; +} + + +/******************************************************************************/ + +EjsObj *ejsAllocObj(EJS_LOC_DEC(ep, loc)) +{ + EjsObj *obj; + EjsSlab *slab; + + obj = (EjsObj*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_OBJ); + + /* + * Add to the allocated block list for the New generation. + */ + if (obj) { + slab = &ep->slabs[EJS_SLAB_OBJ]; + obj->gc.next = slab->allocList[EJS_GEN_NEW].next; + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + obj->gc.allocatedBy = loc; +#endif + + obj->ejs = ep; + slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) obj; + + ep->gc.workDone++; + } + + return obj; +} + + +/******************************************************************************/ + +EjsProperty *ejsAllocProperty(EJS_LOC_DEC(ep, loc)) +{ + EjsProperty *prop; + + prop = (EjsProperty*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_PROPERTY); + mprAssert(prop); + + if (prop) { + prop->var.type = EJS_TYPE_NULL; + prop->var.isProperty = 1; +#if BLD_FEATURE_ALLOC_LEAK_TRACK + prop->var.gc.allocatedBy = loc; +#endif + } + return prop; +} + + +/******************************************************************************/ + +EjsVar *ejsAllocVar(EJS_LOC_DEC(ep, loc)) +{ + EjsVar *vp; + + vp = (EjsVar*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_VAR); + mprAssert(vp); + + if (vp) { +#if BLD_FEATURE_ALLOC_LEAK_TRACK + EjsSlab *slab; + vp->gc.allocatedBy = loc; + slab = &ep->slabs[EJS_SLAB_VAR]; + vp->gc.next = slab->allocList[EJS_GEN_NEW].next; + slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) vp; +#endif +#if BLD_DEBUG + vp->propertyName = 0; +#endif + } + return vp; +} + + +/******************************************************************************/ +/* + * Return the block back to the relevant slab + */ + +void ejsFree(Ejs *ep, void *ptr, int slabIndex) +{ + EjsSlab *slab; + EjsGCLink *block; + + mprAssert(ep); + mprAssert(ptr); + + if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) { + mprAssert(slabIndex >= 0 && slabIndex < EJS_SLAB_MAX); + return; + } + slab = &ep->slabs[slabIndex]; + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + if (slabIndex == EJS_SLAB_VAR) { + EjsVar *vp, *np, *prev; + + /* + * Remove the block rom the alloc list. WARNING: this is slow + * and should not be used in production code. + */ + vp = (EjsVar*) ptr; + prev = 0; + for (np = (EjsVar*) slab->allocList[0].next; np; + np = (EjsVar*) np->gc.next) { + if (vp == np) { + if (prev) { + prev->gc.next = (EjsGCLink*) np->gc.next; + } else { + slab->allocList[0].next = (EjsGCLink*) np->gc.next; + } + break; + } + prev = np; + } + if (np == 0) { + mprAssert(0); + } + } +#endif + + /* + * Insert into the free list. Only use the next ptr + */ + block = (EjsGCLink*) ptr; + +#if BLD_DEBUG +#if !BREW || BREW_SIMULATOR + if ((uint) block == breakAddr) { + mprBreakpoint(MPR_LOC, "Watched Block"); + } +#endif + mprAssert(block->magic == EJS_MAGIC); + block->magic = EJS_MAGIC_FREE; +#endif + + block->next = slab->freeList.next; + slab->freeList.next = block; + +#if BLD_FEATURE_ALLOC_STATS + slab->allocCount--; + if (++slab->freeCount >= slab->peakFree) { + slab->peakFree = slab->freeCount; + } + slab->totalReclaimed++; + if (slabIndex != 2) { + slabIndex = slabIndex; + } +#endif +} + +/******************************************************************************/ +/* + * Mark an object as being in-use. Traverse all properties for referenced + * objects and base classes. + */ + +static void markObjByVar(Ejs *ep, EjsVar *obj) +{ + EjsProperty *pp; + EjsVar *vp, *baseClass; + + mprAssert(ep); + mprAssert(obj); + + obj->objectState->gcMarked = 1; + +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + int indent = min(ep->gc.gcIndent * 2, 32); + mprLog(ep, 0, "%.*s %-24s %.*s 0x%08X", + indent, " ", + obj->propertyName, + 32 - indent, "................................ ", + (uint) obj->objectState); + ep->gc.gcIndent++; + } + ep->gc.objectsInUse++; +#endif + + /* + * Traverse all referenced objects + * OPT -- optimize by directly accessing the object links and not using + * ejsGetFirst/NextProperty. Then just examine objects + * OPT -- first property in global is global. Should optimize this. + */ + pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (pp) { + vp = ejsGetVarPtr(pp); + if (vp->type == EJS_TYPE_OBJECT) { + if (!vp->objectState->gcMarked) { +#if FUTURE + /* + * OPT -- we can use the dirty bit on objects to avoid + * visiting permanent objects that are clean. If so, don't + * forget the else case below. + */ + obj = vp->objectState; + if ((!obj->alive && !obj->permanent) || obj->dirty) +#endif + markObjByVar(ep, vp); + } + + } else { +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + int indent = min(ep->gc.gcIndent * 2, 32); + mprLog(ep, 0, "%.*s %-24s %.*s %s", + indent, " ", + vp->propertyName, + 32 - indent, "................................ ", + ejsGetVarTypeAsString(vp)); + } + ep->gc.propertiesInUse++; +#endif + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + + /* + * Traverse the base class + */ + baseClass = obj->objectState->baseClass; + if (baseClass) { + mprAssert(baseClass->type == EJS_TYPE_OBJECT); + mprAssert(baseClass->objectState); + if (baseClass->objectState) { + if (! baseClass->objectState->gcMarked) { + markObjByVar(ep, baseClass); + } + } + } +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + ep->gc.gcIndent--; + } +#endif +} + + +/******************************************************************************/ +/* + * Mark phase. Examine all variable frames and the return result. + */ + +static void mark(Ejs *ep) +{ + EjsVar *vp; + int i; + +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + mprLog(ep, 0, " "); + mprLog(ep, 0, "GC: Marked Blocks:"); + } +#endif + + if (ep->frames) { + for (i = 0; i < mprGetItemCount(ep->frames); i++) { + + vp = (EjsVar*) mprGetItem(ep->frames, i); + mprAssert(vp->type == EJS_TYPE_OBJECT); + + if (! vp->objectState->gcMarked) { + markObjByVar(ep, vp); + } + } + } + + vp = ep->result; + if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { + markObjByVar(ep, vp); + } + + vp = ep->currentObj; + if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { + markObjByVar(ep, vp); + } + + vp = ejsGetVarPtr(ep->currentProperty); + if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) { + markObjByVar(ep, vp); + } + + /* + * OPT -- we could mark master as "mark permanent" somehow and + * then we would not need to walk the master objects. + */ + if (ep->slabAllocContext == ep->service->master) { + if (ep->service->master->global) { + markObjByVar(ep, ep->service->master->global); + } + } + +#if BLD_DEBUG + if (ep->gc.debugLevel >= 3) { + mprLog(ep, 0, " "); + } +#endif +} + + +/******************************************************************************/ +#if UNUSED + +static void resetMark(EjsVar *obj) +{ + EjsProperty *pp; + EjsVar *vp, *baseClass; + + obj->objectState->gcMarked = 0; + obj->objectState->visited = 1; + + pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); + while (pp) { + vp = ejsGetVarPtr(pp); + if (vp->type == EJS_TYPE_OBJECT && !vp->objectState->visited) { + resetMark(vp); + } + pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); + } + + baseClass = obj->objectState->baseClass; + if (baseClass) { + mprAssert(baseClass->type == EJS_TYPE_OBJECT); + mprAssert(baseClass->objectState); + if (baseClass->objectState) { + if (! baseClass->objectState->visited) { + resetMark(baseClass); + } + } + } + obj->objectState->visited = 0; +} + +/******************************************************************************/ +/* + * Mark phase. Examine all variable frames and the return result. + */ + +static void resetAllMarks(Ejs *ep) +{ + EjsVar *vp; + int i; + + for (i = 0; i < mprGetItemCount(ep->frames); i++) { + vp = (EjsVar*) mprGetItem(ep->frames, i); + resetMark(vp); + } + + if (ep->result && ep->result->type == EJS_TYPE_OBJECT && + ! ep->result->objectState->gcMarked) { + resetMark(ep->result); + } +} + +#endif +/******************************************************************************/ +/* + * Sweep up the garbage + */ + +static void resetMarks(Ejs *ep, EjsSlab *slab) +{ + EjsVar *vp; + EjsObj *obj; + int gen, i; + + for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { + obj = (EjsObj*) slab->allocList[gen].next; + for (; obj; obj = (EjsObj*) obj->gc.next) { + obj->gcMarked = 0; + obj->visited = 0; + } + } + + if (ep->frames) { + for (i = 0; i < mprGetItemCount(ep->frames); i++) { + + vp = (EjsVar*) mprGetItem(ep->frames, i); + mprAssert(vp->type == EJS_TYPE_OBJECT); + + vp->objectState->gcMarked = 0; + vp->objectState->visited = 0; + } + } + + if (ep->result && ep->result->type == EJS_TYPE_OBJECT) { + ep->result->objectState->gcMarked = 0; + } +} + +/******************************************************************************/ +/* + * Mark all permanent and non-alive objects + */ + +static void markPerm(Ejs *ep, uint gen) +{ + EjsSlab *slab; + EjsObj *obj; + + slab = &ep->slabs[EJS_SLAB_OBJ]; + + for (obj = (EjsObj*) slab->allocList[gen].next; obj; ) { + + if (! obj->gcMarked) { + if (!obj->alive || obj->permanent) { + markObj(obj); + } + } + obj = (EjsObj*) obj->gc.next; + + } +} + +/******************************************************************************/ + +static void markObj(EjsObj *obj) +{ + EjsProperty *pp; + EjsPropLink *lp, *head; + EjsObj *op; + + mprAssert(obj); + + obj->gcMarked = 1; + + head = &obj->link; + for (lp = head->next; lp != head; lp = lp->next) { + + pp = ejsGetPropertyFromLink(lp); + + if (pp->var.type == EJS_TYPE_OBJECT) { + op = pp->var.objectState; + if (op != 0 && !op->gcMarked) { + markObj(op); + } + } + } +} + +/******************************************************************************/ +/* + * Sweep up the garbage. Return the number of objects freed. + */ + +static int sweep(Ejs *ep, uint gen) +{ + EjsSlab *slab; + EjsObj *obj, *next, *prev; + int count; + + slab = &ep->slabs[EJS_SLAB_OBJ]; + + /* + * Examine allocated objects in the specified generation (only). + * NOTE: we only sweep object allocated to this interpreter and so + * we do not sweep any permanent objects in the default interpreter. + */ + prev = 0; + count = 0; + for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) { + + next = (EjsObj*) obj->gc.next; + +#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) + if ((uint) obj == breakAddr) { + mprBreakpoint(MPR_LOC, "Watched Block"); + } +#endif + + /* + * If object has not been marked inuse and is not a permanent + * object, then free it. + */ + if (! obj->gcMarked && obj->alive && !obj->permanent) { + +#if BLD_DEBUG + if (ep->gc.debugLevel >= 2) { + if (obj->objName) { + mprLog(ep, 0, "GC: destroy %-18s %10d, %8X", + obj->objName, (uint) obj, (uint) obj); + } else { + mprLog(ep, 0, "GC: destroy UNKNOWN %x", (uint) obj); + } + } +#endif + if (ejsDestroyObj(ep, obj) < 0) { + prev = obj; + obj->gcMarked = 0; + continue; + } + + if (prev) { + prev->gc.next = (EjsGCLink*) next; + } else { + slab->allocList[gen].next = (EjsGCLink*) next; + } + count++; + + } else { + prev = obj; + /* Reset for next time */ + obj->gcMarked = 0; + } + } + + if (gen == (EJS_GEN_OLD - 1)) { + slab->lastRecentBlock = prev; + } +#if BLD_FEATURE_ALLOC_STATS + slab->totalSweeps++; +#endif +#if BLD_DEBUG + if (ep->gc.debugLevel > 0) { + mprLog(ep, 0, "GC: Sweep freed %d objects", count); + } +#endif + return count; +} + +/******************************************************************************/ +/* + * Sweep all variables + */ + +void ejsSweepAll(Ejs *ep) +{ + EjsSlab *slab; + EjsObj *obj, *next, *prev; + int gen; + + slab = &ep->slabs[EJS_SLAB_OBJ]; + + for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { + prev = 0; + for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) { + next = (EjsObj*) obj->gc.next; + ejsDestroyObj(ep, obj); + } + break; + } +} + +/******************************************************************************/ + +bool ejsObjIsCollectable(EjsVar *vp) +{ + if (vp == 0 || !ejsVarIsObject(vp)) { + return 0; + } + return (vp->objectState->alive && !vp->objectState->permanent); +} + +/******************************************************************************/ +#if FUTURE + +static void ageGenerations(Ejs *ep) +{ + EjsSlab *slab; + EjsGCLink *oldList; + int gen; + + slab = &ep->slabs[EJS_SLAB_OBJ]; + + /* + * Age all blocks. First append all (old - 1) blocks onto the old + * alloc list + */ + oldList = &slab->allocList[EJS_GEN_OLD]; + + if (slab->lastRecentBlock) { + slab->lastRecentBlock->gc.next = oldList->next; + oldList->next = (EjsGCLink*) slab->lastRecentBlock; + } + + /* + * Now simply copy all allocation lists up one generation + */ + for (gen = EJS_GEN_OLD - 1; gen > 0; gen--) { + slab->allocList[gen] = slab->allocList[gen - 1]; + } + slab->allocList[0].next = 0; +} + +#endif +/******************************************************************************/ +/* + * Collect the garbage. This is a mark and sweep over all possible objects. + * If an object is not referenced, it and all contained properties will be + * freed. If a slabIndex is provided, the collection halts when a block is + * available for allocation on that slab. + * + * Return 0 if memory is now available after collecting garbage. Otherwise, + * return MPR_ERR_MEMORY. + */ + +int ejsCollectGarbage(Ejs *ep, int slabIndex) +{ + EjsGeneration gen; + + if (ep->flags & EJS_FLAGS_DONT_GC) { + return -1; + } + + /* + * Prevent destructors invoking the garbage collector + */ + if (ep->gc.collecting) { + return 0; + } + ep->gc.collecting = 1; + + resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); + + /* + * Examine each generation of objects starting with the most recent + * generation. Stop scanning when we have a free block to use. + */ + for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) { + + if (slabIndex >= 0 && ep->slabs[slabIndex].freeList.next) { + break; + } + + /* + * FUTURE OPT. Should mark objects in new generation and those + * with a dirty bit set in older generations. Don't need to mark + * entire heap. But how to keep list of dirty objects. + */ + mark(ep); + markPerm(ep, gen); + sweep(ep, gen); + + /* FUTURE - not using generations yet */ + break; + } + + /* + * FUTURE -- not using generations yet. + * + * ageGenerations(ep); + */ + + ep->gc.workDone = 0; + ep->gc.collecting = 0; + + return (gen < EJS_GEN_MAX) ? 0 : MPR_ERR_MEMORY; +} + + +/******************************************************************************/ +/* + * Should be called when the app has been idle for a little while and when it + * is likely to be idle a bit longer. Call ejsIsTimeForGC to see if this is + * true. Return the count of objects collected . + */ + +int ejsIncrementalCollectGarbage(Ejs *ep) +{ + int count; + + if (ep->gc.collecting) { + return 0; + } + + ep->gc.collecting = 1; + + resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); + mark(ep); + + /* Not generational yet */ + count = sweep(ep, EJS_GEN_NEW); + + ep->gc.collecting = 0; + ep->gc.workDone = 0; + + return count; +} + +/******************************************************************************/ +#if BLD_DEBUG + +void ejsDumpObjects(Ejs *ep) +{ + int oldDebugLevel; + + mprLog(ep, 0, "Dump of objects in use\n"); + + oldDebugLevel = ep->gc.debugLevel; + + ep->gc.debugLevel = 3; + ep->gc.objectsInUse = 0; + ep->gc.propertiesInUse = 0; + ep->gc.collecting = 1; + + resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]); + mark(ep); + + ep->gc.collecting = 0; + ep->gc.debugLevel = oldDebugLevel; + + mprLog(ep, 0, "%d objects and %d properties in use", + ep->gc.objectsInUse, ep->gc.propertiesInUse); + mprLog(ep, 0, "%d object bytes, %d property bytes and %d total", + (int) (ep->gc.objectsInUse * sizeof(EjsObj)), + (int) (ep->gc.propertiesInUse * sizeof(EjsProperty)), + (int) ((ep->gc.objectsInUse * sizeof(EjsObj) + + ep->gc.propertiesInUse * sizeof(EjsProperty)))); +} + +#endif +/******************************************************************************/ +/* + * Return true if there is time to do a garbage collection and if we will + * benefit from it. + */ + +int ejsIsTimeForGC(Ejs *ep, int timeTillNextEvent) +{ + EjsGC *gc; + + if (timeTillNextEvent < EJS_MIN_TIME_FOR_GC) { + /* + * Not enough time to complete a collection + */ + return 0; + } + + gc = &ep->gc; + + /* + * Return if we haven't done enough work to warrant a collection + * Trigger a little short of the work quota to try to run GC before + * a demand allocation requires it. + */ + if (!gc->enable || !gc->enableIdleCollect || + (gc->workDone < (gc->workQuota - EJS_GC_MIN_WORK_QUOTA))) { + return 0; + } + +#if UNUSED + mprLog(ep, 0, "Time for GC. Work done %d, time till next event %d", + gc->workDone, timeTillNextEvent); +#endif + return 1; +} + +/******************************************************************************/ +/* + * Return the amount of memory in use by EJS + */ + +uint ejsGetUsedMemory(Ejs *ep) +{ +#if BLD_FEATURE_ALLOC_STATS + EjsSlab *slab; + int i, totalMemory, slabMemory; + + totalMemory = 0; + for (i = 0; i < EJS_SLAB_MAX; i++) { + slab = &ep->slabs[i]; + slabMemory = slab->allocCount * slab->size; + totalMemory += slabMemory; + } + return totalMemory; +#else + return 0; +#endif +} + +/******************************************************************************/ +/* + * Return the amount of memory allocated by EJS + */ + +uint ejsGetAllocatedMemory(Ejs *ep) +{ +#if BLD_FEATURE_ALLOC_STATS + EjsSlab *slab; + int i, totalMemory, slabMemory; + + totalMemory = 0; + for (i = 0; i < EJS_SLAB_MAX; i++) { + slab = &ep->slabs[i]; + slabMemory = (slab->allocCount + slab->freeCount) * slab->size; + totalMemory += slabMemory; + } + return totalMemory; +#else + return 0; +#endif +} + +/******************************************************************************/ +/* + * On a memory allocation failure, go into graceful degrade mode. Set all + * slab allocation chunk increments to 1 so we can create an exception block + * to throw. + */ + +static void ejsGracefulDegrade(Ejs *ep) +{ + EjsSlab *slab; + int i; + + mprLog(ep, 1, "WARNING: Memory almost depleted. In graceful degrade mode"); + for (i = 0; i < EJS_SLAB_MAX; i++) { + slab = &ep->slabs[i]; + slab->allocIncrement = 8; + } + ep->gc.degraded = 1; +} + +/******************************************************************************/ + +int ejsSetGCDebugLevel(Ejs *ep, int debugLevel) +{ + int old; + + old = ep->gc.debugLevel; + ep->gc.debugLevel = debugLevel; + return old; +} + +/******************************************************************************/ + +int ejsSetGCMaxMemory(Ejs *ep, uint maxMemory) +{ + int old; + + old = ep->gc.maxMemory; + ep->gc.maxMemory = maxMemory; + + return old; +} + +/******************************************************************************/ + +bool ejsBlockInUseInt(EjsVar *vp) +{ + if (vp) { +#if BLD_DEBUG + if (vp->gc.magic != EJS_MAGIC) { + return 0; + } + if (vp->type == EJS_TYPE_OBJECT && vp->objectState && + vp->objectState->gc.magic != EJS_MAGIC) { + return 0; + } +#endif + return 1; + } + return 1; +} + +/******************************************************************************/ +#else +void ejsGarbageDummy() {} + +#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-2.0/ejs/ejsLex.c b/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c new file mode 100644 index 0000000000..fbfee6e4d5 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsLex.c @@ -0,0 +1,1033 @@ +/* + * @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-2006. 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 "ejs.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); +static void parseNumber(Ejs *ep, EjsType type); + +/************************************* Code ***********************************/ +/* + * Open a new input script + */ + +int ejsLexOpenScript(Ejs *ep, const char *script) +{ + EjsInput *ip; + + mprAssert(ep); + mprAssert(script); + + if ((ip = mprAllocTypeZeroed(ep, EjsInput)) == NULL) { + return MPR_ERR_MEMORY; + } + ip->next = ep->input; + ep->input = ip; + ip->procName = ep->proc ? ep->proc->procName : NULL; + ip->fileName = ep->fileName ? ep->fileName : NULL; + +/* + * Create the parse token buffer and script buffer + */ + ip->tokServp = ip->tokbuf; + ip->tokEndp = ip->tokbuf; + + ip->script = script; + ip->scriptSize = strlen(script); + ip->scriptServp = (char*) ip->script; + + ip->lineNumber = 1; + ip->lineColumn = 0; + + ip->putBackIndex = -1; + + return 0; +} + +/******************************************************************************/ +/* + * Close the input script + */ + +void ejsLexCloseScript(Ejs *ep) +{ + EjsInput *ip; + + mprAssert(ep); + + ip = ep->input; + mprAssert(ip); + ep->input = ip->next; + + 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++) { + mprStrcpy(state->putBack[i].tokbuf, EJS_MAX_TOKEN, + ip->putBack[i].tokbuf); + state->putBack[i].tid = ip->putBack[i].tid; + } + + mprStrcpy(state->line, sizeof(state->line), ip->line); + + state->lineColumn = ip->lineColumn; + state->lineNumber = ip->lineNumber; +} + +/******************************************************************************/ +/* + * Restore the input state + */ + +void ejsLexRestoreInputState(Ejs *ep, EjsInput *state) +{ + EjsInput *ip; + EjsToken *tp; + int i; + + mprAssert(ep); + mprAssert(state); + + ip = ep->input; + mprAssert(ip); + + mprStrcpy(ip->tokbuf, sizeof(ip->tokbuf), state->tokbuf); + ip->tokServp = state->tokServp; + ip->tokEndp = state->tokEndp; + + ip->script = state->script; + ip->scriptServp = state->scriptServp; + ip->scriptSize = state->scriptSize; + + ip->putBackIndex = state->putBackIndex; + for (i = 0; i <= ip->putBackIndex; i++) { + tp = &ip->putBack[i]; + tp->tid = state->putBack[i].tid; + mprStrcpy(tp->tokbuf, sizeof(tp->tokbuf), state->putBack[i].tokbuf); + } + + mprStrcpy(ip->line, sizeof(ip->line), state->line); + + ip->lineColumn = state->lineColumn; + ip->lineNumber = state->lineNumber; +} + +/******************************************************************************/ +/* + * Free a saved input state + */ + +void ejsLexFreeInputState(Ejs *ep, EjsInput *state) +{ + mprAssert(ep); + mprAssert(state); + + state->putBackIndex = -1; + 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", "delete", "function", + * "class", "extends", "public", "private", "protected", "try", "catch", + * "finally", "throw", "return", "get", "set", "this", "module", "each" + * + * The "new" and "in" reserved words are handled below. The "true", "false", + * "null" "typeof" and "undefined" reserved words are handled as global + * objects. + * + * Other reserved words not supported: + * "break", "case", "continue", "default", "do", + * "instanceof", "switch", "while", "with" + * + * ECMA extensions reserved words (not supported): + * "abstract", "boolean", "byte", "char", "const", + * "debugger", "double", "enum", "export", + * "final", "float", "goto", "implements", "import", "int", + * "interface", "long", "native", "package", + * "short", "static", "super", "synchronized", "transient", "volatile" + * + * FUTURE -- use a hash lookup + */ + +static int checkReservedWord(Ejs *ep, int state, int c, int tid) +{ + /* FUTURE -- probably should return for all tokens != EJS_TOK_ID */ + /* FUTURE -- Should have a hash for this. MUCH faster. */ + + if (!isalpha(ep->token[0]) || tid == EJS_TOK_LITERAL) { + return tid; + } + if (state == EJS_STATE_STMT) { + /* FUTURE OPT -- convert to hash lookup */ + 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, "new") == 0) { + inputPutback(ep, c); + return EJS_TOK_NEW; + } 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, "class") == 0) { + inputPutback(ep, c); + return EJS_TOK_CLASS; + } else if (strcmp(ep->token, "module") == 0) { + inputPutback(ep, c); + return EJS_TOK_MODULE; + } else if (strcmp(ep->token, "extends") == 0) { + inputPutback(ep, c); + return EJS_TOK_EXTENDS; + } else if (strcmp(ep->token, "try") == 0) { + inputPutback(ep, c); + return EJS_TOK_TRY; + } else if (strcmp(ep->token, "catch") == 0) { + inputPutback(ep, c); + return EJS_TOK_CATCH; + } else if (strcmp(ep->token, "finally") == 0) { + inputPutback(ep, c); + return EJS_TOK_FINALLY; + } else if (strcmp(ep->token, "throw") == 0) { + inputPutback(ep, c); + return EJS_TOK_THROW; + } else if (strcmp(ep->token, "public") == 0) { + inputPutback(ep, c); + return EJS_TOK_PUBLIC; + } else if (strcmp(ep->token, "protected") == 0) { + inputPutback(ep, c); + return EJS_TOK_PROTECTED; + } else if (strcmp(ep->token, "private") == 0) { + inputPutback(ep, c); + return EJS_TOK_PRIVATE; + } else if (strcmp(ep->token, "get") == 0) { + inputPutback(ep, c); + return EJS_TOK_GET; + } else if (strcmp(ep->token, "set") == 0) { + inputPutback(ep, c); + return EJS_TOK_SET; + } else if (strcmp(ep->token, "extends") == 0) { + inputPutback(ep, c); + return EJS_TOK_EXTENDS; + } else if (strcmp(ep->token, "try") == 0) { + inputPutback(ep, c); + return EJS_TOK_TRY; + } else if (strcmp(ep->token, "catch") == 0) { + inputPutback(ep, c); + return EJS_TOK_CATCH; + } else if (strcmp(ep->token, "finally") == 0) { + inputPutback(ep, c); + return EJS_TOK_FINALLY; + } else if (strcmp(ep->token, "throw") == 0) { + inputPutback(ep, c); + return EJS_TOK_THROW; + } else if (strcmp(ep->token, "public") == 0) { + inputPutback(ep, c); + return EJS_TOK_PUBLIC; + } else if (strcmp(ep->token, "protected") == 0) { + inputPutback(ep, c); + return EJS_TOK_PROTECTED; + } else if (strcmp(ep->token, "private") == 0) { + inputPutback(ep, c); + return EJS_TOK_PRIVATE; + } else if (strcmp(ep->token, "get") == 0) { + inputPutback(ep, c); + return EJS_TOK_GET; + } else if (strcmp(ep->token, "set") == 0) { + inputPutback(ep, c); + return EJS_TOK_SET; + } else if (strcmp(ep->token, "each") == 0) { + inputPutback(ep, c); + return EJS_TOK_EACH; + } 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; + } + + } else if (state == EJS_STATE_DEC) { + if (strcmp(ep->token, "extends") == 0) { + inputPutback(ep, c); + return EJS_TOK_EXTENDS; + } + } + return tid; +} + +/******************************************************************************/ +/* + * Get the next EJS token + */ + +static int getLexicalToken(Ejs *ep, int state) +{ + EjsType type; + EjsInput *ip; + int done, tid, c, quote, style, idx, isHex; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + ep->tid = -1; + tid = -1; + type = BLD_FEATURE_NUM_TYPE_ID; + isHex = 0; + + /* + * Use a putback tokens first. Don't free strings as caller needs access. + */ + if (ip->putBackIndex >= 0) { + idx = ip->putBackIndex; + tid = ip->putBack[idx].tid; + ep->token = (char*) ip->putBack[idx].tokbuf; + 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) { + ejsSyntaxError(ep, 0); + 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) { + ejsSyntaxError(ep, 0); + 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) { + ejsSyntaxError(ep, 0); + 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; + } + ejsSyntaxError(ep, 0); + 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) { + ejsSyntaxError(ep, 0); + 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) { + ejsSyntaxError(ep, 0); + 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) { + ejsSyntaxError(ep, 0); + 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) { + ejsSyntaxError(ep, 0); + 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 ':': + tokenAddChar(ep, c); + return EJS_TOK_COLON; + + case '|': /* "||" */ + if ((c = inputGetc(ep)) < 0 || c != '|') { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + tokenAddChar(ep, EJS_COND_OR); + return EJS_TOK_LOGICAL; + + case '&': /* "&&" */ + if ((c = inputGetc(ep)) < 0 || c != '&') { + ejsSyntaxError(ep, 0); + return EJS_TOK_ERR; + } + tokenAddChar(ep, EJS_COND_AND); + return EJS_TOK_LOGICAL; + + case '\"': /* String quote */ + case '\'': + quote = c; + if ((c = inputGetc(ep)) < 0) { + ejsSyntaxError(ep, 0); + 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: + if (tokenAddChar(ep, '\\') < 0) { + 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) { + ejsSyntaxError(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; + } + isHex = 1; + if (! isxdigit(c)) { + parseNumber(ep, type); + inputPutback(ep, c); + return EJS_TOK_NUMBER; + } + } else if (! isdigit(c)) { +#if BLD_FEATURE_FLOATING_POINT + if (c == '.' || tolower(c) == 'e' || c == '+' || c == '-') { + /* Fall through */ + type = EJS_TYPE_FLOAT; + } else +#endif + { + parseNumber(ep, 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': + if (isHex) { + do { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + } while (isxdigit(c)); + + } else { +#if BLD_FEATURE_FLOATING_POINT + do { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + c = tolower(c); + if (c == '.' || c == 'e' || c == 'f') { + type = EJS_TYPE_FLOAT; + } + } while (isdigit(c) || c == '.' || c == 'e' || + c == 'f' || + ((type == EJS_TYPE_FLOAT) && (c == '+' || c == '-'))); +#else + do { + if (tokenAddChar(ep, c) < 0) { + return EJS_TOK_ERR; + } + if ((c = inputGetc(ep)) < 0) { + break; + } + } while (isdigit(c)); +#endif + } + + parseNumber(ep, 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 != '\\' && c != '@') { + break; + } + } + if (*ep->token == '\0') { + c = inputGetc(ep); + break; + } + + if (! isalpha((int) *ep->token) && *ep->token != '$' && + *ep->token != '_' && *ep->token != '@') { + ejsError(ep, EJS_SYNTAX_ERROR, "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; + if ((strlen(ep->token) + 1) >= EJS_MAX_ID) { + ejsError(ep, EJS_SYNTAX_ERROR, + "Identifier too big. Max is %d letters.", EJS_MAX_ID); + return EJS_TOK_ERR; + } + done++; + } + } + + /* + * Putback the last extra character for next time + */ + inputPutback(ep, c); + return tid; +} + +/******************************************************************************/ + +static void parseNumber(Ejs *ep, EjsType type) +{ + switch (type) { + case EJS_TYPE_INT: + ep->tokenNumber.integer = ejsParseInteger(ep->token); + ep->tokenNumber.type = type; + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + ep->tokenNumber.floating = atof(ep->token); + ep->tokenNumber.type = type; + break; +#endif + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + ep->tokenNumber.integer64 = ejsParseInteger64(ep->token); + ep->tokenNumber.type = type; + break; +#endif + } +} + +/******************************************************************************/ +/* + * 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; + EjsToken *tp; + int idx; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + ip->putBackIndex += 1; + + mprAssert(ip->putBackIndex < EJS_TOKEN_STACK); + idx = ip->putBackIndex; + + tp = &ip->putBack[idx]; + tp->tid = tid; + + mprStrcpy(tp->tokbuf, sizeof(tp->tokbuf), string); +} + +/******************************************************************************/ +/* + * Add a character to the token buffer + */ + +static int tokenAddChar(Ejs *ep, int c) +{ + EjsInput *ip; + + mprAssert(ep); + ip = ep->input; + mprAssert(ip); + + if (ip->tokEndp >= &ip->tokbuf[sizeof(ip->tokbuf) - 1]) { + ejsSyntaxError(ep, "Token too big"); + return -1; + } + *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 0 && BLD_DEBUG + if (ip->lineColumn > 0) { + printf("PARSED: %s\n", ip->line); + } +#endif + ip->lineNumber++; + ip->lineColumn = 0; + } else if ((ip->lineColumn + 2) < sizeof(ip->line)) { + 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++; + if (--(ip->lineColumn) < 0) { + ip->lineColumn = 0; + } + mprAssert(ip->line); + mprAssert(ip->lineColumn >= 0); + mprAssert(ip->lineColumn < sizeof(ip->line)); + 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-2.0/ejs/ejsParser.c b/source4/lib/appweb/ejs-2.0/ejs/ejsParser.c new file mode 100644 index 0000000000..9fce6d27ee --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsParser.c @@ -0,0 +1,4514 @@ +/* + * @file ejsParser.c + * @brief EJS Parser and Execution + */ +/********************************* Copyright **********************************/ +/* + * @copy default.g + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "ejs.h" + +#if BLD_FEATURE_EJS + +/****************************** Forward Declarations **************************/ + +static int createClass(Ejs *ep, EjsVar *parentClass, + const char *className, EjsVar *baseClass); +static int createProperty(Ejs *ep, EjsVar **obj, const char *id, + int state); +static int evalCond(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs); +static int evalExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs); +#if BLD_FEATURE_FLOATING_POINT +static int evalFloatExpr(Ejs *ep, double l, int rel, double r); +#endif +static int evalBoolExpr(Ejs *ep, int l, int rel, int r); +static int evalNumericExpr(Ejs *ep, EjsNum l, int rel, EjsNum r); +static int evalObjExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs) ; +static int evalStringExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs); +static int evalMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, int flags); +static EjsProperty *findProperty(Ejs *ep, EjsVar *op, const char *property, + int flags); +static EjsVar *pickSpace(Ejs *ep, int state, const char *property, int flags); +static void freeProc(Ejs *ep, EjsProc *proc); +static int parseArgs(Ejs *ep, int state, int flags); +static int parseArrayLiteral(Ejs *ep, int state, int flags, char *id); +static int parseAssignment(Ejs *ep, int state, int flags, char *id); +static int parseClass(Ejs *ep, int state, int flags); +static int parseForInner(Ejs *ep, int state, int flags, + EjsInput *condScript, EjsInput *incrScript, + EjsInput *bodyScript, EjsInput *endScript); +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 parseRegFor(Ejs *ep, int state, int flags); +static int parseForIn(Ejs *ep, int state, int flags, int each); +static int parseId(Ejs *ep, int state, int flags, char **id, int *done); +static int parseInc(Ejs *ep, int state, int flags); +static int parseIf(Ejs *ep, int state, int flags, int *done); +static int parseFunction(Ejs *ep, int state, int flags); +static int parseMethod(Ejs *ep, int state, int flags, char *id); +static int parseObjectLiteral(Ejs *ep, int state, int flags, char *id); +static int parseStmt(Ejs *ep, int state, int flags); +static int parseThrow(Ejs *ep, int state, int flags); +static int parseTry(Ejs *ep, int state, int flags); +static void removeNewlines(Ejs *ep, int state); +static EjsProperty *searchSpacesForProperty(Ejs *ep, int state, EjsVar *obj, + char *property, int flags); +static int assignPropertyValue(Ejs *ep, char *id, int state, EjsVar *value, + int flags); +static int updateProperty(Ejs *ep, EjsVar *obj, const char *id, int state, + EjsVar *value); +static void updateResult(Ejs *ep, int state, int flags, EjsVar *vp); +static int getNextNonSpaceToken(Ejs *ep, int state); + +static int callConstructor(Ejs *ep, EjsVar *thisObj, EjsVar *baseClass, + MprArray *args); +static int callCMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, + EjsVar *prototype); +static int callStringCMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, + EjsVar *prototype); +static int callMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, + EjsVar *prototype); +static int runMethod(Ejs *ep, EjsVar *thisObj, EjsVar *method, + const char *methodName, MprArray *args); + +static EjsInput *getInputStruct(Ejs *ep); +static void freeInputStruct(Ejs *ep, EjsInput *input); + +static void *pushFrame(Ejs *ep, int size); +static void *popFrame(Ejs *ep, int size); + +/************************************* Code ***********************************/ +/* + * Recursive descent parser for EJS + */ + +int ejsParse(Ejs *ep, int state, int flags) +{ + mprAssert(ep); + +#if MOB + if (mprStackCheck(ep)) { + char *stack; + stack = ejsFormatStack(ep); + mprLog(ep, 0, "\nStack grew : MAX %d\n", mprStackSize(ep)); + mprLog(ep, 0, "Stack\n %s\n", stack); + mprFree(stack); + } +#endif + + if (ep->flags & EJS_FLAGS_EXIT) { + return EJS_STATE_RET; + } + + ep->inputMarker = ep->input->scriptServp; + + switch (state) { + /* + * Any statement, method arguments or conditional expressions + */ + case EJS_STATE_STMT: + state = parseStmt(ep, state, flags); + if (state != EJS_STATE_STMT_BLOCK_DONE && state != EJS_STATE_STMT_DONE){ + goto err; + } + break; + + case EJS_STATE_DEC: + state = parseStmt(ep, state, flags); + if (state != EJS_STATE_DEC_DONE) { + goto err; + } + break; + + case EJS_STATE_EXPR: + state = parseStmt(ep, state, flags); + if (state != EJS_STATE_EXPR_DONE) { + goto err; + } + break; + + /* + * Variable declaration list + */ + case EJS_STATE_DEC_LIST: + state = parseDeclaration(ep, state, flags); + if (state != EJS_STATE_DEC_LIST_DONE) { + goto err; + } + break; + + /* + * Method argument string + */ + case EJS_STATE_ARG_LIST: + state = parseArgs(ep, state, flags); + if (state != EJS_STATE_ARG_LIST_DONE) { + goto err; + } + break; + + /* + * Logical condition list (relational operations separated by &&, ||) + */ + case EJS_STATE_COND: + state = parseCond(ep, state, flags); + if (state != EJS_STATE_COND_DONE) { + goto err; + } + break; + + /* + * Expression list + */ + case EJS_STATE_RELEXP: + state = parseExpr(ep, state, flags); + if (state != EJS_STATE_RELEXP_DONE) { + goto err; + } + break; + } + + /* + * Recursion protection + */ + if (ep->input->scriptServp == ep->inputMarker) { + if (ep->recurseCount++ > 20) { + ejsSyntaxError(ep, "Input syntax error"); + state = EJS_STATE_ERR; + } + } else { + ep->recurseCount = 0; + } + + if (state == EJS_STATE_RET || state == EJS_STATE_EOF) { + return state; + } + +done: + return state; + +err: + if (state == EJS_STATE_RET || state == EJS_STATE_EOF) { + goto done; + } + if (state != EJS_STATE_ERR) { + ejsSyntaxError(ep, 0); + } + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseStmt { + EjsProc *saveProc; + EjsProperty *pp; + EjsVar *saveObj, *exception; + char *str, *id; + int done, tid, rs, saveObjPerm, expectEndOfStmt; +} ParseStmt; + +/* + * Parse expression (leftHandSide operator rightHandSide) + */ + + +static int parseStmt(Ejs *ep, int state, int flags) +{ + ParseStmt *sp; + + mprAssert(ep); + + if ((sp = pushFrame(ep, sizeof(ParseStmt))) == 0) { + return EJS_STATE_ERR; + } + + sp->id = 0; + sp->expectEndOfStmt = 0; + sp->saveProc = NULL; + + ep->currentObj = 0; + ep->currentProperty = 0; + + for (sp->done = 0; !sp->done && state != EJS_STATE_ERR; ) { + sp->tid = ejsLexGetToken(ep, state); + +#if (WIN || BREW_SIMULATOR) && BLD_DEBUG && DISABLED + /* MOB -- make cross platform */ + _CrtCheckMemory(); +#endif + + switch (sp->tid) { + default: + ejsLexPutbackToken(ep, sp->tid, ep->token); + goto done; + + case EJS_TOK_EXPR: + if (state == EJS_STATE_EXPR) { + ejsLexPutbackToken(ep, EJS_TOK_EXPR, ep->token); + } + goto done; + + case EJS_TOK_LOGICAL: + ejsLexPutbackToken(ep, sp->tid, ep->token); + goto done; + + case EJS_TOK_ERR: + if (state != EJS_STATE_ERR && !ep->gotException) { + ejsSyntaxError(ep, 0); + } + state = EJS_STATE_ERR; + goto done; + + case EJS_TOK_EOF: + state = EJS_STATE_EOF; + goto done; + + 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, sp->tid, ep->token); + } + goto done; + + case EJS_TOK_LBRACKET: + if (flags & EJS_FLAGS_EXE) { + ep->currentObj = &ep->currentProperty->var; + if (ep->currentObj != 0 && ep->currentObj->type != + EJS_TYPE_OBJECT) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Property reference to a non-object type \"%s\"\n", + sp->id); + goto err; + } + } + + sp->saveObj = ep->currentObj; + sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); + + sp->rs = ejsParse(ep, EJS_STATE_RELEXP, flags); + + ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); + ep->currentObj = sp->saveObj; + + if (sp->rs < 0) { + state = sp->rs; + goto done; + } + + mprFree(sp->id); + /* MOB rc */ + sp->str = ejsVarToString(ep, ep->result); + sp->id = mprStrdup(ep, sp->str); + + if (sp->id[0] == '\0') { + if (flags & EJS_FLAGS_EXE) { + ejsError(ep, EJS_RANGE_ERROR, + "[] expression evaluates to the empty string\n"); + goto err; + } + } else { + sp->pp = searchSpacesForProperty(ep, state, ep->currentObj, + sp->id, flags); + ep->currentProperty = sp->pp; + updateResult(ep, state, flags, ejsGetVarPtr(sp->pp)); + } + + if ((sp->tid = ejsLexGetToken(ep, state)) != EJS_TOK_RBRACKET) { + ejsSyntaxError(ep, "Missing ']'"); + goto err; + } + break; + + case EJS_TOK_PERIOD: + if (flags & EJS_FLAGS_EXE) { + if (ep->currentProperty == 0) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined object \"%s\"", sp->id); + goto err; + } + } + ep->currentObj = &ep->currentProperty->var; + if (flags & EJS_FLAGS_EXE) { + if (ep->currentObj != 0 && ep->currentObj->type != + EJS_TYPE_OBJECT) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Property reference to a non-object type \"%s\"\n", + sp->id); + goto err; + } + } + if ((sp->tid = ejsLexGetToken(ep, state)) != EJS_TOK_ID) { + ejsError(ep, EJS_REFERENCE_ERROR, "Bad property after '.': %s", + ep->token); + goto err; + } + /* Fall through */ + + case EJS_TOK_ID: + state = parseId(ep, state, flags, &sp->id, &sp->done); + if (sp->done && state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + break; + + case EJS_TOK_ASSIGNMENT: + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid == EJS_TOK_LBRACE) { + /* + * var = { name: value, name: value, ... } + */ + if (parseObjectLiteral(ep, state, flags, sp->id) < 0) { + ejsSyntaxError(ep, "Bad object literal"); + goto err; + } + + } else if (sp->tid == EJS_TOK_LBRACKET) { + /* + * var = [ array elements ] + */ + if (parseArrayLiteral(ep, state, flags, sp->id) < 0) { + ejsSyntaxError(ep, "Bad array literal"); + goto err; + } + + } else if (sp->tid == EJS_TOK_EXPR && + (int) *ep->token == EJS_EXPR_LESS) { + /* + * var = <xmlTag> .../</xmlTag> + */ + ejsSyntaxError(ep, "XML literals are not yet supported"); + goto err; + + } else { + /* + * var = expression + */ + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = parseAssignment(ep, state, flags, sp->id); + if (state == EJS_STATE_ERR) { + if (ep->flags & EJS_FLAGS_EXIT) { + state = EJS_STATE_RET; + goto done; + } + if (!ep->gotException) { + ejsSyntaxError(ep, 0); + } + goto err; + } + } + + if (flags & EJS_FLAGS_EXE) { + if (assignPropertyValue(ep, sp->id, state, ep->result, + flags) < 0) { + if (ep->gotException == 0) { + ejsError(ep, EJS_EVAL_ERROR, "Can't set property %s", + sp->id); + } + goto err; + } + } + + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + goto done; + } + break; + + case EJS_TOK_INC_DEC: + state = parseInc(ep, state, flags); + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + break; + + case EJS_TOK_NEW: + /* MOB -- could we remove rs and just use state */ + sp->rs = ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_NEW); + if (sp->rs < 0) { + state = sp->rs; + goto done; + } + break; + + case EJS_TOK_DELETE: + sp->rs = ejsParse(ep, EJS_STATE_EXPR, flags | EJS_FLAGS_DELETE); + if (sp->rs < 0) { + state = sp->rs; + goto done; + } + if (flags & EJS_FLAGS_EXE) { + /* Single place where properties are deleted */ + if (ep->currentObj == 0 || ep->currentProperty == 0) { + ejsError(ep, EJS_EVAL_ERROR, + "Can't find property to delete"); + goto err; + } + if (ep->currentObj->isArray) { + ejsSetArrayLength(ep, ep->currentObj, 0, + ep->currentProperty->name, 0); + } + ejsDeleteProperty(ep, ep->currentObj, + ep->currentProperty->name); + ep->currentProperty = 0; + } + goto done; + + case EJS_TOK_FUNCTION: + /* + * Parse a function declaration + */ + state = parseFunction(ep, state, flags); + goto done; + + case EJS_TOK_THROW: + state = parseThrow(ep, state, flags); + goto done; + + case EJS_TOK_TRY: + state = parseTry(ep, state, flags); + goto done; + + case EJS_TOK_CLASS: + case EJS_TOK_MODULE: + state = parseClass(ep, state, flags); + goto done; + + case EJS_TOK_LITERAL: + /* + * Set the result to the string literal + */ + if (flags & EJS_FLAGS_EXE) { + ejsWriteVarAsString(ep, ep->result, ep->token); + ejsSetVarName(ep, ep->result, ""); + } + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + goto done; + + case EJS_TOK_NUMBER: + /* + * Set the result to the parsed number + */ + if (flags & EJS_FLAGS_EXE) { + ejsWriteVar(ep, ep->result, &ep->tokenNumber, 0); + } + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + goto done; + + case EJS_TOK_METHOD_NAME: + /* + * parse a method() invocation + */ + mprAssert(ep->currentObj); + state = parseMethod(ep, state, flags, sp->id); + if (state == EJS_STATE_STMT) { + sp->expectEndOfStmt = 1; + } + if (ep->flags & EJS_FLAGS_EXIT) { + state = EJS_STATE_RET; + } + goto done; + + case EJS_TOK_IF: + state = parseIf(ep, state, flags, &sp->done); + if (state < 0) { + goto done; + } + break; + + case EJS_TOK_FOR: + state = parseFor(ep, state, flags); + goto done; + + case EJS_TOK_VAR: + if ((sp->rs = ejsParse(ep, EJS_STATE_DEC_LIST, flags)) < 0) { + state = sp->rs; + goto done; + } + goto done; + + case EJS_TOK_COMMA: + ejsLexPutbackToken(ep, sp->tid, ep->token); + goto done; + + case EJS_TOK_LPAREN: + if (state == EJS_STATE_EXPR) { + if ((sp->rs = ejsParse(ep, EJS_STATE_RELEXP, flags)) < 0) { + state = sp->rs; + goto done; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + ejsSyntaxError(ep, 0); + goto err; + } + goto done; + + } else if (state == EJS_STATE_STMT) { + ejsLexPutbackToken(ep, EJS_TOK_METHOD_NAME, ep->token); + } + break; + + case EJS_TOK_RPAREN: + ejsLexPutbackToken(ep, sp->tid, ep->token); + goto done; + + case EJS_TOK_EXTENDS: + if (! (flags & EJS_FLAGS_CLASS_DEC)) { + ejsSyntaxError(ep, 0); + goto err; + } + sp->saveObj = ep->currentObj; + sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); + + sp->rs = ejsParse(ep, EJS_STATE_STMT, flags); + ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); + + if (sp->rs < 0) { + state = sp->rs; + goto done; + } + + if (flags & EJS_FLAGS_EXE) { + if (createClass(ep, sp->saveObj, sp->id, + ejsGetVarPtr(ep->currentProperty)) < 0) { + goto err; + } + } + if (ejsLexGetToken(ep, state) != EJS_TOK_LBRACE) { + ejsSyntaxError(ep, 0); + goto err; + } + ejsLexPutbackToken(ep, ep->tid, ep->token); + goto done; + + case EJS_TOK_LBRACE: + if (flags & EJS_FLAGS_CLASS_DEC) { + if (state == EJS_STATE_DEC) { + if (flags & EJS_FLAGS_EXE) { + if (createClass(ep, ep->currentObj, sp->id, 0) < 0) { + goto err; + } + } + ejsLexPutbackToken(ep, sp->tid, ep->token); + + } else if (state == EJS_STATE_STMT) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + goto done; + } + + /* + * This handles any code in braces except "if () {} else {}" + */ + if (state != EJS_STATE_STMT) { + ejsSyntaxError(ep, 0); + goto err; + } + + /* + * Parse will return EJS_STATE_STMT_BLOCK_DONE when the RBRACE + * is seen. + */ + sp->exception = 0; + do { + state = ejsParse(ep, EJS_STATE_STMT, flags); + if (state == EJS_STATE_ERR) { + /* + * We need to keep parsing to get to the end of the block + */ + if (sp->exception == 0) { + sp->exception = ejsDupVar(ep, ep->result, + EJS_SHALLOW_COPY); + if (sp->exception == 0) { + ejsMemoryError(ep); + goto err; + } + if (sp->exception->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(sp->exception, 0); + mprAssert(sp->exception->objectState->alive == 0); + } + + /* + * If we're in a try block, we need to keep parsing + * so we can find the end of the block and the start + * of the catch block. Otherwise, we are done. + */ + if (!(flags & EJS_FLAGS_TRY)) { + break; + } + } + flags &= ~EJS_FLAGS_EXE; + if (ep->recurseCount > 20) { + break; + } + state = EJS_STATE_STMT_DONE; + ep->gotException = 0; + } + + } while (state == EJS_STATE_STMT_DONE); + + if (sp->exception) { + ep->gotException = 1; + ejsWriteVar(ep, ep->result, sp->exception, EJS_SHALLOW_COPY); + + /* Eat the closing brace */ + ejsLexGetToken(ep, state); + ejsFreeVar(ep, sp->exception); + + goto err; + } + ejsFreeVar(ep, sp->exception); + + if (state < 0) { + goto done; + } + + if (ejsLexGetToken(ep, state) != EJS_TOK_RBRACE) { + ejsSyntaxError(ep, 0); + goto err; + } + state = EJS_STATE_STMT_DONE; + goto done; + + case EJS_TOK_RBRACE: + if (state == EJS_STATE_STMT) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = EJS_STATE_STMT_BLOCK_DONE; + + } else if (state == EJS_STATE_EXPR) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = EJS_STATE_EXPR; + + } else { + ejsSyntaxError(ep, 0); + state = EJS_STATE_ERR; + } + goto done; + + case EJS_TOK_RETURN: + if ((sp->rs = ejsParse(ep, EJS_STATE_RELEXP, flags)) < 0) { + state = sp->rs; + goto done; + } + if (flags & EJS_FLAGS_EXE) { + state = EJS_STATE_RET; + goto done; + } + break; + } + } +done: + mprFree(sp->id); + + if (sp->expectEndOfStmt && state >= 0) { + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid == EJS_TOK_RBRACE) { + ejsLexPutbackToken(ep, EJS_TOK_RBRACE, ep->token); + + } else if (sp->tid != EJS_TOK_SEMI && sp->tid != EJS_TOK_NEWLINE && + sp->tid != EJS_TOK_EOF) { + ejsSyntaxError(ep, 0); + state = EJS_STATE_ERR; + + } else { + /* + * Skip newlines after semi-colon + */ + removeNewlines(ep, state); + } + } + + /* + * Advance the state + */ + switch (state) { + case EJS_STATE_STMT: + case EJS_STATE_STMT_DONE: + state = EJS_STATE_STMT_DONE; + break; + + case EJS_STATE_DEC: + case EJS_STATE_DEC_DONE: + state = EJS_STATE_DEC_DONE; + break; + + case EJS_STATE_EXPR: + case EJS_STATE_EXPR_DONE: + state = EJS_STATE_EXPR_DONE; + break; + + case EJS_STATE_STMT_BLOCK_DONE: + case EJS_STATE_EOF: + case EJS_STATE_RET: + break; + + default: + if (state != EJS_STATE_ERR) { + ejsSyntaxError(ep, 0); + } + state = EJS_STATE_ERR; + } + popFrame(ep, sizeof(ParseStmt)); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseFor { + char *initToken; + int tid, foundVar, initId, each; +} ParseFor; + +/* + * Parse method arguments + */ + +static int parseFor(Ejs *ep, int state, int flags) +{ + ParseFor *sp; + + if ((sp = pushFrame(ep, sizeof(ParseFor))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(ep); + + if (state != EJS_STATE_STMT) { + ejsSyntaxError(ep, 0); + goto err; + } + + if ((sp->tid = ejsLexGetToken(ep, state)) == EJS_TOK_EACH) { + sp->each = 1; + sp->tid = ejsLexGetToken(ep, state); + + } else { + sp->each = 0; + } + + if (sp->tid != EJS_TOK_LPAREN) { + ejsSyntaxError(ep, 0); + goto err; + } + + /* + * Need to peek 2-3 tokens ahead and see if this is a + * for [each] ([var] x in set) + * or + * for (init ; whileCond; incr) + */ + sp->initId = ejsLexGetToken(ep, EJS_STATE_EXPR); + sp->foundVar = 0; + if (sp->initId == EJS_TOK_ID && strcmp(ep->token, "var") == 0) { + sp->foundVar = 1; + sp->initId = ejsLexGetToken(ep, EJS_STATE_EXPR); + } + sp->initToken = mprStrdup(ep, ep->token); + + sp->tid = ejsLexGetToken(ep, EJS_STATE_EXPR); + + ejsLexPutbackToken(ep, sp->tid, ep->token); + ejsLexPutbackToken(ep, sp->initId, sp->initToken); + mprFree(sp->initToken); + + if (sp->foundVar) { + ejsLexPutbackToken(ep, EJS_TOK_ID, "var"); + } + + if (sp->tid == EJS_TOK_IN) { + state = parseForIn(ep, state, flags, sp->each); + + } else { + state = parseRegFor(ep, state, flags); + } + +done: + popFrame(ep, sizeof(ParseFor)); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse method arguments + */ + +static int parseArgs(Ejs *ep, int state, int flags) +{ + EjsVar *vp; + 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; + } + + /* + * If this is part of a constructor, must run methods in args normally + */ + flags &= ~EJS_FLAGS_NEW; + + state = ejsParse(ep, EJS_STATE_RELEXP, flags); + if (state < 0) { + return state; + } + if (flags & EJS_FLAGS_EXE) { + mprAssert(ep->proc->args); + vp = ejsDupVar(ep, ep->result, EJS_SHALLOW_COPY); + if (vp == 0) { + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + /* MOB */ + if (vp->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(vp, 0); + mprAssert(vp->objectState->alive == 0); + } + + /* + * Propagate the name + */ + ejsSetVarName(ep, vp, ep->result->propertyName); + + mprAddItem(ep->proc->args, vp); + + } + /* + * 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) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + return EJS_STATE_ARG_LIST_DONE; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseAssign { + EjsProperty *saveProperty; + EjsVar *saveObj; + int saveObjPerm, savePropPerm, rc; +} ParseAssign; + +/* + * Parse an assignment statement + */ + +static int parseAssignment(Ejs *ep, int state, int flags, char *id) +{ + ParseAssign *sp; + + + if (id == 0) { + if (!ep->gotException) { + ejsSyntaxError(ep, 0); + } + return EJS_STATE_ERR; + } + + if ((sp = pushFrame(ep, sizeof(ParseAssign))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(ep->currentObj); + + /* + * Parse the right hand side of the "=" + */ + sp->saveObj = ep->currentObj; + sp->saveProperty = ep->currentProperty; + + sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); + sp->savePropPerm = ejsMakeObjPermanent(ejsGetVarPtr(sp->saveProperty), 1); + + sp->rc = ejsParse(ep, EJS_STATE_RELEXP, flags | EJS_FLAGS_ASSIGNMENT); + + ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); + ejsMakeObjPermanent(ejsGetVarPtr(sp->saveProperty), sp->savePropPerm); + + if (sp->rc < 0) { + state = EJS_STATE_ERR; + } + + ep->currentObj = sp->saveObj; + ep->currentProperty = sp->saveProperty; + + popFrame(ep, sizeof(ParseAssign)); + + if (! (flags & EJS_FLAGS_EXE)) { + return state; + } + + return state; +} + +/******************************************************************************/ + +static int assignPropertyValue(Ejs *ep, char *id, int state, EjsVar *value, + int flags) +{ + EjsProperty *saveProperty; + EjsVar *saveObj, *obj, *vp; + char *procName; + int saveObjPerm, savePropPerm, rc; + + mprAssert(flags & EJS_FLAGS_EXE); + + if (ep->currentProperty && + !ep->currentProperty->var.flags & EJS_GET_ACCESSOR) { + obj = ep->currentObj; + + } else { + /* + * Handle any set accessors. + * FUTURE OPT -- could be faster + * FUTURE OPT -- coming here even when doing just a set "x = value"; + */ + procName = 0; + if (mprAllocStrcat(MPR_LOC_ARGS(ep), &procName, EJS_MAX_ID + 5, 0, + "-set-", id, 0) > 0) { + + MprArray *args; + + ep->currentProperty = searchSpacesForProperty(ep, state, + ep->currentObj, procName, flags); + + if (ep->currentProperty) { + args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + + vp = ejsDupVar(ep, value, EJS_SHALLOW_COPY); + mprAddItem(args, vp); + mprAssert(! ejsObjIsCollectable(vp)); + + saveObj = ep->currentObj; + saveProperty = ep->currentProperty; + + saveObjPerm = ejsMakeObjPermanent(saveObj, 1); + savePropPerm = ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), + 1); + + /* + * Invoke the set accessor + */ + rc = ejsRunMethod(ep, ep->currentObj, procName, args); + mprFree(procName); + ejsFreeMethodArgs(ep, args); + + ejsMakeObjPermanent(saveObj, saveObjPerm); + ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), savePropPerm); + + ep->currentObj = saveObj; + ep->currentProperty = saveProperty; + + if (rc < 0) { + return EJS_STATE_ERR; + } + return state; + } + mprFree(procName); + } + + if (ep->currentProperty == 0) { + /* + * MOB -- can we omit this as updateProperty below will create + */ + if (createProperty(ep, &obj, id, state) < 0) { + return EJS_STATE_ERR; + } + } + } + + if (updateProperty(ep, obj, id, state, value) < 0) { + return EJS_STATE_ERR; + } + + vp = ejsGetVarPtr(ep->currentProperty); + if (vp->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(vp, 1); + } + + return state; +} + +/******************************************************************************/ + +static int parseObjectLiteral(Ejs *ep, int state, int flags, char *id) +{ + EjsProperty *saveProperty; + EjsVar *saveObj; + EjsVar *obj; + char *name; + int saveObjPerm, savePropPerm, tid; + + name = 0; + + saveObj = ep->currentObj; + saveProperty = ep->currentProperty; + + saveObjPerm = ejsMakeObjPermanent(saveObj, 1); + savePropPerm = ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), 1); + + if (flags & EJS_FLAGS_EXE) { + obj = ejsCreateSimpleObj(ep, "Object"); + if (obj == 0) { + ejsMemoryError(ep); + goto err; + } + mprAssert(! ejsObjIsCollectable(obj)); + + } else { + obj = 0; + } + + do { + tid = getNextNonSpaceToken(ep, state); + if (tid != EJS_TOK_ID) { + ejsSyntaxError(ep, 0); + goto err; + } + name = mprStrdup(ep, ep->token); + + tid = getNextNonSpaceToken(ep, state); + if (tid != EJS_TOK_COLON) { + ejsSyntaxError(ep, 0); + goto err; + } + + if (flags & EJS_FLAGS_EXE) { + /* FUTURE OPT -- can we optimize this. We are double accessing id + with the Put below. Should we be using this or ejsSetProperty + */ + if (ejsCreatePropertyMethod(ep, obj, name) == 0) { + ejsMemoryError(ep); + goto err; + } + } + + if (ejsParse(ep, EJS_STATE_RELEXP, flags) < 0) { + goto err; + } + if (flags & EJS_FLAGS_EXE) { + if (ejsSetPropertyMethod(ep, obj, name, ep->result) == 0) { + ejsMemoryError(ep); + goto err; + } + } + mprFree(name); + name = 0; + + tid = getNextNonSpaceToken(ep, state); + + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_RBRACE) { + ejsSyntaxError(ep, 0); + goto err; + } + + if (flags & EJS_FLAGS_EXE) { + ejsMakeObjLive(obj, 1); + ejsWriteVar(ep, ep->result, obj, EJS_SHALLOW_COPY); + } + +done: + ejsMakeObjPermanent(saveObj, saveObjPerm); + ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), savePropPerm); + + ep->currentObj = saveObj; + ep->currentProperty = saveProperty; + + if (obj) { + ejsFreeVar(ep, obj); + } + return state; + +err: + mprFree(name); + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ + +static int parseArrayLiteral(Ejs *ep, int state, int flags, char *id) +{ + EjsProperty *saveProperty; + EjsVar *saveObj; + EjsVar *obj; + int saveObjPerm, savePropPerm, tid; + + saveObj = ep->currentObj; + saveProperty = ep->currentProperty; + + saveObjPerm = ejsMakeObjPermanent(saveObj, 1); + savePropPerm = ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), 1); + + if (flags & EJS_FLAGS_EXE) { + obj = ejsCreateArray(ep, 0); + if (obj == 0) { + ejsMemoryError(ep); + goto err; + } + mprAssert(! ejsObjIsCollectable(obj)); + + } else { + obj = 0; + } + + do { + if (ejsParse(ep, EJS_STATE_RELEXP, flags) < 0) { + goto err; + } + if (flags & EJS_FLAGS_EXE) { + /* MOB _- should this be put[array.length] */ + if (ejsAddArrayElt(ep, obj, ep->result, EJS_SHALLOW_COPY) == 0) { + goto err; + } + } + + tid = getNextNonSpaceToken(ep, state); + + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_RBRACKET) { + ejsSyntaxError(ep, "Missing right bracket"); + goto err; + } + + if (flags & EJS_FLAGS_EXE) { + ejsMakeObjLive(obj, 1); + ejsWriteVar(ep, ep->result, obj, EJS_SHALLOW_COPY); + } + +done: + ejsMakeObjPermanent(saveObj, saveObjPerm); + ejsMakeObjPermanent(ejsGetVarPtr(saveProperty), savePropPerm); + + ep->currentObj = saveObj; + ep->currentProperty = saveProperty; + + ejsFreeVar(ep, obj); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Create a property. + */ + +/* +MOB -- simplify this. Enforce ep->currentObj to be always set. +Then we can delete this and just call + + ejsCreatePropertyMethod(ep->currentObj, id); +*/ +//XX +static int createProperty(Ejs *ep, EjsVar **objp, const char *id, int state) +{ + EjsVar *obj, *vp; + + mprAssert(id && *id); + mprAssert(objp); + + /* + * Determine the variable scope to use for the property. + * Standard says: "var x" means declare locally. + * "x = 2" means declare globally if x is undefined. + */ + if (ep->currentObj) { + if (ep->currentObj->type != EJS_TYPE_OBJECT) { + ejsSyntaxError(ep, "Reference is not an object"); + return EJS_STATE_ERR; + } + obj = ep->currentObj; + + } else { + /* MOB -- we should never be doing this here. ep->currentObj should + always be set already */ + obj = (state == EJS_STATE_DEC) ? ep->local : ep->global; + } + mprAssert(obj); + + vp = ejsCreatePropertyMethod(ep, obj, id); + if (vp == 0) { + if (!ep->gotException) { + ejsMemoryError(ep); + } + return EJS_STATE_ERR; + } + + *objp = obj; + return state; +} + +/******************************************************************************/ +/* + * Update a property. + * + * Return with ep->currentProperty updated to point to the property. + */ + +static int updateProperty(Ejs *ep, EjsVar *obj, const char *id, int state, + EjsVar *value) +{ + EjsVar *vp; + + /* + * MOB -- do ready-only check here + */ + vp = ejsSetPropertyMethod(ep, obj, id, value); + if (vp == 0) { + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + ep->currentProperty = ejsGetPropertyPtr(vp); + + obj->objectState->dirty = 1; + + return state; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseCond { + EjsVar lhs, rhs; + int tid, operator; +} ParseCond; + +/* + * Parse conditional expression (relational ops separated by ||, &&) + */ + +static int parseCond(Ejs *ep, int state, int flags) +{ + ParseCond *sp; + + if ((sp = pushFrame(ep, sizeof(ParseCond))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(ep); + + if (flags & EJS_FLAGS_EXE) { + ejsClearVar(ep, ep->result); + } + + sp->lhs.type = sp->rhs.type = EJS_TYPE_UNDEFINED; + sp->lhs.objectState = sp->rhs.objectState = 0; + sp->lhs.allocatedData = sp->rhs.allocatedData = 0; + + ejsSetVarName(ep, &sp->lhs, "lhs"); + ejsSetVarName(ep, &sp->rhs, "rhs"); + + sp->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 < 0) { + break; + } + + if (flags & EJS_FLAGS_EXE) { + if (sp->operator > 0) { + /* + * FUTURE -- does not do precedence + */ + ejsWriteVar(ep, &sp->rhs, ep->result, EJS_SHALLOW_COPY); + if (evalCond(ep, &sp->lhs, sp->operator, &sp->rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + /* Result left in ep->result */ + /* MOB */ + if (sp->lhs.type == EJS_TYPE_OBJECT) { + mprAssert(sp->lhs.objectState->alive == 0); + } + if (sp->rhs.type == EJS_TYPE_OBJECT) { + mprAssert(sp->rhs.objectState->alive == 0); + } + } + } + + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid == EJS_TOK_LOGICAL) { + sp->operator = (int) *ep->token; + + } else if (sp->tid == EJS_TOK_RPAREN || sp->tid == EJS_TOK_SEMI) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = EJS_STATE_COND_DONE; + break; + + } else { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + + if (flags & EJS_FLAGS_EXE) { + ejsWriteVar(ep, &sp->lhs, ep->result, EJS_SHALLOW_COPY); + } + + } while (state == EJS_STATE_RELEXP_DONE); + + ejsClearVar(ep, &sp->lhs); + ejsClearVar(ep, &sp->rhs); + + popFrame(ep, sizeof(ParseCond)); + + 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; + * var x = { property: value, property: value ... }; + * var x = [ property: value, property: value ... ]; + * + * 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) { + ejsSyntaxError(ep, 0); + 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 + * Stop on ";" or "in" which is used in a "for (var x in ..." + */ + tid = ejsLexGetToken(ep, state); + + if (tid == EJS_TOK_SEMI) { + return EJS_STATE_DEC_LIST_DONE; + + } else if (tid == EJS_TOK_IN) { + ejsLexPutbackToken(ep, tid, ep->token); + return EJS_STATE_DEC_LIST_DONE; + + } else if (flags & EJS_FLAGS_CLASS_DEC && + (tid == EJS_TOK_LBRACE || tid == EJS_TOK_EXTENDS)) { + ejsLexPutbackToken(ep, tid, ep->token); + return EJS_STATE_DEC_LIST_DONE; + + } else if (tid == EJS_TOK_RPAREN && flags & EJS_FLAGS_CATCH) { + ejsLexPutbackToken(ep, tid, ep->token); + return EJS_STATE_DEC_LIST_DONE; + + } else if (tid != EJS_TOK_COMMA) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + } while (tid == EJS_TOK_COMMA); + + if (tid != EJS_TOK_SEMI) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + return EJS_STATE_DEC_LIST_DONE; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseExpr { + EjsVar lhs, rhs; + int rel, tid, unaryMinus; +} ParseExpr; + +/* + * Parse expression (leftHandSide operator rightHandSide) + */ + +static int parseExpr(Ejs *ep, int state, int flags) +{ + ParseExpr *sp; + + mprAssert(ep); + + if ((sp = pushFrame(ep, sizeof(ParseExpr))) == 0) { + return EJS_STATE_ERR; + } + + if (flags & EJS_FLAGS_EXE) { + ejsClearVar(ep, ep->result); + } + + sp->lhs.type = sp->rhs.type = EJS_TYPE_UNDEFINED; + sp->lhs.objectState = sp->rhs.objectState = 0; + sp->lhs.allocatedData = sp->rhs.allocatedData = 0; + + ejsSetVarName(ep, &sp->lhs, "lhs"); + ejsSetVarName(ep, &sp->rhs, "rhs"); + + sp->rel = 0; + sp->tid = 0; + sp->unaryMinus = 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 (sp->tid == EJS_TOK_LOGICAL) { + state = ejsParse(ep, EJS_STATE_RELEXP, flags); + if (state < 0) { + break; + } + } else { + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid == EJS_TOK_EXPR && (int) *ep->token == EJS_EXPR_MINUS) { + sp->unaryMinus = 1; + + } else { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + + state = ejsParse(ep, EJS_STATE_EXPR, flags); + if (state < 0) { + break; + } + } + + if (flags & EJS_FLAGS_EXE) { + if (sp->unaryMinus) { + switch (ep->result->type) { + default: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_STRING_CMETHOD: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_METHOD: + case EJS_TYPE_PTR: + case EJS_TYPE_OBJECT: + case EJS_TYPE_STRING: + case EJS_TYPE_BOOL: + ejsError(ep, EJS_SYNTAX_ERROR, "Invalid unary minus"); + state = EJS_STATE_ERR; + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + ep->result->floating = - ep->result->floating; + break; +#endif + + case EJS_TYPE_INT: + ep->result->integer = - ep->result->integer; + break; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + ep->result->integer64 = - ep->result->integer64; + break; +#endif + } + } + sp->unaryMinus = 0; + + if (sp->rel > 0) { + ejsWriteVar(ep, &sp->rhs, ep->result, EJS_SHALLOW_COPY); + if (sp->tid == EJS_TOK_LOGICAL) { + if (evalCond(ep, &sp->lhs, sp->rel, &sp->rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + } else { + if (evalExpr(ep, &sp->lhs, sp->rel, &sp->rhs) < 0) { + state = EJS_STATE_ERR; + break; + } + } + } + /* MOB */ + if (sp->lhs.type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(&sp->lhs, 0); + mprAssert(sp->lhs.objectState->alive == 0); + } + if (sp->rhs.type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(&sp->rhs, 0); + mprAssert(sp->rhs.objectState->alive == 0); + } + } + + if ((sp->tid = ejsLexGetToken(ep, state)) == EJS_TOK_EXPR || + sp->tid == EJS_TOK_INC_DEC || sp->tid == EJS_TOK_LOGICAL) { + sp->rel = (int) *ep->token; + ejsWriteVar(ep, &sp->lhs, ep->result, EJS_SHALLOW_COPY); + + } else { + ejsLexPutbackToken(ep, sp->tid, ep->token); + state = EJS_STATE_RELEXP_DONE; + } + + } while (state == EJS_STATE_EXPR_DONE); + + ejsClearVar(ep, &sp->lhs); + ejsClearVar(ep, &sp->rhs); + + popFrame(ep, sizeof(ParseExpr)); + + return state; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseForIn { + EjsInput *endScript, *bodyScript; + EjsProperty *pp, *nextp; + EjsVar *iteratorVar, *setVar, *vp; + int forFlags, tid; +} ParseForIn; + +/* + * Parse the "for ... in" statement. Format for the statement is: + * + * for [each] (var varName in expression) { + * body; + * } + */ + +static int parseForIn(Ejs *ep, int state, int flags, int each) +{ + ParseForIn *sp; + + mprAssert(ep); + + if ((sp = pushFrame(ep, sizeof(ParseForIn))) == 0) { + return EJS_STATE_ERR; + } + + sp->setVar = 0; + sp->iteratorVar = 0; + sp->bodyScript = 0; + sp->endScript = 0; + + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid != EJS_TOK_ID && sp->tid != EJS_TOK_VAR) { + ejsSyntaxError(ep, 0); + goto err; + } + ejsLexPutbackToken(ep, sp->tid, ep->token); + + state = ejsParse(ep, EJS_STATE_EXPR, EJS_FLAGS_FORIN | flags); + if (state < 0) { + goto done; + } + if (flags & EJS_FLAGS_EXE) { + if (ep->currentProperty == 0) { + ejsSyntaxError(ep, 0); + goto err; + } + sp->iteratorVar = &ep->currentProperty->var; + } else { + sp->iteratorVar = 0; + } + + if (ejsLexGetToken(ep, state) != EJS_TOK_IN) { + ejsSyntaxError(ep, 0); + goto err; + } + + /* + * Get the set + */ + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid != EJS_TOK_ID) { + ejsSyntaxError(ep, 0); + goto err; + } + ejsLexPutbackToken(ep, sp->tid, ep->token); + + state = ejsParse(ep, EJS_STATE_EXPR, flags); + if (state < 0) { + goto done; + } + + if ((flags & EJS_FLAGS_EXE) && + (ep->result == 0 || ep->result->type == EJS_TYPE_UNDEFINED)) { + ejsError(ep, EJS_REFERENCE_ERROR, "Can't access array or object"); + goto err; + } + + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + ejsSyntaxError(ep, 0); + goto err; + } + + sp->setVar = ejsDupVar(ep, ep->result, EJS_SHALLOW_COPY); + + sp->bodyScript = getInputStruct(ep); + + /* + * Parse the body and remember the end of the body script + */ + sp->forFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, sp->bodyScript); + + state = ejsParse(ep, EJS_STATE_STMT, sp->forFlags); + if (state < 0) { + goto done; + } + + sp->endScript = getInputStruct(ep); + ejsInitInputState(sp->endScript); + ejsLexSaveInputState(ep, sp->endScript); + + /* + * Enumerate the properties + */ + if (flags & EJS_FLAGS_EXE) { + if (sp->setVar->type == EJS_TYPE_OBJECT) { + + sp->setVar->objectState->preventDeleteProp = 1; + + sp->pp = ejsGetFirstProperty(sp->setVar, 0); + while (sp->pp) { + sp->nextp = ejsGetNextProperty(sp->pp, 0); + if (! sp->pp->dontEnumerate && !sp->pp->delayedDelete) { + if (each) { + sp->vp = ejsWriteVar(ep, sp->iteratorVar, + ejsGetVarPtr(sp->pp), EJS_SHALLOW_COPY); + } else { + sp->vp = ejsWriteVarAsString(ep, sp->iteratorVar, + sp->pp->name); + } + if (sp->vp == 0) { + ejsError(ep, EJS_MEMORY_ERROR, + "Can't write to variable"); + goto err; + } + + ejsLexRestoreInputState(ep, sp->bodyScript); + + state = ejsParse(ep, EJS_STATE_STMT, flags); + + if (state < 0) { + if (sp->setVar->objectState) { + sp->setVar->objectState->preventDeleteProp = 0; + } + goto done; + } + } + sp->pp = sp->nextp; + } + + /* + * Process delayed deletes + */ + if (sp->setVar->objectState) { + sp->setVar->objectState->preventDeleteProp = 0; + if (sp->setVar->objectState->delayedDeleteProp) { + sp->pp = ejsGetFirstProperty(sp->setVar, 0); + while (sp->pp) { + sp->nextp = ejsGetNextProperty(sp->pp, 0); + if (sp->pp->delayedDelete) { + ejsDeleteProperty(ep, sp->setVar, sp->pp->name); + } + sp->pp = sp->nextp; + } + sp->setVar->objectState->delayedDeleteProp = 0; + } + } + + } else { + ejsError(ep, EJS_REFERENCE_ERROR, + "Variable to iterate over is not an array or object"); + goto err; + } + } + + ejsLexRestoreInputState(ep, sp->endScript); + +done: + if (sp->endScript) { + ejsLexFreeInputState(ep, sp->endScript); + ejsLexFreeInputState(ep, sp->bodyScript); + } + + if (sp->bodyScript) { + freeInputStruct(ep, sp->bodyScript); + } + if (sp->endScript) { + freeInputStruct(ep, sp->endScript); + } + + if (sp->setVar) { + ejsFreeVar(ep, sp->setVar); + } + + popFrame(ep, sizeof(ParseForIn)); + + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse the for statement. Format for the expression is: + * + * for (initial; condition; incr) { + * body; + * } + */ + +static int parseRegFor(Ejs *ep, int state, int flags) +{ + EjsInput *condScript, *endScript, *bodyScript, *incrScript; + + endScript = getInputStruct(ep); + bodyScript = getInputStruct(ep); + incrScript = getInputStruct(ep); + condScript = getInputStruct(ep); + + ejsInitInputState(endScript); + ejsInitInputState(bodyScript); + ejsInitInputState(incrScript); + ejsInitInputState(condScript); + + state = parseForInner(ep, state, flags, + condScript, incrScript, bodyScript, endScript); + + ejsLexFreeInputState(ep, condScript); + ejsLexFreeInputState(ep, incrScript); + ejsLexFreeInputState(ep, endScript); + ejsLexFreeInputState(ep, bodyScript); + + freeInputStruct(ep, condScript); + freeInputStruct(ep, incrScript); + freeInputStruct(ep, endScript); + freeInputStruct(ep, bodyScript); + + return state; +} + +/******************************************************************************/ + +static int parseForInner(Ejs *ep, int state, int flags, EjsInput *condScript, + EjsInput *incrScript, EjsInput *bodyScript, EjsInput *endScript) +{ + int forFlags, cond, rs; + + mprAssert(ep); + + /* + * Evaluate the for loop initialization statement + */ + if ((state = ejsParse(ep, EJS_STATE_STMT, flags)) < 0) { + return state; + } + + /* + * 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 ((rs = ejsParse(ep, EJS_STATE_COND, flags)) < 0) { + return rs; + } + + cond = (ep->result->boolean != 0); + + if (ejsLexGetToken(ep, state) != EJS_TOK_SEMI) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* + * Don't execute the loop increment statement or the body + * first time. + */ + forFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, incrScript); + if ((rs = ejsParse(ep, EJS_STATE_EXPR, forFlags)) < 0) { + return rs; + } + + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* + * Parse the body and remember the end of the body script + */ + ejsLexSaveInputState(ep, bodyScript); + if ((rs = ejsParse(ep, EJS_STATE_STMT, forFlags)) < 0) { + return rs; + } + 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); + + if ((rs = ejsParse(ep, EJS_STATE_STMT, flags)) < 0) { + return rs; + } + + /* + * Evaluate the increment script + */ + ejsLexRestoreInputState(ep, incrScript); + if ((rs = ejsParse(ep, EJS_STATE_EXPR, flags)) < 0) { + return rs; + } + /* + * Evaluate the condition + */ + ejsLexRestoreInputState(ep, condScript); + if ((rs = ejsParse(ep, EJS_STATE_COND, flags)) < 0) { + return 0; + } + mprAssert(ep->result->type == EJS_TYPE_BOOL); + cond = (ep->result->boolean != 0); + } + + ejsLexRestoreInputState(ep, endScript); + + return state; +} + +/******************************************************************************/ +/* + * Create the bare class object + */ + +static int createClass(Ejs *ep, EjsVar *obj, const char *className, + EjsVar *baseClass) +{ + EjsVar *classObj, *existingClass; + + existingClass = ejsGetClass(ep, obj, className); + if (existingClass) { + /* + * We allow partial clases and method redefinition + * FUTURE -- should prevent this if the class is sealed. + * DISABLED Error message and return OK. + */ + /* ejsError(ep, EJS_EVAL_ERROR, "Can't create class %s", className); */ + return 0; + } + + if (baseClass == 0) { + baseClass = ejsGetClass(ep, ep->service->globalClass, "Object"); + mprAssert(baseClass); + } + + classObj = ejsCreateSimpleClass(ep, baseClass, className); + if (classObj == 0) { + ejsMemoryError(ep); + return -1; + } + mprAssert(! ejsObjIsCollectable(classObj)); + + ep->currentProperty = ejsSetPropertyAndFree(ep, obj, className, classObj); + mprAssert(ep->currentProperty); + + if (ep->currentProperty == 0) { + return -1; + } + + return 0; +} + +/******************************************************************************/ +/* + * Local vars for parseTry + */ + +typedef struct ParseTry { + EjsVar *exception; + int tid, caught, rs, catchFlags; +} ParseTry; + +/* + * Parse try block + * + * try {} + */ + +static int parseTry(Ejs *ep, int state, int flags) +{ + ParseTry *sp; + + if ((sp = pushFrame(ep, sizeof(ParseTry))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(ep); + + sp->caught = 0; + sp->exception = 0; + sp->catchFlags = flags; + + /* + * Execute the code in the try block + */ + sp->rs = ejsParse(ep, EJS_STATE_STMT, flags | EJS_FLAGS_TRY); + if (sp->rs < 0) { + if (sp->rs == EJS_STATE_ERR) { + sp->exception = ejsDupVar(ep, ep->result, EJS_SHALLOW_COPY); + if (sp->exception == 0) { + ejsMemoryError(ep); + goto err; + } + } else { + state = sp->rs; + goto done; + } + + } else { + sp->catchFlags = flags & ~EJS_FLAGS_EXE; + } + + /* + * On success path or when an exception is caught, we must parse all + * catch and finally blocks. + */ + sp->tid = getNextNonSpaceToken(ep, state); + + if (sp->tid == EJS_TOK_CATCH) { + + ep->gotException = 0; + + sp->tid = getNextNonSpaceToken(ep, state); + + if (sp->tid == EJS_TOK_LBRACE) { + /* + * Unqualified "catch " + */ + ejsLexPutbackToken(ep, sp->tid, ep->token); + if (ejsParse(ep, EJS_STATE_STMT, sp->catchFlags) >= 0) { + sp->caught++; + } + + } else if (sp->tid == EJS_TOK_LPAREN) { + + /* + * Qualified "catch (variable) " + */ + if ((sp->rs = ejsParse(ep, EJS_STATE_DEC_LIST, + sp->catchFlags | EJS_FLAGS_CATCH)) < 0) { + ejsSyntaxError(ep, "Bad catch statement"); + state = sp->rs; + goto done; + } + + sp->tid = getNextNonSpaceToken(ep, state); + if (sp->tid != EJS_TOK_RPAREN) { + ejsSyntaxError(ep, 0); + goto err; + } + + if (sp->catchFlags & EJS_FLAGS_EXE) { + if (ep->currentProperty == 0) { + ejsError(ep, EJS_EVAL_ERROR, "Can't define catch variable"); + goto err; + } + + /* + * Set the catch variable + */ + if (ejsWriteVar(ep, + ejsGetVarPtr(ep->currentProperty), sp->exception, + EJS_SHALLOW_COPY) == 0) { + ejsError(ep, EJS_EVAL_ERROR, "Can't update catch variable"); + goto err; + } + } + + /* + * Parse the catch block + */ + if ((sp->rs = ejsParse(ep, EJS_STATE_STMT, sp->catchFlags)) < 0) { + state = sp->rs; + goto done; + } + sp->caught++; + ep->gotException = 0; + } + sp->tid = getNextNonSpaceToken(ep, state); + } + + /* + * Parse the finally block + */ + if (sp->tid == EJS_TOK_FINALLY) { + if (ejsParse(ep, EJS_STATE_STMT, flags) < 0) { + goto err; + } + } else { + ejsLexPutbackToken(ep, sp->tid, ep->token); + } + + /* + * Set the exception value + */ + if (sp->exception && !sp->caught) { + ejsWriteVar(ep, ep->result, sp->exception, EJS_SHALLOW_COPY); + goto err; + } + + state = EJS_STATE_STMT_DONE; + +done: + if (sp->exception) { + ejsFreeVar(ep, sp->exception); + } + + popFrame(ep, sizeof(ParseTry)); + return state; + + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse throw statement + * + * throw expression + */ + +static int parseThrow(Ejs *ep, int state, int flags) +{ + int rc; + + mprAssert(ep); + + if ((rc = ejsParse(ep, EJS_STATE_EXPR, flags)) < 0) { + return rc; + } + + + if (flags & EJS_FLAGS_EXE) { + /* + * We have thrown the exception so set the state to ERR + */ + ep->gotException = 1; + return EJS_STATE_ERR; + } + return state; +} + +/******************************************************************************/ +/* + * Parse a class and module declaration + * + * class <name> [extends baseClass] { + * [public | private | ... ] var declarations ... + * [constructor] function declarations ... + * } + * + * Modules are identical except declared with a "module" instead of + * "class". Modules cannot be instantiated and are used for mixins. + * + */ + +static int parseClass(Ejs *ep, int state, int flags) +{ + int originalToken, tid, fid; + + mprAssert(ep); + + originalToken = ep->tid; + + /* + * Parse "class Name [extends BaseClass]" + */ + if (ejsParse(ep, EJS_STATE_DEC_LIST, flags | EJS_FLAGS_CLASS_DEC) < 0) { + return EJS_STATE_ERR; + } + + tid = getNextNonSpaceToken(ep, state); + + if (tid != EJS_TOK_LBRACE) { + return EJS_STATE_ERR; + } + + /* + * After parsing the class body, ep->local will contain the actual + * class/module object. So, we save ep->local by creating a new block. + */ + if (flags & EJS_FLAGS_EXE) { + fid = ejsSetBlock(ep, ejsGetVarPtr(ep->currentProperty)); + ejsSetVarName(ep, ep->local, ep->currentProperty->name); + + } else { + fid = -1; + } + + /* FUTURE -- should prevent modules from being instantiated */ + + /* + * Parse class body + */ + do { + state = ejsParse(ep, EJS_STATE_STMT, flags); + if (state < 0) { + if (fid >= 0) { + ejsCloseBlock(ep, fid); + } + return state; + } + tid = getNextNonSpaceToken(ep, state); + if (tid == EJS_TOK_RBRACE) { + break; + } + ejsLexPutbackToken(ep, tid, ep->token); + + } while (state >= 0); + + if (fid >= 0) { + ejsCloseBlock(ep, fid); + } + + if (tid != EJS_TOK_RBRACE) { + ejsSyntaxError(ep, 0); + state = EJS_STATE_ERR; + } + + return state; +} + +/******************************************************************************/ +/* + * Parse a function declaration + */ + +static int parseFunction(Ejs *ep, int state, int flags) +{ + EjsInput *endScript, *bodyScript; + EjsProperty *pp; + EjsVar *func, *funcProp, *currentObj, *vp, *baseClass; + char *procName; + int varFlags, len, tid, bodyFlags, innerState; + + mprAssert(ep); + + func = 0; + varFlags = 0; + + /* + * method <name>(arg, arg, arg) { body }; + * method name(arg, arg, arg) { body }; + */ + + tid = ejsLexGetToken(ep, state); + + if (tid == EJS_TOK_GET) { + varFlags |= EJS_GET_ACCESSOR; + tid = ejsLexGetToken(ep, state); + + } else if (tid == EJS_TOK_SET) { + varFlags |= EJS_SET_ACCESSOR; + tid = ejsLexGetToken(ep, state); + } + + if (tid == EJS_TOK_ID) { + if (varFlags & EJS_SET_ACCESSOR) { + + if (mprAllocStrcat(MPR_LOC_ARGS(ep), &procName, EJS_MAX_ID + 5, + 0, "-set-", ep->token, 0) < 0) { + ejsError(ep, EJS_SYNTAX_ERROR, + "Name %s is too long", ep->token); + return EJS_STATE_ERR; + } + + } else { + procName = mprStrdup(ep, ep->token); + } + + tid = ejsLexGetToken(ep, state); + + } else { + procName = 0; + } + + if (tid != EJS_TOK_LPAREN) { + mprFree(procName); + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* + * Hand craft the method value structure. + */ + if (flags & EJS_FLAGS_EXE) { + func = ejsCreateMethodVar(ep, 0, 0, 0); + if (func == 0) { + mprFree(procName); + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + func->flags = varFlags; + } + + tid = ejsLexGetToken(ep, state); + while (tid == EJS_TOK_ID) { + if (flags & EJS_FLAGS_EXE) { + mprAddItem(func->method.args, + mprStrdup(func->method.args, 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); + ejsFreeVar(ep, func); + ejsSyntaxError(ep, 0); + 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); + ejsFreeVar(ep, func); + ejsSyntaxError(ep, 0); + return EJS_STATE_ERR; + } + + /* + * Register the method name early to allow for recursive + * method calls (see note in ECMA standard, page 71) + */ + funcProp = 0; + if (flags & EJS_FLAGS_EXE && procName) { + currentObj = pickSpace(ep, 0, procName, flags | EJS_FLAGS_LOCAL); + pp = ejsSetProperty(ep, currentObj, procName, func); + if (pp == 0) { + ejsFreeVar(ep, func); + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + funcProp = ejsGetVarPtr(pp); + } + + + bodyScript = getInputStruct(ep); + + /* + * Parse the method body. Turn execute off. + */ + bodyFlags = flags & ~EJS_FLAGS_EXE; + ejsLexSaveInputState(ep, bodyScript); + + do { + innerState = ejsParse(ep, EJS_STATE_STMT, bodyFlags); + } while (innerState == EJS_STATE_STMT_DONE); + + tid = ejsLexGetToken(ep, state); + + if (innerState != EJS_STATE_STMT_BLOCK_DONE || tid != EJS_TOK_RBRACE) { + mprFree(procName); + ejsFreeVar(ep, func); + ejsLexFreeInputState(ep, bodyScript); + if (innerState != EJS_STATE_ERR) { + ejsSyntaxError(ep, 0); + } + freeInputStruct(ep, bodyScript); + return EJS_STATE_ERR; + } + + if (flags & EJS_FLAGS_EXE) { + endScript = getInputStruct(ep); + ejsLexSaveInputState(ep, endScript); + + /* + * Save the method body between the starting and ending parse + * positions. Overwrite the trailing '}' with a null. + */ + len = endScript->scriptServp - bodyScript->scriptServp; + func->method.body = mprAlloc(ep, len + 1); + memcpy(func->method.body, bodyScript->scriptServp, len); + + if (len <= 0) { + func->method.body[0] = '\0'; + } else { + func->method.body[len - 1] = '\0'; + } + ejsLexFreeInputState(ep, bodyScript); + ejsLexFreeInputState(ep, endScript); + freeInputStruct(ep, endScript); + + /* + * If we are in an assignment, don't register the method name, rather + * return the method structure in the parser result. + */ + if (procName) { + currentObj = pickSpace(ep, 0, procName, flags | EJS_FLAGS_LOCAL); + pp = ejsSetProperty(ep, currentObj, procName, func); + if (pp == 0) { + ejsFreeVar(ep, func); + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + + if (currentObj->objectState->className && + strcmp(currentObj->objectState->className, procName) == 0) { + baseClass = currentObj->objectState->baseClass; + if (baseClass) { + if (strstr(func->method.body, "super(") != 0) { + funcProp->callsSuper = 1; + /* + * Define super() to point to the baseClass constructor + */ + vp = ejsGetPropertyAsVar(ep, baseClass, + baseClass->objectState->className); + if (vp) { + mprAssert(vp); + if (ejsSetProperty(ep, currentObj, "super", + vp) == 0) { + ejsFreeVar(ep, func); + ejsMemoryError(ep); + return EJS_STATE_ERR; + } + } + } + } + } + } + /* + * Always return the function. Try for all stmts to be expressions. + */ + /* MOB - rc */ + ejsWriteVar(ep, ep->result, func, EJS_SHALLOW_COPY); + } + freeInputStruct(ep, bodyScript); + + mprFree(procName); + ejsFreeVar(ep, func); + + return state; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseMethod { + EjsProc proc, *saveProc; + EjsVar *saveObj, *newObj; + int saveObjPerm, rc; + +} ParseMethod; + +/* + * Parse a method name and invoke the method. See parseFunction for + * function declarations. + */ + +static int parseMethod(Ejs *ep, int state, int flags, char *id) +{ + ParseMethod *sp; + + if ((sp = pushFrame(ep, sizeof(ParseMethod))) == 0) { + return EJS_STATE_ERR; + } + + /* + * Must save any current ep->proc value for the current stack frame + * to allow for recursive method calls. + */ + sp->saveProc = (ep->proc) ? ep->proc: 0; + + memset(&sp->proc, 0, sizeof(EjsProc)); + sp->proc.procName = mprStrdup(ep, id); + sp->proc.fn = &ep->currentProperty->var; + sp->proc.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + ep->proc = &sp->proc; + +#if BLD_DEBUG + if (strcmp(sp->proc.procName, "printv") == 0) { + flags |= EJS_FLAGS_TRACE_ARGS; + } +#endif + + if (flags & EJS_FLAGS_EXE) { + ejsClearVar(ep, ep->result); + } + + if (! (flags & EJS_FLAGS_NO_ARGS)) { + sp->saveObj = ep->currentObj; + sp->saveObjPerm = ejsMakeObjPermanent(sp->saveObj, 1); + sp->rc = ejsParse(ep, EJS_STATE_ARG_LIST, flags); + ejsMakeObjPermanent(sp->saveObj, sp->saveObjPerm); + if (sp->rc < 0) { + goto err; + } + ep->currentObj = sp->saveObj; + } + +#if BLD_DEBUG + flags &= ~EJS_FLAGS_TRACE_ARGS; +#endif + + /* + * Evaluate the method if required + */ + if (flags & EJS_FLAGS_EXE) { + if (flags & EJS_FLAGS_NEW) { + sp->newObj = ejsCreateObjUsingArgv(ep, ep->currentObj, + sp->proc.procName, sp->proc.args); + + if (sp->newObj == 0) { + state = EJS_STATE_ERR; + + } else { + mprAssert(! ejsObjIsCollectable(sp->newObj)); + + /* + * Return the newly created object as the result of the + * command. NOTE: newObj may not be an object! + */ + /* MOB - rc */ + ejsWriteVar(ep, ep->result, sp->newObj, EJS_SHALLOW_COPY); + if (ejsVarIsObject(sp->newObj)) { + ejsMakeObjLive(sp->newObj, 1); + mprAssert(ejsObjIsCollectable(sp->newObj)); + mprAssert(ejsBlockInUse(sp->newObj)); + } + ejsFreeVar(ep, sp->newObj); + } + + } else { + + if (evalMethod(ep, ep->currentObj, &sp->proc, flags) < 0) { + /* Methods must call ejsError to set exceptions */ + state = EJS_STATE_ERR; + } + } + } + + if (! (flags & EJS_FLAGS_NO_ARGS)) { + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + if (state != EJS_STATE_ERR) { + ejsSyntaxError(ep, 0); + } + state = EJS_STATE_ERR; + } + } + +done: + freeProc(ep, &sp->proc); + ep->proc = sp->saveProc; + + popFrame(ep, sizeof(ParseMethod)); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * 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, int *done) +{ + EjsVar *null; + int tid; + + mprFree(*id); + *id = mprStrdup(ep, ep->token); + + if (ep->currentObj == 0) { + /* First identifier segement */ + ep->currentObj = pickSpace(ep, state, *id, flags); + } + + tid = ejsLexGetToken(ep, state); + if (tid == EJS_TOK_ASSIGNMENT) { + flags |= EJS_FLAGS_LHS; + } + + /* + * Find the referenced variable and store it in currentProperty. + */ + if (flags & EJS_FLAGS_EXE) { + ep->currentProperty = searchSpacesForProperty(ep, state, + ep->currentObj, *id, flags); + + /* + * Handle properties that have been deleted inside an enumeration + */ + if (ep->currentProperty && ep->currentProperty->delayedDelete) { + ep->currentProperty = 0; + } + + if (ep->currentProperty && + ejsVarIsMethod(&ep->currentProperty->var) && + tid != EJS_TOK_LPAREN) { + if (ep->currentProperty->var.flags & EJS_GET_ACCESSOR) { + ejsLexPutbackToken(ep, tid, ep->token); + state = parseMethod(ep, state, flags | EJS_FLAGS_NO_ARGS, *id); + if (ep->flags & EJS_FLAGS_EXIT) { + state = EJS_STATE_RET; + } + if (state >= 0) { + ejsSetVarName(ep, ep->result, ep->currentProperty->name); + } + return state; + } + } + /* + * OPT. We should not have to do this always + */ + updateResult(ep, state, flags, ejsGetVarPtr(ep->currentProperty)); + } + + flags &= ~EJS_FLAGS_LHS; + + if (tid == EJS_TOK_LPAREN) { + if (ep->currentProperty == 0 && (flags & EJS_FLAGS_EXE)) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Method name not defined \"%s\"", *id); + return EJS_STATE_ERR; + } + ejsLexPutbackToken(ep, EJS_TOK_METHOD_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; + } + + if (flags & EJS_FLAGS_CLASS_DEC) { + if (tid == EJS_TOK_LBRACE || tid == EJS_TOK_EXTENDS) { + ejsLexPutbackToken(ep, tid, ep->token); + return state; + } + } + + if (flags & EJS_FLAGS_DELETE) { + if (tid == EJS_TOK_RBRACE) { + ejsLexPutbackToken(ep, tid, ep->token); + } + } + + /* + * 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, EJS_REFERENCE_ERROR, + "Variable already defined \"%s\"", *id); + return EJS_STATE_ERR; + } +#endif + /* + * Create or overwrite if it already exists + * Set newly declared variables to the null value. + */ + null = ejsCreateNullVar(ep); + ep->currentProperty = ejsSetPropertyAndFree(ep, ep->currentObj, + *id, null); + ejsClearVar(ep, ep->result); + + } else if (flags & EJS_FLAGS_FORIN) { + /* + * This allows "for (x" when x has not yet been defined + */ + if (ep->currentProperty == 0) { + /* MOB -- return code */ + ep->currentProperty = ejsCreateProperty(ep, + ep->currentObj, *id); + } + + } else if (ep->currentProperty == 0) { + + if (ep->currentObj && ((ep->currentObj == ep->global || + (ep->currentObj == ep->local)))) { + /* + * Test against currentObj and not currentObj->objectState + * as we must allow "i = global.x" and not allow + * "i = x" where x does not exist. + */ + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined variable \"%s\"", *id); + return EJS_STATE_ERR; + } + + if (flags & EJS_FLAGS_DELETE) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined variable \"%s\"", *id); + return EJS_STATE_ERR; + } + } + } + ejsLexPutbackToken(ep, tid, ep->token); + if (tid == EJS_TOK_RBRACKET || tid == EJS_TOK_COMMA || + tid == EJS_TOK_IN) { + *done = 1; + } + return state; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct ParseIf { + int ifResult, thenFlags, elseFlags, tid, rs; +} ParseIf; + +/* + * Parse an "if" statement + */ + +static int parseIf(Ejs *ep, int state, int flags, int *done) +{ + ParseIf *sp; + + if ((sp = pushFrame(ep, sizeof(ParseIf))) == 0) { + return EJS_STATE_ERR; + } + + if (state != EJS_STATE_STMT) { + goto err; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_LPAREN) { + goto err; + } + + /* + * Evaluate the entire condition list "(condition)" + */ + if (ejsParse(ep, EJS_STATE_COND, flags) < 0) { + goto err; + } + if (ejsLexGetToken(ep, state) != EJS_TOK_RPAREN) { + goto err; + } + + /* + * This is the "then" case. We need to always parse both cases and + * execute only the relevant case. + */ + sp->ifResult = ejsVarToBoolean(ep->result); + if (sp->ifResult) { + sp->thenFlags = flags; + sp->elseFlags = flags & ~EJS_FLAGS_EXE; + } else { + sp->thenFlags = flags & ~EJS_FLAGS_EXE; + sp->elseFlags = flags; + } + + /* + * Process the "then" case. + */ + if ((sp->rs = ejsParse(ep, EJS_STATE_STMT, sp->thenFlags)) < 0) { + if (! ep->gotException) { + state = sp->rs; + goto done; + } + } + + /* + * Check to see if there is an "else" case + */ + removeNewlines(ep, state); + sp->tid = ejsLexGetToken(ep, state); + if (sp->tid != EJS_TOK_ELSE) { + ejsLexPutbackToken(ep, sp->tid, ep->token); + *done = 1; + if (ep->gotException) { + goto err; + } + goto done; + } + + /* + * Process the "else" case. + */ + state = ejsParse(ep, EJS_STATE_STMT, sp->elseFlags); + +done: + *done = 1; + if (ep->gotException) { + state = EJS_STATE_ERR; + } + popFrame(ep, sizeof(ParseIf)); + return state; + + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Parse a postix "++" or "--" statement + */ + +static int parseInc(Ejs *ep, int state, int flags) +{ + EjsVar *one; + + if (! (flags & EJS_FLAGS_EXE)) { + return state; + } + + if (ep->currentProperty == 0) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined variable \"%s\"", ep->token); + return EJS_STATE_ERR; + } + one = ejsCreateIntegerVar(ep, 1); + if (evalExpr(ep, &ep->currentProperty->var, (int) *ep->token, one) < 0) { + ejsFreeVar(ep, one); + return EJS_STATE_ERR; + } + if (ejsWriteVar(ep, &ep->currentProperty->var, ep->result, + EJS_SHALLOW_COPY) < 0) { + ejsError(ep, EJS_IO_ERROR, "Can't write to variable"); + ejsFreeVar(ep, one); + return EJS_STATE_ERR; + } + ejsFreeVar(ep, one); + return state; +} + +/******************************************************************************/ +/* + * Evaluate a condition. Implements &&, ||, !. Returns with a boolean result + * in ep->result. Returns EJS_STATE_ERR on errors, zero if successful. + */ + +static int evalCond(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs) +{ + int l, r, lval; + + mprAssert(rel > 0); + + l = ejsVarToBoolean(lhs); + r = ejsVarToBoolean(rhs); + + switch (rel) { + case EJS_COND_AND: + lval = l && r; + break; + case EJS_COND_OR: + lval = l || r; + break; + default: + ejsError(ep, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + + /* MOB - rc */ + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; +} + + +/******************************************************************************/ +/* + * return true if this string is a valid number + */ + +static int stringIsNumber(const char *s) +{ + char *endptr = NULL; + + if (s == NULL || *s == 0) { + return 0; + } + /* MOB -- not ideal */ +#if BREW + /* MOB this should check all digits and not just the first. */ + /* Does not support floating point - easy */ + + if (isdigit(*s) || (*s == '-' && isdigit(s[1]))) { + return 1; + } +#else + strtod(s, &endptr); +#endif + if (endptr != NULL && *endptr == 0) { + return 1; + } + 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, EjsVar *lhs, int rel, EjsVar *rhs) +{ + EjsNum lval; + char *str; + 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. + * + * MOB -- should we do "valueOf" here also. + */ + if (lhs->type == EJS_TYPE_OBJECT && + (rhs->type != EJS_TYPE_OBJECT && + (rhs->type != EJS_TYPE_UNDEFINED && rhs->type != EJS_TYPE_NULL))) { + if (ejsVarIsNumber(rhs)) { + if (ejsRunMethod(ep, lhs, "toValue", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, lhs, ep->result, EJS_SHALLOW_COPY); + } else { + if (ejsRunMethod(ep, lhs, "toString", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, lhs, ep->result, EJS_SHALLOW_COPY); + } + } + + } else { + if (ejsRunMethod(ep, lhs, "toString", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, lhs, ep->result, EJS_SHALLOW_COPY); + } else { + if (ejsRunMethod(ep, lhs, "toValue", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, lhs, ep->result, EJS_SHALLOW_COPY); + } + } + } + /* Nothing more can be done */ + } + + if (rhs->type == EJS_TYPE_OBJECT && + (lhs->type != EJS_TYPE_OBJECT && + (lhs->type != EJS_TYPE_UNDEFINED && lhs->type != EJS_TYPE_NULL))) { + if (ejsVarIsNumber(lhs)) { + /* If LHS is number, then convert to a value first */ + if (ejsRunMethod(ep, rhs, "toValue", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, rhs, ep->result, EJS_SHALLOW_COPY); + } else { + if (ejsRunMethod(ep, rhs, "toString", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, rhs, ep->result, EJS_SHALLOW_COPY); + } + } + + } else { + /* If LHS is not a number, then convert to a string first */ + if (ejsRunMethod(ep, rhs, "toString", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, rhs, ep->result, EJS_SHALLOW_COPY); + + } else { + if (ejsRunMethod(ep, rhs, "toValue", 0) == 0) { + /* MOB - rc */ + ejsWriteVar(ep, rhs, ep->result, EJS_SHALLOW_COPY); + } + } + } + /* 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 == EJS_TYPE_UNDEFINED || + rhs->type == EJS_TYPE_UNDEFINED) { + return evalBoolExpr(ep, + lhs->type == EJS_TYPE_UNDEFINED, + rel, + rhs->type == EJS_TYPE_UNDEFINED); + } + + if (lhs->type == EJS_TYPE_NULL || rhs->type == EJS_TYPE_NULL) { + return evalBoolExpr(ep, + lhs->type == EJS_TYPE_NULL, + rel, + rhs->type == EJS_TYPE_NULL); + } + } + + /* + * 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 == EJS_TYPE_BOOL && rhs->type == EJS_TYPE_BOOL && + (rel != EJS_EXPR_EQ && rel != EJS_EXPR_NOTEQ && + rel != EJS_EXPR_BOOL_COMP)) { + ejsWriteVarAsNumber(ep, lhs, ejsVarToNumber(lhs)); + } + + /* + * 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 == EJS_TYPE_STRING) { + if (stringIsNumber(lhs->string)) { + ejsWriteVarAsNumber(ep, lhs, ejsVarToNumber(lhs)); + + /* Examine further below */ + + } else { + /* + * Convert the RHS to a string + * MOB rc + */ + str = ejsVarToString(ep, rhs); + ejsWriteVarAsString(ep, rhs, str); + } + +#if BLD_FEATURE_FLOATING_POINT + } else if (lhs->type == EJS_TYPE_FLOAT) { + /* + * Convert rhs to floating + */ + ejsWriteVarAsFloat(ep, rhs, ejsVarToFloat(rhs)); + +#endif +#if BLD_FEATURE_INT64 + } else if (lhs->type == EJS_TYPE_INT64) { + /* + * Convert the rhs to 64 bit + */ + ejsWriteVarAsInteger64(ep, rhs, ejsVarToInteger64(rhs)); +#endif + } else if (lhs->type == EJS_TYPE_BOOL || lhs->type == EJS_TYPE_INT) { + + if (rhs->type == EJS_TYPE_STRING) { + if (stringIsNumber(rhs->string)) { + ejsWriteVarAsNumber(ep, rhs, ejsVarToNumber(rhs)); + } else { + /* + * Convert to lhs to a string + */ + str = ejsVarToString(ep, lhs); + /* MOB -- rc */ + if (str) { + ejsWriteVarAsString(ep, lhs, str); + } + } + +#if BLD_FEATURE_FLOATING_POINT + } else if (rhs->type == EJS_TYPE_FLOAT) { + /* + * Convert lhs to floating + */ + ejsWriteVarAsFloat(ep, lhs, ejsVarToFloat(lhs)); +#endif + + } else { + /* + * Forcibly convert both operands to numbers + */ + ejsWriteVarAsNumber(ep, lhs, ejsVarToNumber(lhs)); + ejsWriteVarAsNumber(ep, rhs, ejsVarToNumber(rhs)); + } + } + } + + /* + * 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 == EJS_TYPE_UNDEFINED || lhs->type == EJS_TYPE_NULL) { + switch (rel) { + case EJS_EXPR_EQ: + lval = lhs->type == rhs->type; + break; + case EJS_EXPR_NOTEQ: + lval = lhs->type != rhs->type; + break; + case EJS_EXPR_BOOL_COMP: + lval = ! ejsVarToBoolean(rhs); + break; + default: + ejsWriteVar(ep, ep->result, rhs, EJS_SHALLOW_COPY); + return 0; + } + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; + } + + /* + * Types are the same here + */ + switch (lhs->type) { + default: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + /* Should be handled above */ + mprAssert(0); + return 0; + + case EJS_TYPE_STRING_CMETHOD: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_METHOD: + case EJS_TYPE_PTR: + ejsWriteVarAsBoolean(ep, ep->result, 0); + return 0; + + case EJS_TYPE_OBJECT: + rc = evalObjExpr(ep, lhs, rel, rhs); + break; + + case EJS_TYPE_BOOL: + rc = evalBoolExpr(ep, lhs->boolean, rel, rhs->boolean); + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + rc = evalFloatExpr(ep, lhs->floating, rel, rhs->floating); + break; +#endif + + case EJS_TYPE_INT: + rc = evalNumericExpr(ep, (EjsNum) lhs->integer, rel, + (EjsNum) rhs->integer); + break; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + rc = evalNumericExpr(ep, (EjsNum) lhs->integer64, rel, + (EjsNum) rhs->integer64); + break; +#endif + + case EJS_TYPE_STRING: + rc = evalStringExpr(ep, lhs, rel, rhs); + } + + /* MOB */ + if (lhs->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(lhs, 0); + mprAssert(lhs->objectState->alive == 0); + } + if (rhs->type == EJS_TYPE_OBJECT) { + ejsMakeObjLive(rhs, 0); + mprAssert(rhs->objectState->alive == 0); + } + + return rc; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Expressions with floating operands + */ + +static int evalFloatExpr(Ejs *ep, double l, int rel, double r) +{ + double lval; + int 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, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, lval != 0); + + } else { + ejsWriteVarAsFloat(ep, ep->result, lval); + } + return 0; +} + +#endif /* BLD_FEATURE_FLOATING_POINT */ +/******************************************************************************/ +/* + * Expressions with object operands + */ + +static int evalObjExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *rhs) +{ + int lval; + + switch (rel) { + case EJS_EXPR_EQ: + lval = lhs->objectState == rhs->objectState; + break; + case EJS_EXPR_NOTEQ: + lval = lhs->objectState != rhs->objectState; + break; + default: + ejsError(ep, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; +} + +/******************************************************************************/ +/* + * Expressions with boolean operands + */ + +static int evalBoolExpr(Ejs *ep, int l, int rel, int r) +{ + int 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, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; +} + +/******************************************************************************/ +/* + * Expressions with numeric operands + */ + +static int evalNumericExpr(Ejs *ep, EjsNum l, int rel, EjsNum r) +{ + EjsNum lval; + int 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, EJS_RANGE_ERROR, "Divide by zero"); + return -1; + } + break; + case EJS_EXPR_MOD: + if (r != 0) { + lval = l % r; + } else { + ejsError(ep, EJS_RANGE_ERROR, "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, EJS_SYNTAX_ERROR, "Bad operator %d", rel); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, lval != 0); + + } else { + ejsWriteVarAsNumber(ep, ep->result, lval); + } + return 0; +} + +/******************************************************************************/ +/* + * Expressions with string operands + */ + +static int evalStringExpr(Ejs *ep, EjsVar *lhs, int rel, EjsVar *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. + */ + ejsClearVar(ep, ep->result); + ejsStrcat(ep, ep->result, lhs); + ejsStrcat(ep, 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: + ejsSyntaxError(ep, "Bad operator"); + return -1; + } + + ejsWriteVarAsBoolean(ep, ep->result, lval); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate a method. obj is set to the current object if a method is being + * run. + */ + +static int evalMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, int flags) +{ + EjsProperty *pp; + EjsVar *saveThis, *prototype; + int saveThisPerm, rc, fid; + + mprAssert(ep); + + rc = 0; + fid = -1; + saveThis = 0; + saveThisPerm = 0; + prototype = proc->fn; + + if (prototype == 0) { + ejsError(ep, EJS_EVAL_ERROR, "Undefined method"); + return EJS_STATE_ERR; + } + + if (prototype->type == EJS_TYPE_OBJECT) { + prototype = ejsGetPropertyAsVar(ep, prototype, proc->procName); + } + + if (prototype) { + /* + * Create a new variable stack frame. ie. new local variables. + * Some C methods (eg. include) don't create a new local context. + */ + if (! (prototype->flags & EJS_NO_LOCAL)) { + fid = ejsOpenBlock(ep); + if (fid < 0) { + return EJS_STATE_ERR; + } + mprAssert(ejsBlockInUse(ep->local)); + + pp = ejsSetProperty(ep, ep->local, "this", obj); + ejsMakePropertyEnumerable(pp, 0); + + /* + * Optimization. Save "this" during this block. + */ + saveThis = ep->thisObject; + ep->thisObject = ejsGetVarPtr(pp); + saveThisPerm = ejsMakeObjPermanent(saveThis, 1); + } + + switch (prototype->type) { + default: + mprAssert(0); + break; + + case EJS_TYPE_STRING_CMETHOD: + rc = callStringCMethod(ep, obj, proc, prototype); + break; + + case EJS_TYPE_CMETHOD: + rc = callCMethod(ep, obj, proc, prototype); + break; + + case EJS_TYPE_METHOD: + rc = callMethod(ep, obj, proc, prototype); + break; + } + + if (fid >= 0) { + ejsMakeObjPermanent(saveThis, saveThisPerm); + ep->thisObject = saveThis; + mprAssert(ejsBlockInUse(ep->local)); + mprAssert(ejsBlockInUse(ep->thisObject)); + ejsCloseBlock(ep, fid); + } + } + + return rc; +} + +/******************************************************************************/ +/* + * Create a new object and call all required constructors. + * obj may be null in which case we look globally for className. + */ + +EjsVar *ejsCreateObjUsingArgvInternal(EJS_LOC_DEC(ep, loc), EjsVar *obj, + const char *className, MprArray *args) +{ + EjsVar *baseClass, *objectClass, *thisObj; + int rc; + + mprAssert(className && *className); + + /* + * Create a new object of the required class and pass it into the + * constructor as the "this" local variable. + */ + baseClass = ejsGetClass(ep, obj, className); + if (baseClass == 0) { + + if (obj && obj->objectState->className && + strcmp(obj->objectState->className, className) == 0) { + /* + * Handle case where we are calling the constructor inside + * the class. In this case, obj == baseClass. + */ + thisObj = ejsCreateSimpleObjUsingClassInt(EJS_LOC_PASS(ep, loc), + obj); + + } else { + + /* + * If the baseClass does not exist, try to create an Object + * We do this for compatibility with JS 1.5 style new Function. + * MOB -- but this masks an error if we really need className. + */ + objectClass = ejsGetClass(ep, 0, "Object"); + thisObj = ejsCreateSimpleObjUsingClassInt(EJS_LOC_PASS(ep, loc), + objectClass); + } + + } else { + thisObj = ejsCreateSimpleObjUsingClassInt(EJS_LOC_PASS(ep, loc), + baseClass); + } + + if (thisObj == 0) { + ejsMemoryError(ep); + return 0; + } + + /* + * Make the object permanent. While currently not alive, the constructor + * below may make the object alive. + */ + ejsMakeObjPermanent(thisObj, 1); + mprAssert(! ejsObjIsCollectable(thisObj)); + + rc = 0; + if (baseClass) { + if (! baseClass->objectState->noConstructor) { + rc = callConstructor(ep, thisObj, baseClass, args); + } + } else { + /* + * className is the function name when calling new on functions + */ + rc = ejsRunMethod(ep, thisObj, className, args); + } + + /* + * Constructor may change the type to a non-object. + * Function() does this. Ensure object is not collectable yet. + */ + if (ejsVarIsObject(thisObj)) { + ejsMakeObjPermanent(thisObj, 0); + ejsMakeObjLive(thisObj, 0); + } + + if (rc < 0) { + if (rc == MPR_ERR_NOT_FOUND) { + /* No constructor (default) */ + return thisObj; + } + if (! (ep->flags & EJS_FLAGS_EXIT)) { + if (! ep->gotException) { + ejsMemoryError(ep); + } + } + ejsFreeVar(ep, thisObj); + return 0; + } + + mprAssert(ejsBlockInUse(thisObj)); + + return thisObj; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct CallCons { + EjsVar *subClassConstructor, *subClass, *method; +} CallCons; + +/* + * Create a new object and call all required constructors. + */ + +static int callConstructor(Ejs *ep, EjsVar *thisObj, EjsVar *baseClass, + MprArray *args) +{ + CallCons *sp; + int state; + + if ((sp = pushFrame(ep, sizeof(CallCons))) == 0) { + return EJS_STATE_ERR; + } + + mprAssert(baseClass); + mprAssert(baseClass->objectState); + + state = 0; + + /* + * method will be null if there is no constructor for this class + */ + sp->method = ejsGetPropertyAsVar(ep, baseClass, + baseClass->objectState->className); + + if (sp->method == 0 || !ejsVarIsMethod(sp->method) || + !sp->method->callsSuper) { + /* + * Invoke base class constructors in reverse order (RECURSIVE) + */ + sp->subClass = baseClass->objectState->baseClass; + if (sp->subClass) { + + /* + * Note that the Object class does not have a constructor for + * speed. Construction for the base Object is done via + * ejsCreateObj above. The code below will invoke constructors + * in the right order (bottom up) via recursion. MOB -- need to + * scan for super() MOB -- Bug. Fails poorly if no constructor. + * Should allows this and invoke a default constructor. + */ + sp->subClassConstructor = ejsGetPropertyAsVar(ep, sp->subClass, + sp->subClass->objectState->className); + + if (sp->subClassConstructor) { + + if (callConstructor(ep, thisObj, sp->subClass, 0) < 0) { + if (! ep->gotException) { + ejsMemoryError(ep); + } + goto err; + } + } + } + } + + if (sp->method) { + /* + * Finally, invoke the constructor for this class itself. + */ + state = runMethod(ep, thisObj, sp->method, + baseClass->objectState->className, args); + } + +done: + popFrame(ep, sizeof(CallCons)); + return state; + +err: + state = EJS_STATE_ERR; + goto done; +} + +/******************************************************************************/ +/* + * Create a new object and call all required constructors using string args. + * MOB -- would be good to parse constructorArgs for "," and break into + * separate args. + * Returned object is not yet collectable. Will have alive bit cleared. + */ + +EjsVar *ejsCreateObj(Ejs *ep, EjsVar *obj, const char *className, + const char *constructorArgs) +{ + MprArray *args; + EjsVar *newp, *vp; + + args = mprCreateItemArray(ep, 0, 0); + if (args == 0) { + return 0; + } + + if (constructorArgs && *constructorArgs) { + vp = ejsCreateStringVarInternal(EJS_LOC_ARGS(ep), constructorArgs); + + if (mprAddItem(args, vp) < 0) { + mprFree(args); + return 0; + } + } + + newp = ejsCreateObjUsingArgv(ep, obj, className, args); + + ejsFreeMethodArgs(ep, args); + + mprAssert(! ejsObjIsCollectable(newp)); + mprAssert(ejsBlockInUse(newp)); + + return newp; +} + +/******************************************************************************/ + +static int callStringCMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, + EjsVar *prototype) +{ + EjsVar **argValues; + MprArray *actualArgs; + char **argBuf, *str; + int i, rc; + + actualArgs = proc->args; + argValues = (EjsVar**) actualArgs->items; + + if (actualArgs->length > 0) { + argBuf = mprAlloc(ep, actualArgs->length * sizeof(char*)); + for (i = 0; i < actualArgs->length; i++) { + str = ejsVarToString(ep, argValues[i]); + /* MOB rc */ + argBuf[i] = mprStrdup(ep, str); + } + } else { + argBuf = 0; + } + + /* + * Call the method depending on the various handle flags + */ + ep->userData = prototype->cMethodWithStrings.userData; + if (prototype->flags & EJS_ALT_HANDLE) { + /* + * Used by the AppWeb GaCompat module. The alt handle is set to the + * web server request struct + */ + rc = ((EjsAltStringCMethod) + prototype->cMethodWithStrings.fn) + (ep, ep->altHandle, obj, actualArgs->length, argBuf); + + } else if (prototype->flags & EJS_PRIMARY_HANDLE) { + /* + * Used by ESP. The primary handle is set to the esp struct + */ + rc = (prototype->cMethodWithStrings.fn)(ep->primaryHandle, + obj, actualArgs->length, argBuf); + + } else { + /* + * Used EJS for the standard procs + */ + rc = (prototype->cMethodWithStrings.fn)(ep, obj, actualArgs->length, + argBuf); + } + + if (actualArgs->length > 0) { + for (i = 0; i < actualArgs->length; i++) { + mprFree(argBuf[i]); + } + mprFree(argBuf); + } + ep->userData = 0; + + return rc; +} + +/******************************************************************************/ + +static int callCMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, EjsVar *prototype) +{ + EjsVar **argValues; + MprArray *actualArgs; + int rc; + + actualArgs = proc->args; + argValues = (EjsVar**) actualArgs->items; + + ep->userData = prototype->cMethod.userData; + + /* + * Call the method depending on the various handle flags + * Sometimes cMethod.fn is NULL if there is no constructor for + * an object. + */ + if (prototype->flags & EJS_ALT_HANDLE) { + /* + * Use by the GaCompat module. The alt handle is set to the + * web server request struct + */ + rc = ((EjsAltCMethod) prototype->cMethod.fn) + (ep, ep->altHandle, obj, actualArgs->length, argValues); + + } else if (prototype->flags & EJS_PRIMARY_HANDLE) { + /* + * Used by ESP. The primary handle is set to the esp struct + */ + rc = (prototype->cMethod.fn) + (ep->primaryHandle, obj, actualArgs->length, argValues); + + } else { + /* + * Used EJS for the standard procs + */ + rc = (prototype->cMethod.fn)(ep, obj, actualArgs->length, argValues); + } + + ep->userData = 0; + + return rc; +} + +/******************************************************************************/ +/* + * Local vars + */ + +typedef struct CallMethod { + MprArray *formalArgs, *actualArgs; + EjsVar *arguments, *callee, **argValues; + char **argNames, buf[16]; + int i, argumentsObj; +} CallMethod; + + +static int callMethod(Ejs *ep, EjsVar *obj, EjsProc *proc, EjsVar *prototype) +{ + CallMethod *sp; + int i; + + if ((sp = pushFrame(ep, sizeof(CallMethod))) == 0) { + return EJS_STATE_ERR; + } + + sp->arguments = 0; + sp->callee = 0; + + sp->actualArgs = proc->args; + sp->argValues = (EjsVar**) sp->actualArgs->items; + sp->formalArgs = prototype->method.args; + sp->argNames = (char**) sp->formalArgs->items; + + /* + * Only create arguments and callee if the function actually uses them + */ + sp->argumentsObj = 0; + if (strstr(prototype->method.body, "arguments") != 0) { + sp->argumentsObj++; + + /* + * Create the arguments and callee variables + * MOB -- should we make real arrays here ? YES + */ + sp->arguments = ejsCreateSimpleObj(ep, "Object"); + ejsSetVarName(ep, sp->arguments, "arguments"); + mprAssert(! ejsObjIsCollectable(sp->arguments)); + + sp->callee = ejsCreateSimpleObj(ep, "Object"); + ejsSetVarName(ep, sp->callee, "callee"); + mprAssert(! ejsObjIsCollectable(sp->callee)); + + /* + * Overwrite the length property + */ + ejsSetPropertyToInteger(ep, sp->arguments, "length", + sp->actualArgs->length); + ejsSetPropertyToInteger(ep, sp->callee, "length", + sp->formalArgs->length); + } + + /* + * Define all the agruments to be set to the actual parameters + */ + for (i = 0; i < sp->formalArgs->length; i++) { + if (i >= sp->actualArgs->length) { + /* MOB -- return code */ + ejsCreateProperty(ep, ep->local, sp->argNames[i]); + + } else { + /* MOB -- return code */ + ejsSetProperty(ep, ep->local, sp->argNames[i], sp->argValues[i]); + } + } + + if (sp->argumentsObj) { + for (i = 0; i < sp->actualArgs->length; i++) { + mprItoa(sp->buf, sizeof(sp->buf), i); + ejsSetProperty(ep, sp->arguments, sp->buf, sp->argValues[i]); + } + + ejsSetPropertyAndFree(ep, sp->arguments, "callee", sp->callee); + ejsSetPropertyAndFree(ep, ep->local, "arguments", sp->arguments); + } + + /* + * Actually run the method + */ + + i = ejsEvalScript(ep, prototype->method.body, 0); + + popFrame(ep, sizeof(CallMethod)); + return i; +} + +/******************************************************************************/ +/* + * Run a method. Obj is set to "this" object. MethodName must exist in it + * or in a sub class. + */ + +int ejsRunMethod(Ejs *ep, EjsVar *obj, const char *methodName, MprArray *args) +{ + EjsProperty *pp; + EjsProc proc, *saveProc; + int rc; + + mprAssert(obj); + mprAssert(methodName && *methodName); + + pp = ejsGetProperty(ep, obj, methodName); + if (pp == 0) { + /* MOB -- this should be all in some common accessor routine */ + pp = ejsGetProperty(ep, ep->local, methodName); + if (pp == 0) { + pp = ejsGetProperty(ep, ep->global, methodName); + if (pp == 0) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Undefined method \"%s\"", methodName); + return MPR_ERR_NOT_FOUND; + } + } + } + + saveProc = ep->proc; + ep->proc = &proc; + + memset(&proc, 0, sizeof(EjsProc)); + + ejsClearVar(ep, ep->result); + + /* MOB -- if closures are going to work, we need to have proc be an + Object and let the GC look after it */ + + proc.fn = &pp->var; + if (proc.fn == 0 || proc.fn->type == EJS_TYPE_UNDEFINED) { + ep->proc = saveProc; + return MPR_ERR_NOT_FOUND; + } + + proc.procName = mprStrdup(ep, methodName); + if (args == 0) { + proc.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + } else { + proc.args = args; + } + + rc = evalMethod(ep, obj, &proc, 0); + + if (args) { + proc.args = 0; + } + freeProc(ep, &proc); + + ep->proc = saveProc; + + return rc; +} + +/******************************************************************************/ +/* + * Run a method. Obj is set to "this" object. MethodName must exist in it + * or in a sub class. + */ + +int ejsRunMethodCmd(Ejs *ep, EjsVar *obj, const char *methodName, + const char *cmdFmt, ...) +{ + MprArray *args; + va_list cmdArgs; + char *buf, *arg, *cp; + int rc; + + mprAssert(methodName && *methodName); + mprAssert(cmdFmt && *cmdFmt); + + va_start(cmdArgs, cmdFmt); + mprAllocVsprintf(MPR_LOC_ARGS(ep), &buf, 0, cmdFmt, cmdArgs); + va_end(cmdArgs); + + args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + + for (arg = cp = buf; cp && *cp; cp++) { + if (*cp == ',') { + *cp = 0; + mprAddItem(args, ejsParseVar(ep, arg, 0)); + arg = cp + 1; + } + } + if (cp > arg) { + mprAddItem(args, ejsParseVar(ep, arg, 0)); + } + + rc = ejsRunMethod(ep, obj, methodName, args); + + ejsFreeMethodArgs(ep, args); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * Run a method. Obj is set to "this" object. + */ + +static int runMethod(Ejs *ep, EjsVar *thisObj, EjsVar *method, + const char *methodName, MprArray *args) +{ + EjsProc proc, *saveProc; + int rc; + + mprAssert(thisObj); + mprAssert(method); + + saveProc = ep->proc; + ep->proc = &proc; + + memset(&proc, 0, sizeof(EjsProc)); + + ejsClearVar(ep, ep->result); + + /* MOB -- if closures are going to work, we need to have proc be an + Object and let the GC look after it */ + + proc.fn = method; + if (proc.fn == 0 || proc.fn->type == EJS_TYPE_UNDEFINED) { + ep->proc = saveProc; + return MPR_ERR_NOT_FOUND; + } + + proc.procName = mprStrdup(ep, methodName); + if (args == 0) { + proc.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + } else { + proc.args = args; + } + + rc = evalMethod(ep, thisObj, &proc, 0); + + if (args) { + proc.args = 0; + } + freeProc(ep, &proc); + + ep->proc = saveProc; + + return rc; +} + +/******************************************************************************/ +/* + * Find which object contains the property given the current context. + * We call this when there is no explicit object and the object must be + * determined by the context. + */ + +static EjsVar *pickSpace(Ejs *ep, int state, const char *property, int flags) +{ + EjsVar *obj; + + mprAssert(ep); + mprAssert(property && *property); + + /* MOB - this is ugly and the logic is confused */ + + 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 this and finally global */ + + if (ejsGetSimpleProperty(ep, ep->local, property)) { + obj = ep->local; + + } else if (ep->thisObject && + findProperty(ep, ep->thisObject, property, flags)) { + obj = ep->thisObject; + + } else { +#if EJS_ECMA_STND + obj = ep->global; +#else + if (flags & EJS_FLAGS_EXE && + !findProperty(ep, ep->global, property, flags)) { + obj = ep->local; + } else { + obj = ep->global; + } +#endif + } + } + 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 method + * names only. Returns the property or NULL. + * MOB -- need to rework this API. + */ + +static EjsProperty *searchSpacesForProperty(Ejs *ep, int state, EjsVar *obj, + char *property, int flags) +{ + EjsProperty *pp; + + if (obj) { + return findProperty(ep, obj, property, flags); + } + + /* MOB -- really should have a search stack */ + + pp = findProperty(ep, ep->local, property, flags); + if (pp == 0 && state != EJS_STATE_DEC) { + + if (ep->thisObject) { + pp = findProperty(ep, ep->thisObject, property, flags); + } + if (pp == 0) { + pp = findProperty(ep, ep->global, property, flags); + } + } + return pp; +} + +/******************************************************************************/ +/* + * Search an object and its base classes to find an object given an object + * an a property name. If not an assignment (LHS), then follow base classes. + * Otherwise, just look in the specified object. + */ + +static EjsProperty *findProperty(Ejs *ep, EjsVar *op, const char *property, + int flags) +{ + /* MOB -- NEW. Remove when EXE fixes are in. */ + if (! (flags & EJS_FLAGS_EXE) && op->type == EJS_TYPE_UNDEFINED) { + return 0; + } + + if (flags & EJS_FLAGS_LHS) { + return ejsGetPropertyPtr(ejsGetSimpleProperty(ep, op, property)); + + } else { + /* + * Follow base classes + */ + return ejsGetPropertyPtr(ejsGetProperty(ep, op, property)); + } +} + +/******************************************************************************/ +/* + * Update result + */ + +static void updateResult(Ejs *ep, int state, int flags, EjsVar *vp) +{ + if (flags & EJS_FLAGS_EXE && state != EJS_STATE_DEC) { + ejsClearVar(ep, ep->result); + if (vp) { + ejsWriteVar(ep, ep->result, vp, EJS_SHALLOW_COPY); + ejsSetVarName(ep, ep->result, vp->propertyName); + } + } +} + +/******************************************************************************/ +/* + * Append to the pointer value + */ + +int ejsStrcat(Ejs *ep, EjsVar *dest, EjsVar *src) +{ + char *oldBuf, *buf, *str; + int oldLen, newLen, len; + + mprAssert(dest); + mprAssert(ejsVarIsString(src)); + + if (ejsVarIsValid(dest)) { + + if (! ejsVarIsString(dest)) { + /* Bad type for dest */ + return -1; + } + + if (! ejsVarIsString(src)) { + str = ejsVarToString(ep, src); + if (str == 0) { + return -1; + } + len = strlen(str); + + } else { + str = src->string; + len = src->length; + } + + oldBuf = dest->string; + oldLen = dest->length; + newLen = oldLen + len + 1; + + if (newLen < MPR_SLAB_STR_MAX) { + buf = oldBuf; + } else { + buf = mprRealloc(ep, oldBuf, newLen); + if (buf == 0) { + return -1; + } + dest->string = buf; + } + memcpy(&buf[oldLen], str, len); + dest->length += len; + + } else { + ejsWriteVarAsString(ep, dest, src->string); + } + return 0; +} + +/******************************************************************************/ +/* + * Exit the script + */ + +void ejsExit(Ejs *ep, int status) +{ + ep->scriptStatus = status; + ep->flags |= EJS_FLAGS_EXIT; +} + +/******************************************************************************/ +/* + * Free an argument list + */ + +static void freeProc(Ejs *ep, EjsProc *proc) +{ + if (proc->args) { + ejsFreeMethodArgs(ep, proc->args); + } + + if (proc->procName) { + mprFree(proc->procName); + proc->procName = NULL; + } +} + +/******************************************************************************/ + +void ejsFreeMethodArgs(Ejs *ep, MprArray *args) +{ + int i; + + for (i = args->length - 1; i >= 0; i--) { + ejsFreeVar(ep, args->items[i]); + mprRemoveItemByIndex(args, i); + } + mprFree(args); +} + +/******************************************************************************/ +/* + * This method 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); +} + +/******************************************************************************/ + +static int getNextNonSpaceToken(Ejs *ep, int state) +{ + int tid; + + do { + tid = ejsLexGetToken(ep, state); + } while (tid == EJS_TOK_NEWLINE); + return tid; +} + +/******************************************************************************/ + +int ejsGetFlags(Ejs *ep) +{ + return ep->flags; +} + +/******************************************************************************/ + +bool ejsIsExiting(Ejs *ep) +{ + return (ep->flags & EJS_FLAGS_EXIT) ? 1: 0; +} + +/******************************************************************************/ + +void ejsClearExiting(Ejs *ep) +{ + ep->flags &= ~EJS_FLAGS_EXIT; +} + +/******************************************************************************/ + +static EjsInput *getInputStruct(Ejs *ep) +{ + EjsInput *input; + + if (ep->inputList) { + input = ep->inputList; + ep->inputList = input->nextInput; + + } else { + input = mprAlloc(ep, sizeof(EjsInput)); + } + return input; +} + +/******************************************************************************/ + +static void freeInputStruct(Ejs *ep, EjsInput *input) +{ + input->nextInput = ep->inputList; + ep->inputList = input; +} + +/******************************************************************************/ + +static void *pushFrame(Ejs *ep, int size) +{ + /* + * Grow down stack + */ + ep->stkPtr -= size; + if (ep->stkPtr < ep->stack) { + mprError(ep, MPR_LOC, "Exceeded parse stack"); + return 0; + } + return ep->stkPtr; +} + +/******************************************************************************/ + +static void *popFrame(Ejs *ep, int size) +{ + ep->stkPtr += size; + if (ep->stkPtr > &ep->stack[EJS_MAX_STACK]) { + mprError(ep, MPR_LOC, "Over poped parse stack"); + return 0; + } + return ep->stkPtr; +} + +/******************************************************************************/ +#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-2.0/ejs/ejsVar.c b/source4/lib/appweb/ejs-2.0/ejs/ejsVar.c new file mode 100644 index 0000000000..5067215f03 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsVar.c @@ -0,0 +1,4033 @@ +/** + * @file ejsVar.c + * @brief Mbedthis Portable Runtime Universal Variable Type + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 methods. + * Properties are indexed by a character name. + */ + +/********************************** Includes **********************************/ + +#include "ejs.h" + +/***************************** Forward Declarations ***************************/ + +static EjsProperty *allocProperty(Ejs *ep, EjsVar *op, const char *property, + int propertyIndex, EjsProperty *last); +static EjsVar *copyVar(EJS_LOC_DEC(ep, loc), EjsVar *dest, + const EjsVar *src, EjsCopyDepth copyDepth); +static EjsObj *createObj(EJS_LOC_DEC(ep, loc)); +static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen); +static int hash(const char *property); +static void unlinkProperty(EjsObj *obj, EjsPropLink *propLink); +static void linkPropertyBefore(EjsObj *obj, EjsPropLink *at, + EjsPropLink *propLink); +static int sortAllProperties(Ejs *ep, EjsProperty *p1, + EjsProperty *p2, const char *propertyName, int order); +static int sortByProperty(Ejs *ep, EjsProperty *p1, EjsProperty *p2, + const char *propertyName, int order); +static int dupString(MPR_LOC_DEC(ctx, loc), uchar **dest, + const void *src, int nbytes); +#if UNUSED && KEEP +static void linkPropertyAfter(EjsObj *obj, EjsPropLink *at, + EjsPropLink *propLink); +#endif + +static EjsProperty *hashLookup(EjsObj *obj, const char *property, + int *propertyIndex, EjsProperty **hashTail); + +/******************************************************************************/ +/********************************** Var Routines ******************************/ +/******************************************************************************/ + +EjsType ejsGetVarType(EjsVar *vp) +{ + mprAssert(vp); + + return vp->type; +} + +/******************************************************************************/ + +void ejsFreeVar(Ejs *ep, EjsVar *vp) +{ + if (vp) { + ejsClearVar(ep, vp); + ejsFree(ep, vp, EJS_SLAB_VAR); + } +} + +/******************************************************************************/ +#if UNUSED +/* + * Clear the value by freeing any allocated data. This will release objects + * so that later garbage collection can reclaim storage if there are no other + * object references. + */ + +void ejsZeroVar(Ejs *ep, EjsVar *vp) +{ + vp->type = EJS_TYPE_UNDEFINED; + vp->objectState = 0; + vp->method.body = 0; + vp->method.args = 0; + vp->callsSuper = 0; + vp->ptr.destructor = 0; + vp->allocatedData = 0; +} + +#endif +/******************************************************************************/ +/* + * Clear the value by freeing any allocated data. This will release objects + * so that later garbage collection can reclaim storage if there are no other + * object references. + */ + +void ejsClearVar(Ejs *ep, EjsVar *vp) +{ + MprArray *argList; + int i; + + mprAssert(vp); + mprAssert(ep); + + if (! vp->allocatedData) { + vp->type = EJS_TYPE_UNDEFINED; + return; + } + if (vp->type == EJS_TYPE_UNDEFINED) { + return; + } + + switch (vp->type) { + default: + break; + + case EJS_TYPE_STRING: + mprFree(vp->string); + vp->string = 0; + break; + + case EJS_TYPE_OBJECT: + /* + * Set the "alive" bit so that the GC will cleanup if no + * other references. + */ + if (vp->objectState) { + vp->objectState->alive = 1; + } + vp->objectState = 0; + break; + + case EJS_TYPE_METHOD: + argList = vp->method.args; + /* + * MOB OPT -- should be able to do just one mprFree(vp->method.args) + */ + mprFree(vp->method.body); + if (argList) { + for (i = 0; i < argList->length; i++) { + mprFree(argList->items[i]); + } + mprFree(vp->method.args); + } + vp->method.args = 0; + vp->method.body = 0; + vp->callsSuper = 0; + break; + + case EJS_TYPE_PTR: + if (vp->ptr.destructor) { + (vp->ptr.destructor)(ep, vp); + } + break; + } + + vp->type = EJS_TYPE_UNDEFINED; + vp->allocatedData = 0; +} + +/******************************************************************************/ +/* + * Initialize an undefined value. + */ + +EjsVar *ejsCreateUndefinedVar(Ejs *ep) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_UNDEFINED; + } + return vp; +} + +/******************************************************************************/ +/* + * Initialize an null value. + */ + +EjsVar *ejsCreateNullVar(Ejs *ep) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_NULL; + } + return vp; +} + +/******************************************************************************/ + +EjsVar *ejsCreateBoolVar(Ejs *ep, int value) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_BOOL; + vp->boolean = value; + } + return vp; +} + +/******************************************************************************/ +/* + * Initialize a C method. + */ + +EjsVar *ejsCreateCMethodVar(Ejs *ep, EjsCMethod fn, void *userData, int flags) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_CMETHOD; + vp->cMethod.fn = fn; + vp->cMethod.userData = userData; + vp->flags = flags; + } + return vp; +} + +/******************************************************************************/ +/* + * Initialize a C method. + */ + +EjsVar *ejsCreateStringCMethodVar(Ejs *ep, EjsStringCMethod fn, + void *userData, int flags) +{ + EjsVar *vp; + + mprAssert(ep); + mprAssert(fn); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_STRING_CMETHOD; + vp->cMethodWithStrings.fn = fn; + vp->cMethodWithStrings.userData = userData; + vp->flags = flags; + } + return vp; +} + +/******************************************************************************/ +/* + * Initialize an opaque pointer. + */ + +EjsVar *ejsCreatePtrVar(Ejs *ep, void *ptr, EjsDestructor destructor) +{ + EjsVar *vp; + + mprAssert(ep); + mprAssert(ptr); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_PTR; + vp->ptr.userPtr = ptr; + vp->ptr.destructor = destructor; + vp->allocatedData = 1; + } + return vp; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Initialize a floating value. + */ + +EjsVar *ejsCreateFloatVar(Ejs *ep, double value) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_FLOAT; + vp->floating = value; + } + return vp; +} + +#endif +/******************************************************************************/ +/* + * Initialize an integer value. + */ + +EjsVar *ejsCreateIntegerVar(Ejs *ep, int value) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_INT; + vp->integer = value; + } + return vp; +} + +/******************************************************************************/ +#if BLD_FEATURE_INT64 +/* + * Initialize a 64-bit integer value. + */ + +EjsVar *ejsCreateInteger64Var(Ejs *ep, int64 value) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_INT64; + vp->integer64 = value; + } + return vp; +} + +#endif /* BLD_FEATURE_INT64 */ +/******************************************************************************/ +/* + * Initialize an number variable. Type is defined by configure. + */ + +EjsVar *ejsCreateNumberVar(Ejs *ep, EjsNum value) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + mprAssert(vp); + + if (vp) { + vp->type = BLD_FEATURE_NUM_TYPE_ID; +#if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64 + vp->integer64 = value; +#elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT + vp->float = value; +#else + vp->integer = value; +#endif + } + return vp; +} + +/******************************************************************************/ +/* + * Initialize a (bare) JavaScript method. args and body can be null. + */ + +EjsVar *ejsCreateMethodVar(Ejs *ep, const char *body, MprArray *args, int flags) +{ + EjsVar *vp; + int i; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + mprAssert(vp); + + if (vp == 0) { + return 0; + } + + vp->type = EJS_TYPE_METHOD; + + vp->allocatedData = 1; + + vp->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + if (vp->method.args == 0) { + mprAssert(vp->method.args); + ejsFreeVar(ep, vp); + return 0; + } + + if (args) { + for (i = 0; i < args->length; i++) { + mprAddItem(vp->method.args, + mprStrdup(vp->method.args, mprGetItem(args, i))); + } + } + vp->method.body = mprStrdup(vp->method.args, body); + + if (vp->method.body == 0) { + ejsFreeVar(ep, vp); + return 0; + } + vp->flags = flags; + + return vp; +} + +/******************************************************************************/ +/* + * Initialize an object variable. + */ + +EjsVar *ejsCreateObjVarInternal(EJS_LOC_DEC(ep, loc)) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_PASS(ep, loc)); + mprAssert(vp); + + if (vp) { + vp->type = EJS_TYPE_OBJECT; + vp->objectState = createObj(EJS_LOC_PASS(ep, loc)); + if (vp->objectState == 0) { + ejsFreeVar(ep, vp); + return 0; + } + vp->allocatedData = 1; + } + return vp; +} + +/******************************************************************************/ +/* + * Initialize a string value. + */ + +EjsVar *ejsCreateStringVarInternal(EJS_LOC_DEC(ep, loc), const char *value) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_PASS(ep, loc)); + mprAssert(vp); + + if (vp) { + vp->type = EJS_TYPE_STRING; + vp->string = mprStrdupInternal(EJS_LOC_PASS(ep, loc), value); + if (vp->string == 0) { + ejsFreeVar(ep, vp); + return 0; + } + vp->length = strlen(vp->string); + vp->allocatedData = 1; + } + return vp; +} + +/******************************************************************************/ +/* + * Initialize a binary string value. + */ + +EjsVar *ejsCreateBinaryStringVar(Ejs *ep, const uchar *value, int len) +{ + EjsVar *vp; + + mprAssert(ep); + + vp = ejsAllocVar(EJS_LOC_ARGS(ep)); + if (vp) { + vp->type = EJS_TYPE_STRING; + vp->length = dupString(MPR_LOC_ARGS(ep), &vp->ustring, value, len); + if (vp->length < 0) { + ejsFreeVar(ep, vp); + return 0; + } + vp->allocatedData = 1; + } + return vp; +} + +/******************************************************************************/ + +void ejsSetClassName(Ejs *ep, EjsVar *vp, const char *name) +{ + EjsObj *obj; + + if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) { + mprAssert(0); + return; + } + obj = vp->objectState; + + if (obj->className) { + mprFree(obj->className); + } + obj->className = mprStrdup(ep, name); +} + +/******************************************************************************/ + +EjsVar *ejsDupVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *src, + EjsCopyDepth copyDepth) +{ + EjsVar *vp; + + vp = ejsAllocVar(EJS_LOC_PASS(ep, loc)); + if (vp == 0) { + return 0; + } + + vp->type = EJS_TYPE_UNDEFINED; + + return copyVar(EJS_LOC_PASS(ep, loc), vp, src, copyDepth); +} + +/******************************************************************************/ +/* + * Set a var to a new value + */ + +EjsVar *ejsWriteVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest, + const EjsVar *src, EjsCopyDepth copyDepth) +{ + mprAssert(dest); + mprAssert(src); + + return copyVar(EJS_LOC_PASS(ep, loc), dest, src, copyDepth); +} + +/******************************************************************************/ +/* + * Set a var using a new bool value + */ + +EjsVar *ejsWriteVarAsBoolean(Ejs *ep, EjsVar *dest, int value) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_TYPE_BOOL; + dest->boolean = value; + dest->allocatedData = 0; + dest->flags = 0; + + return dest; +} + +/******************************************************************************/ +/* + * Set a var using a new C Method + */ + +EjsVar *ejsWriteVarAsCMethod(Ejs *ep, EjsVar *dest, EjsCMethod fn, + void *userData, int flags) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_TYPE_CMETHOD; + dest->cMethod.fn = fn; + dest->cMethod.userData = userData; + dest->flags = flags; + dest->allocatedData = 0; + + return dest; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Set a var using a new float value + */ + +EjsVar *ejsWriteVarAsFloat(Ejs *ep, EjsVar *dest, double value) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_TYPE_FLOAT; + dest->floating = value; + dest->allocatedData = 0; + dest->flags = 0; + + return dest; +} + +#endif +/******************************************************************************/ +/* + * Set a var using a new integer value + */ + +EjsVar *ejsWriteVarAsInteger(Ejs *ep, EjsVar *dest, int value) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_TYPE_INT; + dest->integer = value; + dest->allocatedData = 0; + dest->flags = 0; + + return dest; +} + +/******************************************************************************/ +#if BLD_FEATURE_INT64 +/* + * Set a var using a new integer value + */ + +EjsVar *ejsWriteVarAsInteger64(Ejs *ep, EjsVar *dest, int64 value) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_TYPE_INT64; + dest->integer64 = value; + dest->allocatedData = 0; + dest->flags = 0; + + return dest; +} + +#endif +/******************************************************************************/ +/* + * Set a var using a new Method + */ + +EjsVar *ejsWriteVarAsMethod(Ejs *ep, EjsVar *dest, const char *body, + MprArray *args) +{ + EjsVar **srcArgs, *arg; + int i; + + mprAssert(ep); + mprAssert(dest); + mprAssert(body); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + if (dest->method.args == 0) { + return 0; + } + + dest->type = EJS_TYPE_METHOD; + + if (args) { + srcArgs = (EjsVar**) args->items; + for (i = 0; i < args->length; i++) { + arg = ejsDupVar(ep, srcArgs[i], EJS_SHALLOW_COPY); + if (arg == 0) { + return 0; + } + if (mprAddItem(dest->method.args, arg) < 0) { + return 0; + } + } + } + + dest->method.body = mprStrdup(dest->method.args, body); + if (dest->method.body == 0) { + return 0; + } + + dest->allocatedData = 1; + dest->flags = 0; + + return dest; +} + +/******************************************************************************/ +/* + * Set a var to null + */ + +EjsVar *ejsWriteVarAsNull(Ejs *ep, EjsVar *dest) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_TYPE_NULL; + dest->allocatedData = 0; + dest->flags = 0; + + return dest; +} + +/******************************************************************************/ +/* + * Set a var using a new number value + */ + +EjsVar *ejsWriteVarAsNumber(Ejs *ep, EjsVar *dest, EjsNum value) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_NUM_VAR; + dest->ejsNumber = value; + dest->allocatedData = 0; + dest->flags = 0; + + return dest; +} + +/******************************************************************************/ +/* + * Set a var using a new C Method + */ + +EjsVar *ejsWriteVarAsStringCMethod(Ejs *ep, EjsVar *dest, EjsStringCMethod fn, + void *userData, int flags) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_TYPE_CMETHOD; + dest->cMethodWithStrings.fn = fn; + dest->cMethodWithStrings.userData = userData; + dest->flags = flags; + dest->allocatedData = 0; + + return dest; +} + +/******************************************************************************/ +/* + * Set a var using a new string value + */ + +EjsVar *ejsWriteVarAsStringInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest, + const char *value) +{ + mprAssert(dest); + mprAssert(value); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->string = mprStrdupInternal(EJS_LOC_PASS(ep, loc), value); + if (dest->string == 0) { + return 0; + } + + dest->length = strlen(dest->string); + + dest->type = EJS_TYPE_STRING; + dest->allocatedData = 1; + dest->flags = 0; + + return dest; +} + +/******************************************************************************/ +/* + * Set a var using a new string value + */ + +EjsVar *ejsWriteVarAsBinaryString(Ejs *ep, EjsVar *dest, const uchar *value, + int len) +{ + mprAssert(dest); + mprAssert(value); + + ejsClearVar(ep, dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->length = dupString(MPR_LOC_ARGS(ep), &dest->ustring, value, len); + if (dest->length < 0) { + return 0; + } + + dest->type = EJS_TYPE_STRING; + dest->allocatedData = 1; + dest->flags = 0; + + return dest; +} + +/******************************************************************************/ +/* + * Set a var to undefined + */ + +EjsVar *ejsWriteVarAsUndefined(Ejs *ep, EjsVar *dest) +{ + mprAssert(dest); + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->type = EJS_TYPE_UNDEFINED; + dest->allocatedData = 0; + dest->flags = 0; + + return dest; +} + +/******************************************************************************/ +/* + * Convert a value to a text based representation of its value + * If you provide a format, you MUST ensure you know the type. + * Caller must free the result. + */ + +char *ejsFormatVar(Ejs *ep, const char *fmt, EjsVar *vp) +{ + char *buf, *src, *value, *allocValue; + uchar *ubuf; + int len; + + buf = 0; + allocValue = 0; + value = 0; + + switch (vp->type) { + case EJS_TYPE_UNDEFINED: + value = "undefined"; + break; + + case EJS_TYPE_NULL: + value = "null"; + break; + + case EJS_TYPE_PTR: + if (fmt == NULL || *fmt == '\0') { + len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, + "[Opaque Pointer %p]", vp->ptr.userPtr); + } else { + len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->ptr); + } + goto done; + + case EJS_TYPE_BOOL: + value = (vp->boolean) ? "true" : "false"; + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + if (fmt == NULL || *fmt == '\0') { + fmt = "%f"; + } + len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->floating); + goto done; +#endif + + case EJS_TYPE_INT: + if (fmt == NULL || *fmt == '\0') { + fmt = "%d"; + } + mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->integer); + goto done; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + if (fmt == NULL || *fmt == '\0') { + fmt = "%Ld"; + } + mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, vp->integer64); + goto done; +#endif + + case EJS_TYPE_CMETHOD: + value = "[C Method]"; + break; + + case EJS_TYPE_STRING_CMETHOD: + value = "[C StringMethod]"; + break; + + case EJS_TYPE_METHOD: + value = ejsVarToString(ep, vp); + break; + + case EJS_TYPE_OBJECT: + value = ejsVarToString(ep, vp); + break; + + case EJS_TYPE_STRING: + src = vp->string; + mprAssert(src); + + if (fmt && *fmt && src) { + mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, src); + + } else if (src == NULL) { + buf = mprStrdup(ep, "null"); + + } else { + ubuf = (uchar*) buf; + if (dupString(MPR_LOC_ARGS(ep), &ubuf, src, vp->length) < 0) { + return mprStrdup(ep, ""); + } + buf = (char*) ubuf; + } + break; + + default: + mprAssert(0); + } + + if (fmt == NULL || *fmt == '\0') { + len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, "%s", value); + } else { + len = mprAllocSprintf(MPR_LOC_ARGS(ep), &buf, 0, fmt, value); + } + +done: + if (allocValue) { + mprFree(allocValue); + } + return buf; +} + +/******************************************************************************/ +/* + * Convert the variable to a boolean. Only for primitive types. + */ + +int ejsVarToBoolean(EjsVar *vp) +{ + mprAssert(vp); + + switch (vp->type) { + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_STRING_CMETHOD: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_METHOD: + return 0; + + case EJS_TYPE_OBJECT: + return (vp->objectState != NULL); + + case EJS_TYPE_PTR: + return (vp->ptr.userPtr != NULL); + + case EJS_TYPE_BOOL: + return vp->boolean; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + return (vp->floating != 0 && !ejsIsNan(vp->floating)); +#endif + + case EJS_TYPE_INT: + return (vp->integer != 0); + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + return (vp->integer64 != 0); +#endif + + case EJS_TYPE_STRING: + return (vp->length > 0); +#if UNUSED + if (strcmp(vp->string, "true") == 0 || + strcmp(vp->string, "TRUE") == 0) { + return 1; + + } else if (strcmp(vp->string, "false") == 0 || + strcmp(vp->string, "FALSE") == 0) { + return 0; + + } else { + return atoi(vp->string); + } +#endif + } + + /* Not reached */ + return 0; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Convert the variable to a floating point number. Only for primitive types. + */ + +double ejsVarToFloat(EjsVar *vp) +{ + mprAssert(vp); + + switch (vp->type) { + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_STRING_CMETHOD: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_METHOD: + case EJS_TYPE_OBJECT: + case EJS_TYPE_PTR: + return 0; + + case EJS_TYPE_BOOL: + return (vp->boolean) ? 1.0 : 0.0; + + case EJS_TYPE_FLOAT: + return vp->floating; + + case EJS_TYPE_INT: + return (double) vp->integer; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + return (double) vp->integer64; +#endif + + case EJS_TYPE_STRING: + if (vp->length == 0) { + return 0.0; + } else { + return atof(vp->string); + } + } + + /* Not reached */ + return 0; +} + +#endif +/******************************************************************************/ +/* + * Convert the variable to an Integer type. Only works for primitive types. + */ + +int ejsVarToInteger(EjsVar *vp) +{ + mprAssert(vp); + + switch (vp->type) { + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_STRING_CMETHOD: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_METHOD: + case EJS_TYPE_OBJECT: + return 0; + + case EJS_TYPE_BOOL: + return (vp->boolean) ? 1 : 0; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + if (ejsIsNan(vp->floating)) { + return 0; + } + return (int) vp->floating; +#endif + + case EJS_TYPE_INT: + return vp->integer; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + return (int) vp->integer64; +#endif + + case EJS_TYPE_STRING: + if (vp->length == 0) { + return 0; + } else { + return ejsParseInteger(vp->string); + } + } + + /* Not reached */ + return 0; +} + +/******************************************************************************/ +#if BLD_FEATURE_INT64 +/* + * Convert the variable to an Integer64 type. Only works for primitive types. + */ + +int64 ejsVarToInteger64(EjsVar *vp) +{ + mprAssert(vp); + + switch (vp->type) { + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_STRING_CMETHOD: + case EJS_TYPE_CMETHOD: + case EJS_TYPE_METHOD: + case EJS_TYPE_OBJECT: + case EJS_TYPE_PTR: + return 0; + + case EJS_TYPE_BOOL: + return (vp->boolean) ? 1 : 0; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + if (ejsIsNan(vp->floating)) { + return 0; + } + return (int64) vp->floating; +#endif + + case EJS_TYPE_INT: + return vp->integer; + + case EJS_TYPE_INT64: + return vp->integer64; + + case EJS_TYPE_STRING: + if (vp->length == 0) { + return 0; + } else { + return ejsParseInteger64(vp->string); + } + } + + /* Not reached */ + return 0; +} + +#endif /* BLD_FEATURE_INT64 */ +/******************************************************************************/ +/* + * Convert the variable to a number type. Only works for primitive types. + */ + +EjsNum ejsVarToNumber(EjsVar *vp) +{ +#if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64 + return ejsVarToInteger64(vp); +#elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT + return ejsVarToFloat(vp); +#else + return ejsVarToInteger(vp); +#endif +} + +/******************************************************************************/ +/* + * Convert a var to a string. Store the result in ep->castTemp. If allocated + * set ep->castAlloc to TRUE. Caller must NOT free the result. + */ + +char *ejsVarToString(Ejs *ep, EjsVar *vp) +{ + MprBuf *bp; + char numBuf[16]; + int len, i; + + if (ep->castAlloc) { + mprFree(ep->castTemp); + } + ep->castTemp = 0; + ep->castAlloc = 0; + + switch (vp->type) { + case EJS_TYPE_UNDEFINED: + ep->castTemp = "undefined"; + break; + + case EJS_TYPE_NULL: + ep->castTemp = "null"; + break; + + case EJS_TYPE_PTR: + len = mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0, + "[Opaque Pointer %p]", vp->ptr.userPtr); + ep->castAlloc = 1; + break; + + case EJS_TYPE_BOOL: + if (vp->boolean) { + ep->castTemp = "true"; + } else { + ep->castTemp = "false"; + } + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + len = mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0, + "%f", vp->floating); + ep->castAlloc = 1; + break; +#endif + + case EJS_TYPE_INT: + mprItoa(numBuf, sizeof(numBuf), vp->integer); + ep->castTemp = mprStrdup(ep, numBuf); + ep->castAlloc = 1; + break; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + mprAllocSprintf(MPR_LOC_ARGS(ep), &ep->castTemp, 0, + "%Ld", vp->integer64); + ep->castAlloc = 1; + break; +#endif + + case EJS_TYPE_CMETHOD: + ep->castTemp = "[C Method]"; + break; + + case EJS_TYPE_STRING_CMETHOD: + ep->castTemp = "[C StringMethod]"; + break; + + case EJS_TYPE_METHOD: + bp = mprCreateBuf(ep, 0, 0); + mprPutStringToBuf(bp, "function ("); + for (i = 0; i < vp->method.args->length; i++) { + mprPutStringToBuf(bp, vp->method.args->items[i]); + if ((i + 1) < vp->method.args->length) { + mprPutStringToBuf(bp, ", "); + } + } + mprPutStringToBuf(bp, ") {"); + mprPutStringToBuf(bp, vp->method.body); + mprPutStringToBuf(bp, "}"); + mprAddNullToBuf(bp); + ep->castTemp = mprStealBuf(ep, bp); + ep->castAlloc = 1; + mprFree(bp); + break; + + case EJS_TYPE_OBJECT: + if (ejsRunMethod(ep, vp, "toString", 0) < 0) { + return mprStrdup(ep, "[object Object]"); + } + ep->castTemp = mprStrdup(ep, ep->result->string); + ep->castAlloc = 1; + break; + + case EJS_TYPE_STRING: + if (vp->string == 0) { + ep->castTemp = "null"; + } else { + ep->castTemp = vp->string; + } + break; + + default: + mprAssert(0); + } + + mprAssert(ep->castTemp); + return ep->castTemp; +} + +/******************************************************************************/ + +char *ejsVarToStringEx(Ejs *ep, EjsVar *vp, bool *alloc) +{ + char *str; + + mprAssert(alloc); + + str = ejsVarToString(ep, vp); + *alloc = ep->castAlloc; + ep->castAlloc = 0; + ep->castTemp = 0; + return str; +} + +/******************************************************************************/ +/* + * Parse a string based on formatting instructions and intelligently + * create a variable. + * + * Float format: [+|-]DIGITS][DIGITS][(e|E)[+|-]DIGITS] + */ + +EjsVar *ejsParseVar(Ejs *ep, const char *buf, EjsType preferredType) +{ + EjsType type; + const char *cp; + int isHex; + + mprAssert(buf); + + type = preferredType; + + if (preferredType == EJS_TYPE_UNDEFINED) { + isHex = 0; + if (*buf == '-' || *buf == '+') { + type = EJS_NUM_VAR; + + } else if (!isdigit((int) *buf)) { + if (strcmp(buf, "true") == 0 || strcmp(buf, "false") == 0) { + type = EJS_TYPE_BOOL; + } else { + type = EJS_TYPE_STRING; + } + + } else if (isdigit((int) *buf)) { + type = EJS_NUM_VAR; + cp = buf; + if (*cp && tolower(cp[1]) == 'x') { + cp = &cp[2]; + isHex = 1; + for (cp = buf; *cp; cp++) { + if (! isxdigit((int) *cp)) { + break; + } + } + } else { +#if BLD_FEATURE_FLOATING_POINT + /* Could be integer or float */ + for (cp = buf; *cp; cp++) { + if (! isdigit((int) *cp)) { + int c = tolower(*cp); + if (c == '.' || c == 'e' || c == 'f') { + type = EJS_TYPE_FLOAT; + break; + } + } + } +#endif + } + } + } + + switch (type) { + case EJS_TYPE_OBJECT: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + case EJS_TYPE_PTR: + default: + break; + + case EJS_TYPE_BOOL: + return ejsCreateBoolVar(ep, ejsParseBoolean(buf)); + + case EJS_TYPE_INT: + return ejsCreateIntegerVar(ep, ejsParseInteger(buf)); + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + return ejsCreateInteger64Var(ep, ejsParseInteger64(buf)); +#endif + + case EJS_TYPE_STRING: + if (strcmp(buf, "null") == 0) { + return ejsCreateNullVar(ep); + + } else if (strcmp(buf, "undefined") == 0) { + return ejsCreateUndefinedVar(ep); + } + + return ejsCreateStringVar(ep, buf); + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + return ejsCreateFloatVar(ep, atof(buf)); +#endif + + } + return ejsCreateUndefinedVar(ep); +} + +/******************************************************************************/ +/* + * Convert the variable to a number type. Only works for primitive types. + */ + +bool ejsParseBoolean(const char *s) +{ + if (s == 0 || *s == '\0') { + return 0; + } + if (strcmp(s, "false") == 0 || strcmp(s, "FALSE") == 0) { + return 0; + } + return 1; +} + +/******************************************************************************/ +/* + * Convert the variable to a number type. Only works for primitive types. + */ + +EjsNum ejsParseNumber(const char *s) +{ +#if BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_INT64 + return ejsParseInteger64(s); +#elif BLD_FEATURE_NUM_TYPE_ID == EJS_TYPE_FLOAT + return ejsParseFloat(s); +#else + return ejsParseInteger(s); +#endif +} + +/******************************************************************************/ +#if BLD_FEATURE_INT64 +/* + * Convert the string buffer to an Integer64. + */ + +int64 ejsParseInteger64(const char *str) +{ + const char *cp; + int64 num64; + int radix, c, negative; + + mprAssert(str); + + cp = str; + num64 = 0; + negative = 0; + + if (*cp == '-') { + cp++; + negative = 1; + } else if (*cp == '+') { + cp++; + } + + /* + * Parse a number. Observe hex and octal prefixes (0x, 0) + */ + if (*cp != '0') { + /* + * Normal numbers (Radix 10) + */ + while (isdigit((int) *cp)) { + num64 = (*cp - '0') + (num64 * 10); + cp++; + } + } else { + cp++; + if (tolower(*cp) == 'x') { + cp++; + radix = 16; + while (*cp) { + c = tolower(*cp); + if (isdigit(c)) { + num64 = (c - '0') + (num64 * radix); + } else if (c >= 'a' && c <= 'f') { + num64 = (c - 'a' + 10) + (num64 * radix); + } else { + break; + } + cp++; + } + + } else{ + radix = 8; + while (*cp) { + c = tolower(*cp); + if (isdigit(c) && c < '8') { + num64 = (c - '0') + (num64 * radix); + } else { + break; + } + cp++; + } + } + } + + if (negative) { + return 0 - num64; + } + return num64; +} + +#endif /* BLD_FEATURE_INT64 */ +/******************************************************************************/ +/* + * Convert the string buffer to an Integer. + */ + +int ejsParseInteger(const char *str) +{ + const char *cp; + int num; + int radix, c, negative; + + mprAssert(str); + + cp = str; + num = 0; + negative = 0; + + if (*cp == '-') { + cp++; + negative = 1; + } else if (*cp == '+') { + cp++; + } + + /* + * Parse a number. Observe hex and octal prefixes (0x, 0) + */ + if (*cp != '0') { + /* + * Normal numbers (Radix 10) + */ + while (isdigit((int) *cp)) { + num = (*cp - '0') + (num * 10); + cp++; + } + } else { + cp++; + if (tolower(*cp) == 'x') { + cp++; + radix = 16; + while (*cp) { + c = tolower(*cp); + if (isdigit(c)) { + num = (c - '0') + (num * radix); + } else if (c >= 'a' && c <= 'f') { + num = (c - 'a' + 10) + (num * radix); + } else { + break; + } + cp++; + } + + } else{ + radix = 8; + while (*cp) { + c = tolower(*cp); + if (isdigit(c) && c < '8') { + num = (c - '0') + (num * radix); + } else { + break; + } + cp++; + } + } + } + + if (negative) { + return 0 - num; + } + return num; +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Convert the string buffer to an Floating. + */ + +double ejsParseFloat(const char *str) +{ + return atof(str); +} + +/******************************************************************************/ + +int ejsIsNan(double f) +{ +#if WIN + return _isnan(f); +#elif VXWORKS + /* FUTURE */ + return (0); +#else + return (f == FP_NAN); +#endif +} +/******************************************************************************/ + +int ejsIsInfinite(double f) +{ +#if WIN + return !_finite(f); +#elif VXWORKS + /* FUTURE */ + return (0); +#else + return (f == FP_INFINITE); +#endif +} + +#endif /* BLD_FEATURE_FLOATING_POINT */ + +/******************************************************************************/ +/* + * Single point of control for all assignment to properties. + * + * 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 EjsVar *copyVar(EJS_LOC_DEC(ep, loc), EjsVar *dest, const EjsVar *src, + EjsCopyDepth copyDepth) +{ + Ejs *ejsContext; + EjsObj *srcObj; + EjsProperty *destp; + const char **srcArgs; + char *str; + int i; + + mprAssert(dest); + mprAssert(src); + + if (dest == src) { + return dest; + } + + if (dest->type != EJS_TYPE_UNDEFINED) { + ejsClearVar(ep, dest); + } + + dest->allocatedData = 0; + + switch (src->type) { + default: + case EJS_TYPE_UNDEFINED: + case EJS_TYPE_NULL: + break; + + case EJS_TYPE_BOOL: + dest->boolean = src->boolean; + break; + + case EJS_TYPE_PTR: + dest->ptr = src->ptr; + if (dest->ptr.destructor) { + dest->allocatedData = 1; + } + break; + + case EJS_TYPE_STRING_CMETHOD: + dest->cMethodWithStrings = src->cMethodWithStrings; + break; + + case EJS_TYPE_CMETHOD: + dest->cMethod = src->cMethod; + break; + +#if BLD_FEATURE_FLOATING_POINT + case EJS_TYPE_FLOAT: + dest->floating = src->floating; + break; +#endif + + case EJS_TYPE_INT: + dest->integer = src->integer; + break; + +#if BLD_FEATURE_INT64 + case EJS_TYPE_INT64: + dest->integer64 = src->integer64; + break; +#endif + + case EJS_TYPE_OBJECT: + if (copyDepth == EJS_SHALLOW_COPY) { + + /* + * If doing a shallow copy and the src object is from the same + * interpreter, or we are copying from the master interpreter, or + * we are using a shared slab, then we can do a shallow copy. + * Otherwise, we must do a deep copy. + */ + srcObj = src->objectState; + if (srcObj->ejs == ep || srcObj->ejs == ep->service->master || + (ep->flags & EJS_FLAGS_SHARED_SLAB)) { + dest->objectState = src->objectState; + dest->allocatedData = 1; + break; + } + } + + /* + * Doing a deep or recursive deep. Can get here if doing a shallow + * copy and the object is from another non-master interpeter and not + * using a shared slab. + * + * We must make sure the data is allocated using the right memory + * context. It must be the same as the destination parent object. + * Otherwise, when we free the property memory, the parent may + * have a dangling pointer. + */ + if (dest->isProperty) { + destp = ejsGetPropertyPtr(dest); + if (destp->parentObj == 0) { + ejsContext = ep; + + } else { + mprAssert(destp->parentObj); + ejsContext = destp->parentObj->ejs; + mprAssert(ejsContext); + } + + } else { + ejsContext = ep; + } + + dest->objectState = createObj(EJS_LOC_PASS(ejsContext, loc)); + if (dest->objectState == 0) { + /* Memory Error */ + return 0; + } + + dest->objectState->baseClass = src->objectState->baseClass; + dest->objectState->methods = src->objectState->methods; + dest->objectState->noConstructor = src->objectState->noConstructor; + dest->objectState->objName = + mprStrdup(ejsContext, src->objectState->objName); + + if (dest->objectState->objName == 0) { + return 0; + } + + if (ejsCopyProperties(ep, dest, src, copyDepth) == 0) { + return 0; + } + dest->allocatedData = 1; + break; + + case EJS_TYPE_METHOD: + dest->method.args = mprCreateItemArray(ep, EJS_INC_ARGS, + EJS_MAX_ARGS); + if (dest->method.args == 0) { + return 0; + } + dest->allocatedData = 1; + if (src->method.args) { + srcArgs = (const char**) src->method.args->items; + for (i = 0; i < src->method.args->length; i++) { + str = mprStrdupInternal(EJS_LOC_PASS(dest->method.args, + loc), srcArgs[i]); + if (str == 0) { + mprFree(dest->method.args); + dest->method.args = 0; + return 0; + } + if (mprAddItem(dest->method.args, str) < 0) { + mprFree(dest->method.args); + dest->method.args = 0; + return 0; + } + } + } + dest->method.body = mprStrdup(dest->method.args, src->method.body); + if (dest->method.body == 0) { + mprFree(dest->method.args); + dest->method.args = 0; + return 0; + } + dest->callsSuper = src->callsSuper; + break; + + case EJS_TYPE_STRING: + dest->length = src->length; + if (src->string) { + /* Shallow, deep or recursive deep */ + dest->length = dupString(MPR_LOC_PASS(ep, loc), &dest->ustring, + src->ustring, src->length); + if (dest->length < 0) { + return 0; + } + dest->allocatedData = 1; + + } else { + dest->string = src->string; + dest->allocatedData = 0; + } + break; + } + + dest->type = src->type; + dest->flags = src->flags; + dest->isArray = src->isArray; + + return dest; +} + +/******************************************************************************/ +/* + * Copy all properies in an object. Must preserve property order + */ + +EjsVar *ejsCopyProperties(Ejs *ep, EjsVar *dest, const EjsVar *src, + EjsCopyDepth copyDepth) +{ + EjsProperty *srcProp, *destProp, *last, *next; + int propertyIndex; + + srcProp = ejsGetFirstProperty(src, EJS_ENUM_ALL); + while (srcProp) { + next = ejsGetNextProperty(srcProp, EJS_ENUM_ALL); + if (srcProp->visited) { + srcProp = next; + continue; + } + + /* + * This finds the last variable in the hash chain + * FUTURE OPT. This is slow. If used double link, we could locate the + * tail more easily. + */ + destProp = hashLookup(dest->objectState, srcProp->name, + &propertyIndex, &last); + mprAssert(destProp == 0); + + destProp = allocProperty(ep, dest, srcProp->name, propertyIndex, last); + if (destProp == 0) { + mprAssert(destProp); + return 0; + } + + /* + * Recursively copy the object. If DEEP_COPY, then we + * will do a shallow copy of the object contents. If + * RECURSIVE_DEEP, then we do a deep copy at all levels. + */ + srcProp->visited = 1; + + if (copyVar(EJS_LOC_ARGS(ep), ejsGetVarPtr(destProp), + ejsGetVarPtr(srcProp), + (copyDepth == EJS_DEEP_COPY) ? EJS_SHALLOW_COPY : copyDepth) + == 0) { + return 0; + } + srcProp->visited = 0; + + srcProp = next; + } + return dest; +} + +/******************************************************************************/ +/********************************** Properties ********************************/ +/******************************************************************************/ +/* + * Create a property in an object and return a pointer to it. If the property + * already exists then just return a pointer to it (no error). + * To test for existance of a property, use GetProperty + */ + +static EjsProperty *hashLookup(EjsObj *obj, const char *property, + int *propertyIndex, EjsProperty **hashTail) +{ + EjsProperty *prop, *last; + int index; + + mprAssert(obj); + mprAssert(property); + + if (obj == 0 || property == 0 || *property == '\0') { + mprAssert(0); + return 0; + } + + /* + * Find the property in the hash chain if it exists + */ + index = hash(property); + prop = obj->propertyHash[index]; + for (last = 0; prop != 0; last = prop, prop = prop->hashNext) { + if (prop->name[0] == property[0] && + strcmp(prop->name, property) == 0) { + break; + } + } + if (propertyIndex) { + *propertyIndex = index; + } + if (hashTail) { + *hashTail = last; + } + + return prop; +} + +/******************************************************************************/ +/* + * Create a property in an object and return a pointer to it. If the property + * already exists then just return a pointer to it (no error). If the property + * does not exist, create an undefined variable. To test for existance of a + * property, use GetProperty. + */ + +EjsProperty *ejsCreateSimpleProperty(Ejs *ep, EjsVar *op, const char *property) +{ + EjsProperty *prop, *last; + int propertyIndex; + + if (op == 0 || op->type != EJS_TYPE_OBJECT || property == 0 || + *property == '\0') { + mprAssert(0); + return 0; + } + + /* + * Find the property in the hash chain if it exists + */ + prop = hashLookup(op->objectState, property, &propertyIndex, &last); + + if (prop == 0) { + /* + * Create a new property + */ + prop = allocProperty(ep, op, property, propertyIndex, last); + if (prop == 0) { + mprAssert(prop == 0); + return 0; + } + } + return prop; +} + +/******************************************************************************/ +/* + * Create a property in an object and return a pointer to it. If the property + * already exists then just return a pointer to it (no error). + * To test for existance of a property, use GetProperty + */ + +EjsProperty *ejsCreateSimpleNonUniqueProperty(Ejs *ep, EjsVar *op, + const char *property) +{ + EjsProperty *prop, *last; + int propertyIndex; + + if (op == 0 || op->type != EJS_TYPE_OBJECT || property == 0 || + *property == '\0') { + mprAssert(0); + return 0; + } + + /* + * Find end of chain + */ + propertyIndex = hash(property); + prop = op->objectState->propertyHash[propertyIndex]; + for (last = 0; prop != 0; last = prop, prop = prop->hashNext) { + ; + } + + return allocProperty(ep, op, property, propertyIndex, last); +} + +/******************************************************************************/ +/* + * Find a property in an object and return a pointer to it. + * This does NOT traverse base classes. + */ + +EjsProperty *ejsGetSimpleProperty(Ejs *ep, EjsVar *op, const char *property) +{ + mprAssert(op); + mprAssert(op->type == EJS_TYPE_OBJECT); + mprAssert(property && *property); + + /* + * This is an internal API. It has very little checking. + */ + return hashLookup(op->objectState, property, 0, 0); +} + +/******************************************************************************/ + +/* + * NOTE: There is no ejsSetSimpleProperty as all the ejsSetProperty routines + * operate only on the instance and don't follow base classes. ie. there is + * no simple version required. However, there is a ejsSetBaseProperty routine + * that will follow base classes and is used to set static properties in base + * classes + */ + +/******************************************************************************/ +/******************************* Property Access ******************************/ +/******************************************************************************/ +/* + * The property get routines follow base classes and utilize the propery + * method access routines. The property set routines do not follow base + * classes. The property ejsSetBase... routines do follow base classes. + */ + +/* + * Find a property in an object and return a pointer to it. + * This follows base classes. + */ + +EjsProperty *ejsGetProperty(Ejs *ep, EjsVar *op, const char *property) +{ + EjsVar *vp, *newOp; + int maxBaseClasses = 50; + + do { + if (op->type != EJS_TYPE_OBJECT) { + mprAssert(op->type == EJS_TYPE_OBJECT); + return 0; + } + mprAssert(op->objectState); + + vp = ejsGetPropertyMethod(ep, op, property); + if (vp != 0) { + /* + * Found + */ + break; + } + + newOp = op->objectState->baseClass; + if (newOp == 0) { + if (op->objectState != ep->objectClass->objectState) { + newOp = ep->objectClass; + } + } + op = newOp; + + /* + * A little bit of sanity checking + */ + if (--maxBaseClasses <= 0) { + mprAssert(maxBaseClasses > 0); + break; + } + + } while (op); + + return ejsGetPropertyPtr(vp); +} + +/******************************************************************************/ +/* + * Get the property's variable. Optionally create if it does not exist. + */ + +EjsVar *ejsGetPropertyAsVar(Ejs *ep, EjsVar *vp, const char *property) +{ + return ejsGetVarPtr(ejsGetProperty(ep, vp, property)); +} + +/******************************************************************************/ +/* + * Get the property's value as a binary string. + */ + +const uchar *ejsGetPropertyAsBinaryString(Ejs *ep, EjsVar *obj, + const char *property, int *length) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property)); + if (vp == 0 || ejsVarIsUndefined(vp)) { + return 0; + } + + if (vp->type == EJS_TYPE_STRING) { + if (length) { + *length = vp->length; + } + return vp->ustring; + } + return 0; +} + +/******************************************************************************/ +/* + * Get the property's value as a string. + */ + +const char *ejsGetPropertyAsString(Ejs *ep, EjsVar *obj, const char *property) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property)); + if (vp == 0 || ejsVarIsUndefined(vp)) { + return 0; + } + + if (vp->type == EJS_TYPE_STRING) { + return vp->string; + } + return 0; +} + +/******************************************************************************/ +/* + * Get the property's value as a number. + */ + +BLD_FEATURE_NUM_TYPE ejsGetPropertyAsNumber(Ejs *ep, EjsVar *obj, + const char *property) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property)); + if (vp == 0 || ejsVarIsUndefined(vp)) { + return 0; + } + + return ejsVarToNumber(vp); +} + +/******************************************************************************/ +/* + * Get the property's value as a integer. + */ + +int ejsGetPropertyAsInteger(Ejs *ep, EjsVar *obj, const char *property) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property)); + if (vp == 0 || ejsVarIsUndefined(vp)) { + return 0; + } + + return ejsVarToInteger(vp); +} + +/******************************************************************************/ +/* + * Get the property's value as a boolean. + */ + +bool ejsGetPropertyAsBoolean(Ejs *ep, EjsVar *obj, const char *property) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property)); + if (vp == 0 || ejsVarIsUndefined(vp)) { + return 0; + } + + return ejsVarToBoolean(vp); +} + +/******************************************************************************/ +/* + * Get the property's value as a pointer. + */ + +void *ejsGetPropertyAsPtr(Ejs *ep, EjsVar *obj, const char *property) +{ + EjsVar *vp; + + vp = ejsGetVarPtr(ejsGetProperty(ep, obj, property)); + if (vp == 0 || ejsVarIsUndefined(vp)) { + return 0; + } + if (vp->type == EJS_TYPE_PTR) { + return vp->ptr.userPtr; + } + return 0; +} + +/******************************************************************************/ +/* + * Create a property in the object. This will override any base class + * properties. + * + * MOB -- need to spell out the difference between ejsSetProperty and + * ejsCreateProperty. + */ + +EjsProperty *ejsCreateProperty(Ejs *ep, EjsVar *obj, const char *property) +{ + EjsVar *vp; + + vp = ejsCreatePropertyMethod(ep, obj, property); + return ejsGetPropertyPtr(vp); +} + +/******************************************************************************/ +/* + * Set a property's variable value. Create the property if it does not exist. + * This routine DOES follow base classes. + */ + +EjsProperty *ejsSetBaseProperty(Ejs *ep, EjsVar *op, const char *property, + const EjsVar *value) +{ + EjsVar *vp, *newOp; + int maxBaseClasses = 50; + + do { + if (op->type != EJS_TYPE_OBJECT) { + mprAssert(op->type == EJS_TYPE_OBJECT); + return 0; + } + mprAssert(op->objectState); + + vp = ejsGetPropertyMethod(ep, op, property); + if (vp != 0) { + /* + * Found + */ + vp = ejsSetPropertyMethod(ep, op, property, value); + break; + } + + newOp = op->objectState->baseClass; + if (newOp == 0) { + if (op->objectState != ep->objectClass->objectState) { + newOp = ep->objectClass; + } + } + op = newOp; + + /* + * A little bit of sanity checking + */ + if (--maxBaseClasses <= 0) { + mprAssert(maxBaseClasses > 0); + break; + } + + } while (op); + + return ejsGetPropertyPtr(vp); +} + +/******************************************************************************/ +/* + * Set a property's variable value. Create the property if it does not exist. + * This does NOT follow base classes. Okay when updating instance properties, + * but not for class (static) properties. This does a shallow copy which + * will copy references. + */ + +EjsProperty *ejsSetProperty(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value) +{ + EjsVar *vp; + + vp = ejsSetPropertyMethod(ep, obj, property, value); + + return ejsGetPropertyPtr(vp); +} + +/******************************************************************************/ +/* + * Set a property's variable value by assigning the given value. The caller + * must NOT free value as it is assigned directly into the property's value. + */ + +EjsProperty *ejsSetPropertyAndFree(Ejs *ep, EjsVar *obj, + const char *property, EjsVar *value) +{ + EjsVar *vp; + + vp = ejsSetPropertyMethod(ep, obj, property, value); + + ejsFree(ep, value, EJS_SLAB_VAR); + + return ejsGetPropertyPtr(vp); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToCMethod(Ejs *ep, EjsVar *vp, const char *prop, + EjsCMethod fn, void *userData, int flags) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_CMETHOD); + v.cMethod.fn = fn; + v.cMethod.userData = userData; + v.flags = flags; + + return ejsSetProperty(ep, vp, prop, &v); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToBoolean(Ejs *ep, EjsVar *vp, const char *prop, + int value) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_BOOL); + v.boolean = value; + + return ejsSetProperty(ep, vp, prop, &v); +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT + +EjsProperty *ejsSetPropertyToFloat(Ejs *ep, EjsVar *vp, const char *prop, + double value) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_FLOAT); + v.floating = value; + + return ejsSetProperty(ep, vp, prop, &v); +} + +#endif +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToInteger(Ejs *ep, EjsVar *vp, const char *prop, + int value) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_INT); + v.integer = value; + + return ejsSetProperty(ep, vp, prop, &v); +} + +/******************************************************************************/ +#if BLD_FEATURE_INT64 + +EjsProperty *ejsSetPropertyToInteger64(Ejs *ep, EjsVar *vp, const char *prop, + int64 value) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_INT64); + v.integer64 = value; + + return ejsSetProperty(ep, vp, prop, &v); +} + +#endif +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToNull(Ejs *ep, EjsVar *vp, const char *prop) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_NULL); + + return ejsSetProperty(ep, vp, prop, &v); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToMethod(Ejs *ep, EjsVar *vp, const char *prop, + const char *body, MprArray *args, int flags) +{ + return ejsSetPropertyAndFree(ep, vp, prop, + ejsCreateMethodVar(ep, body, args, flags)); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToNumber(Ejs *ep, EjsVar *vp, const char *prop, + EjsNum value) +{ + return ejsSetPropertyAndFree(ep, vp, prop, ejsCreateNumberVar(ep, value)); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToStringCMethod(Ejs *ep, EjsVar *vp, + const char *prop, EjsStringCMethod fn, void *userData, int flags) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_STRING_CMETHOD); + v.cMethodWithStrings.fn = fn; + v.cMethodWithStrings.userData = userData; + v.flags = flags; + + return ejsSetProperty(ep, vp, prop, &v); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToString(Ejs *ep, EjsVar *vp, const char *prop, + const char *value) +{ + EjsProperty *pp; + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_STRING); + + /* FUTURE OPT */ + v.string = mprStrdupInternal(EJS_LOC_ARGS(ep), value); + if (v.string == 0) { + return 0; + } + v.length = strlen(v.string); + v.allocatedData = 1; + + pp = ejsSetProperty(ep, vp, prop, &v); + + mprFree(v.string); + + return pp; +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToBinaryString(Ejs *ep, EjsVar *vp, + const char *prop, const uchar *value, int len) +{ + EjsProperty *pp; + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_STRING); + + /* FUTURE OPT */ + v.length = dupString(MPR_LOC_ARGS(ep), &v.ustring, value, len); + if (v.length < 0) { + return 0; + } + v.allocatedData = 1; + + pp = ejsSetProperty(ep, vp, prop, &v); + + mprFree(v.ustring); + + return pp; +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToUndefined(Ejs *ep, EjsVar *vp, const char *prop) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_UNDEFINED); + + return ejsSetProperty(ep, vp, prop, &v); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToPtr(Ejs *ep, EjsVar *vp, const char *prop, + void *ptr, EjsDestructor destructor) +{ + EjsVar v; + + ejsInitVar(&v, EJS_TYPE_PTR); + v.ptr.userPtr = ptr; + v.ptr.destructor = destructor; + v.allocatedData = 1; + + return ejsSetProperty(ep, vp, prop, &v); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToNewObj(Ejs *ep, EjsVar *vp, const char *prop, + const char *className, MprArray *args) +{ + return ejsSetPropertyAndFree(ep, vp, prop, + ejsCreateObjUsingArgv(ep, 0, className, args)); +} + +/******************************************************************************/ + +EjsProperty *ejsSetPropertyToObj(Ejs *ep, EjsVar *op, const char *prop) +{ + return ejsSetPropertyAndFree(ep, op, prop, ejsCreateObjVar(ep)); +} + +/******************************************************************************/ +/* + * Convenience routines + */ + +EjsVar *ejsSetPropertyToObjAsVar(Ejs *ep, EjsVar *op, const char *prop) +{ + return ejsGetVarPtr(ejsSetPropertyToObj(ep, op, prop)); +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* + * Create a script method + */ + +EjsProperty *ejsDefineMethod(Ejs *ep, EjsVar *vp, const char *prop, + const char *body, MprArray *args) +{ + if (vp == 0) { + vp = ejsGetGlobalObj(ep); + } + return ejsSetPropertyToMethod(ep, vp, prop, body, args, 0); +} + +/******************************************************************************/ +/* + * Create a C language method + */ + +EjsProperty *ejsDefineCMethod(Ejs *ep, EjsVar *vp, const char *prop, + EjsCMethod fn, int flags) +{ + if (vp == 0) { + vp = ejsGetGlobalObj(ep); + } + return ejsSetPropertyToCMethod(ep, vp, prop, fn, 0, flags); +} + +/******************************************************************************/ +/* + * Define accessors + */ + +EjsProperty *ejsDefineAccessors(Ejs *ep, EjsVar *vp, const char *prop, + const char *getBody, const char *setBody) +{ + EjsProperty *pp; + MprArray *args; + char *propName; + + if (vp == 0) { + vp = ejsGetGlobalObj(ep); + } + + if (ejsSetPropertyToMethod(ep, vp, prop, getBody, 0, EJS_GET_ACCESSOR) < 0){ + ejsMemoryError(ep); + return 0; + } + + /* MOB -- OPT to use SLAB */ + /* MOB -- need to encapsulate this logic */ + + if (mprAllocStrcat(MPR_LOC_ARGS(ep), &propName, EJS_MAX_ID+5, 0, + "-set-", prop, 0) < 0) { + ejsMemoryError(ep); + return 0; + } + + args = mprCreateItemArray(ep, EJS_INC_ARGS, EJS_MAX_ARGS); + mprAddItem(args, mprStrdup(args, "value")); + + pp = ejsSetPropertyToMethod(ep, vp, propName, setBody, args, + EJS_SET_ACCESSOR); + mprFree(propName); + + if (pp == 0) { + ejsMemoryError(ep); + return 0; + } + + return pp; +} + +/******************************************************************************/ +/* + * Define C accessors + */ + +EjsProperty *ejsDefineCAccessors(Ejs *ep, EjsVar *vp, const char *prop, + EjsCMethod getFn, EjsCMethod setFn, int flags) +{ + EjsProperty *pp; + char *propName; + + if (vp == 0) { + vp = ejsGetGlobalObj(ep); + } + pp = ejsSetPropertyToCMethod(ep, vp, prop, getFn, 0, + flags | EJS_GET_ACCESSOR); + if (pp == 0) { + ejsMemoryError(ep); + return 0; + } + + /* MOB -- OPT to use SLAB */ + if (mprAllocStrcat(MPR_LOC_ARGS(ep), &propName, EJS_MAX_ID + 5, 0, + "-set-", prop, 0) < 0) { + ejsMemoryError(ep); + return 0; + } + pp = ejsSetPropertyToCMethod(ep, vp, propName, setFn, 0, + flags | EJS_SET_ACCESSOR); + mprFree(propName); + + if (pp == 0) { + ejsMemoryError(ep); + return 0; + } + return pp; +} + +/******************************************************************************/ +/* + * Create a C language method with string arguments + */ + +EjsProperty *ejsDefineStringCMethod(Ejs *ep, EjsVar *vp, const char *prop, + EjsStringCMethod fn, int flags) +{ + if (vp == 0) { + vp = ejsGetGlobalObj(ep); + } + return ejsSetPropertyToStringCMethod(ep, vp, prop, fn, 0, flags); +} + +/******************************************************************************/ + +void ejsSetCMethodUserData(EjsVar *obj, void *userData) +{ + /* + * This is a little dirty. We rely on the userData being in the same + * place in the var structure. + */ + obj->cMethod.userData = userData; +} + +/******************************************************************************/ + +void ejsSetVarFlags(EjsVar *obj, int flags) +{ + obj->flags = flags; +} + +/******************************************************************************/ + +void *ejsGetCMethodUserData(EjsVar *obj) +{ + return obj->cMethod.userData; +} + +/******************************************************************************/ + +int ejsGetVarFlags(EjsVar *obj) +{ + return obj->flags; +} + +/******************************************************************************/ + +void ejsSetObjDestructor(Ejs *ep, EjsVar *obj, EjsDestructor destructor) +{ + obj->objectState->destructor = destructor; +} + +/******************************************************************************/ + +void ejsClearObjDestructor(Ejs *ep, EjsVar *obj) +{ + obj->objectState->destructor = 0; +} + +/******************************************************************************/ +/* + * Create a new property + */ + +static EjsProperty *allocProperty(Ejs *ep, EjsVar *op, const char *property, + int propertyIndex, EjsProperty *last) +{ + EjsProperty *prop; + EjsObj *obj; + + obj = op->objectState; + + /* + * Allocate the property using the memory context of the owning object + */ + prop = ejsAllocProperty(EJS_LOC_ARGS(obj->ejs)); + if (prop == 0) { + return 0; + } + if (mprStrcpy(prop->name, sizeof(prop->name), property) < 0) { + ejsError(ep, EJS_REFERENCE_ERROR, + "Property name %s is too long. Max is %d letters.", + prop->name, EJS_MAX_ID); + return 0; + } + + ejsSetVarName(ep, ejsGetVarPtr(prop), &prop->name[0]); + + /* + * Do hash linkage + */ + if (last) { + last->hashNext = prop; + } else { + obj->propertyHash[propertyIndex] = prop; + } + +#if BLD_DEBUG + prop->link.propertyName = prop->name; + prop->link.property = prop; + prop->link.head = &obj->link; +#endif + + /* + * Inserting before the dummy head will append to the end + */ + linkPropertyBefore(obj, &obj->link, &prop->link); + + obj->numProperties++; + prop->parentObj = obj; + mprAssert(obj->ejs); + + return prop; +} + +/******************************************************************************/ +/* + * Delete a property from this object + */ + +int ejsDeleteProperty(Ejs *ep, EjsVar *vp, const char *property) +{ + EjsProperty *prop, *last; + EjsObj *obj; + int propertyIndex; + + mprAssert(vp); + mprAssert(property && *property); + mprAssert(vp->type == EJS_TYPE_OBJECT); + + if (vp->type != EJS_TYPE_OBJECT) { + mprAssert(vp->type == EJS_TYPE_OBJECT); + return MPR_ERR_BAD_ARGS; + } + + prop = hashLookup(vp->objectState, property, &propertyIndex, &last); + if (prop == (EjsProperty*) 0) { + return MPR_ERR_NOT_FOUND; + } + obj = vp->objectState; + +#if FUTURE + if (prop->readonly) { + mprAssert(! prop->readonly); + return MPR_ERR_READ_ONLY; + } +#endif + + /* + * If doing enumerations, then the object will mark preventDelete to + * prevent any properties being deleted and thus disturbing the + * traversal. + */ + if (obj->preventDeleteProp) { + obj->delayedDeleteProp = 1; + prop->delayedDelete = 1; + return 0; + } + + /* + * Remove from hash + */ + if (last) { + last->hashNext = prop->hashNext; + } else { + obj->propertyHash[propertyIndex] = prop->hashNext; + } + + unlinkProperty(obj, &prop->link); + obj->numProperties--; + + /* + * Free any property data and return to the slab + */ + if (prop->var.type != EJS_TYPE_OBJECT) { + ejsClearVar(ep, ejsGetVarPtr(prop)); + } + ejsFree(ep, prop, EJS_SLAB_PROPERTY); + + return 0; +} + +/******************************************************************************/ +/* + * Remove a property's value from this object. The property is set to + * undefined. + */ + +EjsVar *ejsClearProperty(Ejs *ep, EjsVar *vp, const char *property) +{ + EjsProperty *prop; + + mprAssert(vp); + mprAssert(property && *property); + mprAssert(vp->type == EJS_TYPE_OBJECT); + + if (vp->type != EJS_TYPE_OBJECT) { + mprAssert(vp->type == EJS_TYPE_OBJECT); + return 0; + } + + prop = hashLookup(vp->objectState, property, 0, 0); + if (prop == (EjsProperty*) 0) { + return 0; + } +#if FUTURE + if (prop->readonly) { + mprAssert(! prop->readonly); + return 0; + } +#endif + + ejsClearVar(ep, &prop->var); + return &prop->var; +} + +/******************************************************************************/ +/* + * Unlink a property from the ordered list of properties + */ + +static void unlinkProperty(EjsObj *obj, EjsPropLink *propLink) +{ + propLink->prev->next = propLink->next; + propLink->next->prev = propLink->prev; +} + +/******************************************************************************/ +#if UNUSED && KEEP +/* + * Insert a link after a specified link. + */ + +static void linkPropertyAfter(EjsObj *obj, EjsPropLink *at, + EjsPropLink *propLink) +{ + propLink->next = at->next; + propLink->prev = at; + + at->next->prev = propLink; + at->next = propLink; +} + +#endif +/******************************************************************************/ +/* + * Insert a link before a specified link. + */ + +static void linkPropertyBefore(EjsObj *obj, EjsPropLink *at, + EjsPropLink *propLink) +{ + propLink->prev = at->prev; + propLink->next = at; + + at->prev->next = propLink; + at->prev = propLink; +} + +/******************************************************************************/ +/* + * This routine will sort properties in an object. If propertyName is not + * null, then the properties in op must be objects with a property of the + * name propertyName. If propertyName is null, then the properties of op + * are directly sorted. If order is 1, they are sorted in ascending order. + * If -1, they are sorted in descending order. + * + * NOTE: arrays keep their original index values. + */ + +void ejsSortProperties(Ejs *ep, EjsVar *op, EjsSortFn fn, + const char *propertyName, int order) +{ + EjsProperty *p1, *p2, *tmp; + EjsPropLink *l1, *l2, *oldL1Spot; + EjsObj *obj; + + obj = op->objectState; + + p1 = ejsGetFirstProperty(op, 0); + while (p1) { + if (p1->dontEnumerate) { + p1 = ejsGetNextProperty(p1, 0); + continue; + } + + p2 = ejsGetFirstProperty(op, 0); + while (p2 && p2 != p1) { + + if (p2->dontEnumerate) { + p2 = ejsGetNextProperty(p2, 0); + continue; + } + + if (fn == 0) { + if (propertyName) { + fn = sortByProperty; + } else { + fn = sortAllProperties; + } + } + + if (fn(ep, p1, p2, propertyName, order) < 0) { + + l1 = &p1->link; + l2 = &p2->link; + + /* + * Swap the properties without disturbing the hash chains. + * l1 is always after l2 in the list. Unlink l1 and remember + * the one after l1. + */ + oldL1Spot = l1->next; + unlinkProperty(obj, l1); + + /* + * Manually reinsert l1 by replacing l2 with l1. l2 is out of + * the chain. + */ + l2->prev->next = l1; + l2->next->prev = l1; + l1->prev = l2->prev; + l1->next = l2->next; + + /* + * Reinsert l2 before the spot where l1 was. + */ + linkPropertyBefore(obj, oldL1Spot, l2); + + /* + * Swap the pointers so we continue to traverse correctly + */ + tmp = p1; + p1 = p2; + p2 = tmp; + } + p2 = ejsGetNextProperty(p2, 0); + } + p1 = ejsGetNextProperty(p1, 0); + } +} + +/******************************************************************************/ +/* + * Sort properties. Strings are sorted in ascending ASCII collating sequence + * Numbers are sorted in increasing numerical order. + */ +static int sortAllProperties(Ejs *ep, EjsProperty *p1, EjsProperty *p2, + const char *propertyName, int order) +{ + EjsVar *v1, *v2; + char *buf1, *buf2; + int rc, buf1Alloc; + + v1 = ejsGetVarPtr(p1); + v2 = ejsGetVarPtr(p2); + + if (v1->type == v2->type) { + /* MOB -- should support Numbers */ + if (v1->type == EJS_TYPE_INT) { + if (v1->integer < v2->integer) { + return - order; + + } else if (v1->integer == v2->integer) { + return 0; + } + return order; + +#if BLD_FEATURE_FLOATING_POINT + } else if (v1->type == EJS_TYPE_FLOAT) { + if (v1->floating < v2->floating) { + return - order; + + } else if (v1->floating == v2->floating) { + return 0; + } + return order; + +#endif + } else if (v1->type == EJS_TYPE_STRING) { + /* MOB -- need binary support ? */ + return strcmp(v1->string, v2->string) * order; + + } else { + + buf1 = ejsVarToStringEx(ep, v1, &buf1Alloc); + buf2 = ejsVarToString(ep, v2); + + rc = strcmp(buf1, buf2); + + if (buf1Alloc) { + mprFree(buf1); + } + + return rc * order; + } + + } else { + /* Type mismatch in array */ + return 0; + } + return 0; +} + +/******************************************************************************/ +/* + * Sort an object by a given property. + */ +static int sortByProperty(Ejs *ep, EjsProperty *p1, EjsProperty *p2, + const char *propertyName, int order) +{ + EjsVar *o1, *o2, *v1, *v2; + char *buf1, *buf2; + int rc, buf1Alloc; + + o1 = ejsGetVarPtr(p1); + o2 = ejsGetVarPtr(p2); + + if (!ejsVarIsObject(o1) || !ejsVarIsObject(o2)) { + mprAssert(ejsVarIsObject(o1)); + mprAssert(ejsVarIsObject(o2)); + return 0; + } + + v1 = ejsGetPropertyAsVar(ep, o1, propertyName); + v2 = ejsGetPropertyAsVar(ep, o2, propertyName); + + if (v1 == 0 || v2 == 0) { + /* Property name not found */ + return 0; + } + + if (v1->type != v2->type) { + mprAssert(v1->type == v2->type); + return 0; + } + + if (v1->type == v2->type) { + /* MOB -- should support Numbers */ + if (v1->type == EJS_TYPE_INT) { + if (v1->integer < v2->integer) { + return -order; + + } else if (v1->integer == v2->integer) { + return 0; + } + return order; + +#if BLD_FEATURE_FLOATING_POINT + } else if (v1->type == EJS_TYPE_FLOAT) { + if (v1->floating < v2->floating) { + return -order; + + } else if (v1->floating == v2->floating) { + return 0; + } + return order; + +#endif + } else if (v1->type == EJS_TYPE_STRING) { + /* MOB -- need binary support ? */ + return strcmp(v1->string, v2->string) * order; + + } else { + buf1 = ejsVarToStringEx(ep, v1, &buf1Alloc); + + buf2 = ejsVarToString(ep, v2); + + rc = strcmp(buf1, buf2); + + if (buf1Alloc) { + mprFree(buf1); + } + + return rc * order; + } + + } else { + /* Type mismatch in array */ + return 0; + } + return 0; +} + +/******************************************************************************/ +/* + * Set a property's name + */ + +void ejsSetPropertyName(EjsProperty *pp, const char *property) +{ + mprStrcpy(pp->name, sizeof(pp->name), property); +} + +/******************************************************************************/ + +int ejsMakePropertyEnumerable(EjsProperty *prop, int enumerate) +{ + int oldValue; + + oldValue = prop->dontEnumerate; + prop->dontEnumerate = !enumerate; + return oldValue; +} + +/******************************************************************************/ + +void ejsMakePropertyPrivate(EjsProperty *prop, int isPrivate) +{ + prop->isPrivate = isPrivate; +} + +/******************************************************************************/ +/* + * Make a variable read only. Can still be deleted. + */ + +void ejsMakePropertyReadOnly(EjsProperty *prop, int readonly) +{ + prop->readonly = readonly; +} + +/******************************************************************************/ + +int ejsMakeObjPermanent(EjsVar *vp, int permanent) +{ + int oldValue; + + if (vp && vp->type == EJS_TYPE_OBJECT) { + oldValue = vp->objectState->permanent; + vp->objectState->permanent = permanent; + } else { + oldValue = 0; + } + return oldValue; +} + +/******************************************************************************/ + +int ejsMakeObjLive(EjsVar *vp, bool alive) +{ + int oldValue; + + oldValue = 0; + if (vp && vp->type == EJS_TYPE_OBJECT) { + oldValue = vp->objectState->alive; + vp->objectState->alive = alive; + } else { + oldValue = 0; + } + return oldValue; +} + +/******************************************************************************/ + +void ejsMakeClassNoConstructor(EjsVar *vp) +{ + mprAssert(vp->type == EJS_TYPE_OBJECT); + + if (vp->type == EJS_TYPE_OBJECT) { + vp->objectState->noConstructor = 1; + } +} + +/******************************************************************************/ +/* + * Get the count of properties. + */ + +int ejsGetPropertyCount(EjsVar *vp) +{ + EjsProperty *pp; + EjsPropLink *lp, *head; + int count; + + mprAssert(vp); + + if (vp->type != EJS_TYPE_OBJECT) { + return 0; + } + + count = 0; + + head = &vp->objectState->link; + for (lp = head->next; lp != head; lp = lp->next) { + pp = ejsGetPropertyFromLink(lp); + if (! pp->dontEnumerate) { + count++; + } + } + return count; +} + +/******************************************************************************/ +/* + * Get the first property in an object. Used for walking all properties in an + * object. This will only enumerate properties in this class and not in base + * classes. + */ + +EjsProperty *ejsGetFirstProperty(const EjsVar *op, int flags) +{ + EjsProperty *pp; + EjsObj *obj; + EjsPropLink *head, *lp; + + mprAssert(op); + mprAssert(op->type == EJS_TYPE_OBJECT); + + if (op->type != EJS_TYPE_OBJECT) { + mprAssert(op->type == EJS_TYPE_OBJECT); + return 0; + } + pp = 0; + + do { + obj = op->objectState; + + head = &obj->link; + lp = head->next; + + while (lp != head) { + pp = ejsGetPropertyFromLink(lp); + if (! pp->dontEnumerate || (flags & EJS_ENUM_HIDDEN)) { + break; + } + lp = lp->next; + } + if (lp != head || op->type != EJS_TYPE_OBJECT || + !(flags & EJS_ENUM_CLASSES)) { + break; + } + + op = obj->baseClass; + + } while (lp == 0 && op); + + return pp; +} + +/******************************************************************************/ +/* + * Get the next property in sequence. This will only enumerate properties in + * this class and not in base classes. + */ + +EjsProperty *ejsGetNextProperty(EjsProperty *last, int flags) +{ + EjsProperty *pp; + EjsObj *obj; + EjsPropLink *lp, *head; + + obj = last->parentObj; + + lp = last->link.next; + head = &obj->link; + pp = 0; + + while (obj) { + while (lp != head) { + pp = ejsGetPropertyFromLink(lp); + if (! pp->dontEnumerate || (flags & EJS_ENUM_HIDDEN)) { + break; + } + lp = lp->next; + } + if (lp != head || !(flags & EJS_ENUM_CLASSES)) { + break; + } + + /* + * Now iterate over properties in base classes (down the chain) + */ + if (obj->baseClass == 0) { + break; + } + + obj = obj->baseClass->objectState; + if (obj == 0) { + break; + } + } + return pp; +} + +/******************************************************************************/ +/* + * Find a variable given a variable name and return the parent object and + * the variable itself. This routine supports literal variable and property + * names that may be objects or arrays but may NOT have expressions. + * Returns -1 on errors or if the variable is not found. + * FUTURE -- Needs OPT + */ + +EjsVar *ejsFindProperty(Ejs *ep, EjsVar **obj, char **property, EjsVar *global, + EjsVar *local, const char *fullName, int create) +{ + EjsProperty *currentProp; + EjsVar *currentObj; + /* MOB -- WARNING BIG */ + char tokBuf[EJS_MAX_ID], propertyName[EJS_MAX_ID]; + char *token, *next, *cp, *endp; + + mprAssert(fullName && *fullName); + + currentProp = 0; + currentObj = 0; + + if (global == 0) { + global = ep->global; + } + + if (obj) { + *obj = 0; + } + if (property) { + *property = 0; + } + + if (fullName == 0) { + return 0; + } + + next = (char*) fullName; + token = getNextVarToken(&next, tokBuf, sizeof(tokBuf)); + mprStrcpy(propertyName, sizeof(propertyName), token); + + if (local) { + currentProp = ejsGetProperty(ep, local, token); + currentObj = local; + } + if (currentProp == 0) { + currentProp = ejsGetProperty(ep, global, token); + currentObj = global; + } + + token = getNextVarToken(&next, tokBuf, sizeof(tokBuf)); + + while (currentObj != 0 && token != 0 && *token) { + + if (currentProp == 0) { + return 0; + } + currentObj = ¤tProp->var; + currentProp = 0; + + if (*token == '[') { + token = getNextVarToken(&next, tokBuf, sizeof(tokBuf)); + + mprStrcpy(propertyName, sizeof(propertyName), token); + cp = propertyName; + if (*cp == '\"') { + cp++; + if ((endp = strchr(cp, '\"')) != 0) { + *endp = '\0'; + } + } else if (*cp == '\'') { + cp++; + if ((endp = strchr(cp, '\'')) != 0) { + *endp = '\0'; + } + } + + currentProp = ejsGetProperty(ep, currentObj, propertyName); + + token = getNextVarToken(&next, tokBuf, sizeof(tokBuf)); + if (*token != ']') { + return 0; + } + + } else if (*token == '.') { + token = getNextVarToken(&next, tokBuf, sizeof(tokBuf)); + if (!isalpha((int) token[0]) && + token[0] != '_' && token[0] != '$') { + return 0; + } + + mprStrcpy(propertyName, sizeof(propertyName), token); + currentProp = ejsGetProperty(ep, currentObj, token); + + } else { + currentProp = ejsGetProperty(ep, currentObj, token); + } + + if (next == 0 || *next == '\0') { + break; + } + token = getNextVarToken(&next, tokBuf, sizeof(tokBuf)); + } + + if (obj) { + *obj = currentObj; + } + + + if (currentProp == 0 && currentObj >= 0 && create) { + currentProp = ejsCreateSimpleProperty(ep, currentObj, propertyName); + } + + if (property) { + *property = currentProp->name; + } + return ejsGetVarPtr(currentProp); +} + +/******************************************************************************/ +/* + * 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; +} + +/******************************************************************************/ + +EjsVar *ejsGetGlobalClass(Ejs *ep) +{ + return ep->global; +} + +/******************************************************************************/ +/*************************** Property Access Methods **************************/ +/******************************************************************************/ +/* + * Create an undefined property. This routine calls the object method hooks. + */ + +/* MOB -- better suffix than "Method" */ +EjsVar *ejsCreatePropertyMethod(Ejs *ep, EjsVar *op, const char *property) +{ + EjsVar *vp; + + mprAssert(ep); + mprAssert(op); + mprAssert(property && *property); + + if (op == 0) { + return 0; + } + + mprAssert(op->type == EJS_TYPE_OBJECT); + mprAssert(op->objectState); + + if (op->objectState == 0) { + return 0; + } + + if (op->objectState->methods == 0) { + vp = ejsGetVarPtr(ejsCreateSimpleProperty(ep, op, property)); + } else { + vp = (op->objectState->methods->createProperty)(ep, op, property); + } + + if (vp == 0) { + mprAssert(vp); + op->objectState->hasErrors = 1; + return 0; + } + + /* + * FUTURE - find a better way. + */ + if (op->isArray) { + ejsSetArrayLength(ep, op, property, 0, 0); + } + return vp; +} + +/******************************************************************************/ + +int ejsDeletePropertyMethod(Ejs *ep, EjsVar *op, const char *property) +{ + int rc; + + mprAssert(ep); + mprAssert(op); + mprAssert(property && *property); + + if (op == 0) { + return -1; + } + + mprAssert(op->type == EJS_TYPE_OBJECT); + mprAssert(op->objectState); + + if (op->objectState == 0) { + return -1; + } + + if (op->objectState->methods == 0) { + rc = ejsDeleteProperty(ep, op, property); + } else { + rc = (op->objectState->methods->deleteProperty)(ep, op, property); + } + + if (rc < 0) { + op->objectState->hasErrors = 1; + } + + op->objectState->dirty = 1; + + return rc; +} + +/******************************************************************************/ +/* + * Set the value of a property. Create if it does not exist + * If the object has property accessor methods defined, use those. + */ + +EjsVar *ejsSetPropertyMethod(Ejs *ep, EjsVar *op, const char *property, + const EjsVar *value) +{ + EjsVar *vp; + + mprAssert(ep); + mprAssert(op); + mprAssert(property && *property); + mprAssert(value); + + if (op == 0) { + return 0; + } + + mprAssert(op->type == EJS_TYPE_OBJECT); + mprAssert(op->objectState); + + if (op->objectState == 0) { + return 0; + } + + if (op->objectState->methods == 0) { + vp = ejsGetVarPtr(ejsCreateSimpleProperty(ep, op, property)); + if (vp && ejsWriteVar(ep, vp, (EjsVar*) value, EJS_SHALLOW_COPY) < 0) { + mprAssert(0); + op->objectState->hasErrors = 1; + return 0; + } + + } else { + vp = (op->objectState->methods->setProperty)(ep, op, property, value); + } + + if (vp == 0) { + mprAssert(vp); + op->objectState->hasErrors = 1; + return 0; + } + + if (vp->type == EJS_TYPE_OBJECT) { + /* + * We make an object alive (and subject to garbage collection) when + * it is referenced in some other object. If this is undesirable, the + * caller should make the object permanent while calling this routine + * and then afterward clear the alive bit by calling ejsMakeObjLive(). + */ + if (op->objectState != vp->objectState) { + vp->objectState->alive = 1; + } +#if BLD_DEBUG + { + EjsProperty *pp = ejsGetPropertyPtr(vp); + ejsSetVarName(ep, vp, &pp->name[0]); + if (value->propertyName == 0) { + ejsSetVarName(ep, (EjsVar*) value, &pp->name[0]); + } + } +#endif + } + + /* + * Trap assignments to array.length. MOB - find a better way. + */ + if (vp->isArrayLength) { + ejsSetArrayLength(ep, op, 0, 0, value); + } + + op->objectState->dirty = 1; + + return vp; +} + +/******************************************************************************/ + +EjsVar *ejsGetPropertyMethod(Ejs *ep, EjsVar *op, const char *property) +{ + mprAssert(ep); + mprAssert(op); + mprAssert(property && *property); + + if (op == 0) { + return 0; + } + + mprAssert(op->type == EJS_TYPE_OBJECT); + mprAssert(op->objectState); + + if (op->objectState == 0) { + return 0; + } + + if (op->objectState->methods == 0) { + return ejsGetVarPtr(ejsGetSimpleProperty(ep, op, property)); + } else { + return (op->objectState->methods->getProperty)(ep, op, property); + } +} + +/******************************************************************************/ +/*************************** Advisory Locking Support *************************/ +/******************************************************************************/ +#if BLD_FEATURE_MULTITHREAD + +void ejsLockObj(EjsVar *vp) +{ + mprAssert(vp); + mprAssert(vp->type == EJS_TYPE_OBJECT); + mprAssert(vp->objectState); + + if (vp->objectState->mutex == 0) { + vp->objectState->mutex = mprCreateLock(vp->objectState->ejs); + } + mprLock(vp->objectState->mutex); +} + +/******************************************************************************/ + +void ejsUnlockObj(EjsVar *vp) +{ + mprAssert(vp); + mprAssert(vp->type == EJS_TYPE_OBJECT); + mprAssert(vp->objectState); + + if (vp->objectState->mutex) { + mprUnlock(vp->objectState->mutex); + } +} + +#endif +/******************************************************************************/ +/************************** Internal Support Routines *************************/ +/******************************************************************************/ +/* + * Create an object. + */ + +static EjsObj *createObj(EJS_LOC_DEC(ep, loc)) +{ + EjsObj *op; + EjsPropLink *lp; + + op = (EjsObj*) ejsAllocObj(EJS_LOC_PASS(ep, loc)); + if (op == NULL) { + return 0; + } + + /* + * The objectState holds the dummy head for the ordered list of properties + */ + lp = &op->link; + lp->next = lp->prev = lp; + +#if BLD_DEBUG + /* + * This makes it much easier to debug the list + */ + lp->head = lp; + lp->propertyName = "dummyHead"; +#endif + + return op; +} + +/******************************************************************************/ +/* + * Destroy an object. Called by the garbage collector if there are no more + * references to an object. + */ + +int ejsDestroyObj(Ejs *ep, EjsObj *obj) +{ + EjsProperty *pp; + EjsPropLink *lp, *head, *nextLink; + + mprAssert(obj); + + if (obj->destructor) { + EjsVar v; + memset(&v, 0, sizeof(v)); + v.type = EJS_TYPE_OBJECT; + v.objectState = obj; + ejsSetVarName(ep, &v, "destructor"); + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + v.gc.allocatedBy = "static"; +#endif + + if ((obj->destructor)(ep, &v) < 0) { + return -1; + } + } + mprFree(obj->objName); + obj->objName = 0; + + /* + * Just for safety. An object may be marked by a GC on the default + * interpreter. After destroying, it won't be on the free list and so + * won't be reset. + */ + obj->gcMarked = 0; + obj->visited = 0; + + head = &obj->link; + for (lp = head->next; lp != head; lp = nextLink) { + + pp = ejsGetPropertyFromLink(lp); + nextLink = lp->next; + + /* + * We don't unlink as we are destroying all properties. + * If an object, we don't need to clear either. + */ + if (pp->var.type != EJS_TYPE_OBJECT) { + ejsClearVar(ep, ejsGetVarPtr(pp)); + } + ejsFree(ep, pp, EJS_SLAB_PROPERTY); + } + +#if BLD_FEATURE_MULTITHREAD + if (obj->mutex) { + mprDestroyLock(obj->mutex); + } +#endif + + ejsFree(ep, obj, EJS_SLAB_OBJ); + return 0; +} + +/******************************************************************************/ +/* + * Fast hash. The history of this algorithm is part of lost computer science + * folk lore. + */ + +static int hash(const char *property) +{ + uint sum; + + mprAssert(property); + + sum = 0; + while (*property) { + sum += (sum * 33) + *property++; + } + + return sum % EJS_OBJ_HASH_SIZE; +} + +/******************************************************************************/ +/* + * Set a new length for an array. If create is non-null, then it is the name + * of a new array index. If delete is set, it is the name of an index being + * deleted. If setLength is set to a variable, it counts the new length for the + * array. Note that create and delete are ignored if they are non-integer + * array indexes (eg. normal properties). + */ + +void ejsSetArrayLength(Ejs *ep, EjsVar *obj, const char *create, + const char *delete, const EjsVar *setLength) +{ + EjsVar *vp; + char idx[16]; + int oldSize, newSize, i; + + vp = ejsGetPropertyAsVar(ep, obj, "length"); + oldSize = vp->integer; + newSize = oldSize; + + if (create) { + if (isdigit(*create)) { + i = atoi(create); + newSize = max(i + 1, oldSize); + } + } else if (delete) { + if (isdigit(*delete)) { + i = atoi(delete); + newSize = (i == (oldSize - 1) ? oldSize - 1 : oldSize); + } + } else { + newSize = setLength->integer; + } + + for (i = newSize; i < oldSize; i++) { + mprItoa(idx, sizeof(idx), i); + ejsDeleteProperty(ep, obj, idx); + } + + if (ejsWriteVarAsInteger(ep, vp, newSize) == 0) { + mprAssert(0); + } +} + +/******************************************************************************/ + +void ejsClearObjErrors(EjsVar *vp) +{ + if (vp == 0 || vp->type != EJS_TYPE_OBJECT || vp->objectState == 0) { + mprAssert(0); + return; + } + vp->objectState->hasErrors = 0; +} + +/******************************************************************************/ + +int ejsObjHasErrors(EjsVar *vp) +{ + if (vp == 0 || vp->type != EJS_TYPE_OBJECT || vp->objectState == 0) { + mprAssert(0); + return -1; + } + return vp->objectState->hasErrors; +} + +/******************************************************************************/ + +bool ejsIsObjDirty(EjsVar *vp) +{ + mprAssert(vp->type == EJS_TYPE_OBJECT && vp->objectState); + + if (vp->type == EJS_TYPE_OBJECT && vp->objectState) { + return vp->objectState->dirty; + } + return 0; +} + +/******************************************************************************/ + +void ejsResetObjDirtyBit(EjsVar *vp) +{ + mprAssert(vp->type == EJS_TYPE_OBJECT && vp->objectState); + + if (vp->type == EJS_TYPE_OBJECT && vp->objectState) { + vp->objectState->dirty = 0; + } +} + +/******************************************************************************/ +/* + * Copy a string. Always null terminate. + */ + +static int dupString(MPR_LOC_DEC(ctx, loc), uchar **dest, const void *src, + int nbytes) +{ + mprAssert(dest); + mprAssert(src); + + if (nbytes > 0) { + *dest = mprMemdupInternal(MPR_LOC_PASS(ctx, loc), src, nbytes + 1); + if (*dest == 0) { + return MPR_ERR_MEMORY; + } + + } else { + *dest = (uchar*) mprAlloc(ctx, 1); + nbytes = 0; + } + + (*dest)[nbytes] = '\0'; + + return nbytes; +} + +/******************************************************************************/ + +const char *ejsGetVarTypeAsString(EjsVar *vp) +{ + switch (vp->type) { + default: + case EJS_TYPE_UNDEFINED: + return "undefined"; + case EJS_TYPE_NULL: + return "null"; + case EJS_TYPE_BOOL: + return "bool"; + case EJS_TYPE_CMETHOD: + return "cmethod"; + case EJS_TYPE_FLOAT: + return "float"; + case EJS_TYPE_INT: + return "int"; + case EJS_TYPE_INT64: + return "int64"; + case EJS_TYPE_OBJECT: + return "object"; + case EJS_TYPE_METHOD: + return "method"; + case EJS_TYPE_STRING: + return "string"; + case EJS_TYPE_STRING_CMETHOD: + return "string method"; + case EJS_TYPE_PTR: + return "ptr"; + } +} + +/******************************************************************************/ + +void *ejsGetVarUserPtr(EjsVar *vp) +{ + mprAssert(vp); + mprAssert(vp->type == EJS_TYPE_PTR); + + if (!ejsVarIsPtr(vp)) { + return 0; + } + return vp->ptr.userPtr; +} + +/******************************************************************************/ + +void ejsSetVarUserPtr(EjsVar *vp, void *data) +{ + mprAssert(vp); + mprAssert(vp->type == EJS_TYPE_PTR); + + vp->ptr.userPtr = data; +} + +/******************************************************************************/ +/* + * Return TRUE if target is a subclass (or the same class) as baseClass. + */ + +bool ejsIsSubClass(EjsVar *target, EjsVar *baseClass) +{ + do { + if (target->objectState == baseClass->objectState) { + return 1; + } + target = target->objectState->baseClass; + } while (target); + + return 0; +} + +/******************************************************************************/ +/* + * 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-2.0/ejs/ejsVar.h b/source4/lib/appweb/ejs-2.0/ejs/ejsVar.h new file mode 100644 index 0000000000..071665e00b --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/ejsVar.h @@ -0,0 +1,1091 @@ +/* + * ejsVar.h -- EJS Universal Variable Type + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ + +/* + * 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 methods. + * Properties are indexed by a character name. A variable may store one of + * the following types: + * + * string, integer, integer-64bit, C method, C method with string args, + * Javascript method, 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_EJS_VAR +#define _h_EJS_VAR 1 + +/********************************* Includes ***********************************/ + +#include "mpr.h" + +/********************************** Defines ***********************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Defined in ejs.h + */ +typedef struct Ejs Ejs; + +/* + * Constants + */ +#if BLD_FEATURE_SQUEEZE + /** + * Maximum property or variable name size + */ + #define EJS_MAX_ID 64 + + /* + * EJS_VAR_HASH_SIZE must be less than the size of the bit field + * propertyIndex in EjsProperty. + */ + #define EJS_OBJ_HASH_SIZE 13 + + /** + * Maximum number of arguments per function call + */ + #define EJS_MAX_ARGS 32 + #define EJS_INC_ARGS 8 /* Frame stack increment */ + +#else + #define EJS_MAX_ID 256 + #define EJS_OBJ_HASH_SIZE 29 + #define EJS_MAX_ARGS 64 + #define EJS_INC_ARGS 8 +#endif + +#define EJS_VAR_MAX_RECURSE 5 /* Max object loops */ + +#if !DOXYGEN +/* + * Forward declare types + */ +struct Ejs; +struct EjsObj; +struct EjsProperty; +struct EjsVar; +#endif + +/** + * @overview EJ primitive variable type + * @description EJ primitive variable values are stored in EjsVar structures. + * The type of the primitive data is described by an EjsType field. + * EjsVar variable types. + * @stability Prototype. + * @library libejs. + * @see EJS_TYPE_UNDEFINED, EJS_TYPE_NULL, EJS_TYPE_BOOL, EJS_TYPE_CMETHOD, + * EJS_TYPE_FLOAT, EJS_TYPE_INT, EJS_TYPE_INT64, EJS_TYPE_OBJECT, + * EJS_TYPE_METHOD, EJS_TYPE_STRING, EJS_TYPE_STRING_CMETHOD, EJS_TYPE_PTR, + */ +typedef uint EjsType; +#define EJS_TYPE_UNDEFINED 0 /**< Undefined. No value has been set */ +#define EJS_TYPE_NULL 1 /**< Value defined to be null. */ +#define EJS_TYPE_BOOL 2 /**< Boolean type. */ +#define EJS_TYPE_CMETHOD 3 /**< C method */ +#define EJS_TYPE_FLOAT 4 /**< Floating point number */ +#define EJS_TYPE_INT 5 /**< Integer number */ +#define EJS_TYPE_INT64 6 /**< 64-bit Integer number */ +#define EJS_TYPE_OBJECT 7 /**< Object reference */ +#define EJS_TYPE_METHOD 8 /**< JavaScript method */ +#define EJS_TYPE_STRING 9 /**< String (immutable) */ +#define EJS_TYPE_STRING_CMETHOD 10 /**< C method with string args */ +#define EJS_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=EJS_TYPE_INT + */ + +/** + * Set to the type used for EJS numeric variables. Will equate to int, int64 + * or double. + */ +typedef BLD_FEATURE_NUM_TYPE EjsNum; + +/** + * Set to the EJS_TYPE used for EJS numeric variables. Will equate to + * EJS_TYPE_INT, EJS_TYPE_INT64 or EJS_TYPE_FLOAT. + */ +#define EJS_NUM_VAR BLD_FEATURE_NUM_TYPE_ID +#define EJS_TYPE_NUM BLD_FEATURE_NUM_TYPE_ID + +/* + * Return TRUE if a variable is a method type + */ +#define ejsVarIsMethod(vp) \ + ((vp)->type == EJS_TYPE_METHOD || (vp)->type == EJS_TYPE_STRING_CMETHOD || \ + (vp)->type == EJS_TYPE_CMETHOD) + +/* + * Return TRUE if a variable is a numeric type + */ +#define ejsVarIsNumber(vp) \ + ((vp)->type == EJS_TYPE_INT || (vp)->type == EJS_TYPE_INT64 || \ + (vp)->type == EJS_TYPE_FLOAT) + +/* + * Return TRUE if a variable is a boolean + */ +#define ejsVarIsBoolean(vp) \ + ((vp)->type == EJS_TYPE_BOOL) + +/* + * Return TRUE if a variable is an integer type + */ +#define ejsVarIsInteger(vp) ((vp)->type == EJS_TYPE_INT) + +/* + * Return TRUE if a variable is a string + */ +#define ejsVarIsString(vp) \ + ((vp)->type == EJS_TYPE_STRING) + +/* + * Return TRUE if a variable is an object + */ +#define ejsVarIsObject(vp) \ + ((vp)->type == EJS_TYPE_OBJECT) + +/* + * Return TRUE if a variable is a floating number + */ +#define ejsVarIsFloating(vp) \ + ((vp)->type == EJS_TYPE_FLOAT) + +/* + * Return TRUE if a variable is undefined + */ +#define ejsVarIsUndefined(var) \ + ((var)->type == EJS_TYPE_UNDEFINED) + +/* + * Return TRUE if a variable is null + */ +#define ejsVarIsNull(var) \ + ((var)->type == EJS_TYPE_NULL) + +/* + * Return TRUE if a variable is a valid type (not null or undefined) + */ +#define ejsVarIsValid(var) \ + (((var)->type != EJS_TYPE_NULL) && ((var)->type != EJS_TYPE_UNDEFINED)) + +/* + * Return TRUE if a variable is a ptr type + */ +#define ejsVarIsPtr(vp) \ + ((vp)->type == EJS_TYPE_PTR) + +/* MOB -- convert all ep to ejs */ +/** + * @overview C Method signature + * @description This is the calling signature for C Methods. + * @param ejs Ejs reference returned from ejsCreateInterp + * @param thisObj Reference to the "this" object. (The object containing the + * method). + * @param argc Number of arguments. + * @param argv Array of arguments. Each argument is held in an EjsVar type. + * @stability Prototype. + * @library libejs. + * @see ejsCreateCMethodVar + */ +typedef int (*EjsCMethod)(struct Ejs *ejs, struct EjsVar *thisObj, + int argc, struct EjsVar **argv); + +/** + * C Method with string arguments signature + * @overview C Method with string arguments signature + * @description This is the calling signature for C Methods. + * @param ejs Ejs reference returned from ejsCreateInterp + * @param thisObj Reference to the "this" object (object containing the + * method. + * @param argc Number of arguments. + * @param argv Array of arguments. Each argument is held in an C string + * pointer. + * @stability Prototype. + * @library libejs. + * @see ejsCreateStringCMethodVar + */ +typedef int (*EjsStringCMethod)(struct Ejs *ep, struct EjsVar *thisObj, + int argc, char **argv); + +/** + * Flags for types: EJS_TYPE_CMETHOD, EJS_TYPE_STRING_CMETHOD + * NOTE: flags == 0 means to use the EJS handle on method callbacks + */ +/* Use the primary handle on method callbacks */ +#define EJS_PRIMARY_HANDLE 0x1 + +/* Use the alternate handle on method callbacks */ +#define EJS_ALT_HANDLE 0x2 + +/** Method should not create a new local variable block */ +#define EJS_NO_LOCAL 0x4 + +/* Method is a get accessor */ +#define EJS_GET_ACCESSOR 0x8 + +/* Method is a set accessor */ +#define EJS_SET_ACCESSOR 0x10 + +/* + * Flags for E4X (Xml type) + */ +/* Node is a text node */ +#define EJS_XML_FLAGS_TEXT 0x1 + +/* Node is a processing instruction */ +#define EJS_XML_FLAGS_PI 0x2 + +/* Node is a comment */ +#define EJS_XML_FLAGS_COMMENT 0x4 + +/* Node is an attribute */ +#define EJS_XML_FLAGS_ATTRIBUTE 0x8 + +/* Node is an element */ +#define EJS_XML_FLAGS_ELEMENT 0x10 + +/** + * Copy depth + * @overview Specifies how an object should be copied + * @description The EjsCopyDepth type specifies how an object's properties + * should be copied. Several routines take EjsCopyDepth parameters to + * control how the properties of an object should be copied. It provides + * three copy options: + * @see ejsWriteVar + */ +typedef enum EjsCopyDepth { + /** + * During an object copy, object property references will be copied so + * that the original object and the copy will share the same reference to + * a property object. Properties containing primitive types including + * strings will have their values copied and will not share references. + */ + EJS_SHALLOW_COPY, /** Copy strings. Copy object references. */ + /* + * During an object copy, object properties will be replicated so that + * the original object and the copy will not share references to the same + * object properties. If the original object's properties are themselves + * objects, their properties will not be copied. Only their references + * will be copied. i.e. the deep copy is one level deep. + */ + EJS_DEEP_COPY, /** Copy strings and copy object contents. */ + /* + * During an object copy, all object properties will be replicated so that + * the original object and the copy will not share references to the same + * object properties. If the original object's properties are themselves + * objects, their properties will be copied. i.e. the copy is of infinite + * depth. + */ + EJS_RECURSIVE_DEEP_COPY /** Copy strings and copy object contents + recursively (complete copy). */ +} EjsCopyDepth; + + +/* + * Enumeration flags + */ +/** Enumerate data properties */ +#define EJS_ENUM_DATA 0x0 + +/** Enumerate sub classes */ +#define EJS_ENUM_CLASSES 0x1 + +/** Enumerate non-enumerable properties */ +#define EJS_ENUM_HIDDEN 0x2 + +/** Enumerate all properties */ +#define EJS_ENUM_ALL (0x3) + +/** Magic number when allocated */ +#define EJS_MAGIC 0xe801e2ec +#define EJS_MAGIC_FREE 0xe701e3ea + + +/* + * Garbage Collection Linkage. Free list only uses the next pointers. + */ +typedef struct EjsGCLink { +#if BLD_DEBUG + uint magic; /* Magic number */ +#endif +#if BLD_FEATURE_ALLOC_LEAK_TRACK + const char *allocatedBy; /* Who allocated this */ +#endif + struct EjsGCLink *next; /* Next property */ +} EjsGCLink; + + +/** + * @overview EJS Variable Type + * @description The EJ language supports an extensive set of primitive types. + * These variable types can efficiently store primitive data types such as + * integers, strings, binary string, booleans, floating point numbers, + * pointer references, and objects. EjsVars are the universal type used by + * EJ to hold objects, classes and properties. + * \n\n + * An EjsVar may store one of the following types: + * @li Boolean + * @li Floating point (if supported in this build) + * @li Integer + * @li 64 bit integer (if supported in this build) + * @li String + * @li Binary string + * @li C function or C++ method + * @li C function with string args + * @li Javascript method + * @li Object + * @li Null value. + * @li Undefined value + * \n\n + * Objects can hold object properties which are themselves EJS variables. + * Properties are hash indexed by the property name and are stored in + * an ordered sequence. i.e. Order of properties is maintained. Objects may + * be referenced by multiple variables and they use garbage collection to + * reclaim memory no longer in use by objects and properties. + * + * @warning This module is @e 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 should + * help minimize required lock times. + * @stability Prototype. + * @library libejs + * @see Ejs, EjsProperty, ejsCreateStringVar, ejsFreeVar + */ + +typedef struct EjsVar { /* Size 12 bytes */ + /* + * GC must be first + */ +#if BLD_DEBUG || BLD_FEATURE_ALLOC_LEAK_TRACK + EjsGCLink gc; /* Garbage collection links */ +#endif + +#if BLD_DEBUG + const char *propertyName; /* Ptr to property name */ +#endif + + /* + * Union of primitive types. When debugging on Linux, don't use unions + * as the gdb debugger can't display them. + */ +#if (!BLD_DEBUG && !VXWORKS) || WIN || BREW_SIMULATOR + union { +#endif + /* + * For debugging, we order the common types first + */ + struct EjsObj *objectState; /* Object state information */ + int integer; + bool boolean; + +#if BLD_FEATURE_FLOATING_POINT + double floating; +#endif +#if BLD_FEATURE_INT64 + int64 integer64; +#endif + + struct { + int length; /* String length (sans null) */ + /* + * All strings always have a trailing null allocated + */ + union { + char *string; /* String */ + uchar *ustring; /* Binary string */ + }; + }; + + struct { /* Javascript methods */ + MprArray *args; /* Null terminated */ + char *body; + } method; + + struct { /* Method with EjsVar args */ + EjsCMethod fn; /* Method pointer */ + void *userData; /* User data for method */ + } cMethod; + + struct { /* Method with string args */ + EjsStringCMethod fn; /* Method pointer */ + void *userData; /* User data for method */ + } cMethodWithStrings; + + struct { + void *userPtr; /* Opaque pointer */ + int (*destructor)(Ejs *ejs, struct EjsVar *vp); + } ptr; + +#if (!BLD_DEBUG && !VXWORKS) || WIN || BREW_SIMULATOR + }; +#endif + + /* + * Packed bit field (32 bits) + */ + uint flags : 8; /* Type specific flags */ + EjsType type : 4; /* Selector into union */ + uint stringLen : 4; /* Length of string if inline */ + uint allocatedData : 1; /* Node needs freeing */ + uint isArray : 1; /* Var is an array */ + uint isArrayLength : 1; /* Var is array.length */ + uint callsSuper : 1; /* Method calls super() */ + uint isProperty : 1; /* Part of a property */ + uint reserved : 11; /* Unused */ + +} EjsVar; + + +/* + * Linkage for the ordered list of properties + */ +typedef struct EjsPropLink { + struct EjsPropLink *next; /* Next property */ + struct EjsPropLink *prev; /* Previous property */ + + /* + * To make debugging easier + */ +#if BLD_DEBUG + const char *propertyName; /* Pointer to name */ + struct EjsProperty *property; /* Pointer to property */ + struct EjsPropLink *head; /* Dummy head of list */ +#endif +} EjsPropLink; + + +/** + * @overview Object Property Type + * @description The EjsProperty type is used to store all object properties. + * It contains the property name, property linkage, propery attributes + * such as public/private, enumerable and readonly settings. It also + * contains an EjsVar to store the property data value. + * @stability Prototype. + * @library libejs + * @see Ejs, EjsVar + */ +typedef struct EjsProperty { /* Size 96 bytes in squeeze */ + /* + * EjsVar must be first. We often take the address of "var" and take + * advantage of if an EjsProperty is null, then &prop->var will be null + * also. Be WARNED. External users should use ejsGetVarPtr and + * ejsGetPropertyPtr to convert between the two. + */ + EjsVar var; /* Property value */ + + /* OPT change this to a pointer to the base class property */ + char name[EJS_MAX_ID]; /* Name */ + + uint visited : 1; /* Has been traversed */ + uint isPrivate : 1; /* Property is private */ + uint isProtected : 1; /* Property is protected */ + uint dontEnumerate : 1; /* Not enumerable */ + uint dontDelete : 1; /* Prevent delete */ + uint readonly : 1; /* Unmodifiable */ + uint allowNonUnique : 1; /* Multiple of same name ok */ + uint delayedDelete : 1; + uint reserved : 24; + + EjsPropLink link; /* Ordered linked list */ + struct EjsProperty *hashNext; /* Hash table linkage */ + + /* MOB -- is this really required */ + struct EjsObj *parentObj; /* Pointer to parent object */ + +} EjsProperty; + + +#define EJS_OP_DOT 0x1 +#define EJS_OP_INDEX 0x2 +#define EJS_OP_PLUS 0x3 +#define EJS_OP_MINUS 0x4 +#define EJS_OP_MULTIPLY 0x5 +#define EJS_OP_DIVIDE 0x6 +#define EJS_OP_CALL 0x7 + +typedef struct EjsOp { + int opType; + +} EjsOp; + +/* + * Propety Access Methods. Used per class. + * MOB -- rename EjsHelpers + */ +typedef struct EjsMethods { +#if FUTURE + int (*create)(Ejs *ep, EjsVar *thisObj); + int (*deleteProperty)(Ejs *ep, EjsVar *thisObj, const char *prop); + EjsVar *(*getProperty)(Ejs *ep, EjsVar *thisObj, const char *prop); + EjsVar *(*setProperty)(Ejs *ep, EjsVar *thisObj, const char *prop); + int (*hasProperty)(Ejs *ep, EjsVar *thisObj, const char *prop); + int (*hasInstance)(Ejs *ep, EjsVar *thisObj, const char *prop); + int (*operate)(Ejs *ep, EjsVar *thisObj, EjsOp op, EjsVar *result, + EjsVar *lhs, EjsVar *rhs, int *code); +#else + + EjsVar *(*createProperty)(Ejs *ep, EjsVar *obj, const char *property); + int (*deleteProperty)(Ejs *ep, EjsVar *obj, const char *property); + EjsVar *(*getProperty)(Ejs *ep, EjsVar *obj, const char *property); + EjsVar *(*setProperty)(Ejs *ep, EjsVar *obj, const char *property, + const EjsVar *value); + /* + * Other implemented internal properties in ECMA-262 are: + * + * [[Construct]] implemented via EjsVar methods + * [[Prototype]] implemented via EjsObj->baseClass + * [[Class]] implemented via EjsObj->baseClass->name + * [[Value]] Implemented via EjsProperty + EjsVar + EjsObj + */ + + /* + * FUTURE -- not implemented + */ + int (*canPut)(Ejs *ep, EjsVar *obj, const char *property); + int (*defaultValue)(Ejs *ep, EjsVar *obj, const char *property, + const char *hint); + int (*hasProperty)(Ejs *ep, EjsVar *obj, const char *property); + EjsVar *(*call)(Ejs *ep, EjsVar *obj, const char *property, + EjsVar *args); + int (*hasInstance)(Ejs *ep, EjsVar *obj, const char *property); + int (*scope)(Ejs *ep, EjsVar *obj, const char *property); + int (*match)(Ejs *ep, EjsVar *obj, const char *property, + const char *string, int index); +#endif +} EjsMethods; + + +/* + * Ejs Object Type + */ +typedef struct EjsObj { + /* + * GC must be first + */ + EjsGCLink gc; /* Garbage collection links */ + + union { + char *objName; /* Object name */ + char *className; /* Class name */ + }; + + struct EjsVar *baseClass; /* Pointer to base class object */ + + EjsPropLink link; /* Ordered list of properties */ + + /* OPT -- dynamically allocate this only if required */ + EjsProperty *propertyHash[EJS_OBJ_HASH_SIZE]; /* Hash chains */ + + /* OPT -- could save this and store off baseClass only */ + EjsMethods *methods; /* Property access methods */ + void *nativeData; /* Native object data */ + + int (*destructor)(Ejs *ejs, struct EjsVar *vp); + + uint numProperties : 16; /* Total count of items */ + uint visited : 1; /* Has been traversed */ + uint gcMarked : 1; /* Node marked in-use by GC */ + uint permanent : 1; /* Permanent object, dont GC */ + uint alive : 1; /* Only GC if alive */ + uint noConstructor : 1; /* Object has no constructor */ + uint dirty : 1; /* Object has been modified */ + uint hasErrors : 1; /* Update error */ + uint preventDeleteProp : 1; /* Don't allow prop deletion */ + uint delayedDeleteProp : 1; /* Delayed delete of props */ + uint reserved : 7; /* Unused */ + + Ejs *ejs; /* Owning interp */ + +#if BLD_FEATURE_MULTITHREAD + MprLock *mutex; /* Advisory mutex lock */ +#endif +} EjsObj; + + +/* + * Define a field macro so code an use numbers in a "generic" fashion. + */ +#if EJS_NUM_VAR == EJS_TYPE_INT || DOXYGEN +/* + * Default numeric type + */ +#define ejsNumber integer +#endif +#if EJS_NUM_VAR == EJS_TYPE_INT64 +/* Default numeric type */ +#define ejsNumber integer64 +#endif +#if EJS_NUM_VAR == EJS_TYPE_FLOAT +/* Default numeric type */ +#define ejsNumber floating +#endif + +typedef BLD_FEATURE_NUM_TYPE EjsNumber; + +/* + * Memory allocation slabs + */ +#define EJS_SLAB_OBJ 0 +#define EJS_SLAB_PROPERTY 1 +#define EJS_SLAB_VAR 2 +#define EJS_SLAB_MAX 3 + +/** + * Object and pointer property destructory type + */ +typedef int (*EjsDestructor)(Ejs *ejs, EjsVar *vp); + +#if BLD_FEATURE_ALLOC_LEAK_TRACK || DOXYGEN +/* + * Line number information args and declarations for ejsAlloc. + * Use EJS_LOC_ARGS in normal user code. + * Use EJS_LOC_DEC in declarations. + * Use EJS_LOC_PASS in layered APIs to pass original line info down. + */ +#define EJS_LOC_ARGS(ejs) ejs, MPR_LOC +#define EJS_LOC_DEC(ejs, loc) Ejs *ejs, const char *loc +#define EJS_LOC_PASS(ejs, loc) ejs, loc +#else +#define EJS_LOC_ARGS(ejs) ejs +#define EJS_LOC_DEC(ejs, loc) Ejs *ejs +#define EJS_LOC_PASS(ejs, loc) ejs +#endif + +/******************************* Internal Prototypes **************************/ + +#define ejsInitVar(vp, varType) \ + if (1) { \ + (vp)->type = varType; \ + (vp)->isArray = 0; \ + (vp)->flags = 0; \ + } else +extern void ejsClearVar(Ejs *ep, EjsVar *vp); + +extern int ejsDestroyObj(Ejs *ep, EjsObj *obj); +extern EjsVar *ejsCreatePropertyMethod(Ejs *ep, EjsVar *obj, + const char *name); +extern EjsVar *ejsSetPropertyMethod(Ejs *ep, EjsVar *obj, const char *name, + const EjsVar *value); +extern EjsVar *ejsGetPropertyMethod(Ejs *ep, EjsVar *obj, const char *name); +extern int ejsDeletePropertyMethod(Ejs *ep, EjsVar *obj, + const char *name); +extern void ejsSetArrayLength(Ejs *ep, EjsVar *obj, const char *creating, + const char *deleting, const EjsVar *setLength); + +/* + * At the moment, these are the same routine + */ +extern void ejsSetClassName(Ejs *ep, EjsVar *obj, const char *name); +#define ejsSetObjName ejsSetObjName + +extern bool ejsIsObjDirty(EjsVar *vp); +extern void ejsResetObjDirtyBit(EjsVar *vp); + +extern int ejsObjHasErrors(EjsVar *vp); +extern void ejsClearObjErrors(EjsVar *vp); + +extern EjsVar *ejsClearProperty(Ejs *ep, EjsVar *obj, const char *prop); + +typedef int (*EjsSortFn)(Ejs *ep, EjsProperty *p1, EjsProperty *p2, + const char *propertyName, int order); +extern void ejsSortProperties(Ejs *ep, EjsVar *obj, EjsSortFn fn, + const char *propertyName, int order); + +#if BLD_DEBUG +#define ejsSetVarName(ep, vp, varName) \ + if (1) { \ + (vp)->propertyName = varName; \ + if ((vp)->type == EJS_TYPE_OBJECT && \ + (vp)->objectState && \ + ((vp)->objectState->objName == 0)) { \ + (vp)->objectState->objName = \ + mprStrdup(ep, varName); \ + } \ + } else +#else +#define ejsSetVarName(ep, vp, varName) +#endif + +EjsVar *ejsFindProperty(Ejs *ep, EjsVar **obj, char **property, + EjsVar *global, EjsVar *local, const char *fullName, + int create); + +extern EjsVar *ejsCopyProperties(Ejs *ep, EjsVar *dest, + const EjsVar *src, EjsCopyDepth copyDepth); + +#define EJS_LINK_OFFSET ((uint) (&((EjsProperty*) 0)->link)) +#define ejsGetPropertyFromLink(lp) \ + ((EjsProperty*) ((char*) lp - EJS_LINK_OFFSET)) + +#define ejsGetObjPtr(vp) ((EjsObj*) vp->objectState) + +extern void ejsMakePropertyPrivate(EjsProperty *pp, int isPrivate); +extern void ejsMakePropertyReadOnly(EjsProperty *pp, int readonly); +extern void ejsMakePropertyUndeleteable(EjsProperty *pp, int deletable); +extern int ejsMakeObjLive(EjsVar *vp, bool alive); +extern void ejsMakeClassNoConstructor(EjsVar *vp); + +extern bool ejsBlockInUseInt(EjsVar *vp); +#if BLD_DEBUG + #define ejsBlockInUse(vp) ejsBlockInUseInt(vp) +#else + #define ejsBlockInUse(vp) +#endif + +/********************************* Prototypes *********************************/ + +/* + * Variable constructors and destructors + */ +extern EjsVar *ejsCreateBinaryStringVar(Ejs *ep, const uchar *value, + int len); +extern EjsVar *ejsCreateBoolVar(Ejs *ep, int value); +extern EjsVar *ejsCreateCMethodVar(Ejs *ep, EjsCMethod fn, + void *userData, int flags); +#if BLD_FEATURE_FLOATING_POINT +extern EjsVar *ejsCreateFloatVar(Ejs *ep, double value); +#endif +extern EjsVar *ejsCreateIntegerVar(Ejs *ep, int value); +#if BLD_FEATURE_INT64 +extern EjsVar *ejsCreateInteger64Var(Ejs *ep, int64 value); +#endif + +extern EjsVar *ejsCreateMethodVar(Ejs *ep, const char *body, + MprArray *args, int flags); +extern EjsVar *ejsCreateNullVar(Ejs *ep); +extern EjsVar *ejsCreateNumberVar(Ejs *ep, EjsNumber value); + +#define ejsCreateObjVar(ep) \ + ejsCreateObjVarInternal(EJS_LOC_ARGS(ep)) +extern EjsVar *ejsCreateObjVarInternal(EJS_LOC_DEC(ep, loc)); + +extern EjsVar *ejsCreatePtrVar(Ejs *ep, void *ptr, EjsDestructor dest); + +extern EjsVar *ejsCreateStringCMethodVar(Ejs *ep, EjsStringCMethod fn, + void *userData, int flags); + +#define ejsCreateStringVar(ep, value) \ + ejsCreateStringVarInternal(EJS_LOC_ARGS(ep), value) +extern EjsVar *ejsCreateStringVarInternal(EJS_LOC_DEC(ep, loc), + const char *value); + +extern EjsVar *ejsCreateUndefinedVar(Ejs *ep); + +/* MOB -- naming. Should be Create/Destroy */ +extern void ejsFreeVar(Ejs *ep, EjsVar *vp); + +/* + * Var support routines + */ +extern int ejsGetVarFlags(EjsVar *vp); +extern void ejsSetVarFlags(EjsVar *obj, int flags); + +extern EjsType ejsGetVarType(EjsVar *vp); +extern const char *ejsGetVarTypeAsString(EjsVar *vp); + +extern void *ejsGetCMethodUserData(EjsVar *obj); +extern void ejsSetCMethodUserData(EjsVar *obj, void *userData); + +extern void *ejsGetVarUserPtr(EjsVar *vp); +extern void ejsSetVarUserPtr(EjsVar *vp, void *data); + + +/* + * Variable access and manipulation. These work on standalone objects. + */ +#define ejsDupVar(ep, src, copyDepth) \ + ejsDupVarInternal(EJS_LOC_ARGS(ep), src, copyDepth) +extern EjsVar *ejsDupVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *src, + EjsCopyDepth copyDepth); +#define ejsWriteVar(ep, dest, src, copyDepth) \ + ejsWriteVarInternal(EJS_LOC_ARGS(ep), dest, src, copyDepth) +extern EjsVar *ejsWriteVarInternal(EJS_LOC_DEC(ep, loc), EjsVar *dest, + const EjsVar *src, EjsCopyDepth copyDepth); +extern EjsVar *ejsWriteVarAsBinaryString(Ejs *ep, EjsVar *dest, + const uchar *value, int len); +extern EjsVar *ejsWriteVarAsBoolean(Ejs *ep, EjsVar *dest, bool value); +extern EjsVar *ejsWriteVarAsCMethod(Ejs *ep, EjsVar *dest, EjsCMethod fn, + void *userData, int flags); +#if BLD_FEATURE_FLOATING_POINT +extern EjsVar *ejsWriteVarAsFloat(Ejs *ep, EjsVar *dest, double value); +#endif +extern EjsVar *ejsWriteVarAsInteger(Ejs *ep, EjsVar *dest, int value); +#if BLD_FEATURE_INT64 +extern EjsVar *ejsWriteVarAsInteger64(Ejs *ep, EjsVar *dest, int64 value); +#endif +extern EjsVar *ejsWriteVarAsMethod(Ejs *ep, EjsVar *dest, + const char *body, MprArray *args); +extern EjsVar *ejsWriteVarAsNull(Ejs *ep, EjsVar *dest); +extern EjsVar *ejsWriteVarAsNumber(Ejs *ep, EjsVar *dest, EjsNum value); +#define ejsWriteVarAsString(ep, dest, value) \ + ejsWriteVarAsStringInternal(EJS_LOC_ARGS(ep), dest, value) +extern EjsVar *ejsWriteVarAsStringInternal(EJS_LOC_DEC(ep, loc), + EjsVar *dest, const char *value); +extern EjsVar *ejsWriteVarAsStringCMethod(Ejs *ep, EjsVar *dest, + EjsStringCMethod fn, void *userData, int flags); +extern EjsVar *ejsWriteVarAsUndefined(Ejs *ep, EjsVar *dest); + +/* + * These routines do not convert types + */ +/* MOB -- make this a fn and pass back the length as an arg */ +#define ejsReadVarAsBinaryString(vp) ((const uchar*) (vp->ustring)); +#define ejsReadVarAsBoolean(vp) (vp->boolean); +#define ejsReadVarAsCMethod(vp) (vp->cMethod); +#if BLD_FEATURE_FLOATING_POINT +#define ejsReadVarAsFloat(vp) (vp->floating); +#endif +#define ejsReadVarAsInteger(vp) (vp->integer); +#if BLD_FEATURE_INT64 +#define ejsReadVarAsInteger64(vp) (vp->int64); +#endif +#define ejsReadVarAsString(vp) ((const char*) (vp->string)); +#define ejsReadVarAsStringCMethod(vp) (vp->cMethodWithStrings); +/* MOB -- remove this fn */ +#define ejsReadVarStringLength(vp) (vp->length); + +/* + * Object property creation routines + */ +extern EjsProperty *ejsCreateProperty(Ejs *ep, EjsVar *obj, const char *prop); +extern EjsProperty *ejsCreateSimpleProperty(Ejs *ep, EjsVar *obj, + const char *prop); +extern EjsProperty *ejsCreateSimpleNonUniqueProperty(Ejs *ep, EjsVar *obj, + const char *prop); +/* MOB -- should be destroy */ +extern int ejsDeleteProperty(Ejs *ep, EjsVar *obj, const char *prop); + + +/* + * Get property routines + */ +extern EjsProperty *ejsGetProperty(Ejs *ep, EjsVar *obj, const char *prop); +extern EjsProperty *ejsGetSimpleProperty(Ejs *ep, EjsVar *obj, + const char *prop); + +extern EjsVar *ejsGetPropertyAsVar(Ejs *ep, EjsVar *obj, + const char *prop); +extern int ejsGetPropertyCount(EjsVar *obj); + +extern const uchar *ejsGetPropertyAsBinaryString(Ejs *ep, EjsVar *obj, + const char *prop, int *length); +extern bool ejsGetPropertyAsBoolean(Ejs *ep, EjsVar *obj, + const char *prop); +extern int ejsGetPropertyAsInteger(Ejs *ep, EjsVar *obj, + const char *prop); +extern int64 ejsGetPropertyAsInteger64(Ejs *ep, EjsVar *obj, + const char *prop); +extern EjsNum ejsGetPropertyAsNumber(Ejs *ep, EjsVar *obj, + const char *prop); +extern void *ejsGetPropertyAsPtr(Ejs *ep, EjsVar *obj, + const char *prop); +extern const char *ejsGetPropertyAsString(Ejs *ep, EjsVar *obj, + const char *prop); + +/* + * Object property update routines + */ +extern EjsProperty *ejsSetBaseProperty(Ejs *ep, EjsVar *obj, const char *prop, + const EjsVar *value); +extern EjsProperty *ejsSetProperty(Ejs *ep, EjsVar *obj, const char *prop, + const EjsVar *value); +extern EjsProperty *ejsSetPropertyAndFree(Ejs *ep, EjsVar *obj, + const char *prop, EjsVar *value); +extern EjsProperty *ejsSetPropertyToBinaryString(Ejs *ep, EjsVar *obj, + const char *prop, const uchar *value, int len); +extern EjsProperty *ejsSetPropertyToBoolean(Ejs *ep, EjsVar *obj, + const char *prop, bool value); +extern EjsProperty *ejsSetPropertyToCMethod(Ejs *ep, EjsVar *obj, + const char *prop, EjsCMethod fn, void *userData, + int flags); +#if BLD_FEATURE_FLOATING_POINT +extern EjsProperty *ejsSetPropertyToFloat(Ejs *ep, EjsVar *obj, + const char *prop, double value); +#endif +extern EjsProperty *ejsSetPropertyToInteger(Ejs *ep, EjsVar *obj, + const char *prop, int value); +#if BLD_FEATURE_INT64 +extern EjsProperty *ejsSetPropertyToInteger64(Ejs *ep, EjsVar *obj, + const char *prop, int64 value); +#endif +extern EjsProperty *ejsSetPropertyToMethod(Ejs *ep, EjsVar *obj, + const char *prop, const char *body, MprArray *args, + int flags); +extern EjsProperty *ejsSetPropertyToNewObj(Ejs *ep, EjsVar *obj, + const char *prop, const char *className, + MprArray *args); +extern EjsProperty *ejsSetPropertyToNull(Ejs *ep, EjsVar *obj, + const char *prop); +extern EjsProperty *ejsSetPropertyToNumber(Ejs *ep, EjsVar *obj, + const char *prop, EjsNum value); +extern EjsProperty *ejsSetPropertyToObj(Ejs *ep, EjsVar *obj, + const char *prop); +extern EjsProperty *ejsSetPropertyToPtr(Ejs *ep, EjsVar *obj, + const char *prop, void *ptr, EjsDestructor destructor); + +extern EjsProperty *ejsSetPropertyToStringCMethod(Ejs *ep, EjsVar *obj, + const char *prop, EjsStringCMethod fn, + void *userData, int flags); +extern EjsProperty *ejsSetPropertyToString(Ejs *ep, EjsVar *obj, + const char *prop, const char *value); +extern EjsProperty *ejsSetPropertyToUndefined(Ejs *ep, EjsVar *obj, + const char *prop); + + +/* Convenience function */ +extern EjsVar *ejsSetPropertyToObjAsVar(Ejs *ep, EjsVar *obj, + const char *prop); +extern void ejsSetObjDestructor(Ejs *ep, EjsVar *obj, + EjsDestructor destructor); +extern void ejsClearObjDestructor(Ejs *ep, EjsVar *obj); + +/* + * Enumeration of properties + * MOB -- should these take an ejs parameter to be consistent + */ +extern EjsProperty *ejsGetFirstProperty(const EjsVar *obj, int flags); +extern EjsProperty *ejsGetNextProperty(EjsProperty *last, int flags); + +/* + * Method definition and control. + */ +extern EjsProperty *ejsDefineMethod(Ejs *ep, EjsVar *obj, const char *prop, + const char *body, MprArray *args); +extern EjsProperty *ejsDefineCMethod(Ejs *ep, EjsVar *obj, const char *prop, + EjsCMethod fn, int flags); + +extern EjsProperty *ejsDefineStringCMethod(Ejs *ep, EjsVar *obj, + const char *prop, EjsStringCMethod fn, int flags); + +extern EjsProperty *ejsDefineAccessors(Ejs *ep, EjsVar *obj, + const char *prop, const char *getBody, + const char *setBody); +extern EjsProperty *ejsDefineCAccessors(Ejs *ep, EjsVar *obj, + const char *prop, EjsCMethod getFn, EjsCMethod setFn, + int flags); + +/* + * Macro to get the variable value portion of a property + */ +#define ejsGetVarPtr(pp) (&((pp)->var)) +#define ejsGetPropertyPtr(vp) ((EjsProperty*) vp) + +/* MOB -- take ejs to be consistent */ +extern int ejsMakePropertyEnumerable(EjsProperty *pp, bool enumerable); +extern int ejsMakeObjPermanent(EjsVar *vp, bool permanent); + + +/* + * Var conversion routines + * MOB -- should these take an Ejs as first arg for consistency + */ +extern bool ejsVarToBoolean(EjsVar *vp); +#if BLD_FEATURE_FLOATING_POINT +extern double ejsVarToFloat(EjsVar *vp); +#endif +extern int ejsVarToInteger(EjsVar *vp); +#if BLD_FEATURE_INT64 +extern int64 ejsVarToInteger64(EjsVar *vp); +#endif +extern EjsNum ejsVarToNumber(EjsVar *vp); +extern char *ejsVarToString(Ejs *ep, EjsVar *vp); +extern char *ejsVarToStringEx(Ejs *ep, EjsVar *vp, bool *alloc); +extern char *ejsFormatVar(Ejs *ep, const char *fmt, EjsVar *vp); + +#if BLD_FEATURE_FLOATING_POINT +extern double ejsParseFloat(const char *str); +#endif +/* + * Parsing and type range checking routines + */ +extern bool ejsParseBoolean(const char *str); +extern int ejsParseInteger(const char *str); +#if BLD_FEATURE_INT64 +extern int64 ejsParseInteger64(const char *str); +#endif +extern EjsNum ejsParseNumber(const char *str); +extern EjsVar *ejsParseVar(Ejs *ep, const char *str, EjsType prefType); + +#if BLD_FEATURE_FLOATING_POINT +extern bool ejsIsInfinite(double f); +extern bool ejsIsNan(double f); +#endif + +/* + * Advisory locking support + */ +#if BLD_FEATURE_MULTITHREAD +extern void ejsLockObj(EjsVar *vp); +extern void ejsUnlockObj(EjsVar *vp); +#endif + +/* + * Just for debugging + */ +extern bool ejsObjIsCollectable(EjsVar *vp); + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ +#endif /* _h_EJS_VAR */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim:tw=78 + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/source4/lib/appweb/ejs-2.0/ejs/lib/event.js b/source4/lib/appweb/ejs-2.0/ejs/lib/event.js new file mode 100644 index 0000000000..283a3ec72f --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/lib/event.js @@ -0,0 +1,141 @@ +/* + * @file event.js + * @brief Event class + * @copy Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + * + * Usage: + * listener = new System.Listener(); + * listener.onClick = function() { + * // Any code here + * } + * eventTarget.addListener(eventName, listener); + * or + * listener = new System.Listener(obj, method); + * eventTarget.addListener(eventName, listener); + * + * To fire events: + * eventTarget.fire(eventName, new System.Event("My Event")); + */ + +/******************************************************************************/ +/* + * Base event class + */ +class System.Event +{ + var type; // keyboard + var timeStamp; + var arg; + + /* MOB -- constructor should take a type */ + function Event(arg) + { + timeStamp = time(); + type = "default"; + this.arg = arg; + } +} + +/* MOB -- should not be needed */ +Event = System.Event; + +class System.Listener +{ + var obj; + var method; + + function Listener(obj, method) + { + if (arguments.length >= 1) { + this.obj = obj; + } else { + this.obj = this; + } + if (arguments.length == 2) { + this.method = method; + } else { + this.method = "onEvent"; + } + } +} +/* MOB -- should not be needed */ +Listener = System.Listener; + + +/* + * The Event target class + */ +class System.EventTarget +{ + // Private + var events; /* Hash of a event names */ + + function EventTarget() + { + events = new Object(); + } + + // Public + function addListener(eventName, listener) + { + var listeners = events[eventName]; + if (listeners == undefined) { + listeners = events[eventName] = new Array(); + } + if (arguments.length == 2) { + var method = eventName; + } + /* MOB OPT */ + for (var i = 0; i < listeners.length; i++) { + var l = listeners[i]; + if (l == listener) { + return; + } + } + listeners[listeners.length] = listener; + } + + function removeListener(eventName, listener) + { + var listeners = events[eventName]; + + if (listeners == undefined) { + return; + } + + for (var i = 0; i < listeners.length; i++) { + var l = listeners[i]; + if (l == listener) { + // MOB -- want listeners.splice here + // listeners.splice(i, 1); + for (var j = i; j < (listeners.length - 1); j++) { + listeners[j] = listeners[j + 1]; + } + delete listeners[listeners.length - 1]; + i = listeners.length; + } + } + } + + function fire(eventName, event) + { + var listeners = events[eventName]; + + if (listeners == undefined) { + // println("Event.fire(): unknown eventName " + eventName); + return; + } + + for (var i = listeners.length - 1; i >= 0; i--) { + var listener = listeners[i]; + var method = listener.obj[listener.method]; + if (method == undefined) { + throw new EvalError("Undefined method: " + listener.method); + } + listener.obj[listener.method](listener, event); + } + } +} + +/* MOB -- should not be needed */ +EventTarget = System.EventTarget; diff --git a/source4/lib/appweb/ejs-2.0/ejs/lib/global.js b/source4/lib/appweb/ejs-2.0/ejs/lib/global.js new file mode 100644 index 0000000000..f2daaa57c0 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/lib/global.js @@ -0,0 +1,34 @@ +/* + * @file global.js + * @brief Misc global functions + * @copy Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ + +/******************************************************************************/ + +function min(a, b) +{ + if (a < b) { + return a; + } else { + return b; + } +} + + +function max(a, b) +{ + if (a > b) { + return a; + } else { + return b; + } +} + +function abs(a) +{ + if (a < 0) { + return -a; + } + return a; +} diff --git a/source4/lib/appweb/ejs-2.0/ejs/lib/startup.js b/source4/lib/appweb/ejs-2.0/ejs/lib/startup.js new file mode 100644 index 0000000000..e627a96e04 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/lib/startup.js @@ -0,0 +1,15 @@ +/* + * @file startup.js + * @brief Embedded JavaScript Startup Code + * @copy Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + * + * Invoked automatically on startup. + */ + +/******************************************************************************/ + +// println("Loading startup.js ..."); + +include("lib/event.js"); +include("lib/global.js"); +include("lib/timer.js"); diff --git a/source4/lib/appweb/ejs-2.0/ejs/lib/timer.js b/source4/lib/appweb/ejs-2.0/ejs/lib/timer.js new file mode 100644 index 0000000000..f4cb8b12ce --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/lib/timer.js @@ -0,0 +1,158 @@ +/* + * @file timer.js + * @brief Timer class + * @copy Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + * + * Usage: + * timer = new System.Timer("name", period); + * timer.onTick = function(arg) { + * // Anything here + * } + * timer.start(); + * or + * + * timer = new System.Timer("name", period, obj, method); + * timer.start(); + */ + +/******************************************************************************/ + +class System.Timer +{ + var id; + + /* MOB -- really need accessor on period. If user updates period, + then due must be updated. */ + var period; + var due; + var runOnce; // Run timer just once + var method; // Callback method + var obj; // Callback object + + function Timer(id, period, obj, method) + { + this.id = id; + this.period = period; + due = time() + period; + + if (arguments.length >= 3) { + this.obj = obj; + } else { + this.obj = this; + } + if (arguments.length >= 4) { + this.method = method; + } else { + this.method = "onTick"; + } + } + + /* MOB this should be deprecated */ + function reschedule(period) + { + /* MOB -- should update the timer service somehow */ + this.period = period; + } + + function run(now) + { + if (obj[method] == undefined) { + trace("Timer cant find timer method " + method); + due = now + this.period; + return; + } + + /* + * Run the timer + */ + try { + obj[method](this); + } + catch (error) { + trace("Timer exception: " + error); + } + + if (runOnce) { + timerService.removeTimer(this); + + } else { + due = now + this.period; + } + } + + function start() + { + if (obj[method] == undefined) { + throw new Error("Callback method is undefined"); + } else { + timerService.addTimer(this); + } + } + + function stop() + { + timerService.removeTimer(this); + } + +} + +/* MOB -- should not need this */ +Timer = System.Timer; + + +/* + * Timer service + */ +class System.TimerService +{ + var timers; + var nextDue; + + function TimerService() + { + timers = new Object(); + nextDue = 0; + global.timerService = this; + } + + function addTimer(timer) + { + timers[timer.id] = timer; + } + + function removeTimer(timer) + { + try { + delete timers[timer.id]; + } + catch {} + } + + function getIdleTime() + { + return nextDue - time(); + } + + function runTimers() + { + var now = time(); + + nextDue = 2147483647; /* MOB -- MATH.MAX_INT; */ + + for each (var timer in timers) + { + if (timer.due < now) { + timer.run(now); + } + } + for each (var timer in timers) + { + if (timer.due < nextDue) { + nextDue = timer.due; + } + } + // println("runTimers leaving with " + (nextDue - now)); + return nextDue - time(); + } +} +TimerService = System.TimerService; diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/.ignore b/source4/lib/appweb/ejs-2.0/ejs/system/.ignore new file mode 100755 index 0000000000..fb5a29031e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/.ignore @@ -0,0 +1 @@ +.updated diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/Makefile b/source4/lib/appweb/ejs-2.0/ejs/system/Makefile new file mode 100755 index 0000000000..2d83662655 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/Makefile @@ -0,0 +1,27 @@ +# +# Makefile to build the EJS Object Model +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I.. -I../../mpr -I../../exml + +include make.dep + +ifeq ($(BLD_HOST_UNIX),1) +PRE_DIRS = UNIX +else +PRE_DIRS = $(BLD_HOST_OS) +endif + +compileExtra: .updated + +.updated: $(FILES) + @touch .updated + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/README.TXT b/source4/lib/appweb/ejs-2.0/ejs/system/README.TXT new file mode 100644 index 0000000000..a24e0e299c --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/README.TXT @@ -0,0 +1,63 @@ +Embedded JavaScript System Model + + +- Need args, arg types and exceptions thrown +- Error classes + +class Global + class System + class environment + var + class GC + void function run() + function tune() + function getUsedMemory() // Should be properties + function getAllocatedMemory() // Should be properties + + var javascript + var null + var undefined + var true + var false + var Nan + var Infinity + + function random // Not implemented + function sleep // Not implemented + function exit + function yield // Not implemented + + Debug + isDebugMode + + Limits + isLimitsMode // Not implemented + stack // Not implemented + heap // Not implemented + flash // Not implemented + + Memory + getUsedMemory() // Should be properties + getAvailableMemory() // Should be properties + used + flash // Not implemented + + assert() + breakpoint() + dirname() + basename() + eval() + exit() + print() + println() + printVars() + sleep() + sort() + time() + typeof() + include() + trace() + printf() // Not implemented + sprintf() + + diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/.ignore b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/.ignore new file mode 100644 index 0000000000..fb5a29031e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/.ignore @@ -0,0 +1 @@ +.updated diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/Makefile b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/Makefile new file mode 100755 index 0000000000..424747052a --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/Makefile @@ -0,0 +1,21 @@ +# +# Makefile to build the EJS Object Model for WIN +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I../.. -I../../../mpr + +include make.dep + +compileExtra: .updated + +.updated: $(FILES) + @touch .updated + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFile.c b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFile.c new file mode 100644 index 0000000000..772303152e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFile.c @@ -0,0 +1,98 @@ +/* + * @file ejsFile.c + * @brief File class for the EJ System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/* + * Default Constructor + */ + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function open(); + */ + +static int openProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.open()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function close(); + */ + +static int closeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.close()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function read(); + */ + +static int readProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.read()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function write(); + */ + +static int writeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.write()\n"); + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineFileClass(Ejs *ep) +{ + EjsVar *fileClass; + + fileClass = ejsDefineClass(ep, "File", "Object", 0); + if (fileClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ep, fileClass, "open", openProc, 0); + ejsDefineCMethod(ep, fileClass, "close", closeProc, 0); + ejsDefineCMethod(ep, fileClass, "read", readProc, 0); + ejsDefineCMethod(ep, fileClass, "write", writeProc, 0); + + return ejsObjHasErrors(fileClass) ? MPR_ERR_CANT_INITIALIZE: 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/UNIX/ejsFileSystem.c b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFileSystem.c new file mode 100755 index 0000000000..7b39c16e4d --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsFileSystem.c @@ -0,0 +1,454 @@ +/* + * @file ejsFileSystem.c + * @brief FileSystem class for the EJ System Object Model + * MOB -- this is almost the same as for Windows. Should common up. + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" +#include <dirent.h> + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function void access(string path); + * MOB - API insufficient. Access for read or write? + */ + +static int accessProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: access(path)"); + return -1; + } + + rc = access(argv[0]->string, 04); + + ejsSetReturnValueToBoolean(ejs, (rc == 0) ? 1 : 0); + return 0; +} + +/******************************************************************************/ +/* + * function void mkdir(string path); + */ + +static int mkdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)"); + return -1; + } + + if (mprMakeDirPath(ejs, argv[0]->string) < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create directory"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void rmdir(string path); + */ + +static int rmdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)"); + return -1; + } + + rc = mprDeleteDir(ejs, argv[0]->string); + + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant remove directory"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void dirList(string path, [bool enumDirs]); + * MOB -- need pattern to match (what about "." and ".." and ".*" + */ + +static int dirListProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + DIR *dir; + struct dirent *dirent; + char path[MPR_MAX_FNAME]; + EjsVar *array, *vp; + uchar enumDirs; + + if (argc < 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: dirList(path)"); + return -1; + } + if (argc == 2) { + enumDirs = ejsVarToBoolean(argv[1]); + } else { + enumDirs = 0; + } + array = ejsCreateArray(ejs, 0); + ejsMakeObjPermanent(array, 1); + + /* + * First collect the files + */ + mprSprintf(path, sizeof(path), "%s/*.*", argv[0]->string); + + dir = opendir(path); + if (dir == 0) { + ejsError(ejs, EJS_ARG_ERROR, "Can't enumerate dirList(path)"); + return -1; + } + + while ((dirent = readdir(dir)) != 0) { + if (dirent->d_name[0] == '.') { + continue; + } + if (!enumDirs || (dirent->d_type & DT_DIR)) { + mprSprintf(path, sizeof(path), "%s/%s", argv[0]->string, + dirent->d_name); + vp = ejsCreateStringVar(ejs, path); + ejsAddArrayElt(ejs, array, vp, EJS_SHALLOW_COPY); + ejsFreeVar(ejs, vp); + } + } + + closedir(dir); + + ejsSetReturnValue(ejs, array); + ejsMakeObjPermanent(array, 0); + + /* + * Can free now as the return value holds the reference + */ + ejsFreeVar(ejs, array); + + return 0; +} + +/******************************************************************************/ +/* + * function void getFreeSpace(); + */ + +static int getFreeSpaceProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if UNUSED + MprApp *app; + uint space; + + app = mprGetApp(ejs); + space = IFILEMGR_GetFreeSpace(app->fileMgr, 0); + ejsSetReturnValueToInteger(ejs, space); +#endif + + return 0; +} + +/******************************************************************************/ +/* + * function void writeFile(string path, var data); + */ + +static int writeFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFile *file; + char *data, *buf; + int bytes, length, rc; + + if (argc != 2 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: writeFile(path, var)"); + return -1; + } + + if (ejsVarIsString(argv[1])) { + data = argv[1]->string; + length = argv[1]->length; + buf = 0; + } else { + buf = data = ejsVarToString(ejs, argv[1]); + length = strlen(data); + } + + /* + * Create fails if already present + */ + rc = mprDelete(ejs, argv[0]->string); + file = mprOpen(ejs, argv[0]->string, O_CREAT | O_WRONLY | O_BINARY, 0664); + if (file == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[0]->string); + mprFree(buf); + return -1; + } + + rc = 0; + bytes = mprWrite(file, data, length); + if (bytes != length) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + } + + mprClose(file); + + mprFree(buf); + return rc; +} + +/******************************************************************************/ +/* + * function string readFile(string path); + */ + +static int readFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprApp *app; + MprFile *file; + MprBuf *buf; + char *data; + int bytes, rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: readFile(path)"); + return -1; + } + buf = mprCreateBuf(ejs, MPR_BUF_INCR, MPR_MAX_BUF); + if (buf == 0) { + ejsMemoryError(ejs); + return -1; + } + + data = mprAlloc(ejs, MPR_BUFSIZE); + if (buf == 0) { + mprFree(buf); + ejsMemoryError(ejs); + return -1; + } + + app = mprGetApp(ejs); + file = mprOpen(ejs, argv[0]->string, O_RDONLY, 0664); + if (file == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string); + mprFree(buf); + return -1; + } + + rc = 0; + while ((bytes = mprRead(file, data, MPR_BUFSIZE)) > 0) { + if (mprPutBlockToBuf(buf, data, bytes) != bytes) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + break; + } + } + + ejsSetReturnValueToBinaryString(ejs, (uchar*) mprGetBufStart(buf), + mprGetBufLength(buf)); + + mprClose(file); + mprFree(data); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * function void remove(string path); + */ + +static int removeProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: remove(path)"); + return -1; + } + + rc = unlink(argv[0]->string); + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant remove file"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void rename(string from, string to); + */ + +static int renameProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: rename(old, new)"); + return -1; + } + + unlink(argv[1]->string); + rc = rename(argv[0]->string, argv[1]->string); + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant rename file"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void copy(string old, string new); + */ + +static int copyProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFile *from, *to; + char *buf; + uint bytes; + int rc; + + if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: copy(old, new)"); + return -1; + } + + buf = mprAlloc(ejs, MPR_BUFSIZE); + if (buf == 0) { + ejsMemoryError(ejs); + return -1; + } + + from = mprOpen(ejs, argv[0]->string, O_RDONLY | O_BINARY, 0664); + if (from == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string); + mprFree(buf); + return -1; + } + + to = mprOpen(ejs, argv[1]->string, O_CREAT | O_BINARY, 0664); + if (to == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[1]->string); + mprClose(from); + mprFree(buf); + return -1; + } + + rc = 0; + while ((bytes = mprRead(from, buf, MPR_BUFSIZE)) > 0) { + if (mprWrite(to, buf, bytes) != bytes) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + break; + } + } + + mprClose(from); + mprClose(to); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * function FileInfo getFileInfo(string path); + * + * MOB -- should create a real class FileInfo + */ + +static int getFileInfoProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFileInfo info; + EjsVar *fileInfo; + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: getFileInfo(path)"); + return -1; + } + + fileInfo = ejsCreateObjVar(ejs); + if (fileInfo == 0) { + ejsMemoryError(ejs); + return -1; + } + ejsMakeObjPermanent(fileInfo, 1); + + rc = mprGetFileInfo(ejs, argv[0]->string, &info); + if (rc < 0) { + ejsMakeObjPermanent(fileInfo, 0); + ejsFreeVar(ejs, fileInfo); + ejsError(ejs, EJS_IO_ERROR, "Cant get file info for %s", + argv[0]->string); + return -1; + } + + ejsSetPropertyToInteger(ejs, fileInfo, "created", info.ctime); + ejsSetPropertyToInteger(ejs, fileInfo, "length", info.size); + ejsSetPropertyToBoolean(ejs, fileInfo, "isDir", info.isDir); + + ejsSetReturnValue(ejs, fileInfo); + ejsMakeObjPermanent(fileInfo, 0); + + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineFileSystemClass(Ejs *ejs) +{ + EjsVar *fileSystemClass; + + fileSystemClass = ejsDefineClass(ejs, "FileSystem", "Object", 0); + if (fileSystemClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ejs, fileSystemClass, "access", accessProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "mkdir", mkdirProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "rmdir", rmdirProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "dirList", dirListProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "writeFile", writeFileProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "readFile", readFileProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "remove", removeProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "rename", renameProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "copy", copyProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "getFileInfo", getFileInfoProc, 0); + + // MOB -- should be a property with accessor + ejsDefineCMethod(ejs, fileSystemClass, "getFreeSpace", getFreeSpaceProc, 0); + + return ejsObjHasErrors(fileSystemClass) ? MPR_ERR_CANT_INITIALIZE: 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/UNIX/ejsHTTP.c b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsHTTP.c new file mode 100755 index 0000000000..25821f6960 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/UNIX/ejsHTTP.c @@ -0,0 +1,488 @@ +/* + * @file ejsHTTP.c + * @brief HTTP class for the EJ System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if UNUSED +/*********************************** Defines **********************************/ + +#define EJS_WEB_PROPERTY "-web" +#define EJS_HTTP_PROPERTY "-http" + +#define EJS_HTTP_DISPOSED 550 + +/* + * Control structure for one HTTP request structure + */ +typedef struct HTTPControl { + Ejs *ejs; + IWebResp *webResp; + AEECallback *callback; + MprBuf *buf; + EjsVar *thisObj; + char *url; + MprTime requestStarted; + uint timeout; +} HTTPControl; + +/****************************** Forward Declarations **************************/ + +static void cleanup(HTTPControl *hp); +static int createWeb(Ejs *ejs, EjsVar *thisObj); +static void brewCallback(HTTPControl *hp); +static int httpDestructor(Ejs *ejs, EjsVar *vp); +static void httpCallback(HTTPControl *hp, int responseCode); +static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv); + +/******************************************************************************/ +/* + * Constructor + */ + +int ejsHTTPConstructor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 0 && argc != 2) { + ejsError(ejs, EJS_ARG_ERROR, + "Bad usage: HTTP([obj = this, method = onComplete]);"); + return -1; + } + + if (createWeb(ejs, thisObj) < 0) { + return -1; + } + + setCallback(ejs, thisObj, argc, argv); + return 0; +} + +/******************************************************************************/ + +static int createWeb(Ejs *ejs, EjsVar *thisObj) +{ + MprApp *app; + void *web; + + app = mprGetApp(ejs); + + /* + * Create one instance of IWeb for the entire application. Do it here + * so only widgets that require HTTP incurr the overhead. + */ + web = mprGetKeyValue(ejs, "bpWeb"); + if (web == 0) { + if (ISHELL_CreateInstance(app->shell, AEECLSID_WEB, &web) != SUCCESS) { + ejsError(ejs, EJS_IO_ERROR, "Can't create IWEB"); + return -1; + } + } + mprSetKeyValue(ejs, "bpWeb", web); + return 0; +} + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function setCallback(obj, methodString); + */ + +static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc >= 1) { + ejsSetProperty(ejs, thisObj, "obj", argv[0]); + } else { + ejsSetProperty(ejs, thisObj, "obj", thisObj); + } + + if (argc >= 2) { + ejsSetProperty(ejs, thisObj, "method", argv[1]); + } else { + ejsSetPropertyToString(ejs, thisObj, "method", "onComplete"); + } + + return 0; +} + +/******************************************************************************/ +/* + * function fetch(); + */ + +static int fetchProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + HTTPControl *hp; + EjsProperty *pp; + MprApp *app; + IWeb *web; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: fetch(url)"); + return -1; + } + + app = mprGetApp(ejs); + web = (IWeb*) mprGetKeyValue(ejs, "bpWeb"); + + /* + * Web options + * + * WEBOPT_USERAGENT (char*) sets user agent + * WEBOPT_HANDLERDATA (void*) + * WEBOPT_CONNECTTIMEOUT (uint) msec + * WEBOPT_CONTENTLENGTH (long) + * WEBOPT_IDLECONNTIMEOUT (int) + * WEBOPT_ACTIVEXACTIONST (uint) Number of active requests + * + * WEBREQUEST_REDIRECT redirect transparently + * + */ + + hp = mprAllocType(ejs, HTTPControl); + if (hp == 0) { + ejsMemoryError(ejs); + return -1; + } + + hp->ejs = ejs; + hp->buf = mprCreateBuf(hp, MPR_BUF_INCR, MPR_MAX_BUF); + if (hp->buf == 0) { + mprFree(hp); + ejsMemoryError(ejs); + return -1; + } + + /* + * We copy thisObj because we need to preserve both the var and the object. + * We pass the var to brewCallback and so it must persist. The call to + * ejsMakeObjPermanent will stop the GC from collecting the object. + */ + hp->thisObj = ejsDupVar(ejs, thisObj, EJS_SHALLOW_COPY); + ejsSetVarName(ejs, hp->thisObj, "internalHttp"); + + /* + * Must keep a reference to the http object + */ + ejsMakeObjPermanent(hp->thisObj, 1); + + /* + * Make a property so we can access the HTTPControl structure from other + * methods. + */ + pp = ejsSetPropertyToPtr(ejs, thisObj, EJS_HTTP_PROPERTY, hp, 0); + ejsMakePropertyEnumerable(pp, 0); + ejsSetObjDestructor(ejs, hp->thisObj, httpDestructor); + + hp->url = mprStrdup(hp, argv[0]->string); + + hp->timeout = ejsGetPropertyAsInteger(ejs, thisObj, "timeout"); + mprGetTime(hp, &hp->requestStarted); + + hp->callback = mprAllocTypeZeroed(hp, AEECallback); + CALLBACK_Init(hp->callback, brewCallback, hp); + + hp->webResp = 0; + IWEB_GetResponse(web, + (web, &hp->webResp, hp->callback, hp->url, + WEBOPT_HANDLERDATA, hp, + WEBOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)", + WEBOPT_CONNECTTIMEOUT, hp->timeout, + WEBOPT_COPYOPTS, TRUE, + WEBOPT_CONTENTLENGTH, 0, + WEBOPT_END)); + + ejsSetPropertyToString(ejs, thisObj, "status", "active"); + + return 0; +} + +/******************************************************************************/ +/* + * Called whenver the http object is deleted. + */ + +static int httpDestructor(Ejs *ejs, EjsVar *thisObj) +{ + HTTPControl *hp; + + /* + * If the httpCallback has run, then this property will not exist + */ + hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY); + + if (hp) { + cleanup(hp); + } + + return 0; +} + +/******************************************************************************/ +/* + * Stop the request immediately without calling the callback + */ + +static int stopProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + HTTPControl *hp; + + hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY); + + if (hp) { + cleanup(hp); + } + + return 0; +} + +/******************************************************************************/ +/* + * Brew HTTP callback. Invoked for any return data. + */ + +static void brewCallback(HTTPControl *hp) +{ + Ejs *ejs; + EjsVar *thisObj; + ISource *source; + WebRespInfo *info; + char data[MPR_BUF_INCR]; + int bytes; + + mprAssert(hp); + mprAssert(hp->webResp); + + info = IWEBRESP_GetInfo(hp->webResp); + + if (info == 0) { + mprAssert(info); + /* should not happen */ + return; + } + + ejs = hp->ejs; + thisObj = hp->thisObj; + + if (! WEB_ERROR_SUCCEEDED(info->nCode)) { + ejsSetPropertyToString(ejs, thisObj, "status", "error"); + httpCallback(hp, info->nCode); + return; + } + + if (hp->timeout) { + if (mprGetTimeRemaining(hp, hp->requestStarted, hp->timeout) <= 0) { + ejsSetPropertyToString(ejs, thisObj, "status", "timeout"); + httpCallback(hp, 504); + return; + } + } + + /* + * Normal success + */ + source = info->pisMessage; + mprAssert(source); + + bytes = ISOURCE_Read(source, data, sizeof(data)); + + switch (bytes) { + case ISOURCE_WAIT: // No data yet + ISOURCE_Readable(source, hp->callback); + break; + + case ISOURCE_ERROR: + ejsSetPropertyToString(ejs, thisObj, "status", "error"); + httpCallback(hp, info->nCode); + break; + + case ISOURCE_END: + mprAddNullToBuf(hp->buf); + ejsSetPropertyToString(ejs, thisObj, "status", "complete"); + httpCallback(hp, info->nCode); + break; + + default: + if (bytes > 0) { + if (mprPutBlockToBuf(hp->buf, data, bytes) != bytes) { + ejsSetPropertyToString(ejs, thisObj, "status", "partialData"); + httpCallback(hp, 500); + } + } + ISOURCE_Readable(source, hp->callback); + break; + } +} + +/******************************************************************************/ +/* + * Invoke the HTTP completion method + */ + +static void httpCallback(HTTPControl *hp, int responseCode) +{ + Ejs *ejs; + EjsVar *thisObj, *callbackObj; + MprArray *args; + char *msg; + const char *callbackMethod; + + mprAssert(hp); + mprAssert(hp->webResp); + + thisObj = hp->thisObj; + ejs = hp->ejs; + + ejsSetPropertyToInteger(ejs, thisObj, "responseCode", responseCode); + if (mprGetBufLength(hp->buf) > 0) { + ejsSetPropertyToBinaryString(ejs, thisObj, "responseData", + mprGetBufStart(hp->buf), mprGetBufLength(hp->buf)); + } + + callbackObj = ejsGetPropertyAsVar(ejs, thisObj, "obj"); + callbackMethod = ejsGetPropertyAsString(ejs, thisObj, "method"); + + if (callbackObj != 0 && callbackMethod != 0) { + + args = mprCreateItemArray(ejs, EJS_INC_ARGS, EJS_MAX_ARGS); + mprAddItem(args, ejsDupVar(ejs, hp->thisObj, EJS_SHALLOW_COPY)); + + if (ejsRunMethod(ejs, callbackObj, callbackMethod, args) < 0) { + msg = ejsGetErrorMsg(ejs); + mprError(ejs, MPR_LOC, "HTTP callback failed. Details: %s", msg); + } + ejsFreeMethodArgs(ejs, args); + + } else if (ejsRunMethod(ejs, thisObj, "onComplete", 0) < 0) { + msg = ejsGetErrorMsg(ejs); + mprError(ejs, MPR_LOC, "HTTP onComplete failed. Details: %s", msg); + } + + cleanup(hp); +} + +/******************************************************************************/ +/* + * Cleanup + */ + +static void cleanup(HTTPControl *hp) +{ + Ejs *ejs; + MprApp *app; + int rc; + + mprAssert(hp); + mprAssert(hp->webResp); + + ejs = hp->ejs; + + if (hp->webResp) { + rc = IWEBRESP_Release(hp->webResp); + // mprAssert(rc == 0); + hp->webResp = 0; + } + + if (hp->callback) { + CALLBACK_Cancel(hp->callback); + mprFree(hp->callback); + hp->callback = 0; + } + + /* + * Once the property is deleted, then if the destructor runs, it will + * notice that the EJS_HTTP_PROPERTY is undefined. + */ + ejsDeleteProperty(ejs, hp->thisObj, EJS_HTTP_PROPERTY); + + /* + * Allow garbage collection to work on thisObj + */ + ejsMakeObjPermanent(hp->thisObj, 0); + ejsFreeVar(ejs, hp->thisObj); + + mprFree(hp->buf); + mprFree(hp->url); + + mprFree(hp); + + app = mprGetApp(ejs); + + + ISHELL_SendEvent(app->shell, (AEECLSID) app->classId, EVT_USER, 0, 0); +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineHTTPClass(Ejs *ejs) +{ + EjsVar *httpClass; + + httpClass = + ejsDefineClass(ejs, "HTTP", "Object", ejsHTTPConstructor); + if (httpClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ejs, httpClass, "fetch", fetchProc, 0); + ejsDefineCMethod(ejs, httpClass, "stop", stopProc, 0); + ejsDefineCMethod(ejs, httpClass, "setCallback", setCallback, 0); + +#if FUTURE + ejsDefineCMethod(ejs, httpClass, "put", put, 0); + ejsDefineCMethod(ejs, httpClass, "upload", upload, 0); + ejsDefineCMethod(ejs, httpClass, "addUploadFile", addUploadFile, 0); + ejsDefineCMethod(ejs, httpClass, "addPostData", addPostData, 0); + ejsDefineCMethod(ejs, httpClass, "setUserPassword", setUserPassword, 0); + ejsDefineCMethod(ejs, httpClass, "addCookie", addCookie, 0); +#endif + + /* + * Define properties + */ + ejsSetPropertyToString(ejs, httpClass, "status", "inactive"); + + /* This default should come from player.xml */ + + ejsSetPropertyToInteger(ejs, httpClass, "timeout", 30 * 1000); + ejsSetPropertyToInteger(ejs, httpClass, "responseCode", 0); + + return ejsObjHasErrors(httpClass) ? MPR_ERR_CANT_INITIALIZE: 0; +} + +/******************************************************************************/ + +void ejsTermHTTPClass(Ejs *ejs) +{ + IWeb *web; + int rc; + + web = (IWeb*) mprGetKeyValue(ejs, "bpWeb"); + if (web) { + rc = IWEB_Release(web); + mprAssert(rc == 0); + } +} + +#endif +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/WIN/.ignore b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/.ignore new file mode 100644 index 0000000000..fb5a29031e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/.ignore @@ -0,0 +1 @@ +.updated diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/WIN/Makefile b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/Makefile new file mode 100755 index 0000000000..424747052a --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/Makefile @@ -0,0 +1,21 @@ +# +# Makefile to build the EJS Object Model for WIN +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I../.. -I../../../mpr + +include make.dep + +compileExtra: .updated + +.updated: $(FILES) + @touch .updated + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFile.c b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFile.c new file mode 100644 index 0000000000..24c521891e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFile.c @@ -0,0 +1,98 @@ +/* + * @file ejsFile.c + * @brief File class for the EJScript System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/* + * Default Constructor + */ + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function open(); + */ + +static int openProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.open()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function close(); + */ + +static int closeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.close()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function read(); + */ + +static int readProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.read()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function write(); + */ + +static int writeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "File.write()\n"); + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineFileClass(Ejs *ep) +{ + EjsVar *fileClass; + + fileClass = ejsDefineClass(ep, "File", "Object", 0); + if (fileClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ep, fileClass, "open", openProc, 0); + ejsDefineCMethod(ep, fileClass, "close", closeProc, 0); + ejsDefineCMethod(ep, fileClass, "read", readProc, 0); + ejsDefineCMethod(ep, fileClass, "write", writeProc, 0); + + return ejsObjHasErrors(fileClass) ? MPR_ERR_CANT_INITIALIZE: 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/WIN/ejsFileSystem.c b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFileSystem.c new file mode 100755 index 0000000000..66c3b84870 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsFileSystem.c @@ -0,0 +1,456 @@ +/* + * @file ejsFileSystem.c + * @brief FileSystem class for the EJ System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/* + * Default Constructor + */ + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function void access(string path); + * MOB - API insufficient. Access for read or write? + */ + +static int accessProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: access(path)"); + return -1; + } + + rc = access(argv[0]->string, 04); + + ejsSetReturnValueToBoolean(ejs, (rc == 0) ? 1 : 0); + return 0; +} + +/******************************************************************************/ +/* + * function void mkdir(string path); + */ + +static int mkdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)"); + return -1; + } + + if (mprMakeDirPath(ejs, argv[0]->string) < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create directory"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void rmdir(string path); + */ + +static int rmdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)"); + return -1; + } + + rc = mprDeleteDir(ejs, argv[0]->string); + + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant remove directory"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void dirList(string path, [bool enumDirs]); + * MOB -- need pattern to match (what about "." and ".." and ".*" + */ + +static int dirListProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + WIN32_FIND_DATA findData; + HANDLE h; + char path[MPR_MAX_FNAME]; + EjsVar *array, *vp; + uchar enumDirs; + + if (argc < 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: dirList(path)"); + return -1; + } + if (argc == 2) { + enumDirs = ejsVarToBoolean(argv[1]); + } else { + enumDirs = 0; + } + array = ejsCreateArray(ejs, 0); + ejsMakeObjPermanent(array, 1); + + /* + * First collect the files + */ + mprSprintf(path, sizeof(path), "%s/*.*", argv[0]->string); + h = FindFirstFile(path, &findData); + if (h == INVALID_HANDLE_VALUE) { + ejsError(ejs, EJS_ARG_ERROR, "Can't enumerate dirList(path)"); + return -1; + } + + do { + if (findData.cFileName[0] == '.') { + continue; + } + if (!enumDirs || + (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + mprSprintf(path, sizeof(path), "%s/%s", argv[0]->string, + findData.cFileName); + vp = ejsCreateStringVar(ejs, path); + ejsAddArrayElt(ejs, array, vp, EJS_SHALLOW_COPY); + ejsFreeVar(ejs, vp); + } + } while (FindNextFile(h, &findData) != 0); + + FindClose(h); + + ejsSetReturnValue(ejs, array); + ejsMakeObjPermanent(array, 0); + + /* + * Can free now as the return value holds the reference + */ + ejsFreeVar(ejs, array); + + return 0; +} + +/******************************************************************************/ +/* + * function void getFreeSpace(); + */ + +static int getFreeSpaceProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if UNUSED + MprApp *app; + uint space; + + app = mprGetApp(ejs); + space = IFILEMGR_GetFreeSpace(app->fileMgr, 0); + ejsSetReturnValueToInteger(ejs, space); +#endif + + return 0; +} + +/******************************************************************************/ +/* + * function void writeFile(string path, var data); + */ + +static int writeFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFile *file; + char *data, *buf; + int bytes, length, rc; + + if (argc != 2 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: writeFile(path, var)"); + return -1; + } + + if (ejsVarIsString(argv[1])) { + data = argv[1]->string; + length = argv[1]->length; + buf = 0; + } else { + buf = data = ejsVarToString(ejs, argv[1]); + length = strlen(data); + } + + /* + * Create fails if already present + */ + rc = mprDelete(ejs, argv[0]->string); + file = mprOpen(ejs, argv[0]->string, O_CREAT | O_WRONLY | O_BINARY, 0664); + if (file == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[0]->string); + mprFree(buf); + return -1; + } + + rc = 0; + bytes = mprWrite(file, data, length); + if (bytes != length) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + } + + mprClose(file); + + mprFree(buf); + return rc; +} + +/******************************************************************************/ +/* + * function string readFile(string path); + */ + +static int readFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprApp *app; + MprFile *file; + MprBuf *buf; + char *data; + int bytes, rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: readFile(path)"); + return -1; + } + buf = mprCreateBuf(ejs, MPR_BUF_INCR, MPR_MAX_BUF); + if (buf == 0) { + ejsMemoryError(ejs); + return -1; + } + + data = mprAlloc(ejs, MPR_BUFSIZE); + if (buf == 0) { + mprFree(buf); + ejsMemoryError(ejs); + return -1; + } + + app = mprGetApp(ejs); + file = mprOpen(ejs, argv[0]->string, O_RDONLY, 0664); + if (file == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string); + mprFree(buf); + return -1; + } + + rc = 0; + while ((bytes = mprRead(file, data, MPR_BUFSIZE)) > 0) { + if (mprPutBlockToBuf(buf, data, bytes) != bytes) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + break; + } + } + + ejsSetReturnValueToBinaryString(ejs, mprGetBufStart(buf), + mprGetBufLength(buf)); + + mprClose(file); + mprFree(data); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * function void remove(string path); + */ + +static int removeProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: remove(path)"); + return -1; + } + + rc = unlink(argv[0]->string); + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant remove file"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void rename(string from, string to); + */ + +static int renameProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int rc; + + if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: rename(old, new)"); + return -1; + } + + unlink(argv[1]->string); + rc = rename(argv[0]->string, argv[1]->string); + if (rc < 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant rename file"); + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * function void copy(string old, string new); + */ + +static int copyProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFile *from, *to; + char *buf; + int bytes, rc; + + if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: copy(old, new)"); + return -1; + } + + buf = mprAlloc(ejs, MPR_BUFSIZE); + if (buf == 0) { + ejsMemoryError(ejs); + return -1; + } + + from = mprOpen(ejs, argv[0]->string, O_RDONLY | O_BINARY, 0664); + if (from == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string); + mprFree(buf); + return -1; + } + + to = mprOpen(ejs, argv[1]->string, O_CREAT | O_BINARY, 0664); + if (to == 0) { + ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[1]->string); + mprClose(from); + mprFree(buf); + return -1; + } + + rc = 0; + while ((bytes = mprRead(from, buf, MPR_BUFSIZE)) > 0) { + if (mprWrite(to, buf, bytes) != bytes) { + ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string); + rc = -1; + break; + } + } + + mprClose(from); + mprClose(to); + mprFree(buf); + + return rc; +} + +/******************************************************************************/ +/* + * function FileInfo getFileInfo(string path); + * + * MOB -- should create a real class FileInfo + */ + +static int getFileInfoProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprFileInfo info; + EjsVar *fileInfo; + int rc; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: getFileInfo(path)"); + return -1; + } + + fileInfo = ejsCreateObjVar(ejs); + if (fileInfo == 0) { + ejsMemoryError(ejs); + return -1; + } + ejsMakeObjPermanent(fileInfo, 1); + + rc = mprGetFileInfo(ejs, argv[0]->string, &info); + if (rc < 0) { + ejsMakeObjPermanent(fileInfo, 0); + ejsFreeVar(ejs, fileInfo); + ejsError(ejs, EJS_IO_ERROR, "Cant get file info for %s", + argv[0]->string); + return -1; + } + + ejsSetPropertyToInteger(ejs, fileInfo, "created", info.ctime); + ejsSetPropertyToInteger(ejs, fileInfo, "length", info.size); + ejsSetPropertyToBoolean(ejs, fileInfo, "isDir", info.isDir); + + ejsSetReturnValue(ejs, fileInfo); + ejsMakeObjPermanent(fileInfo, 0); + + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineFileSystemClass(Ejs *ejs) +{ + EjsVar *fileSystemClass; + + fileSystemClass = ejsDefineClass(ejs, "FileSystem", "Object", 0); + if (fileSystemClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ejs, fileSystemClass, "access", accessProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "mkdir", mkdirProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "rmdir", rmdirProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "dirList", dirListProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "writeFile", writeFileProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "readFile", readFileProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "remove", removeProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "rename", renameProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "copy", copyProc, 0); + ejsDefineCMethod(ejs, fileSystemClass, "getFileInfo", getFileInfoProc, 0); + + // MOB -- should be a property with accessor + ejsDefineCMethod(ejs, fileSystemClass, "getFreeSpace", getFreeSpaceProc, 0); + + return ejsObjHasErrors(fileSystemClass) ? MPR_ERR_CANT_INITIALIZE: 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/WIN/ejsHTTP.c b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsHTTP.c new file mode 100755 index 0000000000..25821f6960 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/WIN/ejsHTTP.c @@ -0,0 +1,488 @@ +/* + * @file ejsHTTP.c + * @brief HTTP class for the EJ System Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if UNUSED +/*********************************** Defines **********************************/ + +#define EJS_WEB_PROPERTY "-web" +#define EJS_HTTP_PROPERTY "-http" + +#define EJS_HTTP_DISPOSED 550 + +/* + * Control structure for one HTTP request structure + */ +typedef struct HTTPControl { + Ejs *ejs; + IWebResp *webResp; + AEECallback *callback; + MprBuf *buf; + EjsVar *thisObj; + char *url; + MprTime requestStarted; + uint timeout; +} HTTPControl; + +/****************************** Forward Declarations **************************/ + +static void cleanup(HTTPControl *hp); +static int createWeb(Ejs *ejs, EjsVar *thisObj); +static void brewCallback(HTTPControl *hp); +static int httpDestructor(Ejs *ejs, EjsVar *vp); +static void httpCallback(HTTPControl *hp, int responseCode); +static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv); + +/******************************************************************************/ +/* + * Constructor + */ + +int ejsHTTPConstructor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 0 && argc != 2) { + ejsError(ejs, EJS_ARG_ERROR, + "Bad usage: HTTP([obj = this, method = onComplete]);"); + return -1; + } + + if (createWeb(ejs, thisObj) < 0) { + return -1; + } + + setCallback(ejs, thisObj, argc, argv); + return 0; +} + +/******************************************************************************/ + +static int createWeb(Ejs *ejs, EjsVar *thisObj) +{ + MprApp *app; + void *web; + + app = mprGetApp(ejs); + + /* + * Create one instance of IWeb for the entire application. Do it here + * so only widgets that require HTTP incurr the overhead. + */ + web = mprGetKeyValue(ejs, "bpWeb"); + if (web == 0) { + if (ISHELL_CreateInstance(app->shell, AEECLSID_WEB, &web) != SUCCESS) { + ejsError(ejs, EJS_IO_ERROR, "Can't create IWEB"); + return -1; + } + } + mprSetKeyValue(ejs, "bpWeb", web); + return 0; +} + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function setCallback(obj, methodString); + */ + +static int setCallback(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc >= 1) { + ejsSetProperty(ejs, thisObj, "obj", argv[0]); + } else { + ejsSetProperty(ejs, thisObj, "obj", thisObj); + } + + if (argc >= 2) { + ejsSetProperty(ejs, thisObj, "method", argv[1]); + } else { + ejsSetPropertyToString(ejs, thisObj, "method", "onComplete"); + } + + return 0; +} + +/******************************************************************************/ +/* + * function fetch(); + */ + +static int fetchProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + HTTPControl *hp; + EjsProperty *pp; + MprApp *app; + IWeb *web; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsError(ejs, EJS_ARG_ERROR, "Bad usage: fetch(url)"); + return -1; + } + + app = mprGetApp(ejs); + web = (IWeb*) mprGetKeyValue(ejs, "bpWeb"); + + /* + * Web options + * + * WEBOPT_USERAGENT (char*) sets user agent + * WEBOPT_HANDLERDATA (void*) + * WEBOPT_CONNECTTIMEOUT (uint) msec + * WEBOPT_CONTENTLENGTH (long) + * WEBOPT_IDLECONNTIMEOUT (int) + * WEBOPT_ACTIVEXACTIONST (uint) Number of active requests + * + * WEBREQUEST_REDIRECT redirect transparently + * + */ + + hp = mprAllocType(ejs, HTTPControl); + if (hp == 0) { + ejsMemoryError(ejs); + return -1; + } + + hp->ejs = ejs; + hp->buf = mprCreateBuf(hp, MPR_BUF_INCR, MPR_MAX_BUF); + if (hp->buf == 0) { + mprFree(hp); + ejsMemoryError(ejs); + return -1; + } + + /* + * We copy thisObj because we need to preserve both the var and the object. + * We pass the var to brewCallback and so it must persist. The call to + * ejsMakeObjPermanent will stop the GC from collecting the object. + */ + hp->thisObj = ejsDupVar(ejs, thisObj, EJS_SHALLOW_COPY); + ejsSetVarName(ejs, hp->thisObj, "internalHttp"); + + /* + * Must keep a reference to the http object + */ + ejsMakeObjPermanent(hp->thisObj, 1); + + /* + * Make a property so we can access the HTTPControl structure from other + * methods. + */ + pp = ejsSetPropertyToPtr(ejs, thisObj, EJS_HTTP_PROPERTY, hp, 0); + ejsMakePropertyEnumerable(pp, 0); + ejsSetObjDestructor(ejs, hp->thisObj, httpDestructor); + + hp->url = mprStrdup(hp, argv[0]->string); + + hp->timeout = ejsGetPropertyAsInteger(ejs, thisObj, "timeout"); + mprGetTime(hp, &hp->requestStarted); + + hp->callback = mprAllocTypeZeroed(hp, AEECallback); + CALLBACK_Init(hp->callback, brewCallback, hp); + + hp->webResp = 0; + IWEB_GetResponse(web, + (web, &hp->webResp, hp->callback, hp->url, + WEBOPT_HANDLERDATA, hp, + WEBOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)", + WEBOPT_CONNECTTIMEOUT, hp->timeout, + WEBOPT_COPYOPTS, TRUE, + WEBOPT_CONTENTLENGTH, 0, + WEBOPT_END)); + + ejsSetPropertyToString(ejs, thisObj, "status", "active"); + + return 0; +} + +/******************************************************************************/ +/* + * Called whenver the http object is deleted. + */ + +static int httpDestructor(Ejs *ejs, EjsVar *thisObj) +{ + HTTPControl *hp; + + /* + * If the httpCallback has run, then this property will not exist + */ + hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY); + + if (hp) { + cleanup(hp); + } + + return 0; +} + +/******************************************************************************/ +/* + * Stop the request immediately without calling the callback + */ + +static int stopProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + HTTPControl *hp; + + hp = ejsGetPropertyAsPtr(ejs, thisObj, EJS_HTTP_PROPERTY); + + if (hp) { + cleanup(hp); + } + + return 0; +} + +/******************************************************************************/ +/* + * Brew HTTP callback. Invoked for any return data. + */ + +static void brewCallback(HTTPControl *hp) +{ + Ejs *ejs; + EjsVar *thisObj; + ISource *source; + WebRespInfo *info; + char data[MPR_BUF_INCR]; + int bytes; + + mprAssert(hp); + mprAssert(hp->webResp); + + info = IWEBRESP_GetInfo(hp->webResp); + + if (info == 0) { + mprAssert(info); + /* should not happen */ + return; + } + + ejs = hp->ejs; + thisObj = hp->thisObj; + + if (! WEB_ERROR_SUCCEEDED(info->nCode)) { + ejsSetPropertyToString(ejs, thisObj, "status", "error"); + httpCallback(hp, info->nCode); + return; + } + + if (hp->timeout) { + if (mprGetTimeRemaining(hp, hp->requestStarted, hp->timeout) <= 0) { + ejsSetPropertyToString(ejs, thisObj, "status", "timeout"); + httpCallback(hp, 504); + return; + } + } + + /* + * Normal success + */ + source = info->pisMessage; + mprAssert(source); + + bytes = ISOURCE_Read(source, data, sizeof(data)); + + switch (bytes) { + case ISOURCE_WAIT: // No data yet + ISOURCE_Readable(source, hp->callback); + break; + + case ISOURCE_ERROR: + ejsSetPropertyToString(ejs, thisObj, "status", "error"); + httpCallback(hp, info->nCode); + break; + + case ISOURCE_END: + mprAddNullToBuf(hp->buf); + ejsSetPropertyToString(ejs, thisObj, "status", "complete"); + httpCallback(hp, info->nCode); + break; + + default: + if (bytes > 0) { + if (mprPutBlockToBuf(hp->buf, data, bytes) != bytes) { + ejsSetPropertyToString(ejs, thisObj, "status", "partialData"); + httpCallback(hp, 500); + } + } + ISOURCE_Readable(source, hp->callback); + break; + } +} + +/******************************************************************************/ +/* + * Invoke the HTTP completion method + */ + +static void httpCallback(HTTPControl *hp, int responseCode) +{ + Ejs *ejs; + EjsVar *thisObj, *callbackObj; + MprArray *args; + char *msg; + const char *callbackMethod; + + mprAssert(hp); + mprAssert(hp->webResp); + + thisObj = hp->thisObj; + ejs = hp->ejs; + + ejsSetPropertyToInteger(ejs, thisObj, "responseCode", responseCode); + if (mprGetBufLength(hp->buf) > 0) { + ejsSetPropertyToBinaryString(ejs, thisObj, "responseData", + mprGetBufStart(hp->buf), mprGetBufLength(hp->buf)); + } + + callbackObj = ejsGetPropertyAsVar(ejs, thisObj, "obj"); + callbackMethod = ejsGetPropertyAsString(ejs, thisObj, "method"); + + if (callbackObj != 0 && callbackMethod != 0) { + + args = mprCreateItemArray(ejs, EJS_INC_ARGS, EJS_MAX_ARGS); + mprAddItem(args, ejsDupVar(ejs, hp->thisObj, EJS_SHALLOW_COPY)); + + if (ejsRunMethod(ejs, callbackObj, callbackMethod, args) < 0) { + msg = ejsGetErrorMsg(ejs); + mprError(ejs, MPR_LOC, "HTTP callback failed. Details: %s", msg); + } + ejsFreeMethodArgs(ejs, args); + + } else if (ejsRunMethod(ejs, thisObj, "onComplete", 0) < 0) { + msg = ejsGetErrorMsg(ejs); + mprError(ejs, MPR_LOC, "HTTP onComplete failed. Details: %s", msg); + } + + cleanup(hp); +} + +/******************************************************************************/ +/* + * Cleanup + */ + +static void cleanup(HTTPControl *hp) +{ + Ejs *ejs; + MprApp *app; + int rc; + + mprAssert(hp); + mprAssert(hp->webResp); + + ejs = hp->ejs; + + if (hp->webResp) { + rc = IWEBRESP_Release(hp->webResp); + // mprAssert(rc == 0); + hp->webResp = 0; + } + + if (hp->callback) { + CALLBACK_Cancel(hp->callback); + mprFree(hp->callback); + hp->callback = 0; + } + + /* + * Once the property is deleted, then if the destructor runs, it will + * notice that the EJS_HTTP_PROPERTY is undefined. + */ + ejsDeleteProperty(ejs, hp->thisObj, EJS_HTTP_PROPERTY); + + /* + * Allow garbage collection to work on thisObj + */ + ejsMakeObjPermanent(hp->thisObj, 0); + ejsFreeVar(ejs, hp->thisObj); + + mprFree(hp->buf); + mprFree(hp->url); + + mprFree(hp); + + app = mprGetApp(ejs); + + + ISHELL_SendEvent(app->shell, (AEECLSID) app->classId, EVT_USER, 0, 0); +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineHTTPClass(Ejs *ejs) +{ + EjsVar *httpClass; + + httpClass = + ejsDefineClass(ejs, "HTTP", "Object", ejsHTTPConstructor); + if (httpClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the methods + */ + ejsDefineCMethod(ejs, httpClass, "fetch", fetchProc, 0); + ejsDefineCMethod(ejs, httpClass, "stop", stopProc, 0); + ejsDefineCMethod(ejs, httpClass, "setCallback", setCallback, 0); + +#if FUTURE + ejsDefineCMethod(ejs, httpClass, "put", put, 0); + ejsDefineCMethod(ejs, httpClass, "upload", upload, 0); + ejsDefineCMethod(ejs, httpClass, "addUploadFile", addUploadFile, 0); + ejsDefineCMethod(ejs, httpClass, "addPostData", addPostData, 0); + ejsDefineCMethod(ejs, httpClass, "setUserPassword", setUserPassword, 0); + ejsDefineCMethod(ejs, httpClass, "addCookie", addCookie, 0); +#endif + + /* + * Define properties + */ + ejsSetPropertyToString(ejs, httpClass, "status", "inactive"); + + /* This default should come from player.xml */ + + ejsSetPropertyToInteger(ejs, httpClass, "timeout", 30 * 1000); + ejsSetPropertyToInteger(ejs, httpClass, "responseCode", 0); + + return ejsObjHasErrors(httpClass) ? MPR_ERR_CANT_INITIALIZE: 0; +} + +/******************************************************************************/ + +void ejsTermHTTPClass(Ejs *ejs) +{ + IWeb *web; + int rc; + + web = (IWeb*) mprGetKeyValue(ejs, "bpWeb"); + if (web) { + rc = IWEB_Release(web); + mprAssert(rc == 0); + } +} + +#endif +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/ejsGC.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsGC.c new file mode 100644 index 0000000000..411975f80e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsGC.c @@ -0,0 +1,326 @@ +/* + * @file ejsGC.c + * @brief Garbage collector class for the EJS Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +#if (WIN || BREW_SIMULATOR) && BLD_DEBUG + +static int checkProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + _CrtCheckMemory(); + return 0; +} + +#endif +/******************************************************************************/ + +static int debugProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Bad args: debug(debugLevel)"); + return -1; + } + + ejsSetGCDebugLevel(ep, ejsVarToInteger(argv[0])); + return 0; +} + +/******************************************************************************/ +/* + * Print stats and dump objects + */ + +static int printStatsProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + bool leakStats; + + if (argc > 1) { + leakStats = ejsVarToInteger(argv[0]); + } else { + leakStats = 0; + } + +#if BLD_FEATURE_ALLOC_STATS + ejsPrintAllocReport(ep, 0); + + mprPrintAllocReport(mprGetApp(ep), leakStats, 0); +#endif + +#if BLD_DEBUG + ejsDumpObjects(ep); +#endif + + return 0; +} + +/******************************************************************************/ + +static int runProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc > 1) { + ejsError(ep, EJS_ARG_ERROR, "Bad args: run([quick])"); + return -1; + } + + if (argc == 1) { + ejsIncrementalCollectGarbage(ep); + } else { + ejsCollectGarbage(ep, -1); + } + return 0; +} + +/******************************************************************************/ + +static int usedMemoryProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ep, ejsGetUsedMemory(ep)); + return 0; +} + +/******************************************************************************/ + +static int allocatedMemoryProc(Ejs *ep, EjsVar *thisObj, int argc, + EjsVar **argv) +{ +#if BLD_FEATURE_ALLOC_STATS + ejsSetReturnValueToInteger(ep, ejsGetAllocatedMemory(ep)); +#endif + return 0; +} + +/******************************************************************************/ + +static int mprMemoryProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if BLD_FEATURE_ALLOC_STATS + ejsSetReturnValueToInteger(ep, mprGetAllocatedMemory(ep)); +#endif + return 0; +} + +/******************************************************************************/ + +static int peakMprMemoryProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if BLD_FEATURE_ALLOC_STATS + ejsSetReturnValueToInteger(ep, mprGetPeakAllocatedMemory(ep)); +#endif + return 0; +} + +/******************************************************************************/ + +static int getDebugLevel(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ep, ep->gc.debugLevel); + return 0; +} + +/******************************************************************************/ + +static int setDebugLevel(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + ep->gc.debugLevel= ejsVarToInteger(argv[0]); + return 0; +} + +/******************************************************************************/ + +static int getEnable(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToBoolean(ep, ep->gc.enable); + return 0; +} + +/******************************************************************************/ + +static int setEnable(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + ep->gc.enable= ejsVarToBoolean(argv[0]); + return 0; +} + +/******************************************************************************/ + +static int getDemandCollect(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToBoolean(ep, ep->gc.enableDemandCollect); + return 0; +} + +/******************************************************************************/ + +static int setDemandCollect(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + ep->gc.enableDemandCollect = ejsVarToBoolean(argv[0]); + return 0; +} + +/******************************************************************************/ + +static int getIdleCollect(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToBoolean(ep, ep->gc.enableIdleCollect); + return 0; +} + +/******************************************************************************/ + +static int setIdleCollect(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + ep->gc.enableIdleCollect = ejsVarToBoolean(argv[0]); + return 0; +} + +/******************************************************************************/ + +static int getWorkQuota(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ep, ep->gc.workQuota); + return 0; +} + +/******************************************************************************/ + +static int setWorkQuota(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int quota; + + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + quota = ejsVarToInteger(argv[0]); + if (quota < EJS_GC_MIN_WORK_QUOTA && quota != 0) { + ejsArgError(ep, "Bad work quota"); + return -1; + } + + ep->gc.workQuota = quota; + return 0; +} + +/******************************************************************************/ + +static int getMaxMemory(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ep, ep->gc.maxMemory); + return 0; +} + +/******************************************************************************/ + +static int setMaxMemory(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int maxMemory; + + if (argc != 1) { + ejsArgError(ep, "Bad arguments"); + return -1; + } + maxMemory = ejsVarToInteger(argv[0]); + if (maxMemory < 0) { + ejsArgError(ep, "Bad maxMemory"); + return -1; + } + + ep->gc.maxMemory = maxMemory; + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineGCClass(Ejs *ep) +{ + EjsVar *gcClass; + int flags; + + flags = EJS_NO_LOCAL; + + /* + * NOTE: We create the GC class and define static methods on it. There + * is no object instance + */ + gcClass = ejsDefineClass(ep, "System.GC", "Object", 0); + if (gcClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * MOB -- convert these to properties with accessors when available + */ + ejsDefineCMethod(ep, gcClass, "printStats", printStatsProc, flags); + ejsDefineCMethod(ep, gcClass, "run", runProc, flags); + + ejsDefineCMethod(ep, gcClass, "getUsedMemory", usedMemoryProc, flags); + ejsDefineCMethod(ep, gcClass, "getAllocatedMemory", allocatedMemoryProc, + flags); + ejsDefineCMethod(ep, gcClass, "getMprMemory", mprMemoryProc, flags); + ejsDefineCMethod(ep, gcClass, "getPeakMprMemory", peakMprMemoryProc, flags); + ejsDefineCMethod(ep, gcClass, "debug", debugProc, flags); + +#if (WIN || BREW_SIMULATOR) && BLD_DEBUG + ejsDefineCMethod(ep, gcClass, "check", checkProc, flags); +#endif + + ejsDefineCAccessors(ep, gcClass, "debugLevel", + getDebugLevel, setDebugLevel, flags); + + ejsDefineCAccessors(ep, gcClass, "enable", + getEnable, setEnable, flags); + + ejsDefineCAccessors(ep, gcClass, "demandCollect", + getDemandCollect, setDemandCollect, flags); + + ejsDefineCAccessors(ep, gcClass, "idleCollect", + getIdleCollect, setIdleCollect, flags); + + ejsDefineCAccessors(ep, gcClass, "workQuota", + getWorkQuota, setWorkQuota, flags); + + ejsDefineCAccessors(ep, gcClass, "maxMemory", + getMaxMemory, setMaxMemory, flags); + + return ejsObjHasErrors(gcClass) ? MPR_ERR_CANT_INITIALIZE : 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/ejsGlobal.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsGlobal.c new file mode 100755 index 0000000000..6ab8f867e1 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsGlobal.c @@ -0,0 +1,785 @@ +/* + * @file ejsGlobal.c + * @brief EJS support methods + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +#if BLD_FEATURE_EJS + +/******************************************************************************/ +/************************************* Code ***********************************/ +/******************************************************************************/ +/* + * assert(condition) + */ + +static int assertProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int b; + + if (argc < 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: assert(condition)"); + return -1; + } + b = ejsVarToBoolean(argv[0]); + if (b == 0) { + ejsError(ep, EJS_ASSERT_ERROR, "Assertion failure at line %d", + ejsGetLineNumber(ep)); + return -1; + } + ejsWriteVarAsBoolean(ep, ep->result, b); + return 0; +} + +/******************************************************************************/ +/* + * breakpoint(msg) + */ + +static int breakpointProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *buf; + + if (argc < 1) { + return 0; + } + buf = ejsVarToString(ep, argv[0]); + if (buf) { + mprBreakpoint(0, buf); + } + return 0; +} + +/******************************************************************************/ +/* + * basename(path) + */ + +static int basenameProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *path; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: basename(path)"); + return -1; + } + + path = ejsVarToString(ep, argv[0]); + if (path == 0) { + return MPR_ERR_MEMORY; + } + ejsSetReturnValueToString(ep, mprGetBaseName(path)); + + return 0; +} + +/******************************************************************************/ +/* + * stripext(path) + */ + +static int stripextProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *cp, *path, *stripPath; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: stripext(path)"); + return -1; + } + + path = ejsVarToString(ep, argv[0]); + if (path == 0) { + return MPR_ERR_MEMORY; + } + stripPath = mprStrdup(ep, path); + + if ((cp = strrchr(stripPath, '.')) != 0) { + *cp = '\0'; + } + + ejsSetReturnValueToString(ep, stripPath); + + mprFree(stripPath); + + return 0; +} + +/******************************************************************************/ +/* + * dirname(path) + */ + +static int dirnameProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *path; + char dirname[MPR_MAX_FNAME]; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: dirname(path)"); + return -1; + } + + path = ejsVarToString(ep, argv[0]); + if (path == 0) { + return MPR_ERR_MEMORY; + } + + ejsSetReturnValueToString(ep, + mprGetDirName(dirname, sizeof(dirname), path)); + + return 0; +} + +/******************************************************************************/ +/* + * trim(string) -- trim white space + */ + +static int trimProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *str, *buf, *cp; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: trim(string)"); + return -1; + } + + str = ejsVarToString(ep, argv[0]); + if (str == 0) { + return MPR_ERR_MEMORY; + } + str = buf = mprStrdup(ep, str); + + while (isspace(*str)) { + str++; + } + cp = &str[strlen(str) - 1]; + while (cp >= str) { + if (isspace(*cp)) { + *cp = '\0'; + } else { + break; + } + cp--; + } + + ejsSetReturnValueToString(ep, str); + + mprFree(buf); + + return 0; +} + +/******************************************************************************/ +/* + * Terminate the script + */ + +static int exitScript(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int status; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "usage: exit(status)"); + return -1; + } + status = (int) ejsVarToInteger(argv[0]); + ejsExit(ep, status); + + ejsWriteVarAsString(ep, ep->result, ""); + return 0; +} + +/******************************************************************************/ +/* + * include javascript libraries. + */ + +static int includeProc(Ejs *ep, EjsVar *thisObj, int argc, char **argv) +{ + int i; + + mprAssert(argv); + + for (i = 0; i < argc; i++) { + if (ejsEvalFile(ep, argv[i], 0) < 0) { + return -1; + } + } + return 0; +} + +/******************************************************************************/ +/* + * include javascript libraries at the global level + */ + +static int includeGlobalProc(Ejs *ep, EjsVar *thisObj, int argc, char **argv) +{ + int fid, i; + + mprAssert(argv); + + /* + * Create a new block and set the context to be the global scope + */ + fid = ejsSetBlock(ep, ep->global); + + for (i = 0; i < argc; i++) { + if (ejsEvalFile(ep, argv[i], 0) < 0) { + ejsCloseBlock(ep, fid); + return -1; + } + } + ejsCloseBlock(ep, fid); + return 0; +} + +/******************************************************************************/ +#if BLD_DEBUG +/* + * Print variables to stdout + */ + +static int printvProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *vp; + char *buf; + int i; + + for (i = 0; i < argc; ) { + vp = argv[i++]; + + /* mprPrintf(ep, "arg[%d] = ", i); */ + + buf = ejsVarToString(ep, vp); + + if (vp->propertyName == 0 || *vp->propertyName == '\0') { + mprPrintf(ep, "%s: ", buf); + + } else if (i < argc) { + mprPrintf(ep, "%s = %s, ", vp->propertyName, buf); + } else { + mprPrintf(ep, "%s = %s\n", vp->propertyName, buf); + } + } + return 0; +} + +#endif +/******************************************************************************/ +/* + * Print the args to stdout + */ + +static int printProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *buf; + int i; + + for (i = 0; i < argc; i++) { + buf = ejsVarToString(ep, argv[i]); + mprPrintf(ep, "%s", buf); + } + return 0; +} + +/******************************************************************************/ +/* + * println + */ + +static int printlnProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + printProc(ep, thisObj, argc, argv); + mprPrintf(ep, "\n"); + return 0; +} + +/******************************************************************************/ +#if FUTURE +/* + * sprintf + */ + +static int sprintfProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + va_list ap; + char *buf; + void **args; + int result; + + if (argc <= 1) { + ejsError(ep, EJS_ARG_ERROR, "Usage: sprintf(fmt, [args ...])"); + return -1; + } + + args = mprAlloc(ep, sizeof(void*) * (argc - 1)); + if (args == 0) { + mprAssert(args); + return -1; + } + + for (i = 1; i < argc; i++) { + args[i - 1] = argv[i]); + } + + va_start(ap, fmt); + *buf = 0; + result = inner(0, &buf, MPR_MAX_STRING, fmt, args); + va_end(ap); + + ejsSetReturnValueToString(ep, buf); + + mprFree(buf); + return 0; +} + +/******************************************************************************/ + +inner(const char *fmt, void **args) +{ + va_list ap; + + va_start(ap, fmt); + *buf = 0; + mprSprintfCore(ctx, &buf, maxSize, fmt, ap, MPR_PRINTF_ARGV); + va_end(ap); +} + +#endif +/******************************************************************************/ +/* + * sleep + */ + +static int sleepProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Usage: sleep(milliseconds)"); + return -1; + } + mprSleep(ep, ejsVarToInteger(argv[0])); + return 0; +} + +/******************************************************************************/ +/* + * sort properties + * FUTURE -- should have option to sort object based on a given property value + * ascending or descending + * Usage: sort(object, order = ascending, property = 0); + */ + +static int sortProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *property; + int error, order; + + error = 0; + property = 0; + + /* + * Default order is increasing + */ + order = 1; + + if (argc < 1 || argc > 3 || !ejsVarIsObject(argv[0])) { + error++; + } + + if (argc >= 2) { + order = ejsVarToInteger(argv[1]); + } + + /* + * If property is not defined, it sorts the properties in the object + */ + if (argc == 3) { + if (! ejsVarIsString(argv[2])) { + error++; + } else { + property = argv[2]->string; + } + } + + if (error) { + ejsError(ep, EJS_ARG_ERROR, "Usage: sort(object, [order], [property])"); + return -1; + } + ejsSortProperties(ep, argv[0], 0, property, order); + return 0; +} + +/******************************************************************************/ +/* + * Get a time mark + * MOB -- WARNING: this can overflow. OK on BREW, but other O/Ss it may have + * overflowed on the first call. It should be renamed. + * MOB -- replace with proper Date. + */ + +static int timeProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + MprTime now; + + mprGetTime(ep, &now); +#if WIN || LINUX || SOLARIS +{ + /* MOB -- poor hack */ + static MprTime initial; + if (initial.sec == 0) { + initial = now; + } + now.sec -= initial.sec; + + if (initial.msec > now.msec) { + now.msec = now.msec + 1000 - initial.msec; + now.sec--; + } else { + now.msec -= initial.msec; + } +} +#endif + /* MOB -- this can overflow */ + ejsSetReturnValueToInteger(ep, now.sec * 1000 + now.msec); + return 0; +} + +/******************************************************************************/ +/* + * MOB -- Temporary Get the date (time since Jan 6, 1980 GMT + */ + +static int dateProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ +#if BREW + uint now; + + now = GETTIMESECONDS(); + ejsSetReturnValueToInteger(ep, now); +#endif + return 0; +} + +/******************************************************************************/ +/* + * strlen(string) + */ + +static int strlenProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *buf; + int len; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Usage: strlen(var)"); + return -1; + } + + len = 0; + if (! ejsVarIsString(argv[0])) { + buf = ejsVarToString(ep, argv[0]); + if (buf) { + len = strlen(buf); + } + + } else { + len = argv[0]->length; + } + + ejsSetReturnValueToInteger(ep, len); + return 0; +} + +/******************************************************************************/ +/* + * toint(num) + */ + +static int tointProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int i; + + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Usage: toint(number)"); + return -1; + } + + i = ejsVarToInteger(argv[0]); + + ejsSetReturnValueToInteger(ep, i); + return 0; +} + +/******************************************************************************/ +/* + * string strstr(string, pat) + */ + +static int strstrProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + char *str, *pat; + char *s; + int strAlloc; + + if (argc != 2) { + ejsError(ep, EJS_ARG_ERROR, "Usage: strstr(string, pat)"); + return -1; + } + + str = ejsVarToString(ep, argv[0]); + + strAlloc = ep->castAlloc; + ep->castTemp = 0; + + pat = ejsVarToString(ep, argv[1]); + + s = strstr(str, pat); + + if (s == 0) { + ejsSetReturnValueToUndefined(ep); + } else { + ejsSetReturnValueToString(ep, s); + } + + if (strAlloc) { + mprFree(str); + } + + return 0; +} + +/******************************************************************************/ +/* + * Trace + */ + +static int traceProc(Ejs *ep, EjsVar *thisObj, int argc, char **argv) +{ + if (argc == 1) { + mprLog(ep, 0, "%s", argv[0]); + + } else if (argc == 2) { + mprLog(ep, atoi(argv[0]), "%s", argv[1]); + + } else { + ejsError(ep, EJS_ARG_ERROR, "Usage: trace([level], message)"); + return -1; + } + ejsWriteVarAsString(ep, ep->result, ""); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate a sub-script. It is evaluated in the same variable scope as + * the calling script / method. + */ + +static int evalScriptProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + EjsVar *arg; + int i; + + ejsWriteVarAsUndefined(ep, ep->result); + + for (i = 0; i < argc; i++) { + arg = argv[i]; + if (arg->type != EJS_TYPE_STRING) { + continue; + } + if (ejsEvalScript(ep, arg->string, 0) < 0) { + return -1; + } + } + /* + * Return with the value of the last expression + */ + return 0; +} + +/******************************************************************************/ + +/* MOB -- need a real datatype returning int, int64, etc */ + +static int typeofProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const struct { + EjsType type; + const char *name; + } types[] = { + { EJS_TYPE_UNDEFINED, "undefined" }, +#if EJS_ECMA_STND + { EJS_TYPE_NULL, "object" }, +#else + { EJS_TYPE_NULL, "null" }, +#endif + { EJS_TYPE_BOOL, "boolean" }, + { EJS_TYPE_CMETHOD, "function" }, + { EJS_TYPE_FLOAT, "number" }, + { EJS_TYPE_INT, "number" }, + { EJS_TYPE_INT64, "number" }, + { EJS_TYPE_OBJECT, "object" }, + { EJS_TYPE_METHOD, "function" }, + { EJS_TYPE_STRING, "string" }, + { EJS_TYPE_STRING_CMETHOD, "function" }, + { EJS_TYPE_PTR, "pointer" } + }; + const char *type; + int i; + + type = NULL; + if (argc != 1) { + ejsError(ep, EJS_ARG_ERROR, "Bad args: typeof(var)"); + return -1; + } + + for (i = 0; i < MPR_ARRAY_SIZE(types); i++) { + if (argv[0]->type == types[i].type) { + type = types[i].name; + break; + } + } + if (type == NULL) { + mprAssert(type); + return -1; + } + + ejsSetReturnValueToString(ep, type); + return 0; +} + +/******************************************************************************/ +/* + * Define the standard properties and methods inherited by all interpreters + * Obj is set to the global class in the default interpreter. When an + * interpreter attempts to write to any property, a copy will be written + * into the interpeters own global space. This is like a "copy-on-write". + */ + +int ejsDefineGlobalProperties(Ejs *ep) +{ + EjsVar *obj; + + obj = ep->service->globalClass; + mprAssert(obj); + + ejsSetPropertyToNull(ep, obj, "null"); + ejsSetPropertyToUndefined(ep, obj, "undefined"); + ejsSetPropertyToBoolean(ep, obj, "true", 1); + ejsSetPropertyToBoolean(ep, obj, "false", 0); + +#if BLD_FEATURE_FLOATING_POINT + { + /* MOB. Fix. This generates warnings on some systems. + This is intended. */ + double d = 0.0; + double e = 0.0; + ejsSetPropertyToFloat(ep, obj, "NaN", e / d); + + d = MAX_FLOAT; + ejsSetPropertyToFloat(ep, obj, "Infinity", d * d); + } +#endif + +#if BLD_FEATURE_LEGACY_API + /* + * DEPRECATED: 2.0. + * So that ESP/ASP can ignore "language=javascript" statements + */ + ejsSetPropertyToInteger(ep, obj, "javascript", 0); +#endif + + /* + * Extension methods. We go directly to the mpr property APIs for speed. + * Flags will cause the callbacks to be supplied the Ejs handle. + */ + ejsDefineCMethod(ep, obj, "assert", assertProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "breakpoint", breakpointProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "basename", basenameProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "dirname", dirnameProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "stripext", stripextProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "trim", trimProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "eval", evalScriptProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "exit", exitScript, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "print", printProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "println", printlnProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "sleep", sleepProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "sort", sortProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "time", timeProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "date", dateProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "strlen", strlenProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "strstr", strstrProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "typeof", typeofProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "toint", tointProc, EJS_NO_LOCAL); + + ejsDefineStringCMethod(ep, obj, "include", includeProc, EJS_NO_LOCAL); + ejsDefineStringCMethod(ep, obj, "includeGlobal", includeGlobalProc, + EJS_NO_LOCAL); + ejsDefineStringCMethod(ep, obj, "trace", traceProc, EJS_NO_LOCAL); + +#if BLD_DEBUG + ejsDefineCMethod(ep, obj, "printv", printvProc, EJS_NO_LOCAL); +#endif + +#if FUTURE + ejsDefineCMethod(ep, obj, "printf", printfProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, obj, "sprintf", sprintfProc, EJS_NO_LOCAL); +#endif + + if (ejsObjHasErrors(obj)) { + return MPR_ERR_CANT_INITIALIZE; + } + 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-2.0/ejs/system/ejsSystem.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystem.c new file mode 100644 index 0000000000..e035e1c740 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystem.c @@ -0,0 +1,112 @@ +/* + * @file ejsSystem.c + * @brief System class for the EJS Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +#if UNUSED +/* + * function int random() + */ + +static int randomProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "random()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function void yield() + */ + +static int yieldProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "yield()\n"); + return 0; +} + +/******************************************************************************/ +/* + * function void sleep(int milliSeconds) + */ + +static int sleepProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ep, "sleep()\n"); + return 0; +} + +#endif +/******************************************************************************/ +/* + * function void exit(int status) + * + * Exit the widget with the given status. All JavaScript processing ceases. + */ + +static int exitProc(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv) +{ + int status; + + status = 0; + if ((argc == 1) && ejsVarIsInteger(argv[0])) { + status = argv[0]->integer; + } + ejsExit(ep, status); + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineSystemClass(Ejs *ep) +{ + EjsVar *systemClass; + + /* + * We create the system class and define static methods on it. + * NOTE: There is no object instance + */ + systemClass = ejsDefineClass(ep, "System", "Object", 0); + if (systemClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + ejsDefineCMethod(ep, systemClass, "exit", exitProc, EJS_NO_LOCAL); + +#if UNUSED + ejsDefineCMethod(ep, systemClass, "random", randomProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, systemClass, "yield", yieldProc, EJS_NO_LOCAL); + ejsDefineCMethod(ep, systemClass, "sleep", sleepProc, EJS_NO_LOCAL); + + /* + * Define properties + */ + ejsSetPropertyToString(systemClass, "name", ""); +#endif + + return ejsObjHasErrors(systemClass) ? MPR_ERR_CANT_INITIALIZE : 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/ejsSystemApp.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemApp.c new file mode 100644 index 0000000000..e2f1ceb363 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemApp.c @@ -0,0 +1,49 @@ +/* + * @file ejsSystemApp.c + * @brief App class + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software Inc, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/************************************ Code ************************************/ + +int ejsDefineAppClass(Ejs *ep) +{ + EjsVar *appClass; + + appClass = ejsDefineClass(ep, "System.App", "Object", 0); + if (appClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define properties + */ + ejsSetPropertyToString(ep, appClass, "name", BLD_PRODUCT); + ejsSetPropertyToString(ep, appClass, "title", BLD_NAME); + ejsSetPropertyToString(ep, appClass, "version", BLD_VERSION); + + /* + * Command line arguments + */ + ejsSetPropertyToNull(ep, appClass, "args"); + + return ejsObjHasErrors(appClass) ? MPR_ERR_CANT_INITIALIZE : 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/ejsSystemDebug.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemDebug.c new file mode 100644 index 0000000000..5a011e2a2d --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemDebug.c @@ -0,0 +1,60 @@ +/* + * @file ejsSystemDebug.c + * @brief System.Debug class + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function bool isDebugMode() + * MOB -- convert to accessor + */ + +static int isDebugMode(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsTrace(ejs, "isDebugMode()\n"); + ejsSetReturnValueToInteger(ejs, mprGetDebugMode(ejs)); + return 0; +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineDebugClass(Ejs *ejs) +{ + EjsVar *systemDebugClass; + + systemDebugClass = ejsDefineClass(ejs, "System.Debug", "Object", 0); + if (systemDebugClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the class methods + */ + ejsDefineCMethod(ejs, systemDebugClass, "isDebugMode", isDebugMode, + EJS_NO_LOCAL); + + return ejsObjHasErrors(systemDebugClass) ? MPR_ERR_CANT_INITIALIZE : 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/ejsSystemLog.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemLog.c new file mode 100644 index 0000000000..66467f8fcf --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemLog.c @@ -0,0 +1,163 @@ +/* + * @file ejsSystemLog.c + * @brief System.Log class for the EJS Object Model + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/*********************************** Usage ************************************/ +/* + * System.Log.setLog(path); + * System.Log.enable; + */ +/******************************************************************************/ + +static void logHandler(MPR_LOC_DEC(ctx, loc), int flags, int level, + const char *msg) +{ + MprApp *app; + char *buf; + int len; + + app = mprGetApp(ctx); + if (app->logFile == 0) { + return; + } + + if (flags & MPR_LOG_SRC) { + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Log %d: %s\n", level, msg); + + } else if (flags & MPR_ERROR_SRC) { + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Error: %s\n", msg); + + } else if (flags & MPR_FATAL_SRC) { + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Fatal: %s\n", msg); + + } else if (flags & MPR_ASSERT_SRC) { +#if BLD_FEATURE_ALLOC_LEAK_TRACK + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Assertion %s, failed at %s\n", + msg, loc); +#else + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "Assertion %s, failed\n", msg); +#endif + + } else if (flags & MPR_RAW) { + /* OPT */ + len = mprAllocSprintf(MPR_LOC_PASS(ctx, loc), &buf, 0, + "%s", msg); + + } else { + return; + } + + mprPuts(app->logFile, buf, len); + + mprFree(buf); +} + +/******************************************************************************/ +/************************************ Methods *********************************/ +/******************************************************************************/ +/* + * function int setLog(string path) + */ + +static int setLog(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + const char *path; + MprFile *file; + MprApp *app; + + if (argc != 1 || !ejsVarIsString(argv[0])) { + ejsArgError(ejs, "Usage: setLog(path)"); + return -1; + } + + app = mprGetApp(ejs); + + /* + * Ignore errors if we can't create the log file. + * Use the app context so this will live longer than the interpreter + * MOB -- this leaks files. + */ + path = argv[0]->string; + file = mprOpen(app, path, O_CREAT | O_TRUNC | O_WRONLY, 0664); + if (file) { + app->logFile = file; + mprSetLogHandler(ejs, logHandler); + } + mprLog(ejs, 0, "Test log"); + + return 0; +} + +/******************************************************************************/ +#if UNUSED + +static int enableSetAccessor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + if (argc != 1) { + ejsArgError(ejs, "Usage: set(value)"); + return -1; + } + ejsSetProperty(ejs, thisObj, "_enabled", argv[0]); + return 0; +} + +/******************************************************************************/ + +static int enableGetAccessor(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValue(ejs, ejsGetPropertyAsVar(ejs, thisObj, "_enabled")); + return 0; +} + +#endif +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineLogClass(Ejs *ejs) +{ + EjsVar *logClass; + + logClass = ejsDefineClass(ejs, "System.Log", "Object", 0); + if (logClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + ejsDefineCMethod(ejs, logClass, "setLog", setLog, EJS_NO_LOCAL); + +#if UNUSED + EjsProperty *pp; + ejsDefineCAccessors(ejs, logClass, "enable", enableSetAccessor, + enableGetAccessor, EJS_NO_LOCAL); + + pp = ejsSetPropertyToBoolean(ejs, logClass, "_enabled", 0); + ejsMakePropertyEnumerable(pp, 0); +#endif + + return ejsObjHasErrors(logClass) ? MPR_ERR_CANT_INITIALIZE : 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/ejs/system/ejsSystemMemory.c b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemMemory.c new file mode 100755 index 0000000000..d10787b1b4 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/ejs/system/ejsSystemMemory.c @@ -0,0 +1,174 @@ +/* + * @file ejsSystemMemory.c + * @brief System.Memory class + */ +/********************************** Copyright *********************************/ +/* + * Copyright (c) Mbedthis Software LLC, 2005-2006. All Rights Reserved. + */ +/********************************** Includes **********************************/ + +#include "ejs.h" + +/****************************** Forward Declarations***************************/ + +static uint getUsedMemory(Ejs *ejs); + +/******************************************************************************/ +/*********************************** Methods *********************************/ +/******************************************************************************/ + +static int getUsedMemoryProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ejs, getUsedMemory(ejs)); + return 0; +} + +/******************************************************************************/ + +static int getUsedStackProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv) +{ + ejsSetReturnValueToInteger(ejs, mprStackSize(ejs)); + return 0; +} + +/******************************************************************************/ +/* + * Public function + */ + +uint ejsGetAvailableMemory(Ejs *ejs) +{ + EjsVar *memoryClass; + uint ram; + + memoryClass = ejsGetClass(ejs, 0, "System.Memory"); + + ram = ejsGetPropertyAsInteger(ejs, memoryClass, "ram"); + return ram - getUsedMemory(ejs); +} + +/******************************************************************************/ + +static int getAvailableMemoryProc(Ejs *ejs, EjsVar *thisObj, int argc, + EjsVar **argv) +{ + EjsVar *memoryClass; + uint ram; + + memoryClass = ejsGetClass(ejs, 0, "System.Memory"); + + ram = ejsGetPropertyAsInteger(ejs, memoryClass, "ram"); +#if BREW + ejsSetReturnValueToInteger(ejs, ram - getUsedMemory(ejs)); +#else + ejsSetReturnValueToInteger(ejs, 0); +#endif + return 0; +} + +/******************************************************************************/ + +static uint getUsedMemory(Ejs *ejs) +{ +#if BREW + MprApp *app; + IHeap *heap; + uint memInUse; + void *ptr; + + app = mprGetApp(ejs); + ptr = (void*) &heap; + if (ISHELL_CreateInstance(app->shell, AEECLSID_HEAP, (void**) ptr) + == SUCCESS) { + memInUse = IHEAP_GetMemStats(heap); + IHEAP_Release(heap); + } else { + memInUse = 0; + } + + return memInUse; +#else + return 0; +#endif +} + +/******************************************************************************/ +/******************************** Initialization ******************************/ +/******************************************************************************/ + +int ejsDefineMemoryClass(Ejs *ejs) +{ + EjsVar *memoryClass; + uint used; + +#if BREW + MprApp *app; + AEEDeviceInfo *info; + + /* + * Get needed information for class properties. + */ + info = mprAllocType(ejs, AEEDeviceInfo); + if (info == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + info->wStructSize = sizeof(AEEDeviceInfo); + app = mprGetApp(ejs); + ISHELL_GetDeviceInfo(app->shell, info); + used = getUsedMemory(ejs); +#else + used = 0; +#endif + + /* + * Create the class + */ + memoryClass = ejsDefineClass(ejs, "System.Memory", "Object", 0); + if (memoryClass == 0) { + return MPR_ERR_CANT_INITIALIZE; + } + + /* + * Define the class methods + * MOB -- change to accessors + */ + ejsDefineCMethod(ejs, memoryClass, "getUsedStack", getUsedStackProc, + EJS_NO_LOCAL); + ejsDefineCMethod(ejs, memoryClass, "getUsedMemory", getUsedMemoryProc, + EJS_NO_LOCAL); + ejsDefineCMethod(ejs, memoryClass, "getAvailableMemory", + getAvailableMemoryProc, EJS_NO_LOCAL); + + /* + * Define properties + */ +#if BREW + ejsSetPropertyToInteger(ejs, memoryClass, "ram", info->dwRAM); + +#if UNUSED + /* MOB -- delete this */ + ejsSetPropertyToInteger(ejs, memoryClass, "available", + info->dwRAM - used); +#endif +#endif + +#if UNUSED + ejsSetPropertyToInteger(ejs, memoryClass, "used", used); + ejsSetPropertyToInteger(ejs, memoryClass, "flash", 0); +#endif + + return ejsObjHasErrors(memoryClass) ? MPR_ERR_CANT_INITIALIZE : 0; +} + +/******************************************************************************/ + +/* + * 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-2.0/exml/Makefile b/source4/lib/appweb/ejs-2.0/exml/Makefile new file mode 100644 index 0000000000..663e65ed53 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/exml/Makefile @@ -0,0 +1,42 @@ +# +# Makefile for Embedded XML (EXML) +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +# +# EXML may be linked into shared handlers so we must build the objects both +# shared and static. +# +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I../mpr + +include make.dep + +ifeq ($(BLD_FEATURE_TEST),1) +POST_DIRS := test +endif + +TARGETS += $(BLD_BIN_DIR)/libexml$(BLD_LIB) + +ifeq ($(BLD_FEATURE_XML),1) +compileExtra: $(TARGETS) +endif + +# MOB -- remove when FEATURE_XML is defined +compileExtra: $(TARGETS) + +$(BLD_BIN_DIR)/libexml$(BLD_LIB): $(FILES) + @bld --library $(BLD_BIN_DIR)/libexml \ + --objectsDir $(BLD_OBJ_DIR) --objectList files --libs mpr + +cleanExtra: + @echo "rm -f $(TARGETS)" | $(BLDOUT) + @rm -f $(TARGETS) + @rm -f $(BLD_BIN_DIR)/libexml.* + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/exml/exml.h b/source4/lib/appweb/ejs-2.0/exml/exml.h new file mode 100644 index 0000000000..44c50a56b9 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/exml/exml.h @@ -0,0 +1,94 @@ +/* + * exml.h -- Embedded Xml Parser header + * + * Copyright (c) Mbedthis Software, LLC, 2003-2003. All Rights Reserved. -- MOB + */ + +#ifndef _h_EXML +#define _h_EXML 1 + +/******************************** Description *********************************/ + +#include "mpr.h" + +/********************************** Defines ***********************************/ + +#if BLD_FEATURE_SQUEEZE + #define EXML_BUFSIZE 512 /* Read buffer size */ +#else + #define EXML_BUFSIZE 1024 /* Read buffer size */ +#endif + +/* + * XML parser states. The states that are passed to the user handler have + * "U" appended to the comment. The error states (ERR and EOF) must be + * negative. + */ +#define EXML_ERR -1 /* Error */ +#define EXML_EOF -2 /* End of input */ +#define EXML_BEGIN 1 /* Before next tag */ +#define EXML_AFTER_LS 2 /* Seen "<" */ +#define EXML_COMMENT 3 /* Seen "<!--" (usr) U */ +#define EXML_NEW_ELT 4 /* Seen "<tag" (usr) U */ +#define EXML_ATT_NAME 5 /* Seen "<tag att" */ +#define EXML_ATT_EQ 6 /* Seen "<tag att" = */ +#define EXML_NEW_ATT 7 /* Seen "<tag att = "val" U */ +#define EXML_SOLO_ELT_DEFINED 8 /* Seen "<tag../>" U */ +#define EXML_ELT_DEFINED 9 /* Seen "<tag...>" U */ +#define EXML_ELT_DATA 10 /* Seen "<tag>....<" U */ +#define EXML_END_ELT 11 /* Seen "<tag>....</tag>" U */ +#define EXML_PI 12 /* Seen "<?processingInst" U */ +#define EXML_CDATA 13 /* Seen "<![CDATA[" U */ + +/* + * Lex tokens + */ +typedef enum ExmlToken { + TOKEN_ERR, + TOKEN_TOO_BIG, /* Token is too big */ + TOKEN_CDATA, + TOKEN_COMMENT, + TOKEN_INSTRUCTIONS, + TOKEN_LS, /* "<" -- Opening a tag */ + TOKEN_LS_SLASH, /* "</" -- Closing a tag */ + TOKEN_GR, /* ">" -- End of an open tag */ + TOKEN_SLASH_GR, /* "/>" -- End of a solo tag */ + TOKEN_TEXT, + TOKEN_EQ, + TOKEN_EOF, + TOKEN_SPACE, +} ExmlToken; + +struct Exml; +typedef int (*ExmlHandler)(struct Exml *xp, int state, + const char *tagName, const char* attName, const char* value); +typedef int (*ExmlInputStream)(struct Exml *xp, void *arg, char *buf, int size); + +/* + * Per XML session structure + */ +typedef struct Exml { + ExmlHandler handler; /* Callback function */ + ExmlInputStream readFn; /* Read data function */ + MprBuf *inBuf; /* Input data queue */ + MprBuf *tokBuf; /* Parsed token buffer */ + int quoteChar; /* XdbAtt quote char */ + int lineNumber; /* Current line no for debug */ + void *parseArg; /* Arg passed to exmlParse() */ + void *inputArg; /* Arg passed to exmlSetInputStream() */ + char *errMsg; /* Error message text */ +} Exml; + +extern Exml *exmlOpen(MprCtx ctx, int initialSize, int maxSize); +extern void exmlClose(Exml *xp); +extern void exmlSetParserHandler(Exml *xp, ExmlHandler h); +extern void exmlSetInputStream(Exml *xp, ExmlInputStream s, void *arg); +extern int exmlParse(Exml *xp); +extern void exmlSetParseArg(Exml *xp, void *parseArg); +extern void *exmlGetParseArg(Exml *xp); +extern const char *exmlGetErrorMsg(Exml *xp); +extern int exmlGetLineNumber(Exml *xp); + +/******************************************************************************/ + +#endif /* _h_EXML */ diff --git a/source4/lib/appweb/ejs-2.0/exml/exmlParser.c b/source4/lib/appweb/ejs-2.0/exml/exmlParser.c new file mode 100644 index 0000000000..14871411a6 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/exml/exmlParser.c @@ -0,0 +1,752 @@ +/* + * exml.c -- A simple SAX style XML parser + */ + +/********************************* Description ********************************/ +/* + * This is a recursive descent parser for XML text files. It is a one-pass + * simple parser that invokes a user supplied callback for key tokens in the + * XML file. The user supplies a read function so that XML files can be parsed + * from disk or in-memory. + */ +/********************************** Includes **********************************/ + +#include "exml.h" + +/****************************** Forward Declarations **************************/ +/* MOB -- FIX */ +#if BLD_FEATURE_EXML || 1 + +static int parseNext(Exml *xp, int state); +static ExmlToken getToken(Exml *xp, int state); +static int getNextChar(Exml *xp); +static int scanFor(Exml *xp, char *str); +static int putLastChar(Exml *xp, int c); +static void error(Exml *xp, char *fmt, ...); +static void trimToken(Exml *xp); + +/************************************ Code ************************************/ + +Exml *exmlOpen(MprCtx ctx, int initialSize, int maxSize) +{ + Exml *xp; + + xp = mprAllocTypeZeroed(ctx, Exml); + + xp->inBuf = mprCreateBuf(xp, EXML_BUFSIZE, EXML_BUFSIZE); + xp->tokBuf = mprCreateBuf(xp, initialSize, maxSize); + + return xp; +} + +/******************************************************************************/ + +void exmlClose(Exml *xp) +{ + mprAssert(xp); + + mprFree(xp); +} + +/******************************************************************************/ + +void exmlSetParserHandler(Exml *xp, ExmlHandler h) +{ + mprAssert(xp); + + xp->handler = h; +} + +/******************************************************************************/ + +void exmlSetInputStream(Exml *xp, ExmlInputStream s, void *arg) +{ + mprAssert(xp); + + xp->readFn = s; + xp->inputArg = arg; +} + +/******************************************************************************/ +/* + * Set the parse arg + */ + +void exmlSetParseArg(Exml *xp, void *parseArg) +{ + mprAssert(xp); + + xp->parseArg = parseArg; +} + +/******************************************************************************/ +/* + * Set the parse arg + */ + +void *exmlGetParseArg(Exml *xp) +{ + mprAssert(xp); + + return xp->parseArg; +} + +/******************************************************************************/ +/* + * Parse an XML file. Return 0 for success, -1 for error. + */ + +int exmlParse(Exml *xp) +{ + mprAssert(xp); + + return parseNext(xp, EXML_BEGIN); +} + +/******************************************************************************/ +/* + * XML parser. This is a recursive descent parser. Return -1 for errors, 0 for + * EOF and 1 if there is still more data to parse. + */ + +static int parseNext(Exml *xp, int state) +{ + ExmlHandler handler; + ExmlToken token; + MprBuf *tokBuf; + char *tname, *aname; + int rc; + + mprAssert(state >= 0); + + tokBuf = xp->tokBuf; + handler = xp->handler; + tname = aname = 0; + rc = 0; + + /* + * In this parse loop, the state is never assigned EOF or ERR. In + * such cases we always return EOF or ERR. + */ + while (1) { + + token = getToken(xp, state); + + if (token == TOKEN_TOO_BIG) { + error(xp, "XML token is too big"); + goto err; + } + + switch (state) { + case EXML_BEGIN: /* ------------------------------------------ */ + /* + * Expect to get an element, comment or processing instruction + */ + switch (token) { + case TOKEN_EOF: + goto exit; + + case TOKEN_LS: + /* + * Recurse to handle the new element, comment etc. + */ + rc = parseNext(xp, EXML_AFTER_LS); + if (rc < 0) { + goto exit; + } + break; + + default: + error(xp, "Syntax error"); + goto err; + } + break; + + case EXML_AFTER_LS: /* ------------------------------------------ */ + switch (token) { + case TOKEN_COMMENT: + state = EXML_COMMENT; + rc = (*handler)(xp, state, "!--", 0, mprGetBufStart(tokBuf)); + if (rc < 0) { + goto err; + } + rc = 1; + goto exit; + + case TOKEN_CDATA: + state = EXML_CDATA; + rc = (*handler)(xp, state, "!--", 0, mprGetBufStart(tokBuf)); + if (rc < 0) { + goto err; + } + rc = 1; + goto exit; + + case TOKEN_INSTRUCTIONS: + /* Just ignore processing instructions */ + rc = 1; + goto exit; + + case TOKEN_TEXT: + state = EXML_NEW_ELT; + tname = mprStrdup(xp, mprGetBufStart(tokBuf)); + if (tname == 0) { + rc = MPR_ERR_MEMORY; + goto exit; + } + rc = (*handler)(xp, state, tname, 0, 0); + if (rc < 0) { + goto err; + } + break; + + default: + error(xp, "Syntax error"); + goto err; + } + break; + + case EXML_NEW_ELT: /* ------------------------------------------ */ + /* + * We have seen the opening "<element" for a new element and have + * not yet seen the terminating ">" of the opening element. + */ + switch (token) { + case TOKEN_TEXT: + /* + * Must be an attribute name + */ + aname = mprStrdup(xp, mprGetBufStart(tokBuf)); + token = getToken(xp, state); + if (token != TOKEN_EQ) { + error(xp, "Missing assignment for attribute \"%s\"", aname); + goto err; + } + + token = getToken(xp, state); + if (token != TOKEN_TEXT) { + error(xp, "Missing value for attribute \"%s\"", aname); + goto err; + } + state = EXML_NEW_ATT; + rc = (*handler)(xp, state, tname, aname, + mprGetBufStart(tokBuf)); + if (rc < 0) { + goto err; + } + state = EXML_NEW_ELT; + break; + + case TOKEN_GR: + /* + * This is ">" the termination of the opening element + */ + if (*tname == '\0') { + error(xp, "Missing element name"); + goto err; + } + + /* + * Tell the user that the opening element is now complete + */ + state = EXML_ELT_DEFINED; + rc = (*handler)(xp, state, tname, 0, 0); + if (rc < 0) { + goto err; + } + state = EXML_ELT_DATA; + break; + + case TOKEN_SLASH_GR: + /* + * If we see a "/>" then this is a solo element + */ + if (*tname == '\0') { + error(xp, "Missing element name"); + goto err; + } + state = EXML_SOLO_ELT_DEFINED; + rc = (*handler)(xp, state, tname, 0, 0); + if (rc < 0) { + goto err; + } + rc = 1; + goto exit; + + default: + error(xp, "Syntax error"); + goto err; + } + break; + + case EXML_ELT_DATA: /* -------------------------------------- */ + /* + * We have seen the full opening element "<name ...>" and now + * await data or another element. + */ + if (token == TOKEN_LS) { + /* + * Recurse to handle the new element, comment etc. + */ + rc = parseNext(xp, EXML_AFTER_LS); + if (rc < 0) { + goto exit; + } + break; + + } else if (token == TOKEN_LS_SLASH) { + state = EXML_END_ELT; + break; + + } else if (token != TOKEN_TEXT) { + goto err; + } + if (mprGetBufLength(tokBuf) > 0) { + /* + * Pass the data between the element to the user + */ + rc = (*handler)(xp, state, tname, 0, mprGetBufStart(tokBuf)); + if (rc < 0) { + goto err; + } + } + break; + + case EXML_END_ELT: /* -------------------------------------- */ + if (token != TOKEN_TEXT) { + error(xp, "Missing closing element name for \"%s\"", tname); + goto err; + } + /* + * The closing element name must match the opening element name + */ + if (strcmp(tname, mprGetBufStart(tokBuf)) != 0) { + error(xp, + "Closing element name \"%s\" does not match on line %d" + "opening name \"%s\"", + mprGetBufStart(tokBuf), xp->lineNumber, tname); + goto err; + } + rc = (*handler)(xp, state, tname, 0, 0); + if (rc < 0) { + goto err; + } + if (getToken(xp, state) != TOKEN_GR) { + error(xp, "Syntax error"); + goto err; + } + return 1; + + case EXML_EOF: /* ---------------------------------------------- */ + goto exit; + + case EXML_ERR: /* ---------------------------------------------- */ + default: + goto err; + } + } + mprAssert(0); + +err: + rc = -1; + +exit: + mprFree(tname); + mprFree(aname); + + return rc; +} + +/******************************************************************************/ +/* + * Lexical analyser for XML. Return the next token reading input as required. + * It uses a one token look ahead and push back mechanism (LAR1 parser). + * Text token identifiers are left in the tokBuf parser buffer on exit. + * This Lex has special cases for the states EXML_ELT_DATA where we + * have an optimized read of element data, and EXML_AFTER_LS where we + * distinguish between element names, processing instructions and comments. + */ + +static ExmlToken getToken(Exml *xp, int state) +{ + MprBuf *tokBuf, *inBuf; + uchar *cp; + int c, rc; + + tokBuf = xp->tokBuf; + inBuf = xp->inBuf; + + mprAssert(state >= 0); + + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + mprFlushBuf(tokBuf); + + /* + * Special case parsing for names and for element data. We do this for + * performance so we can return to the caller the largest token possible + */ + if (state == EXML_ELT_DATA) { + /* + * Read all the data up to the start of the closing element "<" or the + * start of a sub-element. + */ +#if UNUSED + while (isspace(c)) { + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + } +#endif + if (c == '<') { + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + if (c == '/') { + return TOKEN_LS_SLASH; + } + putLastChar(xp, c); + return TOKEN_LS; + } + do { + if (mprPutCharToBuf(tokBuf, c) < 0) { + return TOKEN_TOO_BIG; + } + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + } while (c != '<'); + + /* + * Put back the last look-ahead character + */ + putLastChar(xp, c); + + /* + * If all white space, then zero the token buffer + */ + for (cp = tokBuf->start; *cp; cp++) { + if (!isspace(*cp)) { + return TOKEN_TEXT; + } + } + mprFlushBuf(tokBuf); + return TOKEN_TEXT; + } + + while (1) { + switch (c) { + case ' ': + case '\n': + case '\t': + case '\r': + break; + + case '<': + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + if (c == '/') { + return TOKEN_LS_SLASH; + } + putLastChar(xp, c); + return TOKEN_LS; + + case '=': + return TOKEN_EQ; + + case '>': + return TOKEN_GR; + + case '/': + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + if (c == '>') { + return TOKEN_SLASH_GR; + } + return TOKEN_ERR; + + case '\"': + case '\'': + xp->quoteChar = c; + /* Fall through */ + + default: + /* + * We handle element names, attribute names and attribute values + * here. We do NOT handle data between elements here. Read the + * token. Stop on white space or a closing element ">" + */ + if (xp->quoteChar) { + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + while (c != xp->quoteChar) { + if (mprPutCharToBuf(tokBuf, c) < 0) { + return TOKEN_TOO_BIG; + } + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + } + xp->quoteChar = 0; + + } else { + while (!isspace(c) && c != '>' && c != '/' && c != '=') { + if (mprPutCharToBuf(tokBuf, c) < 0) { + return TOKEN_TOO_BIG; + } + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + } + putLastChar(xp, c); + } + if (mprGetBufLength(tokBuf) <= 0) { + return TOKEN_ERR; + } + mprAddNullToBuf(tokBuf); + + if (state == EXML_AFTER_LS) { + /* + * If we are just inside an element "<", then analyze what we + * have to see if we have an element name, instruction or + * comment. Tokbuf will hold "?" for instructions or "!--" + * for comments. + */ + if (mprLookAtNextCharInBuf(tokBuf) == '?') { + /* Just ignore processing instructions */ + rc = scanFor(xp, "?>"); + if (rc < 0) { + return TOKEN_TOO_BIG; + } else if (rc == 0) { + return TOKEN_ERR; + } + return TOKEN_INSTRUCTIONS; + + } else if (mprLookAtNextCharInBuf(tokBuf) == '!') { + /* + * First discard the comment leadin "!--" and eat leading + * white space. + */ + if (strcmp((char*) tokBuf->start, "![CDATA[") == 0) { + mprFlushBuf(tokBuf); +#if UNUSED + c = mprLookAtNextCharInBuf(inBuf); + while (isspace(c)) { + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + c = mprLookAtNextCharInBuf(inBuf); + } +#endif + rc = scanFor(xp, "]]>"); + if (rc < 0) { + return TOKEN_TOO_BIG; + } else if (rc == 0) { + return TOKEN_ERR; + } + return TOKEN_CDATA; + + } else { + mprFlushBuf(tokBuf); +#if UNUSED + c = mprLookAtNextCharInBuf(inBuf); + while (isspace(c)) { + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + c = mprLookAtNextCharInBuf(inBuf); + } +#endif + rc = scanFor(xp, "-->"); + if (rc < 0) { + return TOKEN_TOO_BIG; + } else if (rc == 0) { + return TOKEN_ERR; + } + return TOKEN_COMMENT; + } + } + } + trimToken(xp); + return TOKEN_TEXT; + } + if ((c = getNextChar(xp)) < 0) { + return TOKEN_EOF; + } + } + + /* Should never get here */ + mprAssert(0); + return TOKEN_ERR; +} + +/******************************************************************************/ +/* + * Scan for a pattern. Eat and discard input up to the pattern. Return 1 if + * the pattern was found, return 0 if not found. Return < 0 on errors. + */ + +static int scanFor(Exml *xp, char *str) +{ + MprBuf *tokBuf; + char *cp; + int c; + + mprAssert(str); + + tokBuf = xp->tokBuf; + + while (1) { + for (cp = str; *cp; cp++) { + if ((c = getNextChar(xp)) < 0) { + return 0; + } + if (tokBuf) { + if (mprPutCharToBuf(tokBuf, c) < 0) { + return -1; + } + } + if (c != *cp) { + break; + } + } + if (*cp == '\0') { + /* + * Remove the pattern from the tokBuf + */ + if (tokBuf) { + mprAdjustBufEnd(tokBuf, -(int) strlen(str)); + trimToken(xp); + } + return 1; + } + } +} + +/******************************************************************************/ +/* + * Get another character. We read and buffer blocks of data if we need more + * data to parse. + */ + +static int getNextChar(Exml *xp) +{ + MprBuf *inBuf; + char c; + int l; + + inBuf = xp->inBuf; + if (mprGetBufLength(inBuf) <= 0) { + /* + * Flush to reset the servp/endp pointers to the start of the buffer + * so we can do a maximal read + */ + mprFlushBuf(inBuf); + l = (xp->readFn)(xp, xp->inputArg, mprGetBufStart(inBuf), + mprGetBufLinearSpace(inBuf)); + if (l <= 0) { + return -1; + } + mprAdjustBufEnd(inBuf, l); + } + c = mprGetCharFromBuf(inBuf); + + if (c == '\n') { + xp->lineNumber++; + } + return c; +} + +/******************************************************************************/ +/* + * Put back a character in the input buffer + */ + +static int putLastChar(Exml *xp, int c) +{ + if (mprInsertCharToBuf(xp->inBuf, (char) c) < 0) { + mprAssert(0); + return -1; + } + if (c == '\n') { + xp->lineNumber--; + } + return 0; +} + +/******************************************************************************/ +/* + * Output a parse message + */ + +static void error(Exml *xp, char *fmt, ...) +{ + va_list args; + char *buf; + + mprAssert(fmt); + + va_start(args, fmt); + mprAllocVsprintf(MPR_LOC_ARGS(xp), &buf, MPR_MAX_STRING, fmt, args); + va_end(args); + + /* + * MOB need to add the failing line text and a pointer to which column + */ + mprFree(xp->errMsg); + mprAllocSprintf(MPR_LOC_ARGS(xp), &xp->errMsg, MPR_MAX_STRING, + "XML error: %s\nAt line %d\n", buf, xp->lineNumber); + + mprFree(buf); +} + +/******************************************************************************/ +/* + * Remove trailing whitespace in a token and ensure it is terminated with + * a NULL for easy parsing + */ + +static void trimToken(Exml *xp) +{ + while (isspace(mprLookAtLastCharInBuf(xp->tokBuf))) { + mprAdjustBufEnd(xp->tokBuf, -1); + } + mprAddNullToBuf(xp->tokBuf); +} + +/******************************************************************************/ + +const char *exmlGetErrorMsg(Exml *xp) +{ + if (xp->errMsg == 0) { + return ""; + } + return xp->errMsg; +} + +/******************************************************************************/ + +int exmlGetLineNumber(Exml *xp) +{ + return xp->lineNumber; +} + +/******************************************************************************/ +#else + +void exmlParserDummy() {} +#endif /* BLD_FEATURE_EXML */ + +/* + * 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-2.0/exml/files b/source4/lib/appweb/ejs-2.0/exml/files new file mode 100644 index 0000000000..0f10ea44dd --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/exml/files @@ -0,0 +1 @@ +${BLD_OBJ_DIR}/exmlParser${BLD_OBJ} diff --git a/source4/lib/appweb/ejs-2.0/mpr/Makefile b/source4/lib/appweb/ejs-2.0/mpr/Makefile new file mode 100644 index 0000000000..6dd0e45d9e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/Makefile @@ -0,0 +1,41 @@ +# +# Makefile for the Mbedthis Portable Runtime (MPR) library +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes + +include make.dep + +ifeq ($(BLD_HOST_UNIX),1) +PRE_DIRS = UNIX +else +PRE_DIRS = $(BLD_HOST_OS) +endif + +POST_DIRS = package + +TARGETS += $(BLD_BIN_DIR)/libmpr$(BLD_LIB) + +compileExtra: $(TARGETS) + +# +# Build the mpr libraries +# +$(BLD_BIN_DIR)/libmpr$(BLD_LIB): files \ + $(shell BLD_OBJ=$(BLD_OBJ) \; BLD_OBJ_DIR=$(BLD_OBJ_DIR) \; \ + eval echo `cat files`) + @bld --library $(BLD_BIN_DIR)/libmpr \ + --objectsDir $(BLD_OBJ_DIR) --objectList files + +cleanExtra: + @echo "rm -f $(TARGETS)" | $(BLDOUT) + @rm -f $(TARGETS) + @rm -f $(BLD_BIN_DIR)/libmpr.* + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/mpr/UNIX/Makefile b/source4/lib/appweb/ejs-2.0/mpr/UNIX/Makefile new file mode 100644 index 0000000000..5259b1e3a0 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/UNIX/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the Mbedthis Portable Runtime (MPR) library for UNIX +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I.. + +include make.dep + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/mpr/UNIX/mprFile.c b/source4/lib/appweb/ejs-2.0/mpr/UNIX/mprFile.c new file mode 100644 index 0000000000..f647f1ed56 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/UNIX/mprFile.c @@ -0,0 +1,86 @@ +/** + * @file mprFile.c + * @brief File services for Unix + * @overview + * @remarks + * See mprGenFile.c for other file services. + */ + +/******************************************************************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/************************************ Code ************************************/ + +int mprGetFileInfo(MprCtx ctx, const char *path, MprFileInfo *info) +{ + struct stat s; + + mprAssert(path); + mprAssert(info); + + if (stat(path, &s) < 0) { + return -1; + } + + info->size = s.st_size; + info->ctime = s.st_ctime; + info->mtime = s.st_mtime; + info->inode = s.st_ino; + info->isDir = (s.st_mode & S_IFDIR) != 0; + info->isReg = (s.st_mode & S_IFREG) != 0; + + if (strcmp(path, "/dev/null") == 0) { + info->isReg = 0; + } + + return 0; +} + +/******************************************************************************/ + +int mprMakeDir(MprCtx ctx, const char *path, int perms) +{ + return mkdir(path, perms); +} + +/******************************************************************************/ +/* + * 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-2.0/mpr/UNIX/mprPlatform.c b/source4/lib/appweb/ejs-2.0/mpr/UNIX/mprPlatform.c new file mode 100644 index 0000000000..2c7fbf8a00 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/UNIX/mprPlatform.c @@ -0,0 +1,218 @@ +/** + * @file mprPlatform.c + * @brief Cross platform routines + * @overview This module provides low level cross platform routines. + * @remarks Most routines in this file are not thread-safe. It is the callers + * responsibility to perform all thread synchronization. + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 **********************************/ +/* + * We need to use the underlying str(cpy) routines to implement our safe + * alternatives + */ +#if !DOXYGEN +#define UNSAFE_FUNCTIONS_OK 1 +#endif + +#include "mpr.h" + +/************************************ Code ************************************/ + +char *mprInetToStr(char *buffer, int bufsize, const struct in_addr in) +{ +#if HAVE_NTOA_R + inet_ntoa_r(in, buffer, bufsize); +#else + uchar *cp; + /* FUTURE -- this is not portable */ + cp = (uchar*) ∈ + mprSprintf(buffer, bufsize, "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); +#endif + return buffer; +} + +/******************************************************************************/ + +void mprSetShell(MprCtx ctx, void *shell) +{ +} + +/******************************************************************************/ + +void *mprGetShell(MprCtx ctx) +{ + return 0; +} + +/******************************************************************************/ +/* + * Sleep. Period given in milliseconds. + */ + +void mprSleep(MprCtx ctx, int milliseconds) +{ + struct timespec timeout; + int rc; + + mprAssert(milliseconds >= 0); + timeout.tv_sec = milliseconds / 1000; + timeout.tv_nsec = (milliseconds % 1000) * 1000000; + do { + rc = nanosleep(&timeout, 0); + } while (rc < 0 && errno == EINTR); +} + +/******************************************************************************/ +/* + * Make intervening directories + */ + +int mprMakeDirPath(MprCtx ctx, const char *path) +{ + char dir[MPR_MAX_PATH], buf[MPR_MAX_PATH]; + char *dirSep; + char *next, *tok; + + dir[0] = '\0'; + dirSep = "/\\"; + + if (path == 0 || *path == '\0') { + return MPR_ERR_BAD_ARGS; + } + + mprStrcpy(buf, sizeof(buf), path); + next = mprStrTok(buf, dirSep, &tok); + if (*buf == '/') { + dir[0] = '/'; + } + while (next != NULL) { + if (strcmp(next, ".") == 0 ) { + next = mprStrTok(NULL, dirSep, &tok); + continue; + } + strcat(dir, next); + if (access(dir, R_OK) != 0) { + if (mkdir(dir, 0666) < 0) { + return MPR_ERR_CANT_CREATE; + } + } + strcat(dir, "/"); + next = mprStrTok(NULL, dirSep, &tok); + } + return 0; +} + +/******************************************************************************/ +/* + * Get a fully qualified file name for the given path. Return with forward + * slashes always + */ + +char *mprGetFullPathName(char *buf, int buflen, const char *path) +{ + if (mprStrcpy(buf, buflen, path) < 0) { + mprAssert(0); + return 0; + } + return buf; +} + +/******************************************************************************/ +/* + * Replacement for gethostbyname that is multi-thread safe + */ + +struct hostent *mprGetHostByName(MprCtx ctx, const char *name) +{ + MprApp *app; + struct hostent *hp; + struct hostent *ip; + int count, i; + + hp = (struct hostent*) mprAlloc(ctx, sizeof(struct hostent)); + memset(hp, 0, sizeof(struct hostent)); + + app = mprGetApp(ctx); + + #undef gethostbyname + + mprGlobalLock(app); + ip = gethostbyname(name); + mprGlobalUnlock(app); + + if (ip == 0) { + return 0; + } + hp->h_addrtype = ip->h_addrtype; + hp->h_length = ip->h_length; + hp->h_name = mprStrdup(hp, ip->h_name); + hp->h_addr_list = 0; + hp->h_aliases = 0; + + for (count = 0; ip->h_addr_list[count] != 0; ) { + count++; + } + if (count > 0) { + count++; + hp->h_addr_list = mprAlloc(hp, count * sizeof(char*)); + for (i = 0; ip->h_addr_list[i] != 0; i++) { + memcpy(&hp->h_addr_list[i], &ip->h_addr_list[i], ip->h_length); + } + hp->h_addr_list[i] = 0; + } + + for (count = 0; ip->h_aliases[count] != 0; ) { + count++; + } + if (count > 0) { + count++; + hp->h_aliases = mprAlloc(hp, count * sizeof(char*)); + for (i = 0; ip->h_aliases[i] != 0; i++) { + hp->h_aliases[i] = mprStrdup(hp, ip->h_aliases[i]); + } + hp->h_aliases[i] = 0; + } + return hp; +} + +/******************************************************************************/ + +/* + * 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-2.0/mpr/UNIX/mprTime.c b/source4/lib/appweb/ejs-2.0/mpr/UNIX/mprTime.c new file mode 100644 index 0000000000..0153c0622d --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/UNIX/mprTime.c @@ -0,0 +1,163 @@ +/** + * @file mprTime.c + * @brief Time handling for Unix + * @overview + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#undef localtime +#undef localtime_r +#undef gmtime +#undef gmtime_r +#undef ctime +#undef ctime_r +#undef asctime +#undef asctime_r + +/******************************************************************************/ +/* + * Returns time in seconds and milliseconds. This is NOT time-of-day. + */ + +MprTime *mprGetTime(MprCtx ctx, MprTime *tp) +{ + struct timeval tv; + + if (gettimeofday(&tv, 0) < 0) { + mprAssert(0); + tp->sec = 0; + tp->msec = 0; + return tp; + } + tp->sec = tv.tv_sec; + tp->msec = tv.tv_usec / 1000; + return tp; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of localtime + */ + +struct tm *mprLocaltime(MprCtx ctx, struct tm *timep, time_t *now) +{ + localtime_r(now, timep); + + return timep; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of gmtime + */ + +struct tm *mprGmtime(MprCtx ctx, time_t *now, struct tm *timep) +{ + gmtime_r(now, timep); + + return timep; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of ctime + */ + +int mprCtime(MprCtx ctx, char *buf, int bufsize, const time_t *timer) +{ + char localBuf[80]; + char *cp; + int len; + + mprAssert(buf); + + mprGlobalLock(ctx); + + cp = ctime_r(timer, localBuf); + if ((int) strlen(cp) >= bufsize) { + mprStrcpy(buf, bufsize, "WONT FIT"); + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + len = mprStrcpy(buf, bufsize, cp); + + if (buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + } + + mprGlobalUnlock(ctx); + + return 0; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of asctime + */ + +int mprAsctime(MprCtx ctx, char *buf, int bufsize, const struct tm *timeptr) +{ + char *cp; + char localBuf[80]; + + cp = asctime_r(timeptr, localBuf); + if ((int) strlen(cp) >= bufsize) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + mprStrcpy(buf, bufsize, cp); + + return strlen(buf); +} + +/******************************************************************************/ + +#ifdef __cplusplus +} +#endif + +/* + * 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-2.0/mpr/VXWORKS/Makefile b/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/Makefile new file mode 100644 index 0000000000..f3a2394b6e --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the Mbedthis Portable Runtime (MPR) library for VXWORKS +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I.. + +include make.dep + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/mprFile.c b/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/mprFile.c new file mode 100644 index 0000000000..ae0b523faa --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/mprFile.c @@ -0,0 +1,85 @@ +/** + * @file mprUnixFile.c + * @brief File services for Unix + * @overview + * @remarks + */ + +/******************************************************************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/************************************ Code ************************************/ + +int mprGetFileInfo(MprCtx ctx, const char *path, MprFileInfo *info) +{ + struct stat s; + + mprAssert(path); + mprAssert(info); + + if (stat(path, &s) < 0) { + return -1; + } + + info->size = s.st_size; + info->ctime = s.st_ctime; + info->mtime = s.st_mtime; + info->inode = s.st_ino; + info->isDir = (s.st_mode & S_IFDIR) != 0; + info->isReg = (s.st_mode & S_IFREG) != 0; + + if (strcmp(path, "/dev/null") == 0) { + info->isReg = 0; + } + + return 0; +} + +/******************************************************************************/ + +int mprMakeDir(MprCtx ctx, const char *path, int perms) +{ + return mkdir(path, perms); +} + +/******************************************************************************/ +/* + * 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-2.0/mpr/VXWORKS/mprPlatform.c b/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/mprPlatform.c new file mode 100644 index 0000000000..29258dfe1c --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/mprPlatform.c @@ -0,0 +1,191 @@ +/** + * @file mprPlatform.c + * @brief Cross platform routines + * @overview This module provides low level cross platform routines. + * @remarks Most routines in this file are not thread-safe. It is the callers + * responsibility to perform all thread synchronization. + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 **********************************/ +/* + * We need to use the underlying str(cpy) routines to implement our safe + * alternatives + */ +#if !DOXYGEN +#define UNSAFE_FUNCTIONS_OK 1 +#endif + +#include "mpr.h" + +/************************************ Code ************************************/ + +char *mprInetToStr(char *buffer, int bufsize, const struct in_addr in) +{ +#if HAVE_NTOA_R + inet_ntoa_r(in, buffer, bufsize); +#else + uchar *cp; + /* FUTURE -- this is not portable */ + cp = (uchar*) ∈ + mprSprintf(buffer, bufsize, "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); +#endif + return buffer; +} + +/******************************************************************************/ + +void mprSetShell(MprCtx ctx, void *shell) +{ +} + +/******************************************************************************/ + +void *mprGetShell(MprCtx ctx) +{ + return 0; +} + +/******************************************************************************/ +/* + * Sleep. Period given in milliseconds. + */ + +void mprSleep(MprCtx ctx, int milliseconds) +{ + struct timeval timeout; + int rc; + + timeout.tv_sec = milliseconds / 1000; + timeout.tv_usec = (milliseconds % 1000) * 1000; + do { + rc = select(1, 0, 0, 0, &timeout); + } while (rc < 0 && errno == EINTR); +} + +/******************************************************************************/ +/* + * Make intervening directories + */ + +int mprMakeDirPath(MprCtx ctx, const char *path) +{ + char dir[MPR_MAX_PATH], buf[MPR_MAX_PATH]; + char *dirSep; + char *next, *tok; + + dir[0] = '\0'; + dirSep = "/\\"; + + if (path == 0 || *path == '\0') { + return MPR_ERR_BAD_ARGS; + } + + mprStrcpy(buf, sizeof(buf), path); + next = mprStrTok(buf, dirSep, &tok); + if (*buf == '/') { + dir[0] = '/'; + } + while (next != NULL) { + if (strcmp(next, ".") == 0 ) { + next = mprStrTok(NULL, dirSep, &tok); + continue; + } + strcat(dir, next); + if (access(dir, R_OK) != 0) { + if (mkdir(dir) < 0) { + return MPR_ERR_CANT_CREATE; + } + } + strcat(dir, "/"); + next = mprStrTok(NULL, dirSep, &tok); + } + return 0; +} + +/******************************************************************************/ +/* + * Get a fully qualified file name for the given path. Return with forward + * slashes always + */ + +char *mprGetFullPathName(char *buf, int buflen, const char *path) +{ + if (mprStrcpy(buf, buflen, path) < 0) { + mprAssert(0); + return 0; + } + return buf; +} + +/******************************************************************************/ +/* + * Replacement for gethostbyname that is multi-thread safe + */ + +struct hostent *mprGetHostByName(MprCtx ctx, const char *name) +{ + struct hostent *hp; + + hp = (struct hostent*) mprAlloc(ctx, sizeof(struct hostent)); + memset(hp, 0, sizeof(struct hostent)); + + struct in_addr inaddr; + inaddr.s_addr = (ulong) hostGetByName(name); + if (inaddr.s_addr < 0) { + mprAssert(0); + return 0; + } + hp->h_addrtype = AF_INET; + hp->h_length = sizeof(int); + hp->h_name = mprStrdup(name); + hp->h_addr_list = 0; + hp->h_aliases = 0; + + hp->h_addr_list = new char*[2]; + hp->h_addr_list[0] = (char *) mprAlloc(hp, sizeof(struct in_addr)); + memcpy(&hp->h_addr_list[0], &inaddr, hp->h_length); + hp->h_addr_list[1] = 0; + + return hp; +} + +/******************************************************************************/ + +/* + * 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-2.0/mpr/VXWORKS/mprTime.c b/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/mprTime.c new file mode 100755 index 0000000000..c9b7560f46 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/VXWORKS/mprTime.c @@ -0,0 +1,163 @@ +/** + * @file mprTime.c + * @brief Time handling for VxWorks + * @overview + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#undef localtime +#undef localtime_r +#undef gmtime +#undef gmtime_r +#undef ctime +#undef ctime_r +#undef asctime +#undef asctime_r + +/******************************************************************************/ +/* + * Returns time in seconds and milliseconds. This is NOT time-of-day. + */ + +MprTime *mprGetTime(MprCtx ctx, MprTime *tp) +{ + struct timeval tv; + + if (gettimeofday(&tv, 0) < 0) { + mprAssert(0); + tp->sec = 0; + tp->msec = 0; + return tp; + } + tp->sec = tv.tv_sec; + tp->msec = tv.tv_usec / 1000; + return tp; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of localtime + */ + +struct tm *mprLocaltime(MprCtx ctx, struct tm *timep, time_t *now) +{ + localtime_r(now, timep); + + return timep; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of gmtime + */ + +struct tm *mprGmtime(MprCtx ctx, time_t *now, struct tm *timep) +{ + gmtime_r(now, timep); + + return timep; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of ctime + */ + +int mprCtime(MprCtx ctx, char *buf, int bufsize, const time_t *timer) +{ + char localBuf[80]; + char *cp; + int len; + + mprAssert(buf); + + mprGlobalLock(ctx); + + cp = ctime_r(timer, localBuf); + if ((int) strlen(cp) >= bufsize) { + mprStrcpy(buf, bufsize, "WONT FIT"); + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + len = mprStrcpy(buf, bufsize, cp); + + if (buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + } + + mprGlobalUnlock(ctx); + + return 0; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of asctime + */ + +int mprAsctime(MprCtx ctx, char *buf, int bufsize, const struct tm *timeptr) +{ + char *cp; + char localBuf[80]; + + cp = asctime_r(timeptr, localBuf); + if ((int) strlen(cp) >= bufsize) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + mprStrcpy(buf, bufsize, cp); + + return strlen(buf); +} + +/******************************************************************************/ + +#ifdef __cplusplus +} +#endif + +/* + * 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-2.0/mpr/WIN/Makefile b/source4/lib/appweb/ejs-2.0/mpr/WIN/Makefile new file mode 100644 index 0000000000..84e30ff8f1 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/WIN/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the Mbedthis Portable Runtime (MPR) library for Windows +# +# Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. +# + +COMPILE := *.c +EXPORT_OBJECTS := yes +MAKE_IFLAGS := -I.. + +include make.dep + +## Local variables: +## tab-width: 4 +## End: +## vim: tw=78 sw=4 ts=4 diff --git a/source4/lib/appweb/ejs-2.0/mpr/WIN/mprFile.c b/source4/lib/appweb/ejs-2.0/mpr/WIN/mprFile.c new file mode 100644 index 0000000000..9ac1669f3d --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/WIN/mprFile.c @@ -0,0 +1,123 @@ +/** + * @file mprWinFile.c + * @brief File services for Windows + * @overview + * @remarks + */ + +/******************************************************************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/************************************ Code ************************************/ + +int mprGetFileInfo(MprCtx ctx, const char *path, MprFileInfo *info) +{ + struct stat s; + + mprAssert(path); + mprAssert(info); + + if (stat(path, &s) < 0) { + return -1; + } + + info->size = s.st_size; + /* + * MOB -- these are time64_t. Loss of precision + */ + info->ctime = (uint) s.st_ctime; + info->mtime = (uint) s.st_mtime; + info->inode = s.st_ino; + info->isDir = (s.st_mode & S_IFDIR) != 0; + info->isReg = (s.st_mode & S_IFREG) != 0; + + /* + * Work hard on windows to determine if the file is a regular file. + * FUTURE -- OPT. Eliminate this CreateFile. + */ + if (info->isReg) { + long fileType, att; + + if ((att = GetFileAttributes(path)) == -1) { + return -1; + } + if (att & (FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_DIRECTORY | + FILE_ATTRIBUTE_ENCRYPTED | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_OFFLINE)) { + /* + * Catch accesses to devices like CON, AUX, NUL, LPT etc + * att will be set to ENCRYPTED on Win9X and NT. + */ + info->isReg = 0; + } + if (info->isReg) { + HANDLE handle; + handle = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, OPEN_EXISTING, 0, 0); + if (handle == INVALID_HANDLE_VALUE) { + info->isReg = 0; + } else { + fileType = GetFileType(handle); + if (fileType == FILE_TYPE_CHAR || fileType == FILE_TYPE_PIPE) { + info->isReg = 0; + } + CloseHandle(handle); + } + } + } + if (strcmp(path, "nul") == 0) { + info->isReg = 0; + } + return 0; +} + +/******************************************************************************/ + +int mprMakeDir(MprCtx ctx, const char *path, int perms) +{ + return mkdir(path, perms); +} + +/******************************************************************************/ +/* + * 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-2.0/mpr/WIN/mprPlatform.c b/source4/lib/appweb/ejs-2.0/mpr/WIN/mprPlatform.c new file mode 100644 index 0000000000..65718694b1 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/WIN/mprPlatform.c @@ -0,0 +1,378 @@ +/** + * @file mprPlatform.c + * @brief Cross platform routines + * @overview This module provides low level cross platform routines. + * @remarks Most routines in this file are not thread-safe. It is the callers + * responsibility to perform all thread synchronization. + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 **********************************/ +/* + * We need to use the underlying str(cpy) routines to implement our safe + * alternatives + */ +#if !DOXYGEN +#define UNSAFE_FUNCTIONS_OK 1 +#endif + +#include "mpr.h" + +/**************************** Forward Declarations ****************************/ + +static const char *getHive(const char *keyPath, HKEY *hive); + +/************************************ Code ************************************/ + +char *mprInetToStr(char *buffer, int bufsize, const struct in_addr in) +{ +#if HAVE_NTOA_R + inet_ntoa_r(in, buffer, bufsize); +#else + uchar *cp; + cp = (uchar*) ∈ + mprSprintf(buffer, bufsize, "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); +#endif + return buffer; +} + +/******************************************************************************/ + +void mprSetShell(MprCtx ctx, void *shell) +{ +} + +/******************************************************************************/ + +void *mprGetShell(MprCtx ctx) +{ + return 0; +} + +/******************************************************************************/ +/* + * Sleep. Period given in milliseconds. + */ + +void mprSleep(MprCtx ctx, int milliseconds) +{ + Sleep(milliseconds); +} + +/******************************************************************************/ +/* + * Make intervening directories + */ + +int mprMakeDirPath(MprCtx ctx, const char *path) +{ + char dir[MPR_MAX_PATH], buf[MPR_MAX_PATH]; + char *dirSep; + char *next, *tok; + + dir[0] = '\0'; + dirSep = "/\\"; + + if (path == 0 || *path == '\0') { + return MPR_ERR_BAD_ARGS; + } + + mprStrcpy(buf, sizeof(buf), path); + next = mprStrTok(buf, dirSep, &tok); + if (*buf == '/') { + dir[0] = '/'; + } + while (next != NULL) { + if (strcmp(next, ".") == 0 ) { + next = mprStrTok(NULL, dirSep, &tok); + continue; + } + strcat(dir, next); + if (access(dir, R_OK) != 0) { + if (_mkdir(dir) < 0) { + return MPR_ERR_CANT_CREATE; + } + } + strcat(dir, "/"); + next = mprStrTok(NULL, dirSep, &tok); + } + return 0; +} + +/******************************************************************************/ +/* + * Get a fully qualified file name for the given path. Return with forward + * slashes always + */ + +char *mprGetFullPathName(char *buf, int buflen, const char *path) +{ +#if (WIN || NW || OS2) && !BLD_FEATURE_ROMFS + char *junk, *cp; + int rc; + + --buflen; + rc = GetFullPathName(path, buflen, buf, &junk); + for (cp = buf; *cp; cp++) { + if (*cp == '\\') { + *cp = '/'; + } + } + buf[buflen] = '\0'; +#else + if (mprStrcpy(buf, buflen, path) < 0) { + mprAssert(0); + return 0; + } +#endif + return buf; +} + +/******************************************************************************/ +/* + * Replacement for gethostbyname that is multi-thread safe + */ + +struct hostent *mprGetHostByName(MprCtx ctx, const char *name) +{ + MprApp *app; + struct hostent *hp; + struct hostent *ip; + int count, i; + + hp = (struct hostent*) mprAlloc(ctx, sizeof(struct hostent)); + memset(hp, 0, sizeof(struct hostent)); + + app = mprGetApp(ctx); + + #undef gethostbyname + + mprGlobalLock(app); + ip = gethostbyname(name); + mprGlobalUnlock(app); + + if (ip == 0) { + return 0; + } + hp->h_addrtype = ip->h_addrtype; + hp->h_length = ip->h_length; + hp->h_name = mprStrdup(hp, ip->h_name); + hp->h_addr_list = 0; + hp->h_aliases = 0; + + for (count = 0; ip->h_addr_list[count] != 0; ) { + count++; + } + if (count > 0) { + count++; + hp->h_addr_list = mprAlloc(hp, count * sizeof(char*)); + for (i = 0; ip->h_addr_list[i] != 0; i++) { + memcpy(&hp->h_addr_list[i], &ip->h_addr_list[i], ip->h_length); + } + hp->h_addr_list[i] = 0; + } + + for (count = 0; ip->h_aliases[count] != 0; ) { + count++; + } + if (count > 0) { + count++; + hp->h_aliases = mprAlloc(hp, count * sizeof(char*)); + for (i = 0; ip->h_aliases[i] != 0; i++) { + hp->h_aliases[i] = mprStrdup(hp, ip->h_aliases[i]); + } + hp->h_aliases[i] = 0; + } + return hp; +} + +/******************************************************************************/ +/* + * Read a registry value. Returns allocated memory in buf. + */ + +int mprReadRegistry(MprCtx ctx, char **buf, int max, const char *key, + const char *name) +{ + HKEY top, h; + char *value; + ulong type, size; + + mprAssert(key && *key); + mprAssert(buf); + + /* + * Get the registry hive + */ + if ((key = getHive(key, &top)) == 0) { + return MPR_ERR_CANT_ACCESS; + } + + if (RegOpenKeyEx(top, key, 0, KEY_READ, &h) != ERROR_SUCCESS) { + return MPR_ERR_CANT_ACCESS; + } + + /* + * Get the type + */ + if (RegQueryValueEx(h, name, 0, &type, 0, &size) != ERROR_SUCCESS) { + RegCloseKey(h); + return MPR_ERR_CANT_READ; + } + if (type != REG_SZ && type != REG_EXPAND_SZ) { + RegCloseKey(h); + return MPR_ERR_BAD_TYPE; + } + + value = (char*) mprAlloc(ctx, size); + if ((int) size > max) { + RegCloseKey(h); + return MPR_ERR_WONT_FIT; + } + if (RegQueryValueEx(h, name, 0, &type, (uchar*) value, &size) != + ERROR_SUCCESS) { + mprFree(value); + RegCloseKey(h); + return MPR_ERR_CANT_READ; + } + + RegCloseKey(h); + *buf = value; + return 0; +} + +/******************************************************************************/ +/* + * Write a string registry value. Returns allocated memory in buf. + */ + +int mprWriteRegistry(MprCtx ctx, const char *key, const char *name, + const char *value) +{ + HKEY top, h, subHandle; + ulong disposition; + + mprAssert(key && *key); + mprAssert(name && *name); + mprAssert(value && *value); + + /* + * Get the registry hive + */ + if ((key = getHive(key, &top)) == 0) { + return MPR_ERR_CANT_ACCESS; + } + + if (name) { + /* + * Write a registry string value + */ + if (RegOpenKeyEx(top, key, 0, KEY_ALL_ACCESS, &h) != ERROR_SUCCESS) { + return MPR_ERR_CANT_ACCESS; + } + if (RegSetValueEx(h, name, 0, REG_SZ, value, strlen(value) + 1) + != ERROR_SUCCESS) { + RegCloseKey(h); + return MPR_ERR_CANT_READ; + } + + } else { + /* + * Create a new sub key + */ + if (RegOpenKeyEx(top, key, 0, KEY_CREATE_SUB_KEY, &h) != ERROR_SUCCESS){ + return MPR_ERR_CANT_ACCESS; + } + if (RegCreateKeyEx(h, name, 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, NULL, &subHandle, &disposition) != ERROR_SUCCESS) { + return MPR_ERR_CANT_ACCESS; + } + RegCloseKey(subHandle); + } + RegCloseKey(h); + return 0; +} + +/******************************************************************************/ +/* + * Determine the registry hive by the first portion of the path. Return + * a pointer to the rest of key path after the hive portion. + */ + +static const char *getHive(const char *keyPath, HKEY *hive) +{ + char key[MPR_MAX_STRING], *cp; + int len; + + mprAssert(keyPath && *keyPath); + + *hive = 0; + + mprStrcpy(key, sizeof(key), keyPath); + key[sizeof(key) - 1] = '\0'; + + if (cp = strchr(key, '\\')) { + *cp++ = '\0'; + } + if (cp == 0 || *cp == '\0') { + return 0; + } + + if (!mprStrcmpAnyCase(key, "HKEY_LOCAL_MACHINE")) { + *hive = HKEY_LOCAL_MACHINE; + } else if (!mprStrcmpAnyCase(key, "HKEY_CURRENT_USER")) { + *hive = HKEY_CURRENT_USER; + } else if (!mprStrcmpAnyCase(key, "HKEY_USERS")) { + *hive = HKEY_USERS; + } else if (!mprStrcmpAnyCase(key, "HKEY_CLASSES_ROOT")) { + *hive = HKEY_CLASSES_ROOT; + } else { + return 0; + } + + if (*hive == 0) { + return 0; + } + len = strlen(key) + 1; + return keyPath + len; +} + +/******************************************************************************/ + +/* + * 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-2.0/mpr/WIN/mprTime.c b/source4/lib/appweb/ejs-2.0/mpr/WIN/mprTime.c new file mode 100644 index 0000000000..74e59c9c73 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/WIN/mprTime.c @@ -0,0 +1,192 @@ +/** + * @file mprTime.c + * @brief Time handling for Windows + * @overview + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************ Code ************************************/ +/* + * Returns time in seconds and milliseconds. This is NOT time-of-day. + */ + +MprTime *mprGetTime(MprCtx ctx, MprTime *tp) +{ + FILETIME fileTime; + int64 now, base; + + GetSystemTimeAsFileTime(&fileTime); + + now = ((((int64) fileTime.dwHighDateTime) << BITS(uint)) + + ((int64) fileTime.dwLowDateTime)); + + /* + * Convert from 100-nanosec units to milliseconds + */ + now = (now / 10000); + + /* + * Adjust to be seconds since Jan 1 1970. Do this to be consistent with + * UNIX but not really required by the API definition. + */ + base = ((UINT64(365) * 86400 * (1970 - 1601)) * 1000); + now -= base; + tp->sec = (uint) (now / 1000); + tp->msec = (uint) (now % 1000); + +#if UNUSED +{ + static int64 start; + + if (start == 0) { + start = now; + } + if (now < start) { + mprLog(ctx, 0, "TIME WENT BACKWARDS"); + mprLog(ctx, 0, "start %Ld", start); + mprLog(ctx, 0, "now %Ld", now); + } + mprLog(ctx, 0, "getTime %Ld", now); + start = now; +} +#endif + + return tp; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of localtime + */ + +struct tm *mprLocaltime(MprCtx ctx, struct tm *timep, time_t *now) +{ + struct tm *tbuf; + mprGlobalLock(ctx); + tbuf = localtime(now); + *timep = *tbuf; + mprGlobalUnlock(ctx); + + return timep; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of gmtime + */ + +struct tm *mprGmtime(MprCtx ctx, time_t *now, struct tm *timep) +{ + struct tm *tbuf; + tbuf = gmtime(now); + *timep = *tbuf; + + return timep; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of ctime + */ + +int mprCtime(MprCtx ctx, char *buf, int bufsize, const time_t *timer) +{ + char *cp; + int len; + + mprAssert(buf); + + mprGlobalLock(ctx); + + cp = ctime(timer); + if ((int) strlen(cp) >= bufsize) { + mprStrcpy(buf, bufsize, "WONT FIT"); + mprAssert(0); + mprGlobalUnlock(ctx); + return MPR_ERR_WONT_FIT; + } + + len = mprStrcpy(buf, bufsize, cp); + if (buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + } + + mprGlobalUnlock(ctx); + + return 0; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of asctime + */ + +int mprAsctime(MprCtx ctx, char *buf, int bufsize, const struct tm *timeptr) +{ + char *cp; + + mprAssert(buf); + mprGlobalLock(ctx); + cp = asctime(timeptr); + if ((int) strlen(cp) >= bufsize) { + mprAssert(0); + mprGlobalUnlock(ctx); + return MPR_ERR_WONT_FIT; + } + mprStrcpy(buf, bufsize, cp); + mprGlobalUnlock(ctx); + + return strlen(buf); +} + +/******************************************************************************/ + +#ifdef __cplusplus +} +#endif + +/* + * 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-2.0/mpr/files b/source4/lib/appweb/ejs-2.0/mpr/files new file mode 100644 index 0000000000..290c9ce790 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/files @@ -0,0 +1,14 @@ +${BLD_OBJ_DIR}/mpr${BLD_OBJ} +${BLD_OBJ_DIR}/mprAlloc${BLD_OBJ} +${BLD_OBJ_DIR}/mprArray${BLD_OBJ} +${BLD_OBJ_DIR}/mprBuf${BLD_OBJ} +${BLD_OBJ_DIR}/mprFile${BLD_OBJ} +${BLD_OBJ_DIR}/mprGenFile${BLD_OBJ} +${BLD_OBJ_DIR}/mprGenTime${BLD_OBJ} +${BLD_OBJ_DIR}/mprLock${BLD_OBJ} +${BLD_OBJ_DIR}/mprLog${BLD_OBJ} +${BLD_OBJ_DIR}/mprPlatform${BLD_OBJ} +${BLD_OBJ_DIR}/mprPrintf${BLD_OBJ} +${BLD_OBJ_DIR}/mprString${BLD_OBJ} +${BLD_OBJ_DIR}/mprSymbol${BLD_OBJ} +${BLD_OBJ_DIR}/mprTime${BLD_OBJ} diff --git a/source4/lib/appweb/ejs-2.0/mpr/mpr.c b/source4/lib/appweb/ejs-2.0/mpr/mpr.c new file mode 100644 index 0000000000..163b51eccf --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mpr.c @@ -0,0 +1,340 @@ +/** + * @file mpr.c + * @brief Mpr initialization + * @overview + * @remarks Most routines in this file are not thread-safe. It is the callers + * responsibility to perform all thread synchronization. + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 **********************************/ +/* + * We need to use the underlying str(cpy) routines to implement our safe + * alternatives + */ +#if !DOXYGEN +#define UNSAFE_FUNCTIONS_OK 1 +#endif + +#include "mpr.h" + +/******************************************************************************/ +/* + * Initialize the MPR. Create the top level memory context. This routine is + * the first call an MPR application must do. If using MprServices, the + * creation of an Mpr object will call this routine. + */ + +MprApp *mprInit(MprAllocCback cback) +{ + return mprInitEx(cback, 0); +} + +/******************************************************************************/ +/* + * Add a shell parameter then do the regular init + */ + +MprApp *mprInitEx(MprAllocCback cback, void *shell) +{ + MprApp *app; + + app = (MprApp*) mprAllocInit(cback); + + mprAssert(app); + if (app == 0) { + return 0; + } + + app->name = mprStrdup(app, BLD_PRODUCT); + app->title = mprStrdup(app, BLD_NAME); + app->version = mprStrdup(app, BLD_VERSION); + + mprSetShell(app, shell); + + app->table = mprCreateSymbolTable(app, 0); + + if (mprStartFileServices(app) < 0) { + mprAllocTerm(app); + return 0; + } + +#if BLD_FEATURE_MULTITHREAD + mprInitThreads(app); +#endif + + /* + * See if any of the preceeding allocations failed + */ + if (mprGetAllocErrors(app) > 0) { + mprAllocTerm(app); + return 0; + } + + /* + * Mark all blocks allocated so far as required. They will then be + * omitted from leak reports. + */ + mprSetRequiredAlloc(app, 1); + + return app; +} + +/******************************************************************************/ +/* + * Terminate the MPR. If doStats is true, then output a memory allocation + * report. + */ + +void mprTerm(MprApp *app, bool doStats) +{ +#if BLD_FEATURE_ALLOC_STATS + if (doStats) { + mprPrintAllocReport(app, 1, "MPR Memory Allocation Report"); + } +#endif + +#if BLD_FEATURE_MULTITHREAD + mprTermThreads(app); +#endif + + mprStopFileServices(app); + +#if BLD_DEBUG + mprValidateAllocTree(app); +#endif + mprAllocTerm(app); +} + +/******************************************************************************/ + +bool mprIsExiting(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + if (app == 0) { + return 1; + } + return app->flags & MPR_APP_EXITING; +} + +/******************************************************************************/ + +int mprHasAllocError(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + if (app == 0) { + return 1; + } + + return app->flags & MPR_APP_ALLOC_ERROR; +} + +/******************************************************************************/ + +void mprSignalExit(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + app->flags |= MPR_APP_EXITING; +} + +/******************************************************************************/ + +void mprSignalAllocError(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + app->flags |= MPR_APP_ALLOC_ERROR; +} + +/******************************************************************************/ + +int mprSetAppName(MprCtx ctx, const char *name, const char *title, + const char *version) +{ + MprApp *app; + + app = mprGetApp(ctx); + + if (name) { + mprFree(app->name); + if ((app->name = mprStrdup(ctx, name)) == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + } + + if (title) { + mprFree(app->title); + if ((app->title = mprStrdup(ctx, title)) == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + } + + if (version) { + mprFree(app->version); + if ((app->version = mprStrdup(ctx, version)) == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + } + return 0; +} + +/******************************************************************************/ + +const char *mprGetAppName(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + return app->name; +} + +/******************************************************************************/ + +const char *mprGetAppTitle(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + return app->title; +} + +/******************************************************************************/ + +const char *mprGetAppVersion(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + return app->version; +} + +/******************************************************************************/ + +int mprSetKeyValue(MprCtx ctx, const char *key, void *ptr) +{ + MprApp *app; + + app = mprGetApp(ctx); + if (mprInsertSymbol(app->table, key, ptr) == 0) { + return MPR_ERR_CANT_WRITE; + } + return 0; +} + +/******************************************************************************/ + +int mprRemoveKeyValue(MprCtx ctx, const char *key) +{ + MprApp *app; + + app = mprGetApp(ctx); + return mprRemoveSymbol(app->table, key); +} + +/******************************************************************************/ + +void *mprGetKeyValue(MprCtx ctx, const char *key) +{ + MprApp *app; + + app = mprGetApp(ctx); + return mprLookupSymbol(app->table, key); +} + +/******************************************************************************/ + +bool mprGetDebugMode(MprCtx ctx) +{ + return mprGetApp(ctx)->debugMode; +} + +/******************************************************************************/ + +void mprSetDebugMode(MprCtx ctx, bool on) +{ + mprGetApp(ctx)->debugMode = on; +} + +/******************************************************************************/ + +void mprSetLogHandler(MprCtx ctx, MprLogHandler handler) +{ + mprGetApp(ctx)->logHandler = handler; +} + +/******************************************************************************/ + +MprLogHandler mprGetLogHandler(MprCtx ctx) +{ + return mprGetApp(ctx)->logHandler; +} + +#if UNUSED +/******************************************************************************/ + +void mprSetMprInstance(MprCtx ctx, void *mprInstance) +{ + mprGetApp(ctx)->mprInstance = mprInstance; +} + +/******************************************************************************/ + +void *mprGetMprInstance(MprCtx ctx) +{ + return mprGetApp(ctx)->mprInstance; +} + +#endif +/******************************************************************************/ + +const char *mprCopyright() +{ + return "Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved."; +} + +/******************************************************************************/ + +/* + * 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-2.0/mpr/mpr.h b/source4/lib/appweb/ejs-2.0/mpr/mpr.h new file mode 100644 index 0000000000..67505a6e01 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mpr.h @@ -0,0 +1,1027 @@ +/* + * @file mpr.h + * @brief Header for the Mbedthis Portable Runtime (MPR) Base. + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + * + * This software is distributed under commercial and open source licenses. + * You may use the GPL open source license described below or you may acquire + * a commercial license from Mbedthis Software. You agree to be fully bound + * by the terms of either license. Consult the LICENSE.TXT distributed with + * this software for full details. + * + * This software is open source; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See the GNU General Public License for more + * details at: http://www.mbedthis.com/downloads/gplLicense.html + * + * This program is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This GPL license does NOT permit incorporating this software into + * proprietary programs. If you are unable to comply with the GPL, you must + * acquire a commercial license to use this software. Commercial licenses + * for this software and support services are available from Mbedthis + * Software at http://www.mbedthis.com + * + * @end + */ +/******************************* Documentation ********************************/ +/* + * See mpr.dox for additional documentation. + */ + +/******************************************************************************/ + +#ifndef _h_MPR +#define _h_MPR 1 + +/***********************************Includes **********************************/ + +#include "mprOs.h" + +/******************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************** Constants *********************************/ + +#if BLD_FEATURE_SQUEEZE +#if BREW || DOXYGEN +/* + * Maximum length of a file path name. Reduced from the system maximum to + * save memory space. + */ +#define MPR_MAX_FNAME 64 /**< Reasonable filename size */ +#define MPR_MAX_PATH 64 /**< Reasonable path name size */ +#define MPR_DEFAULT_STACK (16 * 1024) /**< Default stack size */ +#else +#define MPR_MAX_FNAME 128 /**< Reasonable filename size */ +#define MPR_MAX_PATH 256 /**< Reasonable path name size */ +#define MPR_DEFAULT_STACK (32 * 1024) /**< Default stack size */ +#endif +/* + * Reasonable length of a file name used by the product. Use where you know + * the expected file name and it is certain to be less than this limit. + */ +#define MPR_DEFAULT_ALLOC 64 /**< Default small alloc size */ +#define MPR_DEFAULT_HASH_SIZE 23 /**< Default size of hash table */ +#define MPR_MAX_ARGC 32 /**< Reasonable max of args */ +#define MPR_MAX_STRING 512 /**< Maximum (stack) string size */ +#define MPR_MAX_LOG_STRING 512 /**< Maximum log message */ +#define MPR_MAX_URL 256 /**< Reasonable size of a URL */ +#define MPR_BUFSIZE 512 /**< Reasonable size for buffers */ +#define MPR_SLAB_STR_MAX 32 /**< Size of string slab blocks */ +#define MPR_SLAB_STR_INC 32 /**< Pre-allocate increment */ +#define MPR_SLAB_DEFAULT_INC 8 /**< Default pre-allocate inc */ +#define MPR_ARRAY_INCR 8 /**< Default array growth inc */ +#define MPR_BUF_INCR 1024 /**< Default array growth inc */ +#define MPR_MAX_BUF (1024*4096) /**< Default array growth inc */ + +#define MPR_BLK_HDR_SIZE ((sizeof(struct MprBlk) + 3) & ~3) + +#else +#define MPR_MAX_FNAME 256 +#define MPR_MAX_PATH 1024 +#define MPR_DEFAULT_ALLOC 256 +#define MPR_DEFAULT_HASH_SIZE 43 +#define MPR_DEFAULT_STACK (64 * 1024) +#define MPR_MAX_ARGC 128 +#define MPR_MAX_STRING 4096 +#define MPR_MAX_LOG_STRING 8192 +#define MPR_MAX_URL 1024 +#define MPR_BUFSIZE 1024 +#define MPR_SLAB_STR_MAX 32 +#define MPR_SLAB_STR_INC 64 +#define MPR_SLAB_DEFAULT_INC 16 +#define MPR_ARRAY_INCR 16 +#define MPR_BUF_INCR 1024 +#define MPR_MAX_BUF (1024*4096) + +#define MPR_BLK_HDR_SIZE ((sizeof(struct MprBlk) + 15) & ~15) +#endif + +/** + * Maximum size of a host name string + */ +#define MPR_MAX_IP_NAME 64 + +/** + * Maximum size of an IP address + */ +#define MPR_MAX_IP_ADDR 16 + +/** + * Maximum size of an IP address including port number + */ +#define MPR_MAX_IP_ADDR_PORT 32 + +#define MPR_MAX_SLAB 16 /* Slabs from 32-512 bytes */ + +#define MPR_MAX_TIME_SYNC (10 * 1000) /* Time sync adjustments */ + +/** + * @overview Memory context type + * @description Blocks of memory are allocated using a memory context + * as the parent with the \ref MprApp structure being the root of the + * tree. Any allocated memory block may serve as the memory context for + * subsequent memory allocations. Freeing a block via \ref mprFree will + * release the allocated block and all child blocks. + * @stability Prototype. + * @library libmpr. + * @see mprInit, mprAlloc, mprFree + */ +typedef const void *MprCtx; + +/* + * Allocated memory destructor type + */ +typedef int (*MprDestructor)(void *); + +/******************************** Error Codes *********************************/ + +/* + * Standard MPR return and error codes + */ + +#define MPR_ERR_OK (0) +/**< Success */ + +#define MPR_ERR_BASE (-200) +/**< Base error code */ + +#define MPR_ERR_GENERAL (MPR_ERR_BASE - 1) +/**< General error */ +#define MPR_ERR_ABORTED (MPR_ERR_BASE - 2) +/**< Action aborted */ +#define MPR_ERR_ALREADY_EXISTS (MPR_ERR_BASE - 3) +/**< Item already exists */ +#define MPR_ERR_BAD_ARGS (MPR_ERR_BASE - 4) +/**< Bad arguments or paramaeters */ +#define MPR_ERR_BAD_FORMAT (MPR_ERR_BASE - 5) +/**< Bad input format */ +#define MPR_ERR_BAD_HANDLE (MPR_ERR_BASE - 6) +#define MPR_ERR_BAD_STATE (MPR_ERR_BASE - 7) +/**< Module is in a bad state */ +#define MPR_ERR_BAD_SYNTAX (MPR_ERR_BASE - 8) +/**< Input has bad syntax */ +#define MPR_ERR_BAD_TYPE (MPR_ERR_BASE - 9) +#define MPR_ERR_BAD_VALUE (MPR_ERR_BASE - 10) +#define MPR_ERR_BUSY (MPR_ERR_BASE - 11) +#define MPR_ERR_CANT_ACCESS (MPR_ERR_BASE - 12) +/**< Can't access the file or resource */ +#define MPR_ERR_CANT_COMPLETE (MPR_ERR_BASE - 13) +#define MPR_ERR_CANT_CREATE (MPR_ERR_BASE - 14) +/**< Can't create the file or resource */ +#define MPR_ERR_CANT_INITIALIZE (MPR_ERR_BASE - 15) +#define MPR_ERR_CANT_OPEN (MPR_ERR_BASE - 16) +/**< Can't open the file or resource */ +#define MPR_ERR_CANT_READ (MPR_ERR_BASE - 17) +/**< Can't read from the file or resource */ +#define MPR_ERR_CANT_WRITE (MPR_ERR_BASE - 18) +/**< Can't write to the file or resource */ +#define MPR_ERR_DELETED (MPR_ERR_BASE - 19) +#define MPR_ERR_NETWORK (MPR_ERR_BASE - 20) +#define MPR_ERR_NOT_FOUND (MPR_ERR_BASE - 21) +#define MPR_ERR_NOT_INITIALIZED (MPR_ERR_BASE - 22) +/**< Module or resource is not initialized */ +#define MPR_ERR_NOT_READY (MPR_ERR_BASE - 23) +#define MPR_ERR_READ_ONLY (MPR_ERR_BASE - 24) +/**< The operation timed out */ +#define MPR_ERR_TIMEOUT (MPR_ERR_BASE - 25) +#define MPR_ERR_TOO_MANY (MPR_ERR_BASE - 26) +#define MPR_ERR_WONT_FIT (MPR_ERR_BASE - 27) +#define MPR_ERR_WOULD_BLOCK (MPR_ERR_BASE - 28) +#define MPR_ERR_CANT_ALLOCATE (MPR_ERR_BASE - 29) +// MOB -- rename NO_MEMORY +#define MPR_ERR_MEMORY (MPR_ERR_BASE - 30) +#define MPR_ERR_CANT_DELETE (MPR_ERR_BASE - 31) +#define MPR_ERR_MAX (MPR_ERR_BASE - 32) + +/* + * Standard logging trace levels are 0 to 9 with 0 being the most verbose. + * the These are ored with the error source and type flags. 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 enabled. + */ +#define MPR_ERROR 1 /**< Hard error trace level */ +#define MPR_WARN 2 /**< Soft warning trace level */ +#define MPR_CONFIG 2 /**< Configuration settings trace level. */ +#define MPR_INFO 3 /**< Informational trace only */ +#define MPR_DEBUG 4 /**< Debug information trace level */ +#define MPR_VERBOSE 9 /**< Highest level of trace */ +#define MPR_LEVEL_MASK 0xf /**< Level mask */ + +/* + * Error source flags + */ +#define MPR_ERROR_SRC 0x10 /**< Originated from mprError */ +#define MPR_LOG_SRC 0x20 /**< Originated from mprLog */ +#define MPR_ASSERT_SRC 0x40 /**< Originated from mprAssert */ +#define MPR_FATAL_SRC 0x80 /**< Fatal error. Log and exit */ + +/* + * Log message type flags. Specify what kind of log / error message it is. + * Listener handlers examine this flag to determine if they should process + * the message.Assert messages are trapped when in DEV mode. Otherwise ignored. + */ +#define MPR_LOG_MSG 0x100 /**< Log trace message - not an error */ +#define MPR_ERROR_MSG 0x200 /**< General error */ +#define MPR_ASSERT_MSG 0x400 /**< Assert flags -- trap in debugger */ +#define MPR_USER_MSG 0x800 /**< User message */ + +/* + * Log output modifiers + */ +#define MPR_RAW 0x1000 /**< Raw trace output */ + +/* + * Error line number information. + */ +#define MPR_LINE(s) #s +#define MPR_LINE2(s) MPR_LINE(s) +#define MPR_LINE3 MPR_LINE2(__LINE__) +#define MPR_LOC __FILE__ ":" MPR_LINE3 + +/* + * Macros to pass file and line number information + * Use MPR_LOC_ARGS in normal user code. + * Use MPR_LOC_DEC in declarations. + * Use MPR_LOC_PASS in layered APIs to pass original line info down. + */ +#if BLD_FEATURE_ALLOC_LEAK_TRACK +#define MPR_LOC_ARGS(ctx) ctx, MPR_LOC +#define MPR_LOC_DEC(ctx, loc) MprCtx ctx, const char *loc +#define MPR_LOC_PASS(ctx, loc) ctx, loc +#else +#define MPR_LOC_ARGS(ctx) ctx +#define MPR_LOC_DEC(ctx, loc) MprCtx ctx +#define MPR_LOC_PASS(ctx, loc) ctx +#endif + +/******************************* Debug and Assert *****************************/ + +extern void mprBreakpoint(const char *loc, const char *msg); + +#if BLD_FEATURE_ASSERT + #define mprAssert(C) if (C) ; else mprStaticAssert(MPR_LOC, #C) +#else + #define mprAssert(C) if (1) ; else +#endif + +/********************************* Safe Strings *******************************/ +/* + * Unsafe functions that should not be used. Define UNSAFE_STRINGS_OK before + * including mpr.h if you really want to use these functions. A better approach + * is to undefine them just prior to using them in your C/C++ source file. + */ +#if BLD_FEATURE_SAFE_STRINGS + +#if BLD_FEATURE_PHP4_MODULE || BLD_FEATURE_PHP5_MODULE + #ifndef UNSAFE_FUNCTIONS_OK + #define UNSAFE_FUNCTIONS_OK 1 + #endif +#endif + +#ifndef UNSAFE_FUNCTIONS_OK + #define sprintf UseMprSprintfInstead + #define fprintf UseMprFprintfInstead + #define vsprintf UseMprVsprintfInstead + #define strtok UseMprStrTokInstead + #define gethostbyname UseMprGetHostByNameInstead + #define ctime UseMprCtimeInstead + #define asctime UseMprAsctimeInstead + #define localtime UseMprLocaltimeInstead + #define gmtime UseMprGmtimeInstead + #define malloc UseMprMallocInstead + #define free UseMprFreeInstead + #define realloc UseMprReallocInstead + #define strncpy UseMprStrcpyInstead + #define inet_ntoa UseMprInetToStrInstead + +#if !BREW + #define printf UseMprPrintfInstead +#endif + + #if FUTURE + #define strlen UseMprStrlenInstead + #define strcpy UseMprStrcpyInstead + #endif + +#endif /* UNSAFE_FUNCTIONS_OK */ +#endif /* BLD_FEATURE_SAFE_STRINGS */ + +/******************************************************************************/ + +struct MprBuf; +typedef int (*MprBufProc)(struct MprBuf* bp, void *arg); + +/** + * @overview Dynamic buffer structure + * @description MprBuf is a flexible, dynamic growable buffer structure. It + * utilizes a ring buffer mechanism and is suitable for high performance + * buffering in a variety of situations. + * @stability Prototype. + * @library libmpr. + * @see mprCreateBuf, mprFree, MprArray + */ +typedef struct MprBuf { + uchar *buf; /* Actual buffer for data */ + uchar *endbuf; /* Pointer one past the end of buffer */ + uchar *start; /* Pointer to next data char */ + uchar *end; /* Pointer one past the last data chr */ + int buflen; /* Current size of buffer */ + int maxsize; /* Max size the buffer can ever grow */ + int growBy; /* Next growth increment to use */ + MprBufProc refillProc; /* Auto-refill procedure */ + void *refillArg; /* Refill arg */ +} MprBuf; + +/** + * @overview File structure + * @description MprFile is the cross platform File I/O abstraction control + * structure. + * @stability Prototype. + * @library libmpr. + * @see mprOpen, mprClose, mprRead, mprWrite + */ +typedef struct MprFile +{ + MprBuf *buf; /* Buffer for I/O */ +#if BREW + IFile *fd; /* File handle */ +#else + int fd; +#endif +} MprFile; + +/** + * File information structure + * @overview File information structure + * @description MprFileInfo is the cross platform File information structure. + * @stability Prototype. + * @see mprGetFileInfo, mprOpen, mprClose, mprRead, mprWrite + */ +typedef struct MprFileInfo +{ + uint size; /* File length */ + uint ctime; /* Create time */ + uint mtime; /* Modified time */ + uint inode; /* Inode number */ + int isDir; /* Set if directory */ + int isReg; /* Set if a regular file */ +} MprFileInfo; + +/** + * @overview Mpr time structure. + * @description MprTime is the cross platform time abstraction structure. + * @stability Prototype. + * @library libmpr. + * @see mprGetTime + */ +typedef struct MprTime +{ + uint sec; /* Seconds */ + uint msec; /* Milliseconds */ +} MprTime; + + +/** + * @overview Generic array type + * @description The MprArray is a dynamic growable array suitable for storing + * pointers to arbitrary objects. + * @stability Prototype. + * @library libmpr. + * @see mprCreateItemArray, mprFree, MprBuf + */ +typedef struct MprArray +{ + int capacity; /* Current capacity of the array */ + int length; /* Count of used items */ + int incr; /* Growth increment */ + int maxSize; /* Maximum capacity */ + void **items; +} MprArray; + + +#if BLD_FEATURE_MULTITHREAD +/** + * @overview Multithreading lock control structure + * @description MprLock is used for multithread locking in multithreaded + * applications. + * @library libmpr. + * @see mprCreateLock, mprDestroyLock, mprLock, mprUnlock + */ +typedef struct +{ + #if WIN + CRITICAL_SECTION cs; /* O/S critical section */ + #endif + #if LINUX || MACOSX || SOLARIS + pthread_mutex_t cs; /* O/S critical section */ + #endif + #if VXWORKS + SEM_ID cs; /* Semaphore */ + #endif +} MprLock; +#endif + +/* + * Error and Logging callback + */ +typedef void (*MprLogHandler)(MPR_LOC_DEC(ctx, loc), int flags, + int level, const char *msg); + +/* + * Symbol table + * MOB -- rename hash + */ +typedef struct MprSymbol +{ + struct MprSymbol *next; /* Next symbol in hash chain */ + char *key; /* Symbol key */ + void *data; /* Pointer to symbol data */ + int bucket; /* Hash bucket index */ +} MprSymbol; + +typedef struct MprSymbolTable +{ + MprSymbol **buckets; + int hashSize; /* Size of the buckets array */ + int count; /* Number of symbols in the table */ +} MprSymbolTable; + + +/* + * Memory allocation error callback + */ +struct MprApp; +typedef int (*MprAllocCback)(struct MprApp *app, uint size, uint total, + bool granted); + + +/* + * Slab block pointer links + */ +typedef struct MprSlabBlock { + struct MprSlabBlock *next; +} MprSlabBlock; + + +#if BLD_FEATURE_ALLOC_STATS +/* + * Memory Slab Statistics + */ +typedef struct MprSlabStats { + uint allocCount; /* Number of allocated blocks */ + uint freeCount; /* Number of blocks on the slab freelist */ + uint peakAllocCount; /* Peak allocated */ + uint totalAllocCount; /* Total count of allocation calls */ + uint peakFreeCount; /* Peak on the free list */ + MprSlabBlock *next; +} MprSlabStats; +#endif + + +/* + * Slab control structure + */ +typedef struct MprSlab { + MprSlabBlock *next; + uint preAllocateIncr; /* Pre-allocation increment */ +#if BLD_FEATURE_ALLOC_STATS + MprSlabStats stats; +#endif +} MprSlab; + +/* + * Allocation stats (kept even in production code so we can detect memory + * allocation failures) + */ +typedef struct MprAllocStats +{ + uint bytesAllocated; /* Bytes currently allocated */ + uint peakAllocated; /* Peak bytes allocated */ + uint allocCount; /* Number of allocated blocks */ + uint redLine; /* Warn above this level */ + uint maxMemory; /* Max memory to allocate */ + uint errors; /* Allocation errors */ +} MprAllocStats; + +/* + * Memory allocation control + */ + +typedef struct MprAlloc { + MprSlab *slabs; /* Array[MPR_MAX_SLAB] of MprSlab */ + MprAllocCback cback; /* Memory allocation callback */ + MprAllocStats stats; /* Keep stats even in release */ + int inAllocException; /* Recursive protect */ +} MprAlloc; + + +/* + * MprApp State Flags + */ +#define MPR_APP_EXITING 0x1 /* App is exiting */ +#define MPR_APP_ALLOC_ERROR 0x2 /* App has allocation error */ + +/* MOB -- temporary */ +#define MPR_APP_NEED_GC 0x4 /* App needs GC */ + +/** + * @overview Primary MPR application control structure + * @description The MprApp structure stores critical application state + * information and is the root memory allocation context block. It is + * used as the MprCtx context for other memory allocations and is thus + * the ultimate parent of all allocated memory. + * \n\n + * The MprApp structure is allocated by the mprInit API. + */ +typedef struct MprApp +{ + uint magic; /* Corruption protection */ + MprFile *console; /* Stdout file */ + bool debugMode; /* Run in debug mode (no timers) */ + MprFile *error; /* Stderr file */ + int logLevel; /* Log trace level */ + MprFile *logFile; /* Log file */ + MprLogHandler logHandler; /* Current log handler callback */ + MprSymbolTable *table; + char *name; /* Product name */ + char *title; /* Product title */ + char *version; /* Product version */ + +#if BREW + uint classId; /* Brew class ID */ + IShell *shell; /* Brew shell object */ + IDisplay *display; /* Brew display object */ + IFileMgr *fileMgr; /* File manager */ + ITAPI *tapi; /* TAPI object */ + int displayHeight; /* Display height */ + int displayWidth; /* Display width */ + char *args; /* Command line args */ +#endif + + void *stackStart; /* Start of app stack */ + uint maxStack; /* Max stack size recorded */ + + MprAlloc alloc; /* Memory allocation data */ + int flags; /* App state flags */ + +#if BLD_FEATURE_MULTITHREAD + MprLock *globalLock; + MprLock *allocLock; +#endif +} MprApp; + + +/* + * String type. Minimum size is 8 words (32 bytes). + */ +#define MPR_MAX_INLINE_STR 24 + + +/* + * The block header structure for all allocated memory blocks (32 bytes) + * WARNING: Don't increase the size of this structure. It just fits into + * 32 bytes currently. Alignment requirements will double this size if you + * add one byte! + */ +typedef struct MprBlk +{ + MprApp *app; /* app is the top level alloc context */ + struct MprBlk *parent; /* Parent block */ + struct MprBlk *children; /* First child block */ + struct MprBlk *next; /* Next sibling */ + struct MprBlk *prev; /* Previous sibling */ + MprDestructor destructor; /* Destructor function (optional) */ + uint size; /* Size of block sans HDR_SIZE */ + uint flags; /* Allocation flags and magic number */ +#if BLD_FEATURE_ALLOC_LEAK_TRACK + const char *location; /* Allocating code (file + line) */ +#endif +} MprBlk; + +/******************************************************************************/ +/****************************** Internal Prototypes ***************************/ +/******************************************************************************/ + +extern void mprSignalAllocError(MprCtx ctx); + +/******************************************************************************/ +/********************************** Prototypes ********************************/ +/******************************************************************************/ + +extern MprApp *mprInit(MprAllocCback cback); +extern MprApp *mprInitEx(MprAllocCback cback, void *shell); +extern void mprTerm(MprApp *app, bool doStats); +extern void mprSignalExit(MprCtx ctx); +extern bool mprIsExiting(MprCtx ctx); +extern bool mprHasAllocError(MprCtx ctx); + +#if BLD_DEBUG && UNUSED +extern MprApp *mprGetApp(MprCtx ctx); +#else +#define mprGetApp(ctx) \ + (((MprBlk*) ((char*) ctx - MPR_BLK_HDR_SIZE))->app) +#endif + +/******************************************************************************/ + +extern int mprSetKeyValue(MprCtx ctx, const char *key, void *ptr); +/* MOB -- should this be delete or remove or unset */ +extern int mprRemoveKeyValue(MprCtx ctx, const char *key); +extern void *mprGetKeyValue(MprCtx ctx, const char *key); +/* MOB -- should be setAppName, getAppName */ +extern int mprSetAppName(MprCtx ctx, const char *name, const char *title, + const char *version); +extern const char *mprGetAppName(MprCtx ctx); +extern const char *mprGetAppTitle(MprCtx ctx); +extern const char *mprGetAppVersion(MprCtx ctx); + +/* + * File services + */ +extern void mprStopFileServices(MprCtx ctx); +extern int mprStartFileServices(MprCtx ctx); + +/* + * Item Array + */ +#define mprCreateItemArray(ctx, initialSize, maxSize) \ + mprCreateItemArrayInternal(MPR_LOC_ARGS(ctx), initialSize, \ + maxSize) + +extern MprArray *mprCreateItemArrayInternal(MPR_LOC_DEC(ctx, loc), + int initialSize, int maxSize); +/* MOB -- should be insert not add/delete or insert / remove */ +extern int mprAddItem(MprArray *array, void *item); +extern void mprClearItems(MprArray *array); +extern void mprClearAndFreeItems(MprArray *array); +extern int mprFindItem(MprArray *array, void *item); +extern void *mprGetFirstItem(MprArray *array, int *lastIndex); +extern void *mprGetItem(MprArray *array, int index); +extern int mprGetItemCapacity(MprArray *array); +extern int mprGetItemCount(MprArray *array); +extern void *mprGetNextItem(MprArray *array, int *lastIndex); +extern void *mprGetPrevItem(MprArray *array, int *lastIndex); +extern int mprRemoveItem(MprArray *array, void *item); +extern int mprRemoveItemByIndex(MprArray *array, int index); +extern int mprRemoveRangeOfItems(MprArray *array, int start, int end); + + +/* + * Printf replacements + */ +extern int mprSprintf(char *buf, int maxSize, const char *fmt, ...) + PRINTF_ATTRIBUTE(3,4); +extern int mprVsprintf(char *buf, int maxSize, const char *fmt, + va_list arg) PRINTF_ATTRIBUTE(3,0); +extern char *mprItoa(char *buf, int size, int value); +extern int mprAtoi(const char *str, int radix); + +extern int mprPrintf(MprCtx ctx, const char *fmt, ...) + PRINTF_ATTRIBUTE(2,3); +/* MOB -- NEED DOC */ +extern int mprErrorPrintf(MprCtx ctx, const char *fmt, ...) + PRINTF_ATTRIBUTE(2,3); +extern int mprStaticPrintf(MprCtx ctx, const char *fmt, ...) + PRINTF_ATTRIBUTE(2,3); +extern int mprPrintfError(MprCtx ctx, const char *fmt, ...) + PRINTF_ATTRIBUTE(2,3); +extern int mprFprintf(MprFile *file, const char *fmt, ...) + PRINTF_ATTRIBUTE(2,3); + +/* + * Safe string routines + */ +extern char *mprGetWordTok(char *buf, int bufsize, const char *str, + const char *delim, const char **tok); +extern int mprMemcpy(char *dest, int destMax, const char *src, + int nbytes); +extern int mprStrcat(char *dest, int max, const char *delim, + const char *src, ...); +extern int mprStrcpy(char *dest, int destMax, const char *src); + +extern int mprStrcmpAnyCase(const char *str1, const char *str2); +extern int mprStrcmpAnyCaseCount(const char *str1, const char *str2, + int len); +extern int mprStrlen(const char *src, int max); + +extern char *mprStrLower(char *str); +extern char *mprStrUpper(char *str); +extern char *mprStrTrim(char *str, const char *set); +extern char *mprStrTok(char *str, const char *delim, char **last); + +/* + * Symbol table + */ +extern MprSymbolTable *mprCreateSymbolTable(MprCtx ctx, int hashSize); +extern MprSymbol *mprGetFirstSymbol(MprSymbolTable *table); +extern MprSymbol *mprGetNextSymbol(MprSymbolTable *table, MprSymbol *last); +extern int mprGetSymbolCount(MprSymbolTable *table); +extern MprSymbol *mprInsertSymbol(MprSymbolTable *table, const char *key, + void *ptr); +extern void *mprLookupSymbol(MprSymbolTable *table, const char *key); +extern int mprRemoveSymbol(MprSymbolTable *table, const char *key); + +/* + * File I/O support + */ +extern void mprClose(MprFile *file); +extern int mprDelete(MprCtx ctx, const char *path); +extern int mprDeleteDir(MprCtx ctx, const char *path); +extern int mprGetFileInfo(MprCtx ctx, const char *path, MprFileInfo *info); +extern char *mprGets(MprFile *file, char *buf, uint size); +extern int mprMakeDir(MprCtx ctx, const char *path, int perms); +extern MprFile *mprOpen(MprCtx ctx, const char *path, int omode, int perms); +extern int mprPuts(MprFile *file, const char *buf, uint size); +extern int mprRead(MprFile *file, void *buf, uint size); +extern int mprSeek(MprFile *file, int seekType, long distance); +extern int mprWrite(MprFile *file, const void *buf, uint count); + +extern int mprMakeTempFileName(MprCtx ctx, char *buf, int bufsize, + const char *tmpDir); + + +/* + * Error handling and logging + */ +extern void mprSetLogHandler(MprCtx ctx, MprLogHandler handler); +extern MprLogHandler mprGetLogHandler(MprCtx ctx); + +extern void mprAssertError(MPR_LOC_DEC(ctx, loc), const char *msg); +extern void mprError(MPR_LOC_DEC(ctx, loc), + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +extern void mprFatalError(MPR_LOC_DEC(ctx, loc), + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +extern void mprLog(MprCtx ctx, int level, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +extern void mprRawLog(MprCtx ctx, const char *fmt, ...); +extern void mprStaticAssert(const char *loc, const char *msg); +extern void mprStaticError(MPR_LOC_DEC(ctx, loc), + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +extern void mprUserError(MPR_LOC_DEC(ctx, loc), + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); + +/* + * Dynamic Buffering routines + */ +extern MprBuf *mprCreateBuf(MprCtx ctx, int initialSize, int maxSize); +extern char *mprStealBuf(MprCtx ctx, MprBuf *bp); +extern void mprAddNullToBuf(MprBuf *bp); +extern void mprAdjustBufStart(MprBuf *bp, int size); +extern void mprAdjustBufEnd(MprBuf *bp, int size); +extern void mprCopyBufDown(MprBuf *bp); +extern void mprFlushBuf(MprBuf *bp); +extern int mprGetCharFromBuf(MprBuf *bp); +extern int mprGetBlockFromBuf(MprBuf *bp, uchar *buf, int len); +extern int mprGetBufLength(MprBuf *bp); +extern int mprGetBufLinearSpace(MprBuf *bp); +extern int mprGetBufLinearData(MprBuf *bp); +extern char *mprGetBufOrigin(MprBuf *bp); +extern int mprGetBufSize(MprBuf *bp); +extern int mprGetBufSpace(MprBuf *bp); +extern char *mprGetBufStart(MprBuf *bp); +extern char *mprGetBufEnd(MprBuf *bp); +extern int mprInsertCharToBuf(MprBuf *bp, int c); +extern int mprLookAtNextCharInBuf(MprBuf *bp); +extern int mprLookAtLastCharInBuf(MprBuf *bp); +extern int mprPutCharToBuf(MprBuf *bp, int c); +extern int mprPutBlockToBuf(MprBuf *bp, const char *str, int size); +extern int mprPutIntToBuf(MprBuf *bp, int i); +extern int mprPutStringToBuf(MprBuf *bp, const char *str); +extern int mprPutFmtStringToBuf(MprBuf *bp, const char *fmt, ...); +extern int mprRefillBuf(MprBuf *bp); +extern void mprResetBufIfEmpty(MprBuf *bp); +extern void mprSetBufSize(MprBuf *bp, int initialSize, int maxSize); +extern MprBufProc mprGetBufRefillProc(MprBuf *bp); +extern void mprSetBufRefillProc(MprBuf *bp, MprBufProc fn, void *arg); + +/* + * General other xPlatform routines + */ +extern const char *mprGetBaseName(const char *name); +extern bool mprGetDebugMode(MprCtx ctx); +extern char *mprGetDirName(char *buf, int bufsize, const char *path); +extern char *mprGetFullPathName(char *buf, int buflen, const char *path); +extern int mprGetLogLevel(MprCtx ctx); +extern int mprGetOsError(); + + +extern int mprMakeArgv(MprCtx ctx, const char *prog, const char *cmd, + char ***argv, int *argc); +extern int mprMakeDirPath(MprCtx ctx, const char *path); +extern void mprSetDebugMode(MprCtx ctx, bool on); +extern void mprSetLogLevel(MprCtx ctx, int level); +extern void mprSleep(MprCtx ctx, int msec); +extern void mprSetShell(MprCtx ctx, void *shell); +extern void *mprGetShell(MprCtx ctx); +extern void mprSetClassId(MprCtx ctx, uint classId); +extern uint mprGetClassId(MprCtx ctx); + +#if BREW +extern void mprSetDisplay(MprCtx ctx, void *display); +extern void *mprGetDisplay(MprCtx ctx); +extern void mprSetFileMgr(MprCtx ctx, void *fileMgr); +extern void *mprGetFileMgr(MprCtx ctx); +#else +extern char *mprInetToStr(char *buf, int size, const struct in_addr in); +#endif + +/* + * Memory allocation + */ +extern MprApp *mprAllocInit(MprAllocCback cback); +extern void mprAllocTerm(MprApp *app); +extern void mprAllocAbort(); + +extern void *mprAllocBlock(MPR_LOC_DEC(ctx, loc), uint size); +extern void *mprAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size); +extern void *mprReallocBlock(MPR_LOC_DEC(ctx, loc), void *ptr, uint size); +extern int mprFree(void *ptr); +extern int mprStealAllocBlock(MPR_LOC_DEC(ctx, loc), const void *ptr); +extern void *mprMemdupInternal(MPR_LOC_DEC(ctx, loc), const void *ptr, + uint size); +extern char *mprStrndupInternal(MPR_LOC_DEC(ctx, loc), const char *str, + uint size); +extern char *mprStrdupInternal(MPR_LOC_DEC(ctx, loc), const char *str); + +extern void *mprSlabAllocBlock(MPR_LOC_DEC(ctx, loc), uint size, uint inc); +extern void *mprSlabAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size, + uint inc); + +extern uint mprGetAllocBlockSize(MprCtx ctx); +extern uint mprGetAllocBlockCount(MprCtx ctx); +extern uint mprGetAllocBlockMemory(MprCtx ctx); +extern void *mprGetAllocParent(MprCtx ctx); +extern uint mprGetAllocatedMemory(MprCtx ctx); +extern uint mprGetPeakAllocatedMemory(MprCtx ctx); +extern uint mprGetAllocatedSlabMemory(MprCtx ctx); +extern int mprIsAllocBlockValid(MprCtx ctx); +extern int mprStackCheck(MprCtx ctx); +extern int mprStackSize(MprCtx ctx); +extern int mprGetAllocErrors(MprCtx ctx); +extern void mprClearAllocErrors(MprCtx ctx); + +extern MprDestructor mprSetDestructor(MprCtx ctx, MprDestructor destructor); +extern MprAllocCback mprSetAllocCallback(MprApp *app, MprAllocCback cback); +extern void mprSetAllocLimits(MprApp *app, uint redLine, uint maxMemory); + +#if BLD_FEATURE_ALLOC_STATS +extern MprSlabStats *mprGetSlabAllocStats(MprApp *app, int slabIndex); +extern MprAllocStats *mprGetAllocStats(MprApp *app); +extern void mprPrintAllocReport(MprApp *app, bool doBlocks, + const char *msg); +#endif + +#if BLD_DEBUG +extern int mprPrintAllocBlocks(MprCtx ctx, int indent); +extern const char *mprGetAllocLocation(MprCtx ptr); +#endif + +extern int mprValidateBlock(MprCtx ctx); +extern int mprValidateAllocTree(MprCtx ptr); +extern void mprSetRequiredAlloc(MprCtx ptr, bool recurse); + +/* + * Sprintf style allocators + */ +extern int mprAllocSprintf(MPR_LOC_DEC(ctx, loc), char **buf, int maxSize, + const char *fmt, ...) PRINTF_ATTRIBUTE(5,6); +extern int mprAllocVsprintf(MPR_LOC_DEC(ctx, loc), char **buf, int maxSize, + const char *fmt, va_list arg) PRINTF_ATTRIBUTE(5,0); +extern int mprAllocMemcpy(MPR_LOC_DEC(ctx, loc), char **dest, int destMax, + const void *src, int nbytes); +extern int mprAllocStrcat(MPR_LOC_DEC(ctx, loc), char **dest, int max, + const char *delim, const char *src, ...); +extern int mprAllocStrcpy(MPR_LOC_DEC(ctx, loc), char **dest, int max, + const char *src); +extern int mprReallocStrcat(MPR_LOC_DEC(ctx, loc), char **dest, int max, + int existingLen, const char *delim, const char *src, ...); + +/* + * MACROS: These are the convenience macros to automatically supply file + * names and line numbers when debugging. + */ +#define mprNew(ctx) new(MPR_LOC_ARGS(ctx)) + +#define mprAlloc(ctx, size) mprAllocBlock(MPR_LOC_ARGS(ctx), size) + +#define mprAllocZeroed(ctx, size) mprAllocZeroedBlock(MPR_LOC_ARGS(ctx), size) + +#define mprSlabAlloc(ctx, size, inc) \ + ((type*) mprSlabAllocBlock(MPR_LOC_ARGS(ctx), size, inc)) + +#define mprSlabAllocZeroed(ctx, size, inc) \ + ((type*) mprSlabAllocBlock(MPR_LOC_ARGS(ctx), size, inc)) + +#define mprRealloc(ctx, ptr, size) mprReallocBlock(MPR_LOC_ARGS(ctx), ptr, size) + +#define mprMemdup(ctx, ptr, size) \ + mprMemdupInternal(MPR_LOC_ARGS(ctx), ptr, size) + +#define mprStrdup(ctx, str) mprStrdupInternal(MPR_LOC_ARGS(ctx), str) + +#define mprStrndup(ctx, str, size) mprStrndupDebug(MPR_LOC_ARGS(ctx), str, size) + +/* + * Allocate type macros + */ +#define mprAllocType(ctx, type) \ + ((type*) mprAllocBlock(MPR_LOC_ARGS(ctx), sizeof(type))) + +#define mprAllocTypeZeroed(ctx, type) \ + ((type*) mprAllocZeroedBlock(MPR_LOC_ARGS(ctx), sizeof(type))) + +#define mprSlabAllocType(ctx, type, inc) \ + ((type*) mprSlabAllocBlock(MPR_LOC_ARGS(ctx), sizeof(type), inc)) + +#define mprSlabAllocTypeZeroed(ctx, type, inc) \ + ((type*) mprSlabAllocZeroedBlock(MPR_LOC_ARGS(ctx), sizeof(type), \ + inc)) + +/* + * Multithread locking + */ +#if BLD_FEATURE_MULTITHREAD +extern void mprInitThreads(MprApp *app); +extern void mprTermThreads(MprApp *app); +extern MprLock *mprCreateLock(MprCtx ctx); +extern void mprDestroyLock(MprLock *lock); +extern void mprLock(MprLock *lock); +extern int mprTryLock(MprLock *lock); +extern void mprUnlock(MprLock *lock); +extern void mprGlobalLock(MprCtx ctx); +extern void mprGlobalUnlock(MprCtx ctx); +extern int mprGetCurrentThreadID(); +#else +/* + * Disable multithreading + */ +#define mprInitThreads(ctx, app) +#define mprTermThreads(app) +#define mprCreateLock(ctx) +#define mprDestroyLock(lock) +#define mprLock(lock) +#define mprTryLock(lock) +#define mprUnlock(lock) +#define mprGlobalLock(app) +#define mprGlobalUnlock(app) +#define mprGetCurrentThreadID() +#endif + +/* + * Time + */ +extern MprTime *mprGetTime(MprCtx ctx, MprTime *tp); +extern int mprGetTimeRemaining(MprCtx ctx, MprTime mark, uint timeout); +extern int mprGetElapsedTime(MprCtx ctx, MprTime mark); +extern int mprCompareTime(MprTime *t1, MprTime *t2); +extern uint mprSubtractTime(MprTime *t1, MprTime *t2); +extern void mprAddElapsedToTime(MprTime *time, uint elapsed); + +#if !BREW +extern int mprAsctime(MprCtx ctx, char *buf, int bufsize, + const struct tm *timeptr); +extern int mprCtime(MprCtx ctx, char *buf, int bufsize, + const time_t *timer); +extern struct tm *mprLocaltime(MprCtx ctx, struct tm *timep, time_t *now); +extern struct tm *mprGmtime(MprCtx ctx, time_t* now, struct tm *timep); +extern int mprRfcTime(MprCtx ctx, char *buf, int bufsize, + const struct tm *timep); +#endif /* !BREW */ + +/* + * Host name + */ +extern struct hostent* mprGetHostByName(MprCtx ctx, const char *name); + +#if WIN +extern int mprReadRegistry(MprCtx ctx, char **buf, int max, + const char *key, const char *val); +extern int mprWriteRegistry(MprCtx ctx, const char *key, const char *name, + const char *value); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _h_MPR */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim:tw=78 + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/source4/lib/appweb/ejs-2.0/mpr/mprAlloc.c b/source4/lib/appweb/ejs-2.0/mpr/mprAlloc.c new file mode 100644 index 0000000000..6fcaa63a6c --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprAlloc.c @@ -0,0 +1,1775 @@ +/** + * @file mprAlloc.c + * @brief Memory Allocation + * @overview + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 ***********************************/ + +#define UNSAFE_FUNCTIONS_OK 1 + +#include "mpr.h" + +/******************************* Local Defines ********************************/ +/* + * Set to 1 to disable slab based allocations + */ +#define NO_SLAB 0 + +/* + * Validation mode is quite slow + */ +#define VALIDATE_ALLOC 0 +#if VALIDATE_ALLOC +#define VALIDATE_BLOCK(ptr) mprValidateBlock(ptr) +#else +#define VALIDATE_BLOCK(ptr) +#endif + +/* + * Align on 4 bytes if squeeze, Otherwize on 16 bytes. + */ +#define HDR_SIZE MPR_BLK_HDR_SIZE + +#define APP_MAGIC 0xa571cb80 +#define ALLOC_MAGIC 0xe814ecc0 + +/* + * This must be at least one word to ensure that the smallest allocation is + * 4 bytes. Slab allocations need at least one word to store their next ptr. + */ +#define ALLOC_ALIGN(x) (((x)+3)&~3) +#define GET_HDR(ptr) ((MprBlk*) (((char*) (ptr)) - HDR_SIZE)) +#define GET_PTR(bp) ((void*) (((char*) (bp)) + HDR_SIZE)) +#define VALID_HDR(bp) (((bp)->flags & ~0x3F) == ALLOC_MAGIC) +#define VALID_BLK(ptr) (VALID_HDR(GET_HDR(ptr))) + +/* + * In production releases, mprAssert will compile out (not be included) + * but CHECK_HDR will remain even in production builds. + */ +#define CHECK_HDR(bp) \ + if (1) { if (! VALID_HDR(bp)) { mprAllocAbort(); } } else + +/* + * Chunk the slabs into 32 byte increments. + * This allows for allocations up to 512 bytes via slabs and maximizes + * sharing of slab allocations. + * + * Index map: + * 0 == 32 bytes + * 1 == 64 bytes + * 2 == 96 bytes + * 3 == 128 bytes + * 4 == 160 bytes + * 5 == 192 bytes + * 6 == 224 bytes + * 7 == 256 bytes + * 8 == 288 bytes + * 9 == 320 bytes + * 10 == 352 bytes + * 11 == 384 bytes + * 12 == 416 bytes + * 13 == 448 bytes + * 14 == 480 bytes + * 15 == 512 bytes + */ +#define SLAB_ALIGN(size) ((size + 31) & ~31) +#define GET_SLAB(size) (size >> 6) + +/* + * Block flags + */ +#define ALLOC_FLAGS_FREE 0x1 /* Block is free */ +#define ALLOC_FLAGS_FREEING 0x2 /* Block is being freed */ +#define ALLOC_FLAGS_SLAB_BLOCK 0x4 /* Block was allocated from slab */ +#define ALLOC_FLAGS_REQUIRED 0x8 /* Block is required by alloc */ +#define ALLOC_FLAGS_KEEP 0x10 /* Keep block - don't mprFree */ +#define ALLOC_FLAGS_DONT_OS_FREE 0x20 /* Don't return mem to O/S */ +#define ALLOC_FLAGS_IS_SLAB 0x40 /* Block is a slab */ + +#if BLD_DEBUG && !BREW +/* + * Set this address to break when this address is allocated or freed. This is + * a block address (not a user ptr). + */ +static MprBlk *stopAlloc; +#endif + +#if !BREW +static MprCtx rootCtx; /* Root context if none supplied */ +#endif + +/***************************** Forward Declarations ***************************/ + +static int mprAllocException(MPR_LOC_DEC(ptr, loc), uint size, bool granted); +static void slabFree(MprBlk *bp); +static int growSlab(MPR_LOC_DEC(ctx, loc), MprSlab *slab, uint size, uint inc); + +/******************************************************************************/ +/* + * Put first in file so it is easy to locate in a debugger + */ + +void mprBreakpoint(const char *loc, const char *msg) +{ +} + +/******************************************************************************/ +#if (WIN || BREW_SIMULATOR) && BLD_DEBUG + +int crtReportHook(int type, char *msg, int *retval) +{ + printf("%s\n", msg); + *retval = 0; + return TRUE; +} + +#endif +/******************************************************************************/ +/* + * Initialize the memory subsystem + */ + +MprApp *mprAllocInit(MprAllocCback cback) +{ + MprAllocStats *stats; + MprApp *app; + MprSlab *slab; + MprBlk *bp, *sp; + int i; + + bp = malloc(sizeof(MprApp) + HDR_SIZE); + mprAssert(bp); + if (bp == 0) { + if (cback) { + (*cback)(0, sizeof(MprApp), 0, 0); + } + return 0; + } + memset(bp, 0, sizeof(MprApp) + HDR_SIZE); + + bp->parent = bp; + bp->size = sizeof(MprApp); + bp->flags = ALLOC_MAGIC; + bp->next = bp->prev = bp; + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + bp->location = MPR_LOC; +#endif + + app = (MprApp*) GET_PTR(bp); + app->magic = APP_MAGIC; + + app->alloc.cback = cback; + app->stackStart = (void*) &app; + + bp->app = app; + + app->alloc.slabs = mprAllocZeroedBlock(MPR_LOC_PASS(app, MPR_LOC), + sizeof(MprSlab) * MPR_MAX_SLAB); + if (app->alloc.slabs == 0) { + mprFree(app); + return 0; + } + + /* + * The slab control structures must not be freed. Set keep to safeguard + * against accidents. + */ + sp = GET_HDR(app->alloc.slabs); + sp->flags |= ALLOC_FLAGS_KEEP; + + for (i = 0; i < MPR_MAX_SLAB; i++) { + /* + * This is overriden by requestors calling slabAlloc + */ + slab = &app->alloc.slabs[i]; + slab->preAllocateIncr = MPR_SLAB_DEFAULT_INC; + } + + /* + * Keep aggregated stats even in production code + */ + stats = &app->alloc.stats; + stats->bytesAllocated += sizeof(MprApp); + if (stats->bytesAllocated > stats->peakAllocated) { + stats->peakAllocated = stats->bytesAllocated; + } + stats->allocCount++; + +#if !BREW + rootCtx = app; +#endif +#if (WIN || BREW_SIMULATOR) && BLD_DEBUG + _CrtSetReportHook(crtReportHook); +#endif + return app; +} + +/******************************************************************************/ +/* + * Terminate the alloc module + */ + +void mprAllocTerm(MprApp *app) +{ + MprSlab *slabs; + MprBlk *appBlk, *slabBlk; + + /* + * Must do a carefully ordered cleanup. Need to free all children blocks + * before freeing the slab memory. Save a local pointer to the slabs. + */ + slabs = app->alloc.slabs; + + /* + * Free the app and all children. Set DONT_OS_FREE to prevent free() being + * called on app itself. We need that so we can free the slabs below. + */ + appBlk = GET_HDR(app); + appBlk->flags |= ALLOC_FLAGS_DONT_OS_FREE; + mprFree(app); + + /* + * Slabs are initially marked don't free. We must preserve them while all + * other blocks are freed. Then we clear the don't free flag and free. + * Now we don't have an app structure which is used by mprFree. We must + * fake it. + */ + slabBlk = GET_HDR(slabs); + slabBlk->flags &= ~ALLOC_FLAGS_KEEP; + mprFree(slabs); + + /* + * Now we can finally free the memory for the app structure + */ + free(appBlk); +} + +/******************************************************************************/ +/* + * Allocate a block + */ + +void *mprAllocBlock(MPR_LOC_DEC(ctx, loc), uint size) +{ + MprAllocStats *stats; + MprBlk *bp, *parent; + MprApp *app; + int diff; + + mprAssert(size > 0); + + if (ctx == 0) { +#if BREW + mprAssert(ctx); + return 0; +#else + ctx = rootCtx; +#endif + } + if (size == 0) { + size = 1; + } + + mprAssert(VALID_BLK(ctx)); + parent = GET_HDR(ctx); + mprAssert(VALID_HDR(parent)); + + CHECK_HDR(parent); + + size = ALLOC_ALIGN(size); + + app = parent->app; + + stats = &app->alloc.stats; + + mprLock(app->allocLock); + + stats->bytesAllocated += size + HDR_SIZE; + if (stats->bytesAllocated > stats->peakAllocated) { + stats->peakAllocated = stats->bytesAllocated; + } + + /* + * Prevent allocation if over the maximum + */ + if (stats->maxMemory && stats->bytesAllocated > stats->maxMemory) { + stats->bytesAllocated -= (size + HDR_SIZE); + mprUnlock(app->allocLock); + if (mprAllocException(MPR_LOC_PASS(ctx, loc), size, 0) < 0) { + return 0; + } + mprLock(app->allocLock); + } + + if ((bp = malloc(size + HDR_SIZE)) == 0) { + mprAssert(bp); + stats->errors++; + mprUnlock(app->allocLock); + mprAllocException(MPR_LOC_PASS(ctx, loc), size, 0); + return 0; + } + +#if BLD_DEBUG + memset(bp, 0xf7, size + HDR_SIZE); +#endif + +#if BLD_DEBUG && !BREW + if (bp == stopAlloc) { + mprBreakpoint(MPR_LOC, "breakOnAddr"); + } +#endif + + /* + * Warn if allocation puts us over the red line + */ + if (stats->redLine && stats->bytesAllocated > stats->redLine) { + mprUnlock(app->allocLock); + if (mprAllocException(MPR_LOC_PASS(ctx, loc), size, 1) < 0) { + return 0; + } + mprLock(app->allocLock); + } + + bp->size = size; + bp->flags = ALLOC_MAGIC; + bp->destructor = 0; + + bp->parent = parent; + + if (parent->children == 0) { + parent->children = bp; + bp->next = bp->prev = bp; + + } else { + /* + * Append to the end of the list. Preserve alloc order + */ + bp->next = parent->children; + bp->prev = parent->children->prev; + parent->children->prev->next = bp; + parent->children->prev = bp; + } + + bp->children = 0; + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + bp->location = loc; +#endif + + bp->app = parent->app; + + VALIDATE_BLOCK(GET_PTR(bp)); + + stats->allocCount++; + + /* + * Monitor stack usage + */ + diff = (int) bp->app->stackStart - (int) &stats; + if (diff < 0) { + app->maxStack -= diff; + app->stackStart = (void*) &stats; + diff = 0; + } + + if ((uint) diff > app->maxStack) { + app->maxStack = diff; + } + mprUnlock(app->allocLock); + + return GET_PTR(bp); +} + +/******************************************************************************/ +/* + * Allocate and zero a block + */ + +void *mprAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size) +{ + void *newBlock; + + MprBlk *bp; + + bp = GET_HDR(ctx); + mprAssert(VALID_BLK(ctx)); + + newBlock = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size); + if (newBlock) { + memset(newBlock, 0, size); + } + return newBlock; +} + +/******************************************************************************/ +/* + * Free a block of memory. Free all children recursively. + */ + +int mprFree(void *ptr) +{ + MprAllocStats *stats; + MprBlk *bp, *parent, *cp, *firstChild, *prev; + MprApp *app; + + if (ptr == 0) { + return 0; + } + + mprAssert(VALID_BLK(ptr)); + VALIDATE_BLOCK(ptr); + + bp = GET_HDR(ptr); + +#if BLD_DEBUG && !BREW + if (bp == stopAlloc) { + mprBreakpoint(MPR_LOC, "breakOnAddr"); + } +#endif + + mprAssert(bp); + mprAssert(VALID_HDR(bp)); + + CHECK_HDR(bp); + + /* + * Test if already freed + */ + mprAssert(! (bp->flags & ALLOC_FLAGS_FREE)); + if (bp->flags & ALLOC_FLAGS_FREE) { + return 0; + } + + /* + * Return if recursive freeing or this is a permanent block + */ + app = bp->app; + mprLock(app->allocLock); + if (bp->flags & (ALLOC_FLAGS_FREEING | ALLOC_FLAGS_KEEP)) { + mprUnlock(app->allocLock); + return 0; + } + bp->flags |= ALLOC_FLAGS_FREEING; + + + /* + * Call any destructors + */ + if (bp->destructor) { + mprUnlock(app->allocLock); + if ((bp->destructor)(ptr) < 0) { + return -1; + } + mprLock(app->allocLock); + bp->destructor = 0; + } + + /* + * Free the children. Free in reverse order so firstChild is preserved + * during the list scan as an end of list marker. + */ + if ((firstChild = bp->children) != 0) { + cp = firstChild->prev; + while (cp != firstChild) { + + mprAssert(VALID_HDR(cp)); + VALIDATE_BLOCK(GET_PTR(cp)); + + prev = cp->prev; + + /* + * FUTURE - OPT. Make this inline + */ + mprFree(GET_PTR(cp)); + + cp = prev; + } + + mprFree(GET_PTR(firstChild)); + + /* + * Just for clarity + */ + bp->children = 0; + } + + parent = bp->parent; + + mprAssert(VALID_HDR(parent)); + + /* + * Unlink from the parent + */ + if (parent->children == bp) { + if (bp->next == bp) { + parent->children = 0; + } else { + parent->children = bp->next; + } + } + + /* + * Remove from the sibling chain + */ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + + bp->flags |= ALLOC_FLAGS_FREE; + + /* + * Release the memory. If from a slab, return to the slab. Otherwise, + * return to the O/S. + */ + if (bp->flags & ALLOC_FLAGS_SLAB_BLOCK) { + slabFree(bp); + + } else { + mprAssert(bp); + + /* + * Update the stats + */ + stats = &bp->app->alloc.stats; + stats->bytesAllocated -= (bp->size + HDR_SIZE); + mprAssert(stats->bytesAllocated >= 0); + + stats->allocCount--; + mprAssert(stats->allocCount >= 0); + +#if BLD_DEBUG && !BREW + if (bp == stopAlloc) { + mprBreakpoint(MPR_LOC, "breakOnAddr"); + } +#endif + + /* + * Return to the O/S + */ + if (! (bp->flags & ALLOC_FLAGS_DONT_OS_FREE)) { + free(bp); + } + } + /* OPT */ + if (app != ptr) { + mprUnlock(app->allocLock); + } + + return 0; +} + +/******************************************************************************/ +/* + * Rallocate a block + */ + +void *mprReallocBlock(MPR_LOC_DEC(ctx, loc), void *ptr, uint size) +{ + MprBlk *bp, *newbp, *firstChild, *cp; + MprApp *app; + void *newPtr; + + mprAssert(VALID_BLK(ctx)); + mprAssert(size > 0); + + if (ptr == 0) { + return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size); + } + + mprAssert(VALID_BLK(ptr)); + bp = GET_HDR(ptr); + mprAssert(bp); + mprAssert(VALID_HDR(bp)); + + CHECK_HDR(bp); + + if (size < bp->size) { + return ptr; + } + + newPtr = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size); + if (newPtr == 0) { + bp->flags &= ~ALLOC_FLAGS_FREE; + free(bp); + return 0; + } + + newbp = GET_HDR(newPtr); + mprAssert(newbp->size >= size); + memcpy((char*) newbp + HDR_SIZE, (char*) bp + HDR_SIZE, bp->size); + mprAssert(newbp->size >= size); + + /* + * Fix the next / prev pointers + */ + app = bp->app; + mprLock(app->allocLock); + newbp->next->prev = newbp; + newbp->prev->next = newbp; + + /* + * Need to fix the parent pointer of all children + */ + if ((firstChild = newbp->children) != 0) { + cp = firstChild; + do { + cp->parent = newbp; + cp = cp->next; + } while (cp != firstChild); + } + + /* + * May need to set the children pointer of our parent + */ + if (newbp->parent->children == bp) { + newbp->parent->children = newbp; + } + + /* + * Free the original block + */ + mprFree(ptr); + + mprUnlock(app->allocLock); + + return GET_PTR(newbp); +} + +/******************************************************************************/ +/* + * Allocate a block from a slab + */ + +void *mprSlabAllocBlock(MPR_LOC_DEC(ctx, loc), uint size, uint inc) +{ + +#if NO_SLAB + return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size); +#else + + MprBlk *parent, *bp; + MprSlabBlock *sb; + MprApp *app; + MprSlab *slab; + int slabIndex; + + if (ctx == 0) { + mprAssert(ctx); + return 0; + } + + mprAssert(size > 0); + mprAssert(VALID_BLK(ctx)); + + parent = GET_HDR(ctx); + mprAssert(VALID_HDR(parent)); + + CHECK_HDR(parent); + + size = SLAB_ALIGN(size); + + app = parent->app; + mprAssert(app); + + slabIndex = GET_SLAB(size); + + if (slabIndex < 0 || slabIndex >= MPR_MAX_SLAB) { + return mprAllocBlock(MPR_LOC_PASS(ctx, loc), size); + } + + /* + * Dequeue a block from the slab. "sb" will point to the user data + * portion of the block (i.e. after the MprBlk header). Slabs must be + * allocated off the "slabs" context to ensure they don't get freed + * until after all other blocks are freed. + */ + mprLock(app->allocLock); + slab = &app->alloc.slabs[slabIndex]; + if ((sb = slab->next) == 0) { + if (growSlab(MPR_LOC_ARGS(parent->app->alloc.slabs), + slab, size, inc) < 0) { + mprUnlock(app->allocLock); + return 0; + } + sb = slab->next; + } + mprAssert(sb); + + /* + * Dequeue the block + */ + slab->next = sb->next; + +#if BLD_FEATURE_ALLOC_STATS +{ + MprSlabStats *slabStats; + /* + * Update the slab stats + */ + slabStats = &slab->stats; + slabStats->totalAllocCount++; + slabStats->freeCount--; + slabStats->allocCount++; + if (slabStats->allocCount > slabStats->peakAllocCount) { + slabStats->peakAllocCount = slabStats->allocCount; + } +} +#endif /* BLD_FEATURE_ALLOC_STATS */ + + bp = GET_HDR(sb); + +#if BLD_DEBUG && !BREW + if (bp == stopAlloc) { + mprBreakpoint(MPR_LOC, "breakOnAddr"); + } +#endif + + bp->size = size; + bp->flags = ALLOC_MAGIC | ALLOC_FLAGS_SLAB_BLOCK; + bp->destructor = 0; + + bp->parent = parent; + + if (parent->children == 0) { + parent->children = bp; + bp->next = bp->prev = bp; + + } else { + /* + * Append to the end of the list. Preserve alloc order + */ + bp->next = parent->children; + bp->prev = parent->children->prev; + parent->children->prev->next = bp; + parent->children->prev = bp; + } + + bp->children = 0; + + bp->app = app; + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + bp->location = loc; +#endif + mprUnlock(app->allocLock); + + return GET_PTR(bp); +#endif +} + +/******************************************************************************/ +/* + * Return a block back to its slab + */ + +static void slabFree(MprBlk *bp) +{ + MprSlab *slab; + MprApp *app; + void *ptr; + int slabIndex; + + mprAssert(VALID_HDR(bp)); + + slabIndex = GET_SLAB(bp->size); + mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB); + + if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) { + mprLock(bp->app->allocLock); + slab = &bp->app->alloc.slabs[slabIndex]; + app = bp->app; + +#if BLD_DEBUG + memset(bp, 0xfc, bp->size + HDR_SIZE); +#endif + + ptr = GET_PTR(bp); + ((MprSlabBlock*) ptr)->next = slab->next; + slab->next = ((MprSlabBlock*) ptr); + +#if BLD_FEATURE_ALLOC_STATS +{ + MprSlabStats *slabStats; + slabStats = &slab->stats; + + slabStats->freeCount++; + slabStats->allocCount--; + + if (slabStats->freeCount >= slabStats->peakFreeCount) { + slabStats->peakFreeCount = slabStats->freeCount; + } +} +#endif + mprUnlock(app->allocLock); + } +} + +/******************************************************************************/ +/* + * Grow the slab and return the next free block + * Must be called locked. + */ + +static int growSlab(MPR_LOC_DEC(ctx, loc), MprSlab *slab, uint size, uint inc) +{ + MprBlk *bp; + MprSlabBlock *sb; + int i, chunkSize, len; + + mprAssert(VALID_BLK(ctx)); + mprAssert(slab); + mprAssert(size > 0); + + /* + * Take the maximum requested by anyone + */ + slab->preAllocateIncr = max(slab->preAllocateIncr, inc); + + /* + * We allocate an array of blocks each of user "size" bytes. + */ + chunkSize = HDR_SIZE + size; + len = chunkSize * slab->preAllocateIncr; + bp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len); + +#if BLD_DEBUG + memset(bp, 0xf1, len); +#endif + + if (bp == 0) { + mprAssert(0); + return MPR_ERR_MEMORY; + } + bp->flags |= ALLOC_FLAGS_IS_SLAB; + + /* + * We store the slab information in the user data portion + */ + sb = (MprSlabBlock*) GET_PTR(bp); + + + sb = (MprSlabBlock*) ((char*) sb + len - chunkSize); + for (i = slab->preAllocateIncr - 1; i >= 0; i--) { + sb->next = slab->next; + slab->next = sb; + sb = (MprSlabBlock*) ((char*) sb - chunkSize); + } + +#if BLD_FEATURE_ALLOC_STATS +{ + MprSlabStats *stats; + stats = &slab->stats; + stats->freeCount += slab->preAllocateIncr; + if (stats->freeCount > stats->peakFreeCount) { + stats->peakFreeCount = stats->freeCount; + } +} +#endif + + return 0; +} + +/******************************************************************************/ +/* + * Set the pre-allocate amount + */ + +int mprSetSlabPreAllocate(MprCtx ctx, int slabIndex, int preAllocateIncr) +{ + MprApp *app; + MprSlab *slab; + + mprAssert(VALID_BLK(ctx)); + mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB); + mprAssert(preAllocateIncr > 0); + + if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) { + app = mprGetApp(ctx); + slab = &app->alloc.slabs[slabIndex]; + slab->preAllocateIncr = preAllocateIncr; + } else { + return MPR_ERR_BAD_ARGS; + } + return 0; +} + +/******************************************************************************/ + +void *mprSlabAllocZeroedBlock(MPR_LOC_DEC(ctx, loc), uint size, uint inc) +{ + void *newBlock; + + mprAssert(VALID_BLK(ctx)); + mprAssert(size > 0); + + newBlock = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), size, inc); + if (newBlock) { + memset(newBlock, 0, size); + } + return newBlock; +} + +/******************************************************************************/ +/* + * Internal strdup function. Will use the slab allocator for small strings + */ + +char *mprStrdupInternal(MPR_LOC_DEC(ctx, loc), const char *str) +{ + char *newp; + int len; + + mprAssert(VALID_BLK(ctx)); + + if (str == 0) { + str = ""; + } + + len = strlen(str) + 1; + + if (len < MPR_SLAB_STR_MAX) { + newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX, + MPR_SLAB_STR_INC); + } else { + newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len); + } + + if (newp) { + memcpy(newp, str, len); + } + + return newp; +} + +/******************************************************************************/ +/* + * Internal strndup function. Will use the slab allocator for small strings + */ + +char *mprStrndupInternal(MPR_LOC_DEC(ctx, loc), const char *str, uint size) +{ + char *newp; + uint len; + + mprAssert(VALID_BLK(ctx)); + + if (str == 0) { + str = ""; + } + len = strlen(str) + 1; + len = min(len, size); + + if (len < MPR_SLAB_STR_MAX) { + newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX, + MPR_SLAB_STR_INC); + } else { + newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), len); + } + + if (newp) { + memcpy(newp, str, len); + } + + return newp; +} + +/******************************************************************************/ +/* + * Internal memcpy function. Will use the slab allocator for small strings + */ + +void *mprMemdupInternal(MPR_LOC_DEC(ctx, loc), const void *ptr, uint size) +{ + char *newp; + + mprAssert(VALID_BLK(ctx)); + + if (size < MPR_SLAB_STR_MAX) { + newp = mprSlabAllocBlock(MPR_LOC_PASS(ctx, loc), MPR_SLAB_STR_MAX, + MPR_SLAB_STR_INC); + } else { + newp = mprAllocBlock(MPR_LOC_PASS(ctx, loc), size); + } + + if (newp) { + memcpy(newp, ptr, size); + } + + return newp; +} + +/******************************************************************************/ +/* + * Steal a block from one context and insert in another + */ + +int mprStealAllocBlock(MPR_LOC_DEC(ctx, loc), const void *ptr) +{ + MprBlk *bp, *parent; + + if (ptr == 0) { + return 0; + } + + mprAssert(VALID_BLK(ctx)); + mprAssert(VALID_BLK(ptr)); + + bp = GET_HDR(ptr); + +#if BLD_DEBUG && !BREW + if (bp == stopAlloc) { + mprBreakpoint(MPR_LOC, "breakOnAddr"); + } +#endif + + mprAssert(bp); + mprAssert(VALID_HDR(bp)); + mprAssert(ptr != mprGetAllocParent(ptr)); + + CHECK_HDR(bp); + + mprAssert(bp->prev); + mprAssert(bp->prev->next); + mprAssert(bp->next); + mprAssert(bp->next->prev); + + parent = bp->parent; + mprAssert(VALID_HDR(parent)); + + mprLock(bp->app->allocLock); + if (parent->children == bp) { + if (bp->next == bp) { + parent->children = 0; + } else { + parent->children = bp->next; + } + } + + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + + parent = GET_HDR(ctx); + mprAssert(VALID_HDR(parent)); + bp->parent = parent; + + if (parent->children == 0) { + parent->children = bp; + bp->next = bp->prev = bp; + + } else { + bp->next = parent->children; + bp->prev = parent->children->prev; + parent->children->prev->next = bp; + parent->children->prev = bp; + } + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + bp->location = loc; +#endif + + VALIDATE_BLOCK(GET_PTR(bp)); + + mprUnlock(bp->app->allocLock); + + return 0; +} + +/******************************************************************************/ + +void mprSetRequiredAlloc(MprCtx ptr, bool recurse) +{ + MprBlk *bp, *firstChild, *cp; + + bp = GET_HDR(ptr); + + bp->flags |= ALLOC_FLAGS_REQUIRED; + + if (recurse && (firstChild = bp->children) != 0) { + cp = firstChild; + do { + mprSetRequiredAlloc(GET_PTR(cp), recurse); + cp = cp->next; + } while (cp != firstChild); + } +} + +/******************************************************************************/ +/* + * Monitor stack usage. Return true if the stack has grown + */ + +int mprStackCheck(MprCtx ptr) +{ + MprApp *app; + int size; + + mprAssert(VALID_BLK(ptr)); + + app = mprGetApp(ptr); + + size = (int) app->stackStart - (int) &app; + if (size < 0) { + app->maxStack -= size; + app->stackStart = (void*) &app; + size = 0; + } + if ((uint) size > app->maxStack) { + app->maxStack = size; + return 1; + } + return 0; +} + +/******************************************************************************/ +/* + * Return the stack size + */ + +int mprStackSize(MprCtx ptr) +{ + MprApp *app; + + mprAssert(VALID_BLK(ptr)); + + app = mprGetApp(ptr); + return app->maxStack; +} + +/******************************************************************************/ + +static int mprAllocException(MPR_LOC_DEC(ctx, loc), uint size, bool granted) +{ + MprApp *app; + MprAlloc *alloc; + int rc; + + mprAssert(VALID_BLK(ctx)); + + app = mprGetApp(ctx); + alloc = &app->alloc; + + if (alloc->cback == 0) { + return 0; + } + + mprLock(app->allocLock); + if (alloc->inAllocException == 0) { + alloc->inAllocException = 1; + mprUnlock(app->allocLock); + + rc = (alloc->cback)(app, size, alloc->stats.bytesAllocated, granted); + + mprLock(app->allocLock); + app->alloc.inAllocException = 0; + mprUnlock(app->allocLock); + + return rc; + } + return 0; +} + +/******************************************************************************/ + +void mprSetAllocLimits(MprApp *app, uint redLine, uint maxMemory) +{ + app->alloc.stats.redLine = redLine; + app->alloc.stats.maxMemory = maxMemory; +} + +/******************************************************************************/ + +MprAllocCback mprSetAllocCallback(MprApp *app, MprAllocCback cback) +{ + MprAllocCback old; + + mprAssert(app); + mprAssert(VALID_BLK(app)); + + old = app->alloc.cback; + app->alloc.cback = cback; + return old; +} + +/******************************************************************************/ + +uint mprGetAllocBlockSize(MprCtx ptr) +{ + MprBlk *bp; + + mprAssert(VALID_BLK(ptr)); + + if (ptr == 0) { + return 0; + } + + bp = GET_HDR(ptr); + mprAssert(VALID_HDR(bp)); + + CHECK_HDR(bp); + + return bp->size; +} + +/******************************************************************************/ +/* + * Return the total block count used by a block including all children + */ + +uint mprGetAllocBlockCount(MprCtx ptr) +{ + MprBlk *bp, *firstChild, *cp; + uint count; + + mprAssert(VALID_BLK(ptr)); + + if (ptr == 0) { + return 0; + } + + bp = GET_HDR(ptr); + mprAssert(VALID_HDR(bp)); + + /* + * Add one for itself + */ + count = 1; + if ((firstChild = bp->children) != 0) { + cp = firstChild; + do { + count += mprGetAllocBlockCount(GET_PTR(cp)); + cp = cp->next; + } while (cp != firstChild); + } + return count; +} + +/******************************************************************************/ +/* + * Return the total of all memory allocated including slabs + */ + +uint mprGetAllocBlockMemory(MprCtx ptr) +{ + MprBlk *bp, *firstChild, *cp; + uint count; + + mprAssert(VALID_BLK(ptr)); + + if (ptr == 0) { + return 0; + } + + bp = GET_HDR(ptr); + mprAssert(VALID_HDR(bp)); + + count = bp->size + HDR_SIZE; + if ((firstChild = bp->children) != 0) { + cp = firstChild; + do { + count += mprGetAllocBlockMemory(GET_PTR(cp)); + cp = cp->next; + } while (cp != firstChild); + } + return count; +} + +/******************************************************************************/ +#if BLD_FEATURE_ALLOC_LEAK_TRACK + +const char *mprGetAllocLocation(MprCtx ptr) +{ + MprBlk *bp; + + if (ptr == 0) { + return 0; + } + mprAssert(VALID_BLK(ptr)); + + bp = GET_HDR(ptr); + mprAssert(VALID_HDR(bp)); + return bp->location; +} + +#endif +/******************************************************************************/ + +void *mprGetAllocParent(MprCtx ptr) +{ + MprBlk *bp; + + mprAssert(VALID_BLK(ptr)); + + if (ptr == 0) { + return 0; + } + + bp = GET_HDR(ptr); + mprAssert(VALID_HDR(bp)); + + CHECK_HDR(bp); + + return GET_PTR(bp->parent); +} + +/******************************************************************************/ + +MprAllocStats *mprGetAllocStats(MprApp *app) +{ + mprAssert(VALID_BLK(app)); + + return &app->alloc.stats; +} + +/******************************************************************************/ +#if BLD_FEATURE_ALLOC_STATS + +MprSlabStats *mprGetSlabAllocStats(MprApp *app, int slabIndex) +{ + MprSlab *slab; + + mprAssert(VALID_BLK(app)); + + if (0 <= slabIndex && slabIndex < MPR_MAX_SLAB) { + slab = &app->alloc.slabs[slabIndex]; + return &slab->stats; + } + + mprAssert(0 <= slabIndex && slabIndex < MPR_MAX_SLAB); + return 0; +} + +#endif /* BLD_FEATURE_ALLOC_STATS */ +/******************************************************************************/ +#if BLD_DEBUG + +int mprPrintAllocBlocks(MprCtx ptr, int indent) +{ + MprBlk *bp, *firstChild, *cp; + const char *location; + int subTotal, size, indentSpaces, code; + + subTotal = 0; + + bp = GET_HDR(ptr); + + if (! (bp->flags & ALLOC_FLAGS_REQUIRED)) { + size = bp->size + HDR_SIZE; + + /* + * Take one level off because we don't trace app + */ + indentSpaces = indent; + + if (bp->flags & ALLOC_FLAGS_REQUIRED) { + code = 'R'; + } else if (bp->flags & ALLOC_FLAGS_IS_SLAB) { + code = 'S'; + } else { + code = ' '; + } + +#if BLD_FEATURE_ALLOC_LEAK_TRACK + location = bp->location; +#else + location = ""; +#endif + mprLog(bp->app, 0, + "%c %.*s %-16s %.*s size %5d has %3d deps, total %6d", code, + indentSpaces, " ", + mprGetBaseName(location), + 8 - indent, " ", + size, + mprGetAllocBlockCount(GET_PTR(bp)), + mprGetAllocBlockMemory(GET_PTR(bp)) + /* (uint) bp */ + ); + + subTotal += size; + } + + if ((firstChild = bp->children) != 0) { + cp = firstChild; + do { + subTotal += mprPrintAllocBlocks(GET_PTR(cp), indent + 2); + cp = cp->next; + } while (cp != firstChild); + } + + return subTotal; +} + +#endif +/******************************************************************************/ +#if BLD_FEATURE_ALLOC_STATS +/* + * Print a memory allocation report that includes a list of allocated blocks + * and a statistics summary + */ + +void mprPrintAllocReport(MprApp *app, bool printBlocks, const char *msg) +{ + MprSlabStats *stats; + uint total; + int i, size; + + mprAssert(VALID_BLK(app)); + + if (msg) { + mprLog(app, 0, " "); + mprLog(app, 0, "%s", msg); + } + +#if BLD_DEBUG + /* + * Do block stats + */ + if (printBlocks) { + int sum; + mprLog(app, 0, " "); + sum = mprPrintAllocBlocks(app, 0); + if (sum) { + mprLog(app, 0, " Sum of blocks %d", sum); + } else { + mprLog(app, 0, " None"); + } + } +#endif + + /* + * Do Slab stats + */ + mprLog(app, 0, " "); + mprLog(app, 0, "MPR Slab Memory Stats"); + mprLog(app, 0, " "); + + mprLog(app, 0, + " Index Size Total Allocated Free PeakAlloc PeakFree TotalAlloc"); + + total = 0; + for (i = 0; i < MPR_MAX_SLAB; i++) { + stats = &app->alloc.slabs[i].stats; + size = 1 << (i + 5); + if (stats->totalAllocCount > 0) { + mprLog(app, 0, " %2d %6d %8d %9d %6d %9d %8d %10d", + i, size, size * (stats->allocCount + stats->freeCount), + stats->allocCount, stats->freeCount, + stats->peakAllocCount, stats->peakFreeCount, + stats->totalAllocCount); + total += size * (stats->allocCount + stats->freeCount); + } + } + mprLog(app, 0, " "); + mprLog(app, 0, "MPR Total Allocated Slab RAM: %10d", total); + mprLog(app, 0, "MPR Total Allocated RAM: %10d", + mprGetAllocatedMemory(app)); + mprLog(app, 0, "MPR Peak Allocated RAM: %10d", + mprGetPeakAllocatedMemory(app)); + mprLog(app, 0, " "); +} + +/******************************************************************************/ +/* + * Return the total memory allocated. + */ + +uint mprGetAllocatedMemory(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + + return app->alloc.stats.bytesAllocated; +} + +/******************************************************************************/ +/* + * Return the peak memory allocated. + */ + +uint mprGetPeakAllocatedMemory(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + + return app->alloc.stats.peakAllocated; +} + +/******************************************************************************/ +/* + * Return memory in the MPR slab. This excludes the EJS slabs + */ + +uint mprGetAllocatedSlabMemory(MprCtx ctx) +{ + MprApp *app; + MprSlabStats *stats; + uint total; + int i, size; + + app = mprGetApp(ctx); + + total = 0; + for (i = 0; i < MPR_MAX_SLAB; i++) { + stats = &app->alloc.slabs[i].stats; + size = 1 << (i + 5); + if (stats->totalAllocCount > 0) { + total += size * (stats->allocCount + stats->freeCount); + } + } + return total; +} + +#endif /* BLD_FEATURE_ALLOC_STATS */ +/******************************************************************************/ + +MprDestructor mprSetDestructor(MprCtx ptr, MprDestructor destructor) +{ + MprDestructor old; + MprBlk *bp; + + mprAssert(VALID_BLK(ptr)); + + if (ptr == 0) { + return 0; + } + + bp = GET_HDR(ptr); + + mprAssert(bp); + mprAssert(VALID_HDR(bp)); + mprAssert(ptr != mprGetAllocParent(ptr)); + + CHECK_HDR(bp); + + old = bp->destructor; + bp->destructor = destructor; + + return old; +} + +/******************************************************************************/ + +int mprIsAllocBlockValid(MprCtx ptr) +{ + MprBlk *bp; + + bp = GET_HDR(ptr); + return (bp && VALID_HDR(bp)); +} + +/******************************************************************************/ +#if VALIDATE_ALLOC +/* + * Exhaustive validation of the block and its children. Does not go recursive + * as it would be too slow. + */ + +int mprValidateBlock(MprCtx ptr) +{ + MprBlk *bp, *parent, *cp, *firstChild; + int count; + + mprAssert(ptr); + mprAssert(VALID_BLK(ptr)); + + bp = GET_HDR(ptr); + + mprAssert(bp); + mprAssert(VALID_HDR(bp)); + mprAssert(VALID_HDR(bp->parent)); + + if (ptr != bp->app) { + mprAssert(bp != bp->parent); + } + mprAssert(! (bp->flags & ALLOC_FLAGS_FREE)); + mprAssert(! (bp->flags & ALLOC_FLAGS_FREEING)); + + /* + * + */ + count = 0; + parent = bp->parent; + + if ((firstChild = bp->children) != 0) { + cp = firstChild; + mprAssert((int) cp != 0xfeefee); + do { + mprAssert(bp->next->prev == bp); + mprAssert(bp->prev->next == bp); + mprAssert(bp->prev->parent == parent); + mprAssert(bp->next->parent == parent); + + count++; + cp = cp->next; + + if (bp->next == bp) { + mprAssert(bp->prev == bp); + if (ptr != bp->app) { + mprAssert(parent->children == bp); + } + } + if (bp->prev == bp) { + mprAssert(bp->next == bp); + if (ptr != bp->app) { + mprAssert(parent->children == bp); + } + } + } while (cp != firstChild); + } + + return 0; +} + +#endif +/******************************************************************************/ +/* + * Validate a block and all children + */ + +int mprValidateAllocTree(MprCtx ptr) +{ +#if VALIDATE_ALLOC + MprBlk *bp, *cp, *firstChild; + + mprAssert(ptr); + mprAssert(VALID_BLK(ptr)); + + bp = GET_HDR(ptr); + + mprValidateBlock(GET_PTR(bp)); + + if ((firstChild = bp->children) != 0) { + cp = firstChild; + do { + mprValidateAllocTree(GET_PTR(cp)); + cp = cp->next; + } while (cp != firstChild); + } + +#endif + return 0; +} + +/******************************************************************************/ +#if UNUSED && FUTURE +/* + * Exhaustive validation of the block and its children. Does not go recursive + * as it would be too slow. + */ + +int mprValidateSlabs(MprApp *app) +{ + MprSlab *slab; + MprSlabStats *slabStats; + MprSlabBlock *sp; + int count, i; + + for (i = 0; i < MPR_MAX_SLAB; i++) { + slab = &app->alloc.slabs[i]; + slabStats = &slab->stats; + + count = 0; + for (sp = slab->next; sp; sp = sp->next) { + count++; + } + mprAssert(count == (int) slabStats->freeCount); + } + return 0; +} + +#endif +/******************************************************************************/ + +void mprAllocAbort() +{ +#if BREW + printf("Bad block header"); +#else + exit(255); +#endif +} + +/******************************************************************************/ +#undef mprGetApp +/* + * Get the root parent from any block (which is the MprApp structure) + */ + +MprApp *mprGetApp(MprCtx ptr) +{ + MprBlk *bp; + + mprAssert(ptr); + + bp = GET_HDR(ptr); + mprAssert(VALID_HDR(bp)); + + CHECK_HDR(bp); + + mprAssert(bp->app->magic == APP_MAGIC); + + return bp->app; +} + +/******************************************************************************/ + +int mprGetAllocErrors(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + return app->alloc.stats.errors; +} + +/******************************************************************************/ + +void mprClearAllocErrors(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + app->alloc.stats.errors = 0; +} + +/******************************************************************************/ +/* + * 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-2.0/mpr/mprArray.c b/source4/lib/appweb/ejs-2.0/mpr/mprArray.c new file mode 100644 index 0000000000..95b0a14450 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprArray.c @@ -0,0 +1,385 @@ +/** + * @file mprArray.c + * @brief Growable array structure + * @overview Simple growable array structure. + * @remarks Most routines in this file are not thread-safe. It is the callers + * responsibility to perform all thread synchronization. + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/******************************************************************************/ +/* + * Create a general growable array structure. Use mprFree to destroy. + */ + +MprArray *mprCreateItemArrayInternal(MPR_LOC_DEC(ctx, loc), int initialSize, + int maxSize) +{ + MprArray *array; + int size; + + mprAssert(initialSize <= maxSize); + + array = (MprArray*) mprSlabAllocZeroedBlock(MPR_LOC_PASS(ctx, loc), + sizeof(MprArray), 0); + if (array == 0) { + return 0; + } + + if (initialSize == 0) { + initialSize = MPR_ARRAY_INCR; + } + if (maxSize == 0) { + maxSize = MAXINT; + } + size = initialSize * sizeof(void*); + + array->items = (void**) mprSlabAllocBlock(MPR_LOC_PASS(array, loc), + size, 0); + + if (array->items == 0) { + mprFree(array); + return 0; + } + + array->capacity = initialSize; + array->maxSize = maxSize; + array->incr = min(initialSize * 2, (array->maxSize - array->length)); + array->length = 0; + + return array; +} + +/******************************************************************************/ +/* + * Add an item to the array + */ + +int mprAddItem(MprArray *array, void *item) +{ + int memsize, index, len; + + mprAssert(array); + mprAssert(array->capacity >= 0); + mprAssert(array->length >= 0); + + if (array->length < array->capacity) { + /* + * Room to fit in the current capacity + */ + index = array->length++; + array->items[index] = item; + return index; + } + mprAssert(array->length == array->capacity); + + /* + * Need to grow the array + */ + if (array->capacity >= array->maxSize) { + mprAssert(array->capacity < array->maxSize); + return MPR_ERR_TOO_MANY; + } + + len = array->capacity + array->incr; + memsize = len * sizeof(void*); + + /* + * Grow the array of items + */ + + array->items = (void**) mprRealloc(array, array->items, memsize); + + /* + * Zero the new portion + */ + memset(&array->items[array->capacity], 0, sizeof(void*) * array->incr); + array->capacity = len; + + array->incr = min(array->incr * 2, (array->maxSize - array->length)); + + index = array->length++; + array->items[index] = item; + + return index; +} + +/******************************************************************************/ +/* + * Remove an item from the array + */ + +int mprRemoveItem(MprArray *array, void *item) +{ + int index; + + mprAssert(array); + mprAssert(array->capacity > 0); + mprAssert(array->length > 0); + + index = mprFindItem(array, item); + if (index < 0) { + return index; + } + + return mprRemoveItemByIndex(array, index); +} + +/******************************************************************************/ +/* + * Remove an index from the array + */ + +int mprRemoveItemByIndex(MprArray *array, int index) +{ + void **items; + int i; + + mprAssert(array); + mprAssert(array->capacity > 0); + mprAssert(index >= 0 && index < array->capacity); + mprAssert(array->items[index] != 0); + mprAssert(array->length > 0); + + if (index < 0 || index >= array->length) { + return MPR_ERR_NOT_FOUND; + } + + /* + * Copy down to compress + */ + items = array->items; + for (i = index; i < (array->length - 1); i++) { + items[i] = items[i + 1]; + } + array->length--; + +#if BLD_DEBUG + if (array->length < array->capacity) { + items[array->length] = 0; + } +#endif + return 0; +} + +/******************************************************************************/ + +int mprRemoveRangeOfItems(MprArray *array, int start, int end) +{ + void **items; + int i, count; + + mprAssert(array); + mprAssert(array->capacity > 0); + mprAssert(array->length > 0); + mprAssert(start > end); + + if (start < 0 || start >= array->length) { + return MPR_ERR_NOT_FOUND; + } + if (end < 0 || end >= array->length) { + return MPR_ERR_NOT_FOUND; + } + if (start > end) { + return MPR_ERR_BAD_ARGS; + } + + /* + * Copy down to compress + */ + items = array->items; + count = end - start; + for (i = start; i < (array->length - count); i++) { + items[i] = items[i + count]; + } + array->length -= count; + +#if BLD_DEBUG + if (array->length < array->capacity) { + for (i = array->length; i < array->capacity; i++) { + items[i] = 0; + } + } +#endif + return 0; +} + +/******************************************************************************/ + +void *mprGetItem(MprArray *array, int index) +{ + mprAssert(array); + + if (index < 0 || index >= array->length) { + return 0; + } + return array->items[index]; +} + +/******************************************************************************/ + +void *mprGetFirstItem(MprArray *array, int *last) +{ + mprAssert(array); + mprAssert(last); + + if (array == 0) { + return 0; + } + + *last = 0; + + if (array->length == 0) { + return 0; + } + return array->items[0]; +} + +/******************************************************************************/ + +void *mprGetNextItem(MprArray *array, int *last) +{ + int index; + + mprAssert(array); + mprAssert(last); + mprAssert(*last >= 0); + + index = *last; + + if (++index < array->length) { + *last = index; + return array->items[index]; + } + return 0; +} + +/******************************************************************************/ + +void *mprGetPrevItem(MprArray *array, int *last) +{ + int index; + + mprAssert(array); + mprAssert(last); + mprAssert(*last >= 0); + + if (array == 0) { + return 0; + } + + index = *last; + + if (--index < array->length && index >= 0) { + *last = index; + return array->items[index]; + } + return 0; +} + +/******************************************************************************/ + +int mprGetItemCount(MprArray *array) +{ + mprAssert(array); + + if (array == 0) { + return 0; + } + + return array->length; +} + +/******************************************************************************/ + +int mprGetItemCapacity(MprArray *array) +{ + mprAssert(array); + + if (array == 0) { + return 0; + } + + return array->capacity; +} + +/******************************************************************************/ + +void mprClearAndFreeItems(MprArray *array) +{ + int i; + + mprAssert(array); + + for (i = 0; i < array->length; i++) { + mprFree(array->items[i]); + } +} + +/******************************************************************************/ + +void mprClearItems(MprArray *array) +{ + mprAssert(array); + + array->length = 0; +} + +/******************************************************************************/ + +int mprFindItem(MprArray *array, void *item) +{ + int i; + + mprAssert(array); + + for (i = 0; i < array->length; i++) { + if (array->items[i] == item) { + return i; + } + } + return MPR_ERR_NOT_FOUND; +} + +/******************************************************************************/ +/* + * 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-2.0/mpr/mprBuf.c b/source4/lib/appweb/ejs-2.0/mpr/mprBuf.c new file mode 100644 index 0000000000..ba9888a9fc --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprBuf.c @@ -0,0 +1,535 @@ +/** + * @file mprBuf.c + * @brief Dynamic buffer module + * @overview + * @remarks + */ + +/******************************************************************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/**************************** Forward Declarations ****************************/ + +static int grow(MprBuf *bp); + +/*********************************** Code *************************************/ +/* + * Create a new buffer. "maxsize" is the limit to which the buffer can + * ever grow. -1 means no limit. The buffer can ever only fix maxsize-1 bytes. + * "initialSize" is used to define the amount to increase the size of the + * buffer each time if it becomes full. (Note: grow() will exponentially + * increase this number for performance.) + */ + +MprBuf *mprCreateBuf(MprCtx ctx, int initialSize, int maxSize) +{ + MprBuf *bp; + + if (initialSize <= 0) { + initialSize = MPR_DEFAULT_ALLOC; + } + bp = mprAllocTypeZeroed(ctx, MprBuf); + bp->growBy = MPR_BUFSIZE; + bp->maxsize = 0; + mprSetBufSize(bp, initialSize, maxSize); + return bp; +} + +/******************************************************************************/ +/* + * Set the initial buffer parameters and create the first buffer + */ + +void mprSetBufSize(MprBuf *bp, int initialSize, int max) +{ + mprAssert(initialSize > 0); + + if (max > 0 && initialSize > max) { + initialSize = max; + } + + if (bp->buf && bp->growBy > 0) { + mprFree(bp->buf); + } + + bp->buf = (uchar*) mprAlloc(bp, initialSize); + bp->growBy = initialSize; + bp->maxsize = max; + bp->buflen = initialSize; + bp->endbuf = &bp->buf[bp->buflen]; + bp->start = bp->buf; + bp->end = bp->buf; + *bp->start = '\0'; +} + +/******************************************************************************/ + +char *mprStealBuf(MprCtx ctx, MprBuf *bp) +{ + char *str; + + str = (char*) bp->start; + + mprStealAllocBlock(MPR_LOC_ARGS(ctx), bp->start); + + bp->start = bp->end = bp->buf = bp->endbuf = 0; + bp->buflen = 0; + + return str; +} + +/******************************************************************************/ + +void mprAddNullToBuf(MprBuf *bp) +{ + *((char*) bp->end) = (char) '\0'; +} + +/******************************************************************************/ + +void mprAdjustBufEnd(MprBuf *bp, int size) +{ + mprAssert(bp->buflen == (bp->endbuf - bp->buf)); + mprAssert(size < bp->buflen); + + bp->end += size; + if (bp->end >= bp->endbuf) { + bp->end -= bp->buflen; + } + if (bp->end < bp->buf) { + bp->end += bp->buflen; + } + + if (bp->end >= bp->endbuf) { + mprAssert(bp->end < bp->endbuf); + mprFlushBuf(bp); + } +} + +/******************************************************************************/ +/* + * Adjust the start pointer after a user copy + */ + +void mprAdjustBufStart(MprBuf *bp, int size) +{ + mprAssert(bp->buflen == (bp->endbuf - bp->buf)); + mprAssert(size < bp->buflen); + + bp->start += size; + while (bp->start >= bp->endbuf) { + bp->start -= bp->buflen; + } + while (bp->start < bp->buf) { + bp->start += bp->buflen; + } + + /* + * Flush the buffer if the start pointer is corrupted via a bad size + */ + if (bp->start >= bp->endbuf) { + mprAssert(bp->start < bp->endbuf); + mprFlushBuf(bp); + } +} + + +/******************************************************************************/ + +void mprFlushBuf(MprBuf *bp) +{ + bp->start = bp->buf; + bp->end = bp->buf; +} + +/******************************************************************************/ + +int mprGetCharFromBuf(MprBuf *bp) +{ + int c; + + if (bp->start == bp->end) { + return -1; + } + c = (uchar) *bp->start++; + if (bp->start >= bp->endbuf) { + bp->start = bp->buf; + } + return c; +} + +/******************************************************************************/ + +int mprGetBlockFromBuf(MprBuf *bp, uchar *buf, int size) +{ + int thisLen, bytesRead; + + mprAssert(buf); + mprAssert(size > 0); + mprAssert(bp->buflen == (bp->endbuf - bp->buf)); + + /* + * Get the max bytes in a straight copy + */ + bytesRead = 0; + while (size > 0) { + thisLen = mprGetBufLinearData(bp); + thisLen = min(thisLen, size); + if (thisLen <= 0) { + break; + } + + memcpy(buf, bp->start, thisLen); + buf += thisLen; + bp->start += thisLen; + size -= thisLen; + bytesRead += thisLen; + + if (bp->start >= bp->endbuf) { + bp->start = bp->buf; + } + } + return bytesRead; +} + +/******************************************************************************/ + +int mprGetBufLength(MprBuf *bp) +{ + if (bp->start > bp->end) { + return (bp->buflen + (bp->end - bp->start)); + } else { + return (bp->end - bp->start); + } +} + +/******************************************************************************/ + +int mprGetBufLinearData(MprBuf *bp) +{ + return min(mprGetBufLength(bp), (bp->endbuf - bp->start)); +} + +/******************************************************************************/ + +int mprGetBufLinearSpace(MprBuf *bp) +{ + int len = mprGetBufLength(bp); + int space = bp->buflen - len - 1; + return min((bp->endbuf - bp->end), space); +} + +/******************************************************************************/ + +int mprGetBufSize(MprBuf *bp) +{ + return bp->buflen; +} + +/******************************************************************************/ + +int mprGetBufSpace(MprBuf *bp) +{ + return bp->buflen - mprGetBufLength(bp) - 1; +} + +/******************************************************************************/ + +char *mprGetBufOrigin(MprBuf *bp) +{ + return (char*) bp->buf; +} + +/******************************************************************************/ + +char *mprGetBufStart(MprBuf *bp) +{ + return (char*) bp->start; +} + +/******************************************************************************/ + +char *mprGetBufEnd(MprBuf *bp) +{ + return (char*) bp->end; +} + +/******************************************************************************/ + +int mprInsertCharToBuf(MprBuf *bp, int c) +{ + char *cp; + int space; + + mprAssert(bp->buflen == (bp->endbuf - bp->buf)); + + space = bp->buflen - mprGetBufLength(bp) - 1; + if (space < (int) sizeof(char)) { + if (!grow(bp)) { + return -1; + } + } + if (bp->start <= bp->buf) { + bp->start = bp->endbuf; + } + cp = (char*) bp->start; + *--cp = (char) c; + bp->start = (uchar *) cp; + return 0; +} + +/******************************************************************************/ + +int mprLookAtNextCharInBuf(MprBuf *bp) +{ + if (bp->start == bp->end) { + return -1; + } + return *bp->start; +} + +/******************************************************************************/ + +int mprLookAtLastCharInBuf(MprBuf *bp) +{ + if (bp->start == bp->end) { + return -1; + } + return (bp->end == bp->buf) ? bp->endbuf[-1] : bp->end[-1]; +} + +/******************************************************************************/ + +int mprPutCharToBuf(MprBuf *bp, int c) +{ + char *cp; + int space; + + mprAssert(bp->buflen == (bp->endbuf - bp->buf)); + + space = bp->buflen - mprGetBufLength(bp) - 1; + if (space < (int) sizeof(char)) { + if (! grow(bp)) { + return -1; + } + } + + cp = (char*) bp->end; + *cp++ = (char) c; + bp->end = (uchar *) cp; + if (bp->end >= bp->endbuf) { + bp->end = bp->buf; + } + *((char*) bp->end) = (char) '\0'; + return 0; +} + +/******************************************************************************/ + +int mprPutBlockToBuf(MprBuf *bp, const char *str, int size) +{ + int thisLen, bytes, space; + + mprAssert(str); + mprAssert(size >= 0); + mprAssert(bp->buflen == (bp->endbuf - bp->buf)); + + /* + * Add the max we can in one copy + */ + bytes = 0; + while (size > 0) { + space = mprGetBufLinearSpace(bp); + thisLen = min(space, size); + if (thisLen <= 0) { + if (! grow(bp)) { + break; + } + space = mprGetBufLinearSpace(bp); + thisLen = min(space, size); + } + + memcpy(bp->end, str, thisLen); + str += thisLen; + bp->end += thisLen; + size -= thisLen; + bytes += thisLen; + + if (bp->end >= bp->endbuf) { + bp->end = bp->buf; + } + } + *((char*) bp->end) = (char) '\0'; + return bytes; +} + +/******************************************************************************/ + +int mprPutStringToBuf(MprBuf *bp, const char *str) +{ + return mprPutBlockToBuf(bp, str, strlen(str)); +} + +/******************************************************************************/ + +int mprPutFmtStringToBuf(MprBuf *bp, const char *fmt, ...) +{ + va_list ap; + char *buf; + int rc, len, space; + + va_start(ap, fmt); + space = mprGetBufLinearSpace(bp); + + /* + * Add max that the buffer can grow + */ + space += (bp->maxsize - bp->buflen - 1); + + len = mprAllocVsprintf(MPR_LOC_ARGS(bp), &buf, space, fmt, ap); + rc = mprPutBlockToBuf(bp, buf, len); + + mprFree(buf); + va_end(ap); + return rc; +} + +/******************************************************************************/ +/* + * Grow the buffer to fit new data. Return 1 if the buffer can grow. + * Grow using the growBy size specified when creating the buffer. + */ + +static int grow(MprBuf *bp) +{ + uchar *newbuf; + + if (bp->maxsize > 0 && bp->buflen >= bp->maxsize) { + return 0; + } + + newbuf = (uchar*) mprAlloc(bp, bp->buflen + bp->growBy); + if (bp->buf) { + memcpy(newbuf, bp->buf, bp->buflen); + mprFree(bp->buf); + } + + bp->buflen += bp->growBy; + bp->end = newbuf + (bp->end - bp->buf); + bp->start = newbuf + (bp->start - bp->buf); + bp->buf = newbuf; + bp->endbuf = &bp->buf[bp->buflen]; + + /* + * Increase growBy to reduce overhead + */ + bp->growBy *= 2; + if (bp->maxsize > 0 && (bp->buflen + bp->growBy) > bp->maxsize) { + bp->growBy = bp->maxsize - bp->buflen; + } + return 1; +} + +/******************************************************************************/ +/* + * Add a number to the buffer (always null terminated). + */ + +int mprPutIntToBuf(MprBuf *bp, int i) +{ + char numBuf[16]; + int rc; + + mprItoa(numBuf, sizeof(numBuf), i); + rc = mprPutStringToBuf(bp, numBuf); + *((char*) bp->end) = (char) '\0'; + + return rc; +} + +/******************************************************************************/ + +void mprCopyBufDown(MprBuf *bp) +{ + if (mprGetBufLength(bp) == 0) { + mprFlushBuf(bp); + return; + } + memmove(bp->buf, bp->start, (bp->end - bp->start)); + bp->end -= (bp->start - bp->buf); + bp->start = bp->buf; +} + +/******************************************************************************/ + +MprBufProc mprGetBufRefillProc(MprBuf *bp) +{ + return bp->refillProc; +} + +/******************************************************************************/ + +void mprSetBufRefillProc(MprBuf *bp, MprBufProc fn, void *arg) +{ + bp->refillProc = fn; + bp->refillArg = arg; +} + +/******************************************************************************/ + +int mprRefillBuf(MprBuf *bp) +{ + return (bp->refillProc) ? (bp->refillProc)(bp, bp->refillArg) : 0; +} + +/******************************************************************************/ + +void mprResetBufIfEmpty(MprBuf *bp) +{ + if (mprGetBufLength(bp) == 0) { + mprFlushBuf(bp); + } +} + +/******************************************************************************/ +/* + * 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-2.0/mpr/mprGenFile.c b/source4/lib/appweb/ejs-2.0/mpr/mprGenFile.c new file mode 100644 index 0000000000..517e43853f --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprGenFile.c @@ -0,0 +1,336 @@ +/** + * @file mprGenFile.c + * @brief Generic File services + * @overview + * @remarks + * See OS/mprFile.c for the per O/S portions + */ + +/******************************************************************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/****************************** Forward Declarations **************************/ +#if !BREW + +static int closeDestructor(void *data); + +/************************************ Code ************************************/ + +int mprStartFileServices(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + app->console = mprAllocTypeZeroed(ctx, MprFile); + app->error = mprAllocTypeZeroed(ctx, MprFile); + + /* + * We assume that STDOUT is 1 and STDERR is 2 + */ + app->console->fd = 1; + app->error->fd = 2; + + return 0; +} + +/******************************************************************************/ + +void mprStopFileServices(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + + mprFree(app->console); + app->console = 0; + mprFree(app->error); + app->error = 0; +} + +/******************************************************************************/ + +MprFile *mprOpen(MprCtx ctx, const char *path, int omode, int perms) +{ + MprFile *file; + + mprAssert(path && *path); + + file = mprAllocTypeZeroed(ctx, MprFile); + + file->fd = open(path, omode, perms); + if (file->fd < 0) { + mprFree(file); + return 0; + } + + mprSetDestructor(file, closeDestructor); + return file; +} + +/******************************************************************************/ + +static int closeDestructor(void *data) +{ + MprFile *file = (MprFile*) data; + + mprAssert(file); + + mprClose(file); + return 0; +} + +/******************************************************************************/ + +void mprClose(MprFile *file) +{ + mprAssert(file); + + if (file < 0) { + return; + } + + mprAssert(file->fd >= 0); + close(file->fd); + + mprSetDestructor(file, 0); + mprFree(file); +} + +/******************************************************************************/ + +int mprRead(MprFile *file, void *buf, uint size) +{ + mprAssert(file); + + if (file == 0) { + return MPR_ERR_BAD_HANDLE; + } + + return read(file->fd, buf, size); +} + +/******************************************************************************/ + +int mprWrite(MprFile *file, const void *buf, uint count) +{ + mprAssert(file); + + if (file == 0) { + return MPR_ERR_BAD_HANDLE; + } + + return write(file->fd, buf, count); +} + +/******************************************************************************/ + +int mprSeek(MprFile *file, int seekType, long distance) +{ + mprAssert(file); + + if (file == 0) { + return MPR_ERR_BAD_HANDLE; + } + + return lseek(file->fd, seekType, distance); +} + +/******************************************************************************/ + +int mprDelete(MprCtx ctx, const char *path) +{ + return unlink(path); +} + +/******************************************************************************/ + +int mprDeleteDir(MprCtx ctx, const char *path) +{ + return rmdir(path); +} + +#endif /* !BREW */ +/******************************************************************************/ + +char *mprGets(MprFile *file, char *buf, uint size) +{ + MprBuf *bp; + int count, len, c; + + mprAssert(file); + + if (file == 0) { + return 0; + } + + if (file->buf == 0) { + file->buf = mprCreateBuf(file, MPR_DEFAULT_ALLOC, MPR_MAX_STRING); + } + bp = file->buf; + + /* + * Must leave room for null + */ + count = 0; + while (--size > 0) { + if (mprGetBufLength(bp) == 0) { + mprFlushBuf(bp); + len = mprRead(file, mprGetBufEnd(bp), + mprGetBufLinearSpace(bp)); + if (len <= 0) { + return 0; + } + mprAdjustBufEnd(bp, len); + mprAddNullToBuf(bp); + } + if ((c = mprGetCharFromBuf(bp)) == '\n') { + buf[count] = '\0'; + return buf; + } + buf[count++] = c; + } + buf[count] = '\0'; + return buf; +} + +/******************************************************************************/ + +int mprPuts(MprFile *file, const char *writeBuf, uint count) +{ + MprBuf *bp; + char *buf; + int total, bytes, len; + + mprAssert(file); + + /* + * Buffer output and flush when full. + */ + if (file->buf == 0) { + file->buf = mprCreateBuf(file, MPR_BUFSIZE, 0); + if (file->buf == 0) { + return MPR_ERR_CANT_ALLOCATE; + } + } + bp = file->buf; + + if (mprGetBufLength(bp) > 0 && mprGetBufSpace(bp) < (int) count) { + len = mprGetBufLength(bp); + if (mprWrite(file, mprGetBufStart(bp), len) != len) { + return MPR_ERR_CANT_WRITE; + } + mprFlushBuf(bp); + } + + total = 0; + buf = (char*) writeBuf; + + while (count > 0) { + bytes = mprPutBlockToBuf(bp, buf, count); + if (bytes <= 0) { + return MPR_ERR_CANT_ALLOCATE; + } + count -= bytes; + buf += bytes; + total += bytes; + mprAddNullToBuf(bp); + + if (count > 0) { + len = mprGetBufLength(bp); + if (mprWrite(file, mprGetBufStart(bp), len) != len) { + return MPR_ERR_CANT_WRITE; + } + mprFlushBuf(bp); + } + } + return total; +} + +/******************************************************************************/ + +int mprMakeTempFileName(MprCtx ctx, char *buf, int bufsize, const char *tempDir) +{ + MprFile *file; + MprTime now; + char *dir; + int seed, i; + + if (tempDir == 0) { +#if WIN + char *cp; + dir = mprStrdup(ctx, getenv("TEMP")); + for (cp = dir; *cp; cp++) { + if (*cp == '\\') { + *cp = '/'; + } + } +#else + dir = mprStrdup(ctx, "/tmp"); +#endif + } else { + dir = mprStrdup(ctx, tempDir); + } + + mprGetTime(ctx, &now); + seed = now.msec % 64000; + file = 0; + + for (i = 0; i < 128; i++) { + mprSprintf(buf, bufsize, "%s/MPR_%d_%d.tmp", dir, getpid(), seed++); + file = mprOpen(ctx, buf, O_CREAT | O_EXCL | O_BINARY, 0664); + if (file) { + break; + } + } + + if (file == 0) { + return MPR_ERR_CANT_CREATE; + } + + mprClose(file); + mprFree(dir); + + return 0; +} + +/******************************************************************************/ +/* + * 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-2.0/mpr/mprGenTime.c b/source4/lib/appweb/ejs-2.0/mpr/mprGenTime.c new file mode 100644 index 0000000000..6b0ed97bbc --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprGenTime.c @@ -0,0 +1,195 @@ +/** + * @file mprGenTime.c + * @brief Generic Time handling + * @overview + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************/ +/* + * Return the number of milliseconds until the given timeout has expired. + */ + +int mprGetTimeRemaining(MprCtx ctx, MprTime mark, uint timeout) +{ + MprTime now; + uint diff; + + mprGetTime(ctx, &now); + diff = ((now.sec - mark.sec) * 1000) + (now.msec - mark.msec); + + if (diff < 0) { + /* + * Detect time going backwards + */ + mprAssert(diff >= 0); + diff = 0; + } + return (int) (timeout - diff); +} + +/******************************************************************************/ +/* + * Return the number of milliseconds until the given timeout has expired. + */ + +int mprGetElapsedTime(MprCtx ctx, MprTime mark) +{ + MprTime now; + + mprGetTime(ctx, &now); + return ((now.sec - mark.sec) * 1000) + (now.msec - mark.msec); +} + +/******************************************************************************/ + +void mprAddElapsedToTime(MprTime *time, uint elapsed) +{ + time->sec += elapsed / 1000; + time->msec += elapsed % 1000; + if (time->msec > 1000) { + time->msec -= 1000; + time->sec++; + } +} + +/******************************************************************************/ + +int mprCompareTime(MprTime *t1, MprTime *t2) +{ + if (t1->sec < t2->sec) { + return -1; + } else if (t1->sec == t2->sec) { + if (t1->msec < t2->msec) { + return -1; + } else if (t1->msec == t2->msec) { + return 0; + } + } + return 1; +} + +/******************************************************************************/ + +uint mprSubtractTime(MprTime *t1, MprTime *t2) +{ + return ((t1->sec - t2->sec) * 1000) + (t1->msec - t2->msec); +} + +/******************************************************************************/ +#if !BREW +/* + * Thread-safe RFC822 dates (Eg: "Fri, 07 Jan 2003 12:12:21 GMT") + */ + +int mprRfcTime(MprCtx ctx, char *buf, int bufsize, const struct tm *timep) +{ + char months[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", + "Oct", "Nov", "Dec" + }; + + char days[7][4] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + + char *dayp, *monthp; + int year; + + if (bufsize < 30) { + return MPR_ERR_WONT_FIT; + } + dayp = &days[timep->tm_wday][0]; + *buf++ = *dayp++; + *buf++ = *dayp++; + *buf++ = *dayp++; + *buf++ = ','; + *buf++ = ' '; + + *buf++ = timep->tm_mday / 10 + '0'; + *buf++ = timep->tm_mday % 10 + '0'; + *buf++ = ' '; + + monthp = &months[timep->tm_mon][0]; + *buf++ = *monthp++; + *buf++ = *monthp++; + *buf++ = *monthp++; + *buf++ = ' '; + + year = 1900 + timep->tm_year; + /* This routine isn't y10k ready. */ + *buf++ = year / 1000 + '0'; + *buf++ = year % 1000 / 100 + '0'; + *buf++ = year % 100 / 10 + '0'; + *buf++ = year % 10 + '0'; + *buf++ = ' '; + + *buf++ = timep->tm_hour / 10 + '0'; + *buf++ = timep->tm_hour % 10 + '0'; + *buf++ = ':'; + *buf++ = timep->tm_min / 10 + '0'; + *buf++ = timep->tm_min % 10 + '0'; + *buf++ = ':'; + *buf++ = timep->tm_sec / 10 + '0'; + *buf++ = timep->tm_sec % 10 + '0'; + *buf++ = ' '; + + *buf++ = 'G'; + *buf++ = 'M'; + *buf++ = 'T'; + *buf++ = 0; + return 0; +} + +#endif +/******************************************************************************/ + +#ifdef __cplusplus +} +#endif + +/* + * 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-2.0/mpr/mprLock.c b/source4/lib/appweb/ejs-2.0/mpr/mprLock.c new file mode 100644 index 0000000000..df9ce276d4 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprLock.c @@ -0,0 +1,266 @@ +/** + * @file mprThread.c + * @brief Mbedthis Portable Runtime Base Thread Locking Support + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +#if BLD_FEATURE_MULTITHREAD +/************************************ Code ************************************/ + +void mprInitThreads(MprApp *app) +{ + mprAssert(app); + + if (app->globalLock == 0) { + app->globalLock = mprCreateLock(app); + app->allocLock = mprCreateLock(app); + } +} + +/******************************************************************************/ + +void mprTermThreads(MprApp *app) +{ + mprAssert(app); + + if (app->globalLock) { + mprDestroyLock(app->globalLock); + app->globalLock = 0; + } + if (app->allocLock) { + MprLock *lock = app->allocLock; + app->allocLock = 0; + mprDestroyLock(lock); + } +} + +/******************************************************************************/ + +MprLock *mprCreateLock(MprCtx ctx) +{ + MprLock *lock; + + mprAssert(ctx); + + lock = mprAllocType(ctx, MprLock); + +#if BLD_HOST_UNIX + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); + pthread_mutex_init(&lock->cs, &attr); + pthread_mutexattr_destroy(&attr); +#elif WIN + InitializeCriticalSectionAndSpinCount(&lock->cs, 5000); +#elif VXWORKS + lock->cs = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | + SEM_INVERSION_SAFE); + if (lock->cs == 0) { + mprAssert(0); + mprFree(lock); + return 0; + } +#endif + return lock; +} + +/******************************************************************************/ +/* + * Destroy a lock. Must be locked on entrance. + */ + +void mprDestroyLock(MprLock *lock) +{ + mprAssert(lock); + if (lock == 0) { + return; + } + +#if BLD_HOST_UNIX + pthread_mutex_unlock(&lock->cs); + pthread_mutex_destroy(&lock->cs); +#elif WIN + DeleteCriticalSection(&lock->cs); +#elif VXWORKS + semDelete(lock->cs); +#endif + mprFree(lock); +} + +/******************************************************************************/ +/* + * Lock a mutex + */ + +void mprLock(MprLock *lock) +{ + /* + * OPT -- Do this just so we can allocate MprApp before we have created its + * lock. Should remove this test here and in mprUnlock. + */ + if (lock == 0) { + return; + } + +#if BLD_HOST_UNIX + pthread_mutex_lock(&lock->cs); +#elif WIN + EnterCriticalSection(&lock->cs); +#elif VXWORKS + semTake(lock->cs, WAIT_FOREVER); +#endif +} + +/******************************************************************************/ +/* + * Try to attain a lock. Do not block! + */ + +int mprTryLock(MprLock *lock) +{ + mprAssert(lock); + +#if BLD_HOST_UNIX + { + int err; + + if ((err = pthread_mutex_trylock(&lock->cs)) != 0) { + if (err == EBUSY) { + return MPR_ERR_BUSY; + } else { + return MPR_ERR_CANT_ACCESS; + } + } + return 0; + } +#elif WIN + if (TryEnterCriticalSection(&lock->cs) == 0) { + return MPR_ERR_BUSY; + } +#elif VXWORKS + { + int rc; + + rc = semTake(cs, NO_WAIT); + if (rc == -1) { + mprAssert(0); + } + if (rc == S_objLib_OBJ_UNAVAILABLE) { + return MPR_ERR_BUSY; + } else { + return MPR_ERR_CANT_ACCESS; + } + /* Success */ + return 0; + } +#endif + return 0; +} + +/******************************************************************************/ +/* + * Unlock. + */ + +void mprUnlock(MprLock *lock) +{ + if (lock == 0) { + return; + } + +#if BLD_HOST_UNIX + pthread_mutex_unlock(&lock->cs); +#elif WIN + LeaveCriticalSection(&lock->cs); +#elif VXWORKS + semGive(lock->cs); +#endif +} + +/******************************************************************************/ +/* + * Big global lock. Avoid using this. + */ + +void mprGlobalLock(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + mprAssert(app); + + if (app && app->globalLock) { + mprLock(app->globalLock); + } +} + +/******************************************************************************/ + +void mprGlobalUnlock(MprCtx ctx) +{ + MprApp *app; + + app = mprGetApp(ctx); + mprAssert(app); + + if (app && app->globalLock) { + mprUnlock(app->globalLock); + } +} + +/******************************************************************************/ + +int mprGetCurrentThreadID() +{ +#if BLD_HOST_UNIX + return (int) pthread_self(); +#elif WIN + return GetCurrentThreadId(); +#elif VXWORKS + return (int) pthread_self(); +#endif +} + +/******************************************************************************/ +#endif /* BLD_FEATURE_MULTITHREAD */ + +/* + * 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-2.0/mpr/mprLog.c b/source4/lib/appweb/ejs-2.0/mpr/mprLog.c new file mode 100644 index 0000000000..1eb175ed95 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprLog.c @@ -0,0 +1,602 @@ +/** + * @file mprLog.c + * @brief Mbedthis Portable Runtime (MPR) Logging and error reporting. + * @remarks We always provide these routines. + */ + +/*********************************** License **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/****************************** Forward Declarations **************************/ + +static void defaultLogHandler(MPR_LOC_DEC(ctx, loc), int flags, + int level, const char *msg); +static void logOutput(MPR_LOC_DEC(ctx, loc), int flags, int level, + const char *msg); + +/************************************ Code ************************************/ + +void mprLog(MprCtx ctx, int level, const char *fmt, ...) +{ + va_list args; + char *buf; + + if (level > mprGetLogLevel(ctx)) { + return; + } + + va_start(args, fmt); + mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, args); + va_end(args); + + logOutput(MPR_LOC_ARGS(ctx), MPR_LOG_SRC, level, buf); + + va_end(args); + mprFree(buf); +} + +/*****************************************************************************/ +/* + * Do raw output + */ + +void mprRawLog(MprCtx ctx, const char *fmt, ...) +{ + va_list args; + char *buf; + int len; + + va_start(args, fmt); + len = mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, args); + va_end(args); + + logOutput(MPR_LOC_ARGS(ctx), MPR_RAW, 0, buf); + mprFree(buf); +} + +/*****************************************************************************/ +/* + * Handle an error + */ + +void mprError(MPR_LOC_DEC(ctx, loc), const char *fmt, ...) +{ + va_list args; + char *buf; + int len; + + va_start(args, fmt); + len = mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, args); + va_end(args); + + logOutput(MPR_LOC_PASS(ctx, loc), MPR_ERROR_MSG | MPR_ERROR_SRC, 0, buf); + + mprFree(buf); +} + +/*****************************************************************************/ +/* + * Handle an error that should be displayed to the user + */ + +void mprUserError(MPR_LOC_DEC(ctx, loc), const char *fmt, ...) +{ + va_list args; + char *buf; + int len; + + va_start(args, fmt); + len = mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, args); + va_end(args); + + logOutput(MPR_LOC_PASS(ctx, loc), MPR_USER_MSG | MPR_ERROR_SRC, 0, buf); + + mprFree(buf); +} + +/*****************************************************************************/ +/* + * Handle a fatal error. Forcibly shutdown the application. + */ + +void mprFatalError(MPR_LOC_DEC(ctx, loc), const char *fmt, ...) +{ + va_list args; + char *buf; + int len; + + va_start(args, fmt); + len = mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, args); + va_end(args); + + logOutput(MPR_LOC_PASS(ctx, loc), MPR_USER_MSG | MPR_FATAL_SRC, 0, buf); + + mprFree(buf); + +#if BREW + mprSignalExit(ctx); +#else + exit(2); +#endif +} + +/*****************************************************************************/ +/* + * Handle a program assertion + */ + +void mprAssertError(MPR_LOC_DEC(ctx, loc), const char *msg) +{ + logOutput(MPR_LOC_PASS(ctx, loc), MPR_ASSERT_MSG | MPR_ASSERT_SRC, 0, msg); +} + +/*****************************************************************************/ +/* + * Handle an error + */ + +void mprStaticError(MPR_LOC_DEC(ctx, loc), const char *fmt, ...) +{ + va_list args; + int len; + char buf[MPR_MAX_STRING]; + + va_start(args, fmt); + len = mprVsprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + logOutput(MPR_LOC_PASS(ctx, loc), MPR_ERROR_MSG | MPR_ERROR_SRC, 0, buf); +} + +/*****************************************************************************/ +/* + * Direct output to the standard output. Does not hook into the logging + * system and does not allocate memory. + */ + +void mprStaticAssert(const char *loc, const char *msg) +{ +#if BLD_DEBUG + char buf[MPR_MAX_STRING]; + int len; + + len = mprSprintf(buf, sizeof(buf), "Assertion %s, failed at %s\n", + msg, loc); + mprBreakpoint(loc, buf); + +#if BLD_HOST_UNIX + /* + * MOB -- but is stdout always okay to use + */ + write(1, buf, len); +#elif BREW || WIN + /* + * Only time we use printf. We can't get an alloc context so we have + * to use real print + */ +#if BREW && !BREW_SIMULATOR + printf(" MP: %s\n", buf); +#else + printf("%s\n", buf); +#endif + +#endif +#endif +} + +/*****************************************************************************/ + +int mprGetLogLevel(MprCtx ctx) +{ + return mprGetApp(ctx)->logLevel; +} + +/******************************************************************************/ + +void mprSetLogLevel(MprCtx ctx, int level) +{ + mprGetApp(ctx)->logLevel = level; +} + +/*****************************************************************************/ +/* + * Output a log message to the log handler + */ + +static void logOutput(MPR_LOC_DEC(ctx, loc), int flags, int level, + const char *msg) +{ + MprLogHandler handler; + + if (flags & (MPR_ERROR_SRC | MPR_FATAL_SRC | MPR_ASSERT_SRC)) { + mprBreakpoint(MPR_LOC, 0); + } + + mprAssert(ctx != 0); + handler = mprGetApp(ctx)->logHandler; + if (handler != 0) { + (handler)(MPR_LOC_PASS(ctx, loc), flags, level, msg); + return; + } + defaultLogHandler(MPR_LOC_PASS(ctx, loc), flags, level, msg); +} + +/*****************************************************************************/ +/* + * Default log output is just to the console + */ + +static void defaultLogHandler(MPR_LOC_DEC(ctx, loc), int flags, + int level, const char *msg) +{ + MprApp *app; + char *prefix; + + app = mprGetApp(ctx); + prefix = app->name; + + while (*msg == '\n') { + mprPrintf(ctx, "\n"); + msg++; + } + + if (flags & MPR_LOG_SRC) { +#if BREW && !BREW_SIMULATOR + mprPrintf(ctx, "%s\n", msg); +#else + mprPrintf(ctx, "%s: %d: %s\n", prefix, level, msg); +#endif + + } else if (flags & MPR_ERROR_SRC) { + /* + * Use static printing to avoid malloc when the messages are small. + * This is important for memory allocation errors. + */ + if (strlen(msg) < (MPR_MAX_STRING - 32)) { + mprStaticPrintf(ctx, "%s: Error: %s\n", prefix, msg); + } else { + mprPrintf(ctx, "%s: Error: %s\n", prefix, msg); + } + + } else if (flags & MPR_FATAL_SRC) { + mprPrintf(ctx, "%s: Fatal: %s\n", prefix, msg); + + } else if (flags & MPR_ASSERT_SRC) { +#if BLD_FEATURE_ALLOC_LEAK_TRACK + mprPrintf(ctx, "%s: Assertion %s, failed at %s\n", prefix, msg, loc); +#else + mprPrintf(ctx, "%s: Assertion %s, failed\n", prefix, msg); +#endif + + } else if (flags & MPR_RAW) { + mprPrintf(ctx, "%s", msg); + + } else { + return; + } +} + +/*****************************************************************************/ +/* + * Map the O/S error code to portable error codes. + */ + +int mprGetOsError() +{ +#if WIN + int rc; + rc = GetLastError(); + + /* + * Client has closed the pipe + */ + if (rc == ERROR_NO_DATA) { + return EPIPE; + } + return rc; +#endif +#if LINUX || VXWORKS || SOLARIS + return errno; +#endif +#if BREW + /* + * No such thing on Brew. Errors are per class + */ + return 0; +#endif +} + +/******************************************************************************/ +#if UNUSED + +const char *mprGetErrorMsg(int err) +{ + /* + * MPR error messages. Declare here so we don't have any globals. + */ + char *mprErrMessages[] = { + /* 0 MPR_ERR_OK */ "Success", + /* -201 MPR_ERR_GENERAL */ "General error", + /* -202 MPR_ERR_ABORTED */ "Aborted", + /* -203 MPR_ERR_ALREADY_EXISTS */ "Already exists", + /* -204 MPR_ERR_BAD_ARGS */ "Bad args", + /* -205 MPR_ERR_BAD_FORMAT */ "Bad format", + /* -206 MPR_ERR_BAD_HANDLE */ "Bad handle", + /* -207 MPR_ERR_BAD_STATE */ "Bad state", + /* -208 MPR_ERR_BAD_SYNTAX */ "Bad syntax", + /* -209 MPR_ERR_BAD_TYPE */ "Bad type", + /* -210 MPR_ERR_BAD_VALUE */ "Bad value", + /* -211 MPR_ERR_BUSY */ "Busy", + /* -212 MPR_ERR_CANT_ACCESS */ "Can't access", + /* -213 MPR_ERR_CANT_COMPLETE */ "Can't complete", + /* -214 MPR_ERR_CANT_CREATE */ "Can't create", + /* -215 MPR_ERR_CANT_INITIALIZE */ "Can't initialize", + /* -216 MPR_ERR_CANT_OPEN */ "Can't open", + /* -217 MPR_ERR_CANT_READ */ "Can't read", + /* -218 MPR_ERR_CANT_WRITE */ "Can't write", + /* -219 MPR_ERR_DELETED */ "Already deleted", + /* -220 MPR_ERR_NETWORK */ "Network error", + /* -221 MPR_ERR_NOT_FOUND */ "Not found", + /* -222 MPR_ERR_NOT_INITIALIZED */ "Not initialized", + /* -223 MPR_ERR_NOT_READY */ "Not ready", + /* -224 MPR_ERR_READ_ONLY */ "Read only", + /* -225 MPR_ERR_TIMEOUT */ "Timeout", + /* -226 MPR_ERR_TOO_MANY */ "Too many", + /* -227 MPR_ERR_WONT_FIT */ "Won't fit", + /* -228 MPR_ERR_WOULD_BLOCK */ "Would block", + /* -229 MPR_ERR_CANT_ALLOCATE */ "Can't allocate", + }; + int mprNumErr = sizeof(mprErrMessages) / sizeof(char*); + +/* + * Operating system error messages + */ +#if WIN +char *osErrMessages[] = +{ + /* 0 */ "No error", + /* 1 EPERM */ "Operation not permitted", + /* 2 ENOENT */ "No such file or directory", + /* 3 ESRCH */ "No such process", + /* 4 EINTR */ "Interrupted function call", + /* 5 EIO */ "I/O error", + /* 6 ENXIO */ "No such device or address", + /* 7 E2BIG */ "Arg list too long", + /* 8 ENOEXEC */ "Exec format error", + /* 9 EBADF */ "Bad file number", + /* 10 ECHILD */ "No child processes", + /* 11 EAGAIN */ "Try again", + /* 12 ENOMEM */ "Out of memory", + /* 13 EACCES */ "Permission denied", + /* 14 EFAULT */ "Bad address", + /* 15 ENOTBLK */ "Unknown error", + /* 16 EBUSY */ "Resource busy", + /* 17 EEXIST */ "File exists", + /* 18 EXDEV */ "Improper link", + /* 19 ENODEV */ "No such device", + /* 20 ENOTDIR */ "Not a directory", + /* 21 EISDIR */ "Is a directory", + /* 22 EINVAL */ "Invalid argument", + /* 23 ENFILE */ "Too many open files in system", + /* 24 EMFILE */ "Too many open files", + /* 25 ENOTTY */ "Inappropriate I/O control operation", + /* 26 ETXTBSY */ "Unknown error", + /* 27 EFBIG */ "File too large", + /* 28 ENOSPC */ "No space left on device", + /* 29 ESPIPE */ "Invalid seek", + /* 30 EROFS */ "Read-only file system", + /* 31 EMLINK */ "Too many links", + /* 32 EPIPE */ "Broken pipe", + /* 33 EDOM */ "Domain error", + /* 34 ERANGE */ "Result too large", + /* 35 EUCLEAN */ "Unknown error", + /* 36 EDEADLK */ "Resource deadlock would occur", + /* 37 UNKNOWN */ "Unknown error", + /* 38 ENAMETOOLONG */ "Filename too long", + /* 39 ENOLCK */ "No locks available", + /* 40 ENOSYS */ "Function not implemented", + /* 41 ENOTEMPTY */ "Directory not empty", + /* 42 EILSEQ */ "Illegal byte sequence", + /* 43 ENETDOWN */ "Network is down", + /* 44 ECONNRESET */ "Connection reset", + /* 45 ECONNREFUSED */ "Connection refused", + /* 46 EADDRINUSE */ "Address already in use" + +}; + +#else /* WIN */ + +char *osErrMessages[] = +{ + /* 0 */ "Success" + /* 1 EPERM */ "Operation not permitted" + /* 2 ENOENT */ "No such file or directory" + /* 3 ESRCH */ "No such process" + /* 4 EINTR */ "Interrupted system call" + /* 5 EIO */ "I/O error" + /* 6 ENXIO */ "No such device or address" + /* 7 E2BIG */ "Arg list too long" + /* 8 ENOEXEC */ "Exec format error" + /* 9 EBADF */ "Bad file number" + /* 10 ECHILD */ "No child processes" + /* 11 EAGAIN */ "Try again" + /* 12 ENOMEM */ "Out of memory" + /* 13 EACCES */ "Permission denied" + /* 14 EFAULT */ "Bad address" + /* 15 ENOTBLK */ "Block device required" + /* 16 EBUSY */ "Device or resource busy" + /* 17 EEXIST */ "File exists" + /* 18 EXDEV */ "Cross-device link" + /* 19 ENODEV */ "No such device" + /* 20 ENOTDIR */ "Not a directory" + /* 21 EISDIR */ "Is a directory" + /* 22 EINVAL */ "Invalid argument" + /* 23 ENFILE */ "File table overflow" + /* 24 EMFILE */ "Too many open files" + /* 25 ENOTTY */ "Not a typewriter" + /* 26 ETXTBSY */ "Text file busy" + /* 27 EFBIG */ "File too large" + /* 28 ENOSPC */ "No space left on device" + /* 29 ESPIPE */ "Illegal seek" + /* 30 EROFS */ "Read-only file system" + /* 31 EMLINK */ "Too many links" + /* 32 EPIPE */ "Broken pipe" + /* 33 EDOM */ "Math argument out of domain of func" + /* 34 ERANGE */ "Math result not representable" + /* 35 EDEADLK */ "Resource deadlock would occur" + /* 36 ENAMETOOLONG */ "File name too long" + /* 37 ENOLCK */ "No record locks available" + /* 38 ENOSYS */ "Function not implemented" + /* 39 ENOTEMPTY */ "Directory not empty" + /* 40 ELOOP */ "Too many symbolic links encountered" + /* 41 EWOULDBLOCK EAGAIN */"Operation would block" + /* 42 ENOMSG */ "No message of desired type" + /* 43 EIDRM */ "Identifier removed" + +#if !BLD_FEATURE_SQUEEZE + /* 44 ECHRNG */ "Channel number out of range" + /* 45 EL2NSYNC */ "Level 2 not synchronized" + /* 46 EL3HLT */ "Level 3 halted" + /* 47 EL3RST */ "Level 3 reset" + /* 48 ELNRNG */ "Link number out of range" + /* 49 EUNATCH */ "Protocol driver not attached" + /* 50 ENOCSI */ "No CSI structure available" + /* 51 EL2HLT */ "Level 2 halted" + /* 52 EBADE */ "Invalid exchange" + /* 53 EBADR */ "Invalid request descriptor" + /* 54 EXFULL */ "Exchange full" + /* 55 ENOANO */ "No anode" + /* 56 EBADRQC */ "Invalid request code" + /* 57 EBADSLT */ "Invalid slot" + /* 59 EBFONT */ "Bad font file format" + /* 60 ENOSTR */ "Device not a stream" + /* 61 ENODATA */ "No data available" + /* 62 ETIME */ "Timer expired" + /* 63 ENOSR */ "Out of streams resources" + /* 64 ENONET */ "Machine is not on the network" + /* 65 ENOPKG */ "Package not installed" + /* 66 EREMOTE */ "Object is remote" + /* 67 ENOLINK */ "Link has been severed" + /* 68 EADV */ "Advertise error" + /* 69 ESRMNT */ "Srmount error" + /* 70 ECOMM */ "Communication error on send" + /* 71 EPROTO */ "Protocol error" + /* 72 EMULTIHOP */ "Multihop attempted" + /* 73 EDOTDOT */ "RFS specific error" + /* 74 EBADMSG */ "Not a data message" + /* 75 EOVERFLOW */ "Value too large for defined data type" + /* 76 ENOTUNIQ */ "Name not unique on network" + /* 77 EBADFD */ "File descriptor in bad state" + /* 78 EREMCHG */ "Remote address changed" + /* 79 ELIBACC */ "Can not access a needed shared library" + /* 80 ELIBBAD */ "Accessing a corrupted shared library" + /* 81 ELIBSCN */ ".lib section in a.out corrupted" + /* 82 ELIBMAX */ "Linking in too many shared libraries" + /* 83 ELIBEXEC */ "Cannot exec a shared library directly" + /* 84 EILSEQ */ "Illegal byte sequence" + /* 85 ERESTART */ "Interrupted system call should be restarted" + /* 86 ESTRPIPE */ "Streams pipe error" + /* 87 EUSERS */ "Too many users" + /* 88 ENOTSOCK */ "Socket operation on non-socket" + /* 89 EDESTADDRREQ */ "Destination address required" + /* 90 EMSGSIZE */ "Message too long" + /* 91 EPROTOTYPE */ "Protocol wrong type for socket" + /* 92 ENOPROTOOPT */ "Protocol not available" + /* 93 EPROTONOSUPPORT */ "Protocol not supported" + /* 94 ESOCKTNOSUPPORT */ "Socket type not supported" + /* 95 EOPNOTSUPP */ "Operation not supported on transport endpoint" + /* 96 EPFNOSUPPORT */ "Protocol family not supported" + /* 97 EAFNOSUPPORT */ "Address family not supported by protocol" + /* 98 EADDRINUSE */ "Address already in use" + /* 99 EADDRNOTAVAIL */ "Cannot assign requested address" + /* 100 ENETDOWN */ "Network is down" + /* 101 ENETUNREACH */ "Network is unreachable" + /* 102 ENETRESET */ "Network dropped connection because of reset" + /* 103 ECONNABORTED */ "Software caused connection abort" + /* 104 ECONNRESET */ "Connection reset by peer" + /* 105 ENOBUFS */ "No buffer space available" + /* 106 EISCONN */ "Transport endpoint is already connected" + /* 107 ENOTCONN */ "Transport endpoint is not connected" + /* 108 ESHUTDOWN */ "Cannot send after transport endpoint shutdown" + /* 109 ETOOMANYREFS */ "Too many references: cannot splice" + /* 110 ETIMEDOUT */ "Connection timed out" + /* 111 ECONNREFUSED */ "Connection refused" + /* 112 EHOSTDOWN */ "Host is down" + /* 113 EHOSTUNREACH */ "No route to host" + /* 114 EALREADY */ "Operation already in progress" + /* 115 EINPROGRESS */ "Operation now in progress" + /* 116 ESTALE */ "Stale NFS file handle" + /* 117 EUCLEAN */ "Structure needs cleaning" + /* 118 ENOTNAM */ "Not a XENIX named type file" + /* 119 ENAVAIL */ "No XENIX semaphores available" + /* 120 EISNAM */ "Is a named type file" + /* 121 EREMOTEIO */ "Remote I/O error" + /* 122 EDQUOT */ "Quota exceeded" + /* 123 ENOMEDIUM */ "No medium found" + /* 124 EMEDIUMTYPE */ "Wrong medium type" +}; +#endif /* BLD_FEATURE_SQUEEZE */ +#endif /* WIN */ + + int osNumErr = sizeof(osErrMessages) / sizeof(char*); + + if (err < MPR_ERR_BASE) { + err = MPR_ERR_BASE - err; + if (err < 0 || err >= mprNumErr) { + return "Bad error code"; + } + return mprErrMessages[err]; + + } else { + /* + * Negative O/S error code. Map to a positive standard Posix error. + */ + err = -err; + if (err < 0 || err >= osNumErr) { + return "Bad O/S error code"; + } + return osErrMessages[err]; + } +} + +#endif +/*****************************************************************************/ + +/* + * 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-2.0/mpr/mprOs.h b/source4/lib/appweb/ejs-2.0/mpr/mprOs.h new file mode 100644 index 0000000000..bed4ca5979 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprOs.h @@ -0,0 +1,707 @@ +/* + * @file mprOs.h + * @brief Include O/S headers and smooth out per-O/S differences + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + * + * This software is distributed under commercial and open source licenses. + * You may use the GPL open source license described below or you may acquire + * a commercial license from Mbedthis Software. You agree to be fully bound + * by the terms of either license. Consult the LICENSE.TXT distributed with + * this software for full details. + * + * This software is open source; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See the GNU General Public License for more + * details at: http://www.mbedthis.com/downloads/gplLicense.html + * + * This program is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This GPL license does NOT permit incorporating this software into + * proprietary programs. If you are unable to comply with the GPL, you must + * acquire a commercial license to use this software. Commercial licenses + * for this software and support services are available from Mbedthis + * Software at http://www.mbedthis.com + * + * @end + */ + +/******************************* Documentation ********************************/ + +/* + * This header is part of the Mbedthis Portable Runtime and aims to include + * all necessary O/S headers and to unify the constants and declarations + * required by Mbedthis products. It can be included by C or C++ programs. + */ + +/******************************************************************************/ + +#ifndef _h_MPR_OS_HDRS +#define _h_MPR_OS_HDRS 1 + +#include "buildConfig.h" + +/********************************* CPU Families *******************************/ +/* + * Porters, add your CPU families here and update configure code. + */ +#define MPR_CPU_UNKNOWN 0 +#define MPR_CPU_IX86 1 +#define MPR_CPU_PPC 2 +#define MPR_CPU_SPARC 3 +#define MPR_CPU_XSCALE 4 +#define MPR_CPU_ARM 5 +#define MPR_CPU_MIPS 6 +#define MPR_CPU_68K 7 +#define MPR_CPU_SIMNT 8 /* VxWorks NT simulator */ +#define MPR_CPU_SIMSPARC 9 /* VxWorks sparc simulator */ + +/********************************* O/S Includes *******************************/ + +#if LINUX || SOLARIS + #include <sys/types.h> + #include <time.h> + #include <arpa/inet.h> + #include <ctype.h> + #include <dirent.h> + #include <dlfcn.h> + #include <fcntl.h> + #include <grp.h> + #include <errno.h> + #include <libgen.h> + #include <limits.h> + #include <netdb.h> + #include <net/if.h> + #include <netinet/in.h> + #include <netinet/tcp.h> + #include <netinet/ip.h> + #include <pthread.h> + #include <pwd.h> + #include <resolv.h> + #include <signal.h> + #include <stdarg.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <syslog.h> + #include <sys/ioctl.h> + #include <sys/stat.h> + #include <sys/param.h> + #include <sys/resource.h> + #include <sys/sem.h> + #include <sys/shm.h> + #include <sys/socket.h> + #include <sys/select.h> + #include <sys/time.h> + #include <sys/times.h> + #include <sys/utsname.h> + #include <sys/wait.h> + #include <unistd.h> + +#if LINUX + #include <stdint.h> +#endif + +#if SOLARIS + #include <netinet/in_systm.h> +#endif + +#if BLD_FEATURE_FLOATING_POINT + #define __USE_ISOC99 1 + #include <math.h> + #include <values.h> +#endif + +#endif /* LINUX || SOLARIS */ + +#if VXWORKS + #include <vxWorks.h> + #include <envLib.h> + #include <sys/types.h> + #include <time.h> + #include <arpa/inet.h> + #include <ctype.h> + #include <dirent.h> + #include <fcntl.h> + #include <errno.h> + #include <limits.h> + #include <loadLib.h> + #include <netdb.h> + #include <net/if.h> + #include <netinet/tcp.h> + #include <netinet/in.h> + #include <netinet/ip.h> + #include <signal.h> + #include <stdarg.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <sysSymTbl.h> + #include <sys/fcntlcom.h> + #include <sys/ioctl.h> + #include <sys/stat.h> + #include <sys/socket.h> + #include <sys/times.h> + #include <sys/wait.h> + #include <unistd.h> + #include <unldLib.h> + + #if BLD_FEATURE_FLOATING_POINT + #include <float.h> + #define __USE_ISOC99 1 + #include <math.h> + #endif + + #include <sockLib.h> + #include <inetLib.h> + #include <ioLib.h> + #include <pipeDrv.h> + #include <hostLib.h> + #include <netdb.h> + #include <tickLib.h> + #include <taskHookLib.h> + +#endif /* VXWORKS */ + +#if MACOSX + #include <time.h> + #include <arpa/inet.h> + #include <ctype.h> + #include <fcntl.h> + #include <grp.h> + #include <errno.h> + #include <libgen.h> + #include <limits.h> + #include <mach-o/dyld.h> + #include <netdb.h> + #include <net/if.h> + #include <netinet/in_systm.h> + #include <netinet/in.h> + #include <netinet/tcp.h> + #include <netinet/ip.h> + #include <pthread.h> + #include <pwd.h> + #include <resolv.h> + #include <signal.h> + #include <stdarg.h> + #include <stdio.h> + #include <stdlib.h> + #include <stdint.h> + #include <string.h> + #include <syslog.h> + #include <sys/ioctl.h> + #include <sys/types.h> + #include <sys/stat.h> + #include <sys/param.h> + #include <sys/resource.h> + #include <sys/sem.h> + #include <sys/shm.h> + #include <sys/socket.h> + #include <sys/select.h> + #include <sys/time.h> + #include <sys/times.h> + #include <sys/types.h> + #include <sys/utsname.h> + #include <sys/wait.h> + #include <unistd.h> +#endif /* MACOSX */ + +#if WIN + /* + * We replace insecure functions with Mbedthis replacements + */ + #define _CRT_SECURE_NO_DEPRECATE 1 + + #include <ctype.h> + #include <conio.h> + #include <direct.h> + #include <errno.h> + #include <fcntl.h> + #include <io.h> + #include <limits.h> + #include <malloc.h> + #include <process.h> + #include <sys/stat.h> + #include <sys/types.h> + #include <stddef.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <stdarg.h> + #include <time.h> + #define WIN32_LEAN_AND_MEAN + #include <winsock2.h> + #include <windows.h> + #include <winbase.h> + #if BLD_FEATURE_FLOATING_POINT + #include <float.h> + #endif + #include <shlobj.h> + #include <shellapi.h> + #include <wincrypt.h> + +#if BLD_DEBUG + #include <crtdbg.h> +#endif + #include "mprUnix.h" +#endif /* WIN */ + +#if BREW + #if BLD_FEATURE_FLOATING_POINT + #warning "Floating point is not supported on Brew" + #endif + #if BLD_FEATURE_MULTITHREAD + #warning "Multithreading is not supported on Brew" + #endif + + #include "AEEModGen.h" + #include "AEEAppGen.h" + #include "BREWVersion.h" + + #if BREW_MAJ_VER == 2 + /* + * Fix for BREW 2.X + */ + #ifdef __GNUC__ + #define __inline extern __inline__ + #endif + #include "AEENet.h" + #undef __inline + #endif + + #include "AEE.h" + #include "AEEBitmap.h" + #include "AEEDisp.h" + #include "AEEFile.h" + #include "AEEHeap.h" + #include "AEEImageCtl.h" + #include "AEEMedia.h" + #include "AEEMediaUtil.h" + #include "AEEMimeTypes.h" + #include "AEEStdLib.h" + #include "AEEShell.h" + #include "AEESoundPlayer.h" + #include "AEEText.h" + #include "AEETransform.h" + #include "AEEWeb.h" + #if BREW_MAJ_VER >= 3 + #include "AEESMS.h" + #endif + #include "AEETAPI.h" + +#if 0 + #include "AEESound.h" + #include "AEEDb.h" + #include "AEEMenu.h" +#endif + +#endif /* BREW */ + +/******************************************************************************/ +/******************************* General Defines ******************************/ +/******************************************************************************/ + +#ifndef MAXINT +#if INT_MAX + #define MAXINT INT_MAX +#else + #define MAXINT 0x7fffffff +#endif +#endif + +#ifndef BITSPERBYTE +#define BITSPERBYTE (8 * sizeof(char)) +#endif + +#define BITS(type) (BITSPERBYTE * (int) sizeof(type)) + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define MPR_ARRAY_SIZE(type) (sizeof(type) / sizeof(type[0])) + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) && !DOXYGEN && BLD_FEATURE_ALLOC_LEAK_TRACK +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +typedef char *MprStr; /* Used for dynamic strings */ + +#ifdef __cplusplus +extern "C" { +#else +typedef int bool; +#endif + +/******************************************************************************/ +/******************************** Linux Defines *******************************/ +/******************************************************************************/ + +#if LINUX + typedef unsigned char uchar; + +#if BLD_FEATURE_INT64 + __extension__ typedef long long int int64; + __extension__ typedef unsigned long long int uint64; + #define INT64(x) (x##LL) + #define UINT64(x) (x##ULL) +#endif + + #define closesocket(x) close(x) + #define MPR_BINARY "" + #define MPR_TEXT "" + #define O_BINARY 0 + #define O_TEXT 0 + #define SOCKET_ERROR -1 + #define MPR_DLL_EXT ".so" + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT MAXFLOAT +#endif + +/* + * For some reason it is removed from fedora pthreads.h and only + * comes in for UNIX96 + */ +extern int pthread_mutexattr_gettype (__const pthread_mutexattr_t *__restrict + __attr, int *__restrict __kind) __THROW; +/* Set the mutex kind attribute in *ATTR to KIND (either PTHREAD_MUTEX_NORMAL, + PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK, or + PTHREAD_MUTEX_DEFAULT). */ +extern int pthread_mutexattr_settype (pthread_mutexattr_t *__attr, int __kind) + __THROW; + +#endif /* LINUX */ + +/******************************************************************************/ +/******************************* VxWorks Defines ******************************/ +/******************************************************************************/ + +#if VXWORKS + + typedef unsigned char uchar; + typedef unsigned int uint; + typedef unsigned long ulong; + + #define HAVE_SOCKLEN_T + typedef int socklen_t; + +#if BLD_FEATURE_INT64 + typedef long long int int64; + typedef unsigned long long int uint64; + #define INT64(x) (x##LL) + #define UINT64(x) (x##ULL) +#endif + + #define closesocket(x) close(x) + #define getpid() taskIdSelf() + #define MPR_BINARY "" + #define MPR_TEXT "" + #define O_BINARY 0 + #define O_TEXT 0 + #define SOCKET_ERROR -1 + #define MPR_DLL_EXT ".so" + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT FLT_MAX +#endif + + #undef R_OK + #define R_OK 4 + #undef W_OK + #define W_OK 2 + #undef X_OK + #define X_OK 1 + #undef F_OK + #define F_OK 0 + + #define MSG_NOSIGNAL 0 + + extern int access(char *path, int mode); + extern int sysClkRateGet(); + +#endif /* VXWORKS */ + +/******************************************************************************/ +/******************************** MacOsx Defines ******************************/ +/******************************************************************************/ +#if MACOSX + typedef unsigned long ulong; + typedef unsigned char uchar; + +#if BLD_FEATURE_INT64 + __extension__ typedef long long int int64; + __extension__ typedef unsigned long long int uint64; + #define INT64(x) (x##LL) + #define UINT64(x) (x##ULL) +#endif + + #define closesocket(x) close(x) + #define MPR_BINARY "" + #define MPR_TEXT "" + #define O_BINARY 0 + #define O_TEXT 0 + #define SOCKET_ERROR -1 + #define MPR_DLL_EXT ".dylib" + #define MSG_NOSIGNAL 0 + #define __WALL 0x40000000 + #define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT MAXFLOAT +#endif + +#endif /* MACOSX */ + +/******************************************************************************/ +/******************************* Windows Defines ******************************/ +/******************************************************************************/ + +#if WIN + typedef unsigned char uchar; + typedef unsigned int uint; + typedef unsigned long ulong; + typedef unsigned short ushort; + +/* + * We always define INT64 types on windows + */ +#if BLD_FEATURE_INT64 || 1 + typedef __int64 int64; + typedef unsigned __int64 uint64; + #define INT64(x) (x##i64) + #define UINT64(x) (x##Ui64) +#endif + + typedef int uid_t; + typedef void *handle; + typedef char *caddr_t; + typedef long pid_t; + typedef int gid_t; + typedef ushort mode_t; + typedef void *siginfo_t; + + #define HAVE_SOCKLEN_T + typedef int socklen_t; + + #undef R_OK + #define R_OK 4 + #undef W_OK + #define W_OK 2 + + /* + * On windows map X_OK to R_OK + */ + #undef X_OK + #define X_OK 4 + #undef F_OK + #define F_OK 0 + + #ifndef EADDRINUSE + #define EADDRINUSE 46 + #endif + #ifndef EWOULDBLOCK + #define EWOULDBLOCK EAGAIN + #endif + #ifndef ENETDOWN + #define ENETDOWN 43 + #endif + #ifndef ECONNRESET + #define ECONNRESET 44 + #endif + #ifndef ECONNREFUSED + #define ECONNREFUSED 45 + #endif + + #define MSG_NOSIGNAL 0 + #define MPR_BINARY "b" + #define MPR_TEXT "t" + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT DBL_MAX +#endif + +#ifndef FILE_FLAG_FIRST_PIPE_INSTANCE +#define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 +#endif + + #define MPR_DLL_EXT ".dll" +#endif /* WIN */ + +/******************************************************************************/ +/****************************** Solaris Defines *******************************/ +/******************************************************************************/ + +#if SOLARIS + typedef unsigned char uchar; + +#if BLD_FEATURE_INT64 + typedef long long int int64; + typedef unsigned long long int uint64; + #define INT64(x) (x##LL) + #define UINT64(x) (x##ULL) +#endif + + #define closesocket(x) close(x) + #define MPR_BINARY "" + #define MPR_TEXT "" + #define O_BINARY 0 + #define O_TEXT 0 + #define SOCKET_ERROR -1 + #define MPR_DLL_EXT ".so" + #define MSG_NOSIGNAL 0 + #define INADDR_NONE ((in_addr_t) 0xffffffff) + #define __WALL 0 + #define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE + +#if BLD_FEATURE_FLOATING_POINT + #define MAX_FLOAT MAXFLOAT +#endif + +#endif /* SOLARIS */ + +/******************************************************************************/ +/********************************* BREW Defines *******************************/ +/******************************************************************************/ + +#if BREW + typedef unsigned char uchar; + typedef unsigned int uint; + typedef unsigned long ulong; + typedef unsigned short ushort; + + typedef uint off_t; + typedef long pid_t; + +#if UNUSED + typedef int uid_t; + typedef void *handle; + typedef char *caddr_t; + typedef int gid_t; + typedef ushort mode_t; + typedef void *siginfo_t; + + #define HAVE_SOCKLEN_T + typedef int socklen_t; + + #ifndef EADDRINUSE + #define EADDRINUSE 46 + #endif + #ifndef EWOULDBLOCK + #define EWOULDBLOCK EAGAIN + #endif + #ifndef ENETDOWN + #define ENETDOWN 43 + #endif + #ifndef ECONNRESET + #define ECONNRESET 44 + #endif + #ifndef ECONNREFUSED + #define ECONNREFUSED 45 + #endif + + #define MSG_NOSIGNAL 0 + #define MPR_BINARY "b" + #define MPR_TEXT "t" + + #define MPR_DLL_EXT ".dll" +#endif + + #define O_RDONLY 0 + #define O_WRONLY 1 + #define O_RDWR 2 + #define O_CREAT 0x200 + #define O_TRUNC 0x400 + #define O_BINARY 0 + #define O_TEXT 0x20000 + #define O_EXCL 0x40000 + #define O_APPEND 0x80000 + + #define R_OK 4 + #define W_OK 2 + #define X_OK 1 + #define F_OK 0 + + #define SEEK_SET 0 + #define SEEK_CUR 1 + #define SEEK_END 2 + +#if UNUSED +struct stat { + uint st_size; +}; +#endif + +extern int getpid(); +extern int isalnum(int c); +extern int isalpha(int c); +extern int isdigit(int c); +extern int islower(int c); +extern int isupper(int c); +extern int isspace(int c); +extern int isxdigit(int c); + +extern uint strlen(const char *str); +extern char *strstr(const char *string, const char *strSet); +extern void *memset(const void *dest, int c, uint count); +extern void exit(int status); +extern char *strpbrk(const char *str, const char *set); +extern uint strspn(const char *str, const char *set); +extern int tolower(int c); +extern int toupper(int c); +extern void *memcpy(void *dest, const void *src, uint count); +extern void *memmove(void *dest, const void *src, uint count); + +extern int atoi(const char *str); +extern void free(void *ptr); +extern void *malloc(uint size); +extern void *realloc(void *ptr, uint size); +extern char *strcat(char *dest, const char *src); +extern char *strchr(const char *str, int c); +extern int strcmp(const char *s1, const char *s2); +extern int strncmp(const char *s1, const char *s2, uint count); +extern char *strcpy(char *dest, const char *src); +extern char *strncpy(char *dest, const char *src, uint count); +extern char *strrchr(const char *str, int c); + +#undef printf +#define printf DBGPRINTF + +#if BREW_SIMULATOR && BLD_DEBUG +extern _CRTIMP int __cdecl _CrtCheckMemory(void); +extern _CRTIMP int __cdecl _CrtSetReportHook(); +#endif + +#endif /* BREW */ + +/******************************************************************************/ +#ifdef __cplusplus +} +#endif + +#endif /* _h_MPR_OS_HDRS */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim:tw=78 + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/source4/lib/appweb/ejs-2.0/mpr/mprPrintf.c b/source4/lib/appweb/ejs-2.0/mpr/mprPrintf.c new file mode 100644 index 0000000000..2d0951acfa --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprPrintf.c @@ -0,0 +1,924 @@ +/** + * @file mprPrintf.c + * @brief Printf routines safe for embedded programming + * @overview This module provides safe replacements for the standard + * printf formatting routines. + * @remarks Most routines in this file are not thread-safe. It is the callers + * responsibility to perform all thread synchronization. + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 **********************************/ +/* + * We need to use the underlying str(cpy) routines to implement our safe + * alternatives + */ +#if !DOXYGEN +#define UNSAFE_FUNCTIONS_OK 1 +#endif + +#include "mpr.h" + +/*********************************** Defines **********************************/ +/* + * Class definitions + */ +#define CLASS_NORMAL 0 /* [All other] Normal characters */ +#define CLASS_PERCENT 1 /* [%] Begin format */ +#define CLASS_MODIFIER 2 /* [-+ #,] Modifiers */ +#define CLASS_ZERO 3 /* [0] Special modifier */ +#define CLASS_STAR 4 /* [*] Width supplied by arg */ +#define CLASS_DIGIT 5 /* [1-9] Field widths */ +#define CLASS_DOT 6 /* [.] Introduce precision */ +#define CLASS_BITS 7 /* [hlL] Length bits */ +#define CLASS_TYPE 8 /* [cdfinopsSuxX] Type specifiers */ + +#define STATE_NORMAL 0 /* Normal chars in format string */ +#define STATE_PERCENT 1 /* "%" */ +#define STATE_MODIFIER 2 /* Read flag */ +#define STATE_WIDTH 3 /* Width spec */ +#define STATE_DOT 4 /* "." */ +#define STATE_PRECISION 5 /* Precision spec */ +#define STATE_BITS 6 /* Size spec */ +#define STATE_TYPE 7 /* Data type */ +#define STATE_COUNT 8 + +/* + * Format: %[modifier][width][precision][bits][type] + * + * #define CLASS_MODIFIER 2 [-+ #,] Modifiers + * #define CLASS_BITS 7 [hlL] Length bits + */ + + +/* + * Flags + */ +#define SPRINTF_LEFT 0x1 /* Left align */ +#define SPRINTF_SIGN 0x2 /* Always sign the result */ +#define SPRINTF_LEAD_SPACE 0x4 /* put leading space for +ve numbers */ +#define SPRINTF_ALTERNATE 0x8 /* Alternate format */ +#define SPRINTF_LEAD_ZERO 0x10 /* Zero pad */ +#define SPRINTF_SHORT 0x20 /* 16-bit */ +#define SPRINTF_LONG 0x40 /* 32-bit */ +#if BLD_FEATURE_INT64 +#define SPRINTF_LONGLONG 0x80 /* 64-bit */ +#endif +#define SPRINTF_COMMA 0x100 /* Thousand comma separators */ +#define SPRINTF_UPPER_CASE 0x200 /* As the name says for numbers */ + +typedef struct Format { + uchar *buf; + uchar *endbuf; + uchar *start; + uchar *end; + int growBy; + int maxsize; + + int precision; + int radix; + int width; + int flags; + int len; +} Format; + +static int growBuf(MPR_LOC_DEC(ctx, loc), Format *fmt); + +#define BPUT(ctx, loc, fmt, c) \ + if (1) { \ + /* Less one to allow room for the null */ \ + if ((fmt)->end >= ((fmt)->endbuf - sizeof(char))) { \ + if (growBuf(MPR_LOC_PASS(ctx, loc), fmt)) { \ + *(fmt)->end++ = (c); \ + } \ + } else { \ + *(fmt)->end++ = (c); \ + } \ + } else + +#define BPUTNULL(ctx, loc, fmt) \ + if (1) { \ + if ((fmt)->end > (fmt)->endbuf) { \ + if (growBuf(MPR_LOC_PASS(ctx, loc), fmt)) { \ + *(fmt)->end = '\0'; \ + } \ + } else { \ + *(fmt)->end = '\0'; \ + } \ + } else + +/******************************************************************************/ + +#if BLD_FEATURE_INT64 +#define unum uint64 +#define num int64 +#else +#define unum uint +#define num int +#endif + +/***************************** Forward Declarations ***************************/ +#ifdef __cplusplus +extern "C" { +#endif + +static int getState(char c, int state); +static int mprSprintfCore(MPR_LOC_DEC(ctx, loc), char **s, + int maxsize, const char *fmt, va_list arg); +static void outNum(MPR_LOC_DEC(ctx, loc), Format *fmt, const char *prefix, + unum val); + +#if BLD_FEATURE_FLOATING_POINT +static void outFloat(MPR_LOC_DEC(ctx, loc), Format *fmt, char specChar, + double value); +#endif + +/******************************************************************************/ + +int mprPrintf(MprCtx ctx, const char *fmt, ...) +{ + va_list ap; + char *buf; + int len; + MprApp *app; + + /* No asserts here as this is used as part of assert reporting */ + + app = mprGetApp(ctx); + + va_start(ap, fmt); + len = mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, ap); + va_end(ap); + if (len >= 0 && app->console) { + len = mprWrite(app->console, buf, len); + } + mprFree(buf); + + return len; +} + +/******************************************************************************/ + +int mprErrorPrintf(MprCtx ctx, const char *fmt, ...) +{ + va_list ap; + char *buf; + int len; + MprApp *app; + + /* No asserts here as this is used as part of assert reporting */ + + app = mprGetApp(ctx); + + va_start(ap, fmt); + len = mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, ap); + va_end(ap); + if (len >= 0 && app->error) { + len = mprWrite(app->error, buf, len); + } + mprFree(buf); + + return len; +} + +/******************************************************************************/ + +int mprFprintf(MprFile *file, const char *fmt, ...) +{ + va_list ap; + char *buf; + int len; + + if (file == 0) { + return MPR_ERR_BAD_HANDLE; + } + + va_start(ap, fmt); + len = mprAllocVsprintf(MPR_LOC_ARGS(file), &buf, 0, fmt, ap); + va_end(ap); + + if (len >= 0) { + len = mprWrite(file, buf, len); + } + mprFree(buf); + return len; +} + +/******************************************************************************/ +/* + * Printf with a static buffer. Used internally only. WILL NOT MALLOC. + */ + +int mprStaticPrintf(MprCtx ctx, const char *fmt, ...) +{ + va_list ap; + char buf[MPR_MAX_STRING]; + char *bufp; + int len; + MprApp *app; + + app = mprGetApp(ctx); + + va_start(ap, fmt); + bufp = buf; + len = mprSprintfCore(MPR_LOC_ARGS(0), &bufp, MPR_MAX_STRING, fmt, ap); + va_end(ap); + if (len >= 0) { + len = mprWrite(app->console, buf, len); + } + return len; +} + +/******************************************************************************/ + +int mprSprintf(char *buf, int n, const char *fmt, ...) +{ + va_list ap; + int result; + + mprAssert(buf); + mprAssert(fmt); + mprAssert(n > 0); + + va_start(ap, fmt); + result = mprSprintfCore(MPR_LOC_ARGS(0), &buf, n, fmt, ap); + va_end(ap); + return result; +} + +/******************************************************************************/ + +int mprVsprintf(char *buf, int n, const char *fmt, va_list arg) +{ + mprAssert(buf); + mprAssert(fmt); + mprAssert(n > 0); + + return mprSprintfCore(MPR_LOC_ARGS(0), &buf, n, fmt, arg); +} + +/******************************************************************************/ + +int mprAllocSprintf(MPR_LOC_DEC(ctx, loc), char **buf, int maxSize, + const char *fmt, ...) +{ + va_list ap; + int result; + + mprAssert(buf); + mprAssert(fmt); + + *buf = 0; + va_start(ap, fmt); + result = mprSprintfCore(MPR_LOC_PASS(ctx, loc), buf, maxSize, fmt, ap); + va_end(ap); + return result; +} + +/******************************************************************************/ + +int mprAllocVsprintf(MPR_LOC_DEC(ctx, loc), char **buf, int maxSize, + const char *fmt, va_list arg) +{ + mprAssert(buf); + mprAssert(fmt); + + *buf = 0; + return mprSprintfCore(MPR_LOC_PASS(ctx, loc), buf, maxSize, fmt, arg); +} + +/******************************************************************************/ + +static int getState(char c, int state) +{ + /* + * Declared here to remove all static / globals + * FUTURE OPT -- need to measure this. Could be slow on BREW. + */ + + char stateMap[] = { + /* STATES: Normal Percent Modifier Width Dot Prec Bits Type */ + /* CLASS 0 1 2 3 4 5 6 7 */ + /* Normal 0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* Percent 1 */ 1, 0, 1, 1, 1, 1, 1, 1, + /* Modifier 2 */ 0, 2, 2, 0, 0, 0, 0, 0, + /* Zero 3 */ 0, 2, 2, 3, 0, 5, 0, 0, + /* Star 4 */ 0, 3, 3, 0, 5, 0, 0, 0, + /* Digit 5 */ 0, 3, 3, 3, 5, 5, 0, 0, + /* Dot 6 */ 0, 4, 4, 4, 0, 0, 0, 0, + /* Bits 7 */ 0, 6, 6, 6, 6, 6, 6, 0, + /* Types 8 */ 0, 7, 7, 7, 7, 7, 7, 0, + }; + + /* + * Format: %[modifier][width][precision][bits][type] + */ + char classMap[] = { + /* 0 ' ' ! " # $ % & ' */ + 2, 0, 0, 2, 0, 1, 0, 0, + /* 07 ( ) * + , - . / */ + 0, 0, 4, 2, 2, 2, 6, 0, + /* 10 0 1 2 3 4 5 6 7 */ + 3, 5, 5, 5, 5, 5, 5, 5, + /* 17 8 9 : ; < = > ? */ + 5, 5, 0, 0, 0, 0, 0, 0, + /* 20 @ A B C D E F G */ + 0, 0, 0, 0, 0, 0, 0, 0, + /* 27 H I J K L M N O */ + 0, 0, 0, 0, 7, 0, 0, 0, + /* 30 P Q R S T U V W */ + 0, 0, 0, 8, 0, 0, 0, 0, + /* 37 X Y Z [ \ ] ^ _ */ + 8, 0, 0, 0, 0, 0, 0, 0, + /* 40 ' a b c d e f g */ + 0, 0, 0, 8, 8, 0, 8, 0, + /* 47 h i j k l m n o */ + 7, 8, 0, 0, 7, 0, 8, 8, + /* 50 p q r s t u v w */ + 8, 0, 0, 8, 0, 8, 0, 0, + /* 57 x y z */ + 8, 0, 0, + }; + + int chrClass; + + if (c < ' ' || c > 'z') { + chrClass = CLASS_NORMAL; + } else { + mprAssert((c - ' ') < (int) sizeof(classMap)); + chrClass = classMap[(c - ' ')]; + } + mprAssert((chrClass * STATE_COUNT + state) < (int) sizeof(stateMap)); + state = stateMap[chrClass * STATE_COUNT + state]; + return state; +} + +/******************************************************************************/ + +static int mprSprintfCore(MPR_LOC_DEC(ctx, loc), char **bufPtr, + int maxsize, const char *spec, va_list arg) +{ + Format fmt; + char *cp; + char c; + char *sValue; + num iValue; + unum uValue; + int count, i, len, state; + + mprAssert(bufPtr); + mprAssert(spec); + + if (*bufPtr != 0) { + mprAssert(maxsize > 0); + fmt.buf = (uchar*) *bufPtr; + fmt.endbuf = &fmt.buf[maxsize]; + fmt.growBy = 0; + } else { + if (maxsize <= 0) { + maxsize = MAXINT; + } + + len = min(MPR_DEFAULT_ALLOC, maxsize); + fmt.buf = (uchar*) mprAllocBlock(MPR_LOC_PASS(ctx, loc), len); + fmt.endbuf = &fmt.buf[len]; + fmt.growBy = MPR_DEFAULT_ALLOC * 2; + } + + fmt.maxsize = maxsize; + fmt.start = fmt.buf; + fmt.end = fmt.buf; + fmt.len = 0; + *fmt.start = '\0'; + + state = STATE_NORMAL; + + while ((c = *spec++) != '\0') { + state = getState(c, state); + + switch (state) { + case STATE_NORMAL: + BPUT(ctx, loc, &fmt, c); + break; + + case STATE_PERCENT: + fmt.precision = -1; + fmt.width = 0; + fmt.flags = 0; + break; + + case STATE_MODIFIER: + switch (c) { + case '+': + fmt.flags |= SPRINTF_SIGN; + break; + case '-': + fmt.flags |= SPRINTF_LEFT; + break; + case '#': + fmt.flags |= SPRINTF_ALTERNATE; + break; + case '0': + fmt.flags |= SPRINTF_LEAD_ZERO; + break; + case ' ': + fmt.flags |= SPRINTF_LEAD_SPACE; + break; + case ',': + fmt.flags |= SPRINTF_COMMA; + break; + } + break; + + case STATE_WIDTH: + if (c == '*') { + fmt.width = va_arg(arg, int); + if (fmt.width < 0) { + fmt.width = -fmt.width; + fmt.flags |= SPRINTF_LEFT; + } + } else { + while (isdigit((int)c)) { + fmt.width = fmt.width * 10 + (c - '0'); + c = *spec++; + } + spec--; + } + break; + + case STATE_DOT: + fmt.precision = 0; + fmt.flags &= ~SPRINTF_LEAD_ZERO; + break; + + case STATE_PRECISION: + if (c == '*') { + fmt.precision = va_arg(arg, int); + } else { + while (isdigit((int) c)) { + fmt.precision = fmt.precision * 10 + (c - '0'); + c = *spec++; + } + spec--; + } + break; + + case STATE_BITS: + switch (c) { +#if BLD_FEATURE_INT64 + case 'L': + fmt.flags |= SPRINTF_LONGLONG; /* 64 bit */ + break; +#endif + + case 'l': + fmt.flags |= SPRINTF_LONG; + break; + + case 'h': + fmt.flags |= SPRINTF_SHORT; + break; + } + break; + + case STATE_TYPE: + switch (c) { +#if BLD_FEATURE_FLOATING_POINT + case 'e': + case 'g': + case 'f': + fmt.radix = 10; + outFloat(MPR_LOC_PASS(ctx, loc), &fmt, c, + (double) va_arg(arg, double)); + break; +#endif + case 'c': + BPUT(ctx, loc, &fmt, (char) va_arg(arg, int)); + break; + + case 's': + case 'S': + sValue = va_arg(arg, char*); + if (sValue == 0) { + sValue = "null"; + len = strlen(sValue); + } else if (fmt.flags & SPRINTF_ALTERNATE) { + sValue++; + len = (int) *sValue; + } else if (fmt.precision >= 0) { + /* + * Can't use strlen(), the string may not have a null + */ + cp = sValue; + for (len = 0; len < fmt.precision; len++) { + if (*cp++ == '\0') { + break; + } + } + } else { + len = strlen(sValue); + } + if (!(fmt.flags & SPRINTF_LEFT)) { + for (i = len; i < fmt.width; i++) { + BPUT(ctx, loc, &fmt, (char) ' '); + } + } + for (i = 0; i < len && *sValue; i++) { + BPUT(ctx, loc, &fmt, *sValue++); + } + if (fmt.flags & SPRINTF_LEFT) { + for (i = len; i < fmt.width; i++) { + BPUT(ctx, loc, &fmt, (char) ' '); + } + } + break; + + case 'i': + ; + case 'd': + fmt.radix = 10; + if (fmt.flags & SPRINTF_SHORT) { + iValue = (short) va_arg(arg, int); + } else if (fmt.flags & SPRINTF_LONG) { + iValue = va_arg(arg, long); +#if BLD_FEATURE_INT64 + } else if (fmt.flags & SPRINTF_LONGLONG) { + iValue = va_arg(arg, num); +#endif + } else { + iValue = va_arg(arg, int); + } + if (iValue >= 0) { + if (fmt.flags & SPRINTF_LEAD_SPACE) { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, " ", iValue); + } else if (fmt.flags & SPRINTF_SIGN) { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, "+", iValue); + } else { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, 0, iValue); + } + } else { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, "-", -iValue); + } + break; + + case 'X': + fmt.flags |= SPRINTF_UPPER_CASE; + /* Fall through */ + case 'o': + case 'x': + case 'u': + if (fmt.flags & SPRINTF_SHORT) { + uValue = (ushort) va_arg(arg, uint); + } else if (fmt.flags & SPRINTF_LONG) { + uValue = va_arg(arg, ulong); +#if BLD_FEATURE_INT64 + } else if (fmt.flags & SPRINTF_LONGLONG) { + uValue = va_arg(arg, unum); +#endif + } else { + uValue = va_arg(arg, uint); + } + if (c == 'u') { + fmt.radix = 10; + outNum(MPR_LOC_PASS(ctx, loc), &fmt, 0, uValue); + } else if (c == 'o') { + fmt.radix = 8; + if (fmt.flags & SPRINTF_ALTERNATE && uValue != 0) { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, "0", uValue); + } else { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, 0, uValue); + } + } else { + fmt.radix = 16; + if (fmt.flags & SPRINTF_ALTERNATE && uValue != 0) { + if (c == 'X') { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, "0X", uValue); + } else { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, "0x", uValue); + } + } else { + outNum(MPR_LOC_PASS(ctx, loc), &fmt, 0, uValue); + } + } + break; + + case 'n': /* Count of chars seen thus far */ + if (fmt.flags & SPRINTF_SHORT) { + short *count = va_arg(arg, short*); + *count = fmt.end - fmt.start; + } else if (fmt.flags & SPRINTF_LONG) { + long *count = va_arg(arg, long*); + *count = fmt.end - fmt.start; + } else { + int *count = va_arg(arg, int *); + *count = fmt.end - fmt.start; + } + break; + + case 'p': /* Pointer */ +#if __WORDSIZE == 64 && BLD_FEATURE_INT64 + uValue = (unum) va_arg(arg, void*); +#else + uValue = (uint) (int) va_arg(arg, void*); +#endif + fmt.radix = 16; + outNum(MPR_LOC_PASS(ctx, loc), &fmt, "0x", uValue); + break; + + default: + BPUT(ctx, loc, &fmt, c); + } + } + } + BPUTNULL(ctx, loc, &fmt); + + count = fmt.end - fmt.start; + if (*bufPtr == 0) { + *bufPtr = (char*) fmt.buf; + } + return count; +} + +/******************************************************************************/ +/* + * Output a number according to the given format. If BLD_FEATURE_INT64 is + * defined, then uses 64 bits universally. Slower but smaller code. + */ + +static void outNum(MPR_LOC_DEC(ctx, loc), Format *fmt, const char *prefix, + unum value) +{ + char numBuf[64]; + char *cp; + char *endp; + char c; + int letter, len, leadingZeros, i, fill; + + endp = &numBuf[sizeof(numBuf) - 1]; + *endp = '\0'; + cp = endp; + + /* + * Convert to ascii + */ + if (fmt->radix == 16) { + do { + letter = (int) (value % fmt->radix); + if (letter > 9) { + if (fmt->flags & SPRINTF_UPPER_CASE) { + letter = 'A' + letter - 10; + } else { + letter = 'a' + letter - 10; + } + } else { + letter += '0'; + } + *--cp = letter; + value /= fmt->radix; + } while (value > 0); + + } else if (fmt->flags & SPRINTF_COMMA) { + i = 1; + do { + *--cp = '0' + (int) (value % fmt->radix); + value /= fmt->radix; + if ((i++ % 3) == 0 && value > 0) { + *--cp = ','; + } + } while (value > 0); + } else { + do { + *--cp = '0' + (int) (value % fmt->radix); + value /= fmt->radix; + } while (value > 0); + } + + len = endp - cp; + fill = fmt->width - len; + + if (prefix != 0) { + fill -= strlen(prefix); + } + leadingZeros = (fmt->precision > len) ? fmt->precision - len : 0; + fill -= leadingZeros; + + if (!(fmt->flags & SPRINTF_LEFT)) { + c = (fmt->flags & SPRINTF_LEAD_ZERO) ? '0': ' '; + for (i = 0; i < fill; i++) { + BPUT(ctx, loc, fmt, c); + } + } + if (prefix != 0) { + while (*prefix) { + BPUT(ctx, loc, fmt, *prefix++); + } + } + for (i = 0; i < leadingZeros; i++) { + BPUT(ctx, loc, fmt, '0'); + } + while (*cp) { + BPUT(ctx, loc, fmt, *cp); + cp++; + } + if (fmt->flags & SPRINTF_LEFT) { + for (i = 0; i < fill; i++) { + BPUT(ctx, loc, fmt, ' '); + } + } +} + +/******************************************************************************/ +#if BLD_FEATURE_FLOATING_POINT +/* + * Output a floating point number + */ + +static void outFloat(MPR_LOC_DEC(ctx, loc), Format *fmt, char specChar, + double value) +{ + char *cp; +#if FUTURE + char numBuf[64]; + char *endp; + char c; + int letter, len, leadingZeros, i, fill, width, precision; + + endp = &numBuf[sizeof(numBuf) - 1]; + *endp = '\0'; + + precision = fmt->precision; + if (precision < 0) { + precision = 6; + } else if (precision > (sizeof(numBuf) - 1)) { + precision = (sizeof(numBuf) - 1); + } + width = min(fmt->width, sizeof(numBuf) - 1); + + if (__isnanl(value)) { + "nan" + } else if (__isinfl(value)) { + "infinity" + } else if (value < 0) { + prefix = "-"; + } else if (fmt.flags & SPRINTF_LEAD_SPACE) { + prefix = " "; + } else if (fmt.flags & SPRINTF_SIGN) { + prefix = "+"; + } + + + /* + * Do the exponent part + */ + cp = &numBuf[sizeof(numBuf) - precision]; + for (i = 0; i < precision; i++) { + *cp++ = '0' + (int) (value % fmt->radix); + value /= fmt->radix; + } + + /* + * Do the decimal part + */ + if (fmt->flags & SPRINTF_COMMA) { + i = 1; + do { + *--cp = '0' + (int) (value % fmt->radix); + value /= fmt->radix; + if ((i++ % 3) == 0 && value > 0) { + *--cp = ','; + } + } while (value >= 1.0); + + } else { + do { + *--cp = '0' + (int) (value % fmt->radix); + value /= fmt->radix; + } while (value > 1.0); + } + + len = endp - cp; + fill = fmt->width - len; + + if (prefix != 0) { + fill -= strlen(prefix); + } + + leadingZeros = (fmt->precision > len) ? fmt->precision - len : 0; + fill -= leadingZeros; + + if (!(fmt->flags & SPRINTF_LEFT)) { + c = (fmt->flags & SPRINTF_LEAD_ZERO) ? '0': ' '; + for (i = 0; i < fill; i++) { + BPUT(ctx, loc, fmt, c); + } + } + if (prefix != 0) { + BPUT(ctx, loc, fmt, prefix); + } + for (i = 0; i < leadingZeros; i++) { + BPUT(ctx, loc, fmt, '0'); + } + BPUT(ctx, loc, fmt, cp); + if (fmt->flags & SPRINTF_LEFT) { + for (i = 0; i < fill; i++) { + BPUT(ctx, loc, fmt, ' '); + } + } +#else + char numBuf[64]; + if (specChar == 'f') { + sprintf(numBuf, "%*.*f", fmt->width, fmt->precision, value); + } else if (specChar == 'g') { + sprintf(numBuf, "%*.*g", fmt->width, fmt->precision, value); + } else if (specChar == 'e') { + sprintf(numBuf, "%*.*e", fmt->width, fmt->precision, value); + } + for (cp = numBuf; *cp; cp++) { + BPUT(ctx, loc, fmt, *cp); + } +#endif +} + +#endif /* BLD_FEATURE_FLOATING_POINT */ +/******************************************************************************/ +/* + * Grow the buffer to fit new data. Return 1 if the buffer can grow. + * Grow using the growBy size specified when creating the buffer. + */ + +static int growBuf(MPR_LOC_DEC(ctx, loc), Format *fmt) +{ + uchar *newbuf; + int buflen; + + buflen = fmt->endbuf - fmt->buf; + if (fmt->maxsize >= 0 && buflen >= fmt->maxsize) { + return 0; + } + if (fmt->growBy < 0) { + /* + * User supplied buffer + */ + return 0; + } + + newbuf = (uchar*) mprAlloc(ctx, buflen + fmt->growBy); + if (fmt->buf) { + memcpy(newbuf, fmt->buf, buflen); + mprFree(fmt->buf); + } + + buflen += fmt->growBy; + fmt->end = newbuf + (fmt->end - fmt->buf); + fmt->start = newbuf + (fmt->start - fmt->buf); + fmt->buf = newbuf; + fmt->endbuf = &fmt->buf[buflen]; + + /* + * Increase growBy to reduce overhead + */ + if ((buflen + (fmt->growBy * 2)) < fmt->maxsize) { + fmt->growBy *= 2; + } + return 1; +} + +/******************************************************************************/ + +/* + * 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-2.0/mpr/mprString.c b/source4/lib/appweb/ejs-2.0/mpr/mprString.c new file mode 100644 index 0000000000..b688667bcc --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprString.c @@ -0,0 +1,727 @@ +/** + * @file mprString.c + * @brief String routines safe for embedded programming + * @overview This module provides safe replacements for the standard + * string library. + * @remarks Most routines in this file are not thread-safe. It is the callers + * responsibility to perform all thread synchronization. + */ + +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/********************************** Includes **********************************/ +/* + * We need to use the underlying str(cpy) routines to implement our safe + * alternatives + */ +#if !DOXYGEN +#define UNSAFE_FUNCTIONS_OK 1 +#endif + +/******************************************************************************/ +/**************************** Safe String Handling ****************************/ +/******************************************************************************/ + +int mprStrcpy(char *dest, int destMax, const char *src) +{ + int len; + + mprAssert(dest); + mprAssert(destMax >= 0); + mprAssert(src); + + len = strlen(src); + if (destMax > 0 && len >= destMax && len > 0) { + return MPR_ERR_WONT_FIT; + } + if (len > 0) { + memcpy(dest, src, len); + dest[len] = '\0'; + } else { + *dest = '\0'; + len = 0; + } + return len; +} + +/******************************************************************************/ + +int mprAllocStrcpy(MPR_LOC_DEC(ctx, loc), char **dest, int destMax, + const char *src) +{ + int len; + + mprAssert(dest); + mprAssert(destMax >= 0); + mprAssert(src); + + len = strlen(src); + if (destMax > 0 && len >= destMax) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + if (len > 0) { + *dest = (char*) mprAllocBlock(MPR_LOC_PASS(ctx, loc), len); + memcpy(*dest, src, len); + (*dest)[len] = '\0'; + } else { + *dest = (char*) mprAlloc(ctx, 1); + *dest = '\0'; + len = 0; + } + return len; +} + +/******************************************************************************/ + +int mprMemcpy(char *dest, int destMax, const char *src, int nbytes) +{ + mprAssert(dest); + mprAssert(destMax <= 0 || destMax >= nbytes); + mprAssert(src); + mprAssert(nbytes >= 0); + + if (destMax > 0 && nbytes > destMax) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + if (nbytes > 0) { + memcpy(dest, src, nbytes); + return nbytes; + } else { + return 0; + } +} + +/******************************************************************************/ + +int mprAllocMemcpy(MPR_LOC_DEC(ctx, loc), char **dest, int destMax, + const void *src, int nbytes) +{ + mprAssert(dest); + mprAssert(src); + mprAssert(nbytes > 0); + mprAssert(destMax <= 0 || destMax >= nbytes); + + if (destMax > 0 && nbytes > destMax) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + if (nbytes > 0) { + *dest = (char*) mprAllocBlock(MPR_LOC_PASS(ctx,loc), nbytes); + if (*dest == 0) { + return MPR_ERR_MEMORY; + } + memcpy(*dest, src, nbytes); + } else { + *dest = (char*) mprAlloc(ctx, 1); + } + return nbytes; +} + +/******************************************************************************/ + +static int mprCoreStrcat(MPR_LOC_DEC(ctx, loc), char **destp, int destMax, + int existingLen, const char *delim, const char *src, va_list args) +{ + va_list ap; + char *dest, *str, *dp; + 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; + if (existingLen > 0) { + addBytes += sepLen; + } + str = (char*) src; + + while (str) { + addBytes += strlen(str); + str = va_arg(ap, char*); + if (str) { + addBytes += sepLen; + } + } + + required = existingLen + addBytes + 1; + if (destMax > 0 && required >= destMax) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + + if (ctx != 0) { + if (dest == 0) { + dest = (char*) mprAllocBlock(MPR_LOC_PASS(ctx, loc), required); + } else { + dest = (char*) mprReallocBlock(MPR_LOC_PASS(ctx, loc), dest, + required); + } + } else { + dest = (char*) *destp; + } + + dp = &dest[existingLen]; + if (delim && existingLen > 0) { + strcpy(dp, delim); + dp += sepLen; + } + + if (addBytes > 0) { +#ifdef __va_copy + __va_copy(ap, args); +#else + ap = args; +#endif + str = (char*) 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*) mprAlloc(ctx, 1); + } + *dp = '\0'; + + *destp = dest; + mprAssert(dp < &dest[required]); + return required - 1; +} + +/******************************************************************************/ + +int mprStrcat(char *dest, int destMax, const char *delim, const char *src, ...) +{ + va_list ap; + int rc; + + mprAssert(dest); + mprAssert(src); + + va_start(ap, src); + rc = mprCoreStrcat(MPR_LOC_ARGS(0), &dest, destMax, strlen(dest), + delim, src, ap); + va_end(ap); + return rc; +} + +/******************************************************************************/ + +int mprAllocStrcat(MPR_LOC_DEC(ctx, loc), char **destp, int destMax, + const char *delim, const char *src, ...) +{ + va_list ap; + int rc; + + mprAssert(destp); + mprAssert(src); + + *destp = 0; + va_start(ap, src); + rc = mprCoreStrcat(MPR_LOC_PASS(ctx, loc), destp, destMax, 0, delim, + src, ap); + va_end(ap); + return rc; +} + +/******************************************************************************/ + +int mprReallocStrcat(MPR_LOC_DEC(ctx, loc), char **destp, int destMax, + int existingLen, const char *delim, const char *src,...) +{ + va_list ap; + int rc; + + va_start(ap, src); + rc = mprCoreStrcat(MPR_LOC_PASS(ctx, loc), destp, destMax, existingLen, + delim, src, ap); + va_end(ap); + return rc; +} + +/******************************************************************************/ + +int mprStrlen(const char *src, int max) +{ + int len; + + len = strlen(src); + if (len >= max) { + mprAssert(0); + return MPR_ERR_WONT_FIT; + } + return len; +} + +/******************************************************************************/ + +char *mprStrTrim(char *str, const char *set) +{ + int len, i; + + if (str == 0 || set == 0) { + return str; + } + + i = strspn(str, set); + str += i; + + len = strlen(str); + while (strspn(&str[len - 1], set) > 0) { + str[len - 1] = '\0'; + len--; + } + return str; +} + +/******************************************************************************/ +/* + * Map a string to lower case (overwrites original string) + */ + +char *mprStrLower(char *str) +{ + char *cp; + + mprAssert(str); + + if (str == 0) { + return 0; + } + + for (cp = str; *cp; cp++) { + if (isupper(*cp)) { + *cp = (char) tolower(*cp); + } + } + return str; +} + +/******************************************************************************/ +/* + * Map a string to upper case (overwrites buffer) + */ + +char *mprStrUpper(char *str) +{ + char *cp; + + mprAssert(str); + if (str == 0) { + return 0; + } + + for (cp = str; *cp; cp++) { + if (islower(*cp)) { + *cp = (char) toupper(*cp); + } + } + return str; +} + +/******************************************************************************/ +/* + * Case insensitive string comparison. Stop at the end of str1. + */ + +int mprStrcmpAnyCase(const char *str1, const char *str2) +{ + int rc; + + if (str1 == 0 || str2 == 0) { + return -1; + } + if (str1 == str2) { + return 0; + } + + for (rc = 0; *str1 && rc == 0; str1++, str2++) { + rc = tolower(*str1) - tolower(*str2); + } + if (*str2) { + return -1; + } + return rc; +} + +/******************************************************************************/ +/* + * Case insensitive string comparison. Limited by length + */ + +int mprStrcmpAnyCaseCount(const char *str1, const char *str2, int len) +{ + int rc; + + if (str1 == 0 || str2 == 0) { + return -1; + } + if (str1 == str2) { + return 0; + } + + for (rc = 0; len-- > 0 && *str1 && rc == 0; str1++, str2++) { + rc = tolower(*str1) - tolower(*str2); + } + return rc; +} + +/******************************************************************************/ +/* + * Return the last portion of a pathname + */ + +const char *mprGetBaseName(const char *name) +{ + char *cp; + + cp = strrchr(name, '/'); + + if (cp == 0) { + cp = strrchr(name, '\\'); + if (cp == 0) { + return name; + } + } + if (cp == name) { + if (cp[1] == '\0') { + return name; + } + } else { + if (cp[1] == '\0') { + return ""; + } + } + return &cp[1]; +} + +/******************************************************************************/ +/* + * Return the directory portion of a pathname into the users buffer. + */ + +char *mprGetDirName(char *buf, int bufsize, const 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 buf; + } + } + + if (cp == path && cp[1] == '\0') { + strcpy(buf, "."); + return buf; + } + + dlen = cp - path; + if (dlen < bufsize) { + if (dlen == 0) { + dlen++; + } + mprMemcpy(buf, bufsize, path, dlen); + buf[dlen] = '\0'; + return buf; + } + return 0; +} + +/******************************************************************************/ +/* + * Thread-safe wrapping of strtok. Note "str" is modifed as per strtok() + */ + +char *mprStrTok(char *str, const char *delim, char **last) +{ + char *start, *end; + int i; + + start = str ? str : *last; + + if (start == 0) { + return 0; + } + + i = strspn(start, delim); + start += i; + if (*start == '\0') { + *last = 0; + return 0; + } + end = strpbrk(start, delim); + if (end) { + *end++ = '\0'; + i = strspn(end, delim); + end += i; + } + *last = end; + return start; +} + +/******************************************************************************/ +/* + * Split the buffer into word tokens + */ + +char *mprGetWordTok(char *buf, int bufsize, const char *str, const char *delim, + const char **tok) +{ + const char *start, *end; + int i, len; + + 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) { + len = min(end - start, bufsize - 1); + mprMemcpy(buf, bufsize, start, len); + buf[len] = '\0'; + } else { + if (mprStrcpy(buf, bufsize, start) < 0) { + buf[bufsize - 1] = '\0'; + return 0; + } + buf[bufsize - 1] = '\0'; + } + *tok = end; + return buf; +} + +/******************************************************************************/ +/* + * Format a number as a string. + */ + +char *mprItoa(char *buf, int size, int value) +{ + char numBuf[16]; + char *cp, *dp, *endp; + int negative; + + cp = &numBuf[sizeof(numBuf)]; + *--cp = '\0'; + + if (value < 0) { + negative = 1; + value = -value; + size--; + } else { + negative = 0; + } + + do { + *--cp = '0' + (value % 10); + value /= 10; + } while (value > 0); + + if (negative) { + *--cp = '-'; + } + + dp = buf; + endp = &buf[size]; + while (dp < endp && *cp) { + *dp++ = *cp++; + } + *dp++ = '\0'; + return buf; +} + +/******************************************************************************/ +/* + * Parse an ascii number. Supports radix 10 or 16. + */ + +int mprAtoi(const char *str, int radix) +{ + int c, val, negative; + + mprAssert(radix == 10 || radix == 16); + + if (str == 0) { + return 0; + } + + val = 0; + if (radix == 10 && *str == '-') { + negative = 1; + str++; + } else { + negative = 0; + } + + if (radix == 10) { + while (*str && isdigit(*str)) { + val = (val * radix) + *str - '0'; + str++; + } + } else if (radix == 16) { + if (*str == '0' && tolower(str[1]) == 'x') { + str += 2; + } + while (*str) { + c = tolower(*str); + if (isdigit(c)) { + val = (val * radix) + c - '0'; + } else if (c >= 'a' && c <= 'f') { + val = (val * radix) + c - 'a' + 10; + } else { + break; + } + str++; + } + } + + return (negative) ? -val: val; +} + +/******************************************************************************/ +/* + * Make an argv array. Caller must free by calling mprFree(argv) to free + * everything. + */ + +int mprMakeArgv(MprCtx ctx, const char *program, const char *cmd, + char ***argvp, int *argcp) +{ + char *cp, **argv, *buf, *args; + int size, argc; + + /* + * Allocate one buffer for argv and the actual args themselves + */ + size = strlen(cmd) + 1; + + buf = (char*) mprAlloc(ctx, (MPR_MAX_ARGC * sizeof(char*)) + size); + if (buf == 0) { + return MPR_ERR_MEMORY; + } + + args = &buf[MPR_MAX_ARGC * sizeof(char*)]; + strcpy(args, cmd); + argv = (char**) buf; + + argc = 0; + if (program) { + argv[argc++] = (char*) mprStrdup(ctx, program); + } + + for (cp = args; cp && *cp != '\0'; argc++) { + if (argc >= MPR_MAX_ARGC) { + mprAssert(argc < MPR_MAX_ARGC); + mprFree(buf); + *argvp = 0; + if (argcp) { + *argcp = 0; + } + return MPR_ERR_TOO_MANY; + } + while (isspace(*cp)) { + cp++; + } + if (*cp == '\0') { + break; + } + if (*cp == '"') { + cp++; + argv[argc] = cp; + while ((*cp != '\0') && (*cp != '"')) { + cp++; + } + } else { + argv[argc] = cp; + while (*cp != '\0' && !isspace(*cp)) { + cp++; + } + } + if (*cp != '\0') { + *cp++ = '\0'; + } + } + argv[argc] = 0; + + if (argcp) { + *argcp = argc; + } + *argvp = argv; + + return argc; +} + +/******************************************************************************/ + +/* + * 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-2.0/mpr/mprSymbol.c b/source4/lib/appweb/ejs-2.0/mpr/mprSymbol.c new file mode 100644 index 0000000000..11ac278db4 --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprSymbol.c @@ -0,0 +1,279 @@ +/* + * @file mprSym.cpp + * @brief Fast hashing symbol table lookup module + * @overview This symbol table uses a fast key lookup mechanism. Keys are + * strings and the value entries are arbitrary pointers. The keys are + * hashed into a series of buckets which then have a chain of hash + * entries using the standard doubly linked list classes (List/Link). + * The chain in in collating sequence so search time through the chain + * is on average (N/hashSize)/2. + * @remarks This module is not thread-safe. It is the callers responsibility + * to perform all thread synchronization. + */ +/********************************* Copyright **********************************/ +/* + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. 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 "mpr.h" + +/**************************** Forward Declarations ****************************/ + +static int hashIndex(const char *key, int size); +static MprSymbol *lookupInner(int *bucketIndex, MprSymbol **prevSp, + MprSymbolTable *table, const char *key); + +/*********************************** Code *************************************/ +/* + * Create a new symbol table of a given size. Caller should provide a size + * that is a prime number for the greatest efficiency. Caller should use + * mprFree to free the symbol table. + */ + +MprSymbolTable *mprCreateSymbolTable(MprCtx ctx, int hashSize) +{ + MprSymbolTable *table; + + table = mprAllocTypeZeroed(ctx, MprSymbolTable); + if (table == 0) { + return 0; + } + + if (hashSize < MPR_DEFAULT_HASH_SIZE) { + hashSize = MPR_DEFAULT_HASH_SIZE; + } + table->hashSize = hashSize; + + table->count = 0; + table->hashSize = hashSize; + table->buckets = mprAllocZeroedBlock(MPR_LOC_ARGS(table), + sizeof(MprSymbol*) * hashSize); + + if (table->buckets == 0) { + mprFree(table); + return 0; + } + + return table; +} + +/******************************************************************************/ +/* + * Insert an entry into the symbol table. If the entry already exists, update + * its value. Order of insertion is not preserved. + */ + +MprSymbol *mprInsertSymbol(MprSymbolTable *table, const char *key, void *ptr) +{ + MprSymbol *sp, *prevSp; + int index; + + sp = lookupInner(&index, &prevSp, table, key); + + if (sp != 0) { + /* + * Already exists. Just update the data. + */ + sp->data = ptr; + return sp; + } + + /* + * New entry + */ + sp = mprAllocTypeZeroed(table, MprSymbol); + if (sp == 0) { + return 0; + } + + sp->data = ptr; + sp->key = mprStrdup(sp, key); + sp->bucket = index; + + sp->next = table->buckets[index]; + table->buckets[index] = sp; + + table->count++; + return sp; +} + +/******************************************************************************/ +/* + * Remove an entry from the table + */ + +int mprRemoveSymbol(MprSymbolTable *table, const char *key) +{ + MprSymbol *sp, *prevSp; + int index; + + if ((sp = lookupInner(&index, &prevSp, table, key)) == 0) { + return MPR_ERR_NOT_FOUND; + } + + if (prevSp) { + prevSp->next = sp->next; + } else { + table->buckets[index] = sp->next; + } + table->count--; + + mprFree(sp); + return 0; +} + +/******************************************************************************/ +/* + * Lookup a key and return the hash entry + */ + +void *mprLookupSymbol(MprSymbolTable *table, const char *key) +{ + MprSymbol *sp; + + mprAssert(key); + + sp = lookupInner(0, 0, table, key); + if (sp == 0) { + return 0; + } + return sp->data; +} + +/******************************************************************************/ + +static MprSymbol *lookupInner(int *bucketIndex, MprSymbol **prevSp, + MprSymbolTable *table, const char *key) +{ + MprSymbol *sp, *prev; + int index, rc; + + mprAssert(key); + + index = hashIndex(key, table->hashSize); + if (bucketIndex) { + *bucketIndex = index; + } + + sp = table->buckets[index]; + prev = 0; + + while (sp) { + rc = strcmp(sp->key, key); + if (rc == 0) { + if (prevSp) { + *prevSp = prev; + } + return sp; + } + prev = sp; + mprAssert(sp != sp->next); + sp = sp->next; + } + return 0; +} + +/******************************************************************************/ + +int mprGetSymbolCount(MprSymbolTable *table) +{ + return table->count; +} + +/******************************************************************************/ +/* + * Return the first entry in the table. + */ + +MprSymbol *mprGetFirstSymTab(MprSymbolTable *table) +{ + MprSymbol *sp; + int i; + + mprAssert(table); + + for (i = 0; i < table->hashSize; i++) { + if ((sp = (MprSymbol*) table->buckets[i]) != 0) { + return sp; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Return the next entry in the table + */ + +MprSymbol *mprGetNextSymTab(MprSymbolTable *table, MprSymbol *last) +{ + MprSymbol *sp; + int i; + + mprAssert(table); + + if (last->next) { + return last->next; + } + + for (i = last->bucket + 1; i < table->hashSize; i++) { + if ((sp = (MprSymbol*) table->buckets[i]) != 0) { + return sp; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Hash the key to produce a hash index. + */ + +static int hashIndex(const char *key, int size) +{ + uint sum; + + sum = 0; + while (*key) { + sum += (sum * 33) + *key++; + } + + return sum % size; +} + +/******************************************************************************/ +/* + * 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-2.0/mpr/mprUnix.h b/source4/lib/appweb/ejs-2.0/mpr/mprUnix.h new file mode 100644 index 0000000000..fbbe29ae9c --- /dev/null +++ b/source4/lib/appweb/ejs-2.0/mpr/mprUnix.h @@ -0,0 +1,105 @@ +/* + * @file mprUnix.h + * @brief Make windows a bit more unix like + * @copy default + * + * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. + * + * This software is distributed under commercial and open source licenses. + * You may use the GPL open source license described below or you may acquire + * a commercial license from Mbedthis Software. You agree to be fully bound + * by the terms of either license. Consult the LICENSE.TXT distributed with + * this software for full details. + * + * This software is open source; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See the GNU General Public License for more + * details at: http://www.mbedthis.com/downloads/gplLicense.html + * + * This program is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This GPL license does NOT permit incorporating this software into + * proprietary programs. If you are unable to comply with the GPL, you must + * acquire a commercial license to use this software. Commercial licenses + * for this software and support services are available from Mbedthis + * Software at http://www.mbedthis.com + * + * @end + */ + +/******************************* Documentation ********************************/ + +/* + * This header is part of the Mbedthis Portable Runtime and aims to include + * all necessary O/S headers and to unify the constants and declarations + * required by Mbedthis products. It can be included by C or C++ programs. + */ + +/******************************************************************************/ + +#ifndef _h_MPR_UNIX +#define _h_MPR_UNIX 1 + +/******************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Define BLD_NO_POSIX_REMAP if these defines mess with your app + */ +#if WIN && !BLD_NO_POSIX_REMAP +/* + * MOB -- clashes with ATL + */ +#define access _access +#define close _close +#define fileno _fileno +#define fstat _fstat +#define getpid _getpid +#define open _open +#define putenv _putenv +#define read _read +#define stat _stat +#define umask _umask +#define unlink _unlink +#define write _write +#define strdup _strdup +#define lseek _lseek +#define getcwd _getcwd +#define chdir _chdir + +#define mkdir(a,b) _mkdir(a) +#define rmdir(a) _rmdir(a) + +#define R_OK 4 +#define W_OK 2 +#define MPR_TEXT "t" + +extern void srand48(long); +extern long lrand48(void); +extern long ulimit(int, ...); +extern long nap(long); +extern int getuid(void); +extern int geteuid(void); +#endif + + +/******************************************************************************/ +#ifdef __cplusplus +} +#endif + +#endif /* _h_MPR_UNIX */ + +/* + * 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 + */ |