/* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011, 2012, 2013, 2014 Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* * Copyright (C) 2011, 2012, 2013, 2014 Symisc Systems. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Redistributions in any form must be accompanied by information on * how to obtain complete source code for the PH7 engine and any * accompanying software that uses the PH7 engine software. * The source code must either be included in the distribution * or be available for no more than the cost of distribution plus * a nominal fee, and must be freely redistributable under reasonable * conditions. For an executable file, complete source code means * the source code for all modules it contains.It does not include * source code for modules or files that typically accompany the major * components of the operating system on which the executable file runs. * * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * $SymiscID: ph7.c v2.1 UNIX|WIN32/64 2012-09-15 09:00 stable $ */ /* This file is an amalgamation of many separate C source files from PH7 version 2.1. * By combining all the individual C code files into this single large file, the entire code * can be compiled as a single translation unit.This allows many compilers to do optimization's * that would not be possible if the files were compiled separately.Performance improvements * are commonly seen when PH7 is compiled as a single translation unit. * * This file is all you need to compile PH7.To use PH7 in other programs, you need * this file and the "ph7.h" header file that defines the programming interface to the * PH7 engine.(If you do not have the "ph7.h" header file at hand, you will find * a copy embedded within the text of this file.Search for "Header file: " to find * the start of the embedded ph7.h header file.) Additional code files may be needed if * you want a wrapper to interface PH7 with your choice of programming language. * To get the official documentation,please visit http://ph7.symisc.net/ */ /* * Make the sure the following is defined in the amalgamation build */ #ifndef PH7_AMALGAMATION #define PH7_AMALGAMATION #endif /* PH7_AMALGAMATION */ /* * Embedded header file for the PH7 engine: */ /* * ---------------------------------------------------------- * File: ph7.h * MD5: b5527f9c7eb410a9f9367a6b03014a65 * ---------------------------------------------------------- */ /* This file was automatically generated. Do not edit (except for compile time directive)! */ #ifndef _PH7_H_ #define _PH7_H_ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* * Copyright (C) 2011, 2012 Symisc Systems. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Redistributions in any form must be accompanied by information on * how to obtain complete source code for the PH7 engine and any * accompanying software that uses the PH7 engine software. * The source code must either be included in the distribution * or be available for no more than the cost of distribution plus * a nominal fee, and must be freely redistributable under reasonable * conditions. For an executable file, complete source code means * the source code for all modules it contains.It does not include * source code for modules or files that typically accompany the major * components of the operating system on which the executable file runs. * * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $SymiscID: ph7.h v2.1 UNIX|WIN32/64 2012-09-15 09:43 stable $ */ #include /* needed for the definition of va_list */ /* * Compile time engine version, signature, identification in the symisc source tree * and copyright notice. * Each macro have an equivalent C interface associated with it that provide the same * information but are associated with the library instead of the header file. * Refer to [ph7_lib_version()], [ph7_lib_signature()], [ph7_lib_ident()] and * [ph7_lib_copyright()] for more information. */ /* * The PH7_VERSION C preprocessor macroevaluates to a string literal * that is the ph7 version in the format "X.Y.Z" where X is the major * version number and Y is the minor version number and Z is the release * number. */ #define PH7_VERSION "2.1.4" /* * The PH7_VERSION_NUMBER C preprocessor macro resolves to an integer * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same * numbers used in [PH7_VERSION]. */ #define PH7_VERSION_NUMBER 2001004 /* * The PH7_SIG C preprocessor macro evaluates to a string * literal which is the public signature of the ph7 engine. * This signature could be included for example in a host-application * generated Server MIME header as follows: * Server: YourWebServer/x.x PH7/x.x.x \r\n */ #define PH7_SIG "PH7/2.1.4" /* * PH7 identification in the Symisc source tree: * Each particular check-in of a particular software released * by symisc systems have an unique identifier associated with it. * This macro hold the one associated with ph7. */ #define PH7_IDENT "ph7:c193f4d8a6b90ee60f9afad11840f1010054fdf9" /* * Copyright notice. * If you have any questions about the licensing situation,please * visit http://ph7.symisc.net/licensing.html * or contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net */ #define PH7_COPYRIGHT "Copyright (C) Symisc Systems 2011-2012, http://ph7.symisc.net/" /* Make sure we can call this stuff from C++ */ #ifdef __cplusplus extern "C" { #endif /* Forward declaration to public objects */ typedef struct ph7_io_stream ph7_io_stream; typedef struct ph7_context ph7_context; typedef struct ph7_value ph7_value; typedef struct ph7_vfs ph7_vfs; typedef struct ph7_vm ph7_vm; typedef struct ph7 ph7; /* * ------------------------------ * Compile time directives * ------------------------------ * For most purposes, PH7 can be built just fine using the default compilation options. * However, if required, the compile-time options documented below can be used to omit * PH7 features (resulting in a smaller compiled library size) or to change the default * values of some parameters. * Every effort has been made to ensure that the various combinations of compilation * options work harmoniously and produce a working library. * * PH7_ENABLE_THREADS * This option controls whether or not code is included in PH7 to enable it to operate * safely in a multithreaded environment. The default is not. That is,all mutexing code * is omitted and it is unsafe to use PH7 in a multithreaded program. When compiled * with the PH7_ENABLE_THREADS directive enabled, PH7 can be used in a multithreaded * program and it's safe to share the same virtual machine and engine instance between * two or more threads. * The value of PH7_ENABLE_THREADS can be determined at run-time using the * ph7_lib_is_threadsafe() interface.When PH7 has been compiled with PH7_ENABLE_THREAD * then the threading mode can be altered at run-time using the ph7_lib_config() * interface together with one of these verbs: * PH7_LIB_CONFIG_THREAD_LEVEL_SINGLE * PH7_LIB_CONFIG_THREAD_LEVEL_MULTI * Also note,platforms others than Windows and UNIX systems must install their own * mutex subsystem via ph7_lib_config() with a configuration verb set to * PH7_LIB_CONFIG_USER_MUTEX. Otherwise the library is not threadsafe. * Note that you must link PH7 with the POSIX threads library under UNIX-like systems * (i.e: -lpthread).Otherwise you will get a link time error. * Options To Omit/Enable Features: * The following options can be used to reduce the size of the compiled library * by omitting optional features. This is probably only useful in embedded systems * where space is especially tight, as even with all features included the PH7 library * is relatively small. Don't forget to tell your compiler to optimize for binary * size! (the -Os option if using GCC). * Telling your compiler to optimize for size usually has a much larger impact * on library footprint than employing any of these compile-time options. * PH7_DISABLE_BUILTIN_FUNC * PH7 come with more than 460 built-in functions suitable for most purposes ranging * from string/XML/INI processing to ZIP extracting, Base64 encoding/decoding and so on. * If this directive is enabled, all the built-in functions are omitted from the build. * Note that language construct functions such as is_int(), is_string(), func_get_arg(), * define(), etc. are not omitted from the build and are not affected by this directive. * PH7_ENABLE_MATH_FUNC * If this directive is enabled, built-in math functions such as sqrt(),abs(), * log(), ceil(), etc. are included in the build. Note that you may need to link * PH7 with the math library in same linux/BSD flavor (i.e: -lm).Otherwise you * will get a link time error. * PH7_DISABLE_DISK_IO * If this directive is enabled, built-in Virtual File System functions such as * chdir(), mkdir(), chroot(), unlink(), delete(), etc. are omitted from the build. * PH7_DISABLE_HASH_IO * If this directive is enabled, built-in hash functions such as md5(), sha1(), * md5_file(), crc32(), etc. are omitted from the build. * PH7_OMIT_FLOATING_POINT * This option is used to omit floating-point number support from the PH7 library * if compiling for a processor that lacks floating point support. When specified * the library will substitute 64-bit integer arithmetic for floating-point which * mean that 25.e-3 and 25 are equals and are of type integer. */ /* Symisc public definitions */ #if !defined(SYMISC_STANDARD_DEFS) #define SYMISC_STANDARD_DEFS #if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE) /* Windows Systems */ #if !defined(__WINNT__) #define __WINNT__ #endif #else /* * By default we will assume that we are compiling on a UNIX systems. * Otherwise the OS_OTHER directive must be defined. */ #if !defined(OS_OTHER) #if !defined(__UNIXES__) #define __UNIXES__ #endif /* __UNIXES__ */ #else #endif /* OS_OTHER */ #endif /* __WINNT__/__UNIXES__ */ #if defined(_MSC_VER) || defined(__BORLANDC__) typedef signed __int64 sxi64; /* 64 bits(8 bytes) signed int64 */ typedef unsigned __int64 sxu64; /* 64 bits(8 bytes) unsigned int64 */ #else typedef signed long long int sxi64; /* 64 bits(8 bytes) signed int64 */ typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */ #endif /* _MSC_VER */ /* Signature of the consumer routine */ typedef int (*ProcConsumer)(const void *,unsigned int,void *); /* Forward reference */ typedef struct SyMutexMethods SyMutexMethods; typedef struct SyMemMethods SyMemMethods; typedef struct SyString SyString; typedef struct syiovec syiovec; typedef struct SyMutex SyMutex; typedef struct Sytm Sytm; /* Scatter and gather array. */ struct syiovec { #if defined (__WINNT__) /* Same fields type and offset as WSABUF structure defined one winsock2 header */ unsigned long nLen; char *pBase; #else void *pBase; unsigned long nLen; #endif }; struct SyString { const char *zString; /* Raw string (may not be null terminated) */ unsigned int nByte; /* Raw string length */ }; /* Time structure. */ struct Sytm { int tm_sec; /* seconds (0 - 60) */ int tm_min; /* minutes (0 - 59) */ int tm_hour; /* hours (0 - 23) */ int tm_mday; /* day of month (1 - 31) */ int tm_mon; /* month of year (0 - 11) */ int tm_year; /* year + 1900 */ int tm_wday; /* day of week (Sunday = 0) */ int tm_yday; /* day of year (0 - 365) */ int tm_isdst; /* is summer time in effect? */ char *tm_zone; /* abbreviation of timezone name */ long tm_gmtoff; /* offset from UTC in seconds */ }; /* Convert a tm structure (struct tm *) found in to a Sytm structure */ #define STRUCT_TM_TO_SYTM(pTM,pSYTM) \ (pSYTM)->tm_hour = (pTM)->tm_hour;\ (pSYTM)->tm_min = (pTM)->tm_min;\ (pSYTM)->tm_sec = (pTM)->tm_sec;\ (pSYTM)->tm_mon = (pTM)->tm_mon;\ (pSYTM)->tm_mday = (pTM)->tm_mday;\ (pSYTM)->tm_year = (pTM)->tm_year + 1900;\ (pSYTM)->tm_yday = (pTM)->tm_yday;\ (pSYTM)->tm_wday = (pTM)->tm_wday;\ (pSYTM)->tm_isdst = (pTM)->tm_isdst;\ (pSYTM)->tm_gmtoff = 0;\ (pSYTM)->tm_zone = 0; /* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */ #define SYSTEMTIME_TO_SYTM(pSYSTIME,pSYTM) \ (pSYTM)->tm_hour = (pSYSTIME)->wHour;\ (pSYTM)->tm_min = (pSYSTIME)->wMinute;\ (pSYTM)->tm_sec = (pSYSTIME)->wSecond;\ (pSYTM)->tm_mon = (pSYSTIME)->wMonth - 1;\ (pSYTM)->tm_mday = (pSYSTIME)->wDay;\ (pSYTM)->tm_year = (pSYSTIME)->wYear;\ (pSYTM)->tm_yday = 0;\ (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\ (pSYTM)->tm_gmtoff = 0;\ (pSYTM)->tm_isdst = -1;\ (pSYTM)->tm_zone = 0; /* Dynamic memory allocation methods. */ struct SyMemMethods { void * (*xAlloc)(unsigned int); /* [Required:] Allocate a memory chunk */ void * (*xRealloc)(void *,unsigned int); /* [Required:] Re-allocate a memory chunk */ void (*xFree)(void *); /* [Required:] Release a memory chunk */ unsigned int (*xChunkSize)(void *); /* [Optional:] Return chunk size */ int (*xInit)(void *); /* [Optional:] Initialization callback */ void (*xRelease)(void *); /* [Optional:] Release callback */ void *pUserData; /* [Optional:] First argument to xInit() and xRelease() */ }; /* Out of memory callback signature. */ typedef int (*ProcMemError)(void *); /* Mutex methods. */ struct SyMutexMethods { int (*xGlobalInit)(void); /* [Optional:] Global mutex initialization */ void (*xGlobalRelease)(void); /* [Optional:] Global Release callback () */ SyMutex * (*xNew)(int); /* [Required:] Request a new mutex */ void (*xRelease)(SyMutex *); /* [Optional:] Release a mutex */ void (*xEnter)(SyMutex *); /* [Required:] Enter mutex */ int (*xTryEnter)(SyMutex *); /* [Optional:] Try to enter a mutex */ void (*xLeave)(SyMutex *); /* [Required:] Leave a locked mutex */ }; #if defined (_MSC_VER) || defined (__MINGW32__) || defined (__GNUC__) && defined (__declspec) #define SX_APIIMPORT __declspec(dllimport) #define SX_APIEXPORT __declspec(dllexport) #else #define SX_APIIMPORT #define SX_APIEXPORT #endif /* Standard return values from Symisc public interfaces */ #define SXRET_OK 0 /* Not an error */ #define SXERR_MEM (-1) /* Out of memory */ #define SXERR_IO (-2) /* IO error */ #define SXERR_EMPTY (-3) /* Empty field */ #define SXERR_LOCKED (-4) /* Locked operation */ #define SXERR_ORANGE (-5) /* Out of range value */ #define SXERR_NOTFOUND (-6) /* Item not found */ #define SXERR_LIMIT (-7) /* Limit reached */ #define SXERR_MORE (-8) /* Need more input */ #define SXERR_INVALID (-9) /* Invalid parameter */ #define SXERR_ABORT (-10) /* User callback request an operation abort */ #define SXERR_EXISTS (-11) /* Item exists */ #define SXERR_SYNTAX (-12) /* Syntax error */ #define SXERR_UNKNOWN (-13) /* Unknown error */ #define SXERR_BUSY (-14) /* Busy operation */ #define SXERR_OVERFLOW (-15) /* Stack or buffer overflow */ #define SXERR_WILLBLOCK (-16) /* Operation will block */ #define SXERR_NOTIMPLEMENTED (-17) /* Operation not implemented */ #define SXERR_EOF (-18) /* End of input */ #define SXERR_PERM (-19) /* Permission error */ #define SXERR_NOOP (-20) /* No-op */ #define SXERR_FORMAT (-21) /* Invalid format */ #define SXERR_NEXT (-22) /* Not an error */ #define SXERR_OS (-23) /* System call return an error */ #define SXERR_CORRUPT (-24) /* Corrupted pointer */ #define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */ #define SXERR_NOMATCH (-26) /* No match */ #define SXERR_RESET (-27) /* Operation reset */ #define SXERR_DONE (-28) /* Not an error */ #define SXERR_SHORT (-29) /* Buffer too short */ #define SXERR_PATH (-30) /* Path error */ #define SXERR_TIMEOUT (-31) /* Timeout */ #define SXERR_BIG (-32) /* Too big for processing */ #define SXERR_RETRY (-33) /* Retry your call */ #define SXERR_IGNORE (-63) /* Ignore */ #endif /* SYMISC_PUBLIC_DEFS */ /* Standard PH7 return values */ #define PH7_OK SXRET_OK /* Successful result */ /* beginning-of-error-codes */ #define PH7_NOMEM SXERR_MEM /* Out of memory */ #define PH7_ABORT SXERR_ABORT /* Foreign Function request operation abort/Another thread have released this instance */ #define PH7_IO_ERR SXERR_IO /* IO error */ #define PH7_CORRUPT SXERR_CORRUPT /* Corrupt pointer/Unknown configuration option */ #define PH7_LOOKED SXERR_LOCKED /* Forbidden Operation */ #define PH7_COMPILE_ERR (-70) /* Compilation error */ #define PH7_VM_ERR (-71) /* Virtual machine error */ /* end-of-error-codes */ /* * If compiling for a processor that lacks floating point * support, substitute integer for floating-point. */ #ifdef PH7_OMIT_FLOATING_POINT typedef sxi64 ph7_real; #else typedef double ph7_real; #endif typedef sxi64 ph7_int64; #define PH7_APIEXPORT SX_APIEXPORT /* * Engine Configuration Commands. * * The following set of constants are the available configuration verbs that can * be used by the host-application to configure the PH7 engine. * These constants must be passed as the second argument to the [ph7_config()] * interface. * Each options require a variable number of arguments. * The [ph7_config()] interface will return PH7_OK on success, any other * return value indicates failure. * For a full discussion on the configuration verbs and their expected * parameters, please refer to this page: * http://ph7.symisc.net/c_api_func.html#ph7_config */ #define PH7_CONFIG_ERR_OUTPUT 1 /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut,unsigned int nLen,void *pUserData),void *pUserData */ #define PH7_CONFIG_ERR_ABORT 2 /* RESERVED FOR FUTURE USE */ #define PH7_CONFIG_ERR_LOG 3 /* TWO ARGUMENTS: const char **pzBuf,int *pLen */ /* * Virtual Machine Configuration Commands. * * The following set of constants are the available configuration verbs that can * be used by the host-application to configure the PH7 Virtual machine. * These constants must be passed as the second argument to the [ph7_vm_config()] * interface. * Each options require a variable number of arguments. * The [ph7_vm_config()] interface will return PH7_OK on success, any other return * value indicates failure. * There are many options but the most importants are: PH7_VM_CONFIG_OUTPUT which install * a VM output consumer callback, PH7_VM_CONFIG_HTTP_REQUEST which parse and register * a HTTP request and PH7_VM_CONFIG_ARGV_ENTRY which populate the $argv array. * For a full discussion on the configuration verbs and their expected parameters, please * refer to this page: * http://ph7.symisc.net/c_api_func.html#ph7_vm_config */ #define PH7_VM_CONFIG_OUTPUT 1 /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut,unsigned int nLen,void *pUserData),void *pUserData */ #define PH7_VM_CONFIG_IMPORT_PATH 3 /* ONE ARGUMENT: const char *zIncludePath */ #define PH7_VM_CONFIG_ERR_REPORT 4 /* NO ARGUMENTS: Report all run-time errors in the VM output */ #define PH7_VM_CONFIG_RECURSION_DEPTH 5 /* ONE ARGUMENT: int nMaxDepth */ #define PH7_VM_OUTPUT_LENGTH 6 /* ONE ARGUMENT: unsigned int *pLength */ #define PH7_VM_CONFIG_CREATE_SUPER 7 /* TWO ARGUMENTS: const char *zName,ph7_value *pValue */ #define PH7_VM_CONFIG_CREATE_VAR 8 /* TWO ARGUMENTS: const char *zName,ph7_value *pValue */ #define PH7_VM_CONFIG_HTTP_REQUEST 9 /* TWO ARGUMENTS: const char *zRawRequest,int nRequestLength */ #define PH7_VM_CONFIG_SERVER_ATTR 10 /* THREE ARGUMENTS: const char *zKey,const char *zValue,int nLen */ #define PH7_VM_CONFIG_ENV_ATTR 11 /* THREE ARGUMENTS: const char *zKey,const char *zValue,int nLen */ #define PH7_VM_CONFIG_SESSION_ATTR 12 /* THREE ARGUMENTS: const char *zKey,const char *zValue,int nLen */ #define PH7_VM_CONFIG_POST_ATTR 13 /* THREE ARGUMENTS: const char *zKey,const char *zValue,int nLen */ #define PH7_VM_CONFIG_GET_ATTR 14 /* THREE ARGUMENTS: const char *zKey,const char *zValue,int nLen */ #define PH7_VM_CONFIG_COOKIE_ATTR 15 /* THREE ARGUMENTS: const char *zKey,const char *zValue,int nLen */ #define PH7_VM_CONFIG_HEADER_ATTR 16 /* THREE ARGUMENTS: const char *zKey,const char *zValue,int nLen */ #define PH7_VM_CONFIG_EXEC_VALUE 17 /* ONE ARGUMENT: ph7_value **ppValue */ #define PH7_VM_CONFIG_IO_STREAM 18 /* ONE ARGUMENT: const ph7_io_stream *pStream */ #define PH7_VM_CONFIG_ARGV_ENTRY 19 /* ONE ARGUMENT: const char *zValue */ #define PH7_VM_CONFIG_EXTRACT_OUTPUT 20 /* TWO ARGUMENTS: const void **ppOut,unsigned int *pOutputLen */ #define PH7_VM_CONFIG_ERR_LOG_HANDLER 21 /* ONE ARGUMENT: void (*xErrLog)(const char *,int,const char *,const char *) */ /* * Global Library Configuration Commands. * * The following set of constants are the available configuration verbs that can * be used by the host-application to configure the whole library. * These constants must be passed as the first argument to the [ph7_lib_config()] * interface. * Each options require a variable number of arguments. * The [ph7_lib_config()] interface will return PH7_OK on success, any other return * value indicates failure. * Notes: * The default configuration is recommended for most applications and so the call to * [ph7_lib_config()] is usually not necessary. It is provided to support rare * applications with unusual needs. * The [ph7_lib_config()] interface is not threadsafe. The application must insure that * no other [ph7_*()] interfaces are invoked by other threads while [ph7_lib_config()] * is running. Furthermore, [ph7_lib_config()] may only be invoked prior to library * initialization using [ph7_lib_init()] or [ph7_init()] or after shutdown * by [ph7_lib_shutdown()]. If [ph7_lib_config()] is called after [ph7_lib_init()] * or [ph7_init()] and before [ph7_lib_shutdown()] then it will return PH7_LOCKED. * Refer to the official documentation for more information on the configuration verbs * and their expected parameters. * For a full discussion on the configuration verbs and their expected parameters,please * refer to this page: * http://ph7.symisc.net/c_api_func.html#Global_Library_Management_Interfaces */ #define PH7_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ #define PH7_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *),void *pUserData */ #define PH7_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ #define PH7_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */ #define PH7_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */ #define PH7_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const ph7_vfs *pVfs */ /* * Compile-time flags. * The new compile interfaces [ph7_compile_v2()] and [ph7_compile_file()] takes * as their last argument zero or more combination of compile time flags. * These flags are used to control the behavior of the PH7 compiler while * processing the input. * Refer to the official documentation for additional information. */ #define PH7_PHP_ONLY 0x01 /* If this flag is set then the code to compile is assumed * to be plain PHP only. That is, there is no need to delimit * the PHP code using the standard tags such as or . * Everything will pass through the PH7 compiler. */ #define PH7_PHP_EXPR 0x02 /* This flag is reserved for future use. */ /* * Call Context Error Message Serverity Level. * * The following constans are the allowed severity level that can * passed as the second argument to the [ph7_context_throw_error()] or * [ph7_context_throw_error_format()] interfaces. * Refer to the official documentation for additional information. */ #define PH7_CTX_ERR 1 /* Call context error such as unexpected number of arguments,invalid types and so on. */ #define PH7_CTX_WARNING 2 /* Call context Warning */ #define PH7_CTX_NOTICE 3 /* Call context Notice */ /* Current VFS structure version*/ #define PH7_VFS_VERSION 2 /* * PH7 Virtual File System (VFS). * * An instance of the ph7_vfs object defines the interface between the PH7 core * and the underlying operating system. The "vfs" in the name of the object stands * for "virtual file system". The vfs is used to implement PHP system functions * such as mkdir(), chdir(), stat(), get_user_name() and many more. * The value of the iVersion field is initially 2 but may be larger in future versions * of PH7. * Additional fields may be appended to this object when the iVersion value is increased. * Only a single vfs can be registered within the PH7 core. Vfs registration is done * using the ph7_lib_config() interface with a configuration verb set to PH7_LIB_CONFIG_VFS. * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users does not have to * worry about registering and installing a vfs since PH7 come with a built-in vfs for these * platforms which implement most the methods defined below. * Host-application running on exotic systems (ie: Other than Windows and UNIX systems) must * register their own vfs in order to be able to use and call PHP system function. * Also note that the ph7_compile_file() interface depend on the xMmap() method of the underlying * vfs which mean that this method must be available (Always the case using the built-in VFS) * in order to use this interface. * Developers wishing to implement the vfs methods can contact symisc systems to obtain * the PH7 VFS C/C++ Specification manual. */ struct ph7_vfs { const char *zName; /* Underlying VFS name [i.e: FreeBSD/Linux/Windows...] */ int iVersion; /* Current VFS structure version [default 2] */ /* Directory functions */ int (*xChdir)(const char *); /* Change directory */ int (*xChroot)(const char *); /* Change the root directory */ int (*xGetcwd)(ph7_context *); /* Get the current working directory */ int (*xMkdir)(const char *,int,int); /* Make directory */ int (*xRmdir)(const char *); /* Remove directory */ int (*xIsdir)(const char *); /* Tells whether the filename is a directory */ int (*xRename)(const char *,const char *); /* Renames a file or directory */ int (*xRealpath)(const char *,ph7_context *); /* Return canonicalized absolute pathname*/ /* Systems functions */ int (*xSleep)(unsigned int); /* Delay execution in microseconds */ int (*xUnlink)(const char *); /* Deletes a file */ int (*xFileExists)(const char *); /* Checks whether a file or directory exists */ int (*xChmod)(const char *,int); /* Changes file mode */ int (*xChown)(const char *,const char *); /* Changes file owner */ int (*xChgrp)(const char *,const char *); /* Changes file group */ ph7_int64 (*xFreeSpace)(const char *); /* Available space on filesystem or disk partition */ ph7_int64 (*xTotalSpace)(const char *); /* Total space on filesystem or disk partition */ ph7_int64 (*xFileSize)(const char *); /* Gets file size */ ph7_int64 (*xFileAtime)(const char *); /* Gets last access time of file */ ph7_int64 (*xFileMtime)(const char *); /* Gets file modification time */ ph7_int64 (*xFileCtime)(const char *); /* Gets inode change time of file */ int (*xStat)(const char *,ph7_value *,ph7_value *); /* Gives information about a file */ int (*xlStat)(const char *,ph7_value *,ph7_value *); /* Gives information about a file */ int (*xIsfile)(const char *); /* Tells whether the filename is a regular file */ int (*xIslink)(const char *); /* Tells whether the filename is a symbolic link */ int (*xReadable)(const char *); /* Tells whether a file exists and is readable */ int (*xWritable)(const char *); /* Tells whether the filename is writable */ int (*xExecutable)(const char *); /* Tells whether the filename is executable */ int (*xFiletype)(const char *,ph7_context *); /* Gets file type [i.e: fifo,dir,file..] */ int (*xGetenv)(const char *,ph7_context *); /* Gets the value of an environment variable */ int (*xSetenv)(const char *,const char *); /* Sets the value of an environment variable */ int (*xTouch)(const char *,ph7_int64,ph7_int64); /* Sets access and modification time of file */ int (*xMmap)(const char *,void **,ph7_int64 *); /* Read-only memory map of the whole file */ void (*xUnmap)(void *,ph7_int64); /* Unmap a memory view */ int (*xLink)(const char *,const char *,int); /* Create hard or symbolic link */ int (*xUmask)(int); /* Change the current umask */ void (*xTempDir)(ph7_context *); /* Get path of the temporary directory */ unsigned int (*xProcessId)(void); /* Get running process ID */ int (*xUid)(void); /* user ID of the process */ int (*xGid)(void); /* group ID of the process */ void (*xUsername)(ph7_context *); /* Running username */ int (*xExec)(const char *,ph7_context *); /* Execute an external program */ }; /* Current PH7 IO stream structure version. */ #define PH7_IO_STREAM_VERSION 1 /* * Possible open mode flags that can be passed to the xOpen() routine * of the underlying IO stream device . * Refer to the PH7 IO Stream C/C++ specification manual (http://ph7.symisc.net/io_stream_spec.html) * for additional information. */ #define PH7_IO_OPEN_RDONLY 0x001 /* Read-only open */ #define PH7_IO_OPEN_WRONLY 0x002 /* Write-only open */ #define PH7_IO_OPEN_RDWR 0x004 /* Read-write open. */ #define PH7_IO_OPEN_CREATE 0x008 /* If the file does not exist it will be created */ #define PH7_IO_OPEN_TRUNC 0x010 /* Truncate the file to zero length */ #define PH7_IO_OPEN_APPEND 0x020 /* Append mode.The file offset is positioned at the end of the file */ #define PH7_IO_OPEN_EXCL 0x040 /* Ensure that this call creates the file,the file must not exist before */ #define PH7_IO_OPEN_BINARY 0x080 /* Simple hint: Data is binary */ #define PH7_IO_OPEN_TEMP 0x100 /* Simple hint: Temporary file */ #define PH7_IO_OPEN_TEXT 0x200 /* Simple hint: Data is textual */ /* * PH7 IO Stream Device. * * An instance of the ph7_io_stream object defines the interface between the PH7 core * and the underlying stream device. * A stream is a smart mechanism for generalizing file, network, data compression * and other IO operations which share a common set of functions using an abstracted * unified interface. * A stream device is additional code which tells the stream how to handle specific * protocols/encodings. For example, the http device knows how to translate a URL * into an HTTP/1.1 request for a file on a remote server. * PH7 come with two built-in IO streams device: * The file:// stream which perform very efficient disk IO and the php:// stream * which is a special stream that allow access various I/O streams (See the PHP official * documentation for more information on this stream). * A stream is referenced as: scheme://target * scheme(string) - The name of the wrapper to be used. Examples include: file,http,https,ftp, * ftps, compress.zlib, compress.bz2, and php. If no wrapper is specified,the function default * is used (typically file://). * target - Depends on the device used. For filesystem related streams this is typically a path * and filename of the desired file.For network related streams this is typically a hostname,often * with a path appended. * IO stream devices are registered using a call to ph7_vm_config() with a configuration verb * set to PH7_VM_CONFIG_IO_STREAM. * Currently the PH7 development team is working on the implementation of the http:// and ftp:// * IO stream protocols. These devices will be available in the next major release of the PH7 engine. * Developers wishing to implement their own IO stream devices must understand and follow * The PH7 IO Stream C/C++ specification manual (http://ph7.symisc.net/io_stream_spec.html). */ struct ph7_io_stream { const char *zName; /* Underlying stream name [i.e: file/http/zip/php,..] */ int iVersion; /* IO stream structure version [default 1]*/ int (*xOpen)(const char *,int,ph7_value *,void **); /* Open handle*/ int (*xOpenDir)(const char *,ph7_value *,void **); /* Open directory handle */ void (*xClose)(void *); /* Close file handle */ void (*xCloseDir)(void *); /* Close directory handle */ ph7_int64 (*xRead)(void *,void *,ph7_int64); /* Read from the open stream */ int (*xReadDir)(void *,ph7_context *); /* Read entry from directory handle */ ph7_int64 (*xWrite)(void *,const void *,ph7_int64); /* Write to the open stream */ int (*xSeek)(void *,ph7_int64,int); /* Seek on the open stream */ int (*xLock)(void *,int); /* Lock/Unlock the open stream */ void (*xRewindDir)(void *); /* Rewind directory handle */ ph7_int64 (*xTell)(void *); /* Current position of the stream read/write pointer */ int (*xTrunc)(void *,ph7_int64); /* Truncates the open stream to a given length */ int (*xSync)(void *); /* Flush open stream data */ int (*xStat)(void *,ph7_value *,ph7_value *); /* Stat an open stream handle */ }; /* * C-API-REF: Please refer to the official documentation for interfaces * purpose and expected parameters. */ /* Engine Handling Interfaces */ PH7_APIEXPORT int ph7_init(ph7 **ppEngine); PH7_APIEXPORT int ph7_config(ph7 *pEngine,int nConfigOp,...); PH7_APIEXPORT int ph7_release(ph7 *pEngine); /* Compile Interfaces */ PH7_APIEXPORT int ph7_compile(ph7 *pEngine,const char *zSource,int nLen,ph7_vm **ppOutVm); PH7_APIEXPORT int ph7_compile_v2(ph7 *pEngine,const char *zSource,int nLen,ph7_vm **ppOutVm,int iFlags); PH7_APIEXPORT int ph7_compile_file(ph7 *pEngine,const char *zFilePath,ph7_vm **ppOutVm,int iFlags); /* Virtual Machine Handling Interfaces */ PH7_APIEXPORT int ph7_vm_config(ph7_vm *pVm,int iConfigOp,...); PH7_APIEXPORT int ph7_vm_exec(ph7_vm *pVm,int *pExitStatus); PH7_APIEXPORT int ph7_vm_reset(ph7_vm *pVm); PH7_APIEXPORT int ph7_vm_release(ph7_vm *pVm); PH7_APIEXPORT int ph7_vm_dump_v2(ph7_vm *pVm,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData); /* In-process Extending Interfaces */ PH7_APIEXPORT int ph7_create_function(ph7_vm *pVm,const char *zName,int (*xFunc)(ph7_context *,int,ph7_value **),void *pUserData); PH7_APIEXPORT int ph7_delete_function(ph7_vm *pVm,const char *zName); PH7_APIEXPORT int ph7_create_constant(ph7_vm *pVm,const char *zName,void (*xExpand)(ph7_value *,void *),void *pUserData); PH7_APIEXPORT int ph7_delete_constant(ph7_vm *pVm,const char *zName); /* Foreign Function Parameter Values */ PH7_APIEXPORT int ph7_value_to_int(ph7_value *pValue); PH7_APIEXPORT int ph7_value_to_bool(ph7_value *pValue); PH7_APIEXPORT ph7_int64 ph7_value_to_int64(ph7_value *pValue); PH7_APIEXPORT double ph7_value_to_double(ph7_value *pValue); PH7_APIEXPORT const char * ph7_value_to_string(ph7_value *pValue,int *pLen); PH7_APIEXPORT void * ph7_value_to_resource(ph7_value *pValue); PH7_APIEXPORT int ph7_value_compare(ph7_value *pLeft,ph7_value *pRight,int bStrict); /* Setting The Result Of A Foreign Function */ PH7_APIEXPORT int ph7_result_int(ph7_context *pCtx,int iValue); PH7_APIEXPORT int ph7_result_int64(ph7_context *pCtx,ph7_int64 iValue); PH7_APIEXPORT int ph7_result_bool(ph7_context *pCtx,int iBool); PH7_APIEXPORT int ph7_result_double(ph7_context *pCtx,double Value); PH7_APIEXPORT int ph7_result_null(ph7_context *pCtx); PH7_APIEXPORT int ph7_result_string(ph7_context *pCtx,const char *zString,int nLen); PH7_APIEXPORT int ph7_result_string_format(ph7_context *pCtx,const char *zFormat,...); PH7_APIEXPORT int ph7_result_value(ph7_context *pCtx,ph7_value *pValue); PH7_APIEXPORT int ph7_result_resource(ph7_context *pCtx,void *pUserData); /* Call Context Handling Interfaces */ PH7_APIEXPORT int ph7_context_output(ph7_context *pCtx,const char *zString,int nLen); PH7_APIEXPORT int ph7_context_output_format(ph7_context *pCtx,const char *zFormat,...); PH7_APIEXPORT int ph7_context_throw_error(ph7_context *pCtx,int iErr,const char *zErr); PH7_APIEXPORT int ph7_context_throw_error_format(ph7_context *pCtx,int iErr,const char *zFormat,...); PH7_APIEXPORT unsigned int ph7_context_random_num(ph7_context *pCtx); PH7_APIEXPORT int ph7_context_random_string(ph7_context *pCtx,char *zBuf,int nBuflen); PH7_APIEXPORT void * ph7_context_user_data(ph7_context *pCtx); PH7_APIEXPORT int ph7_context_push_aux_data(ph7_context *pCtx,void *pUserData); PH7_APIEXPORT void * ph7_context_peek_aux_data(ph7_context *pCtx); PH7_APIEXPORT void * ph7_context_pop_aux_data(ph7_context *pCtx); PH7_APIEXPORT unsigned int ph7_context_result_buf_length(ph7_context *pCtx); PH7_APIEXPORT const char * ph7_function_name(ph7_context *pCtx); /* Call Context Memory Management Interfaces */ PH7_APIEXPORT void * ph7_context_alloc_chunk(ph7_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease); PH7_APIEXPORT void * ph7_context_realloc_chunk(ph7_context *pCtx,void *pChunk,unsigned int nByte); PH7_APIEXPORT void ph7_context_free_chunk(ph7_context *pCtx,void *pChunk); /* On Demand Dynamically Typed Value Object allocation interfaces */ PH7_APIEXPORT ph7_value * ph7_new_scalar(ph7_vm *pVm); PH7_APIEXPORT ph7_value * ph7_new_array(ph7_vm *pVm); PH7_APIEXPORT int ph7_release_value(ph7_vm *pVm,ph7_value *pValue); PH7_APIEXPORT ph7_value * ph7_context_new_scalar(ph7_context *pCtx); PH7_APIEXPORT ph7_value * ph7_context_new_array(ph7_context *pCtx); PH7_APIEXPORT void ph7_context_release_value(ph7_context *pCtx,ph7_value *pValue); /* Dynamically Typed Value Object Management Interfaces */ PH7_APIEXPORT int ph7_value_int(ph7_value *pVal,int iValue); PH7_APIEXPORT int ph7_value_int64(ph7_value *pVal,ph7_int64 iValue); PH7_APIEXPORT int ph7_value_bool(ph7_value *pVal,int iBool); PH7_APIEXPORT int ph7_value_null(ph7_value *pVal); PH7_APIEXPORT int ph7_value_double(ph7_value *pVal,double Value); PH7_APIEXPORT int ph7_value_string(ph7_value *pVal,const char *zString,int nLen); PH7_APIEXPORT int ph7_value_string_format(ph7_value *pVal,const char *zFormat,...); PH7_APIEXPORT int ph7_value_reset_string_cursor(ph7_value *pVal); PH7_APIEXPORT int ph7_value_resource(ph7_value *pVal,void *pUserData); PH7_APIEXPORT int ph7_value_release(ph7_value *pVal); PH7_APIEXPORT ph7_value * ph7_array_fetch(ph7_value *pArray,const char *zKey,int nByte); PH7_APIEXPORT int ph7_array_walk(ph7_value *pArray,int (*xWalk)(ph7_value *,ph7_value *,void *),void *pUserData); PH7_APIEXPORT int ph7_array_add_elem(ph7_value *pArray,ph7_value *pKey,ph7_value *pValue); PH7_APIEXPORT int ph7_array_add_strkey_elem(ph7_value *pArray,const char *zKey,ph7_value *pValue); PH7_APIEXPORT int ph7_array_add_intkey_elem(ph7_value *pArray,int iKey,ph7_value *pValue); PH7_APIEXPORT unsigned int ph7_array_count(ph7_value *pArray); PH7_APIEXPORT int ph7_object_walk(ph7_value *pObject,int (*xWalk)(const char *,ph7_value *,void *),void *pUserData); PH7_APIEXPORT ph7_value * ph7_object_fetch_attr(ph7_value *pObject,const char *zAttr); PH7_APIEXPORT const char * ph7_object_get_class_name(ph7_value *pObject,int *pLength); PH7_APIEXPORT int ph7_value_is_int(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_float(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_bool(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_string(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_null(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_numeric(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_callable(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_scalar(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_array(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_object(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_resource(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_empty(ph7_value *pVal); /* Global Library Management Interfaces */ PH7_APIEXPORT int ph7_lib_init(void); PH7_APIEXPORT int ph7_lib_config(int nConfigOp,...); PH7_APIEXPORT int ph7_lib_shutdown(void); PH7_APIEXPORT int ph7_lib_is_threadsafe(void); PH7_APIEXPORT const char * ph7_lib_version(void); PH7_APIEXPORT const char * ph7_lib_signature(void); PH7_APIEXPORT const char * ph7_lib_ident(void); PH7_APIEXPORT const char * ph7_lib_copyright(void); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _PH7_H_ */ /* * ---------------------------------------------------------- * File: ph7int.h * MD5: cdd8bb8c737e7e3ae5b14e01a01b98dd * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: ph7int.h v1.9 FreeBSD 2012-08-13 26:25 devel $ */ #ifndef __PH7INT_H__ #define __PH7INT_H__ /* Internal interface definitions for PH7. */ #ifdef PH7_AMALGAMATION /* Marker for routines not intended for external use */ #define PH7_PRIVATE static #else #define PH7_PRIVATE #include "ph7.h" #endif #ifndef PH7_PI /* Value of PI */ #define PH7_PI 3.1415926535898 #endif /* * Constants for the largest and smallest possible 64-bit signed integers. * These macros are designed to work correctly on both 32-bit and 64-bit * compilers. */ #ifndef LARGEST_INT64 #define LARGEST_INT64 (0xffffffff|(((sxi64)0x7fffffff)<<32)) #endif #ifndef SMALLEST_INT64 #define SMALLEST_INT64 (((sxi64)-1) - LARGEST_INT64) #endif /* Forward declaration of private structures */ typedef struct ph7_class_instance ph7_class_instance; typedef struct ph7_foreach_info ph7_foreach_info; typedef struct ph7_foreach_step ph7_foreach_step; typedef struct ph7_hashmap_node ph7_hashmap_node; typedef struct ph7_hashmap ph7_hashmap; typedef struct ph7_class ph7_class; /* Symisc Standard types */ #if !defined(SYMISC_STD_TYPES) #define SYMISC_STD_TYPES #ifdef __WINNT__ /* Disable nuisance warnings on Borland compilers */ #if defined(__BORLANDC__) #pragma warn -rch /* unreachable code */ #pragma warn -ccc /* Condition is always true or false */ #pragma warn -aus /* Assigned value is never used */ #pragma warn -csu /* Comparing signed and unsigned */ #pragma warn -spa /* Suspicious pointer arithmetic */ #endif #endif typedef signed char sxi8; /* signed char */ typedef unsigned char sxu8; /* unsigned char */ typedef signed short int sxi16; /* 16 bits(2 bytes) signed integer */ typedef unsigned short int sxu16; /* 16 bits(2 bytes) unsigned integer */ typedef int sxi32; /* 32 bits(4 bytes) integer */ typedef unsigned int sxu32; /* 32 bits(4 bytes) unsigned integer */ typedef long sxptr; typedef unsigned long sxuptr; typedef long sxlong; typedef unsigned long sxulong; typedef sxi32 sxofft; typedef sxi64 sxofft64; typedef long double sxlongreal; typedef double sxreal; #define SXI8_HIGH 0x7F #define SXU8_HIGH 0xFF #define SXI16_HIGH 0x7FFF #define SXU16_HIGH 0xFFFF #define SXI32_HIGH 0x7FFFFFFF #define SXU32_HIGH 0xFFFFFFFF #define SXI64_HIGH 0x7FFFFFFFFFFFFFFF #define SXU64_HIGH 0xFFFFFFFFFFFFFFFF #if !defined(TRUE) #define TRUE 1 #endif #if !defined(FALSE) #define FALSE 0 #endif /* * The following macros are used to cast pointers to integers and * integers to pointers. */ #if defined(__PTRDIFF_TYPE__) # define SX_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define SX_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) #elif !defined(__GNUC__) # define SX_INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define SX_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #else # define SX_INT_TO_PTR(X) ((void*)(X)) # define SX_PTR_TO_INT(X) ((int)(X)) #endif #define SXMIN(a,b) ((a < b) ? (a) : (b)) #define SXMAX(a,b) ((a < b) ? (b) : (a)) #endif /* SYMISC_STD_TYPES */ /* Symisc Run-time API private definitions */ #if !defined(SYMISC_PRIVATE_DEFS) #define SYMISC_PRIVATE_DEFS typedef sxi32 (*ProcRawStrCmp)(const SyString *,const SyString *); #define SyStringData(RAW) ((RAW)->zString) #define SyStringLength(RAW) ((RAW)->nByte) #define SyStringInitFromBuf(RAW,ZBUF,NLEN){\ (RAW)->zString = (const char *)ZBUF;\ (RAW)->nByte = (sxu32)(NLEN);\ } #define SyStringUpdatePtr(RAW,NBYTES){\ if( NBYTES > (RAW)->nByte ){\ (RAW)->nByte = 0;\ }else{\ (RAW)->zString += NBYTES;\ (RAW)->nByte -= NBYTES;\ }\ } #define SyStringDupPtr(RAW1,RAW2)\ (RAW1)->zString = (RAW2)->zString;\ (RAW1)->nByte = (RAW2)->nByte; #define SyStringTrimLeadingChar(RAW,CHAR)\ while((RAW)->nByte > 0 && (RAW)->zString[0] == CHAR ){\ (RAW)->zString++;\ (RAW)->nByte--;\ } #define SyStringTrimTrailingChar(RAW,CHAR)\ while((RAW)->nByte > 0 && (RAW)->zString[(RAW)->nByte - 1] == CHAR){\ (RAW)->nByte--;\ } #define SyStringCmp(RAW1,RAW2,xCMP)\ (((RAW1)->nByte == (RAW2)->nByte) ? xCMP((RAW1)->zString,(RAW2)->zString,(RAW2)->nByte) : (sxi32)((RAW1)->nByte - (RAW2)->nByte)) #define SyStringCmp2(RAW1,RAW2,xCMP)\ (((RAW1)->nByte >= (RAW2)->nByte) ? xCMP((RAW1)->zString,(RAW2)->zString,(RAW2)->nByte) : (sxi32)((RAW2)->nByte - (RAW1)->nByte)) #define SyStringCharCmp(RAW,CHAR) \ (((RAW)->nByte == sizeof(char)) ? ((RAW)->zString[0] == CHAR ? 0 : CHAR - (RAW)->zString[0]) : ((RAW)->zString[0] == CHAR ? 0 : (RAW)->nByte - sizeof(char))) #define SX_ADDR(PTR) ((sxptr)PTR) #define SX_ARRAYSIZE(X) (sizeof(X)/sizeof(X[0])) #define SXUNUSED(P) (P = 0) #define SX_EMPTY(PTR) (PTR == 0) #define SX_EMPTY_STR(STR) (STR == 0 || STR[0] == 0 ) typedef struct SyMemBackend SyMemBackend; typedef struct SyBlob SyBlob; typedef struct SySet SySet; /* Standard function signatures */ typedef sxi32 (*ProcCmp)(const void *,const void *,sxu32); typedef sxi32 (*ProcPatternMatch)(const char *,sxu32,const char *,sxu32,sxu32 *); typedef sxi32 (*ProcSearch)(const void *,sxu32,const void *,sxu32,ProcCmp,sxu32 *); typedef sxu32 (*ProcHash)(const void *,sxu32); typedef sxi32 (*ProcHashSum)(const void *,sxu32,unsigned char *,sxu32); typedef sxi32 (*ProcSort)(void *,sxu32,sxu32,ProcCmp); #define MACRO_LIST_PUSH(Head,Item)\ Item->pNext = Head;\ Head = Item; #define MACRO_LD_PUSH(Head,Item)\ if( Head == 0 ){\ Head = Item;\ }else{\ Item->pNext = Head;\ Head->pPrev = Item;\ Head = Item;\ } #define MACRO_LD_REMOVE(Head,Item)\ if( Head == Item ){\ Head = Head->pNext;\ }\ if( Item->pPrev ){ Item->pPrev->pNext = Item->pNext;}\ if( Item->pNext ){ Item->pNext->pPrev = Item->pPrev;} /* * A generic dynamic set. */ struct SySet { SyMemBackend *pAllocator; /* Memory backend */ void *pBase; /* Base pointer */ sxu32 nUsed; /* Total number of used slots */ sxu32 nSize; /* Total number of available slots */ sxu32 eSize; /* Size of a single slot */ sxu32 nCursor; /* Loop cursor */ void *pUserData; /* User private data associated with this container */ }; #define SySetBasePtr(S) ((S)->pBase) #define SySetBasePtrJump(S,OFFT) (&((char *)(S)->pBase)[OFFT*(S)->eSize]) #define SySetUsed(S) ((S)->nUsed) #define SySetSize(S) ((S)->nSize) #define SySetElemSize(S) ((S)->eSize) #define SySetCursor(S) ((S)->nCursor) #define SySetGetAllocator(S) ((S)->pAllocator) #define SySetSetUserData(S,DATA) ((S)->pUserData = DATA) #define SySetGetUserData(S) ((S)->pUserData) /* * A variable length containers for generic data. */ struct SyBlob { SyMemBackend *pAllocator; /* Memory backend */ void *pBlob; /* Base pointer */ sxu32 nByte; /* Total number of used bytes */ sxu32 mByte; /* Total number of available bytes */ sxu32 nFlags; /* Blob internal flags,see below */ }; #define SXBLOB_LOCKED 0x01 /* Blob is locked [i.e: Cannot auto grow] */ #define SXBLOB_STATIC 0x02 /* Not allocated from heap */ #define SXBLOB_RDONLY 0x04 /* Read-Only data */ #define SyBlobFreeSpace(BLOB) ((BLOB)->mByte - (BLOB)->nByte) #define SyBlobLength(BLOB) ((BLOB)->nByte) #define SyBlobData(BLOB) ((BLOB)->pBlob) #define SyBlobCurData(BLOB) ((void*)(&((char*)(BLOB)->pBlob)[(BLOB)->nByte])) #define SyBlobDataAt(BLOB,OFFT) ((void *)(&((char *)(BLOB)->pBlob)[OFFT])) #define SyBlobGetAllocator(BLOB) ((BLOB)->pAllocator) #define SXMEM_POOL_INCR 3 #define SXMEM_POOL_NBUCKETS 12 #define SXMEM_BACKEND_MAGIC 0xBAC3E67D #define SXMEM_BACKEND_CORRUPT(BACKEND) (BACKEND == 0 || BACKEND->nMagic != SXMEM_BACKEND_MAGIC) #define SXMEM_BACKEND_RETRY 3 /* A memory backend subsystem is defined by an instance of the following structures */ typedef union SyMemHeader SyMemHeader; typedef struct SyMemBlock SyMemBlock; struct SyMemBlock { SyMemBlock *pNext,*pPrev; /* Chain of allocated memory blocks */ #ifdef UNTRUST sxu32 nGuard; /* magic number associated with each valid block,so we * can detect misuse. */ #endif }; /* * Header associated with each valid memory pool block. */ union SyMemHeader { SyMemHeader *pNext; /* Next chunk of size 1 << (nBucket + SXMEM_POOL_INCR) in the list */ sxu32 nBucket; /* Bucket index in aPool[] */ }; struct SyMemBackend { const SyMutexMethods *pMutexMethods; /* Mutex methods */ const SyMemMethods *pMethods; /* Memory allocation methods */ SyMemBlock *pBlocks; /* List of valid memory blocks */ sxu32 nBlock; /* Total number of memory blocks allocated so far */ ProcMemError xMemError; /* Out-of memory callback */ void *pUserData; /* First arg to xMemError() */ SyMutex *pMutex; /* Per instance mutex */ sxu32 nMagic; /* Sanity check against misuse */ SyMemHeader *apPool[SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR]; /* Pool of memory chunks */ }; /* Mutex types */ #define SXMUTEX_TYPE_FAST 1 #define SXMUTEX_TYPE_RECURSIVE 2 #define SXMUTEX_TYPE_STATIC_1 3 #define SXMUTEX_TYPE_STATIC_2 4 #define SXMUTEX_TYPE_STATIC_3 5 #define SXMUTEX_TYPE_STATIC_4 6 #define SXMUTEX_TYPE_STATIC_5 7 #define SXMUTEX_TYPE_STATIC_6 8 #define SyMutexGlobalInit(METHOD){\ if( (METHOD)->xGlobalInit ){\ (METHOD)->xGlobalInit();\ }\ } #define SyMutexGlobalRelease(METHOD){\ if( (METHOD)->xGlobalRelease ){\ (METHOD)->xGlobalRelease();\ }\ } #define SyMutexNew(METHOD,TYPE) (METHOD)->xNew(TYPE) #define SyMutexRelease(METHOD,MUTEX){\ if( MUTEX && (METHOD)->xRelease ){\ (METHOD)->xRelease(MUTEX);\ }\ } #define SyMutexEnter(METHOD,MUTEX){\ if( MUTEX ){\ (METHOD)->xEnter(MUTEX);\ }\ } #define SyMutexTryEnter(METHOD,MUTEX){\ if( MUTEX && (METHOD)->xTryEnter ){\ (METHOD)->xTryEnter(MUTEX);\ }\ } #define SyMutexLeave(METHOD,MUTEX){\ if( MUTEX ){\ (METHOD)->xLeave(MUTEX);\ }\ } /* Comparison,byte swap,byte copy macros */ #define SX_MACRO_FAST_CMP(X1,X2,SIZE,RC){\ register unsigned char *r1 = (unsigned char *)X1;\ register unsigned char *r2 = (unsigned char *)X2;\ register sxu32 LEN = SIZE;\ for(;;){\ if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\ if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\ if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\ if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\ }\ RC = !LEN ? 0 : r1[0] - r2[0];\ } #define SX_MACRO_FAST_MEMCPY(SRC,DST,SIZ){\ register unsigned char *xSrc = (unsigned char *)SRC;\ register unsigned char *xDst = (unsigned char *)DST;\ register sxu32 xLen = SIZ;\ for(;;){\ if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\ if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\ if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\ if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\ }\ } #define SX_MACRO_BYTE_SWAP(X,Y,Z){\ register unsigned char *s = (unsigned char *)X;\ register unsigned char *d = (unsigned char *)Y;\ sxu32 ZLong = Z; \ sxi32 c; \ for(;;){\ if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\ if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\ if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\ if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\ }\ } #define SX_MSEC_PER_SEC (1000) /* Millisec per seconds */ #define SX_USEC_PER_SEC (1000000) /* Microsec per seconds */ #define SX_NSEC_PER_SEC (1000000000) /* Nanosec per seconds */ #endif /* SYMISC_PRIVATE_DEFS */ /* Symisc Run-time API auxiliary definitions */ #if !defined(SYMISC_PRIVATE_AUX_DEFS) #define SYMISC_PRIVATE_AUX_DEFS typedef struct SyHashEntry_Pr SyHashEntry_Pr; typedef struct SyHashEntry SyHashEntry; typedef struct SyHash SyHash; /* * Each public hashtable entry is represented by an instance * of the following structure. */ struct SyHashEntry { const void *pKey; /* Hash key */ sxu32 nKeyLen; /* Key length */ void *pUserData; /* User private data */ }; #define SyHashEntryGetUserData(ENTRY) ((ENTRY)->pUserData) #define SyHashEntryGetKey(ENTRY) ((ENTRY)->pKey) /* Each active hashtable is identified by an instance of the following structure */ struct SyHash { SyMemBackend *pAllocator; /* Memory backend */ ProcHash xHash; /* Hash function */ ProcCmp xCmp; /* Comparison function */ SyHashEntry_Pr *pList,*pCurrent; /* Linked list of hash entries user for linear traversal */ sxu32 nEntry; /* Total number of entries */ SyHashEntry_Pr **apBucket; /* Hash buckets */ sxu32 nBucketSize; /* Current bucket size */ }; #define SXHASH_BUCKET_SIZE 16 /* Initial bucket size: must be a power of two */ #define SXHASH_FILL_FACTOR 3 /* Hash access macro */ #define SyHashFunc(HASH) ((HASH)->xHash) #define SyHashCmpFunc(HASH) ((HASH)->xCmp) #define SyHashTotalEntry(HASH) ((HASH)->nEntry) #define SyHashGetPool(HASH) ((HASH)->pAllocator) /* * An instance of the following structure define a single context * for an Pseudo Random Number Generator. * * Nothing in this file or anywhere else in the library does any kind of * encryption. The RC4 algorithm is being used as a PRNG (pseudo-random * number generator) not as an encryption device. * This implementation is taken from the SQLite3 source tree. */ typedef struct SyPRNGCtx SyPRNGCtx; struct SyPRNGCtx { sxu8 i,j; /* State variables */ unsigned char s[256]; /* State variables */ sxu16 nMagic; /* Sanity check */ }; typedef sxi32 (*ProcRandomSeed)(void *,unsigned int,void *); /* High resolution timer.*/ typedef struct sytime sytime; struct sytime { long tm_sec; /* seconds */ long tm_usec; /* microseconds */ }; /* Forward declaration */ typedef struct SyStream SyStream; typedef struct SyToken SyToken; typedef struct SyLex SyLex; /* * Tokenizer callback signature. */ typedef sxi32 (*ProcTokenizer)(SyStream *,SyToken *,void *,void *); /* * Each token in the input is represented by an instance * of the following structure. */ struct SyToken { SyString sData; /* Token text and length */ sxu32 nType; /* Token type */ sxu32 nLine; /* Token line number */ void *pUserData; /* User private data associated with this token */ }; /* * During tokenization, information about the state of the input * stream is held in an instance of the following structure. */ struct SyStream { const unsigned char *zInput; /* Complete text of the input */ const unsigned char *zText; /* Current input we are processing */ const unsigned char *zEnd; /* End of input marker */ sxu32 nLine; /* Total number of processed lines */ sxu32 nIgn; /* Total number of ignored tokens */ SySet *pSet; /* Token containers */ }; /* * Each lexer is represented by an instance of the following structure. */ struct SyLex { SyStream sStream; /* Input stream */ ProcTokenizer xTokenizer; /* Tokenizer callback */ void * pUserData; /* Third argument to xTokenizer() */ SySet *pTokenSet; /* Token set */ }; #define SyLexTotalToken(LEX) SySetTotalEntry(&(LEX)->aTokenSet) #define SyLexTotalLines(LEX) ((LEX)->sStream.nLine) #define SyLexTotalIgnored(LEX) ((LEX)->sStream.nIgn) #define XLEX_IN_LEN(STREAM) (sxu32)(STREAM->zEnd - STREAM->zText) #endif /* SYMISC_PRIVATE_AUX_DEFS */ /* ** Notes on UTF-8 (According to SQLite3 authors): ** ** Byte-0 Byte-1 Byte-2 Byte-3 Value ** 0xxxxxxx 00000000 00000000 0xxxxxxx ** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx ** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx ** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx ** */ /* ** Assuming zIn points to the first byte of a UTF-8 character, ** advance zIn to point to the first byte of the next UTF-8 character. */ #define SX_JMP_UTF8(zIn,zEnd)\ while(zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){ zIn++; } #define SX_WRITE_UTF8(zOut, c) { \ if( c<0x00080 ){ \ *zOut++ = (sxu8)(c&0xFF); \ }else if( c<0x00800 ){ \ *zOut++ = 0xC0 + (sxu8)((c>>6)&0x1F); \ *zOut++ = 0x80 + (sxu8)(c & 0x3F); \ }else if( c<0x10000 ){ \ *zOut++ = 0xE0 + (sxu8)((c>>12)&0x0F); \ *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F); \ *zOut++ = 0x80 + (sxu8)(c & 0x3F); \ }else{ \ *zOut++ = 0xF0 + (sxu8)((c>>18) & 0x07); \ *zOut++ = 0x80 + (sxu8)((c>>12) & 0x3F); \ *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F); \ *zOut++ = 0x80 + (sxu8)(c & 0x3F); \ } \ } /* Rely on the standard ctype */ #include #define SyToUpper(c) toupper(c) #define SyToLower(c) tolower(c) #define SyisUpper(c) isupper(c) #define SyisLower(c) islower(c) #define SyisSpace(c) isspace(c) #define SyisBlank(c) isspace(c) #define SyisAlpha(c) isalpha(c) #define SyisDigit(c) isdigit(c) #define SyisHex(c) isxdigit(c) #define SyisPrint(c) isprint(c) #define SyisPunct(c) ispunct(c) #define SyisSpec(c) iscntrl(c) #define SyisCtrl(c) iscntrl(c) #define SyisAscii(c) isascii(c) #define SyisAlphaNum(c) isalnum(c) #define SyisGraph(c) isgraph(c) #define SyDigToHex(c) "0123456789ABCDEF"[c & 0x0F] #define SyDigToInt(c) ((c < 0xc0 && SyisDigit(c))? (c - '0') : 0 ) #define SyCharToUpper(c) ((c < 0xc0 && SyisLower(c))? SyToUpper(c) : c) #define SyCharToLower(c) ((c < 0xc0 && SyisUpper(c))? SyToLower(c) : c) /* Remove white space/NUL byte from a raw string */ #define SyStringLeftTrim(RAW)\ while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\ (RAW)->nByte--;\ (RAW)->zString++;\ } #define SyStringLeftTrimSafe(RAW)\ while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && ((RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\ (RAW)->nByte--;\ (RAW)->zString++;\ } #define SyStringRightTrim(RAW)\ while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\ (RAW)->nByte--;\ } #define SyStringRightTrimSafe(RAW)\ while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && \ (( RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\ (RAW)->nByte--;\ } #define SyStringFullTrim(RAW)\ while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\ (RAW)->nByte--;\ (RAW)->zString++;\ }\ while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\ (RAW)->nByte--;\ } #define SyStringFullTrimSafe(RAW)\ while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && \ ( (RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\ (RAW)->nByte--;\ (RAW)->zString++;\ }\ while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && \ ( (RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\ (RAW)->nByte--;\ } #ifndef PH7_DISABLE_BUILTIN_FUNC /* * An XML raw text,CDATA,tag name and son is parsed out and stored * in an instance of the following structure. */ typedef struct SyXMLRawStr SyXMLRawStr; struct SyXMLRawStr { const char *zString; /* Raw text [UTF-8 ENCODED EXCEPT CDATA] [NOT NULL TERMINATED] */ sxu32 nByte; /* Text length */ sxu32 nLine; /* Line number this text occurs */ }; /* * Event callback signatures. */ typedef sxi32 (*ProcXMLStartTagHandler)(SyXMLRawStr * ,SyXMLRawStr *,sxu32,SyXMLRawStr *,void *); typedef sxi32 (*ProcXMLTextHandler)(SyXMLRawStr *,void *); typedef sxi32 (*ProcXMLEndTagHandler)(SyXMLRawStr * ,SyXMLRawStr *,void *); typedef sxi32 (*ProcXMLPIHandler)(SyXMLRawStr *,SyXMLRawStr *,void *); typedef sxi32 (*ProcXMLDoctypeHandler)(SyXMLRawStr *,void *); typedef sxi32 (*ProcXMLSyntaxErrorHandler)(const char *,int,SyToken *,void *); typedef sxi32 (*ProcXMLStartDocument)(void *); typedef sxi32 (*ProcXMLNameSpaceStart)(SyXMLRawStr *,SyXMLRawStr *,void *); typedef sxi32 (*ProcXMLNameSpaceEnd)(SyXMLRawStr *,void *); typedef sxi32 (*ProcXMLEndDocument)(void *); /* XML processing control flags */ #define SXML_ENABLE_NAMESPACE 0x01 /* Parse XML with namespace support enbaled */ #define SXML_ENABLE_QUERY 0x02 /* Not used */ #define SXML_OPTION_CASE_FOLDING 0x04 /* Controls whether case-folding is enabled for this XML parser */ #define SXML_OPTION_SKIP_TAGSTART 0x08 /* Specify how many characters should be skipped in the beginning of a tag name.*/ #define SXML_OPTION_SKIP_WHITE 0x10 /* Whether to skip values consisting of whitespace characters. */ #define SXML_OPTION_TARGET_ENCODING 0x20 /* Default encoding: UTF-8 */ /* XML error codes */ enum xml_err_code{ SXML_ERROR_NONE = 1, SXML_ERROR_NO_MEMORY, SXML_ERROR_SYNTAX, SXML_ERROR_NO_ELEMENTS, SXML_ERROR_INVALID_TOKEN, SXML_ERROR_UNCLOSED_TOKEN, SXML_ERROR_PARTIAL_CHAR, SXML_ERROR_TAG_MISMATCH, SXML_ERROR_DUPLICATE_ATTRIBUTE, SXML_ERROR_JUNK_AFTER_DOC_ELEMENT, SXML_ERROR_PARAM_ENTITY_REF, SXML_ERROR_UNDEFINED_ENTITY, SXML_ERROR_RECURSIVE_ENTITY_REF, SXML_ERROR_ASYNC_ENTITY, SXML_ERROR_BAD_CHAR_REF, SXML_ERROR_BINARY_ENTITY_REF, SXML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, SXML_ERROR_MISPLACED_XML_PI, SXML_ERROR_UNKNOWN_ENCODING, SXML_ERROR_INCORRECT_ENCODING, SXML_ERROR_UNCLOSED_CDATA_SECTION, SXML_ERROR_EXTERNAL_ENTITY_HANDLING }; /* Each active XML SAX parser is represented by an instance * of the following structure. */ typedef struct SyXMLParser SyXMLParser; struct SyXMLParser { SyMemBackend *pAllocator; /* Memory backend */ void *pUserData; /* User private data forwarded varbatim by the XML parser * as the last argument to the users callbacks. */ SyHash hns; /* Namespace hashtable */ SySet sToken; /* XML tokens */ SyLex sLex; /* Lexical analyzer */ sxi32 nFlags; /* Control flags */ /* User callbacks */ ProcXMLStartTagHandler xStartTag; /* Start element handler */ ProcXMLEndTagHandler xEndTag; /* End element handler */ ProcXMLTextHandler xRaw; /* Raw text/CDATA handler */ ProcXMLDoctypeHandler xDoctype; /* DOCTYPE handler */ ProcXMLPIHandler xPi; /* Processing instruction (PI) handler*/ ProcXMLSyntaxErrorHandler xError; /* Error handler */ ProcXMLStartDocument xStartDoc; /* StartDoc handler */ ProcXMLEndDocument xEndDoc; /* EndDoc handler */ ProcXMLNameSpaceStart xNameSpace; /* Namespace declaration handler */ ProcXMLNameSpaceEnd xNameSpaceEnd; /* End namespace declaration handler */ }; /* * -------------- * Archive extractor: * -------------- * Each open ZIP/TAR archive is identified by an instance of the following structure. * That is, a process can open one or more archives and manipulates them in thread safe * way by simply working with pointers to the following structure. * Each entry in the archive is remembered in a hashtable. * Lookup is very fast and entry with the same name are chained together. */ typedef struct SyArchiveEntry SyArchiveEntry; typedef struct SyArchive SyArchive; struct SyArchive { SyMemBackend *pAllocator; /* Memory backend */ SyArchiveEntry *pCursor; /* Cursor for linear traversal of archive entries */ SyArchiveEntry *pList; /* Pointer to the List of the loaded archive */ SyArchiveEntry **apHash; /* Hashtable for archive entry */ ProcRawStrCmp xCmp; /* Hash comparison function */ ProcHash xHash; /* Hash Function */ sxu32 nSize; /* Hashtable size */ sxu32 nEntry; /* Total number of entries in the zip/tar archive */ sxu32 nLoaded; /* Total number of entries loaded in memory */ sxu32 nCentralOfft; /* Central directory offset(ZIP only. Otherwise Zero) */ sxu32 nCentralSize; /* Central directory size(ZIP only. Otherwise Zero) */ void *pUserData; /* Upper layer private data */ sxu32 nMagic; /* Sanity check */ }; #define SXARCH_MAGIC 0xDEAD635A #define SXARCH_INVALID(ARCH) (ARCH == 0 || ARCH->nMagic != SXARCH_MAGIC) #define SXARCH_ENTRY_INVALID(ENTRY) (ENTRY == 0 || ENTRY->nMagic != SXARCH_MAGIC) #define SyArchiveHashFunc(ARCH) (ARCH)->xHash #define SyArchiveCmpFunc(ARCH) (ARCH)->xCmp #define SyArchiveUserData(ARCH) (ARCH)->pUserData #define SyArchiveSetUserData(ARCH,DATA) (ARCH)->pUserData = DATA /* * Each loaded archive record is identified by an instance * of the following structure. */ struct SyArchiveEntry { sxu32 nByte; /* Contents size before compression */ sxu32 nByteCompr; /* Contents size after compression */ sxu32 nReadCount; /* Read counter */ sxu32 nCrc; /* Contents CRC32 */ Sytm sFmt; /* Last-modification time */ sxu32 nOfft; /* Data offset. */ sxu16 nComprMeth; /* Compression method 0 == stored/8 == deflated and so on (see appnote.txt)*/ sxu16 nExtra; /* Extra size if any */ SyString sFileName; /* entry name & length */ sxu32 nDup; /* Total number of entries with the same name */ SyArchiveEntry *pNextHash,*pPrevHash; /* Hash collision chains */ SyArchiveEntry *pNextName; /* Next entry with the same name */ SyArchiveEntry *pNext,*pPrev; /* Next and previous entry in the list */ sxu32 nHash; /* Hash of the entry name */ void *pUserData; /* User data */ sxu32 nMagic; /* Sanity check */ }; /* * Extra flags for extending the file local header */ #define SXZIP_EXTRA_TIMESTAMP 0x001 /* Extended UNIX timestamp */ #endif /* PH7_DISABLE_BUILTIN_FUNC */ #ifndef PH7_DISABLE_HASH_FUNC /* MD5 context */ typedef struct MD5Context MD5Context; struct MD5Context { sxu32 buf[4]; sxu32 bits[2]; unsigned char in[64]; }; /* SHA1 context */ typedef struct SHA1Context SHA1Context; struct SHA1Context { unsigned int state[5]; unsigned int count[2]; unsigned char buffer[64]; }; #endif /* PH7_DISABLE_HASH_FUNC */ /* PH7 private declaration */ /* * Memory Objects. * Internally, the PH7 virtual machine manipulates nearly all PHP values * [i.e: string, int, float, resource, object, bool, null] as ph7_values structures. * Each ph7_values struct may cache multiple representations (string, integer etc.) * of the same value. */ struct ph7_value { ph7_real rVal; /* Real value */ union{ sxi64 iVal; /* Integer value */ void *pOther; /* Other values (Object, Array, Resource, Namespace, etc.) */ }x; sxi32 iFlags; /* Control flags (see below) */ ph7_vm *pVm; /* Virtual machine that own this instance */ SyBlob sBlob; /* String values */ sxu32 nIdx; /* Index number of this entry in the global object allocator */ }; /* Allowed value types. */ #define MEMOBJ_STRING 0x001 /* Memory value is a UTF-8 string */ #define MEMOBJ_INT 0x002 /* Memory value is an integer */ #define MEMOBJ_REAL 0x004 /* Memory value is a real number */ #define MEMOBJ_BOOL 0x008 /* Memory value is a boolean */ #define MEMOBJ_NULL 0x020 /* Memory value is NULL */ #define MEMOBJ_HASHMAP 0x040 /* Memory value is a hashmap aka 'array' in the PHP jargon */ #define MEMOBJ_OBJ 0x080 /* Memory value is an object [i.e: class instance] */ #define MEMOBJ_RES 0x100 /* Memory value is a resource [User private data] */ #define MEMOBJ_REFERENCE 0x400 /* Memory value hold a reference (64-bit index) of another ph7_value */ /* Mask of all known types */ #define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES) /* Scalar variables * According to the PHP language reference manual * Scalar variables are those containing an integer, float, string or boolean. * Types array, object and resource are not scalar. */ #define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) #define MEMOBJ_AUX (MEMOBJ_REFERENCE) /* * The following macro clear the current ph7_value type and replace * it with the given one. */ #define MemObjSetType(OBJ,TYPE) ((OBJ)->iFlags = ((OBJ)->iFlags&~MEMOBJ_ALL)|TYPE) /* ph7_value cast method signature */ typedef sxi32 (*ProcMemObjCast)(ph7_value *); /* Forward reference */ typedef struct ph7_output_consumer ph7_output_consumer; typedef struct ph7_user_func ph7_user_func; typedef struct ph7_conf ph7_conf; /* * An instance of the following structure store the default VM output * consumer and it's private data. * Client-programs can register their own output consumer callback * via the [PH7_VM_CONFIG_OUTPUT] configuration directive. * Please refer to the official documentation for more information * on how to register an output consumer callback. */ struct ph7_output_consumer { ProcConsumer xConsumer; /* VM output consumer routine */ void *pUserData; /* Third argument to xConsumer() */ ProcConsumer xDef; /* Default output consumer routine */ void *pDefData; /* Third argument to xDef() */ }; /* * PH7 engine [i.e: ph7 instance] configuration is stored in * an instance of the following structure. * Please refer to the official documentation for more information * on how to configure your ph7 engine instance. */ struct ph7_conf { ProcConsumer xErr; /* Compile-time error consumer callback */ void *pErrData; /* Third argument to xErr() */ SyBlob sErrConsumer; /* Default error consumer */ }; /* * Signature of the C function responsible of expanding constant values. */ typedef void (*ProcConstant)(ph7_value *,void *); /* * Each registered constant [i.e: __TIME__, __DATE__, PHP_OS, INT_MAX, etc.] is stored * in an instance of the following structure. * Please refer to the official documentation for more information * on how to create/install foreign constants. */ typedef struct ph7_constant ph7_constant; struct ph7_constant { SyString sName; /* Constant name */ ProcConstant xExpand; /* Function responsible of expanding constant value */ void *pUserData; /* Last argument to xExpand() */ }; typedef struct ph7_aux_data ph7_aux_data; /* * Auxiliary data associated with each foreign function is stored * in a stack of the following structure. * Note that automatic tracked chunks are also stored in an instance * of this structure. */ struct ph7_aux_data { void *pAuxData; /* Aux data */ }; /* Foreign functions signature */ typedef int (*ProchHostFunction)(ph7_context *,int,ph7_value **); /* * Each installed foreign function is recored in an instance of the following * structure. * Please refer to the official documentation for more information on how * to create/install foreign functions. */ struct ph7_user_func { ph7_vm *pVm; /* VM that own this instance */ SyString sName; /* Foreign function name */ ProchHostFunction xFunc; /* Implementation of the foreign function */ void *pUserData; /* User private data [Refer to the official documentation for more information]*/ SySet aAux; /* Stack of auxiliary data [Refer to the official documentation for more information]*/ }; /* * The 'context' argument for an installable function. A pointer to an * instance of this structure is the first argument to the routines used * implement the foreign functions. */ struct ph7_context { ph7_user_func *pFunc; /* Function information. */ ph7_value *pRet; /* Return value is stored here. */ SySet sVar; /* Container of dynamically allocated ph7_values * [i.e: Garbage collection purposes.] */ SySet sChunk; /* Track dynamically allocated chunks [ph7_aux_data instance]. * [i.e: Garbage collection purposes.] */ ph7_vm *pVm; /* Virtual machine that own this context */ sxi32 iFlags; /* Call flags */ }; /* * Each hashmap entry [i.e: array(4,5,6)] is recorded in an instance * of the following structure. */ struct ph7_hashmap_node { ph7_hashmap *pMap; /* Hashmap that own this instance */ sxi32 iType; /* Node type */ union{ sxi64 iKey; /* Int key */ SyBlob sKey; /* Blob key */ }xKey; sxi32 iFlags; /* Control flags */ sxu32 nHash; /* Key hash value */ sxu32 nValIdx; /* Value stored in this node */ ph7_hashmap_node *pNext,*pPrev; /* Link to other entries [i.e: linear traversal] */ ph7_hashmap_node *pNextCollide,*pPrevCollide; /* Collision chain */ }; /* * Each active hashmap aka array in the PHP jargon is represented * by an instance of the following structure. */ struct ph7_hashmap { ph7_vm *pVm; /* VM that own this instance */ ph7_hashmap_node **apBucket; /* Hash bucket */ ph7_hashmap_node *pFirst; /* First inserted entry */ ph7_hashmap_node *pLast; /* Last inserted entry */ ph7_hashmap_node *pCur; /* Current entry */ sxu32 nSize; /* Bucket size */ sxu32 nEntry; /* Total number of inserted entries */ sxu32 (*xIntHash)(sxi64); /* Hash function for int_keys */ sxu32 (*xBlobHash)(const void *,sxu32); /* Hash function for blob_keys */ sxi64 iNextIdx; /* Next available automatically assigned index */ sxi32 iRef; /* Reference count */ }; /* An instance of the following structure is the context * for the FOREACH_STEP/FOREACH_INIT VM instructions. * Those instructions are used to implement the 'foreach' * statement. * This structure is made available to these instructions * as the P3 operand. */ struct ph7_foreach_info { SyString sKey; /* Key name. Empty otherwise*/ SyString sValue; /* Value name */ sxi32 iFlags; /* Control flags */ SySet aStep; /* Stack of steps [i.e: ph7_foreach_step instance] */ }; struct ph7_foreach_step { sxi32 iFlags; /* Control flags (see below) */ /* Iterate on those values */ union { ph7_hashmap *pMap; /* Hashmap [i.e: array in the PHP jargon] iteration * Ex: foreach(array(1,2,3) as $key=>$value){} */ ph7_class_instance *pThis; /* Class instance [i.e: object] iteration */ }xIter; }; /* Foreach step control flags */ #define PH7_4EACH_STEP_HASHMAP 0x001 /* Hashmap iteration */ #define PH7_4EACH_STEP_OBJECT 0x002 /* Object iteration */ #define PH7_4EACH_STEP_KEY 0x004 /* Make Key available */ #define PH7_4EACH_STEP_REF 0x008 /* Pass value by reference not copy */ /* * Each PH7 engine is identified by an instance of the following structure. * Please refer to the official documentation for more information * on how to configure your PH7 engine instance. */ struct ph7 { SyMemBackend sAllocator; /* Low level memory allocation subsystem */ const ph7_vfs *pVfs; /* Underlying Virtual File System */ ph7_conf xConf; /* Configuration */ #if defined(PH7_ENABLE_THREADS) const SyMutexMethods *pMethods; /* Mutex methods */ SyMutex *pMutex; /* Per-engine mutex */ #endif ph7_vm *pVms; /* List of active VM */ sxi32 iVm; /* Total number of active VM */ ph7 *pNext,*pPrev; /* List of active engines */ sxu32 nMagic; /* Sanity check against misuse */ }; /* Code generation data structures */ typedef sxi32 (*ProcErrorGen)(void *,sxi32,sxu32,const char *,...); typedef struct ph7_expr_node ph7_expr_node; typedef struct ph7_expr_op ph7_expr_op; typedef struct ph7_gen_state ph7_gen_state; typedef struct GenBlock GenBlock; typedef sxi32 (*ProcLangConstruct)(ph7_gen_state *); typedef sxi32 (*ProcNodeConstruct)(ph7_gen_state *,sxi32); /* * Each supported operator [i.e: +, -, ==, *, %, >>, >=, new, etc.] is represented * by an instance of the following structure. * The PH7 parser does not use any external tools and is 100% handcoded. * That is, the PH7 parser is thread-safe ,full reentrant, produce consistant * compile-time errrors and at least 7 times faster than the standard PHP parser. */ struct ph7_expr_op { SyString sOp; /* String representation of the operator [i.e: "+","*","=="...] */ sxi32 iOp; /* Operator ID */ sxi32 iPrec; /* Operator precedence: 1 == Highest */ sxi32 iAssoc; /* Operator associativity (either left,right or non-associative) */ sxi32 iVmOp; /* VM OP code for this operator [i.e: PH7_OP_EQ,PH7_OP_LT,PH7_OP_MUL...]*/ }; /* * Each expression node is parsed out and recorded * in an instance of the following structure. */ struct ph7_expr_node { const ph7_expr_op *pOp; /* Operator ID or NULL if literal, constant, variable, function or class method call */ ph7_expr_node *pLeft; /* Left expression tree */ ph7_expr_node *pRight; /* Right expression tree */ SyToken *pStart; /* Stream of tokens that belong to this node */ SyToken *pEnd; /* End of token stream */ sxi32 iFlags; /* Node construct flags */ ProcNodeConstruct xCode; /* C routine responsible of compiling this node */ SySet aNodeArgs; /* Node arguments. Only used by postfix operators [i.e: function call]*/ ph7_expr_node *pCond; /* Condition: Only used by the ternary operator '?:' */ }; /* Node Construct flags */ #define EXPR_NODE_PRE_INCR 0x01 /* Pre-icrement/decrement [i.e: ++$i,--$j] node */ /* * A block of instructions is recorded in an instance of the following structure. * This structure is used only during compile-time and have no meaning * during bytecode execution. */ struct GenBlock { ph7_gen_state *pGen; /* State of the code generator */ GenBlock *pParent; /* Upper block or NULL if global */ sxu32 nFirstInstr; /* First instruction to execute */ sxi32 iFlags; /* Block control flags (see below) */ SySet aJumpFix; /* Jump fixup (JumpFixup instance) */ void *pUserData; /* Upper layer private data */ /* The following two fields are used only when compiling * the 'do..while()' language construct. */ sxu8 bPostContinue; /* TRUE when compiling the do..while() statement */ SySet aPostContFix; /* Post-continue jump fix */ }; /* * Code generator state is remembered in an instance of the following * structure. We put the information in this structure and pass around * a pointer to this structure, rather than pass around all of the * information separately. This helps reduce the number of arguments * to generator functions. * This structure is used only during compile-time and have no meaning * during bytecode execution. */ struct ph7_gen_state { ph7_vm *pVm; /* VM that own this instance */ SyHash hLiteral; /* Constant string Literals table */ SyHash hNumLiteral; /* Numeric literals table */ SyHash hVar; /* Collected variable hashtable */ GenBlock *pCurrent; /* Current processed block */ GenBlock sGlobal; /* Global block */ ProcConsumer xErr; /* Error consumer callback */ void *pErrData; /* Third argument to xErr() */ SySet aLabel; /* Label table */ SySet aGoto; /* Gotos table */ SyBlob sWorker; /* General purpose working buffer */ SyBlob sErrBuf; /* Error buffer */ SyToken *pIn; /* Current processed token */ SyToken *pEnd; /* Last token in the stream */ sxu32 nErr; /* Total number of compilation error */ SyToken *pRawIn; /* Current processed raw token */ SyToken *pRawEnd; /* Last raw token in the stream */ SySet *pTokenSet; /* Token containers */ }; /* Forward references */ typedef struct ph7_vm_func_closure_env ph7_vm_func_closure_env; typedef struct ph7_vm_func_static_var ph7_vm_func_static_var; typedef struct ph7_vm_func_arg ph7_vm_func_arg; typedef struct ph7_vm_func ph7_vm_func; typedef struct VmFrame VmFrame; /* * Each collected function argument is recorded in an instance * of the following structure. * Note that as an extension, PH7 implements full type hinting * which mean that any function can have it's own signature. * Example: * function foo(int $a,string $b,float $c,ClassInstance $d){} * This is how the powerful function overloading mechanism is * implemented. * Note that as an extension, PH7 allow function arguments to have * any complex default value associated with them unlike the standard * PHP engine. * Example: * function foo(int $a = rand() & 1023){} * now, when foo is called without arguments [i.e: foo()] the * $a variable (first parameter) will be set to a random number * between 0 and 1023 inclusive. * Refer to the official documentation for more information on this * mechanism and other extension introduced by the PH7 engine. */ struct ph7_vm_func_arg { SyString sName; /* Argument name */ SySet aByteCode; /* Compiled default value associated with this argument */ sxu32 nType; /* Type of this argument [i.e: array, int, string, float, object, etc.] */ SyString sClass; /* Class name if the argument expect a class instance [i.e: function foo(BaseClass $bar){} ] */ sxi32 iFlags; /* Configuration flags */ }; /* * Each static variable is parsed out and remembered in an instance * of the following structure. * Note that as an extension, PH7 allow static variable have * any complex default value associated with them unlike the standard * PHP engine. * Example: * static $rand_str = 'PH7'.rand_str(3); // Concatenate 'PH7' with * // a random three characters(English alphabet) * var_dump($rand_str); * //You should see something like this * string(6 'PH7awt'); */ struct ph7_vm_func_static_var { SyString sName; /* Static variable name */ SySet aByteCode; /* Compiled initialization expression */ sxu32 nIdx; /* Object index in the global memory object container */ }; /* * Each imported variable from the outside closure environnment is recoded * in an instance of the following structure. */ struct ph7_vm_func_closure_env { SyString sName; /* Imported variable name */ int iFlags; /* Control flags */ ph7_value sValue; /* Imported variable value */ sxu32 nIdx; /* Reference to the bounded variable if passed by reference *[Example: * $x = 1; * $closure = function() use (&$x) { ++$x; } * $closure(); *] */ }; /* Function configuration flags */ #define VM_FUNC_ARG_BY_REF 0x001 /* Argument passed by reference */ #define VM_FUNC_ARG_HAS_DEF 0x002 /* Argument has default value associated with it */ #define VM_FUNC_REF_RETURN 0x004 /* Return by reference */ #define VM_FUNC_CLASS_METHOD 0x008 /* VM function is in fact a class method */ #define VM_FUNC_CLOSURE 0x010 /* VM function is a closure */ #define VM_FUNC_ARG_IGNORE 0x020 /* Do not install argument in the current frame */ /* * Each user defined function is parsed out and stored in an instance * of the following structure. * PH7 introduced some powerfull extensions to the PHP 5 programming * language like function overloading, type hinting, complex default * arguments values and many more. * Please refer to the official documentation for more information. */ struct ph7_vm_func { SySet aArgs; /* Expected arguments (ph7_vm_func_arg instance) */ SySet aStatic; /* Static variable (ph7_vm_func_static_var instance) */ SyString sName; /* Function name */ SySet aByteCode; /* Compiled function body */ SySet aClosureEnv; /* Closure environment (ph7_vm_func_closure_env instace) */ sxi32 iFlags; /* VM function configuration */ SyString sSignature; /* Function signature used to implement function overloading * (Refer to the official docuemntation for more information * on this powerfull feature) */ void *pUserData; /* Upper layer private data associated with this instance */ ph7_vm_func *pNextName; /* Next VM function with the same name as this one */ }; /* Forward reference */ typedef struct ph7_builtin_constant ph7_builtin_constant; typedef struct ph7_builtin_func ph7_builtin_func; /* * Each built-in foreign function (C function) is stored in an * instance of the following structure. * Please refer to the official documentation for more information * on how to create/install foreign functions. */ struct ph7_builtin_func { const char *zName; /* Function name [i.e: strlen(), rand(), array_merge(), etc.]*/ ProchHostFunction xFunc; /* C routine performing the computation */ }; /* * Each built-in foreign constant is stored in an instance * of the following structure. * Please refer to the official documentation for more information * on how to create/install foreign constants. */ struct ph7_builtin_constant { const char *zName; /* Constant name */ ProcConstant xExpand; /* C routine responsible of expanding constant value*/ }; /* Forward reference */ typedef struct ph7_class_method ph7_class_method; typedef struct ph7_class_attr ph7_class_attr; /* * Each class is parsed out and stored in an instance of the following structure. * PH7 introduced powerfull extensions to the PHP 5 OO subsystems. * Please refer to the official documentation for more information. */ struct ph7_class { ph7_class *pBase; /* Base class if any */ SyHash hDerived; /* Derived [child] classes */ SyString sName; /* Class full qualified name */ sxi32 iFlags; /* Class configuration flags [i.e: final, interface, abstract, etc.] */ SyHash hAttr; /* Class attributes [i.e: variables and constants] */ SyHash hMethod; /* Class methods */ sxu32 nLine; /* Line number on which this class was declared */ SySet aInterface; /* Implemented interface container */ ph7_class *pNextName; /* Next class [interface, abstract, etc.] with the same name */ }; /* Class configuration flags */ #define PH7_CLASS_FINAL 0x001 /* Class is final [cannot be extended] */ #define PH7_CLASS_INTERFACE 0x002 /* Class is interface */ #define PH7_CLASS_ABSTRACT 0x004 /* Class is abstract */ /* Class attribute/methods/constants protection levels */ #define PH7_CLASS_PROT_PUBLIC 1 /* public */ #define PH7_CLASS_PROT_PROTECTED 2 /* protected */ #define PH7_CLASS_PROT_PRIVATE 3 /* private */ /* * each class attribute (variable, constants) is parsed out and stored * in an instance of the following structure. */ struct ph7_class_attr { SyString sName; /* Atrribute name */ sxi32 iFlags; /* Attribute configuration [i.e: static, variable, constant, etc.] */ sxi32 iProtection; /* Protection level [i.e: public, private, protected] */ SySet aByteCode; /* Compiled attribute body */ sxu32 nIdx; /* Attribute index */ sxu32 nLine; /* Line number on which this attribute was defined */ }; /* Attribute configuration */ #define PH7_CLASS_ATTR_STATIC 0x001 /* Static attribute */ #define PH7_CLASS_ATTR_CONSTANT 0x002 /* Constant attribute */ #define PH7_CLASS_ATTR_ABSTRACT 0x004 /* Abstract method */ #define PH7_CLASS_ATTR_FINAL 0x008 /* Final method */ /* * Each class method is parsed out and stored in an instance of the following * structure. * PH7 introduced some powerfull extensions to the PHP 5 programming * language like function overloading,type hinting,complex default * arguments and many more. * Please refer to the official documentation for more information. */ struct ph7_class_method { ph7_vm_func sFunc; /* Compiled method body */ SyString sVmName; /* Automatically generated name assigned to this method. * Typically this is "[class_name__method_name@random_string]" */ sxi32 iProtection; /* Protection level [i.e: public,private,protected] */ sxi32 iFlags; /* Methods configuration */ sxi32 iCloneDepth; /* Clone depth [Only used by the magic method __clone ] */ sxu32 nLine; /* Line on which this method was defined */ }; /* * Each active object (class instance) is represented by an instance of * the following structure. */ struct ph7_class_instance { ph7_vm *pVm; /* VM that own this instance */ ph7_class *pClass; /* Object is an instance of this class */ SyHash hAttr; /* Hashtable of active class members */ sxi32 iRef; /* Reference count */ sxi32 iFlags; /* Control flags */ }; /* * A single instruction of the virtual machine has an opcode * and as many as three operands. * Each VM instruction resulting from compiling a PHP script * is stored in an instance of the following structure. */ typedef struct VmInstr VmInstr; struct VmInstr { sxu8 iOp; /* Operation to preform */ sxi32 iP1; /* First operand */ sxu32 iP2; /* Second operand (Often the jump destination) */ void *p3; /* Third operand (Often Upper layer private data) */ }; /* Each active class instance attribute is represented by an instance * of the following structure. */ typedef struct VmClassAttr VmClassAttr; struct VmClassAttr { ph7_class_attr *pAttr; /* Class attribute */ sxu32 nIdx; /* Memory object index */ }; /* Forward reference */ typedef struct VmRefObj VmRefObj; /* * Each catch [i.e catch(Exception $e){ } ] block is parsed out and stored * in an instance of the following structure. */ typedef struct ph7_exception_block ph7_exception_block; typedef struct ph7_exception ph7_exception; struct ph7_exception_block { SyString sClass; /* Exception class name [i.e: Exception,MyException...] */ SyString sThis; /* Instance name [i.e: $e..] */ SySet sByteCode; /* Block compiled instructions */ }; /* * Context for the exception mechanism. */ struct ph7_exception { ph7_vm *pVm; /* VM that own this exception */ SySet sEntry; /* Compiled 'catch' blocks (ph7_exception_block instance) * container. */ VmFrame *pFrame; /* Frame that trigger the exception */ }; /* Forward reference */ typedef struct ph7_case_expr ph7_case_expr; typedef struct ph7_switch ph7_switch; /* * Each compiled case block in a swicth statement is compiled * and stored in an instance of the following structure. */ struct ph7_case_expr { SySet aByteCode; /* Compiled body of the case block */ sxu32 nStart; /* First instruction to execute */ }; /* * Each compiled switch statement is parsed out and stored * in an instance of the following structure. */ struct ph7_switch { SySet aCaseExpr; /* Compile case block */ sxu32 nOut; /* First instruction to execute after this statement */ sxu32 nDefault; /* First instruction to execute in the default block */ }; /* Assertion flags */ #define PH7_ASSERT_DISABLE 0x01 /* Disable assertion */ #define PH7_ASSERT_WARNING 0x02 /* Issue a warning for each failed assertion */ #define PH7_ASSERT_BAIL 0x04 /* Terminate execution on failed assertions */ #define PH7_ASSERT_QUIET_EVAL 0x08 /* Not used */ #define PH7_ASSERT_CALLBACK 0x10 /* Callback to call on failed assertions */ /* * error_log() consumer function signature. * Refer to the [PH7_VM_CONFIG_ERR_LOG_HANDLER] configuration directive * for more information on how to register an error_log consumer(). */ typedef void (*ProcErrLog)(const char *,int,const char *,const char *); /* * An instance of the following structure hold the bytecode instructions * resulting from compiling a PHP script. * This structure contains the complete state of the virtual machine. */ struct ph7_vm { SyMemBackend sAllocator; /* Memory backend */ #if defined(PH7_ENABLE_THREADS) SyMutex *pMutex; /* Recursive mutex associated with VM. */ #endif ph7 *pEngine; /* Interpreter that own this VM */ SySet aByteCode; /* Default bytecode container */ SySet *pByteContainer; /* Current bytecode container */ VmFrame *pFrame; /* Stack of active frames */ SyPRNGCtx sPrng; /* PRNG context */ SySet aMemObj; /* Object allocation table */ SySet aLitObj; /* Literals allocation table */ ph7_value *aOps; /* Operand stack */ SySet aFreeObj; /* Stack of free memory objects */ SyHash hClass; /* Compiled classes container */ SyHash hConstant; /* Host-application and user defined constants container */ SyHash hHostFunction; /* Host-application installable functions */ SyHash hFunction; /* Compiled functions */ SyHash hSuper; /* Superglobals hashtable */ SyHash hPDO; /* PDO installed drivers */ SyBlob sConsumer; /* Default VM consumer [i.e Redirect all VM output to this blob] */ SyBlob sWorker; /* General purpose working buffer */ SyBlob sArgv; /* $argv[] collector [refer to the [getopt()] implementation for more information] */ SySet aFiles; /* Stack of processed files */ SySet aPaths; /* Set of import paths */ SySet aIncluded; /* Set of included files */ SySet aOB; /* Stackable output buffers */ SySet aShutdown; /* Stack of shutdown user callbacks */ SySet aException; /* Stack of loaded exception */ SySet aIOstream; /* Installed IO stream container */ const ph7_io_stream *pDefStream; /* Default IO stream [i.e: typically this is the 'file://' stream] */ ph7_value sExec; /* Compiled script return value [Can be extracted via the PH7_VM_CONFIG_EXEC_VALUE directive]*/ ph7_value aExceptionCB[2]; /* Installed exception handler callbacks via [set_exception_handler()] */ ph7_value aErrCB[2]; /* Installed error handler callback via [set_error_handler()] */ void *pStdin; /* STDIN IO stream */ void *pStdout; /* STDOUT IO stream */ void *pStderr; /* STDERR IO stream */ int bErrReport; /* TRUE to report all runtime Error/Warning/Notice */ int nRecursionDepth; /* Current recursion depth */ int nMaxDepth; /* Maximum allowed recusion depth */ int nObDepth; /* OB depth */ int nExceptDepth; /* Exception depth */ int closure_cnt; /* Loaded closures counter */ int json_rc; /* JSON return status [refer to json_encode()/json_decode()]*/ sxu32 unique_id; /* Random number used to generate unique ID [refer to uniqid() for more info]*/ ProcErrLog xErrLog; /* error_log() consumer [refer to PH7_VM_CONFIG_ERR_LOG_HANDLER] */ sxu32 nOutputLen; /* Total number of generated output */ ph7_output_consumer sVmConsumer; /* Registered output consumer callback */ int iAssertFlags; /* Assertion flags */ ph7_value sAssertCallback; /* Callback to call on failed assertions */ VmRefObj **apRefObj; /* Hashtable of referenced object */ VmRefObj *pRefList; /* List of referenced memory objects */ sxu32 nRefSize; /* apRefObj[] size */ sxu32 nRefUsed; /* Total entries in apRefObj[] */ SySet aSelf; /* 'self' stack used for static member access [i.e: self::MyConstant] */ ph7_hashmap *pGlobal; /* $GLOBALS hashmap */ sxu32 nGlobalIdx; /* $GLOBALS index */ sxi32 iExitStatus; /* Script exit status */ ph7_gen_state sCodeGen; /* Code generator module */ ph7_vm *pNext,*pPrev; /* List of active VM's */ sxu32 nMagic; /* Sanity check against misuse */ }; /* * Allowed value for ph7_vm.nMagic */ #define PH7_VM_INIT 0xFADE9512 /* VM correctly initialized */ #define PH7_VM_RUN 0xEA271285 /* VM ready to execute PH7 bytecode */ #define PH7_VM_EXEC 0xCAFE2DAD /* VM executing PH7 bytecode */ #define PH7_VM_STALE 0xBAD1DEAD /* Stale VM */ /* * Error codes according to the PHP language reference manual. */ enum iErrCode { E_ERROR = 1, /* Fatal run-time errors. These indicate errors that can not be recovered * from, such as a memory allocation problem. Execution of the script is * halted. * The only fatal error under PH7 is an out-of-memory. All others erros * even a call to undefined function will not halt script execution. */ E_WARNING = 2, /* Run-time warnings (non-fatal errors). Execution of the script is not halted. */ E_PARSE = 4, /* Compile-time parse errors. Parse errors should only be generated by the parser.*/ E_NOTICE = 8, /* Run-time notices. Indicate that the script encountered something that could * indicate an error, but could also happen in the normal course of running a script. */ E_CORE_WARNING = 16, /* Fatal errors that occur during PHP's initial startup. This is like an E_ERROR * except it is generated by the core of PHP. */ E_USER_ERROR = 256, /* User-generated error message.*/ E_USER_WARNING = 512, /* User-generated warning message.*/ E_USER_NOTICE = 1024, /* User-generated notice message.*/ E_STRICT = 2048, /* Enable to have PHP suggest changes to your code which will ensure the best interoperability * and forward compatibility of your code. */ E_RECOVERABLE_ERROR = 4096, /* Catchable fatal error. It indicates that a probably dangerous error occured, but did not * leave the Engine in an unstable state. If the error is not caught by a user defined handle * the application aborts as it was an E_ERROR. */ E_DEPRECATED = 8192, /* Run-time notices. Enable this to receive warnings about code that will not * work in future versions. */ E_USER_DEPRECATED = 16384, /* User-generated warning message. */ E_ALL = 32767 /* All errors and warnings */ }; /* * Each VM instruction resulting from compiling a PHP script is represented * by one of the following OP codes. * The program consists of a linear sequence of operations. Each operation * has an opcode and 3 operands.Operands P1 is an integer. * Operand P2 is an unsigned integer and operand P3 is a memory address. * Few opcodes use all 3 operands. */ enum ph7_vm_op { PH7_OP_DONE = 1, /* Done */ PH7_OP_HALT, /* Halt */ PH7_OP_LOAD, /* Load memory object */ PH7_OP_LOADC, /* Load constant */ PH7_OP_LOAD_IDX, /* Load array entry */ PH7_OP_LOAD_MAP, /* Load hashmap('array') */ PH7_OP_LOAD_LIST, /* Load list */ PH7_OP_LOAD_CLOSURE, /* Load closure */ PH7_OP_NOOP, /* NOOP */ PH7_OP_JMP, /* Unconditional jump */ PH7_OP_JZ, /* Jump on zero (FALSE jump) */ PH7_OP_JNZ, /* Jump on non-zero (TRUE jump) */ PH7_OP_POP, /* Stack POP */ PH7_OP_CAT, /* Concatenation */ PH7_OP_CVT_INT, /* Integer cast */ PH7_OP_CVT_STR, /* String cast */ PH7_OP_CVT_REAL, /* Float cast */ PH7_OP_CALL, /* Function call */ PH7_OP_UMINUS, /* Unary minus '-'*/ PH7_OP_UPLUS, /* Unary plus '+'*/ PH7_OP_BITNOT, /* Bitwise not '~' */ PH7_OP_LNOT, /* Logical not '!' */ PH7_OP_MUL, /* Multiplication '*' */ PH7_OP_DIV, /* Division '/' */ PH7_OP_MOD, /* Modulus '%' */ PH7_OP_ADD, /* Add '+' */ PH7_OP_SUB, /* Sub '-' */ PH7_OP_SHL, /* Left shift '<<' */ PH7_OP_SHR, /* Right shift '>>' */ PH7_OP_LT, /* Less than '<' */ PH7_OP_LE, /* Less or equal '<=' */ PH7_OP_GT, /* Greater than '>' */ PH7_OP_GE, /* Greater or equal '>=' */ PH7_OP_EQ, /* Equal '==' */ PH7_OP_NEQ, /* Not equal '!=' */ PH7_OP_TEQ, /* Type equal '===' */ PH7_OP_TNE, /* Type not equal '!==' */ PH7_OP_BAND, /* Bitwise and '&' */ PH7_OP_BXOR, /* Bitwise xor '^' */ PH7_OP_BOR, /* Bitwise or '|' */ PH7_OP_LAND, /* Logical and '&&','and' */ PH7_OP_LOR, /* Logical or '||','or' */ PH7_OP_LXOR, /* Logical xor 'xor' */ PH7_OP_STORE, /* Store Object */ PH7_OP_STORE_IDX, /* Store indexed object */ PH7_OP_STORE_IDX_REF,/* Store indexed object by reference */ PH7_OP_PULL, /* Stack pull */ PH7_OP_SWAP, /* Stack swap */ PH7_OP_YIELD, /* Stack yield */ PH7_OP_CVT_BOOL, /* Boolean cast */ PH7_OP_CVT_NUMC, /* Numeric (integer,real or both) type cast */ PH7_OP_INCR, /* Increment ++ */ PH7_OP_DECR, /* Decrement -- */ PH7_OP_SEQ, /* 'eq' String equal: Strict string comparison */ PH7_OP_SNE, /* 'ne' String not equal: Strict string comparison */ PH7_OP_NEW, /* new */ PH7_OP_CLONE, /* clone */ PH7_OP_ADD_STORE, /* Add and store '+=' */ PH7_OP_SUB_STORE, /* Sub and store '-=' */ PH7_OP_MUL_STORE, /* Mul and store '*=' */ PH7_OP_DIV_STORE, /* Div and store '/=' */ PH7_OP_MOD_STORE, /* Mod and store '%=' */ PH7_OP_CAT_STORE, /* Cat and store '.=' */ PH7_OP_SHL_STORE, /* Shift left and store '>>=' */ PH7_OP_SHR_STORE, /* Shift right and store '<<=' */ PH7_OP_BAND_STORE, /* Bitand and store '&=' */ PH7_OP_BOR_STORE, /* Bitor and store '|=' */ PH7_OP_BXOR_STORE, /* Bitxor and store '^=' */ PH7_OP_CONSUME, /* Consume VM output */ PH7_OP_LOAD_REF, /* Load reference */ PH7_OP_STORE_REF, /* Store a reference to a variable*/ PH7_OP_MEMBER, /* Class member run-time access */ PH7_OP_UPLINK, /* Run-Time frame link */ PH7_OP_CVT_NULL, /* NULL cast */ PH7_OP_CVT_ARRAY, /* Array cast */ PH7_OP_CVT_OBJ, /* Object cast */ PH7_OP_FOREACH_INIT, /* For each init */ PH7_OP_FOREACH_STEP, /* For each step */ PH7_OP_IS_A, /* Instanceof */ PH7_OP_LOAD_EXCEPTION,/* Load an exception */ PH7_OP_POP_EXCEPTION, /* POP an exception */ PH7_OP_THROW, /* Throw exception */ PH7_OP_SWITCH, /* Switch operation */ PH7_OP_ERR_CTRL /* Error control */ }; /* -- END-OF INSTRUCTIONS -- */ /* * Expression Operators ID. */ enum ph7_expr_id { EXPR_OP_NEW = 1, /* new */ EXPR_OP_CLONE, /* clone */ EXPR_OP_ARROW, /* -> */ EXPR_OP_DC, /* :: */ EXPR_OP_SUBSCRIPT, /* []: Subscripting */ EXPR_OP_FUNC_CALL, /* func_call() */ EXPR_OP_INCR, /* ++ */ EXPR_OP_DECR, /* -- */ EXPR_OP_BITNOT, /* ~ */ EXPR_OP_UMINUS, /* Unary minus */ EXPR_OP_UPLUS, /* Unary plus */ EXPR_OP_TYPECAST, /* Type cast [i.e: (int),(float),(string)...] */ EXPR_OP_ALT, /* @ */ EXPR_OP_INSTOF, /* instanceof */ EXPR_OP_LOGNOT, /* logical not ! */ EXPR_OP_MUL, /* Multiplication */ EXPR_OP_DIV, /* division */ EXPR_OP_MOD, /* Modulus */ EXPR_OP_ADD, /* Addition */ EXPR_OP_SUB, /* Substraction */ EXPR_OP_DOT, /* Concatenation */ EXPR_OP_SHL, /* Left shift */ EXPR_OP_SHR, /* Right shift */ EXPR_OP_LT, /* Less than */ EXPR_OP_LE, /* Less equal */ EXPR_OP_GT, /* Greater than */ EXPR_OP_GE, /* Greater equal */ EXPR_OP_EQ, /* Equal == */ EXPR_OP_NE, /* Not equal != <> */ EXPR_OP_TEQ, /* Type equal === */ EXPR_OP_TNE, /* Type not equal !== */ EXPR_OP_SEQ, /* String equal 'eq' */ EXPR_OP_SNE, /* String not equal 'ne' */ EXPR_OP_BAND, /* Biwise and '&' */ EXPR_OP_REF, /* Reference operator '&' */ EXPR_OP_XOR, /* bitwise xor '^' */ EXPR_OP_BOR, /* bitwise or '|' */ EXPR_OP_LAND, /* Logical and '&&','and' */ EXPR_OP_LOR, /* Logical or '||','or'*/ EXPR_OP_LXOR, /* Logical xor 'xor' */ EXPR_OP_QUESTY, /* Ternary operator '?' */ EXPR_OP_ASSIGN, /* Assignment '=' */ EXPR_OP_ADD_ASSIGN, /* Combined operator: += */ EXPR_OP_SUB_ASSIGN, /* Combined operator: -= */ EXPR_OP_MUL_ASSIGN, /* Combined operator: *= */ EXPR_OP_DIV_ASSIGN, /* Combined operator: /= */ EXPR_OP_MOD_ASSIGN, /* Combined operator: %= */ EXPR_OP_DOT_ASSIGN, /* Combined operator: .= */ EXPR_OP_AND_ASSIGN, /* Combined operator: &= */ EXPR_OP_OR_ASSIGN, /* Combined operator: |= */ EXPR_OP_XOR_ASSIGN, /* Combined operator: ^= */ EXPR_OP_SHL_ASSIGN, /* Combined operator: <<= */ EXPR_OP_SHR_ASSIGN, /* Combined operator: >>= */ EXPR_OP_COMMA /* Comma expression */ }; /* * Very high level tokens. */ #define PH7_TOKEN_RAW 0x001 /* Raw text [i.e: HTML,XML...] */ #define PH7_TOKEN_PHP 0x002 /* PHP chunk */ /* * Lexer token codes * The following set of constants are the tokens recognized * by the lexer when processing PHP input. * Important: Token values MUST BE A POWER OF TWO. */ #define PH7_TK_INTEGER 0x0000001 /* Integer */ #define PH7_TK_REAL 0x0000002 /* Real number */ #define PH7_TK_NUM (PH7_TK_INTEGER|PH7_TK_REAL) /* Numeric token,either integer or real */ #define PH7_TK_KEYWORD 0x0000004 /* Keyword [i.e: while,for,if,foreach...] */ #define PH7_TK_ID 0x0000008 /* Alphanumeric or UTF-8 stream */ #define PH7_TK_DOLLAR 0x0000010 /* '$' Dollar sign */ #define PH7_TK_OP 0x0000020 /* Operator [i.e: +,*,/...] */ #define PH7_TK_OCB 0x0000040 /* Open curly brace'{' */ #define PH7_TK_CCB 0x0000080 /* Closing curly brace'}' */ #define PH7_TK_NSSEP 0x0000100 /* Namespace separator '\' */ #define PH7_TK_LPAREN 0x0000200 /* Left parenthesis '(' */ #define PH7_TK_RPAREN 0x0000400 /* Right parenthesis ')' */ #define PH7_TK_OSB 0x0000800 /* Open square bracket '[' */ #define PH7_TK_CSB 0x0001000 /* Closing square bracket ']' */ #define PH7_TK_DSTR 0x0002000 /* Double quoted string "$str" */ #define PH7_TK_SSTR 0x0004000 /* Single quoted string 'str' */ #define PH7_TK_HEREDOC 0x0008000 /* Heredoc <<< */ #define PH7_TK_NOWDOC 0x0010000 /* Nowdoc <<< */ #define PH7_TK_COMMA 0x0020000 /* Comma ',' */ #define PH7_TK_SEMI 0x0040000 /* Semi-colon ";" */ #define PH7_TK_BSTR 0x0080000 /* Backtick quoted string [i.e: Shell command `date`] */ #define PH7_TK_COLON 0x0100000 /* single Colon ':' */ #define PH7_TK_AMPER 0x0200000 /* Ampersand '&' */ #define PH7_TK_EQUAL 0x0400000 /* Equal '=' */ #define PH7_TK_ARRAY_OP 0x0800000 /* Array operator '=>' */ #define PH7_TK_OTHER 0x1000000 /* Other symbols */ /* * PHP keyword. * These words have special meaning in PHP. Some of them represent things which look like * functions, some look like constants, and so on, but they're not, really: they are language constructs. * You cannot use any of the following words as constants, class names, function or method names. * Using them as variable names is generally OK, but could lead to confusion. */ #define PH7_TKWRD_EXTENDS 1 /* extends */ #define PH7_TKWRD_ENDSWITCH 2 /* endswitch */ #define PH7_TKWRD_SWITCH 3 /* switch */ #define PH7_TKWRD_PRINT 4 /* print */ #define PH7_TKWRD_INTERFACE 5 /* interface */ #define PH7_TKWRD_ENDDEC 6 /* enddeclare */ #define PH7_TKWRD_DECLARE 7 /* declare */ /* The number '8' is reserved for PH7_TK_ID */ #define PH7_TKWRD_REQONCE 9 /* require_once */ #define PH7_TKWRD_REQUIRE 10 /* require */ #define PH7_TKWRD_ELIF 0x4000000 /* elseif: MUST BE A POWER OF TWO */ #define PH7_TKWRD_ELSE 0x8000000 /* else: MUST BE A POWER OF TWO */ #define PH7_TKWRD_IF 13 /* if */ #define PH7_TKWRD_FINAL 14 /* final */ #define PH7_TKWRD_LIST 15 /* list */ #define PH7_TKWRD_STATIC 16 /* static */ #define PH7_TKWRD_CASE 17 /* case */ #define PH7_TKWRD_SELF 18 /* self */ #define PH7_TKWRD_FUNCTION 19 /* function */ #define PH7_TKWRD_NAMESPACE 20 /* namespace */ #define PH7_TKWRD_ENDIF 0x400000 /* endif: MUST BE A POWER OF TWO */ #define PH7_TKWRD_CLONE 0x80 /* clone: MUST BE A POWER OF TWO */ #define PH7_TKWRD_NEW 0x100 /* new: MUST BE A POWER OF TWO */ #define PH7_TKWRD_CONST 22 /* const */ #define PH7_TKWRD_THROW 23 /* throw */ #define PH7_TKWRD_USE 24 /* use */ #define PH7_TKWRD_ENDWHILE 0x800000 /* endwhile: MUST BE A POWER OF TWO */ #define PH7_TKWRD_WHILE 26 /* while */ #define PH7_TKWRD_EVAL 27 /* eval */ #define PH7_TKWRD_VAR 28 /* var */ #define PH7_TKWRD_ARRAY 0x200 /* array: MUST BE A POWER OF TWO */ #define PH7_TKWRD_ABSTRACT 29 /* abstract */ #define PH7_TKWRD_TRY 30 /* try */ #define PH7_TKWRD_AND 0x400 /* and: MUST BE A POWER OF TWO */ #define PH7_TKWRD_DEFAULT 31 /* default */ #define PH7_TKWRD_CLASS 32 /* class */ #define PH7_TKWRD_AS 33 /* as */ #define PH7_TKWRD_CONTINUE 34 /* continue */ #define PH7_TKWRD_EXIT 35 /* exit */ #define PH7_TKWRD_DIE 36 /* die */ #define PH7_TKWRD_ECHO 37 /* echo */ #define PH7_TKWRD_GLOBAL 38 /* global */ #define PH7_TKWRD_IMPLEMENTS 39 /* implements */ #define PH7_TKWRD_INCONCE 40 /* include_once */ #define PH7_TKWRD_INCLUDE 41 /* include */ #define PH7_TKWRD_EMPTY 42 /* empty */ #define PH7_TKWRD_INSTANCEOF 0x800 /* instanceof: MUST BE A POWER OF TWO */ #define PH7_TKWRD_ISSET 43 /* isset */ #define PH7_TKWRD_PARENT 44 /* parent */ #define PH7_TKWRD_PRIVATE 45 /* private */ #define PH7_TKWRD_ENDFOR 0x1000000 /* endfor: MUST BE A POWER OF TWO */ #define PH7_TKWRD_END4EACH 0x2000000 /* endforeach: MUST BE A POWER OF TWO */ #define PH7_TKWRD_FOR 48 /* for */ #define PH7_TKWRD_FOREACH 49 /* foreach */ #define PH7_TKWRD_OR 0x1000 /* or: MUST BE A POWER OF TWO */ #define PH7_TKWRD_PROTECTED 50 /* protected */ #define PH7_TKWRD_DO 51 /* do */ #define PH7_TKWRD_PUBLIC 52 /* public */ #define PH7_TKWRD_CATCH 53 /* catch */ #define PH7_TKWRD_RETURN 54 /* return */ #define PH7_TKWRD_UNSET 0x2000 /* unset: MUST BE A POWER OF TWO */ #define PH7_TKWRD_XOR 0x4000 /* xor: MUST BE A POWER OF TWO */ #define PH7_TKWRD_BREAK 55 /* break */ #define PH7_TKWRD_GOTO 56 /* goto */ #define PH7_TKWRD_BOOL 0x8000 /* bool: MUST BE A POWER OF TWO */ #define PH7_TKWRD_INT 0x10000 /* int: MUST BE A POWER OF TWO */ #define PH7_TKWRD_FLOAT 0x20000 /* float: MUST BE A POWER OF TWO */ #define PH7_TKWRD_STRING 0x40000 /* string: MUST BE A POWER OF TWO */ #define PH7_TKWRD_OBJECT 0x80000 /* object: MUST BE A POWER OF TWO */ #define PH7_TKWRD_SEQ 0x100000 /* String string comparison operator: 'eq' equal MUST BE A POWER OF TWO */ #define PH7_TKWRD_SNE 0x200000 /* String string comparison operator: 'ne' not equal MUST BE A POWER OF TWO */ /* JSON encoding/decoding related definition */ enum json_err_code{ JSON_ERROR_NONE = 0, /* No error has occurred. */ JSON_ERROR_DEPTH, /* The maximum stack depth has been exceeded. */ JSON_ERROR_STATE_MISMATCH, /* Occurs with underflow or with the modes mismatch. */ JSON_ERROR_CTRL_CHAR, /* Control character error, possibly incorrectly encoded. */ JSON_ERROR_SYNTAX, /* Syntax error. */ JSON_ERROR_UTF8 /* Malformed UTF-8 characters */ }; /* The following constants can be combined to form options for json_encode(). */ #define JSON_HEX_TAG 0x01 /* All < and > are converted to \u003C and \u003E. */ #define JSON_HEX_AMP 0x02 /* All &s are converted to \u0026. */ #define JSON_HEX_APOS 0x04 /* All ' are converted to \u0027. */ #define JSON_HEX_QUOT 0x08 /* All " are converted to \u0022. */ #define JSON_FORCE_OBJECT 0x10 /* Outputs an object rather than an array */ #define JSON_NUMERIC_CHECK 0x20 /* Encodes numeric strings as numbers. */ #define JSON_BIGINT_AS_STRING 0x40 /* Not used */ #define JSON_PRETTY_PRINT 0x80 /* Use whitespace in returned data to format it.*/ #define JSON_UNESCAPED_SLASHES 0x100 /* Don't escape '/' */ #define JSON_UNESCAPED_UNICODE 0x200 /* Not used */ /* memobj.c function prototypes */ PH7_PRIVATE sxi32 PH7_MemObjDump(SyBlob *pOut,ph7_value *pObj,int ShowType,int nTab,int nDepth,int isRef); PH7_PRIVATE const char * PH7_MemObjTypeDump(ph7_value *pVal); PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1,ph7_value *pObj2,int bAddStore); PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1,ph7_value *pObj2,int bStrict,int iNest); PH7_PRIVATE sxi32 PH7_MemObjInitFromString(ph7_vm *pVm,ph7_value *pObj,const SyString *pVal); PH7_PRIVATE sxi32 PH7_MemObjInitFromArray(ph7_vm *pVm,ph7_value *pObj,ph7_hashmap *pArray); #if 0 /* Not used in the current release of the PH7 engine */ PH7_PRIVATE sxi32 PH7_MemObjInitFromReal(ph7_vm *pVm,ph7_value *pObj,ph7_real rVal); #endif PH7_PRIVATE sxi32 PH7_MemObjInitFromInt(ph7_vm *pVm,ph7_value *pObj,sxi64 iVal); PH7_PRIVATE sxi32 PH7_MemObjInitFromBool(ph7_vm *pVm,ph7_value *pObj,sxi32 iVal); PH7_PRIVATE sxi32 PH7_MemObjInit(ph7_vm *pVm,ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjStringAppend(ph7_value *pObj,const char *zData,sxu32 nLen); #if 0 /* Not used in the current release of the PH7 engine */ PH7_PRIVATE sxi32 PH7_MemObjStringFormat(ph7_value *pObj,const char *zFormat,va_list ap); #endif PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc,ph7_value *pDest); PH7_PRIVATE sxi32 PH7_MemObjLoad(ph7_value *pSrc,ph7_value *pDest); PH7_PRIVATE sxi32 PH7_MemObjRelease(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToNumeric(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjTryInteger(ph7_value *pObj); PH7_PRIVATE ProcMemObjCast PH7_MemObjCastMethod(sxi32 iFlags); PH7_PRIVATE sxi32 PH7_MemObjIsNumeric(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjIsEmpty(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToHashmap(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToObject(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToString(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToNull(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToReal(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToInteger(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToBool(ph7_value *pObj); PH7_PRIVATE sxi64 PH7_TokenValueToInt64(SyString *pData); /* lex.c function prototypes */ PH7_PRIVATE sxi32 PH7_TokenizeRawText(const char *zInput,sxu32 nLen,SySet *pOut); PH7_PRIVATE sxi32 PH7_TokenizePHP(const char *zInput,sxu32 nLen,sxu32 nLineStart,SySet *pOut); /* vm.c function prototypes */ PH7_PRIVATE void PH7_VmReleaseContextValue(ph7_context *pCtx,ph7_value *pValue); PH7_PRIVATE sxi32 PH7_VmInitFuncState(ph7_vm *pVm,ph7_vm_func *pFunc,const char *zName,sxu32 nByte, sxi32 iFlags,void *pUserData); PH7_PRIVATE sxi32 PH7_VmInstallUserFunction(ph7_vm *pVm,ph7_vm_func *pFunc,SyString *pName); PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame(ph7_vm *pVm,ph7_class_instance *pObj); PH7_PRIVATE sxi32 PH7_VmRefObjRemove(ph7_vm *pVm,sxu32 nIdx,SyHashEntry *pEntry,ph7_hashmap_node *pMapEntry); PH7_PRIVATE sxi32 PH7_VmRefObjInstall(ph7_vm *pVm,sxu32 nIdx,SyHashEntry *pEntry,ph7_hashmap_node *pMapEntry,sxi32 iFlags); PH7_PRIVATE sxi32 PH7_VmPushFilePath(ph7_vm *pVm,const char *zPath,int nLen,sxu8 bMain,sxi32 *pNew); PH7_PRIVATE ph7_class * PH7_VmExtractClass(ph7_vm *pVm,const char *zName,sxu32 nByte,sxi32 iLoadable,sxi32 iNest); PH7_PRIVATE sxi32 PH7_VmRegisterConstant(ph7_vm *pVm,const SyString *pName,ProcConstant xExpand,void *pUserData); PH7_PRIVATE sxi32 PH7_VmInstallForeignFunction(ph7_vm *pVm,const SyString *pName,ProchHostFunction xFunc,void *pUserData); PH7_PRIVATE sxi32 PH7_VmInstallClass(ph7_vm *pVm,ph7_class *pClass); PH7_PRIVATE sxi32 PH7_VmBlobConsumer(const void *pSrc,unsigned int nLen,void *pUserData); PH7_PRIVATE ph7_value * PH7_ReserveMemObj(ph7_vm *pVm); PH7_PRIVATE ph7_value * PH7_ReserveConstObj(ph7_vm *pVm,sxu32 *pIndex); PH7_PRIVATE sxi32 PH7_VmOutputConsume(ph7_vm *pVm,SyString *pString); PH7_PRIVATE sxi32 PH7_VmOutputConsumeAp(ph7_vm *pVm,const char *zFormat,va_list ap); PH7_PRIVATE sxi32 PH7_VmThrowErrorAp(ph7_vm *pVm,SyString *pFuncName,sxi32 iErr,const char *zFormat,va_list ap); PH7_PRIVATE sxi32 PH7_VmThrowError(ph7_vm *pVm,SyString *pFuncName,sxi32 iErr,const char *zMessage); PH7_PRIVATE void PH7_VmExpandConstantValue(ph7_value *pVal,void *pUserData); PH7_PRIVATE sxi32 PH7_VmDump(ph7_vm *pVm,ProcConsumer xConsumer,void *pUserData); PH7_PRIVATE sxi32 PH7_VmInit(ph7_vm *pVm,ph7 *pEngine); PH7_PRIVATE sxi32 PH7_VmConfigure(ph7_vm *pVm,sxi32 nOp,va_list ap); PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm); PH7_PRIVATE sxi32 PH7_VmRelease(ph7_vm *pVm); PH7_PRIVATE sxi32 PH7_VmReset(ph7_vm *pVm); PH7_PRIVATE sxi32 PH7_VmMakeReady(ph7_vm *pVm); PH7_PRIVATE sxu32 PH7_VmInstrLength(ph7_vm *pVm); PH7_PRIVATE VmInstr * PH7_VmPopInstr(ph7_vm *pVm); PH7_PRIVATE VmInstr * PH7_VmPeekInstr(ph7_vm *pVm); PH7_PRIVATE VmInstr * PH7_VmPeekNextInstr(ph7_vm *pVm); PH7_PRIVATE VmInstr *PH7_VmGetInstr(ph7_vm *pVm,sxu32 nIndex); PH7_PRIVATE SySet * PH7_VmGetByteCodeContainer(ph7_vm *pVm); PH7_PRIVATE sxi32 PH7_VmSetByteCodeContainer(ph7_vm *pVm,SySet *pContainer); PH7_PRIVATE sxi32 PH7_VmEmitInstr(ph7_vm *pVm,sxi32 iOp,sxi32 iP1,sxu32 iP2,void *p3,sxu32 *pIndex); PH7_PRIVATE sxu32 PH7_VmRandomNum(ph7_vm *pVm); PH7_PRIVATE sxi32 PH7_VmCallClassMethod(ph7_vm *pVm,ph7_class_instance *pThis,ph7_class_method *pMethod, ph7_value *pResult,int nArg,ph7_value **apArg); PH7_PRIVATE sxi32 PH7_VmCallUserFunction(ph7_vm *pVm,ph7_value *pFunc,int nArg,ph7_value **apArg,ph7_value *pResult); PH7_PRIVATE sxi32 PH7_VmCallUserFunctionAp(ph7_vm *pVm,ph7_value *pFunc,ph7_value *pResult,...); PH7_PRIVATE sxi32 PH7_VmUnsetMemObj(ph7_vm *pVm,sxu32 nObjIdx,int bForce); PH7_PRIVATE void PH7_VmRandomString(ph7_vm *pVm,char *zBuf,int nLen); PH7_PRIVATE ph7_class * PH7_VmPeekTopClass(ph7_vm *pVm); PH7_PRIVATE int PH7_VmIsCallable(ph7_vm *pVm,ph7_value *pValue,int CallInvoke); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE const ph7_io_stream * PH7_VmGetStreamDevice(ph7_vm *pVm,const char **pzDevice,int nByte); #endif /* PH7_DISABLE_BUILTIN_FUNC */ PH7_PRIVATE int PH7_Utf8Read( const unsigned char *z, /* First byte of UTF-8 character */ const unsigned char *zTerm, /* Pretend this byte is 0x00 */ const unsigned char **pzNext /* Write first byte past UTF-8 char here */ ); /* parse.c function prototypes */ PH7_PRIVATE int PH7_IsLangConstruct(sxu32 nKeyID,sxu8 bCheckFunc); PH7_PRIVATE sxi32 PH7_ExprMakeTree(ph7_gen_state *pGen,SySet *pExprNode,ph7_expr_node **ppRoot); PH7_PRIVATE sxi32 PH7_GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext); PH7_PRIVATE void PH7_DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd); PH7_PRIVATE const ph7_expr_op * PH7_ExprExtractOperator(SyString *pStr,SyToken *pLast); PH7_PRIVATE sxi32 PH7_ExprFreeTree(ph7_gen_state *pGen,SySet *pNodeSet); /* compile.c function prototypes */ PH7_PRIVATE ProcNodeConstruct PH7_GetNodeHandler(sxu32 nNodeType); PH7_PRIVATE sxi32 PH7_CompileLangConstruct(ph7_gen_state *pGen,sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileVariable(ph7_gen_state *pGen,sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileLiteral(ph7_gen_state *pGen,sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileSimpleString(ph7_gen_state *pGen,sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileString(ph7_gen_state *pGen,sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen,sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileList(ph7_gen_state *pGen,sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileAnnonFunc(ph7_gen_state *pGen,sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_InitCodeGenerator(ph7_vm *pVm,ProcConsumer xErr,void *pErrData); PH7_PRIVATE sxi32 PH7_ResetCodeGenerator(ph7_vm *pVm,ProcConsumer xErr,void *pErrData); PH7_PRIVATE sxi32 PH7_GenCompileError(ph7_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...); PH7_PRIVATE sxi32 PH7_CompileScript(ph7_vm *pVm,SyString *pScript,sxi32 iFlags); /* constant.c function prototypes */ PH7_PRIVATE void PH7_RegisterBuiltInConstant(ph7_vm *pVm); /* builtin.c function prototypes */ PH7_PRIVATE void PH7_RegisterBuiltInFunction(ph7_vm *pVm); /* hashmap.c function prototypes */ PH7_PRIVATE ph7_hashmap * PH7_NewHashmap(ph7_vm *pVm,sxu32 (*xIntHash)(sxi64),sxu32 (*xBlobHash)(const void *,sxu32)); PH7_PRIVATE sxi32 PH7_HashmapCreateSuper(ph7_vm *pVm); PH7_PRIVATE sxi32 PH7_HashmapRelease(ph7_hashmap *pMap,int FreeDS); PH7_PRIVATE void PH7_HashmapUnref(ph7_hashmap *pMap); PH7_PRIVATE sxi32 PH7_HashmapLookup(ph7_hashmap *pMap,ph7_value *pKey,ph7_hashmap_node **ppNode); PH7_PRIVATE sxi32 PH7_HashmapInsert(ph7_hashmap *pMap,ph7_value *pKey,ph7_value *pVal); PH7_PRIVATE sxi32 PH7_HashmapInsertByRef(ph7_hashmap *pMap,ph7_value *pKey,sxu32 nRefIdx); PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft,ph7_hashmap *pRight); PH7_PRIVATE void PH7_HashmapUnlinkNode(ph7_hashmap_node *pNode,int bRestore); PH7_PRIVATE sxi32 PH7_HashmapDup(ph7_hashmap *pSrc,ph7_hashmap *pDest); PH7_PRIVATE sxi32 PH7_HashmapCmp(ph7_hashmap *pLeft,ph7_hashmap *pRight,int bStrict); PH7_PRIVATE void PH7_HashmapResetLoopCursor(ph7_hashmap *pMap); PH7_PRIVATE ph7_hashmap_node * PH7_HashmapGetNextEntry(ph7_hashmap *pMap); PH7_PRIVATE void PH7_HashmapExtractNodeValue(ph7_hashmap_node *pNode,ph7_value *pValue,int bStore); PH7_PRIVATE void PH7_HashmapExtractNodeKey(ph7_hashmap_node *pNode,ph7_value *pKey); PH7_PRIVATE void PH7_RegisterHashmapFunctions(ph7_vm *pVm); PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut,ph7_hashmap *pMap,int ShowType,int nTab,int nDepth); PH7_PRIVATE sxi32 PH7_HashmapWalk(ph7_hashmap *pMap,int (*xWalk)(ph7_value *,ph7_value *,void *),void *pUserData); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE int PH7_HashmapValuesToSet(ph7_hashmap *pMap,SySet *pOut); /* builtin.c function prototypes */ PH7_PRIVATE sxi32 PH7_InputFormat(int (*xConsumer)(ph7_context *,const char *,int,void *), ph7_context *pCtx,const char *zIn,int nByte,int nArg,ph7_value **apArg,void *pUserData,int vf); PH7_PRIVATE sxi32 PH7_ProcessCsv(const char *zInput,int nByte,int delim,int encl, int escape,sxi32 (*xConsumer)(const char *,int,void *),void *pUserData); PH7_PRIVATE sxi32 PH7_CsvConsumer(const char *zToken,int nTokenLen,void *pUserData); PH7_PRIVATE sxi32 PH7_StripTagsFromString(ph7_context *pCtx,const char *zIn,int nByte,const char *zTaglist,int nTaglen); PH7_PRIVATE sxi32 PH7_ParseIniString(ph7_context *pCtx,const char *zIn,sxu32 nByte,int bProcessSection); #endif /* oo.c function prototypes */ PH7_PRIVATE ph7_class * PH7_NewRawClass(ph7_vm *pVm,const SyString *pName,sxu32 nLine); PH7_PRIVATE ph7_class_attr * PH7_NewClassAttr(ph7_vm *pVm,const SyString *pName,sxu32 nLine,sxi32 iProtection,sxi32 iFlags); PH7_PRIVATE ph7_class_method * PH7_NewClassMethod(ph7_vm *pVm,ph7_class *pClass,const SyString *pName,sxu32 nLine, sxi32 iProtection,sxi32 iFlags,sxi32 iFuncFlags); PH7_PRIVATE ph7_class_method * PH7_ClassExtractMethod(ph7_class *pClass,const char *zName,sxu32 nByte); PH7_PRIVATE ph7_class_attr * PH7_ClassExtractAttribute(ph7_class *pClass,const char *zName,sxu32 nByte); PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_class *pClass,ph7_class_attr *pAttr); PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass,ph7_class_method *pMeth); PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen,ph7_class *pSub,ph7_class *pBase); PH7_PRIVATE sxi32 PH7_ClassInterfaceInherit(ph7_class *pSub,ph7_class *pBase); PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_class *pMain,ph7_class *pInterface); PH7_PRIVATE ph7_class_instance * PH7_NewClassInstance(ph7_vm *pVm,ph7_class *pClass); PH7_PRIVATE ph7_class_instance * PH7_CloneClassInstance(ph7_class_instance *pSrc); PH7_PRIVATE sxi32 PH7_ClassInstanceCmp(ph7_class_instance *pLeft,ph7_class_instance *pRight,int bStrict,int iNest); PH7_PRIVATE void PH7_ClassInstanceUnref(ph7_class_instance *pThis); PH7_PRIVATE sxi32 PH7_ClassInstanceDump(SyBlob *pOut,ph7_class_instance *pThis,int ShowType,int nTab,int nDepth); PH7_PRIVATE sxi32 PH7_ClassInstanceCallMagicMethod(ph7_vm *pVm,ph7_class *pClass,ph7_class_instance *pThis,const char *zMethod, sxu32 nByte,const SyString *pAttrName); PH7_PRIVATE ph7_value * PH7_ClassInstanceExtractAttrValue(ph7_class_instance *pThis,VmClassAttr *pAttr); PH7_PRIVATE sxi32 PH7_ClassInstanceToHashmap(ph7_class_instance *pThis,ph7_hashmap *pMap); PH7_PRIVATE sxi32 PH7_ClassInstanceWalk(ph7_class_instance *pThis, int (*xWalk)(const char *,ph7_value *,void *),void *pUserData); PH7_PRIVATE ph7_value * PH7_ClassInstanceFetchAttr(ph7_class_instance *pThis,const SyString *pName); /* vfs.c */ #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE void * PH7_StreamOpenHandle(ph7_vm *pVm,const ph7_io_stream *pStream,const char *zFile, int iFlags,int use_include,ph7_value *pResource,int bPushInclude,int *pNew); PH7_PRIVATE sxi32 PH7_StreamReadWholeFile(void *pHandle,const ph7_io_stream *pStream,SyBlob *pOut); PH7_PRIVATE void PH7_StreamCloseHandle(const ph7_io_stream *pStream,void *pHandle); #endif /* PH7_DISABLE_BUILTIN_FUNC */ PH7_PRIVATE const char * PH7_ExtractDirName(const char *zPath,int nByte,int *pLen); PH7_PRIVATE sxi32 PH7_RegisterIORoutine(ph7_vm *pVm); PH7_PRIVATE const ph7_vfs * PH7_ExportBuiltinVfs(void); PH7_PRIVATE void * PH7_ExportStdin(ph7_vm *pVm); PH7_PRIVATE void * PH7_ExportStdout(ph7_vm *pVm); PH7_PRIVATE void * PH7_ExportStderr(ph7_vm *pVm); /* lib.c function prototypes */ #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyXMLParserInit(SyXMLParser *pParser,SyMemBackend *pAllocator,sxi32 iFlags); PH7_PRIVATE sxi32 SyXMLParserSetEventHandler(SyXMLParser *pParser, void *pUserData, ProcXMLStartTagHandler xStartTag, ProcXMLTextHandler xRaw, ProcXMLSyntaxErrorHandler xErr, ProcXMLStartDocument xStartDoc, ProcXMLEndTagHandler xEndTag, ProcXMLPIHandler xPi, ProcXMLEndDocument xEndDoc, ProcXMLDoctypeHandler xDoctype, ProcXMLNameSpaceStart xNameSpace, ProcXMLNameSpaceEnd xNameSpaceEnd ); PH7_PRIVATE sxi32 SyXMLProcess(SyXMLParser *pParser,const char *zInput,sxu32 nByte); PH7_PRIVATE sxi32 SyXMLParserRelease(SyXMLParser *pParser); PH7_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch,SyMemBackend *pAllocator,ProcHash xHash,ProcRawStrCmp xCmp); PH7_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch); PH7_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch); PH7_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch,SyArchiveEntry **ppEntry); PH7_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch,const char *zBuf,sxu32 nLen); #endif /* PH7_DISABLE_BUILTIN_FUNC */ #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn,sxu32 nLen,ProcConsumer xConsumer,void *pConsumerData); #endif /* PH7_DISABLE_BUILTIN_FUNC */ #ifndef PH7_DISABLE_BUILTIN_FUNC #ifndef PH7_DISABLE_HASH_FUNC PH7_PRIVATE sxu32 SyCrc32(const void *pSrc,sxu32 nLen); PH7_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len); PH7_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx); PH7_PRIVATE sxi32 MD5Init(MD5Context *pCtx); PH7_PRIVATE sxi32 SyMD5Compute(const void *pIn,sxu32 nLen,unsigned char zDigest[16]); PH7_PRIVATE void SHA1Init(SHA1Context *context); PH7_PRIVATE void SHA1Update(SHA1Context *context,const unsigned char *data,unsigned int len); PH7_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]); PH7_PRIVATE sxi32 SySha1Compute(const void *pIn,sxu32 nLen,unsigned char zDigest[20]); #endif #endif /* PH7_DISABLE_BUILTIN_FUNC */ PH7_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx,void *pBuf,sxu32 nLen); PH7_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx,ProcRandomSeed xSeed,void *pUserData); PH7_PRIVATE sxu32 SyBufferFormat(char *zBuf,sxu32 nLen,const char *zFormat,...); PH7_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob,const char *zFormat,va_list ap); PH7_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob,const char *zFormat,...); PH7_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer,void *pData,const char *zFormat,...); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE const char *SyTimeGetMonth(sxi32 iMonth); PH7_PRIVATE const char *SyTimeGetDay(sxi32 iDay); #endif /* PH7_DISABLE_BUILTIN_FUNC */ PH7_PRIVATE sxi32 SyUriDecode(const char *zSrc,sxu32 nLen,ProcConsumer xConsumer,void *pUserData,int bUTF8); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyUriEncode(const char *zSrc,sxu32 nLen,ProcConsumer xConsumer,void *pUserData); #endif PH7_PRIVATE sxi32 SyLexRelease(SyLex *pLex); PH7_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex,const char *zInput,sxu32 nLen,void *pCtxData,ProcSort xSort,ProcCmp xCmp); PH7_PRIVATE sxi32 SyLexInit(SyLex *pLex,SySet *pSet,ProcTokenizer xTokenizer,void *pUserData); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyBase64Decode(const char *zB64,sxu32 nLen,ProcConsumer xConsumer,void *pUserData); PH7_PRIVATE sxi32 SyBase64Encode(const char *zSrc,sxu32 nLen,ProcConsumer xConsumer,void *pUserData); #endif /* PH7_DISABLE_BUILTIN_FUNC */ PH7_PRIVATE sxi32 SyStrToReal(const char *zSrc,sxu32 nLen,void *pOutVal,const char **zRest); PH7_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc,sxu32 nLen,void *pOutVal,const char **zRest); PH7_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc,sxu32 nLen,void *pOutVal,const char **zRest); PH7_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc,sxu32 nLen,void *pOutVal,const char **zRest); PH7_PRIVATE sxi32 SyHexToint(sxi32 c); PH7_PRIVATE sxi32 SyStrToInt64(const char *zSrc,sxu32 nLen,void *pOutVal,const char **zRest); PH7_PRIVATE sxi32 SyStrToInt32(const char *zSrc,sxu32 nLen,void *pOutVal,const char **zRest); PH7_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc,sxu32 nLen,sxu8 *pReal,const char **pzTail); PH7_PRIVATE SyHashEntry *SyHashLastEntry(SyHash *pHash); PH7_PRIVATE sxi32 SyHashInsert(SyHash *pHash,const void *pKey,sxu32 nKeyLen,void *pUserData); PH7_PRIVATE sxi32 SyHashForEach(SyHash *pHash,sxi32(*xStep)(SyHashEntry *,void *),void *pUserData); PH7_PRIVATE SyHashEntry *SyHashGetNextEntry(SyHash *pHash); PH7_PRIVATE sxi32 SyHashResetLoopCursor(SyHash *pHash); PH7_PRIVATE sxi32 SyHashDeleteEntry2(SyHashEntry *pEntry); PH7_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash,const void *pKey,sxu32 nKeyLen,void **ppUserData); PH7_PRIVATE SyHashEntry *SyHashGet(SyHash *pHash,const void *pKey,sxu32 nKeyLen); PH7_PRIVATE sxi32 SyHashRelease(SyHash *pHash); PH7_PRIVATE sxi32 SyHashInit(SyHash *pHash,SyMemBackend *pAllocator,ProcHash xHash,ProcCmp xCmp); PH7_PRIVATE sxu32 SyStrHash(const void *pSrc,sxu32 nLen); PH7_PRIVATE void *SySetAt(SySet *pSet,sxu32 nIdx); PH7_PRIVATE void *SySetPop(SySet *pSet); PH7_PRIVATE void *SySetPeek(SySet *pSet); PH7_PRIVATE sxi32 SySetRelease(SySet *pSet); PH7_PRIVATE sxi32 SySetReset(SySet *pSet); PH7_PRIVATE sxi32 SySetResetCursor(SySet *pSet); PH7_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet,void **ppEntry); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE void * SySetPeekCurrentEntry(SySet *pSet); #endif /* PH7_DISABLE_BUILTIN_FUNC */ PH7_PRIVATE sxi32 SySetTruncate(SySet *pSet,sxu32 nNewSize); PH7_PRIVATE sxi32 SySetAlloc(SySet *pSet,sxi32 nItem); PH7_PRIVATE sxi32 SySetPut(SySet *pSet,const void *pItem); PH7_PRIVATE sxi32 SySetInit(SySet *pSet,SyMemBackend *pAllocator,sxu32 ElemSize); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyBlobSearch(const void *pBlob,sxu32 nLen,const void *pPattern,sxu32 pLen,sxu32 *pOfft); #endif PH7_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob); PH7_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob); PH7_PRIVATE sxi32 SyBlobCmp(SyBlob *pLeft,SyBlob *pRight); PH7_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc,SyBlob *pDest); PH7_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob); PH7_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob,const void *pData,sxu32 nSize); PH7_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob,const void *pData,sxu32 nByte); PH7_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob,SyMemBackend *pAllocator); PH7_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob,void *pBuffer,sxu32 nSize); PH7_PRIVATE char *SyMemBackendStrDup(SyMemBackend *pBackend,const char *zSrc,sxu32 nSize); PH7_PRIVATE void *SyMemBackendDup(SyMemBackend *pBackend,const void *pSrc,sxu32 nSize); PH7_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend); PH7_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend,const SyMemMethods *pMethods,ProcMemError xMemErr,void *pUserData); PH7_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend,ProcMemError xMemErr,void *pUserData); PH7_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,SyMemBackend *pParent); #if 0 /* Not used in the current release of the PH7 engine */ PH7_PRIVATE void *SyMemBackendPoolRealloc(SyMemBackend *pBackend,void *pOld,sxu32 nByte); #endif PH7_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend,void *pChunk); PH7_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend,sxu32 nByte); PH7_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend,void *pChunk); PH7_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend,void *pOld,sxu32 nByte); PH7_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend,sxu32 nByte); #if defined(PH7_ENABLE_THREADS) PH7_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend,const SyMutexMethods *pMethods); PH7_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend); #endif PH7_PRIVATE sxu32 SyMemcpy(const void *pSrc,void *pDest,sxu32 nLen); PH7_PRIVATE sxi32 SyMemcmp(const void *pB1,const void *pB2,sxu32 nSize); PH7_PRIVATE void SyZero(void *pSrc,sxu32 nSize); PH7_PRIVATE sxi32 SyStrnicmp(const char *zLeft,const char *zRight,sxu32 SLen); PH7_PRIVATE sxi32 SyStrnmicmp(const void *pLeft, const void *pRight,sxu32 SLen); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyStrncmp(const char *zLeft,const char *zRight,sxu32 nLen); #endif PH7_PRIVATE sxi32 SyByteListFind(const char *zSrc,sxu32 nLen,const char *zList,sxu32 *pFirstPos); #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyByteFind2(const char *zStr,sxu32 nLen,sxi32 c,sxu32 *pPos); #endif PH7_PRIVATE sxi32 SyByteFind(const char *zStr,sxu32 nLen,sxi32 c,sxu32 *pPos); PH7_PRIVATE sxu32 SyStrlen(const char *zSrc); #if defined(PH7_ENABLE_THREADS) PH7_PRIVATE const SyMutexMethods *SyMutexExportMethods(void); PH7_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend,const SyMutexMethods *pMethods); PH7_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend); #endif #endif /* __PH7INT_H__ */ /* * ---------------------------------------------------------- * File: vm.c * MD5: fed926a5df137d2896badd8911a0b752 * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: vm.c v1.4 FreeBSD 2012-09-10 00:06 stable $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* * The code in this file implements execution method of the PH7 Virtual Machine. * The PH7 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program * which is then executed by the virtual machine implemented here to do the work of the PHP * statements. * PH7 bytecode programs are similar in form to assembly language. The program consists * of a linear sequence of operations .Each operation has an opcode and 3 operands. * Operands P1 and P2 are integers where the first is signed while the second is unsigned. * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually * the jump destination used by the OP_JMP,OP_JZ,OP_JNZ,... instructions. * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands. * Computation results are stored on a stack. Each entry on the stack is of type ph7_value. * PH7 uses the ph7_value object to represent all values that can be stored in a PHP variable. * Since PHP uses dynamic typing for the values it stores. Values stored in ph7_value objects * can be integers,floating point values,strings,arrays,class instances (object in the PHP jargon) * and so on. * Internally,the PH7 virtual machine manipulates nearly all PHP values as ph7_values structures. * Each ph7_value may cache multiple representations(string,integer etc.) of the same value. * An implicit conversion from one type to the other occurs as necessary. * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does * the work of interpreting a PH7 bytecode program. But other routines are also provided * to help in building up a program instruction by instruction. Also note that sepcial * functions that need access to the underlying virtual machine details such as [die()], * [func_get_args()],[call_user_func()],[ob_start()] and many more are implemented here. */ /* * Each active virtual machine frame is represented by an instance * of the following structure. * VM Frame hold local variables and other stuff related to function call. */ struct VmFrame { VmFrame *pParent; /* Parent frame or NULL if global scope */ void *pUserData; /* Upper layer private data associated with this frame */ ph7_class_instance *pThis; /* Current class instance [i.e: the '$this' variable].NULL otherwise */ SySet sLocal; /* Local variables container (VmSlot instance) */ ph7_vm *pVm; /* VM that own this frame */ SyHash hVar; /* Variable hashtable for fast lookup */ SySet sArg; /* Function arguments container */ SySet sRef; /* Local reference table (VmSlot instance) */ sxi32 iFlags; /* Frame configuration flags (See below)*/ sxu32 iExceptionJump; /* Exception jump destination */ }; #define VM_FRAME_EXCEPTION 0x01 /* Special Exception frame */ #define VM_FRAME_THROW 0x02 /* An exception was thrown */ #define VM_FRAME_CATCH 0x04 /* Catch frame */ /* * When a user defined variable is released (via manual unset($x) or garbage collected) * memory object index is stored in an instance of the following structure and put * in the free object table so that it can be reused again without allocating * a new memory object. */ typedef struct VmSlot VmSlot; struct VmSlot { sxu32 nIdx; /* Index in pVm->aMemObj[] */ void *pUserData; /* Upper-layer private data */ }; /* * An entry in the reference table is represented by an instance of the * follwoing table. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ struct VmRefObj { SySet aReference; /* Table of references to this memory object */ SySet aArrEntries; /* Foreign hashmap entries [i.e: array(&$a) ] */ sxu32 nIdx; /* Referenced object index */ sxi32 iFlags; /* Configuration flags */ VmRefObj *pNextCollide,*pPrevCollide; /* Collision link */ VmRefObj *pNext,*pPrev; /* List of all referenced objects */ }; #define VM_REF_IDX_KEEP 0x001 /* Do not restore the memory object to the free list */ /* * Output control buffer entry. * Refer to the implementation of [ob_start()] for more information. */ typedef struct VmObEntry VmObEntry; struct VmObEntry { ph7_value sCallback; /* User defined callback */ SyBlob sOB; /* Output buffer consumer */ }; /* * Each installed shutdown callback (registered using [register_shutdown_function()] ) * is stored in an instance of the following structure. * Refer to the implementation of [register_shutdown_function(()] for more information. */ typedef struct VmShutdownCB VmShutdownCB; struct VmShutdownCB { ph7_value sCallback; /* Shutdown callback */ ph7_value aArg[10]; /* Callback arguments (10 maximum arguments) */ int nArg; /* Total number of given arguments */ }; /* Uncaught exception code value */ #define PH7_EXCEPTION -255 /* * Each parsed URI is recorded and stored in an instance of the following structure. * This structure and it's related routines are taken verbatim from the xHT project * [A modern embeddable HTTP engine implementing all the RFC2616 methods] * the xHT project is developed internally by Symisc Systems. */ typedef struct SyhttpUri SyhttpUri; struct SyhttpUri { SyString sHost; /* Hostname or IP address */ SyString sPort; /* Port number */ SyString sPath; /* Mandatory resource path passed verbatim (Not decoded) */ SyString sQuery; /* Query part */ SyString sFragment; /* Fragment part */ SyString sScheme; /* Scheme */ SyString sUser; /* Username */ SyString sPass; /* Password */ SyString sRaw; /* Raw URI */ }; /* * An instance of the following structure is used to record all MIME headers seen * during a HTTP interaction. * This structure and it's related routines are taken verbatim from the xHT project * [A modern embeddable HTTP engine implementing all the RFC2616 methods] * the xHT project is developed internally by Symisc Systems. */ typedef struct SyhttpHeader SyhttpHeader; struct SyhttpHeader { SyString sName; /* Header name [i.e:"Content-Type","Host","User-Agent"]. NOT NUL TERMINATED */ SyString sValue; /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */ }; /* * Supported HTTP methods. */ #define HTTP_METHOD_GET 1 /* GET */ #define HTTP_METHOD_HEAD 2 /* HEAD */ #define HTTP_METHOD_POST 3 /* POST */ #define HTTP_METHOD_PUT 4 /* PUT */ #define HTTP_METHOD_OTHR 5 /* Other HTTP methods [i.e: DELETE,TRACE,OPTIONS...]*/ /* * Supported HTTP protocol version. */ #define HTTP_PROTO_10 1 /* HTTP/1.0 */ #define HTTP_PROTO_11 2 /* HTTP/1.1 */ /* * Register a constant and it's associated expansion callback so that * it can be expanded from the target PHP program. * The constant expansion mechanism under PH7 is extremely powerful yet * simple and work as follows: * Each registered constant have a C procedure associated with it. * This procedure known as the constant expansion callback is responsible * of expanding the invoked constant to the desired value,for example: * The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI). * The "__OS__" constant procedure expands to the name of the host Operating Systems * (Windows,Linux,...) and so on. * Please refer to the official documentation for additional information. */ PH7_PRIVATE sxi32 PH7_VmRegisterConstant( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Constant name */ ProcConstant xExpand, /* Constant expansion callback */ void *pUserData /* Last argument to xExpand() */ ) { ph7_constant *pCons; SyHashEntry *pEntry; char *zDupName; sxi32 rc; pEntry = SyHashGet(&pVm->hConstant,(const void *)pName->zString,pName->nByte); if( pEntry ){ /* Overwrite the old definition and return immediately */ pCons = (ph7_constant *)pEntry->pUserData; pCons->xExpand = xExpand; pCons->pUserData = pUserData; return SXRET_OK; } /* Allocate a new constant instance */ pCons = (ph7_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_constant)); if( pCons == 0 ){ return 0; } /* Duplicate constant name */ zDupName = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte); if( zDupName == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pCons); return 0; } /* Install the constant */ SyStringInitFromBuf(&pCons->sName,zDupName,pName->nByte); pCons->xExpand = xExpand; pCons->pUserData = pUserData; rc = SyHashInsert(&pVm->hConstant,(const void *)zDupName,SyStringLength(&pCons->sName),pCons); if( rc != SXRET_OK ){ SyMemBackendFree(&pVm->sAllocator,zDupName); SyMemBackendPoolFree(&pVm->sAllocator,pCons); return rc; } /* All done,constant can be invoked from PHP code */ return SXRET_OK; } /* * Allocate a new foreign function instance. * This function return SXRET_OK on success. Any other * return value indicates failure. * Please refer to the official documentation for an introduction to * the foreign function mechanism. */ static sxi32 PH7_NewForeignFunction( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Foreign function name */ ProchHostFunction xFunc, /* Foreign function implementation */ void *pUserData, /* Foreign function private data */ ph7_user_func **ppOut /* OUT: VM image of the foreign function */ ) { ph7_user_func *pFunc; char *zDup; /* Allocate a new user function */ pFunc = (ph7_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_user_func)); if( pFunc == 0 ){ return SXERR_MEM; } /* Duplicate function name */ zDup = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte); if( zDup == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pFunc); return SXERR_MEM; } /* Zero the structure */ SyZero(pFunc,sizeof(ph7_user_func)); /* Initialize structure fields */ SyStringInitFromBuf(&pFunc->sName,zDup,pName->nByte); pFunc->pVm = pVm; pFunc->xFunc = xFunc; pFunc->pUserData = pUserData; SySetInit(&pFunc->aAux,&pVm->sAllocator,sizeof(ph7_aux_data)); /* Write a pointer to the new function */ *ppOut = pFunc; return SXRET_OK; } /* * Install a foreign function and it's associated callback so that * it can be invoked from the target PHP code. * This function return SXRET_OK on successful registration. Any other * return value indicates failure. * Please refer to the official documentation for an introduction to * the foreign function mechanism. */ PH7_PRIVATE sxi32 PH7_VmInstallForeignFunction( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Foreign function name */ ProchHostFunction xFunc, /* Foreign function implementation */ void *pUserData /* Foreign function private data */ ) { ph7_user_func *pFunc; SyHashEntry *pEntry; sxi32 rc; /* Overwrite any previously registered function with the same name */ pEntry = SyHashGet(&pVm->hHostFunction,pName->zString,pName->nByte); if( pEntry ){ pFunc = (ph7_user_func *)pEntry->pUserData; pFunc->pUserData = pUserData; pFunc->xFunc = xFunc; SySetReset(&pFunc->aAux); return SXRET_OK; } /* Create a new user function */ rc = PH7_NewForeignFunction(&(*pVm),&(*pName),xFunc,pUserData,&pFunc); if( rc != SXRET_OK ){ return rc; } /* Install the function in the corresponding hashtable */ rc = SyHashInsert(&pVm->hHostFunction,SyStringData(&pFunc->sName),pName->nByte,pFunc); if( rc != SXRET_OK ){ SyMemBackendFree(&pVm->sAllocator,(void *)SyStringData(&pFunc->sName)); SyMemBackendPoolFree(&pVm->sAllocator,pFunc); return rc; } /* User function successfully installed */ return SXRET_OK; } /* * Initialize a VM function. */ PH7_PRIVATE sxi32 PH7_VmInitFuncState( ph7_vm *pVm, /* Target VM */ ph7_vm_func *pFunc, /* Target Fucntion */ const char *zName, /* Function name */ sxu32 nByte, /* zName length */ sxi32 iFlags, /* Configuration flags */ void *pUserData /* Function private data */ ) { /* Zero the structure */ SyZero(pFunc,sizeof(ph7_vm_func)); /* Initialize structure fields */ /* Arguments container */ SySetInit(&pFunc->aArgs,&pVm->sAllocator,sizeof(ph7_vm_func_arg)); /* Static variable container */ SySetInit(&pFunc->aStatic,&pVm->sAllocator,sizeof(ph7_vm_func_static_var)); /* Bytecode container */ SySetInit(&pFunc->aByteCode,&pVm->sAllocator,sizeof(VmInstr)); /* Preallocate some instruction slots */ SySetAlloc(&pFunc->aByteCode,0x10); /* Closure environment */ SySetInit(&pFunc->aClosureEnv,&pVm->sAllocator,sizeof(ph7_vm_func_closure_env)); pFunc->iFlags = iFlags; pFunc->pUserData = pUserData; SyStringInitFromBuf(&pFunc->sName,zName,nByte); return SXRET_OK; } /* * Install a user defined function in the corresponding VM container. */ PH7_PRIVATE sxi32 PH7_VmInstallUserFunction( ph7_vm *pVm, /* Target VM */ ph7_vm_func *pFunc, /* Target function */ SyString *pName /* Function name */ ) { SyHashEntry *pEntry; sxi32 rc; if( pName == 0 ){ /* Use the built-in name */ pName = &pFunc->sName; } /* Check for duplicates (functions with the same name) first */ pEntry = SyHashGet(&pVm->hFunction,pName->zString,pName->nByte); if( pEntry ){ ph7_vm_func *pLink = (ph7_vm_func *)pEntry->pUserData; if( pLink != pFunc ){ /* Link */ pFunc->pNextName = pLink; pEntry->pUserData = pFunc; } return SXRET_OK; } /* First time seen */ pFunc->pNextName = 0; rc = SyHashInsert(&pVm->hFunction,pName->zString,pName->nByte,pFunc); return rc; } /* * Install a user defined class in the corresponding VM container. */ PH7_PRIVATE sxi32 PH7_VmInstallClass( ph7_vm *pVm, /* Target VM */ ph7_class *pClass /* Target Class */ ) { SyString *pName = &pClass->sName; SyHashEntry *pEntry; sxi32 rc; /* Check for duplicates */ pEntry = SyHashGet(&pVm->hClass,(const void *)pName->zString,pName->nByte); if( pEntry ){ ph7_class *pLink = (ph7_class *)pEntry->pUserData; /* Link entry with the same name */ pClass->pNextName = pLink; pEntry->pUserData = pClass; return SXRET_OK; } pClass->pNextName = 0; /* Perform a simple hashtable insertion */ rc = SyHashInsert(&pVm->hClass,(const void *)pName->zString,pName->nByte,pClass); return rc; } /* * Instruction builder interface. */ PH7_PRIVATE sxi32 PH7_VmEmitInstr( ph7_vm *pVm, /* Target VM */ sxi32 iOp, /* Operation to perform */ sxi32 iP1, /* First operand */ sxu32 iP2, /* Second operand */ void *p3, /* Third operand */ sxu32 *pIndex /* Instruction index. NULL otherwise */ ) { VmInstr sInstr; sxi32 rc; /* Fill the VM instruction */ sInstr.iOp = (sxu8)iOp; sInstr.iP1 = iP1; sInstr.iP2 = iP2; sInstr.p3 = p3; if( pIndex ){ /* Instruction index in the bytecode array */ *pIndex = SySetUsed(pVm->pByteContainer); } /* Finally,record the instruction */ rc = SySetPut(pVm->pByteContainer,(const void *)&sInstr); if( rc != SXRET_OK ){ PH7_GenCompileError(&pVm->sCodeGen,E_ERROR,1,"Fatal,Cannot emit instruction due to a memory failure"); /* Fall throw */ } return rc; } /* * Swap the current bytecode container with the given one. */ PH7_PRIVATE sxi32 PH7_VmSetByteCodeContainer(ph7_vm *pVm,SySet *pContainer) { if( pContainer == 0 ){ /* Point to the default container */ pVm->pByteContainer = &pVm->aByteCode; }else{ /* Change container */ pVm->pByteContainer = &(*pContainer); } return SXRET_OK; } /* * Return the current bytecode container. */ PH7_PRIVATE SySet * PH7_VmGetByteCodeContainer(ph7_vm *pVm) { return pVm->pByteContainer; } /* * Extract the VM instruction rooted at nIndex. */ PH7_PRIVATE VmInstr * PH7_VmGetInstr(ph7_vm *pVm,sxu32 nIndex) { VmInstr *pInstr; pInstr = (VmInstr *)SySetAt(pVm->pByteContainer,nIndex); return pInstr; } /* * Return the total number of VM instructions recorded so far. */ PH7_PRIVATE sxu32 PH7_VmInstrLength(ph7_vm *pVm) { return SySetUsed(pVm->pByteContainer); } /* * Pop the last VM instruction. */ PH7_PRIVATE VmInstr * PH7_VmPopInstr(ph7_vm *pVm) { return (VmInstr *)SySetPop(pVm->pByteContainer); } /* * Peek the last VM instruction. */ PH7_PRIVATE VmInstr * PH7_VmPeekInstr(ph7_vm *pVm) { return (VmInstr *)SySetPeek(pVm->pByteContainer); } PH7_PRIVATE VmInstr * PH7_VmPeekNextInstr(ph7_vm *pVm) { VmInstr *aInstr; sxu32 n; n = SySetUsed(pVm->pByteContainer); if( n < 2 ){ return 0; } aInstr = (VmInstr *)SySetBasePtr(pVm->pByteContainer); return &aInstr[n - 2]; } /* * Allocate a new virtual machine frame. */ static VmFrame * VmNewFrame( ph7_vm *pVm, /* Target VM */ void *pUserData, /* Upper-layer private data */ ph7_class_instance *pThis /* Top most class instance [i.e: Object in the PHP jargon]. NULL otherwise */ ) { VmFrame *pFrame; /* Allocate a new vm frame */ pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(VmFrame)); if( pFrame == 0 ){ return 0; } /* Zero the structure */ SyZero(pFrame,sizeof(VmFrame)); /* Initialize frame fields */ pFrame->pUserData = pUserData; pFrame->pThis = pThis; pFrame->pVm = pVm; SyHashInit(&pFrame->hVar,&pVm->sAllocator,0,0); SySetInit(&pFrame->sArg,&pVm->sAllocator,sizeof(VmSlot)); SySetInit(&pFrame->sLocal,&pVm->sAllocator,sizeof(VmSlot)); SySetInit(&pFrame->sRef,&pVm->sAllocator,sizeof(VmSlot)); return pFrame; } /* * Enter a VM frame. */ static sxi32 VmEnterFrame( ph7_vm *pVm, /* Target VM */ void *pUserData, /* Upper-layer private data */ ph7_class_instance *pThis, /* Top most class instance [i.e: Object in the PHP jargon]. NULL otherwise */ VmFrame **ppFrame /* OUT: Top most active frame */ ) { VmFrame *pFrame; /* Allocate a new frame */ pFrame = VmNewFrame(&(*pVm),pUserData,pThis); if( pFrame == 0 ){ return SXERR_MEM; } /* Link to the list of active VM frame */ pFrame->pParent = pVm->pFrame; pVm->pFrame = pFrame; if( ppFrame ){ /* Write a pointer to the new VM frame */ *ppFrame = pFrame; } return SXRET_OK; } /* * Link a foreign variable with the TOP most active frame. * Refer to the PH7_OP_UPLINK instruction implementation for more * information. */ static sxi32 VmFrameLink(ph7_vm *pVm,SyString *pName) { VmFrame *pTarget,*pFrame; SyHashEntry *pEntry = 0; sxi32 rc; /* Point to the upper frame */ pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } pTarget = pFrame; pFrame = pTarget->pParent; while( pFrame ){ if( (pFrame->iFlags & VM_FRAME_EXCEPTION) == 0 ){ /* Query the current frame */ pEntry = SyHashGet(&pFrame->hVar,(const void *)pName->zString,pName->nByte); if( pEntry ){ /* Variable found */ break; } } /* Point to the upper frame */ pFrame = pFrame->pParent; } if( pEntry == 0 ){ /* Inexistant variable */ return SXERR_NOTFOUND; } /* Link to the current frame */ rc = SyHashInsert(&pTarget->hVar,pEntry->pKey,pEntry->nKeyLen,pEntry->pUserData); if( rc == SXRET_OK ){ sxu32 nIdx; nIdx = SX_PTR_TO_INT(pEntry->pUserData); PH7_VmRefObjInstall(&(*pVm),nIdx,SyHashLastEntry(&pTarget->hVar),0,0); } return rc; } /* * Leave the top-most active frame. */ static void VmLeaveFrame(ph7_vm *pVm) { VmFrame *pFrame = pVm->pFrame; if( pFrame ){ /* Unlink from the list of active VM frame */ pVm->pFrame = pFrame->pParent; if( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) == 0 ){ VmSlot *aSlot; sxu32 n; /* Restore local variable to the free pool so that they can be reused again */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal); for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){ /* Unset the local variable */ PH7_VmUnsetMemObj(&(*pVm),aSlot[n].nIdx,FALSE); } /* Remove local reference */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sRef); for(n = 0 ; n < SySetUsed(&pFrame->sRef) ; ++n ){ PH7_VmRefObjRemove(&(*pVm),aSlot[n].nIdx,(SyHashEntry *)aSlot[n].pUserData,0); } } /* Release internal containers */ SyHashRelease(&pFrame->hVar); SySetRelease(&pFrame->sArg); SySetRelease(&pFrame->sLocal); SySetRelease(&pFrame->sRef); /* Release the whole structure */ SyMemBackendPoolFree(&pVm->sAllocator,pFrame); } } /* * Compare two functions signature and return the comparison result. */ static int VmOverloadCompare(SyString *pFirst,SyString *pSecond) { const char *zSend = &pSecond->zString[pSecond->nByte]; const char *zFend = &pFirst->zString[pFirst->nByte]; const char *zSin = pSecond->zString; const char *zFin = pFirst->zString; const char *zPtr = zFin; for(;;){ if( zFin >= zFend || zSin >= zSend ){ break; } if( zFin[0] != zSin[0] ){ /* mismatch */ break; } zFin++; zSin++; } return (int)(zFin-zPtr); } /* * Select the appropriate VM function for the current call context. * This is the implementation of the powerful 'function overloading' feature * introduced by the version 2 of the PH7 engine. * Refer to the official documentation for more information. */ static ph7_vm_func * VmOverload( ph7_vm *pVm, /* Target VM */ ph7_vm_func *pList, /* Linked list of candidates for overloading */ ph7_value *aArg, /* Array of passed arguments */ int nArg /* Total number of passed arguments */ ) { int iTarget,i,j,iCur,iMax; ph7_vm_func *apSet[10]; /* Maximum number of candidates */ ph7_vm_func *pLink; SyString sArgSig; SyBlob sSig; pLink = pList; i = 0; /* Put functions expecting the same number of passed arguments */ while( i < (int)SX_ARRAYSIZE(apSet) ){ if( pLink == 0 ){ break; } if( (int)SySetUsed(&pLink->aArgs) == nArg ){ /* Candidate for overloading */ apSet[i++] = pLink; } /* Point to the next entry */ pLink = pLink->pNextName; } if( i < 1 ){ /* No candidates,return the head of the list */ return pList; } if( nArg < 1 || i < 2 ){ /* Return the only candidate */ return apSet[0]; } /* Calculate function signature */ SyBlobInit(&sSig,&pVm->sAllocator); for( j = 0 ; j < nArg ; j++ ){ int c = 'n'; /* null */ if( aArg[j].iFlags & MEMOBJ_HASHMAP ){ /* Hashmap */ c = 'h'; }else if( aArg[j].iFlags & MEMOBJ_BOOL ){ /* bool */ c = 'b'; }else if( aArg[j].iFlags & MEMOBJ_INT ){ /* int */ c = 'i'; }else if( aArg[j].iFlags & MEMOBJ_STRING ){ /* String */ c = 's'; }else if( aArg[j].iFlags & MEMOBJ_REAL ){ /* Float */ c = 'f'; }else if( aArg[j].iFlags & MEMOBJ_OBJ ){ /* Class instance */ ph7_class *pClass = ((ph7_class_instance *)aArg[j].x.pOther)->pClass; SyString *pName = &pClass->sName; SyBlobAppend(&sSig,(const void *)pName->zString,pName->nByte); c = -1; } if( c > 0 ){ SyBlobAppend(&sSig,(const void *)&c,sizeof(char)); } } SyStringInitFromBuf(&sArgSig,SyBlobData(&sSig),SyBlobLength(&sSig)); iTarget = 0; iMax = -1; /* Select the appropriate function */ for( j = 0 ; j < i ; j++ ){ /* Compare the two signatures */ iCur = VmOverloadCompare(&sArgSig,&apSet[j]->sSignature); if( iCur > iMax ){ iMax = iCur; iTarget = j; } } SyBlobRelease(&sSig); /* Appropriate function for the current call context */ return apSet[iTarget]; } /* Forward declaration */ static sxi32 VmLocalExec(ph7_vm *pVm,SySet *pByteCode,ph7_value *pResult); static sxi32 VmErrorFormat(ph7_vm *pVm,sxi32 iErr,const char *zFormat,...); /* * Mount a compiled class into the freshly created vitual machine so that * it can be instanciated from the executed PHP script. */ static sxi32 VmMountUserClass( ph7_vm *pVm, /* Target VM */ ph7_class *pClass /* Class to be mounted */ ) { ph7_class_method *pMeth; ph7_class_attr *pAttr; SyHashEntry *pEntry; sxi32 rc; /* Reset the loop cursor */ SyHashResetLoopCursor(&pClass->hAttr); /* Process only static and constant attribute */ while( (pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0 ){ /* Extract the current attribute */ pAttr = (ph7_class_attr *)pEntry->pUserData; if( pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC) ){ ph7_value *pMemObj; /* Reserve a memory object for this constant/static attribute */ pMemObj = PH7_ReserveMemObj(&(*pVm)); if( pMemObj == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Cannot reserve a memory object for class attribute '%z->%z' due to a memory failure", &pClass->sName,&pAttr->sName ); return SXERR_MEM; } if( SySetUsed(&pAttr->aByteCode) > 0 ){ /* Initialize attribute default value (any complex expression) */ VmLocalExec(&(*pVm),&pAttr->aByteCode,pMemObj); } /* Record attribute index */ pAttr->nIdx = pMemObj->nIdx; /* Install static attribute in the reference table */ PH7_VmRefObjInstall(&(*pVm),pMemObj->nIdx,0,0,VM_REF_IDX_KEEP); } } /* Install class methods */ if( pClass->iFlags & PH7_CLASS_INTERFACE ){ /* Do not mount interface methods since they are signatures only. */ return SXRET_OK; } /* Create constructor alias if not yet done */ if( SyHashGet(&pClass->hMethod,"__construct",sizeof("__construct")-1) == 0 ){ /* User constructor with the same base class name */ pEntry = SyHashGet(&pClass->hMethod,SyStringData(&pClass->sName),SyStringLength(&pClass->sName)); if( pEntry ){ pMeth = (ph7_class_method *)pEntry->pUserData; /* Create the alias */ SyHashInsert(&pClass->hMethod,"__construct",sizeof("__construct")-1,pMeth); } } /* Install the methods now */ SyHashResetLoopCursor(&pClass->hMethod); while((pEntry = SyHashGetNextEntry(&pClass->hMethod)) != 0 ){ pMeth = (ph7_class_method *)pEntry->pUserData; if( (pMeth->iFlags & PH7_CLASS_ATTR_ABSTRACT) == 0 ){ rc = PH7_VmInstallUserFunction(&(*pVm),&pMeth->sFunc,&pMeth->sVmName); if( rc != SXRET_OK ){ return rc; } } } return SXRET_OK; } /* * Allocate a private frame for attributes of the given * class instance (Object in the PHP jargon). */ PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame( ph7_vm *pVm, /* Target VM */ ph7_class_instance *pObj /* Class instance */ ) { ph7_class *pClass = pObj->pClass; ph7_class_attr *pAttr; SyHashEntry *pEntry; sxi32 rc; /* Install class attribute in the private frame associated with this instance */ SyHashResetLoopCursor(&pClass->hAttr); while( (pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0 ){ VmClassAttr *pVmAttr; /* Extract the current attribute */ pAttr = (ph7_class_attr *)pEntry->pUserData; pVmAttr = (VmClassAttr *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(VmClassAttr)); if( pVmAttr == 0 ){ return SXERR_MEM; } pVmAttr->pAttr = pAttr; if( (pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC)) == 0 ){ ph7_value *pMemObj; /* Reserve a memory object for this attribute */ pMemObj = PH7_ReserveMemObj(&(*pVm)); if( pMemObj == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pVmAttr); return SXERR_MEM; } pVmAttr->nIdx = pMemObj->nIdx; if( SySetUsed(&pAttr->aByteCode) > 0 ){ /* Initialize attribute default value (any complex expression) */ VmLocalExec(&(*pVm),&pAttr->aByteCode,pMemObj); } rc = SyHashInsert(&pObj->hAttr,SyStringData(&pAttr->sName),SyStringLength(&pAttr->sName),pVmAttr); if( rc != SXRET_OK ){ VmSlot sSlot; /* Restore memory object */ sSlot.nIdx = pMemObj->nIdx; sSlot.pUserData = 0; SySetPut(&pVm->aFreeObj,(const void *)&sSlot); SyMemBackendPoolFree(&pVm->sAllocator,pVmAttr); return SXERR_MEM; } /* Install attribute in the reference table */ PH7_VmRefObjInstall(&(*pVm),pMemObj->nIdx,0,0,VM_REF_IDX_KEEP); }else{ /* Install static/constant attribute */ pVmAttr->nIdx = pAttr->nIdx; rc = SyHashInsert(&pObj->hAttr,SyStringData(&pAttr->sName),SyStringLength(&pAttr->sName),pVmAttr); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pVm->sAllocator,pVmAttr); return SXERR_MEM; } } } return SXRET_OK; } /* Forward declaration */ static VmRefObj * VmRefObjExtract(ph7_vm *pVm,sxu32 nObjIdx); static sxi32 VmRefObjUnlink(ph7_vm *pVm,VmRefObj *pRef); /* * Dummy read-only buffer used for slot reservation. */ static const char zDummy[sizeof(ph7_value)] = { 0 }; /* Must be >= sizeof(ph7_value) */ /* * Reserve a constant memory object. * Return a pointer to the raw ph7_value on success. NULL on failure. */ PH7_PRIVATE ph7_value * PH7_ReserveConstObj(ph7_vm *pVm,sxu32 *pIndex) { ph7_value *pObj; sxi32 rc; if( pIndex ){ /* Object index in the object table */ *pIndex = SySetUsed(&pVm->aLitObj); } /* Reserve a slot for the new object */ rc = SySetPut(&pVm->aLitObj,(const void *)zDummy); if( rc != SXRET_OK ){ /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ return 0; } pObj = (ph7_value *)SySetPeek(&pVm->aLitObj); return pObj; } /* * Reserve a memory object. * Return a pointer to the raw ph7_value on success. NULL on failure. */ PH7_PRIVATE ph7_value * VmReserveMemObj(ph7_vm *pVm,sxu32 *pIndex) { ph7_value *pObj; sxi32 rc; if( pIndex ){ /* Object index in the object table */ *pIndex = SySetUsed(&pVm->aMemObj); } /* Reserve a slot for the new object */ rc = SySetPut(&pVm->aMemObj,(const void *)zDummy); if( rc != SXRET_OK ){ /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ return 0; } pObj = (ph7_value *)SySetPeek(&pVm->aMemObj); return pObj; } /* Forward declaration */ static sxi32 VmEvalChunk(ph7_vm *pVm,ph7_context *pCtx,SyString *pChunk,int iFlags,int bTrueReturn); /* * Built-in classes/interfaces and some functions that cannot be implemented * directly as foreign functions. */ #define PH7_BUILTIN_LIB \ "class Exception { "\ "protected $message = 'Unknown exception';"\ "protected $code = 0;"\ "protected $file;"\ "protected $line;"\ "protected $trace;"\ "protected $previous;"\ "public function __construct($message = null, $code = 0, Exception $previous = null){"\ " if( isset($message) ){"\ " $this->message = $message;"\ " }"\ " $this->code = $code;"\ " $this->file = __FILE__;"\ " $this->line = __LINE__;"\ " $this->trace = debug_backtrace();"\ " if( isset($previous) ){"\ " $this->previous = $previous;"\ " }"\ "}"\ "public function getMessage(){"\ " return $this->message;"\ "}"\ " public function getCode(){"\ " return $this->code;"\ "}"\ "public function getFile(){"\ " return $this->file;"\ "}"\ "public function getLine(){"\ " return $this->line;"\ "}"\ "public function getTrace(){"\ " return $this->trace;"\ "}"\ "public function getTraceAsString(){"\ " return debug_string_backtrace();"\ "}"\ "public function getPrevious(){"\ " return $this->previous;"\ "}"\ "public function __toString(){"\ " return $this->file.' '.$this->line.' '.$this->code.' '.$this->message;"\ "}"\ "}"\ "class ErrorException extends Exception { "\ "protected $severity;"\ "public function __construct(string $message = null,"\ "int $code = 0,int $severity = 1,string $filename = __FILE__ ,int $lineno = __LINE__ ,Exception $previous = null){"\ " if( isset($message) ){"\ " $this->message = $message;"\ " }"\ " $this->severity = $severity;"\ " $this->code = $code;"\ " $this->file = $filename;"\ " $this->line = $lineno;"\ " $this->trace = debug_backtrace();"\ " if( isset($previous) ){"\ " $this->previous = $previous;"\ " }"\ "}"\ "public function getSeverity(){"\ " return $this->severity;"\ "}"\ "}"\ "interface Iterator {"\ "public function current();"\ "public function key();"\ "public function next();"\ "public function rewind();"\ "public function valid();"\ "}"\ "interface IteratorAggregate {"\ "public function getIterator();"\ "}"\ "interface Serializable {"\ "public function serialize();"\ "public function unserialize(string $serialized);"\ "}"\ "/* Directory releated IO */"\ "class Directory {"\ "public $handle = null;"\ "public $path = null;"\ "public function __construct(string $path)"\ "{"\ " $this->handle = opendir($path);"\ " if( $this->handle !== FALSE ){"\ " $this->path = $path;"\ " }"\ "}"\ "public function __destruct()"\ "{"\ " if( $this->handle != null ){"\ " closedir($this->handle);"\ " }"\ "}"\ "public function read()"\ "{"\ " return readdir($this->handle);"\ "}"\ "public function rewind()"\ "{"\ " rewinddir($this->handle);"\ "}"\ "public function close()"\ "{"\ " closedir($this->handle);"\ " $this->handle = null;"\ "}"\ "}"\ "class stdClass{"\ " public $value;"\ " /* Magic methods */"\ " public function __toInt(){ return (int)$this->value; }"\ " public function __toBool(){ return (bool)$this->value; }"\ " public function __toFloat(){ return (float)$this->value; }"\ " public function __toString(){ return (string)$this->value; }"\ " function __construct($v){ $this->value = $v; }"\ "}"\ "function dir(string $path){"\ " return new Directory($path);"\ "}"\ "function Dir(string $path){"\ " return new Directory($path);"\ "}"\ "function scandir(string $directory,int $sort_order = SCANDIR_SORT_ASCENDING)"\ "{"\ " if( func_num_args() < 1 ){ return FALSE; }"\ " $aDir = array();"\ " $pHandle = opendir($directory);"\ " if( $pHandle == FALSE ){ return FALSE; }"\ " while(FALSE !== ($pEntry = readdir($pHandle)) ){"\ " $aDir[] = $pEntry;"\ " }"\ " closedir($pHandle);"\ " if( $sort_order == SCANDIR_SORT_DESCENDING ){"\ " rsort($aDir);"\ " }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\ " sort($aDir);"\ " }"\ " return $aDir;"\ "}"\ "function glob(string $pattern,int $iFlags = 0){"\ "/* Open the target directory */"\ "$zDir = dirname($pattern);"\ "if(!is_string($zDir) ){ $zDir = './'; }"\ "$pHandle = opendir($zDir);"\ "if( $pHandle == FALSE ){"\ " /* IO error while opening the current directory,return FALSE */"\ " return FALSE;"\ "}"\ "$pattern = basename($pattern);"\ "$pArray = array(); /* Empty array */"\ "/* Loop throw available entries */"\ "while( FALSE !== ($pEntry = readdir($pHandle)) ){"\ " /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\ " $rc = strglob($pattern,$pEntry);"\ " if( $rc ){"\ " if( is_dir($pEntry) ){"\ " if( $iFlags & GLOB_MARK ){"\ " /* Adds a slash to each directory returned */"\ " $pEntry .= DIRECTORY_SEPARATOR;"\ " }"\ " }else if( $iFlags & GLOB_ONLYDIR ){"\ " /* Not a directory,ignore */"\ " continue;"\ " }"\ " /* Add the entry */"\ " $pArray[] = $pEntry;"\ " }"\ " }"\ "/* Close the handle */"\ "closedir($pHandle);"\ "if( ($iFlags & GLOB_NOSORT) == 0 ){"\ " /* Sort the array */"\ " sort($pArray);"\ "}"\ "if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\ " /* Return the search pattern if no files matching were found */"\ " $pArray[] = $pattern;"\ "}"\ "/* Return the created array */"\ "return $pArray;"\ "}"\ "/* Creates a temporary file */"\ "function tmpfile(){"\ " /* Extract the temp directory */"\ " $zTempDir = sys_get_temp_dir();"\ " if( strlen($zTempDir) < 1 ){"\ " /* Use the current dir */"\ " $zTempDir = '.';"\ " }"\ " /* Create the file */"\ " $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'PH7'.rand_str(12),'w+');"\ " return $pHandle;"\ "}"\ "/* Creates a temporary filename */"\ "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */,string $zPrefix = 'PH7')"\ "{"\ " return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\ "}"\ "function array_unshift(&$pArray ){"\ " if( func_num_args() < 1 || !is_array($pArray) ){ return 0; }"\ "/* Copy arguments */"\ "$nArgs = func_num_args();"\ "$pNew = array();"\ "for( $i = 1 ; $i < $nArgs ; ++$i ){"\ " $pNew[] = func_get_arg($i);"\ "}"\ "/* Make a copy of the old entries */"\ "$pOld = array_copy($pArray);"\ "/* Erase */"\ "array_erase($pArray);"\ "/* Unshift */"\ "$pArray = array_merge($pNew,$pOld);"\ "return sizeof($pArray);"\ "}"\ "function array_merge_recursive($array1, $array2){"\ "if( func_num_args() < 1 ){ return NULL; }"\ "$arrays = func_get_args();"\ "$narrays = count($arrays);"\ "$ret = $arrays[0];"\ "for ($i = 1; $i < $narrays; $i++) {"\ " if( array_same($ret,$arrays[$i]) ){ /* Same instance */continue;}"\ " foreach ($arrays[$i] as $key => $value) {"\ " if (((string) $key) === ((string) intval($key))) {"\ " $ret[] = $value;"\ " }else{"\ " if (is_array($value) && isset($ret[$key]) ) {"\ " $ret[$key] = array_merge_recursive($ret[$key], $value);"\ " }else {"\ " $ret[$key] = $value;"\ " }"\ " }"\ " }"\ "}"\ " return $ret;"\ "}"\ "function max(){"\ " $pArgs = func_get_args();"\ " if( sizeof($pArgs) < 1 ){"\ " return null;"\ " }"\ " if( sizeof($pArgs) < 2 ){"\ " $pArg = $pArgs[0];"\ " if( !is_array($pArg) ){"\ " return $pArg; "\ " }"\ " if( sizeof($pArg) < 1 ){"\ " return null;"\ " }"\ " $pArg = array_copy($pArgs[0]);"\ " reset($pArg);"\ " $max = current($pArg);"\ " while( FALSE !== ($val = next($pArg)) ){"\ " if( $val > $max ){"\ " $max = $val;"\ " }"\ " }"\ " return $max;"\ " }"\ " $max = $pArgs[0];"\ " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\ " $val = $pArgs[$i];"\ "if( $val > $max ){"\ " $max = $val;"\ "}"\ " }"\ " return $max;"\ "}"\ "function min(){"\ " $pArgs = func_get_args();"\ " if( sizeof($pArgs) < 1 ){"\ " return null;"\ " }"\ " if( sizeof($pArgs) < 2 ){"\ " $pArg = $pArgs[0];"\ " if( !is_array($pArg) ){"\ " return $pArg; "\ " }"\ " if( sizeof($pArg) < 1 ){"\ " return null;"\ " }"\ " $pArg = array_copy($pArgs[0]);"\ " reset($pArg);"\ " $min = current($pArg);"\ " while( FALSE !== ($val = next($pArg)) ){"\ " if( $val < $min ){"\ " $min = $val;"\ " }"\ " }"\ " return $min;"\ " }"\ " $min = $pArgs[0];"\ " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\ " $val = $pArgs[$i];"\ "if( $val < $min ){"\ " $min = $val;"\ " }"\ " }"\ " return $min;"\ "}"\ "function fileowner(string $file){"\ " $a = stat($file);"\ " if( !is_array($a) ){"\ " return false;"\ " }"\ " return $a['uid'];"\ "}"\ "function filegroup(string $file){"\ " $a = stat($file);"\ " if( !is_array($a) ){"\ " return false;"\ " }"\ " return $a['gid'];"\ "}"\ "function fileinode(string $file){"\ " $a = stat($file);"\ " if( !is_array($a) ){"\ " return false;"\ " }"\ " return $a['ino'];"\ "}" /* * Initialize a freshly allocated PH7 Virtual Machine so that we can * start compiling the target PHP program. */ PH7_PRIVATE sxi32 PH7_VmInit( ph7_vm *pVm, /* Initialize this */ ph7 *pEngine /* Master engine */ ) { SyString sBuiltin; ph7_value *pObj; sxi32 rc; /* Zero the structure */ SyZero(pVm,sizeof(ph7_vm)); /* Initialize VM fields */ pVm->pEngine = &(*pEngine); SyMemBackendInitFromParent(&pVm->sAllocator,&pEngine->sAllocator); /* Instructions containers */ SySetInit(&pVm->aByteCode,&pVm->sAllocator,sizeof(VmInstr)); SySetAlloc(&pVm->aByteCode,0xFF); pVm->pByteContainer = &pVm->aByteCode; /* Object containers */ SySetInit(&pVm->aMemObj,&pVm->sAllocator,sizeof(ph7_value)); SySetAlloc(&pVm->aMemObj,0xFF); /* Virtual machine internal containers */ SyBlobInit(&pVm->sConsumer,&pVm->sAllocator); SyBlobInit(&pVm->sWorker,&pVm->sAllocator); SyBlobInit(&pVm->sArgv,&pVm->sAllocator); SySetInit(&pVm->aLitObj,&pVm->sAllocator,sizeof(ph7_value)); SySetAlloc(&pVm->aLitObj,0xFF); SyHashInit(&pVm->hHostFunction,&pVm->sAllocator,0,0); SyHashInit(&pVm->hFunction,&pVm->sAllocator,0,0); SyHashInit(&pVm->hClass,&pVm->sAllocator,SyStrHash,SyStrnmicmp); SyHashInit(&pVm->hConstant,&pVm->sAllocator,0,0); SyHashInit(&pVm->hSuper,&pVm->sAllocator,0,0); SyHashInit(&pVm->hPDO,&pVm->sAllocator,0,0); SySetInit(&pVm->aFreeObj,&pVm->sAllocator,sizeof(VmSlot)); SySetInit(&pVm->aSelf,&pVm->sAllocator,sizeof(ph7_class *)); SySetInit(&pVm->aShutdown,&pVm->sAllocator,sizeof(VmShutdownCB)); SySetInit(&pVm->aException,&pVm->sAllocator,sizeof(ph7_exception *)); /* Configuration containers */ SySetInit(&pVm->aFiles,&pVm->sAllocator,sizeof(SyString)); SySetInit(&pVm->aPaths,&pVm->sAllocator,sizeof(SyString)); SySetInit(&pVm->aIncluded,&pVm->sAllocator,sizeof(SyString)); SySetInit(&pVm->aOB,&pVm->sAllocator,sizeof(VmObEntry)); SySetInit(&pVm->aIOstream,&pVm->sAllocator,sizeof(ph7_io_stream *)); /* Error callbacks containers */ PH7_MemObjInit(&(*pVm),&pVm->aExceptionCB[0]); PH7_MemObjInit(&(*pVm),&pVm->aExceptionCB[1]); PH7_MemObjInit(&(*pVm),&pVm->aErrCB[0]); PH7_MemObjInit(&(*pVm),&pVm->aErrCB[1]); PH7_MemObjInit(&(*pVm),&pVm->sAssertCallback); /* Set a default recursion limit */ #if defined(__WINNT__) || defined(__UNIXES__) pVm->nMaxDepth = 32; #else pVm->nMaxDepth = 16; #endif /* Default assertion flags */ pVm->iAssertFlags = PH7_ASSERT_WARNING; /* Issue a warning for each failed assertion */ /* JSON return status */ pVm->json_rc = JSON_ERROR_NONE; /* PRNG context */ SyRandomnessInit(&pVm->sPrng,0,0); /* Install the null constant */ pObj = PH7_ReserveConstObj(&(*pVm),0); if( pObj == 0 ){ rc = SXERR_MEM; goto Err; } PH7_MemObjInit(pVm,pObj); /* Install the boolean TRUE constant */ pObj = PH7_ReserveConstObj(&(*pVm),0); if( pObj == 0 ){ rc = SXERR_MEM; goto Err; } PH7_MemObjInitFromBool(pVm,pObj,1); /* Install the boolean FALSE constant */ pObj = PH7_ReserveConstObj(&(*pVm),0); if( pObj == 0 ){ rc = SXERR_MEM; goto Err; } PH7_MemObjInitFromBool(pVm,pObj,0); /* Create the global frame */ rc = VmEnterFrame(&(*pVm),0,0,0); if( rc != SXRET_OK ){ goto Err; } /* Initialize the code generator */ rc = PH7_InitCodeGenerator(pVm,pEngine->xConf.xErr,pEngine->xConf.pErrData); if( rc != SXRET_OK ){ goto Err; } /* VM correctly initialized,set the magic number */ pVm->nMagic = PH7_VM_INIT; SyStringInitFromBuf(&sBuiltin,PH7_BUILTIN_LIB,sizeof(PH7_BUILTIN_LIB)-1); /* Compile the built-in library */ VmEvalChunk(&(*pVm),0,&sBuiltin,PH7_PHP_ONLY,FALSE); /* Reset the code generator */ PH7_ResetCodeGenerator(&(*pVm),pEngine->xConf.xErr,pEngine->xConf.pErrData); return SXRET_OK; Err: SyMemBackendRelease(&pVm->sAllocator); return rc; } /* * Default VM output consumer callback.That is,all VM output is redirected to this * routine which store the output in an internal blob. * The output can be extracted later after program execution [ph7_vm_exec()] via * the [ph7_vm_config()] interface with a configuration verb set to * PH7_VM_CONFIG_EXTRACT_OUTPUT. * Refer to the official docurmentation for additional information. * Note that for performance reason it's preferable to install a VM output * consumer callback via (PH7_VM_CONFIG_OUTPUT) rather than waiting for the VM * to finish executing and extracting the output. */ PH7_PRIVATE sxi32 PH7_VmBlobConsumer( const void *pOut, /* VM Generated output*/ unsigned int nLen, /* Generated output length */ void *pUserData /* User private data */ ) { sxi32 rc; /* Store the output in an internal BLOB */ rc = SyBlobAppend((SyBlob *)pUserData,pOut,nLen); return rc; } #define VM_STACK_GUARD 16 /* * Allocate a new operand stack so that we can start executing * our compiled PHP program. * Return a pointer to the operand stack (array of ph7_values) * on success. NULL (Fatal error) on failure. */ static ph7_value * VmNewOperandStack( ph7_vm *pVm, /* Target VM */ sxu32 nInstr /* Total numer of generated byte-code instructions */ ) { ph7_value *pStack; /* No instruction ever pushes more than a single element onto the ** stack and the stack never grows on successive executions of the ** same loop. So the total number of instructions is an upper bound ** on the maximum stack depth required. ** ** Allocation all the stack space we will ever need. */ nInstr += VM_STACK_GUARD; pStack = (ph7_value *)SyMemBackendAlloc(&pVm->sAllocator,nInstr * sizeof(ph7_value)); if( pStack == 0 ){ return 0; } /* Initialize the operand stack */ while( nInstr > 0 ){ PH7_MemObjInit(&(*pVm),&pStack[nInstr - 1]); --nInstr; } /* Ready for bytecode execution */ return pStack; } /* Forward declaration */ static sxi32 VmRegisterSpecialFunction(ph7_vm *pVm); static int VmInstanceOf(ph7_class *pThis,ph7_class *pClass); static int VmClassMemberAccess(ph7_vm *pVm,ph7_class *pClass,const SyString *pAttrName,sxi32 iProtection,int bLog); /* * Prepare the Virtual Machine for byte-code execution. * This routine gets called by the PH7 engine after * successful compilation of the target PHP program. */ PH7_PRIVATE sxi32 PH7_VmMakeReady( ph7_vm *pVm /* Target VM */ ) { SyHashEntry *pEntry; sxi32 rc; if( pVm->nMagic != PH7_VM_INIT ){ /* Initialize your VM first */ return SXERR_CORRUPT; } /* Mark the VM ready for byte-code execution */ pVm->nMagic = PH7_VM_RUN; /* Release the code generator now we have compiled our program */ PH7_ResetCodeGenerator(pVm,0,0); /* Emit the DONE instruction */ rc = PH7_VmEmitInstr(&(*pVm),PH7_OP_DONE,0,0,0,0); if( rc != SXRET_OK ){ return SXERR_MEM; } /* Script return value */ PH7_MemObjInit(&(*pVm),&pVm->sExec); /* Assume a NULL return value */ /* Allocate a new operand stack */ pVm->aOps = VmNewOperandStack(&(*pVm),SySetUsed(pVm->pByteContainer)); if( pVm->aOps == 0 ){ return SXERR_MEM; } /* Set the default VM output consumer callback and it's * private data. */ pVm->sVmConsumer.xConsumer = PH7_VmBlobConsumer; pVm->sVmConsumer.pUserData = &pVm->sConsumer; /* Allocate the reference table */ pVm->nRefSize = 0x10; /* Must be a power of two for fast arithemtic */ pVm->apRefObj = (VmRefObj **)SyMemBackendAlloc(&pVm->sAllocator,sizeof(VmRefObj *) * pVm->nRefSize); if( pVm->apRefObj == 0 ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_MEM; } /* Zero the reference table */ SyZero(pVm->apRefObj,sizeof(VmRefObj *) * pVm->nRefSize); /* Register special functions first [i.e: print, json_encode(), func_get_args(), die, etc.] */ rc = VmRegisterSpecialFunction(&(*pVm)); if( rc != SXRET_OK ){ /* Don't worry about freeing memory, everything will be released shortly */ return rc; } /* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */ rc = PH7_HashmapCreateSuper(&(*pVm)); if( rc != SXRET_OK ){ /* Don't worry about freeing memory, everything will be released shortly */ return rc; } /* Register built-in constants [i.e: PHP_EOL, PHP_OS...] */ PH7_RegisterBuiltInConstant(&(*pVm)); /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */ PH7_RegisterBuiltInFunction(&(*pVm)); /* Initialize and install static and constants class attributes */ SyHashResetLoopCursor(&pVm->hClass); while((pEntry = SyHashGetNextEntry(&pVm->hClass)) != 0 ){ rc = VmMountUserClass(&(*pVm),(ph7_class *)pEntry->pUserData); if( rc != SXRET_OK ){ return rc; } } /* Random number betwwen 0 and 1023 used to generate unique ID */ pVm->unique_id = PH7_VmRandomNum(&(*pVm)) & 1023; /* VM is ready for bytecode execution */ return SXRET_OK; } /* * Reset a Virtual Machine to it's initial state. */ PH7_PRIVATE sxi32 PH7_VmReset(ph7_vm *pVm) { if( pVm->nMagic != PH7_VM_RUN && pVm->nMagic != PH7_VM_EXEC ){ return SXERR_CORRUPT; } /* TICKET 1433-003: As of this version, the VM is automatically reset */ SyBlobReset(&pVm->sConsumer); PH7_MemObjRelease(&pVm->sExec); /* Set the ready flag */ pVm->nMagic = PH7_VM_RUN; return SXRET_OK; } /* * Release a Virtual Machine. * Every virtual machine must be destroyed in order to avoid memory leaks. */ PH7_PRIVATE sxi32 PH7_VmRelease(ph7_vm *pVm) { /* Set the stale magic number */ pVm->nMagic = PH7_VM_STALE; /* Release the private memory subsystem */ SyMemBackendRelease(&pVm->sAllocator); return SXRET_OK; } /* * Initialize a foreign function call context. * The context in which a foreign function executes is stored in a ph7_context object. * A pointer to a ph7_context object is always first parameter to application-defined foreign * functions. * The application-defined foreign function implementation will pass this pointer through into * calls to dozens of interfaces,these includes ph7_result_int(), ph7_result_string(), ph7_result_value(), * ph7_context_new_scalar(), ph7_context_alloc_chunk(), ph7_context_output(), ph7_context_throw_error() * and many more. Refer to the C/C++ Interfaces documentation for additional information. */ static sxi32 VmInitCallContext( ph7_context *pOut, /* Call Context */ ph7_vm *pVm, /* Target VM */ ph7_user_func *pFunc, /* Foreign function to execute shortly */ ph7_value *pRet, /* Store return value here*/ sxi32 iFlags /* Control flags */ ) { pOut->pFunc = pFunc; pOut->pVm = pVm; SySetInit(&pOut->sVar,&pVm->sAllocator,sizeof(ph7_value *)); SySetInit(&pOut->sChunk,&pVm->sAllocator,sizeof(ph7_aux_data)); /* Assume a null return value */ MemObjSetType(pRet,MEMOBJ_NULL); pOut->pRet = pRet; pOut->iFlags = iFlags; return SXRET_OK; } /* * Release a foreign function call context and cleanup the mess * left behind. */ static void VmReleaseCallContext(ph7_context *pCtx) { sxu32 n; if( SySetUsed(&pCtx->sVar) > 0 ){ ph7_value **apObj = (ph7_value **)SySetBasePtr(&pCtx->sVar); for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){ if( apObj[n] == 0 ){ /* Already released */ continue; } PH7_MemObjRelease(apObj[n]); SyMemBackendPoolFree(&pCtx->pVm->sAllocator,apObj[n]); } SySetRelease(&pCtx->sVar); } if( SySetUsed(&pCtx->sChunk) > 0 ){ ph7_aux_data *aAux; void *pChunk; /* Automatic release of dynamically allocated chunk * using [ph7_context_alloc_chunk()]. */ aAux = (ph7_aux_data *)SySetBasePtr(&pCtx->sChunk); for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){ pChunk = aAux[n].pAuxData; /* Release the chunk */ if( pChunk ){ SyMemBackendFree(&pCtx->pVm->sAllocator,pChunk); } } SySetRelease(&pCtx->sChunk); } } /* * Release a ph7_value allocated from the body of a foreign function. * Refer to [ph7_context_release_value()] for additional information. */ PH7_PRIVATE void PH7_VmReleaseContextValue( ph7_context *pCtx, /* Call context */ ph7_value *pValue /* Release this value */ ) { if( pValue == 0 ){ /* NULL value is a harmless operation */ return; } if( SySetUsed(&pCtx->sVar) > 0 ){ ph7_value **apObj = (ph7_value **)SySetBasePtr(&pCtx->sVar); sxu32 n; for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){ if( apObj[n] == pValue ){ PH7_MemObjRelease(pValue); SyMemBackendPoolFree(&pCtx->pVm->sAllocator,pValue); /* Mark as released */ apObj[n] = 0; break; } } } } /* * Pop and release as many memory object from the operand stack. */ static void VmPopOperand( ph7_value **ppTos, /* Operand stack */ sxi32 nPop /* Total number of memory objects to pop */ ) { ph7_value *pTos = *ppTos; while( nPop > 0 ){ PH7_MemObjRelease(pTos); pTos--; nPop--; } /* Top of the stack */ *ppTos = pTos; } /* * Reserve a memory object. * Return a pointer to the raw ph7_value on success. NULL on failure. */ PH7_PRIVATE ph7_value * PH7_ReserveMemObj(ph7_vm *pVm) { ph7_value *pObj = 0; VmSlot *pSlot; sxu32 nIdx; /* Check for a free slot */ nIdx = SXU32_HIGH; /* cc warning */ pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj); if( pSlot ){ pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pSlot->nIdx); nIdx = pSlot->nIdx; } if( pObj == 0 ){ /* Reserve a new memory object */ pObj = VmReserveMemObj(&(*pVm),&nIdx); if( pObj == 0 ){ return 0; } } /* Set a null default value */ PH7_MemObjInit(&(*pVm),pObj); pObj->nIdx = nIdx; return pObj; } /* * Insert an entry by reference (not copy) in the given hashmap. */ static sxi32 VmHashmapRefInsert( ph7_hashmap *pMap, /* Target hashmap */ const char *zKey, /* Entry key */ sxu32 nByte, /* Key length */ sxu32 nRefIdx /* Entry index in the object pool */ ) { ph7_value sKey; sxi32 rc; PH7_MemObjInitFromString(pMap->pVm,&sKey,0); PH7_MemObjStringAppend(&sKey,zKey,nByte); /* Perform the insertion */ rc = PH7_HashmapInsertByRef(&(*pMap),&sKey,nRefIdx); PH7_MemObjRelease(&sKey); return rc; } /* * Extract a variable value from the top active VM frame. * Return a pointer to the variable value on success. * NULL otherwise (non-existent variable/Out-of-memory,...). */ static ph7_value * VmExtractMemObj( ph7_vm *pVm, /* Target VM */ const SyString *pName, /* Variable name */ int bDup, /* True to duplicate variable name */ int bCreate /* True to create the variable if non-existent */ ) { int bNullify = FALSE; SyHashEntry *pEntry; VmFrame *pFrame; ph7_value *pObj; sxu32 nIdx; sxi32 rc; /* Point to the top active frame */ pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; /* Parent frame */ } /* Perform the lookup */ if( pName == 0 || pName->nByte < 1 ){ static const SyString sAnnon = { " " , sizeof(char) }; pName = &sAnnon; /* Always nullify the object */ bNullify = TRUE; bDup = FALSE; } /* Check the superglobals table first */ pEntry = SyHashGet(&pVm->hSuper,(const void *)pName->zString,pName->nByte); if( pEntry == 0 ){ /* Query the top active frame */ pEntry = SyHashGet(&pFrame->hVar,(const void *)pName->zString,pName->nByte); if( pEntry == 0 ){ char *zName = (char *)pName->zString; VmSlot sLocal; if( !bCreate ){ /* Do not create the variable,return NULL instead */ return 0; } /* No such variable,automatically create a new one and install * it in the current frame. */ pObj = PH7_ReserveMemObj(&(*pVm)); if( pObj == 0 ){ return 0; } nIdx = pObj->nIdx; if( bDup ){ /* Duplicate name */ zName = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte); if( zName == 0 ){ return 0; } } /* Link to the top active VM frame */ rc = SyHashInsert(&pFrame->hVar,zName,pName->nByte,SX_INT_TO_PTR(nIdx)); if( rc != SXRET_OK ){ /* Return the slot to the free pool */ sLocal.nIdx = nIdx; sLocal.pUserData = 0; SySetPut(&pVm->aFreeObj,(const void *)&sLocal); return 0; } if( pFrame->pParent != 0 ){ /* Local variable */ sLocal.nIdx = nIdx; SySetPut(&pFrame->sLocal,(const void *)&sLocal); }else{ /* Register in the $GLOBALS array */ VmHashmapRefInsert(pVm->pGlobal,pName->zString,pName->nByte,nIdx); } /* Install in the reference table */ PH7_VmRefObjInstall(&(*pVm),nIdx,SyHashLastEntry(&pFrame->hVar),0,0); /* Save object index */ pObj->nIdx = nIdx; }else{ /* Extract variable contents */ nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( bNullify && pObj ){ PH7_MemObjRelease(pObj); } } }else{ /* Superglobal */ nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); } return pObj; } /* * Extract a superglobal variable such as $_GET,$_POST,$_HEADERS,.... * Return a pointer to the variable value on success.NULL otherwise. */ static ph7_value * VmExtractSuper( ph7_vm *pVm, /* Target VM */ const char *zName, /* Superglobal name: NOT NULL TERMINATED */ sxu32 nByte /* zName length */ ) { SyHashEntry *pEntry; ph7_value *pValue; sxu32 nIdx; /* Query the superglobal table */ pEntry = SyHashGet(&pVm->hSuper,(const void *)zName,nByte); if( pEntry == 0 ){ /* No such entry */ return 0; } /* Extract the superglobal index in the global object pool */ nIdx = SX_PTR_TO_INT(pEntry->pUserData); /* Extract the variable value */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); return pValue; } /* * Perform a raw hashmap insertion. * Refer to the [PH7_VmConfigure()] implementation for additional information. */ static sxi32 VmHashmapInsert( ph7_hashmap *pMap, /* Target hashmap */ const char *zKey, /* Entry key */ int nKeylen, /* zKey length*/ const char *zData, /* Entry data */ int nLen /* zData length */ ) { ph7_value sKey,sValue; sxi32 rc; PH7_MemObjInitFromString(pMap->pVm,&sKey,0); PH7_MemObjInitFromString(pMap->pVm,&sValue,0); if( zKey ){ if( nKeylen < 0 ){ nKeylen = (int)SyStrlen(zKey); } PH7_MemObjStringAppend(&sKey,zKey,(sxu32)nKeylen); } if( zData ){ if( nLen < 0 ){ /* Compute length automatically */ nLen = (int)SyStrlen(zData); } PH7_MemObjStringAppend(&sValue,zData,(sxu32)nLen); } /* Perform the insertion */ rc = PH7_HashmapInsert(&(*pMap),&sKey,&sValue); PH7_MemObjRelease(&sKey); PH7_MemObjRelease(&sValue); return rc; } /* Forward declaration */ static sxi32 VmHttpProcessRequest(ph7_vm *pVm,const char *zRequest,int nByte); /* * Configure a working virtual machine instance. * * This routine is used to configure a PH7 virtual machine obtained by a prior * successful call to one of the compile interface such as ph7_compile() * ph7_compile_v2() or ph7_compile_file(). * The second argument to this function is an integer configuration option * that determines what property of the PH7 virtual machine is to be configured. * Subsequent arguments vary depending on the configuration option in the second * argument. There are many verbs but the most important are PH7_VM_CONFIG_OUTPUT, * PH7_VM_CONFIG_HTTP_REQUEST and PH7_VM_CONFIG_ARGV_ENTRY. * Refer to the official documentation for the list of allowed verbs. */ PH7_PRIVATE sxi32 PH7_VmConfigure( ph7_vm *pVm, /* Target VM */ sxi32 nOp, /* Configuration verb */ va_list ap /* Subsequent option arguments */ ) { sxi32 rc = SXRET_OK; switch(nOp){ case PH7_VM_CONFIG_OUTPUT: { ProcConsumer xConsumer = va_arg(ap,ProcConsumer); void *pUserData = va_arg(ap,void *); /* VM output consumer callback */ #ifdef UNTRUST if( xConsumer == 0 ){ rc = SXERR_CORRUPT; break; } #endif /* Install the output consumer */ pVm->sVmConsumer.xConsumer = xConsumer; pVm->sVmConsumer.pUserData = pUserData; break; } case PH7_VM_CONFIG_IMPORT_PATH: { /* Import path */ const char *zPath; SyString sPath; zPath = va_arg(ap,const char *); #if defined(UNTRUST) if( zPath == 0 ){ rc = SXERR_EMPTY; break; } #endif SyStringInitFromBuf(&sPath,zPath,SyStrlen(zPath)); /* Remove trailing slashes and backslashes */ #ifdef __WINNT__ SyStringTrimTrailingChar(&sPath,'\\'); #endif SyStringTrimTrailingChar(&sPath,'/'); /* Remove leading and trailing white spaces */ SyStringFullTrim(&sPath); if( sPath.nByte > 0 ){ /* Store the path in the corresponding conatiner */ rc = SySetPut(&pVm->aPaths,(const void *)&sPath); } break; } case PH7_VM_CONFIG_ERR_REPORT: /* Run-Time Error report */ pVm->bErrReport = 1; break; case PH7_VM_CONFIG_RECURSION_DEPTH:{ /* Recursion depth */ int nDepth = va_arg(ap,int); if( nDepth > 2 && nDepth < 1024 ){ pVm->nMaxDepth = nDepth; } break; } case PH7_VM_OUTPUT_LENGTH: { /* VM output length in bytes */ sxu32 *pOut = va_arg(ap,sxu32 *); #ifdef UNTRUST if( pOut == 0 ){ rc = SXERR_CORRUPT; break; } #endif *pOut = pVm->nOutputLen; break; } case PH7_VM_CONFIG_CREATE_SUPER: case PH7_VM_CONFIG_CREATE_VAR: { /* Create a new superglobal/global variable */ const char *zName = va_arg(ap,const char *); ph7_value *pValue = va_arg(ap,ph7_value *); SyHashEntry *pEntry; ph7_value *pObj; sxu32 nByte; sxu32 nIdx; #ifdef UNTRUST if( SX_EMPTY_STR(zName) || pValue == 0 ){ rc = SXERR_CORRUPT; break; } #endif nByte = SyStrlen(zName); if( nOp == PH7_VM_CONFIG_CREATE_SUPER ){ /* Check if the superglobal is already installed */ pEntry = SyHashGet(&pVm->hSuper,(const void *)zName,nByte); }else{ /* Query the top active VM frame */ pEntry = SyHashGet(&pVm->pFrame->hVar,(const void *)zName,nByte); } if( pEntry ){ /* Variable already installed */ nIdx = SX_PTR_TO_INT(pEntry->pUserData); /* Extract contents */ pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( pObj ){ /* Overwrite old contents */ PH7_MemObjStore(pValue,pObj); } }else{ /* Install a new variable */ pObj = PH7_ReserveMemObj(&(*pVm)); if( pObj == 0 ){ rc = SXERR_MEM; break; } nIdx = pObj->nIdx; /* Copy value */ PH7_MemObjStore(pValue,pObj); if( nOp == PH7_VM_CONFIG_CREATE_SUPER ){ /* Install the superglobal */ rc = SyHashInsert(&pVm->hSuper,(const void *)zName,nByte,SX_INT_TO_PTR(nIdx)); }else{ /* Install in the current frame */ rc = SyHashInsert(&pVm->pFrame->hVar,(const void *)zName,nByte,SX_INT_TO_PTR(nIdx)); } if( rc == SXRET_OK ){ SyHashEntry *pRef; if( nOp == PH7_VM_CONFIG_CREATE_SUPER ){ pRef = SyHashLastEntry(&pVm->hSuper); }else{ pRef = SyHashLastEntry(&pVm->pFrame->hVar); } /* Install in the reference table */ PH7_VmRefObjInstall(&(*pVm),nIdx,pRef,0,0); if( nOp == PH7_VM_CONFIG_CREATE_SUPER || pVm->pFrame->pParent == 0){ /* Register in the $GLOBALS array */ VmHashmapRefInsert(pVm->pGlobal,zName,nByte,nIdx); } } } break; } case PH7_VM_CONFIG_SERVER_ATTR: case PH7_VM_CONFIG_ENV_ATTR: case PH7_VM_CONFIG_SESSION_ATTR: case PH7_VM_CONFIG_POST_ATTR: case PH7_VM_CONFIG_GET_ATTR: case PH7_VM_CONFIG_COOKIE_ATTR: case PH7_VM_CONFIG_HEADER_ATTR: { const char *zKey = va_arg(ap,const char *); const char *zValue = va_arg(ap,const char *); int nLen = va_arg(ap,int); ph7_hashmap *pMap; ph7_value *pValue; if( nOp == PH7_VM_CONFIG_ENV_ATTR ){ /* Extract the $_ENV superglobal */ pValue = VmExtractSuper(&(*pVm),"_ENV",sizeof("_ENV")-1); }else if(nOp == PH7_VM_CONFIG_POST_ATTR ){ /* Extract the $_POST superglobal */ pValue = VmExtractSuper(&(*pVm),"_POST",sizeof("_POST")-1); }else if(nOp == PH7_VM_CONFIG_GET_ATTR ){ /* Extract the $_GET superglobal */ pValue = VmExtractSuper(&(*pVm),"_GET",sizeof("_GET")-1); }else if(nOp == PH7_VM_CONFIG_COOKIE_ATTR ){ /* Extract the $_COOKIE superglobal */ pValue = VmExtractSuper(&(*pVm),"_COOKIE",sizeof("_COOKIE")-1); }else if(nOp == PH7_VM_CONFIG_SESSION_ATTR ){ /* Extract the $_SESSION superglobal */ pValue = VmExtractSuper(&(*pVm),"_SESSION",sizeof("_SESSION")-1); }else if( nOp == PH7_VM_CONFIG_HEADER_ATTR ){ /* Extract the $_HEADER superglobale */ pValue = VmExtractSuper(&(*pVm),"_HEADER",sizeof("_HEADER")-1); }else{ /* Extract the $_SERVER superglobal */ pValue = VmExtractSuper(&(*pVm),"_SERVER",sizeof("_SERVER")-1); } if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* No such entry */ rc = SXERR_NOTFOUND; break; } /* Point to the hashmap */ pMap = (ph7_hashmap *)pValue->x.pOther; /* Perform the insertion */ rc = VmHashmapInsert(pMap,zKey,-1,zValue,nLen); break; } case PH7_VM_CONFIG_ARGV_ENTRY:{ /* Script arguments */ const char *zValue = va_arg(ap,const char *); ph7_hashmap *pMap; ph7_value *pValue; sxu32 n; if( SX_EMPTY_STR(zValue) ){ rc = SXERR_EMPTY; break; } /* Extract the $argv array */ pValue = VmExtractSuper(&(*pVm),"argv",sizeof("argv")-1); if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* No such entry */ rc = SXERR_NOTFOUND; break; } /* Point to the hashmap */ pMap = (ph7_hashmap *)pValue->x.pOther; /* Perform the insertion */ n = (sxu32)SyStrlen(zValue); rc = VmHashmapInsert(pMap,0,0,zValue,(int)n); if( rc == SXRET_OK ){ if( pMap->nEntry > 1 ){ /* Append space separator first */ SyBlobAppend(&pVm->sArgv,(const void *)" ",sizeof(char)); } SyBlobAppend(&pVm->sArgv,(const void *)zValue,n); } break; } case PH7_VM_CONFIG_ERR_LOG_HANDLER: { /* error_log() consumer */ ProcErrLog xErrLog = va_arg(ap,ProcErrLog); pVm->xErrLog = xErrLog; break; } case PH7_VM_CONFIG_EXEC_VALUE: { /* Script return value */ ph7_value **ppValue = va_arg(ap,ph7_value **); #ifdef UNTRUST if( ppValue == 0 ){ rc = SXERR_CORRUPT; break; } #endif *ppValue = &pVm->sExec; break; } case PH7_VM_CONFIG_IO_STREAM: { /* Register an IO stream device */ const ph7_io_stream *pStream = va_arg(ap,const ph7_io_stream *); /* Make sure we are dealing with a valid IO stream */ if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 || pStream->xOpen == 0 || pStream->xRead == 0 ){ /* Invalid stream */ rc = SXERR_INVALID; break; } if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName,"file",sizeof("file")-1) == 0 ){ /* Make the 'file://' stream the defaut stream device */ pVm->pDefStream = pStream; } /* Insert in the appropriate container */ rc = SySetPut(&pVm->aIOstream,(const void *)&pStream); break; } case PH7_VM_CONFIG_EXTRACT_OUTPUT: { /* Point to the VM internal output consumer buffer */ const void **ppOut = va_arg(ap,const void **); unsigned int *pLen = va_arg(ap,unsigned int *); #ifdef UNTRUST if( ppOut == 0 || pLen == 0 ){ rc = SXERR_CORRUPT; break; } #endif *ppOut = SyBlobData(&pVm->sConsumer); *pLen = SyBlobLength(&pVm->sConsumer); break; } case PH7_VM_CONFIG_HTTP_REQUEST:{ /* Raw HTTP request*/ const char *zRequest = va_arg(ap,const char *); int nByte = va_arg(ap,int); if( SX_EMPTY_STR(zRequest) ){ rc = SXERR_EMPTY; break; } if( nByte < 0 ){ /* Compute length automatically */ nByte = (int)SyStrlen(zRequest); } /* Process the request */ rc = VmHttpProcessRequest(&(*pVm),zRequest,nByte); break; } default: /* Unknown configuration option */ rc = SXERR_UNKNOWN; break; } return rc; } /* Forward declaration */ static const char * VmInstrToString(sxi32 nOp); /* * This routine is used to dump PH7 byte-code instructions to a human readable * format. * The dump is redirected to the given consumer callback which is responsible * of consuming the generated dump perhaps redirecting it to its standard output * (STDOUT). */ static sxi32 VmByteCodeDump( SySet *pByteCode, /* Bytecode container */ ProcConsumer xConsumer, /* Dump consumer callback */ void *pUserData /* Last argument to xConsumer() */ ) { static const char zDump[] = { "====================================================\n" "PH7 VM Dump Copyright (C) 2011-2012 Symisc Systems\n" " http://www.symisc.net/\n" "====================================================\n" }; VmInstr *pInstr,*pEnd; sxi32 rc = SXRET_OK; sxu32 n; /* Point to the PH7 instructions */ pInstr = (VmInstr *)SySetBasePtr(pByteCode); pEnd = &pInstr[SySetUsed(pByteCode)]; n = 0; xConsumer((const void *)zDump,sizeof(zDump)-1,pUserData); /* Dump instructions */ for(;;){ if( pInstr >= pEnd ){ /* No more instructions */ break; } /* Format and call the consumer callback */ rc = SyProcFormat(xConsumer,pUserData,"%s %8d %8u %#8x [%u]\n", VmInstrToString(pInstr->iOp),pInstr->iP1,pInstr->iP2, SX_PTR_TO_INT(pInstr->p3),n); if( rc != SXRET_OK ){ /* Consumer routine request an operation abort */ return rc; } ++n; pInstr++; /* Next instruction in the stream */ } return rc; } /* Forward declaration */ static int VmObConsumer(const void *pData,unsigned int nDataLen,void *pUserData); static sxi32 VmUncaughtException(ph7_vm *pVm,ph7_class_instance *pThis); static sxi32 VmThrowException(ph7_vm *pVm,ph7_class_instance *pThis); /* * Consume a generated run-time error message by invoking the VM output * consumer callback. */ static sxi32 VmCallErrorHandler(ph7_vm *pVm,SyBlob *pMsg) { ph7_output_consumer *pCons = &pVm->sVmConsumer; sxi32 rc = SXRET_OK; /* Append a new line */ #ifdef __WINNT__ SyBlobAppend(pMsg,"\r\n",sizeof("\r\n")-1); #else SyBlobAppend(pMsg,"\n",sizeof(char)); #endif /* Invoke the output consumer callback */ rc = pCons->xConsumer(SyBlobData(pMsg),SyBlobLength(pMsg),pCons->pUserData); if( pCons->xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += SyBlobLength(pMsg); } return rc; } /* * Throw a run-time error and invoke the supplied VM output consumer callback. * Refer to the implementation of [ph7_context_throw_error()] for additional * information. */ PH7_PRIVATE sxi32 PH7_VmThrowError( ph7_vm *pVm, /* Target VM */ SyString *pFuncName, /* Function name. NULL otherwise */ sxi32 iErr, /* Severity level: [i.e: Error,Warning or Notice]*/ const char *zMessage /* Null terminated error message */ ) { SyBlob *pWorker = &pVm->sWorker; SyString *pFile; char *zErr; sxi32 rc; if( !pVm->bErrReport ){ /* Don't bother reporting errors */ return SXRET_OK; } /* Reset the working buffer */ SyBlobReset(pWorker); /* Peek the processed file if available */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile ){ /* Append file name */ SyBlobAppend(pWorker,pFile->zString,pFile->nByte); SyBlobAppend(pWorker,(const void *)" ",sizeof(char)); } zErr = "Error: "; switch(iErr){ case PH7_CTX_WARNING: zErr = "Warning: "; break; case PH7_CTX_NOTICE: zErr = "Notice: "; break; default: iErr = PH7_CTX_ERR; break; } SyBlobAppend(pWorker,zErr,SyStrlen(zErr)); if( pFuncName ){ /* Append function name first */ SyBlobAppend(pWorker,pFuncName->zString,pFuncName->nByte); SyBlobAppend(pWorker,"(): ",sizeof("(): ")-1); } SyBlobAppend(pWorker,zMessage,SyStrlen(zMessage)); /* Consume the error message */ rc = VmCallErrorHandler(&(*pVm),pWorker); return rc; } /* * Format and throw a run-time error and invoke the supplied VM output consumer callback. * Refer to the implementation of [ph7_context_throw_error_format()] for additional * information. */ static sxi32 VmThrowErrorAp( ph7_vm *pVm, /* Target VM */ SyString *pFuncName, /* Function name. NULL otherwise */ sxi32 iErr, /* Severity level: [i.e: Error,Warning or Notice] */ const char *zFormat, /* Format message */ va_list ap /* Variable list of arguments */ ) { SyBlob *pWorker = &pVm->sWorker; SyString *pFile; char *zErr; sxi32 rc; if( !pVm->bErrReport ){ /* Don't bother reporting errors */ return SXRET_OK; } /* Reset the working buffer */ SyBlobReset(pWorker); /* Peek the processed file if available */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile ){ /* Append file name */ SyBlobAppend(pWorker,pFile->zString,pFile->nByte); SyBlobAppend(pWorker,(const void *)" ",sizeof(char)); } zErr = "Error: "; switch(iErr){ case PH7_CTX_WARNING: zErr = "Warning: "; break; case PH7_CTX_NOTICE: zErr = "Notice: "; break; default: iErr = PH7_CTX_ERR; break; } SyBlobAppend(pWorker,zErr,SyStrlen(zErr)); if( pFuncName ){ /* Append function name first */ SyBlobAppend(pWorker,pFuncName->zString,pFuncName->nByte); SyBlobAppend(pWorker,"(): ",sizeof("(): ")-1); } SyBlobFormatAp(pWorker,zFormat,ap); /* Consume the error message */ rc = VmCallErrorHandler(&(*pVm),pWorker); return rc; } /* * Format and throw a run-time error and invoke the supplied VM output consumer callback. * Refer to the implementation of [ph7_context_throw_error_format()] for additional * information. * ------------------------------------ * Simple boring wrapper function. * ------------------------------------ */ static sxi32 VmErrorFormat(ph7_vm *pVm,sxi32 iErr,const char *zFormat,...) { va_list ap; sxi32 rc; va_start(ap,zFormat); rc = VmThrowErrorAp(&(*pVm),0,iErr,zFormat,ap); va_end(ap); return rc; } /* * Format and throw a run-time error and invoke the supplied VM output consumer callback. * Refer to the implementation of [ph7_context_throw_error_format()] for additional * information. * ------------------------------------ * Simple boring wrapper function. * ------------------------------------ */ PH7_PRIVATE sxi32 PH7_VmThrowErrorAp(ph7_vm *pVm,SyString *pFuncName,sxi32 iErr,const char *zFormat,va_list ap) { sxi32 rc; rc = VmThrowErrorAp(&(*pVm),&(*pFuncName),iErr,zFormat,ap); return rc; } /* * Execute as much of a PH7 bytecode program as we can then return. * * [PH7_VmMakeReady()] must be called before this routine in order to * close the program with a final OP_DONE and to set up the default * consumer routines and other stuff. Refer to the implementation * of [PH7_VmMakeReady()] for additional information. * If the installed VM output consumer callback ever returns PH7_ABORT * then the program execution is halted. * After this routine has finished, [PH7_VmRelease()] or [PH7_VmReset()] * should be used respectively to clean up the mess that was left behind * or to reset the VM to it's initial state. */ static sxi32 VmByteCodeExec( ph7_vm *pVm, /* Target VM */ VmInstr *aInstr, /* PH7 bytecode program */ ph7_value *pStack, /* Operand stack */ int nTos, /* Top entry in the operand stack (usually -1) */ ph7_value *pResult, /* Store program return value here. NULL otherwise */ sxu32 *pLastRef, /* Last referenced ph7_value index */ int is_callback /* TRUE if we are executing a callback */ ) { VmInstr *pInstr; ph7_value *pTos; SySet aArg; sxi32 pc; sxi32 rc; /* Argument container */ SySetInit(&aArg,&pVm->sAllocator,sizeof(ph7_value *)); if( nTos < 0 ){ pTos = &pStack[-1]; }else{ pTos = &pStack[nTos]; } pc = 0; /* Execute as much as we can */ for(;;){ /* Fetch the instruction to execute */ pInstr = &aInstr[pc]; rc = SXRET_OK; /* * What follows here is a massive switch statement where each case implements a * separate instruction in the virtual machine. If we follow the usual * indentation convention each case should be indented by 6 spaces. But * that is a lot of wasted space on the left margin. So the code within * the switch statement will break with convention and be flush-left. */ switch(pInstr->iOp){ /* * DONE: P1 * * * * Program execution completed: Clean up the mess left behind * and return immediately. */ case PH7_OP_DONE: if( pInstr->iP1 ){ #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( pLastRef ){ *pLastRef = pTos->nIdx; } if( pResult ){ /* Execution result */ PH7_MemObjStore(pTos,pResult); } VmPopOperand(&pTos,1); }else if( pLastRef ){ /* Nothing referenced */ *pLastRef = SXU32_HIGH; } goto Done; /* * HALT: P1 * * * * Program execution aborted: Clean up the mess left behind * and abort immediately. */ case PH7_OP_HALT: if( pInstr->iP1 ){ #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( pLastRef ){ *pLastRef = pTos->nIdx; } if( pTos->iFlags & MEMOBJ_STRING ){ if( SyBlobLength(&pTos->sBlob) > 0 ){ /* Output the exit message */ pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob), pVm->sVmConsumer.pUserData); if( pVm->sVmConsumer.xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += SyBlobLength(&pTos->sBlob); } } }else if(pTos->iFlags & MEMOBJ_INT ){ /* Record exit status */ pVm->iExitStatus = (sxi32)pTos->x.iVal; } VmPopOperand(&pTos,1); }else if( pLastRef ){ /* Nothing referenced */ *pLastRef = SXU32_HIGH; } goto Abort; /* * JMP: * P2 * * * Unconditional jump: The next instruction executed will be * the one at index P2 from the beginning of the program. */ case PH7_OP_JMP: pc = pInstr->iP2 - 1; break; /* * JZ: P1 P2 * * * Take the jump if the top value is zero (FALSE jump).Pop the top most * entry in the stack if P1 is zero. */ case PH7_OP_JZ: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Get a boolean value */ if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } if( !pTos->x.iVal ){ /* Take the jump */ pc = pInstr->iP2 - 1; } if( !pInstr->iP1 ){ VmPopOperand(&pTos,1); } break; /* * JNZ: P1 P2 * * * Take the jump if the top value is not zero (TRUE jump).Pop the top most * entry in the stack if P1 is zero. */ case PH7_OP_JNZ: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Get a boolean value */ if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } if( pTos->x.iVal ){ /* Take the jump */ pc = pInstr->iP2 - 1; } if( !pInstr->iP1 ){ VmPopOperand(&pTos,1); } break; /* * NOOP: * * * * * Do nothing. This instruction is often useful as a jump * destination. */ case PH7_OP_NOOP: break; /* * POP: P1 * * * * Pop P1 elements from the operand stack. */ case PH7_OP_POP: { sxi32 n = pInstr->iP1; if( &pTos[-n+1] < pStack ){ /* TICKET 1433-51 Stack underflow must be handled at run-time */ n = (sxi32)(pTos - pStack); } VmPopOperand(&pTos,n); break; } /* * CVT_INT: * * * * * Force the top of the stack to be an integer. */ case PH7_OP_CVT_INT: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if((pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_INT); break; /* * CVT_REAL: * * * * * Force the top of the stack to be a real. */ case PH7_OP_CVT_REAL: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if((pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_REAL); break; /* * CVT_STR: * * * * * Force the top of the stack to be a string. */ case PH7_OP_CVT_STR: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pTos); } break; /* * CVT_BOOL: * * * * * Force the top of the stack to be a boolean. */ case PH7_OP_CVT_BOOL: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } break; /* * CVT_NULL: * * * * * Nullify the top of the stack. */ case PH7_OP_CVT_NULL: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif PH7_MemObjRelease(pTos); break; /* * CVT_NUMC: * * * * * Force the top of the stack to be a numeric type (integer,real or both). */ case PH7_OP_CVT_NUMC: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a numeric cast */ PH7_MemObjToNumeric(pTos); break; /* * CVT_ARRAY: * * * * * Force the top of the stack to be a hashmap aka 'array'. */ case PH7_OP_CVT_ARRAY: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a hashmap cast */ rc = PH7_MemObjToHashmap(pTos); if( rc != SXRET_OK ){ /* Not so fatal,emit a simple warning */ PH7_VmThrowError(&(*pVm),0,PH7_CTX_WARNING, "PH7 engine is running out of memory while performing an array cast"); } break; /* * CVT_OBJ: * * * * * Force the top of the stack to be a class instance (Object in the PHP jargon). */ case PH7_OP_CVT_OBJ: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & MEMOBJ_OBJ) == 0 ){ /* Force a 'stdClass()' cast */ PH7_MemObjToObject(pTos); } break; /* * ERR_CTRL * * * * * Error control operator. */ case PH7_OP_ERR_CTRL: /* * TICKET 1433-038: * As of this version ,the error control operator '@' is a no-op,simply * use the public API,to control error output. */ break; /* * IS_A * * * * * Pop the top two operands from the stack and check whether the first operand * is an object and is an instance of the second operand (which must be a string * holding a class name or an object). * Push TRUE on success. FALSE otherwise. */ case PH7_OP_IS_A:{ ph7_value *pNos = &pTos[-1]; sxi32 iRes = 0; /* assume false by default */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if( pNos->iFlags& MEMOBJ_OBJ ){ ph7_class_instance *pThis = (ph7_class_instance *)pNos->x.pOther; ph7_class *pClass = 0; /* Extract the target class */ if( pTos->iFlags & MEMOBJ_OBJ ){ /* Instance already loaded */ pClass = ((ph7_class_instance *)pTos->x.pOther)->pClass; }else if( pTos->iFlags & MEMOBJ_STRING && SyBlobLength(&pTos->sBlob) > 0 ){ /* Perform the query */ pClass = PH7_VmExtractClass(&(*pVm),(const char *)SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob),FALSE,0); } if( pClass ){ /* Perform the query */ iRes = VmInstanceOf(pThis->pClass,pClass); } } /* Push result */ VmPopOperand(&pTos,1); PH7_MemObjRelease(pTos); pTos->x.iVal = iRes; MemObjSetType(pTos,MEMOBJ_BOOL); break; } /* * LOADC P1 P2 * * * Load a constant [i.e: PHP_EOL,PHP_OS,__TIME__,...] indexed at P2 in the constant pool. * If P1 is set,then this constant is candidate for expansion via user installable callbacks. */ case PH7_OP_LOADC: { ph7_value *pObj; /* Reserve a room */ pTos++; if( (pObj = (ph7_value *)SySetAt(&pVm->aLitObj,pInstr->iP2)) != 0 ){ if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){ SyHashEntry *pEntry; /* Candidate for expansion via user defined callbacks */ pEntry = SyHashGet(&pVm->hConstant,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob)); if( pEntry ){ ph7_constant *pCons = (ph7_constant *)pEntry->pUserData; /* Set a NULL default value */ MemObjSetType(pTos,MEMOBJ_NULL); SyBlobReset(&pTos->sBlob); /* Invoke the callback and deal with the expanded value */ pCons->xExpand(pTos,pCons->pUserData); /* Mark as constant */ pTos->nIdx = SXU32_HIGH; break; } } PH7_MemObjLoad(pObj,pTos); }else{ /* Set a NULL value */ MemObjSetType(pTos,MEMOBJ_NULL); } /* Mark as constant */ pTos->nIdx = SXU32_HIGH; break; } /* * LOAD: P1 * P3 * * Load a variable where it's name is taken from the top of the stack or * from the P3 operand. * If P1 is set,then perform a lookup only.In other words do not create * the variable if non existent and push the NULL constant instead. */ case PH7_OP_LOAD:{ ph7_value *pObj; SyString sName; if( pInstr->p3 == 0 ){ /* Take the variable name from the top of the stack */ #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a string cast */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pTos); } SyStringInitFromBuf(&sName,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); }else{ SyStringInitFromBuf(&sName,pInstr->p3,SyStrlen((const char *)pInstr->p3)); /* Reserve a room for the target object */ pTos++; } /* Extract the requested memory object */ pObj = VmExtractMemObj(&(*pVm),&sName,pInstr->p3 ? FALSE : TRUE,pInstr->iP1 != 1); if( pObj == 0 ){ if( pInstr->iP1 ){ /* Variable not found,load NULL */ if( !pInstr->p3 ){ PH7_MemObjRelease(pTos); }else{ MemObjSetType(pTos,MEMOBJ_NULL); } pTos->nIdx = SXU32_HIGH; /* Mark as constant */ break; }else{ /* Fatal error */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Fatal, PH7 engine is running out of memory while loading variable '%z'",&sName); goto Abort; } } /* Load variable contents */ PH7_MemObjLoad(pObj,pTos); pTos->nIdx = pObj->nIdx; break; } /* * LOAD_MAP P1 * * * * Allocate a new empty hashmap (array in the PHP jargon) and push it on the stack. * If the P1 operand is greater than zero then pop P1 elements from the * stack and insert them (key => value pair) in the new hashmap. */ case PH7_OP_LOAD_MAP: { ph7_hashmap *pMap; /* Allocate a new hashmap instance */ pMap = PH7_NewHashmap(&(*pVm),0,0); if( pMap == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Fatal, PH7 engine is running out of memory while loading array at instruction #:%d",pc); goto Abort; } if( pInstr->iP1 > 0 ){ ph7_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */ /* Perform the insertion */ while( pEntry < pTos ){ if( pEntry[1].iFlags & MEMOBJ_REFERENCE ){ /* Insertion by reference */ PH7_HashmapInsertByRef(pMap, (pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry, (sxu32)pEntry[1].x.iVal ); }else{ /* Standard insertion */ PH7_HashmapInsert(pMap, (pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry, &pEntry[1] ); } /* Next pair on the stack */ pEntry += 2; } /* Pop P1 elements */ VmPopOperand(&pTos,pInstr->iP1); } /* Push the hashmap */ pTos++; pTos->nIdx = SXU32_HIGH; pTos->x.pOther = pMap; MemObjSetType(pTos,MEMOBJ_HASHMAP); break; } /* * LOAD_LIST: P1 * * * * Assign hashmap entries values to the top P1 entries. * This is the VM implementation of the list() PHP construct. * Caveats: * This implementation support only a single nesting level. */ case PH7_OP_LOAD_LIST: { ph7_value *pEntry; if( pInstr->iP1 <= 0 ){ /* Empty list,break immediately */ break; } pEntry = &pTos[-pInstr->iP1+1]; #ifdef UNTRUST if( &pEntry[-1] < pStack ){ goto Abort; } #endif if( pEntry[-1].iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pEntry[-1].x.pOther; ph7_hashmap_node *pNode; ph7_value sKey,*pObj; /* Start Copying */ PH7_MemObjInitFromInt(&(*pVm),&sKey,0); while( pEntry <= pTos ){ if( pEntry->nIdx != SXU32_HIGH /* Variable not constant */ ){ rc = PH7_HashmapLookup(pMap,&sKey,&pNode); if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pEntry->nIdx)) != 0 ){ if( rc == SXRET_OK ){ /* Store node value */ PH7_HashmapExtractNodeValue(pNode,pObj,TRUE); }else{ /* Nullify the variable */ PH7_MemObjRelease(pObj); } } } sKey.x.iVal++; /* Next numeric index */ pEntry++; } } VmPopOperand(&pTos,pInstr->iP1); break; } /* * LOAD_IDX: P1 P2 * * * Load a hasmap entry where it's index (either numeric or string) is taken * from the stack. * If the index does not refer to a valid element,then push the NULL constant * instead. */ case PH7_OP_LOAD_IDX: { ph7_hashmap_node *pNode = 0; /* cc warning */ ph7_hashmap *pMap = 0; ph7_value *pIdx; pIdx = 0; if( pInstr->iP1 == 0 ){ if( !pInstr->iP2){ /* No available index,load NULL */ if( pTos >= pStack ){ PH7_MemObjRelease(pTos); }else{ /* TICKET 1433-020: Empty stack */ pTos++; MemObjSetType(pTos,MEMOBJ_NULL); pTos->nIdx = SXU32_HIGH; } /* Emit a notice */ PH7_VmThrowError(&(*pVm),0,PH7_CTX_NOTICE, "Array: Attempt to access an undefined index,PH7 is loading NULL"); break; } }else{ pIdx = pTos; pTos--; } if( pTos->iFlags & MEMOBJ_STRING ){ /* String access */ if( pIdx ){ sxu32 nOfft; if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){ /* Force an int cast */ PH7_MemObjToInteger(pIdx); } nOfft = (sxu32)pIdx->x.iVal; if( nOfft >= SyBlobLength(&pTos->sBlob) ){ /* Invalid offset,load null */ PH7_MemObjRelease(pTos); }else{ const char *zData = (const char *)SyBlobData(&pTos->sBlob); int c = zData[nOfft]; PH7_MemObjRelease(pTos); MemObjSetType(pTos,MEMOBJ_STRING); SyBlobAppend(&pTos->sBlob,(const void *)&c,sizeof(char)); } }else{ /* No available index,load NULL */ MemObjSetType(pTos,MEMOBJ_NULL); } break; } if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){ if( pTos->nIdx != SXU32_HIGH ){ ph7_value *pObj; if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjToHashmap(pObj); PH7_MemObjLoad(pObj,pTos); } } } rc = SXERR_NOTFOUND; /* Assume the index is invalid */ if( pTos->iFlags & MEMOBJ_HASHMAP ){ /* Point to the hashmap */ pMap = (ph7_hashmap *)pTos->x.pOther; if( pIdx ){ /* Load the desired entry */ rc = PH7_HashmapLookup(pMap,pIdx,&pNode); } if( rc != SXRET_OK && pInstr->iP2 ){ /* Create a new empty entry */ rc = PH7_HashmapInsert(pMap,pIdx,0); if( rc == SXRET_OK ){ /* Point to the last inserted entry */ pNode = pMap->pLast; } } } if( pIdx ){ PH7_MemObjRelease(pIdx); } if( rc == SXRET_OK ){ /* Load entry contents */ if( pMap->iRef < 2 ){ /* TICKET 1433-42: Array will be deleted shortly,so we will make a copy * of the entry value,rather than pointing to it. */ pTos->nIdx = SXU32_HIGH; PH7_HashmapExtractNodeValue(pNode,pTos,TRUE); }else{ pTos->nIdx = pNode->nValIdx; PH7_HashmapExtractNodeValue(pNode,pTos,FALSE); PH7_HashmapUnref(pMap); } }else{ /* No such entry,load NULL */ PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; } break; } /* * LOAD_CLOSURE * * P3 * * Set-up closure environment described by the P3 oeprand and push the closure * name in the stack. */ case PH7_OP_LOAD_CLOSURE:{ ph7_vm_func *pFunc = (ph7_vm_func *)pInstr->p3; if( pFunc->iFlags & VM_FUNC_CLOSURE ){ ph7_vm_func_closure_env *aEnv,*pEnv,sEnv; ph7_vm_func *pClosure; char *zName; sxu32 mLen; sxu32 n; /* Create a new VM function */ pClosure = (ph7_vm_func *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_vm_func)); /* Generate an unique closure name */ zName = (char *)SyMemBackendAlloc(&pVm->sAllocator,sizeof("[closure_]")+64); if( pClosure == 0 || zName == 0){ PH7_VmThrowError(pVm,0,E_ERROR,"Fatal: PH7 is running out of memory while creating closure environment"); goto Abort; } mLen = SyBufferFormat(zName,sizeof("[closure_]")+64,"[closure_%d]",pVm->closure_cnt++); while( SyHashGet(&pVm->hFunction,zName,mLen) != 0 && mLen < (sizeof("[closure_]")+60/* not 64 */) ){ mLen = SyBufferFormat(zName,sizeof("[closure_]")+64,"[closure_%d]",pVm->closure_cnt++); } /* Zero the stucture */ SyZero(pClosure,sizeof(ph7_vm_func)); /* Perform a structure assignment on read-only items */ pClosure->aArgs = pFunc->aArgs; pClosure->aByteCode = pFunc->aByteCode; pClosure->aStatic = pFunc->aStatic; pClosure->iFlags = pFunc->iFlags; pClosure->pUserData = pFunc->pUserData; pClosure->sSignature = pFunc->sSignature; SyStringInitFromBuf(&pClosure->sName,zName,mLen); /* Register the closure */ PH7_VmInstallUserFunction(pVm,pClosure,0); /* Set up closure environment */ SySetInit(&pClosure->aClosureEnv,&pVm->sAllocator,sizeof(ph7_vm_func_closure_env)); aEnv = (ph7_vm_func_closure_env *)SySetBasePtr(&pFunc->aClosureEnv); for( n = 0 ; n < SySetUsed(&pFunc->aClosureEnv) ; ++n ){ ph7_value *pValue; pEnv = &aEnv[n]; sEnv.sName = pEnv->sName; sEnv.iFlags = pEnv->iFlags; sEnv.nIdx = SXU32_HIGH; PH7_MemObjInit(pVm,&sEnv.sValue); if( sEnv.iFlags & VM_FUNC_ARG_BY_REF ){ /* Pass by reference */ PH7_VmThrowError(pVm,0,PH7_CTX_WARNING, "Closure: Pass by reference is disabled in the current release of the PH7 engine,PH7 is switching to pass by value" ); } /* Standard pass by value */ pValue = VmExtractMemObj(pVm,&sEnv.sName,FALSE,FALSE); if( pValue ){ /* Copy imported value */ PH7_MemObjStore(pValue,&sEnv.sValue); } /* Insert the imported variable */ SySetPut(&pClosure->aClosureEnv,(const void *)&sEnv); } /* Finally,load the closure name on the stack */ pTos++; PH7_MemObjStringAppend(pTos,zName,mLen); } break; } /* * STORE * P2 P3 * * Perform a store (Assignment) operation. */ case PH7_OP_STORE: { ph7_value *pObj; SyString sName; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( pInstr->iP2 ){ sxu32 nIdx; /* Member store operation */ nIdx = pTos->nIdx; VmPopOperand(&pTos,1); if( nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute,PH7 is loading NULL"); pTos->nIdx = SXU32_HIGH; }else{ /* Point to the desired memory object */ pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( pObj ){ /* Perform the store operation */ PH7_MemObjStore(pTos,pObj); } } break; }else if( pInstr->p3 == 0 ){ /* Take the variable name from the next on the stack */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } SyStringInitFromBuf(&sName,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); pTos--; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif }else{ SyStringInitFromBuf(&sName,pInstr->p3,SyStrlen((const char *)pInstr->p3)); } /* Extract the desired variable and if not available dynamically create it */ pObj = VmExtractMemObj(&(*pVm),&sName,pInstr->p3 ? FALSE : TRUE,TRUE); if( pObj == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Fatal, PH7 engine is running out of memory while loading variable '%z'",&sName); goto Abort; } if( !pInstr->p3 ){ PH7_MemObjRelease(&pTos[1]); } /* Perform the store operation */ PH7_MemObjStore(pTos,pObj); break; } /* * STORE_IDX: P1 * P3 * STORE_IDX_R: P1 * P3 * * Perfrom a store operation an a hashmap entry. */ case PH7_OP_STORE_IDX: case PH7_OP_STORE_IDX_REF: { ph7_hashmap *pMap = 0; /* cc warning */ ph7_value *pKey; sxu32 nIdx; if( pInstr->iP1 ){ /* Key is next on stack */ pKey = pTos; pTos--; }else{ pKey = 0; } nIdx = pTos->nIdx; if( pTos->iFlags & MEMOBJ_HASHMAP ){ /* Hashmap already loaded */ pMap = (ph7_hashmap *)pTos->x.pOther; if( pMap->iRef < 2 ){ /* TICKET 1433-48: Prevent garbage collection */ pMap->iRef = 2; } }else{ ph7_value *pObj; pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( pObj == 0 ){ if( pKey ){ PH7_MemObjRelease(pKey); } VmPopOperand(&pTos,1); break; } /* Phase#1: Load the array */ if( (pObj->iFlags & MEMOBJ_STRING) && (pInstr->iOp != PH7_OP_STORE_IDX_REF) ){ VmPopOperand(&pTos,1); if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } if( pKey == 0 ){ /* Append string */ if( SyBlobLength(&pTos->sBlob) > 0 ){ SyBlobAppend(&pObj->sBlob,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); } }else{ sxu32 nOfft; if((pKey->iFlags & MEMOBJ_INT)){ /* Force an int cast */ PH7_MemObjToInteger(pKey); } nOfft = (sxu32)pKey->x.iVal; if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){ const char *zBlob = (const char *)SyBlobData(&pTos->sBlob); char *zData = (char *)SyBlobData(&pObj->sBlob); zData[nOfft] = zBlob[0]; }else{ if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){ /* Perform an append operation */ SyBlobAppend(&pObj->sBlob,SyBlobData(&pTos->sBlob),sizeof(char)); } } } if( pKey ){ PH7_MemObjRelease(pKey); } break; }else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* Force a hashmap cast */ rc = PH7_MemObjToHashmap(pObj); if( rc != SXRET_OK ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Fatal, PH7 engine is running out of memory while creating a new array"); goto Abort; } } pMap = (ph7_hashmap *)pObj->x.pOther; } VmPopOperand(&pTos,1); /* Phase#2: Perform the insertion */ if( pInstr->iOp == PH7_OP_STORE_IDX_REF && pTos->nIdx != SXU32_HIGH ){ /* Insertion by reference */ PH7_HashmapInsertByRef(pMap,pKey,pTos->nIdx); }else{ PH7_HashmapInsert(pMap,pKey,pTos); } if( pKey ){ PH7_MemObjRelease(pKey); } break; } /* * INCR: P1 * * * * Force a numeric cast and increment the top of the stack by 1. * If the P1 operand is set then perform a duplication of the top of * the stack and increment after that. */ case PH7_OP_INCR: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES)) == 0 ){ if( pTos->nIdx != SXU32_HIGH ){ ph7_value *pObj; if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ /* Force a numeric cast */ PH7_MemObjToNumeric(pObj); if( pObj->iFlags & MEMOBJ_REAL ){ pObj->rVal++; /* Try to get an integer representation */ PH7_MemObjTryInteger(pTos); }else{ pObj->x.iVal++; MemObjSetType(pTos,MEMOBJ_INT); } if( pInstr->iP1 ){ /* Pre-icrement */ PH7_MemObjStore(pObj,pTos); } } }else{ if( pInstr->iP1 ){ /* Force a numeric cast */ PH7_MemObjToNumeric(pTos); /* Pre-increment */ if( pTos->iFlags & MEMOBJ_REAL ){ pTos->rVal++; /* Try to get an integer representation */ PH7_MemObjTryInteger(pTos); }else{ pTos->x.iVal++; MemObjSetType(pTos,MEMOBJ_INT); } } } } break; /* * DECR: P1 * * * * Force a numeric cast and decrement the top of the stack by 1. * If the P1 operand is set then perform a duplication of the top of the stack * and decrement after that. */ case PH7_OP_DECR: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){ /* Force a numeric cast */ PH7_MemObjToNumeric(pTos); if( pTos->nIdx != SXU32_HIGH ){ ph7_value *pObj; if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ /* Force a numeric cast */ PH7_MemObjToNumeric(pObj); if( pObj->iFlags & MEMOBJ_REAL ){ pObj->rVal--; /* Try to get an integer representation */ PH7_MemObjTryInteger(pTos); }else{ pObj->x.iVal--; MemObjSetType(pTos,MEMOBJ_INT); } if( pInstr->iP1 ){ /* Pre-icrement */ PH7_MemObjStore(pObj,pTos); } } }else{ if( pInstr->iP1 ){ /* Pre-increment */ if( pTos->iFlags & MEMOBJ_REAL ){ pTos->rVal--; /* Try to get an integer representation */ PH7_MemObjTryInteger(pTos); }else{ pTos->x.iVal--; MemObjSetType(pTos,MEMOBJ_INT); } } } } break; /* * UMINUS: * * * * * Perform a unary minus operation. */ case PH7_OP_UMINUS: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a numeric (integer,real or both) cast */ PH7_MemObjToNumeric(pTos); if( pTos->iFlags & MEMOBJ_REAL ){ pTos->rVal = -pTos->rVal; } if( pTos->iFlags & MEMOBJ_INT ){ pTos->x.iVal = -pTos->x.iVal; } break; /* * UPLUS: * * * * * Perform a unary plus operation. */ case PH7_OP_UPLUS: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a numeric (integer,real or both) cast */ PH7_MemObjToNumeric(pTos); if( pTos->iFlags & MEMOBJ_REAL ){ pTos->rVal = +pTos->rVal; } if( pTos->iFlags & MEMOBJ_INT ){ pTos->x.iVal = +pTos->x.iVal; } break; /* * OP_LNOT: * * * * * Interpret the top of the stack as a boolean value. Replace it * with its complement. */ case PH7_OP_LNOT: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force a boolean cast */ if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } pTos->x.iVal = !pTos->x.iVal; break; /* * OP_BITNOT: * * * * * Interpret the top of the stack as an value.Replace it * with its ones-complement. */ case PH7_OP_BITNOT: #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Force an integer cast */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } pTos->x.iVal = ~pTos->x.iVal; break; /* OP_MUL * * * * OP_MUL_STORE * * * * * Pop the top two elements from the stack, multiply them together, * and push the result back onto the stack. */ case PH7_OP_MUL: case PH7_OP_MUL_STORE: { ph7_value *pNos = &pTos[-1]; /* Force the operand to be numeric */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif PH7_MemObjToNumeric(pTos); PH7_MemObjToNumeric(pNos); /* Perform the requested operation */ if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ /* Floating point arithemic */ ph7_real a,b,r; if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } a = pNos->rVal; b = pTos->rVal; r = a * b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); }else{ /* Integer arithmetic */ sxi64 a,b,r; a = pNos->x.iVal; b = pTos->x.iVal; r = a * b; /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); } if( pInstr->iOp == PH7_OP_MUL_STORE ){ ph7_value *pObj; if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } } VmPopOperand(&pTos,1); break; } /* OP_ADD * * * * * Pop the top two elements from the stack, add them together, * and push the result back onto the stack. */ case PH7_OP_ADD:{ ph7_value *pNos = &pTos[-1]; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Perform the addition */ PH7_MemObjAdd(pNos,pTos,FALSE); VmPopOperand(&pTos,1); break; } /* * OP_ADD_STORE * * * * * Pop the top two elements from the stack, add them together, * and push the result back onto the stack. */ case PH7_OP_ADD_STORE:{ ph7_value *pNos = &pTos[-1]; ph7_value *pObj; sxu32 nIdx; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Perform the addition */ nIdx = pTos->nIdx; PH7_MemObjAdd(pTos,pNos,TRUE); /* Peform the store operation */ if( nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx)) != 0 ){ PH7_MemObjStore(pTos,pObj); } /* Ticket 1433-35: Perform a stack dup */ PH7_MemObjStore(pTos,pNos); VmPopOperand(&pTos,1); break; } /* OP_SUB * * * * * Pop the top two elements from the stack, subtract the * first (what was next on the stack) from the second (the * top of the stack) and push the result back onto the stack. */ case PH7_OP_SUB: { ph7_value *pNos = &pTos[-1]; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ /* Floating point arithemic */ ph7_real a,b,r; if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } a = pNos->rVal; b = pTos->rVal; r = a - b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); }else{ /* Integer arithmetic */ sxi64 a,b,r; a = pNos->x.iVal; b = pTos->x.iVal; r = a - b; /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); } VmPopOperand(&pTos,1); break; } /* OP_SUB_STORE * * * * * Pop the top two elements from the stack, subtract the * first (what was next on the stack) from the second (the * top of the stack) and push the result back onto the stack. */ case PH7_OP_SUB_STORE: { ph7_value *pNos = &pTos[-1]; ph7_value *pObj; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){ /* Floating point arithemic */ ph7_real a,b,r; if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } a = pTos->rVal; b = pNos->rVal; r = a - b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); }else{ /* Integer arithmetic */ sxi64 a,b,r; a = pTos->x.iVal; b = pNos->x.iVal; r = a - b; /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); } if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* * OP_MOD * * * * * Pop the top two elements from the stack, divide the * first (what was next on the stack) from the second (the * top of the stack) and push the remainder after division * onto the stack. * Note: Only integer arithemtic is allowed. */ case PH7_OP_MOD:{ ph7_value *pNos = &pTos[-1]; sxi64 a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pNos->x.iVal; b = pTos->x.iVal; if( b == 0 ){ r = 0; VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Division by zero %qd%%0",a); /* goto Abort; */ }else{ r = a%b; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); VmPopOperand(&pTos,1); break; } /* * OP_MOD_STORE * * * * * Pop the top two elements from the stack, divide the * first (what was next on the stack) from the second (the * top of the stack) and push the remainder after division * onto the stack. * Note: Only integer arithemtic is allowed. */ case PH7_OP_MOD_STORE: { ph7_value *pNos = &pTos[-1]; ph7_value *pObj; sxi64 a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pTos->x.iVal; b = pNos->x.iVal; if( b == 0 ){ r = 0; VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Division by zero %qd%%0",a); /* goto Abort; */ }else{ r = a%b; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* * OP_DIV * * * * * Pop the top two elements from the stack, divide the * first (what was next on the stack) from the second (the * top of the stack) and push the result onto the stack. * Note: Only floating point arithemtic is allowed. */ case PH7_OP_DIV:{ ph7_value *pNos = &pTos[-1]; ph7_real a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be real */ if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } /* Perform the requested operation */ a = pNos->rVal; b = pTos->rVal; if( b == 0 ){ /* Division by zero */ r = 0; PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Division by zero"); /* goto Abort; */ }else{ r = a/b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); } VmPopOperand(&pTos,1); break; } /* * OP_DIV_STORE * * * * * Pop the top two elements from the stack, divide the * first (what was next on the stack) from the second (the * top of the stack) and push the result onto the stack. * Note: Only floating point arithemtic is allowed. */ case PH7_OP_DIV_STORE:{ ph7_value *pNos = &pTos[-1]; ph7_value *pObj; ph7_real a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be real */ if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pTos); } if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pNos); } /* Perform the requested operation */ a = pTos->rVal; b = pNos->rVal; if( b == 0 ){ /* Division by zero */ r = 0; VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Division by zero %qd/0",a); /* goto Abort; */ }else{ r = a/b; /* Push the result */ pNos->rVal = r; MemObjSetType(pNos,MEMOBJ_REAL); /* Try to get an integer representation */ PH7_MemObjTryInteger(pNos); } if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* OP_BAND * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise AND of the * two elements. */ /* OP_BOR * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise OR of the * two elements. */ /* OP_BXOR * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise XOR of the * two elements. */ case PH7_OP_BAND: case PH7_OP_BOR: case PH7_OP_BXOR:{ ph7_value *pNos = &pTos[-1]; sxi64 a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pNos->x.iVal; b = pTos->x.iVal; switch(pInstr->iOp){ case PH7_OP_BOR_STORE: case PH7_OP_BOR: r = a|b; break; case PH7_OP_BXOR_STORE: case PH7_OP_BXOR: r = a^b; break; case PH7_OP_BAND_STORE: case PH7_OP_BAND: default: r = a&b; break; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); VmPopOperand(&pTos,1); break; } /* OP_BAND_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise AND of the * two elements. */ /* OP_BOR_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise OR of the * two elements. */ /* OP_BXOR_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the bit-wise XOR of the * two elements. */ case PH7_OP_BAND_STORE: case PH7_OP_BOR_STORE: case PH7_OP_BXOR_STORE:{ ph7_value *pNos = &pTos[-1]; ph7_value *pObj; sxi64 a,b,r; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pTos->x.iVal; b = pNos->x.iVal; switch(pInstr->iOp){ case PH7_OP_BOR_STORE: case PH7_OP_BOR: r = a|b; break; case PH7_OP_BXOR_STORE: case PH7_OP_BXOR: r = a^b; break; case PH7_OP_BAND_STORE: case PH7_OP_BAND: default: r = a&b; break; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* OP_SHL * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the second element shifted * left by N bits where N is the top element on the stack. * Note: Only integer arithmetic is allowed. */ /* OP_SHR * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the second element shifted * right by N bits where N is the top element on the stack. * Note: Only integer arithmetic is allowed. */ case PH7_OP_SHL: case PH7_OP_SHR: { ph7_value *pNos = &pTos[-1]; sxi64 a,r; sxi32 b; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pNos->x.iVal; b = (sxi32)pTos->x.iVal; if( pInstr->iOp == PH7_OP_SHL ){ r = a << b; }else{ r = a >> b; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); VmPopOperand(&pTos,1); break; } /* OP_SHL_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the second element shifted * left by N bits where N is the top element on the stack. * Note: Only integer arithmetic is allowed. */ /* OP_SHR_STORE * * * * * Pop the top two elements from the stack. Convert both elements * to integers. Push back onto the stack the second element shifted * right by N bits where N is the top element on the stack. * Note: Only integer arithmetic is allowed. */ case PH7_OP_SHL_STORE: case PH7_OP_SHR_STORE: { ph7_value *pNos = &pTos[-1]; ph7_value *pObj; sxi64 a,r; sxi32 b; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force the operands to be integer */ if( (pTos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pTos); } if( (pNos->iFlags & MEMOBJ_INT) == 0 ){ PH7_MemObjToInteger(pNos); } /* Perform the requested operation */ a = pTos->x.iVal; b = (sxi32)pNos->x.iVal; if( pInstr->iOp == PH7_OP_SHL_STORE ){ r = a << b; }else{ r = a >> b; } /* Push the result */ pNos->x.iVal = r; MemObjSetType(pNos,MEMOBJ_INT); if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pNos,pObj); } VmPopOperand(&pTos,1); break; } /* CAT: P1 * * * * Pop P1 elements from the stack. Concatenate them togeher and push the result * back. */ case PH7_OP_CAT:{ ph7_value *pNos,*pCur; if( pInstr->iP1 < 1 ){ pNos = &pTos[-1]; }else{ pNos = &pTos[-pInstr->iP1+1]; } #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force a string cast */ if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pNos); } pCur = &pNos[1]; while( pCur <= pTos ){ if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pCur); } /* Perform the concatenation */ if( SyBlobLength(&pCur->sBlob) > 0 ){ PH7_MemObjStringAppend(pNos,(const char *)SyBlobData(&pCur->sBlob),SyBlobLength(&pCur->sBlob)); } SyBlobRelease(&pCur->sBlob); pCur++; } pTos = pNos; break; } /* CAT_STORE: * * * * * Pop two elements from the stack. Concatenate them togeher and push the result * back. */ case PH7_OP_CAT_STORE:{ ph7_value *pNos = &pTos[-1]; ph7_value *pObj; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if((pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } if((pNos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pNos); } /* Perform the concatenation (Reverse order) */ if( SyBlobLength(&pNos->sBlob) > 0 ){ PH7_MemObjStringAppend(pTos,(const char *)SyBlobData(&pNos->sBlob),SyBlobLength(&pNos->sBlob)); } /* Perform the store operation */ if( pTos->nIdx == SXU32_HIGH ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Cannot perform assignment on a constant class attribute"); }else if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pTos->nIdx)) != 0 ){ PH7_MemObjStore(pTos,pObj); } PH7_MemObjStore(pTos,pNos); VmPopOperand(&pTos,1); break; } /* OP_AND: * * * * * Pop two values off the stack. Take the logical AND of the * two values and push the resulting boolean value back onto the * stack. */ /* OP_OR: * * * * * Pop two values off the stack. Take the logical OR of the * two values and push the resulting boolean value back onto the * stack. */ case PH7_OP_LAND: case PH7_OP_LOR: { ph7_value *pNos = &pTos[-1]; sxi32 v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force a boolean cast */ if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pNos); } v1 = pNos->x.iVal == 0 ? 1 : 0; v2 = pTos->x.iVal == 0 ? 1 : 0; if( pInstr->iOp == PH7_OP_LAND ){ static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; v1 = and_logic[v1*3+v2]; }else{ static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 }; v1 = or_logic[v1*3+v2]; } if( v1 == 2 ){ v1 = 1; } VmPopOperand(&pTos,1); pTos->x.iVal = v1 == 0 ? 1 : 0; MemObjSetType(pTos,MEMOBJ_BOOL); break; } /* OP_LXOR: * * * * * Pop two values off the stack. Take the logical XOR of the * two values and push the resulting boolean value back onto the * stack. * According to the PHP language reference manual: * $a xor $b is evaluated to TRUE if either $a or $b is * TRUE,but not both. */ case PH7_OP_LXOR:{ ph7_value *pNos = &pTos[-1]; sxi32 v = 0; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force a boolean cast */ if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pTos); } if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pNos); } if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){ v = 1; } VmPopOperand(&pTos,1); pTos->x.iVal = v; MemObjSetType(pTos,MEMOBJ_BOOL); break; } /* OP_EQ P1 P2 P3 * * Pop the top two elements from the stack. If they are equal, then * jump to instruction P2. Otherwise, continue to the next instruction. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ /* OP_NEQ P1 P2 P3 * * Pop the top two elements from the stack. If they are not equal, then * jump to instruction P2. Otherwise, continue to the next instruction. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ case PH7_OP_EQ: case PH7_OP_NEQ: { ph7_value *pNos = &pTos[-1]; /* Perform the comparison and act accordingly */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif rc = PH7_MemObjCmp(pNos,pTos,FALSE,0); if( pInstr->iOp == PH7_OP_EQ ){ rc = rc == 0; }else{ rc = rc != 0; } VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_TEQ P1 P2 * * * Pop the top two elements from the stack. If they have the same type and are equal * then jump to instruction P2. Otherwise, continue to the next instruction. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ case PH7_OP_TEQ: { ph7_value *pNos = &pTos[-1]; /* Perform the comparison and act accordingly */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif rc = PH7_MemObjCmp(pNos,pTos,TRUE,0) == 0; VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_TNE P1 P2 * * * Pop the top two elements from the stack.If they are not equal an they are not * of the same type, then jump to instruction P2. Otherwise, continue to the next * instruction. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. * */ case PH7_OP_TNE: { ph7_value *pNos = &pTos[-1]; /* Perform the comparison and act accordingly */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif rc = PH7_MemObjCmp(pNos,pTos,TRUE,0) != 0; VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_LT P1 P2 P3 * * Pop the top two elements from the stack. If the second element (the top of stack) * is less than the first (next on stack),then jump to instruction P2.Otherwise * continue to the next instruction. In other words, jump if pNosiOp == PH7_OP_LE ){ rc = rc < 1; }else{ rc = rc < 0; } VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_GT P1 P2 P3 * * Pop the top two elements from the stack. If the second element (the top of stack) * is greater than the first (next on stack),then jump to instruction P2.Otherwise * continue to the next instruction. In other words, jump if pNosiOp == PH7_OP_GE ){ rc = rc >= 0; }else{ rc = rc > 0; } VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* OP_SEQ P1 P2 * * Strict string comparison. * Pop the top two elements from the stack. If they are equal (pure text comparison) * then jump to instruction P2. Otherwise, continue to the next instruction. * If either operand is NULL then the comparison result is FALSE. * The SyMemcmp() routine is used for the comparison. For a numeric comparison * use PH7_OP_EQ. * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ /* OP_SNE P1 P2 * * Strict string comparison. * Pop the top two elements from the stack. If they are not equal (pure text comparison) * then jump to instruction P2. Otherwise, continue to the next instruction. * If either operand is NULL then the comparison result is FALSE. * The SyMemcmp() routine is used for the comparison. For a numeric comparison * use PH7_OP_EQ. * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the * stack if the jump would have been taken, or a 0 (FALSE) if not. */ case PH7_OP_SEQ: case PH7_OP_SNE: { ph7_value *pNos = &pTos[-1]; SyString s1,s2; /* Perform the comparison and act accordingly */ #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif /* Force a string cast */ if((pTos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pTos); } if((pNos->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pNos); } SyStringInitFromBuf(&s1,SyBlobData(&pNos->sBlob),SyBlobLength(&pNos->sBlob)); SyStringInitFromBuf(&s2,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); rc = SyStringCmp(&s1,&s2,SyMemcmp); if( pInstr->iOp == PH7_OP_NEQ ){ rc = rc != 0; }else{ rc = rc == 0; } VmPopOperand(&pTos,1); if( !pInstr->iP2 ){ /* Push comparison result without taking the jump */ PH7_MemObjRelease(pTos); pTos->x.iVal = rc; /* Invalidate any prior representation */ MemObjSetType(pTos,MEMOBJ_BOOL); }else{ if( rc ){ /* Jump to the desired location */ pc = pInstr->iP2 - 1; VmPopOperand(&pTos,1); } } break; } /* * OP_LOAD_REF * * * * Push the index of a referenced object on the stack. */ case PH7_OP_LOAD_REF: { sxu32 nIdx; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Extract memory object index */ nIdx = pTos->nIdx; if( nIdx != SXU32_HIGH /* Not a constant */ ){ /* Nullify the object */ PH7_MemObjRelease(pTos); /* Mark as constant and store the index on the top of the stack */ pTos->x.iVal = (sxi64)nIdx; pTos->nIdx = SXU32_HIGH; pTos->iFlags = MEMOBJ_INT|MEMOBJ_REFERENCE; } break; } /* * OP_STORE_REF * * P3 * Perform an assignment operation by reference. */ case PH7_OP_STORE_REF: { SyString sName = { 0 , 0 }; SyHashEntry *pEntry; sxu32 nIdx; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( pInstr->p3 == 0 ){ char *zName; /* Take the variable name from the Next on the stack */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } if( SyBlobLength(&pTos->sBlob) > 0 ){ zName = SyMemBackendStrDup(&pVm->sAllocator, (const char *)SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); if( zName ){ SyStringInitFromBuf(&sName,zName,SyBlobLength(&pTos->sBlob)); } } PH7_MemObjRelease(pTos); pTos--; }else{ SyStringInitFromBuf(&sName,pInstr->p3,SyStrlen((const char *)pInstr->p3)); } nIdx = pTos->nIdx; if(nIdx == SXU32_HIGH ){ if( (pTos->iFlags & (MEMOBJ_OBJ|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "Reference operator require a variable not a constant as it's right operand"); }else{ ph7_value *pObj; /* Extract the desired variable and if not available dynamically create it */ pObj = VmExtractMemObj(&(*pVm),&sName,FALSE,TRUE); if( pObj == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Fatal, PH7 engine is running out of memory while loading variable '%z'",&sName); goto Abort; } /* Perform the store operation */ PH7_MemObjStore(pTos,pObj); pTos->nIdx = pObj->nIdx; } }else if( sName.nByte > 0){ if( (pTos->iFlags & MEMOBJ_HASHMAP) && (pVm->pGlobal == (ph7_hashmap *)pTos->x.pOther) ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"$GLOBALS is a read-only array and therefore cannot be referenced"); }else{ VmFrame *pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } /* Query the local frame */ pEntry = SyHashGet(&pFrame->hVar,(const void *)sName.zString,sName.nByte); if( pEntry ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Referenced variable name '%z' already exists",&sName); }else{ rc = SyHashInsert(&pFrame->hVar,(const void *)sName.zString,sName.nByte,SX_INT_TO_PTR(nIdx)); if( pFrame->pParent == 0 ){ /* Insert in the $GLOBALS array */ VmHashmapRefInsert(pVm->pGlobal,sName.zString,sName.nByte,nIdx); } if( rc == SXRET_OK ){ PH7_VmRefObjInstall(&(*pVm),nIdx,SyHashLastEntry(&pFrame->hVar),0,0); } } } } break; } /* * OP_UPLINK P1 * * * Link a variable to the top active VM frame. * This is used to implement the 'global' PHP construct. */ case PH7_OP_UPLINK: { if( pVm->pFrame->pParent ){ ph7_value *pLink = &pTos[-pInstr->iP1+1]; SyString sName; /* Perform the link */ while( pLink <= pTos ){ if((pLink->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pLink); } SyStringInitFromBuf(&sName,SyBlobData(&pLink->sBlob),SyBlobLength(&pLink->sBlob)); if( sName.nByte > 0 ){ VmFrameLink(&(*pVm),&sName); } pLink++; } } VmPopOperand(&pTos,pInstr->iP1); break; } /* * OP_LOAD_EXCEPTION * P2 P3 * Push an exception in the corresponding container so that * it can be thrown later by the OP_THROW instruction. */ case PH7_OP_LOAD_EXCEPTION: { ph7_exception *pException = (ph7_exception *)pInstr->p3; VmFrame *pFrame; SySetPut(&pVm->aException,(const void *)&pException); /* Create the exception frame */ rc = VmEnterFrame(&(*pVm),0,0,&pFrame); if( rc != SXRET_OK ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Fatal PH7 engine is runnig out of memory"); goto Abort; } /* Mark the special frame */ pFrame->iFlags |= VM_FRAME_EXCEPTION; pFrame->iExceptionJump = pInstr->iP2; /* Point to the frame that trigger the exception */ pFrame = pFrame->pParent; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ pFrame = pFrame->pParent; } pException->pFrame = pFrame; break; } /* * OP_POP_EXCEPTION * * P3 * Pop a previously pushed exception from the corresponding container. */ case PH7_OP_POP_EXCEPTION: { ph7_exception *pException = (ph7_exception *)pInstr->p3; if( SySetUsed(&pVm->aException) > 0 ){ ph7_exception **apException; /* Pop the loaded exception */ apException = (ph7_exception **)SySetBasePtr(&pVm->aException); if( pException == apException[SySetUsed(&pVm->aException) - 1] ){ (void)SySetPop(&pVm->aException); } } pException->pFrame = 0; /* Leave the exception frame */ VmLeaveFrame(&(*pVm)); break; } /* * OP_THROW * P2 * * Throw an user exception. */ case PH7_OP_THROW: { VmFrame *pFrame = pVm->pFrame; sxu32 nJump = pInstr->iP2; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } /* Tell the upper layer that an exception was thrown */ pFrame->iFlags |= VM_FRAME_THROW; if( pTos->iFlags & MEMOBJ_OBJ ){ ph7_class_instance *pThis = (ph7_class_instance *)pTos->x.pOther; ph7_class *pException; /* Make sure the loaded object is an instance of the 'Exception' base class. */ pException = PH7_VmExtractClass(&(*pVm),"Exception",sizeof("Exception")-1,TRUE,0); if( pException == 0 || !VmInstanceOf(pThis->pClass,pException) ){ /* Exceptions must be valid objects derived from the Exception base class */ rc = VmUncaughtException(&(*pVm),pThis); if( rc == SXERR_ABORT ){ /* Abort processing immediately */ goto Abort; } }else{ /* Throw the exception */ rc = VmThrowException(&(*pVm),pThis); if( rc == SXERR_ABORT ){ /* Abort processing immediately */ goto Abort; } } }else{ /* Expecting a class instance */ VmUncaughtException(&(*pVm),0); if( rc == SXERR_ABORT ){ /* Abort processing immediately */ goto Abort; } } /* Pop the top entry */ VmPopOperand(&pTos,1); /* Perform an unconditional jump */ pc = nJump - 1; break; } /* * OP_FOREACH_INIT * P2 P3 * Prepare a foreach step. */ case PH7_OP_FOREACH_INIT: { ph7_foreach_info *pInfo = (ph7_foreach_info *)pInstr->p3; void *pName; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif if( SyStringLength(&pInfo->sValue) < 1 ){ /* Take the variable name from the top of the stack */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } /* Duplicate name */ if( SyBlobLength(&pTos->sBlob) > 0 ){ pName = SyMemBackendDup(&pVm->sAllocator,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); SyStringInitFromBuf(&pInfo->sValue,pName,SyBlobLength(&pTos->sBlob)); } VmPopOperand(&pTos,1); } if( (pInfo->iFlags & PH7_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(pTos); } /* Duplicate name */ if( SyBlobLength(&pTos->sBlob) > 0 ){ pName = SyMemBackendDup(&pVm->sAllocator,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); SyStringInitFromBuf(&pInfo->sKey,pName,SyBlobLength(&pTos->sBlob)); } VmPopOperand(&pTos,1); } /* Make sure we are dealing with a hashmap aka 'array' or an object */ if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){ /* Jump out of the loop */ if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_WARNING,"Invalid argument supplied for the foreach statement,expecting array or class instance"); } pc = pInstr->iP2 - 1; }else{ ph7_foreach_step *pStep; pStep = (ph7_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_foreach_step)); if( pStep == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"PH7 is running out of memory while preparing the 'foreach' step"); /* Jump out of the loop */ pc = pInstr->iP2 - 1; }else{ /* Zero the structure */ SyZero(pStep,sizeof(ph7_foreach_step)); /* Prepare the step */ pStep->iFlags = pInfo->iFlags; if( pTos->iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pTos->x.pOther; /* Reset the internal loop cursor */ PH7_HashmapResetLoopCursor(pMap); /* Mark the step */ pStep->iFlags |= PH7_4EACH_STEP_HASHMAP; pStep->xIter.pMap = pMap; pMap->iRef++; }else{ ph7_class_instance *pThis = (ph7_class_instance *)pTos->x.pOther; /* Reset the loop cursor */ SyHashResetLoopCursor(&pThis->hAttr); /* Mark the step */ pStep->iFlags |= PH7_4EACH_STEP_OBJECT; pStep->xIter.pThis = pThis; pThis->iRef++; } } if( SXRET_OK != SySetPut(&pInfo->aStep,(const void *)&pStep) ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"PH7 is running out of memory while preparing the 'foreach' step"); SyMemBackendPoolFree(&pVm->sAllocator,pStep); /* Jump out of the loop */ pc = pInstr->iP2 - 1; } } VmPopOperand(&pTos,1); break; } /* * OP_FOREACH_STEP * P2 P3 * Perform a foreach step. Jump to P2 at the end of the step. */ case PH7_OP_FOREACH_STEP: { ph7_foreach_info *pInfo = (ph7_foreach_info *)pInstr->p3; ph7_foreach_step **apStep,*pStep; ph7_value *pValue; VmFrame *pFrame; /* Peek the last step */ apStep = (ph7_foreach_step **)SySetBasePtr(&pInfo->aStep); pStep = apStep[SySetUsed(&pInfo->aStep) - 1]; pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pStep->iFlags & PH7_4EACH_STEP_HASHMAP ){ ph7_hashmap *pMap = pStep->xIter.pMap; ph7_hashmap_node *pNode; /* Extract the current node value */ pNode = PH7_HashmapGetNextEntry(pMap); if( pNode == 0 ){ /* No more entry to process */ pc = pInstr->iP2 - 1; /* Jump to this destination */ if( pStep->iFlags & PH7_4EACH_STEP_REF ){ /* Break the reference with the last element */ SyHashDeleteEntry(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue),0); } /* Automatically reset the loop cursor */ PH7_HashmapResetLoopCursor(pMap); /* Cleanup the mess left behind */ SyMemBackendPoolFree(&pVm->sAllocator,pStep); SySetPop(&pInfo->aStep); PH7_HashmapUnref(pMap); }else{ if( (pStep->iFlags & PH7_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){ ph7_value *pKey = VmExtractMemObj(&(*pVm),&pInfo->sKey,FALSE,TRUE); if( pKey ){ PH7_HashmapExtractNodeKey(pNode,pKey); } } if( pStep->iFlags & PH7_4EACH_STEP_REF ){ SyHashEntry *pEntry; /* Pass by reference */ pEntry = SyHashGet(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue)); if( pEntry ){ pEntry->pUserData = SX_INT_TO_PTR(pNode->nValIdx); }else{ SyHashInsert(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue), SX_INT_TO_PTR(pNode->nValIdx)); } }else{ /* Make a copy of the entry value */ pValue = VmExtractMemObj(&(*pVm),&pInfo->sValue,FALSE,TRUE); if( pValue ){ PH7_HashmapExtractNodeValue(pNode,pValue,TRUE); } } } }else{ ph7_class_instance *pThis = pStep->xIter.pThis; VmClassAttr *pVmAttr = 0; /* Stupid cc -06 warning */ SyHashEntry *pEntry; /* Point to the next attribute */ while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){ pVmAttr = (VmClassAttr *)pEntry->pUserData; /* Check access permission */ if( VmClassMemberAccess(&(*pVm),pThis->pClass,&pVmAttr->pAttr->sName, pVmAttr->pAttr->iProtection,FALSE) ){ break; /* Access is granted */ } } if( pEntry == 0 ){ /* Clean up the mess left behind */ pc = pInstr->iP2 - 1; /* Jump to this destination */ if( pStep->iFlags & PH7_4EACH_STEP_REF ){ /* Break the reference with the last element */ SyHashDeleteEntry(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue),0); } SyMemBackendPoolFree(&pVm->sAllocator,pStep); SySetPop(&pInfo->aStep); PH7_ClassInstanceUnref(pThis); }else{ SyString *pAttrName = &pVmAttr->pAttr->sName; ph7_value *pAttrValue; if( (pStep->iFlags & PH7_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0){ /* Fill with the current attribute name */ ph7_value *pKey = VmExtractMemObj(&(*pVm),&pInfo->sKey,FALSE,TRUE); if( pKey ){ SyBlobReset(&pKey->sBlob); SyBlobAppend(&pKey->sBlob,pAttrName->zString,pAttrName->nByte); MemObjSetType(pKey,MEMOBJ_STRING); } } /* Extract attribute value */ pAttrValue = PH7_ClassInstanceExtractAttrValue(pThis,pVmAttr); if( pAttrValue ){ if( pStep->iFlags & PH7_4EACH_STEP_REF ){ /* Pass by reference */ pEntry = SyHashGet(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue)); if( pEntry ){ pEntry->pUserData = SX_INT_TO_PTR(pVmAttr->nIdx); }else{ SyHashInsert(&pFrame->hVar,SyStringData(&pInfo->sValue),SyStringLength(&pInfo->sValue), SX_INT_TO_PTR(pVmAttr->nIdx)); } }else{ /* Make a copy of the attribute value */ pValue = VmExtractMemObj(&(*pVm),&pInfo->sValue,FALSE,TRUE); if( pValue ){ PH7_MemObjStore(pAttrValue,pValue); } } } } } break; } /* * OP_MEMBER P1 P2 * Load class attribute/method on the stack. */ case PH7_OP_MEMBER: { ph7_class_instance *pThis; ph7_value *pNos; SyString sName; if( !pInstr->iP1 ){ pNos = &pTos[-1]; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif if( pNos->iFlags & MEMOBJ_OBJ ){ ph7_class *pClass; /* Class already instantiated */ pThis = (ph7_class_instance *)pNos->x.pOther; /* Point to the instantiated class */ pClass = pThis->pClass; /* Extract attribute name first */ SyStringInitFromBuf(&sName,(const char *)SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); if( pInstr->iP2 ){ /* Method call */ ph7_class_method *pMeth = 0; if( sName.nByte > 0 ){ /* Extract the target method */ pMeth = PH7_ClassExtractMethod(pClass,sName.zString,sName.nByte); } if( pMeth == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Undefined class method '%z->%z',PH7 is loading NULL", &pClass->sName,&sName ); /* Call the '__Call()' magic method if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pClass,pThis,"__call",sizeof("__call")-1,&sName); /* Pop the method name from the stack */ VmPopOperand(&pTos,1); PH7_MemObjRelease(pTos); }else{ /* Push method name on the stack */ PH7_MemObjRelease(pTos); SyBlobAppend(&pTos->sBlob,SyStringData(&pMeth->sVmName),SyStringLength(&pMeth->sVmName)); MemObjSetType(pTos,MEMOBJ_STRING); } pTos->nIdx = SXU32_HIGH; }else{ /* Attribute access */ VmClassAttr *pObjAttr = 0; SyHashEntry *pEntry; /* Extract the target attribute */ if( sName.nByte > 0 ){ pEntry = SyHashGet(&pThis->hAttr,(const void *)sName.zString,sName.nByte); if( pEntry ){ /* Point to the attribute value */ pObjAttr = (VmClassAttr *)pEntry->pUserData; } } if( pObjAttr == 0 ){ /* No such attribute,load null */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Undefined class attribute '%z->%z',PH7 is loading NULL", &pClass->sName,&sName); /* Call the __get magic method if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pClass,pThis,"__get",sizeof("__get")-1,&sName); } VmPopOperand(&pTos,1); /* TICKET 1433-49: Deffer garbage collection until attribute loading. * This is due to the following case: * (new TestClass())->foo; */ pThis->iRef++; PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; /* Assume we are loading a constant */ if( pObjAttr ){ ph7_value *pValue = 0; /* cc warning */ /* Check attribute access */ if( VmClassMemberAccess(&(*pVm),pClass,&pObjAttr->pAttr->sName,pObjAttr->pAttr->iProtection,TRUE) ){ /* Load attribute */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pObjAttr->nIdx); if( pValue ){ if( pThis->iRef < 2 ){ /* Perform a store operation,rather than a load operation since * the class instance '$this' will be deleted shortly. */ PH7_MemObjStore(pValue,pTos); }else{ /* Simple load */ PH7_MemObjLoad(pValue,pTos); } if( (pObjAttr->pAttr->iFlags & PH7_CLASS_ATTR_CONSTANT) == 0 ){ if( pThis->iRef > 1 ){ /* Load attribute index */ pTos->nIdx = pObjAttr->nIdx; } } } } } /* Safely unreference the object */ PH7_ClassInstanceUnref(pThis); } }else{ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"'->': Expecting class instance as left operand,PH7 is loading NULL"); VmPopOperand(&pTos,1); PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; /* Assume we are loading a constant */ } }else{ /* Static member access using class name */ pNos = pTos; pThis = 0; if( !pInstr->p3 ){ SyStringInitFromBuf(&sName,(const char *)SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); pNos--; #ifdef UNTRUST if( pNos < pStack ){ goto Abort; } #endif }else{ /* Attribute name already computed */ SyStringInitFromBuf(&sName,pInstr->p3,SyStrlen((const char *)pInstr->p3)); } if( pNos->iFlags & (MEMOBJ_STRING|MEMOBJ_OBJ) ){ ph7_class *pClass = 0; if( pNos->iFlags & MEMOBJ_OBJ ){ /* Class already instantiated */ pThis = (ph7_class_instance *)pNos->x.pOther; pClass = pThis->pClass; pThis->iRef++; /* Deffer garbage collection */ }else{ /* Try to extract the target class */ if( SyBlobLength(&pNos->sBlob) > 0 ){ pClass = PH7_VmExtractClass(&(*pVm),(const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob),FALSE,0); } } if( pClass == 0 ){ /* Undefined class */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Call to undefined class '%.*s',PH7 is loading NULL", SyBlobLength(&pNos->sBlob),(const char *)SyBlobData(&pNos->sBlob) ); if( !pInstr->p3 ){ VmPopOperand(&pTos,1); } PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; }else{ if( pInstr->iP2 ){ /* Method call */ ph7_class_method *pMeth = 0; if( sName.nByte > 0 && (pClass->iFlags & PH7_CLASS_INTERFACE) == 0){ /* Extract the target method */ pMeth = PH7_ClassExtractMethod(pClass,sName.zString,sName.nByte); } if( pMeth == 0 || (pMeth->iFlags & PH7_CLASS_ATTR_ABSTRACT) ){ if( pMeth ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Cannot call abstract method '%z:%z',PH7 is loading NULL", &pClass->sName,&sName ); }else{ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Undefined class static method '%z::%z',PH7 is loading NULL", &pClass->sName,&sName ); /* Call the '__CallStatic()' magic method if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pClass,0,"__callStatic",sizeof("__callStatic")-1,&sName); } /* Pop the method name from the stack */ if( !pInstr->p3 ){ VmPopOperand(&pTos,1); } PH7_MemObjRelease(pTos); }else{ /* Push method name on the stack */ PH7_MemObjRelease(pTos); SyBlobAppend(&pTos->sBlob,SyStringData(&pMeth->sVmName),SyStringLength(&pMeth->sVmName)); MemObjSetType(pTos,MEMOBJ_STRING); } pTos->nIdx = SXU32_HIGH; }else{ /* Attribute access */ ph7_class_attr *pAttr = 0; /* Extract the target attribute */ if( sName.nByte > 0 ){ pAttr = PH7_ClassExtractAttribute(pClass,sName.zString,sName.nByte); } if( pAttr == 0 ){ /* No such attribute,load null */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Undefined class attribute '%z::%z',PH7 is loading NULL", &pClass->sName,&sName); /* Call the __get magic method if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pClass,0,"__get",sizeof("__get")-1,&sName); } /* Pop the attribute name from the stack */ if( !pInstr->p3 ){ VmPopOperand(&pTos,1); } PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; if( pAttr ){ if( (pAttr->iFlags & (PH7_CLASS_ATTR_STATIC|PH7_CLASS_ATTR_CONSTANT)) == 0 ){ /* Access to a non static attribute */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Access to a non-static class attribute '%z::%z',PH7 is loading NULL", &pClass->sName,&pAttr->sName ); }else{ ph7_value *pValue; /* Check if the access to the attribute is allowed */ if( VmClassMemberAccess(&(*pVm),pClass,&pAttr->sName,pAttr->iProtection,TRUE) ){ /* Load the desired attribute */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pAttr->nIdx); if( pValue ){ PH7_MemObjLoad(pValue,pTos); if( pAttr->iFlags & PH7_CLASS_ATTR_STATIC ){ /* Load index number */ pTos->nIdx = pAttr->nIdx; } } } } } } if( pThis ){ /* Safely unreference the object */ PH7_ClassInstanceUnref(pThis); } } }else{ /* Pop operands */ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR,"Invalid class name,PH7 is loading NULL"); if( !pInstr->p3 ){ VmPopOperand(&pTos,1); } PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; } } break; } /* * OP_NEW P1 * * * * Create a new class instance (Object in the PHP jargon) and push that object on the stack. */ case PH7_OP_NEW: { ph7_value *pArg = &pTos[-pInstr->iP1]; /* Constructor arguments (if available) */ ph7_class *pClass = 0; ph7_class_instance *pNew; if( (pTos->iFlags & MEMOBJ_STRING) && SyBlobLength(&pTos->sBlob) > 0 ){ /* Try to extract the desired class */ pClass = PH7_VmExtractClass(&(*pVm),(const char *)SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob),TRUE /* Only loadable class but not 'interface' or 'abstract' class*/,0); }else if( pTos->iFlags & MEMOBJ_OBJ ){ /* Take the base class from the loaded instance */ pClass = ((ph7_class_instance *)pTos->x.pOther)->pClass; } if( pClass == 0 ){ /* No such class */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"Class '%.*s' is not defined,PH7 is loading NULL", SyBlobLength(&pTos->sBlob),(const char *)SyBlobData(&pTos->sBlob) ); PH7_MemObjRelease(pTos); if( pInstr->iP1 > 0 ){ /* Pop given arguments */ VmPopOperand(&pTos,pInstr->iP1); } }else{ ph7_class_method *pCons; /* Create a new class instance */ pNew = PH7_NewClassInstance(&(*pVm),pClass); if( pNew == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Cannot create new class '%z' instance due to a memory failure,PH7 is loading NULL", &pClass->sName ); PH7_MemObjRelease(pTos); if( pInstr->iP1 > 0 ){ /* Pop given arguments */ VmPopOperand(&pTos,pInstr->iP1); } break; } /* Check if a constructor is available */ pCons = PH7_ClassExtractMethod(pClass,"__construct",sizeof("__construct")-1); if( pCons == 0 ){ SyString *pName = &pClass->sName; /* Check for a constructor with the same base class name */ pCons = PH7_ClassExtractMethod(pClass,pName->zString,pName->nByte); } if( pCons ){ /* Call the class constructor */ SySetReset(&aArg); while( pArg < pTos ){ SySetPut(&aArg,(const void *)&pArg); pArg++; } if( pVm->bErrReport ){ ph7_vm_func_arg *pFuncArg; sxu32 n; n = SySetUsed(&aArg); /* Emit a notice for missing arguments */ while( n < SySetUsed(&pCons->sFunc.aArgs) ){ pFuncArg = (ph7_vm_func_arg *)SySetAt(&pCons->sFunc.aArgs,n); if( pFuncArg ){ if( SySetUsed(&pFuncArg->aByteCode) < 1 ){ VmErrorFormat(&(*pVm),PH7_CTX_NOTICE,"Missing constructor argument %u($%z) for class '%z'", n+1,&pFuncArg->sName,&pClass->sName); } } n++; } } PH7_VmCallClassMethod(&(*pVm),pNew,pCons,0,(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg)); /* TICKET 1433-52: Unsetting $this in the constructor body */ if( pNew->iRef < 1 ){ pNew->iRef = 1; } } if( pInstr->iP1 > 0 ){ /* Pop given arguments */ VmPopOperand(&pTos,pInstr->iP1); } PH7_MemObjRelease(pTos); pTos->x.pOther = pNew; MemObjSetType(pTos,MEMOBJ_OBJ); } break; } /* * OP_CLONE * * * * Perfome a clone operation. */ case PH7_OP_CLONE: { ph7_class_instance *pSrc,*pClone; #ifdef UNTRUST if( pTos < pStack ){ goto Abort; } #endif /* Make sure we are dealing with a class instance */ if( (pTos->iFlags & MEMOBJ_OBJ) == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "Clone: Expecting a class instance as left operand,PH7 is loading NULL"); PH7_MemObjRelease(pTos); break; } /* Point to the source */ pSrc = (ph7_class_instance *)pTos->x.pOther; /* Perform the clone operation */ pClone = PH7_CloneClassInstance(pSrc); PH7_MemObjRelease(pTos); if( pClone == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "Clone: cannot make an object clone due to a memory failure,PH7 is loading NULL"); }else{ /* Load the cloned object */ pTos->x.pOther = pClone; MemObjSetType(pTos,MEMOBJ_OBJ); } break; } /* * OP_SWITCH * * P3 * This is the bytecode implementation of the complex switch() PHP construct. */ case PH7_OP_SWITCH: { ph7_switch *pSwitch = (ph7_switch *)pInstr->p3; ph7_case_expr *aCase,*pCase; ph7_value sValue,sCaseValue; sxu32 n,nEntry; #ifdef UNTRUST if( pSwitch == 0 || pTos < pStack ){ goto Abort; } #endif /* Point to the case table */ aCase = (ph7_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr); nEntry = SySetUsed(&pSwitch->aCaseExpr); /* Select the appropriate case block to execute */ PH7_MemObjInit(pVm,&sValue); PH7_MemObjInit(pVm,&sCaseValue); for( n = 0 ; n < nEntry ; ++n ){ pCase = &aCase[n]; PH7_MemObjLoad(pTos,&sValue); /* Execute the case expression first */ VmLocalExec(pVm,&pCase->aByteCode,&sCaseValue); /* Compare the two expression */ rc = PH7_MemObjCmp(&sValue,&sCaseValue,FALSE,0); PH7_MemObjRelease(&sValue); PH7_MemObjRelease(&sCaseValue); if( rc == 0 ){ /* Value match,jump to this block */ pc = pCase->nStart - 1; break; } } VmPopOperand(&pTos,1); if( n >= nEntry ){ /* No approprite case to execute,jump to the default case */ if( pSwitch->nDefault > 0 ){ pc = pSwitch->nDefault - 1; }else{ /* No default case,jump out of this switch */ pc = pSwitch->nOut - 1; } } break; } /* * OP_CALL P1 * * * Call a PHP or a foreign function and push the return value of the called * function on the stack. */ case PH7_OP_CALL: { ph7_value *pArg = &pTos[-pInstr->iP1]; SyHashEntry *pEntry; SyString sName; /* Extract function name */ if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){ if( pTos->iFlags & MEMOBJ_HASHMAP ){ ph7_value sResult; SySetReset(&aArg); while( pArg < pTos ){ SySetPut(&aArg,(const void *)&pArg); pArg++; } PH7_MemObjInit(pVm,&sResult); /* May be a class instance and it's static method */ PH7_VmCallUserFunction(pVm,pTos,(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg),&sResult); SySetReset(&aArg); /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Copy result */ PH7_MemObjStore(&sResult,pTos); PH7_MemObjRelease(&sResult); }else{ if( pTos->iFlags & MEMOBJ_OBJ ){ ph7_class_instance *pThis = (ph7_class_instance *)pTos->x.pOther; /* Call the magic method '__invoke' if available */ PH7_ClassInstanceCallMagicMethod(&(*pVm),pThis->pClass,pThis,"__invoke",sizeof("__invoke")-1,0); }else{ /* Raise exception: Invalid function name */ VmErrorFormat(&(*pVm),PH7_CTX_WARNING,"Invalid function name,NULL will be returned"); } /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); } break; } SyStringInitFromBuf(&sName,SyBlobData(&pTos->sBlob),SyBlobLength(&pTos->sBlob)); /* Check for a compiled function first */ pEntry = SyHashGet(&pVm->hFunction,(const void *)sName.zString,sName.nByte); if( pEntry ){ ph7_vm_func_arg *aFormalArg; ph7_class_instance *pThis; ph7_value *pFrameStack; ph7_vm_func *pVmFunc; ph7_class *pSelf; VmFrame *pFrame; ph7_value *pObj; VmSlot sArg; sxu32 n; /* initialize fields */ pVmFunc = (ph7_vm_func *)pEntry->pUserData; pThis = 0; pSelf = 0; if( pVmFunc->iFlags & VM_FUNC_CLASS_METHOD ){ ph7_class_method *pMeth; /* Class method call */ ph7_value *pTarget = &pTos[-1]; if( pTarget >= pStack && (pTarget->iFlags & (MEMOBJ_STRING|MEMOBJ_OBJ|MEMOBJ_NULL)) ){ /* Extract the 'this' pointer */ if(pTarget->iFlags & MEMOBJ_OBJ ){ /* Instance already loaded */ pThis = (ph7_class_instance *)pTarget->x.pOther; pThis->iRef++; pSelf = pThis->pClass; } if( pSelf == 0 ){ if( (pTarget->iFlags & MEMOBJ_STRING) && SyBlobLength(&pTarget->sBlob) > 0 ){ /* "Late Static Binding" class name */ pSelf = PH7_VmExtractClass(&(*pVm),(const char *)SyBlobData(&pTarget->sBlob), SyBlobLength(&pTarget->sBlob),FALSE,0); } if( pSelf == 0 ){ pSelf = (ph7_class *)pVmFunc->pUserData; } } if( pThis == 0 ){ VmFrame *pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent ){ /* TICKET-1433-52: Make sure the '$this' variable is available to the current scope */ pThis = pFrame->pThis; if( pThis ){ pThis->iRef++; } } } VmPopOperand(&pTos,1); PH7_MemObjRelease(pTos); /* Synchronize pointers */ pArg = &pTos[-pInstr->iP1]; /* TICKET 1433-50: This is a very very unlikely scenario that occurs when the 'genius' * user have already computed the random generated unique class method name * and tries to call it outside it's context [i.e: global scope]. In that * case we have to synchrnoize pointers to avoid stack underflow. */ while( pArg < pStack ){ pArg++; } if( pSelf ){ /* Paranoid edition */ /* Check if the call is allowed */ pMeth = PH7_ClassExtractMethod(pSelf,pVmFunc->sName.zString,pVmFunc->sName.nByte); if( pMeth && pMeth->iProtection != PH7_CLASS_PROT_PUBLIC ){ if( !VmClassMemberAccess(&(*pVm),pSelf,&pVmFunc->sName,pMeth->iProtection,TRUE) ){ /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); break; } } } } } /* Check The recursion limit */ if( pVm->nRecursionDepth > pVm->nMaxDepth ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Recursion limit reached while invoking user function '%z',PH7 will set a NULL return value", &pVmFunc->sName); /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); break; } if( pVmFunc->pNextName ){ /* Function is candidate for overloading,select the appropriate function to call */ pVmFunc = VmOverload(&(*pVm),pVmFunc,pArg,(int)(pTos-pArg)); } /* Extract the formal argument set */ aFormalArg = (ph7_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs); /* Create a new VM frame */ rc = VmEnterFrame(&(*pVm),pVmFunc,pThis,&pFrame); if( rc != SXRET_OK ){ /* Raise exception: Out of memory */ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "PH7 is running out of memory while calling function '%z',NULL will be returned", &pVmFunc->sName); /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); break; } if( (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD) && pThis ){ /* Install the '$this' variable */ static const SyString sThis = { "this" , sizeof("this") - 1 }; pObj = VmExtractMemObj(&(*pVm),&sThis,FALSE,TRUE); if( pObj ){ /* Reflect the change */ pObj->x.pOther = pThis; MemObjSetType(pObj,MEMOBJ_OBJ); } } if( SySetUsed(&pVmFunc->aStatic) > 0 ){ ph7_vm_func_static_var *pStatic,*aStatic; /* Install static variables */ aStatic = (ph7_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic); for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){ pStatic = &aStatic[n]; if( pStatic->nIdx == SXU32_HIGH ){ /* Initialize the static variables */ pObj = VmReserveMemObj(&(*pVm),&pStatic->nIdx); if( pObj ){ /* Assume a NULL initialization value */ PH7_MemObjInit(&(*pVm),pObj); if( SySetUsed(&pStatic->aByteCode) > 0 ){ /* Evaluate initialization expression (Any complex expression) */ VmLocalExec(&(*pVm),&pStatic->aByteCode,pObj); } pObj->nIdx = pStatic->nIdx; }else{ continue; } } /* Install in the current frame */ SyHashInsert(&pFrame->hVar,SyStringData(&pStatic->sName),SyStringLength(&pStatic->sName), SX_INT_TO_PTR(pStatic->nIdx)); } } /* Push arguments in the local frame */ n = 0; while( pArg < pTos ){ if( n < SySetUsed(&pVmFunc->aArgs) ){ if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){ /* NULL values are redirected to default arguments */ rc = VmLocalExec(&(*pVm),&aFormalArg[n].aByteCode,pArg); if( rc == PH7_ABORT ){ goto Abort; } } /* Make sure the given arguments are of the correct type */ if( aFormalArg[n].nType > 0 ){ if ( aFormalArg[n].nType == SXU32_HIGH ){ /* Argument must be a class instance [i.e: object] */ SyString *pName = &aFormalArg[n].sClass; ph7_class *pClass; /* Try to extract the desired class */ pClass = PH7_VmExtractClass(&(*pVm),pName->zString,pName->nByte,TRUE,0); if( pClass ){ if( (pArg->iFlags & MEMOBJ_OBJ) == 0 ){ if( (pArg->iFlags & MEMOBJ_NULL) == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_WARNING, "Function '%z()':Argument %u must be an object of type '%z',PH7 is loading NULL instead", &pVmFunc->sName,n+1,pName); PH7_MemObjRelease(pArg); } }else{ ph7_class_instance *pThis = (ph7_class_instance *)pArg->x.pOther; /* Make sure the object is an instance of the given class */ if( ! VmInstanceOf(pThis->pClass,pClass) ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Function '%z()':Argument %u must be an object of type '%z',PH7 is loading NULL instead", &pVmFunc->sName,n+1,pName); PH7_MemObjRelease(pArg); } } } }else if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){ ProcMemObjCast xCast = PH7_MemObjCastMethod(aFormalArg[n].nType); /* Cast to the desired type */ xCast(pArg); } } if( aFormalArg[n].iFlags & VM_FUNC_ARG_BY_REF ){ /* Pass by reference */ if( pArg->nIdx == SXU32_HIGH ){ /* Expecting a variable,not a constant,raise an exception */ if((pArg->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES|MEMOBJ_NULL)) == 0){ VmErrorFormat(&(*pVm),PH7_CTX_WARNING, "Function '%z',%d argument: Pass by reference,expecting a variable not a " "constant,PH7 is switching to pass by value",&pVmFunc->sName,n+1); } /* Switch to pass by value */ pObj = VmExtractMemObj(&(*pVm),&aFormalArg[n].sName,FALSE,TRUE); }else{ SyHashEntry *pRefEntry; /* Install the referenced variable in the private function frame */ pRefEntry = SyHashGet(&pFrame->hVar,SyStringData(&aFormalArg[n].sName),SyStringLength(&aFormalArg[n].sName)); if( pRefEntry == 0 ){ SyHashInsert(&pFrame->hVar,SyStringData(&aFormalArg[n].sName), SyStringLength(&aFormalArg[n].sName),SX_INT_TO_PTR(pArg->nIdx)); sArg.nIdx = pArg->nIdx; sArg.pUserData = 0; SySetPut(&pFrame->sArg,(const void *)&sArg); } pObj = 0; } }else{ /* Pass by value,make a copy of the given argument */ pObj = VmExtractMemObj(&(*pVm),&aFormalArg[n].sName,FALSE,TRUE); } }else{ char zName[32]; SyString sName; /* Set a dummy name */ sName.nByte = SyBufferFormat(zName,sizeof(zName),"[%u]apArg",n); sName.zString = zName; /* Annonymous argument */ pObj = VmExtractMemObj(&(*pVm),&sName,TRUE,TRUE); } if( pObj ){ PH7_MemObjStore(pArg,pObj); /* Insert argument index */ sArg.nIdx = pObj->nIdx; sArg.pUserData = 0; SySetPut(&pFrame->sArg,(const void *)&sArg); } PH7_MemObjRelease(pArg); pArg++; ++n; } /* Set up closure environment */ if( pVmFunc->iFlags & VM_FUNC_CLOSURE ){ ph7_vm_func_closure_env *aEnv,*pEnv; ph7_value *pValue; sxu32 n; aEnv = (ph7_vm_func_closure_env *)SySetBasePtr(&pVmFunc->aClosureEnv); for(n = 0 ; n < SySetUsed(&pVmFunc->aClosureEnv) ; ++n ){ pEnv = &aEnv[n]; if( (pEnv->iFlags & VM_FUNC_ARG_IGNORE) && (pEnv->sValue.iFlags & MEMOBJ_NULL) ){ /* Do not install null value */ continue; } pValue = VmExtractMemObj(pVm,&pEnv->sName,FALSE,TRUE); if( pValue == 0 ){ continue; } /* Invalidate any prior representation */ PH7_MemObjRelease(pValue); /* Duplicate bound variable value */ PH7_MemObjStore(&pEnv->sValue,pValue); } } /* Process default values */ while( n < SySetUsed(&pVmFunc->aArgs) ){ if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){ pObj = VmExtractMemObj(&(*pVm),&aFormalArg[n].sName,FALSE,TRUE); if( pObj ){ /* Evaluate the default value and extract it's result */ rc = VmLocalExec(&(*pVm),&aFormalArg[n].aByteCode,pObj); if( rc == PH7_ABORT ){ goto Abort; } /* Insert argument index */ sArg.nIdx = pObj->nIdx; sArg.pUserData = 0; SySetPut(&pFrame->sArg,(const void *)&sArg); /* Make sure the default argument is of the correct type */ if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){ ProcMemObjCast xCast = PH7_MemObjCastMethod(aFormalArg[n].nType); /* Cast to the desired type */ xCast(pObj); } } } ++n; } /* Pop arguments,function name from the operand stack and assume the function * does not return anything. */ PH7_MemObjRelease(pTos); pTos = &pTos[-pInstr->iP1]; /* Allocate a new operand stack and evaluate the function body */ pFrameStack = VmNewOperandStack(&(*pVm),SySetUsed(&pVmFunc->aByteCode)); if( pFrameStack == 0 ){ /* Raise exception: Out of memory */ VmErrorFormat(&(*pVm),PH7_CTX_ERR,"PH7 is running out of memory while calling function '%z',NULL will be returned", &pVmFunc->sName); if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } break; } if( pSelf ){ /* Push class name */ SySetPut(&pVm->aSelf,(const void *)&pSelf); } /* Increment nesting level */ pVm->nRecursionDepth++; /* Execute function body */ rc = VmByteCodeExec(&(*pVm),(VmInstr *)SySetBasePtr(&pVmFunc->aByteCode),pFrameStack,-1,pTos,&n,FALSE); /* Decrement nesting level */ pVm->nRecursionDepth--; if( pSelf ){ /* Pop class name */ (void)SySetPop(&pVm->aSelf); } /* Cleanup the mess left behind */ if( (pVmFunc->iFlags & VM_FUNC_REF_RETURN) && rc == SXRET_OK ){ /* Return by reference,reflect that */ if( n != SXU32_HIGH ){ VmSlot *aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal); sxu32 i; /* Make sure the referenced object is not a local variable */ for( i = 0 ; i < SySetUsed(&pFrame->sLocal) ; ++i ){ if( n == aSlot[i].nIdx ){ pObj = (ph7_value *)SySetAt(&pVm->aMemObj,n); if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_OBJ|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_NOTICE, "Function '%z',return by reference: Cannot reference local variable,PH7 is switching to return by value", &pVmFunc->sName); } n = SXU32_HIGH; break; } } }else{ if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){ VmErrorFormat(&(*pVm),PH7_CTX_NOTICE, "Function '%z',return by reference: Cannot reference constant expression,PH7 is switching to return by value", &pVmFunc->sName); } } pTos->nIdx = n; } /* Cleanup the mess left behind */ if( rc != PH7_ABORT && ((pFrame->iFlags & VM_FRAME_THROW) || rc == PH7_EXCEPTION) ){ /* An exception was throw in this frame */ pFrame = pFrame->pParent; if( !is_callback && pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) && pFrame->iExceptionJump > 0 ){ /* Pop the resutlt */ VmPopOperand(&pTos,1); /* Jump to this destination */ pc = pFrame->iExceptionJump - 1; rc = PH7_OK; }else{ if( pFrame->pParent ){ rc = PH7_EXCEPTION; }else{ /* Continue normal execution */ rc = PH7_OK; } } } /* Free the operand stack */ SyMemBackendFree(&pVm->sAllocator,pFrameStack); /* Leave the frame */ VmLeaveFrame(&(*pVm)); if( rc == PH7_ABORT ){ /* Abort processing immeditaley */ goto Abort; }else if( rc == PH7_EXCEPTION ){ goto Exception; } }else{ ph7_user_func *pFunc; ph7_context sCtx; ph7_value sRet; /* Look for an installed foreign function */ pEntry = SyHashGet(&pVm->hHostFunction,(const void *)sName.zString,sName.nByte); if( pEntry == 0 ){ /* Call to undefined function */ VmErrorFormat(&(*pVm),PH7_CTX_WARNING,"Call to undefined function '%z',NULL will be returned",&sName); /* Pop given arguments */ if( pInstr->iP1 > 0 ){ VmPopOperand(&pTos,pInstr->iP1); } /* Assume a null return value so that the program continue it's execution normally */ PH7_MemObjRelease(pTos); break; } pFunc = (ph7_user_func *)pEntry->pUserData; /* Start collecting function arguments */ SySetReset(&aArg); while( pArg < pTos ){ SySetPut(&aArg,(const void *)&pArg); pArg++; } /* Assume a null return value */ PH7_MemObjInit(&(*pVm),&sRet); /* Init the call context */ VmInitCallContext(&sCtx,&(*pVm),pFunc,&sRet,0); /* Call the foreign function */ rc = pFunc->xFunc(&sCtx,(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg)); /* Release the call context */ VmReleaseCallContext(&sCtx); if( rc == PH7_ABORT ){ goto Abort; } if( pInstr->iP1 > 0 ){ /* Pop function name and arguments */ VmPopOperand(&pTos,pInstr->iP1); } /* Save foreign function return value */ PH7_MemObjStore(&sRet,pTos); PH7_MemObjRelease(&sRet); } break; } /* * OP_CONSUME: P1 * * * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack. */ case PH7_OP_CONSUME: { ph7_output_consumer *pCons = &pVm->sVmConsumer; ph7_value *pCur,*pOut = pTos; pOut = &pTos[-pInstr->iP1 + 1]; pCur = pOut; /* Start the consume process */ while( pOut <= pTos ){ /* Force a string cast */ if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pOut); } if( SyBlobLength(&pOut->sBlob) > 0 ){ /*SyBlobNullAppend(&pOut->sBlob);*/ /* Invoke the output consumer callback */ rc = pCons->xConsumer(SyBlobData(&pOut->sBlob),SyBlobLength(&pOut->sBlob),pCons->pUserData); if( pCons->xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += SyBlobLength(&pOut->sBlob); } SyBlobRelease(&pOut->sBlob); if( rc == SXERR_ABORT ){ /* Output consumer callback request an operation abort. */ goto Abort; } } pOut++; } pTos = &pCur[-1]; break; } } /* Switch() */ pc++; /* Next instruction in the stream */ } /* For(;;) */ Done: SySetRelease(&aArg); return SXRET_OK; Abort: SySetRelease(&aArg); while( pTos >= pStack ){ PH7_MemObjRelease(pTos); pTos--; } return PH7_ABORT; Exception: SySetRelease(&aArg); while( pTos >= pStack ){ PH7_MemObjRelease(pTos); pTos--; } return PH7_EXCEPTION; } /* * Execute as much of a local PH7 bytecode program as we can then return. * This function is a wrapper around [VmByteCodeExec()]. * See block-comment on that function for additional information. */ static sxi32 VmLocalExec(ph7_vm *pVm,SySet *pByteCode,ph7_value *pResult) { ph7_value *pStack; sxi32 rc; /* Allocate a new operand stack */ pStack = VmNewOperandStack(&(*pVm),SySetUsed(pByteCode)); if( pStack == 0 ){ return SXERR_MEM; } /* Execute the program */ rc = VmByteCodeExec(&(*pVm),(VmInstr *)SySetBasePtr(pByteCode),pStack,-1,&(*pResult),0,FALSE); /* Free the operand stack */ SyMemBackendFree(&pVm->sAllocator,pStack); /* Execution result */ return rc; } /* * Invoke any installed shutdown callbacks. * Shutdown callbacks are kept in a stack and are registered using one * or more calls to [register_shutdown_function()]. * These callbacks are invoked by the virtual machine when the program * execution ends. * Refer to the implementation of [register_shutdown_function()] for * additional information. */ static void VmInvokeShutdownCallbacks(ph7_vm *pVm) { VmShutdownCB *pEntry; ph7_value *apArg[10]; sxu32 n,nEntry; int i; /* Point to the stack of registered callbacks */ nEntry = SySetUsed(&pVm->aShutdown); for( i = 0 ; i < (int)SX_ARRAYSIZE(apArg) ; i++ ){ apArg[i] = 0; } for( n = 0 ; n < nEntry ; ++n ){ pEntry = (VmShutdownCB *)SySetAt(&pVm->aShutdown,n); if( pEntry ){ /* Prepare callback arguments if any */ for( i = 0 ; i < pEntry->nArg ; i++ ){ if( i >= (int)SX_ARRAYSIZE(apArg) ){ break; } apArg[i] = &pEntry->aArg[i]; } /* Invoke the callback */ PH7_VmCallUserFunction(&(*pVm),&pEntry->sCallback,pEntry->nArg,apArg,0); /* * TICKET 1433-56: Try re-access the same entry since the invoked * callback may call [register_shutdown_function()] in it's body. */ pEntry = (VmShutdownCB *)SySetAt(&pVm->aShutdown,n); if( pEntry ){ PH7_MemObjRelease(&pEntry->sCallback); for( i = 0 ; i < pEntry->nArg ; ++i ){ PH7_MemObjRelease(apArg[i]); } } } } SySetReset(&pVm->aShutdown); } /* * Execute as much of a PH7 bytecode program as we can then return. * This function is a wrapper around [VmByteCodeExec()]. * See block-comment on that function for additional information. */ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) { /* Make sure we are ready to execute this program */ if( pVm->nMagic != PH7_VM_RUN ){ return pVm->nMagic == PH7_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */ } /* Set the execution magic number */ pVm->nMagic = PH7_VM_EXEC; /* Execute the program */ VmByteCodeExec(&(*pVm),(VmInstr *)SySetBasePtr(pVm->pByteContainer),pVm->aOps,-1,&pVm->sExec,0,FALSE); /* Invoke any shutdown callbacks */ VmInvokeShutdownCallbacks(&(*pVm)); /* * TICKET 1433-100: Do not remove the PH7_VM_EXEC magic number * so that any following call to [ph7_vm_exec()] without calling * [ph7_vm_reset()] first would fail. */ return SXRET_OK; } /* * Invoke the installed VM output consumer callback to consume * the desired message. * Refer to the implementation of [ph7_context_output()] defined * in 'api.c' for additional information. */ PH7_PRIVATE sxi32 PH7_VmOutputConsume( ph7_vm *pVm, /* Target VM */ SyString *pString /* Message to output */ ) { ph7_output_consumer *pCons = &pVm->sVmConsumer; sxi32 rc = SXRET_OK; /* Call the output consumer */ if( pString->nByte > 0 ){ rc = pCons->xConsumer((const void *)pString->zString,pString->nByte,pCons->pUserData); if( pCons->xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += pString->nByte; } } return rc; } /* * Format a message and invoke the installed VM output consumer * callback to consume the formatted message. * Refer to the implementation of [ph7_context_output_format()] defined * in 'api.c' for additional information. */ PH7_PRIVATE sxi32 PH7_VmOutputConsumeAp( ph7_vm *pVm, /* Target VM */ const char *zFormat, /* Formatted message to output */ va_list ap /* Variable list of arguments */ ) { ph7_output_consumer *pCons = &pVm->sVmConsumer; sxi32 rc = SXRET_OK; SyBlob sWorker; /* Format the message and call the output consumer */ SyBlobInit(&sWorker,&pVm->sAllocator); SyBlobFormatAp(&sWorker,zFormat,ap); if( SyBlobLength(&sWorker) > 0 ){ /* Consume the formatted message */ rc = pCons->xConsumer(SyBlobData(&sWorker),SyBlobLength(&sWorker),pCons->pUserData); } if( pCons->xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += SyBlobLength(&sWorker); } /* Release the working buffer */ SyBlobRelease(&sWorker); return rc; } /* * Return a string representation of the given PH7 OP code. * This function never fail and always return a pointer * to a null terminated string. */ static const char * VmInstrToString(sxi32 nOp) { const char *zOp = "Unknown "; switch(nOp){ case PH7_OP_DONE: zOp = "DONE "; break; case PH7_OP_HALT: zOp = "HALT "; break; case PH7_OP_LOAD: zOp = "LOAD "; break; case PH7_OP_LOADC: zOp = "LOADC "; break; case PH7_OP_LOAD_MAP: zOp = "LOAD_MAP "; break; case PH7_OP_LOAD_LIST: zOp = "LOAD_LIST "; break; case PH7_OP_LOAD_IDX: zOp = "LOAD_IDX "; break; case PH7_OP_LOAD_CLOSURE: zOp = "LOAD_CLOSR "; break; case PH7_OP_NOOP: zOp = "NOOP "; break; case PH7_OP_JMP: zOp = "JMP "; break; case PH7_OP_JZ: zOp = "JZ "; break; case PH7_OP_JNZ: zOp = "JNZ "; break; case PH7_OP_POP: zOp = "POP "; break; case PH7_OP_CAT: zOp = "CAT "; break; case PH7_OP_CVT_INT: zOp = "CVT_INT "; break; case PH7_OP_CVT_STR: zOp = "CVT_STR "; break; case PH7_OP_CVT_REAL: zOp = "CVT_REAL "; break; case PH7_OP_CALL: zOp = "CALL "; break; case PH7_OP_UMINUS: zOp = "UMINUS "; break; case PH7_OP_UPLUS: zOp = "UPLUS "; break; case PH7_OP_BITNOT: zOp = "BITNOT "; break; case PH7_OP_LNOT: zOp = "LOGNOT "; break; case PH7_OP_MUL: zOp = "MUL "; break; case PH7_OP_DIV: zOp = "DIV "; break; case PH7_OP_MOD: zOp = "MOD "; break; case PH7_OP_ADD: zOp = "ADD "; break; case PH7_OP_SUB: zOp = "SUB "; break; case PH7_OP_SHL: zOp = "SHL "; break; case PH7_OP_SHR: zOp = "SHR "; break; case PH7_OP_LT: zOp = "LT "; break; case PH7_OP_LE: zOp = "LE "; break; case PH7_OP_GT: zOp = "GT "; break; case PH7_OP_GE: zOp = "GE "; break; case PH7_OP_EQ: zOp = "EQ "; break; case PH7_OP_NEQ: zOp = "NEQ "; break; case PH7_OP_TEQ: zOp = "TEQ "; break; case PH7_OP_TNE: zOp = "TNE "; break; case PH7_OP_BAND: zOp = "BITAND "; break; case PH7_OP_BXOR: zOp = "BITXOR "; break; case PH7_OP_BOR: zOp = "BITOR "; break; case PH7_OP_LAND: zOp = "LOGAND "; break; case PH7_OP_LOR: zOp = "LOGOR "; break; case PH7_OP_LXOR: zOp = "LOGXOR "; break; case PH7_OP_STORE: zOp = "STORE "; break; case PH7_OP_STORE_IDX: zOp = "STORE_IDX "; break; case PH7_OP_STORE_IDX_REF: zOp = "STORE_IDX_R"; break; case PH7_OP_PULL: zOp = "PULL "; break; case PH7_OP_SWAP: zOp = "SWAP "; break; case PH7_OP_YIELD: zOp = "YIELD "; break; case PH7_OP_CVT_BOOL: zOp = "CVT_BOOL "; break; case PH7_OP_CVT_NULL: zOp = "CVT_NULL "; break; case PH7_OP_CVT_ARRAY: zOp = "CVT_ARRAY "; break; case PH7_OP_CVT_OBJ: zOp = "CVT_OBJ "; break; case PH7_OP_CVT_NUMC: zOp = "CVT_NUMC "; break; case PH7_OP_INCR: zOp = "INCR "; break; case PH7_OP_DECR: zOp = "DECR "; break; case PH7_OP_SEQ: zOp = "SEQ "; break; case PH7_OP_SNE: zOp = "SNE "; break; case PH7_OP_NEW: zOp = "NEW "; break; case PH7_OP_CLONE: zOp = "CLONE "; break; case PH7_OP_ADD_STORE: zOp = "ADD_STORE "; break; case PH7_OP_SUB_STORE: zOp = "SUB_STORE "; break; case PH7_OP_MUL_STORE: zOp = "MUL_STORE "; break; case PH7_OP_DIV_STORE: zOp = "DIV_STORE "; break; case PH7_OP_MOD_STORE: zOp = "MOD_STORE "; break; case PH7_OP_CAT_STORE: zOp = "CAT_STORE "; break; case PH7_OP_SHL_STORE: zOp = "SHL_STORE "; break; case PH7_OP_SHR_STORE: zOp = "SHR_STORE "; break; case PH7_OP_BAND_STORE: zOp = "BAND_STORE "; break; case PH7_OP_BOR_STORE: zOp = "BOR_STORE "; break; case PH7_OP_BXOR_STORE: zOp = "BXOR_STORE "; break; case PH7_OP_CONSUME: zOp = "CONSUME "; break; case PH7_OP_LOAD_REF: zOp = "LOAD_REF "; break; case PH7_OP_STORE_REF: zOp = "STORE_REF "; break; case PH7_OP_MEMBER: zOp = "MEMBER "; break; case PH7_OP_UPLINK: zOp = "UPLINK "; break; case PH7_OP_ERR_CTRL: zOp = "ERR_CTRL "; break; case PH7_OP_IS_A: zOp = "IS_A "; break; case PH7_OP_SWITCH: zOp = "SWITCH "; break; case PH7_OP_LOAD_EXCEPTION: zOp = "LOAD_EXCEP "; break; case PH7_OP_POP_EXCEPTION: zOp = "POP_EXCEP "; break; case PH7_OP_THROW: zOp = "THROW "; break; case PH7_OP_FOREACH_INIT: zOp = "4EACH_INIT "; break; case PH7_OP_FOREACH_STEP: zOp = "4EACH_STEP "; break; default: break; } return zOp; } /* * Dump PH7 bytecodes instructions to a human readable format. * The xConsumer() callback which is an used defined function * is responsible of consuming the generated dump. */ PH7_PRIVATE sxi32 PH7_VmDump( ph7_vm *pVm, /* Target VM */ ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */ void *pUserData /* Last argument to xConsumer() */ ) { sxi32 rc; rc = VmByteCodeDump(pVm->pByteContainer,xConsumer,pUserData); return rc; } /* * Default constant expansion callback used by the 'const' statement if used * outside a class body [i.e: global or function scope]. * Refer to the implementation of [PH7_CompileConstant()] defined * in 'compile.c' for additional information. */ PH7_PRIVATE void PH7_VmExpandConstantValue(ph7_value *pVal,void *pUserData) { SySet *pByteCode = (SySet *)pUserData; /* Evaluate and expand constant value */ VmLocalExec((ph7_vm *)SySetGetUserData(pByteCode),pByteCode,(ph7_value *)pVal); } /* * Section: * Function handling functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * int func_num_args(void) * Returns the number of arguments passed to the function. * Parameters * None. * Return * Total number of arguments passed into the current user-defined function * or -1 if called from the globe scope. */ static int vm_builtin_func_num_args(ph7_context *pCtx,int nArg,ph7_value **apArg) { VmFrame *pFrame; ph7_vm *pVm; /* Point to the target VM */ pVm = pCtx->pVm; /* Current frame */ pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent == 0 ){ SXUNUSED(nArg); SXUNUSED(apArg); /* Global frame,return -1 */ ph7_result_int(pCtx,-1); return SXRET_OK; } /* Total number of arguments passed to the enclosing function */ nArg = (int)SySetUsed(&pFrame->sArg); ph7_result_int(pCtx,nArg); return SXRET_OK; } /* * value func_get_arg(int $arg_num) * Return an item from the argument list. * Parameters * Argument number(index start from zero). * Return * Returns the specified argument or FALSE on error. */ static int vm_builtin_func_get_arg(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pObj = 0; VmSlot *pSlot = 0; VmFrame *pFrame; ph7_vm *pVm; /* Point to the target VM */ pVm = pCtx->pVm; /* Current frame */ pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( nArg < 1 || pFrame->pParent == 0 ){ /* Global frame or Missing arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Called in the global scope"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Extract the desired index */ nArg = ph7_value_to_int(apArg[0]); if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){ /* Invalid index,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } /* Extract the desired argument */ if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg,(sxu32)nArg)) != 0 ){ if( (pObj = (ph7_value *)SySetAt(&pVm->aMemObj,pSlot->nIdx)) != 0 ){ /* Return the desired argument */ ph7_result_value(pCtx,(ph7_value *)pObj); }else{ /* No such argument,return false */ ph7_result_bool(pCtx,0); } }else{ /* CAN'T HAPPEN */ ph7_result_bool(pCtx,0); } return SXRET_OK; } /* * array func_get_args_byref(void) * Returns an array comprising a function's argument list. * Parameters * None. * Return * Returns an array in which each element is a POINTER to the corresponding * member of the current user-defined function's argument list. * Otherwise FALSE is returned on failure. * NOTE: * Arguments are returned to the array by reference. */ static int vm_builtin_func_get_args_byref(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray; VmFrame *pFrame; VmSlot *aSlot; sxu32 n; /* Point to the current frame */ pFrame = pCtx->pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent == 0 ){ /* Global frame,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Called in the global scope"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Start filling the array with the given arguments (Pass by reference) */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){ PH7_HashmapInsertByRef((ph7_hashmap *)pArray->x.pOther,0/*Automatic index assign*/,aSlot[n].nIdx); } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * array func_get_args(void) * Returns an array comprising a copy of function's argument list. * Parameters * None. * Return * Returns an array in which each element is a copy of the corresponding * member of the current user-defined function's argument list. * Otherwise FALSE is returned on failure. */ static int vm_builtin_func_get_args(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pObj = 0; ph7_value *pArray; VmFrame *pFrame; VmSlot *aSlot; sxu32 n; /* Point to the current frame */ pFrame = pCtx->pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent == 0 ){ /* Global frame,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Called in the global scope"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Start filling the array with the given arguments */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){ pObj = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj,aSlot[n].nIdx); if( pObj ){ ph7_array_add_elem(pArray,0/* Automatic index assign*/,pObj); } } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * bool function_exists(string $name) * Return TRUE if the given function has been defined. * Parameters * The name of the desired function. * Return * Return TRUE if the given function has been defined.False otherwise */ static int vm_builtin_func_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zName; ph7_vm *pVm; int nLen; int res; if( nArg < 1 ){ /* Missing argument,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } /* Point to the target VM */ pVm = pCtx->pVm; /* Extract the function name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Assume the function is not defined */ res = 0; /* Perform the lookup */ if( SyHashGet(&pVm->hFunction,(const void *)zName,(sxu32)nLen) != 0 || SyHashGet(&pVm->hHostFunction,(const void *)zName,(sxu32)nLen) != 0 ){ /* Function is defined */ res = 1; } ph7_result_bool(pCtx,res); return SXRET_OK; } /* Forward declaration */ static ph7_class * VmExtractClassFromValue(ph7_vm *pVm,ph7_value *pArg); /* * Verify that the contents of a variable can be called as a function. * [i.e: Whether it is callable or not]. * Return TRUE if callable.FALSE otherwise. */ PH7_PRIVATE int PH7_VmIsCallable(ph7_vm *pVm,ph7_value *pValue,int CallInvoke) { int res = 0; if( pValue->iFlags & MEMOBJ_OBJ ){ /* Call the magic method __invoke if available */ ph7_class_instance *pThis = (ph7_class_instance *)pValue->x.pOther; ph7_class_method *pMethod; pMethod = PH7_ClassExtractMethod(pThis->pClass,"__invoke",sizeof("__invoke")-1); if( pMethod && CallInvoke ){ ph7_value sResult; sxi32 rc; /* Invoke the magic method and extract the result */ PH7_MemObjInit(pVm,&sResult); rc = PH7_VmCallClassMethod(pVm,pThis,pMethod,&sResult,0,0); if( rc == SXRET_OK && (sResult.iFlags & (MEMOBJ_BOOL|MEMOBJ_INT)) ){ res = sResult.x.iVal != 0; } PH7_MemObjRelease(&sResult); } }else if( pValue->iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pValue->x.pOther; if( pMap->nEntry > 1 ){ ph7_class *pClass; ph7_value *pV; /* Extract the target class */ pV = (ph7_value *)SySetAt(&pVm->aMemObj,pMap->pFirst->nValIdx); if( pV ){ pClass = VmExtractClassFromValue(pVm,pV); if( pClass ){ ph7_class_method *pMethod; /* Extract the target method */ pV = (ph7_value *)SySetAt(&pVm->aMemObj,pMap->pFirst->pPrev->nValIdx); if( pV && (pV->iFlags & MEMOBJ_STRING) && SyBlobLength(&pV->sBlob) > 0 ){ /* Perform the lookup */ pMethod = PH7_ClassExtractMethod(pClass,(const char *)SyBlobData(&pV->sBlob),SyBlobLength(&pV->sBlob)); if( pMethod ){ /* Method is callable */ res = 1; } } } } } }else if( pValue->iFlags & MEMOBJ_STRING ){ const char *zName; int nLen; /* Extract the name */ zName = ph7_value_to_string(pValue,&nLen); /* Perform the lookup */ if( SyHashGet(&pVm->hFunction,(const void *)zName,(sxu32)nLen) != 0 || SyHashGet(&pVm->hHostFunction,(const void *)zName,(sxu32)nLen) != 0 ){ /* Function is callable */ res = 1; } } return res; } /* * bool is_callable(callable $name[,bool $syntax_only = false]) * Verify that the contents of a variable can be called as a function. * Parameters * $name * The callback function to check * $syntax_only * If set to TRUE the function only verifies that name might be a function or method. * It will only reject simple variables that are not strings, or an array that does * not have a valid structure to be used as a callback. The valid ones are supposed * to have only 2 entries, the first of which is an object or a string, and the second * a string. * Return * TRUE if name is callable, FALSE otherwise. */ static int vm_builtin_is_callable(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm; int res; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } /* Point to the target VM */ pVm = pCtx->pVm; /* Perform the requested operation */ res = PH7_VmIsCallable(pVm,apArg[0],TRUE); ph7_result_bool(pCtx,res); return SXRET_OK; } /* * Hash walker callback used by the [get_defined_functions()] function * defined below. */ static int VmHashFuncStep(SyHashEntry *pEntry,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; ph7_value sName; sxi32 rc; /* Prepare the function name for insertion */ PH7_MemObjInitFromString(pArray->pVm,&sName,0); PH7_MemObjStringAppend(&sName,(const char *)pEntry->pKey,pEntry->nKeyLen); /* Perform the insertion */ rc = ph7_array_add_elem(pArray,0/* Automatic index assign */,&sName); /* Will make it's own copy */ PH7_MemObjRelease(&sName); return rc; } /* * array get_defined_functions(void) * Returns an array of all defined functions. * Parameter * None. * Return * Returns an multidimensional array containing a list of all defined functions * both built-in (internal) and user-defined. * The internal functions will be accessible via $arr["internal"], and the user * defined ones using $arr["user"]. * Note: * NULL is returned on failure. */ static int vm_builtin_get_defined_func(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pEntry; /* NOTE: * Don't worry about freeing memory here,every allocated resource will be released * automatically by the engine as soon we return from this foreign function. */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } pEntry = ph7_context_new_array(pCtx); if( pEntry == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Fill with the appropriate information */ SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pEntry); /* Create the 'internal' index */ ph7_array_add_strkey_elem(pArray,"internal",pEntry); /* Will make it's own copy */ /* Create the user-func array */ pEntry = ph7_context_new_array(pCtx); if( pEntry == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Fill with the appropriate information */ SyHashForEach(&pCtx->pVm->hFunction,VmHashFuncStep,pEntry); /* Create the 'user' index */ ph7_array_add_strkey_elem(pArray,"user",pEntry); /* Will make it's own copy */ /* Return the multi-dimensional array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * void register_shutdown_function(callable $callback[,mixed $param,...) * Register a function for execution on shutdown. * Note * Multiple calls to register_shutdown_function() can be made, and each will * be called in the same order as they were registered. * Parameters * $callback * The shutdown callback to register. * $param * One or more Parameter to pass to the registered callback. * Return * Nothing. */ static int vm_builtin_register_shutdown_function(ph7_context *pCtx,int nArg,ph7_value **apArg) { VmShutdownCB sEntry; int i,j; if( nArg < 1 || (apArg[0]->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP)) == 0 ){ /* Missing/Invalid arguments,return immediately */ return PH7_OK; } /* Zero the Entry */ SyZero(&sEntry,sizeof(VmShutdownCB)); /* Initialize fields */ PH7_MemObjInit(pCtx->pVm,&sEntry.sCallback); /* Save the callback name for later invocation name */ PH7_MemObjStore(apArg[0],&sEntry.sCallback); for( i = 0 ; i < (int)SX_ARRAYSIZE(sEntry.aArg) ; ++i ){ PH7_MemObjInit(pCtx->pVm,&sEntry.aArg[i]); } /* Copy arguments */ for(j = 0, i = 1 ; i < nArg ; j++,i++ ){ if( j >= (int)SX_ARRAYSIZE(sEntry.aArg) ){ /* Limit reached */ break; } PH7_MemObjStore(apArg[i],&sEntry.aArg[j]); } sEntry.nArg = j; /* Install the callback */ SySetPut(&pCtx->pVm->aShutdown,(const void *)&sEntry); return PH7_OK; } /* * Section: * Class handling functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * Extract the top active class. NULL is returned * if the class stack is empty. */ PH7_PRIVATE ph7_class * PH7_VmPeekTopClass(ph7_vm *pVm) { SySet *pSet = &pVm->aSelf; ph7_class **apClass; if( SySetUsed(pSet) <= 0 ){ /* Empty stack,return NULL */ return 0; } /* Peek the last entry */ apClass = (ph7_class **)SySetBasePtr(pSet); return apClass[pSet->nUsed - 1]; } /* * string get_class ([ object $object = NULL ] ) * Returns the name of the class of an object * Parameters * object * The tested object. This parameter may be omitted when inside a class. * Return * The name of the class of which object is an instance. * Returns FALSE if object is not an object. * If object is omitted when inside a class, the name of that class is returned. */ static int vm_builtin_get_class(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_class *pClass; SyString *pName; if( nArg < 1 ){ /* Check if we are inside a class */ pClass = PH7_VmPeekTopClass(pCtx->pVm); if( pClass ){ /* Point to the class name */ pName = &pClass->sName; ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ /* Not inside class,return FALSE */ ph7_result_bool(pCtx,0); } }else{ /* Extract the target class */ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); if( pClass ){ pName = &pClass->sName; /* Return the class name */ ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ /* Not a class instance,return FALSE */ ph7_result_bool(pCtx,0); } } return PH7_OK; } /* * string get_parent_class([object $object = NULL ] ) * Returns the name of the parent class of an object * Parameters * object * The tested object. This parameter may be omitted when inside a class. * Return * The name of the parent class of which object is an instance. * Returns FALSE if object is not an object or if the object does * not have a parent. * If object is omitted when inside a class, the name of that class is returned. */ static int vm_builtin_get_parent_class(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_class *pClass; SyString *pName; if( nArg < 1 ){ /* Check if we are inside a class [i.e: a method call]*/ pClass = PH7_VmPeekTopClass(pCtx->pVm); if( pClass && pClass->pBase ){ /* Point to the class name */ pName = &pClass->pBase->sName; ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ /* Not inside class,return FALSE */ ph7_result_bool(pCtx,0); } }else{ /* Extract the target class */ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); if( pClass ){ if( pClass->pBase ){ pName = &pClass->pBase->sName; /* Return the parent class name */ ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ /* Object does not have a parent class */ ph7_result_bool(pCtx,0); } }else{ /* Not a class instance,return FALSE */ ph7_result_bool(pCtx,0); } } return PH7_OK; } /* * string get_called_class(void) * Gets the name of the class the static method is called in. * Parameters * None. * Return * Returns the class name. Returns FALSE if called from outside a class. */ static int vm_builtin_get_called_class(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_class *pClass; /* Check if we are inside a class [i.e: a method call] */ pClass = PH7_VmPeekTopClass(pCtx->pVm); if( pClass ){ SyString *pName; /* Point to the class name */ pName = &pClass->sName; ph7_result_string(pCtx,pName->zString,(int)pName->nByte); }else{ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Not inside class,return FALSE */ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * Extract a ph7_class from the given ph7_value. * The given value must be of type object [i.e: class instance] or * string which hold the class name. */ static ph7_class * VmExtractClassFromValue(ph7_vm *pVm,ph7_value *pArg) { ph7_class *pClass = 0; if( ph7_value_is_object(pArg) ){ /* Class instance already loaded,no need to perform a lookup */ pClass = ((ph7_class_instance *)pArg->x.pOther)->pClass; }else if( ph7_value_is_string(pArg) ){ const char *zClass; int nLen; /* Extract class name */ zClass = ph7_value_to_string(pArg,&nLen); if( nLen > 0 ){ SyHashEntry *pEntry; /* Perform a lookup */ pEntry = SyHashGet(&pVm->hClass,(const void *)zClass,(sxu32)nLen); if( pEntry ){ /* Point to the desired class */ pClass = (ph7_class *)pEntry->pUserData; } } } return pClass; } /* * bool property_exists(mixed $class,string $property) * Checks if the object or class has a property. * Parameters * class * The class name or an object of the class to test for * property * The name of the property * Return * Returns TRUE if the property exists,FALSE otherwise. */ static int vm_builtin_property_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume attribute does not exists */ if( nArg > 1 ){ ph7_class *pClass; pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); if( pClass ){ const char *zName; int nLen; /* Extract attribute name */ zName = ph7_value_to_string(apArg[1],&nLen); if( nLen > 0 ){ /* Perform the lookup in the attribute and method table */ if( SyHashGet(&pClass->hAttr,(const void *)zName,(sxu32)nLen) != 0 || SyHashGet(&pClass->hMethod,(const void *)zName,(sxu32)nLen) != 0 ){ /* property exists,flag that */ res = 1; } } } } ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool method_exists(mixed $class,string $method) * Checks if the given method is a class member. * Parameters * class * The class name or an object of the class to test for * property * The name of the method * Return * Returns TRUE if the method exists,FALSE otherwise. */ static int vm_builtin_method_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume method does not exists */ if( nArg > 1 ){ ph7_class *pClass; pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); if( pClass ){ const char *zName; int nLen; /* Extract method name */ zName = ph7_value_to_string(apArg[1],&nLen); if( nLen > 0 ){ /* Perform the lookup in the method table */ if( SyHashGet(&pClass->hMethod,(const void *)zName,(sxu32)nLen) != 0 ){ /* method exists,flag that */ res = 1; } } } } ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool class_exists(string $class_name [, bool $autoload = true ] ) * Checks if the class has been defined. * Parameters * class_name * The class name. The name is matched in a case-sensitive manner * unlinke the standard PHP engine. * autoload * Whether or not to call __autoload by default. * Return * TRUE if class_name is a defined class, FALSE otherwise. */ static int vm_builtin_class_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume class does not exists */ if( nArg > 0 ){ const char *zName; int nLen; /* Extract given name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Perform a hashlookup */ if( nLen > 0 && SyHashGet(&pCtx->pVm->hClass,(const void *)zName,(sxu32)nLen) != 0 ){ /* class is available */ res = 1; } } ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool interface_exists(string $class_name [, bool $autoload = true ] ) * Checks if the interface has been defined. * Parameters * class_name * The class name. The name is matched in a case-sensitive manner * unlinke the standard PHP engine. * autoload * Whether or not to call __autoload by default. * Return * TRUE if class_name is a defined class, FALSE otherwise. */ static int vm_builtin_interface_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume class does not exists */ if( nArg > 0 ){ SyHashEntry *pEntry = 0; const char *zName; int nLen; /* Extract given name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Perform a hashlookup */ if( nLen > 0 ){ pEntry = SyHashGet(&pCtx->pVm->hClass,(const void *)zName,(sxu32)nLen); } if( pEntry ){ ph7_class *pClass = (ph7_class *)pEntry->pUserData; while( pClass ){ if( pClass->iFlags & PH7_CLASS_INTERFACE ){ /* interface is available */ res = 1; break; } /* Next with the same name */ pClass = pClass->pNextName; } } } ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool class_alias([string $original[,string $alias ]]) * Creates an alias for a class. * Parameters * original * The original class. * alias * The alias name for the class. * Return * Returns TRUE on success or FALSE on failure. */ static int vm_builtin_class_alias(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zOld,*zNew; int nOldLen,nNewLen; SyHashEntry *pEntry; ph7_class *pClass; char *zDup; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract old class name */ zOld = ph7_value_to_string(apArg[0],&nOldLen); /* Extract alias name */ zNew = ph7_value_to_string(apArg[1],&nNewLen); if( nNewLen < 1 ){ /* Invalid alias name,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform a hash lookup */ pEntry = SyHashGet(&pCtx->pVm->hClass,(const void *)zOld,(sxu32)nOldLen); if( pEntry == 0 ){ /* No such class,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the class */ pClass = (ph7_class *)pEntry->pUserData; /* Duplicate alias name */ zDup = SyMemBackendStrDup(&pCtx->pVm->sAllocator,zNew,(sxu32)nNewLen); if( zDup == 0 ){ /* Out of memory,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Create the alias */ rc = SyHashInsert(&pCtx->pVm->hClass,(const void *)zDup,(sxu32)nNewLen,pClass); if( rc != SXRET_OK ){ SyMemBackendFree(&pCtx->pVm->sAllocator,zDup); } ph7_result_bool(pCtx,rc == SXRET_OK); return PH7_OK; } /* * array get_declared_classes(void) * Returns an array with the name of the defined classes * Parameters * None * Return * Returns an array of the names of the declared classes * in the current script. * Note: * NULL is returned on failure. */ static int vm_builtin_get_declared_classes(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pName,*pArray; SyHashEntry *pEntry; /* Create a new array first */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); if( pArray == 0 || pName == 0){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined classes */ SyHashResetLoopCursor(&pCtx->pVm->hClass); while((pEntry = SyHashGetNextEntry(&pCtx->pVm->hClass)) != 0 ){ ph7_class *pClass = (ph7_class *)pEntry->pUserData; /* Do not register classes defined as interfaces */ if( (pClass->iFlags & PH7_CLASS_INTERFACE) == 0 ){ ph7_value_string(pName,SyStringData(&pClass->sName),(int)SyStringLength(&pClass->sName)); /* insert class name */ ph7_array_add_elem(pArray,0/*Automatic index assign*/,pName); /* Will make it's own copy */ /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } } /* Return the created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array get_declared_interfaces(void) * Returns an array with the name of the defined interfaces * Parameters * None * Return * Returns an array of the names of the declared interfaces * in the current script. * Note: * NULL is returned on failure. */ static int vm_builtin_get_declared_interfaces(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pName,*pArray; SyHashEntry *pEntry; /* Create a new array first */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); if( pArray == 0 || pName == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined classes */ SyHashResetLoopCursor(&pCtx->pVm->hClass); while((pEntry = SyHashGetNextEntry(&pCtx->pVm->hClass)) != 0 ){ ph7_class *pClass = (ph7_class *)pEntry->pUserData; /* Register classes defined as interfaces only */ if( pClass->iFlags & PH7_CLASS_INTERFACE ){ ph7_value_string(pName,SyStringData(&pClass->sName),(int)SyStringLength(&pClass->sName)); /* insert interface name */ ph7_array_add_elem(pArray,0/*Automatic index assign*/,pName); /* Will make it's own copy */ /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } } /* Return the created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array get_class_methods(string/object $class_name) * Returns an array with the name of the class methods * Parameters * class_name * The class name or class instance * Return * Returns an array of method names defined for the class specified by class_name. * In case of an error, it returns NULL. * Note: * NULL is returned on failure. */ static int vm_builtin_get_class_methods(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pName,*pArray; SyHashEntry *pEntry; ph7_class *pClass; /* Extract the target class first */ pClass = 0; if( nArg > 0 ){ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); } if( pClass == 0 ){ /* No such class,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); if( pArray == 0 || pName == 0){ /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined methods */ SyHashResetLoopCursor(&pClass->hMethod); while((pEntry = SyHashGetNextEntry(&pClass->hMethod)) != 0 ){ ph7_class_method *pMethod = (ph7_class_method *)pEntry->pUserData; /* Insert method name */ ph7_value_string(pName,SyStringData(&pMethod->sFunc.sName),(int)SyStringLength(&pMethod->sFunc.sName)); ph7_array_add_elem(pArray,0/*Automatic index assign*/,pName); /* Will make it's own copy */ /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } /* Return the created array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory here,everything will be relased * automatically as soon we return from this foreign function. */ return PH7_OK; } /* * This function return TRUE(1) if the given class attribute stored * in the pAttrName parameter is visible and thus can be extracted * from the current scope.Otherwise FALSE is returned. */ static int VmClassMemberAccess( ph7_vm *pVm, /* Target VM */ ph7_class *pClass, /* Target Class */ const SyString *pAttrName, /* Attribute name */ sxi32 iProtection, /* Attribute protection level [i.e: public,protected or private] */ int bLog /* TRUE to log forbidden access. */ ) { if( iProtection != PH7_CLASS_PROT_PUBLIC ){ VmFrame *pFrame = pVm->pFrame; ph7_vm_func *pVmFunc; while( pFrame->pParent && (pFrame->iFlags & (VM_FRAME_EXCEPTION|VM_FRAME_CATCH) ) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } pVmFunc = (ph7_vm_func *)pFrame->pUserData; if( pVmFunc == 0 || (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD) == 0 ){ goto dis; /* Access is forbidden */ } if( iProtection == PH7_CLASS_PROT_PRIVATE ){ /* Must be the same instance */ if( (ph7_class *)pVmFunc->pUserData != pClass ){ goto dis; /* Access is forbidden */ } }else{ /* Protected */ ph7_class *pBase = (ph7_class *)pVmFunc->pUserData; /* Must be a derived class */ if( !VmInstanceOf(pClass,pBase) ){ goto dis; /* Access is forbidden */ } } } return 1; /* Access is granted */ dis: if( bLog ){ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Access to the class attribute '%z->%z' is forbidden", &pClass->sName,pAttrName); } return 0; /* Access is forbidden */ } /* * array get_class_vars(string/object $class_name) * Get the default properties of the class * Parameters * class_name * The class name or class instance * Return * Returns an associative array of declared properties visible from the current scope * with their default value. The resulting array elements are in the form * of varname => value. * Note: * NULL is returned on failure. */ static int vm_builtin_get_class_vars(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pName,*pArray,sValue; SyHashEntry *pEntry; ph7_class *pClass; /* Extract the target class first */ pClass = 0; if( nArg > 0 ){ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); } if( pClass == 0 ){ /* No such class,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); PH7_MemObjInit(pCtx->pVm,&sValue); if( pArray == 0 || pName == 0){ /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined attribute visible from the current scope */ SyHashResetLoopCursor(&pClass->hAttr); while((pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0 ){ ph7_class_attr *pAttr = (ph7_class_attr *)pEntry->pUserData; /* Check if the access is allowed */ if( VmClassMemberAccess(pCtx->pVm,pClass,&pAttr->sName,pAttr->iProtection,FALSE) ){ SyString *pAttrName = &pAttr->sName; ph7_value *pValue = 0; if( pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC) ){ /* Extract static attribute value which is always computed */ pValue = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj,pAttr->nIdx); }else{ if( SySetUsed(&pAttr->aByteCode) > 0 ){ PH7_MemObjRelease(&sValue); /* Compute default value (any complex expression) associated with this attribute */ VmLocalExec(pCtx->pVm,&pAttr->aByteCode,&sValue); pValue = &sValue; } } /* Fill in the array */ ph7_value_string(pName,pAttrName->zString,pAttrName->nByte); ph7_array_add_elem(pArray,pName,pValue); /* Will make it's own copy */ /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } } PH7_MemObjRelease(&sValue); /* Return the created array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory here,everything will be relased * automatically as soon we return from this foreign function. */ return PH7_OK; } /* * array get_object_vars(object $this) * Gets the properties of the given object * Parameters * this * A class instance * Return * Returns an associative array of defined object accessible non-static properties * for the specified object in scope. If a property have not been assigned a value * it will be returned with a NULL value. * Note: * NULL is returned on failure. */ static int vm_builtin_get_object_vars(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_class_instance *pThis = 0; ph7_value *pName,*pArray; SyHashEntry *pEntry; if( nArg > 0 && (apArg[0]->iFlags & MEMOBJ_OBJ) ){ /* Extract the target instance */ pThis = (ph7_class_instance *)apArg[0]->x.pOther; } if( pThis == 0 ){ /* No such instance,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); pName = ph7_context_new_scalar(pCtx); if( pArray == 0 || pName == 0){ /* Out of memory,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array with the defined attribute visible from the current scope */ SyHashResetLoopCursor(&pThis->hAttr); while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){ VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData; SyString *pAttrName; if( pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_STATIC|PH7_CLASS_ATTR_CONSTANT) ){ /* Only non-static/constant attributes are extracted */ continue; } pAttrName = &pVmAttr->pAttr->sName; /* Check if the access is allowed */ if( VmClassMemberAccess(pCtx->pVm,pThis->pClass,pAttrName,pVmAttr->pAttr->iProtection,FALSE) ){ ph7_value *pValue = 0; /* Extract attribute */ pValue = PH7_ClassInstanceExtractAttrValue(pThis,pVmAttr); if( pValue ){ /* Insert attribute name in the array */ ph7_value_string(pName,pAttrName->zString,pAttrName->nByte); ph7_array_add_elem(pArray,pName,pValue); /* Will make it's own copy */ } /* Reset the cursor */ ph7_value_reset_string_cursor(pName); } } /* Return the created array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory here,everything will be relased * automatically as soon we return from this foreign function. */ return PH7_OK; } /* * This function returns TRUE if the given class is an implemented * interface.Otherwise FALSE is returned. */ static int VmQueryInterfaceSet(ph7_class *pClass,SySet *pSet) { ph7_class **apInterface; sxu32 n; if( SySetUsed(pSet) < 1 ){ /* Empty interface container */ return FALSE; } /* Point to the set of implemented interfaces */ apInterface = (ph7_class **)SySetBasePtr(pSet); /* Perform the lookup */ for( n = 0 ; n < SySetUsed(pSet) ; n++ ){ if( apInterface[n] == pClass ){ return TRUE; } } return FALSE; } /* * This function returns TRUE if the given class (first argument) * is an instance of the main class (second argument). * Otherwise FALSE is returned. */ static int VmInstanceOf(ph7_class *pThis,ph7_class *pClass) { ph7_class *pParent; sxi32 rc; if( pThis == pClass ){ /* Instance of the same class */ return TRUE; } /* Check implemented interfaces */ rc = VmQueryInterfaceSet(pClass,&pThis->aInterface); if( rc ){ return TRUE; } /* Check parent classes */ pParent = pThis->pBase; while( pParent ){ if( pParent == pClass ){ /* Same instance */ return TRUE; } /* Check the implemented interfaces */ rc = VmQueryInterfaceSet(pClass,&pParent->aInterface); if( rc ){ return TRUE; } /* Point to the parent class */ pParent = pParent->pBase; } /* Not an instance of the the given class */ return FALSE; } /* * This function returns TRUE if the given class (first argument) * is a subclass of the main class (second argument). * Otherwise FALSE is returned. */ static int VmSubclassOf(ph7_class *pClass,ph7_class *pBase) { SySet *pInterface = &pClass->aInterface; SyHashEntry *pEntry; SyString *pName; sxi32 rc; while( pClass ){ pName = &pClass->sName; /* Query the derived hashtable */ pEntry = SyHashGet(&pBase->hDerived,(const void *)pName->zString,pName->nByte); if( pEntry ){ return TRUE; } pClass = pClass->pBase; } rc = VmQueryInterfaceSet(pBase,pInterface); if( rc ){ return TRUE; } /* Not a subclass */ return FALSE; } /* * bool is_a(object $object,string $class_name) * Checks if the object is of this class or has this class as one of its parents. * Parameters * object * The tested object * class_name * The class name * Return * Returns TRUE if the object is of this class or has this class as one of its * parents, FALSE otherwise. */ static int vm_builtin_is_a(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume FALSE by default */ if( nArg > 1 && ph7_value_is_object(apArg[0]) ){ ph7_class_instance *pThis = (ph7_class_instance *)apArg[0]->x.pOther; ph7_class *pClass; /* Extract the given class */ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[1]); if( pClass ){ /* Perform the query */ res = VmInstanceOf(pThis->pClass,pClass); } } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_subclass_of(object/string $object,object/string $class_name) * Checks if the object has this class as one of its parents. * Parameters * object * The tested object * class_name * The class name * Return * This function returns TRUE if the object , belongs to a class * which is a subclass of class_name, FALSE otherwise. */ static int vm_builtin_is_subclass_of(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume FALSE by default */ if( nArg > 1 ){ ph7_class *pClass,*pMain; /* Extract the given classes */ pClass = VmExtractClassFromValue(pCtx->pVm,apArg[0]); pMain = VmExtractClassFromValue(pCtx->pVm,apArg[1]); if( pClass && pMain ){ /* Perform the query */ res = VmSubclassOf(pClass,pMain); } } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * Call a class method where the name of the method is stored in the pMethod * parameter and the given arguments are stored in the apArg[] array. * Return SXRET_OK if the method was successfuly called.Any other * return value indicates failure. */ PH7_PRIVATE sxi32 PH7_VmCallClassMethod( ph7_vm *pVm, /* Target VM */ ph7_class_instance *pThis, /* Target class instance [i.e: Object in the PHP jargon]*/ ph7_class_method *pMethod, /* Method name */ ph7_value *pResult, /* Store method return value here. NULL otherwise */ int nArg, /* Total number of given arguments */ ph7_value **apArg /* Method arguments */ ) { ph7_value *aStack; VmInstr aInstr[2]; int iCursor; int i; /* Create a new operand stack */ aStack = VmNewOperandStack(&(*pVm),2/* Method name + Aux data */+nArg); if( aStack == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "PH7 is running out of memory while invoking class method"); return SXERR_MEM; } /* Fill the operand stack with the given arguments */ for( i = 0 ; i < nArg ; i++ ){ PH7_MemObjLoad(apArg[i],&aStack[i]); /* * Symisc eXtension: * Parameters to [call_user_func()] can be passed by reference. */ aStack[i].nIdx = apArg[i]->nIdx; } iCursor = nArg + 1; if( pThis ){ /* * Push the class instance so that the '$this' variable will be available. */ pThis->iRef++; /* Increment reference count */ aStack[i].x.pOther = pThis; aStack[i].iFlags = MEMOBJ_OBJ; } aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */ i++; /* Push method name */ SyBlobReset(&aStack[i].sBlob); SyBlobAppend(&aStack[i].sBlob,(const void *)SyStringData(&pMethod->sVmName),SyStringLength(&pMethod->sVmName)); aStack[i].iFlags = MEMOBJ_STRING; aStack[i].nIdx = SXU32_HIGH; /* Emit the CALL istruction */ aInstr[0].iOp = PH7_OP_CALL; aInstr[0].iP1 = nArg; /* Total number of given arguments */ aInstr[0].iP2 = 0; aInstr[0].p3 = 0; /* Emit the DONE instruction */ aInstr[1].iOp = PH7_OP_DONE; aInstr[1].iP1 = 1; /* Extract method return value */ aInstr[1].iP2 = 0; aInstr[1].p3 = 0; /* Execute the method body (if available) */ VmByteCodeExec(&(*pVm),aInstr,aStack,iCursor,pResult,0,TRUE); /* Clean up the mess left behind */ SyMemBackendFree(&pVm->sAllocator,aStack); return PH7_OK; } /* * Call a user defined or foreign function where the name of the function * is stored in the pFunc parameter and the given arguments are stored * in the apArg[] array. * Return SXRET_OK if the function was successfuly called.Any other * return value indicates failure. */ PH7_PRIVATE sxi32 PH7_VmCallUserFunction( ph7_vm *pVm, /* Target VM */ ph7_value *pFunc, /* Callback name */ int nArg, /* Total number of given arguments */ ph7_value **apArg, /* Callback arguments */ ph7_value *pResult /* Store callback return value here. NULL otherwise */ ) { ph7_value *aStack; VmInstr aInstr[2]; int i; if((pFunc->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP)) == 0 ){ /* Don't bother processing,it's invalid anyway */ if( pResult ){ /* Assume a null return value */ PH7_MemObjRelease(pResult); } return SXERR_INVALID; } if( pFunc->iFlags & MEMOBJ_HASHMAP ){ /* Class method */ ph7_hashmap *pMap = (ph7_hashmap *)pFunc->x.pOther; ph7_class_method *pMethod = 0; ph7_class_instance *pThis = 0; ph7_class *pClass = 0; ph7_value *pValue; sxi32 rc; if( pMap->nEntry < 2 /* Class name/instance + method name */){ /* Empty hashmap,nothing to call */ if( pResult ){ /* Assume a null return value */ PH7_MemObjRelease(pResult); } return SXRET_OK; } /* Extract the class name or an instance of it */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pMap->pFirst->nValIdx); if( pValue ){ pClass = VmExtractClassFromValue(&(*pVm),pValue); } if( pClass == 0 ){ /* No such class,return NULL */ if( pResult ){ PH7_MemObjRelease(pResult); } return SXRET_OK; } if( pValue->iFlags & MEMOBJ_OBJ ){ /* Point to the class instance */ pThis = (ph7_class_instance *)pValue->x.pOther; } /* Try to extract the method */ pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pMap->pFirst->pPrev->nValIdx); if( pValue ){ if( (pValue->iFlags & MEMOBJ_STRING) && SyBlobLength(&pValue->sBlob) > 0 ){ pMethod = PH7_ClassExtractMethod(pClass,(const char *)SyBlobData(&pValue->sBlob), SyBlobLength(&pValue->sBlob)); } } if( pMethod == 0 ){ /* No such method,return NULL */ if( pResult ){ PH7_MemObjRelease(pResult); } return SXRET_OK; } /* Call the class method */ rc = PH7_VmCallClassMethod(&(*pVm),pThis,pMethod,pResult,nArg,apArg); return rc; } /* Create a new operand stack */ aStack = VmNewOperandStack(&(*pVm),1+nArg); if( aStack == 0 ){ PH7_VmThrowError(&(*pVm),0,PH7_CTX_ERR, "PH7 is running out of memory while invoking user callback"); if( pResult ){ /* Assume a null return value */ PH7_MemObjRelease(pResult); } return SXERR_MEM; } /* Fill the operand stack with the given arguments */ for( i = 0 ; i < nArg ; i++ ){ PH7_MemObjLoad(apArg[i],&aStack[i]); /* * Symisc eXtension: * Parameters to [call_user_func()] can be passed by reference. */ aStack[i].nIdx = apArg[i]->nIdx; } /* Push the function name */ PH7_MemObjLoad(pFunc,&aStack[i]); aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */ /* Emit the CALL istruction */ aInstr[0].iOp = PH7_OP_CALL; aInstr[0].iP1 = nArg; /* Total number of given arguments */ aInstr[0].iP2 = 0; aInstr[0].p3 = 0; /* Emit the DONE instruction */ aInstr[1].iOp = PH7_OP_DONE; aInstr[1].iP1 = 1; /* Extract function return value if available */ aInstr[1].iP2 = 0; aInstr[1].p3 = 0; /* Execute the function body (if available) */ VmByteCodeExec(&(*pVm),aInstr,aStack,nArg,pResult,0,TRUE); /* Clean up the mess left behind */ SyMemBackendFree(&pVm->sAllocator,aStack); return PH7_OK; } /* * Call a user defined or foreign function whith a varibale number * of arguments where the name of the function is stored in the pFunc * parameter. * Return SXRET_OK if the function was successfuly called.Any other * return value indicates failure. */ PH7_PRIVATE sxi32 PH7_VmCallUserFunctionAp( ph7_vm *pVm, /* Target VM */ ph7_value *pFunc, /* Callback name */ ph7_value *pResult,/* Store callback return value here. NULL otherwise */ ... /* 0 (Zero) or more Callback arguments */ ) { ph7_value *pArg; SySet aArg; va_list ap; sxi32 rc; SySetInit(&aArg,&pVm->sAllocator,sizeof(ph7_value *)); /* Copy arguments one after one */ va_start(ap,pResult); for(;;){ pArg = va_arg(ap,ph7_value *); if( pArg == 0 ){ break; } SySetPut(&aArg,(const void *)&pArg); } /* Call the core routine */ rc = PH7_VmCallUserFunction(&(*pVm),pFunc,(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg),pResult); /* Cleanup */ SySetRelease(&aArg); return rc; } /* * value call_user_func(callable $callback[,value $parameter[, value $... ]]) * Call the callback given by the first parameter. * Parameter * $callback * The callable to be called. * ... * Zero or more parameters to be passed to the callback. * Return * Th return value of the callback, or FALSE on error. */ static int vm_builtin_call_user_func(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value sResult; /* Store callback return value here */ sxi32 rc; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } PH7_MemObjInit(pCtx->pVm,&sResult); sResult.nIdx = SXU32_HIGH; /* Mark as constant */ /* Try to invoke the callback */ rc = PH7_VmCallUserFunction(pCtx->pVm,apArg[0],nArg - 1,&apArg[1],&sResult); if( rc != SXRET_OK ){ /* An error occured while invoking the given callback [i.e: not defined] */ ph7_result_bool(pCtx,0); /* return false */ }else{ /* Callback result */ ph7_result_value(pCtx,&sResult); /* Will make it's own copy */ } PH7_MemObjRelease(&sResult); return PH7_OK; } /* * value call_user_func_array(callable $callback,array $param_arr) * Call a callback with an array of parameters. * Parameter * $callback * The callable to be called. * $param_arr * The parameters to be passed to the callback, as an indexed array. * Return * Returns the return value of the callback, or FALSE on error. */ static int vm_builtin_call_user_func_array(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; /* Current hashmap entry */ ph7_value *pValue,sResult;/* Store callback return value here */ ph7_hashmap *pMap; /* Target hashmap */ SySet aArg; /* Arguments containers */ sxi32 rc; sxu32 n; if( nArg < 2 || !ph7_value_is_array(apArg[1]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } PH7_MemObjInit(pCtx->pVm,&sResult); sResult.nIdx = SXU32_HIGH; /* Mark as constant */ /* Initialize the arguments container */ SySetInit(&aArg,&pCtx->pVm->sAllocator,sizeof(ph7_value *)); /* Turn hashmap entries into callback arguments */ pMap = (ph7_hashmap *)apArg[1]->x.pOther; pEntry = pMap->pFirst; /* First inserted entry */ for( n = 0 ; n < pMap->nEntry ; n++ ){ /* Extract node value */ if( (pValue = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj,pEntry->nValIdx)) != 0 ){ SySetPut(&aArg,(const void *)&pValue); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Try to invoke the callback */ rc = PH7_VmCallUserFunction(pCtx->pVm,apArg[0],(int)SySetUsed(&aArg),(ph7_value **)SySetBasePtr(&aArg),&sResult); if( rc != SXRET_OK ){ /* An error occured while invoking the given callback [i.e: not defined] */ ph7_result_bool(pCtx,0); /* return false */ }else{ /* Callback result */ ph7_result_value(pCtx,&sResult); /* Will make it's own copy */ } /* Cleanup the mess left behind */ PH7_MemObjRelease(&sResult); SySetRelease(&aArg); return PH7_OK; } /* * bool defined(string $name) * Checks whether a given named constant exists. * Parameter: * Name of the desired constant. * Return * TRUE if the given constant exists.FALSE otherwise. */ static int vm_builtin_defined(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zName; int nLen = 0; int res = 0; if( nArg < 1 ){ /* Missing constant name,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Missing constant name"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Extract constant name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Perform the lookup */ if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant,(const void *)zName,(sxu32)nLen) != 0 ){ /* Already defined */ res = 1; } ph7_result_bool(pCtx,res); return SXRET_OK; } /* * Constant expansion callback used by the [define()] function defined * below. */ static void VmExpandUserConstant(ph7_value *pVal,void *pUserData) { ph7_value *pConstantValue = (ph7_value *)pUserData; /* Expand constant value */ PH7_MemObjStore(pConstantValue,pVal); } /* * bool define(string $constant_name,expression value) * Defines a named constant at runtime. * Parameter: * $constant_name * The name of the constant * $value * Constant value * Return: * TRUE on success,FALSE on failure. */ static int vm_builtin_define(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zName; /* Constant name */ ph7_value *pValue; /* Duplicated constant value */ int nLen = 0; /* Name length */ sxi32 rc; if( nArg < 2 ){ /* Missing arguments,throw a ntoice and return false */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Missing constant name/value pair"); ph7_result_bool(pCtx,0); return SXRET_OK; } if( !ph7_value_is_string(apArg[0]) ){ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Invalid constant name"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Extract constant name */ zName = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Empty constant name"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Duplicate constant value */ pValue = (ph7_value *)SyMemBackendPoolAlloc(&pCtx->pVm->sAllocator,sizeof(ph7_value)); if( pValue == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Cannot register constant due to a memory failure"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Initialize the memory object */ PH7_MemObjInit(pCtx->pVm,pValue); /* Register the constant */ rc = ph7_create_constant(pCtx->pVm,zName,VmExpandUserConstant,pValue); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pCtx->pVm->sAllocator,pValue); ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Cannot register constant due to a memory failure"); ph7_result_bool(pCtx,0); return SXRET_OK; } /* Duplicate constant value */ PH7_MemObjStore(apArg[1],pValue); if( nArg == 3 && ph7_value_is_bool(apArg[2]) && ph7_value_to_bool(apArg[2]) ){ /* Lower case the constant name */ char *zCur = (char *)zName; while( zCur < &zName[nLen] ){ if( (unsigned char)zCur[0] >= 0xc0 ){ /* UTF-8 stream */ zCur++; while( zCur < &zName[nLen] && (((unsigned char)zCur[0] & 0xc0) == 0x80) ){ zCur++; } continue; } if( SyisUpper(zCur[0]) ){ int c = SyToLower(zCur[0]); zCur[0] = (char)c; } zCur++; } /* Finally,register the constant */ ph7_create_constant(pCtx->pVm,zName,VmExpandUserConstant,pValue); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return SXRET_OK; } /* * value constant(string $name) * Returns the value of a constant * Parameter * $name * Name of the constant. * Return * Constant value or NULL if not defined. */ static int vm_builtin_constant(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyHashEntry *pEntry; ph7_constant *pCons; const char *zName; /* Constant name */ ph7_value sVal; /* Constant value */ int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Invallid argument,return NULL */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Missing/Invalid constant name"); ph7_result_null(pCtx); return SXRET_OK; } /* Extract the constant name */ zName = ph7_value_to_string(apArg[0],&nLen); /* Perform the query */ pEntry = SyHashGet(&pCtx->pVm->hConstant,(const void *)zName,(sxu32)nLen); if( pEntry == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_NOTICE,"'%.*s': Undefined constant",nLen,zName); ph7_result_null(pCtx); return SXRET_OK; } PH7_MemObjInit(pCtx->pVm,&sVal); /* Point to the structure that describe the constant */ pCons = (ph7_constant *)SyHashEntryGetUserData(pEntry); /* Extract constant value by calling it's associated callback */ pCons->xExpand(&sVal,pCons->pUserData); /* Return that value */ ph7_result_value(pCtx,&sVal); /* Cleanup */ PH7_MemObjRelease(&sVal); return SXRET_OK; } /* * Hash walker callback used by the [get_defined_constants()] function * defined below. */ static int VmHashConstStep(SyHashEntry *pEntry,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; ph7_value sName; sxi32 rc; /* Prepare the constant name for insertion */ PH7_MemObjInitFromString(pArray->pVm,&sName,0); PH7_MemObjStringAppend(&sName,(const char *)pEntry->pKey,pEntry->nKeyLen); /* Perform the insertion */ rc = ph7_array_add_elem(pArray,0,&sName); /* Will make it's own copy */ PH7_MemObjRelease(&sName); return rc; } /* * array get_defined_constants(void) * Returns an associative array with the names of all defined * constants. * Parameters * NONE. * Returns * Returns the names of all the constants currently defined. */ static int vm_builtin_get_defined_constants(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray; /* Create the array first*/ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Fill the array with the defined constants */ SyHashForEach(&pCtx->pVm->hConstant,VmHashConstStep,pArray); /* Return the created array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * Section: * Output Control (OB) functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* Forward declaration */ static void VmObRestore(ph7_vm *pVm,VmObEntry *pEntry); /* * void ob_clean(void) * This function discards the contents of the output buffer. * This function does not destroy the output buffer like ob_end_clean() does. * Parameter * None * Return * No value is returned. */ static int vm_builtin_ob_clean(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Peek the top most OB */ pOb = (VmObEntry *)SySetPeek(&pVm->aOB); if( pOb ){ SyBlobRelease(&pOb->sOB); } return PH7_OK; } /* * bool ob_end_clean(void) * Clean (erase) the output buffer and turn off output buffering * This function discards the contents of the topmost output buffer and turns * off this output buffering. If you want to further process the buffer's contents * you have to call ob_get_contents() before ob_end_clean() as the buffer contents * are discarded when ob_end_clean() is called. * Parameter * None * Return * Returns TRUE on success or FALSE on failure. Reasons for failure are first that you called * the function without an active buffer or that for some reason a buffer could not be deleted * (possible for special buffer) */ static int vm_builtin_ob_end_clean(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; /* Pop the top most OB */ pOb = (VmObEntry *)SySetPop(&pVm->aOB); if( pOb == 0){ /* No such OB,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); }else{ /* Release */ VmObRestore(pVm,pOb); /* Return true */ ph7_result_bool(pCtx,1); } return PH7_OK; } /* * string ob_get_contents(void) * Gets the contents of the output buffer without clearing it. * Parameter * None * Return * This will return the contents of the output buffer or FALSE, if output buffering isn't active. */ static int vm_builtin_ob_get_contents(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; /* Peek the top most OB */ pOb = (VmObEntry *)SySetPeek(&pVm->aOB); if( pOb == 0 ){ /* No active OB,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); }else{ /* Return contents */ ph7_result_string(pCtx,(const char *)SyBlobData(&pOb->sOB),(int)SyBlobLength(&pOb->sOB)); } return PH7_OK; } /* * string ob_get_clean(void) * string ob_get_flush(void) * Get current buffer contents and delete current output buffer. * Parameter * None * Return * This will return the contents of the output buffer or FALSE, if output buffering isn't active. */ static int vm_builtin_ob_get_clean(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; /* Pop the top most OB */ pOb = (VmObEntry *)SySetPop(&pVm->aOB); if( pOb == 0 ){ /* No active OB,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); }else{ /* Return contents */ ph7_result_string(pCtx,(const char *)SyBlobData(&pOb->sOB),(int)SyBlobLength(&pOb->sOB)); /* Will make it's own copy */ /* Release */ VmObRestore(pVm,pOb); } return PH7_OK; } /* * int ob_get_length(void) * Return the length of the output buffer. * Parameter * None * Return * Returns the length of the output buffer contents or FALSE if no buffering is active. */ static int vm_builtin_ob_get_length(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; /* Peek the top most OB */ pOb = (VmObEntry *)SySetPeek(&pVm->aOB); if( pOb == 0 ){ /* No active OB,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); }else{ /* Return OB length */ ph7_result_int64(pCtx,(ph7_int64)SyBlobLength(&pOb->sOB)); } return PH7_OK; } /* * int ob_get_level(void) * Returns the nesting level of the output buffering mechanism. * Parameter * None * Return * Returns the level of nested output buffering handlers or zero if output buffering is not active. */ static int vm_builtin_ob_get_level(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; int iNest; SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Nesting level */ iNest = (int)SySetUsed(&pVm->aOB); /* Return the nesting value */ ph7_result_int(pCtx,iNest); return PH7_OK; } /* * Output Buffer(OB) default VM consumer routine.All VM output is now redirected * to a stackable internal buffer,until the user call [ob_get_clean(),ob_end_clean(),...]. * Refer to the implementation of [ob_start()] for more information. */ static int VmObConsumer(const void *pData,unsigned int nDataLen,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; VmObEntry *pEntry; ph7_value sResult; /* Peek the top most entry */ pEntry = (VmObEntry *)SySetPeek(&pVm->aOB); if( pEntry == 0 ){ /* CAN'T HAPPEN */ return PH7_OK; } PH7_MemObjInit(pVm,&sResult); if( ph7_value_is_callable(&pEntry->sCallback) && pVm->nObDepth < 15 ){ ph7_value sArg,*apArg[2]; /* Fill the first argument */ PH7_MemObjInitFromString(pVm,&sArg,0); PH7_MemObjStringAppend(&sArg,(const char *)pData,nDataLen); apArg[0] = &sArg; /* Call the 'filter' callback */ pVm->nObDepth++; PH7_VmCallUserFunction(pVm,&pEntry->sCallback,1,apArg,&sResult); pVm->nObDepth--; if( sResult.iFlags & MEMOBJ_STRING ){ /* Extract the function result */ pData = SyBlobData(&sResult.sBlob); nDataLen = SyBlobLength(&sResult.sBlob); } PH7_MemObjRelease(&sArg); } if( nDataLen > 0 ){ /* Redirect the VM output to the internal buffer */ SyBlobAppend(&pEntry->sOB,pData,nDataLen); } /* Release */ PH7_MemObjRelease(&sResult); return PH7_OK; } /* * Restore the default consumer. * Refer to the implementation of [ob_end_clean()] for more * information. */ static void VmObRestore(ph7_vm *pVm,VmObEntry *pEntry) { ph7_output_consumer *pCons = &pVm->sVmConsumer; if( SySetUsed(&pVm->aOB) < 1 ){ /* No more stackable OB */ pCons->xConsumer = pCons->xDef; pCons->pUserData = pCons->pDefData; } /* Release OB data */ PH7_MemObjRelease(&pEntry->sCallback); SyBlobRelease(&pEntry->sOB); } /* * bool ob_start([ callback $output_callback] ) * This function will turn output buffering on. While output buffering is active no output * is sent from the script (other than headers), instead the output is stored in an internal * buffer. * Parameter * $output_callback * An optional output_callback function may be specified. This function takes a string * as a parameter and should return a string. The function will be called when the output * buffer is flushed (sent) or cleaned (with ob_flush(), ob_clean() or similar function) * or when the output buffer is flushed to the browser at the end of the request. * When output_callback is called, it will receive the contents of the output buffer * as its parameter and is expected to return a new output buffer as a result, which will * be sent to the browser. If the output_callback is not a callable function, this function * will return FALSE. * If the callback function has two parameters, the second parameter is filled with * a bit-field consisting of PHP_OUTPUT_HANDLER_START, PHP_OUTPUT_HANDLER_CONT * and PHP_OUTPUT_HANDLER_END. * If output_callback returns FALSE original input is sent to the browser. * The output_callback parameter may be bypassed by passing a NULL value. * Return * Returns TRUE on success or FALSE on failure. */ static int vm_builtin_ob_start(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry sOb; sxi32 rc; /* Initialize the OB entry */ PH7_MemObjInit(pCtx->pVm,&sOb.sCallback); SyBlobInit(&sOb.sOB,&pVm->sAllocator); if( nArg > 0 && (apArg[0]->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP)) ){ /* Save the callback name for later invocation */ PH7_MemObjStore(apArg[0],&sOb.sCallback); } /* Push in the stack */ rc = SySetPut(&pVm->aOB,(const void *)&sOb); if( rc != SXRET_OK ){ PH7_MemObjRelease(&sOb.sCallback); }else{ ph7_output_consumer *pCons = &pVm->sVmConsumer; /* Substitute the default VM consumer */ if( pCons->xConsumer != VmObConsumer ){ pCons->xDef = pCons->xConsumer; pCons->pDefData = pCons->pUserData; /* Install the new consumer */ pCons->xConsumer = VmObConsumer; pCons->pUserData = pVm; } } ph7_result_bool(pCtx,rc == SXRET_OK); return PH7_OK; } /* * Flush Output buffer to the default VM output consumer. * Refer to the implementation of [ob_flush()] for more * information. */ static sxi32 VmObFlush(ph7_vm *pVm,VmObEntry *pEntry,int bRelease) { SyBlob *pBlob = &pEntry->sOB; sxi32 rc; /* Flush contents */ rc = PH7_OK; if( SyBlobLength(pBlob) > 0 ){ /* Call the VM output consumer */ rc = pVm->sVmConsumer.xDef(SyBlobData(pBlob),SyBlobLength(pBlob),pVm->sVmConsumer.pDefData); /* Increment VM output counter */ pVm->nOutputLen += SyBlobLength(pBlob); if( rc != PH7_ABORT ){ rc = PH7_OK; } } if( bRelease ){ VmObRestore(&(*pVm),pEntry); }else{ /* Reset the blob */ SyBlobReset(pBlob); } return rc; } /* * void ob_flush(void) * void flush(void) * Flush (send) the output buffer. * Parameter * None * Return * No return value. */ static int vm_builtin_ob_flush(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; sxi32 rc; /* Peek the top most OB entry */ pOb = (VmObEntry *)SySetPeek(&pVm->aOB); if( pOb == 0 ){ /* Empty stack,return immediately */ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Flush contents */ rc = VmObFlush(pVm,pOb,FALSE); return rc; } /* * bool ob_end_flush(void) * Flush (send) the output buffer and turn off output buffering. * Parameter * None * Return * Returns TRUE on success or FALSE on failure. Reasons for failure are first * that you called the function without an active buffer or that for some reason * a buffer could not be deleted (possible for special buffer). */ static int vm_builtin_ob_end_flush(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; VmObEntry *pOb; sxi32 rc; /* Pop the top most OB entry */ pOb = (VmObEntry *)SySetPop(&pVm->aOB); if( pOb == 0 ){ /* Empty stack,return FALSE */ ph7_result_bool(pCtx,0); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Flush contents */ rc = VmObFlush(pVm,pOb,TRUE); /* Return true */ ph7_result_bool(pCtx,1); return rc; } /* * void ob_implicit_flush([int $flag = true ]) * ob_implicit_flush() will turn implicit flushing on or off. * Implicit flushing will result in a flush operation after every * output call, so that explicit calls to flush() will no longer be needed. * Parameter * $flag * TRUE to turn implicit flushing on, FALSE otherwise. * Return * Nothing */ static int vm_builtin_ob_implicit_flush(ph7_context *pCtx,int nArg,ph7_value **apArg) { /* NOTE: As of this version,this function is a no-op. * PH7 is smart enough to flush it's internal buffer when appropriate. */ SXUNUSED(pCtx); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* * array ob_list_handlers(void) * Lists all output handlers in use. * Parameter * None * Return * This will return an array with the output handlers in use (if any). */ static int vm_builtin_ob_list_handlers(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pArray; VmObEntry *aEntry; ph7_value sVal; sxu32 n; if( SySetUsed(&pVm->aOB) < 1 ){ /* Empty stack,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ /* Out of memory,return NULL */ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_null(pCtx); return PH7_OK; } PH7_MemObjInit(pVm,&sVal); /* Point to the installed OB entries */ aEntry = (VmObEntry *)SySetBasePtr(&pVm->aOB); /* Perform the requested operation */ for( n = 0 ; n < SySetUsed(&pVm->aOB) ; n++ ){ VmObEntry *pEntry = &aEntry[n]; /* Extract handler name */ SyBlobReset(&sVal.sBlob); if( pEntry->sCallback.iFlags & MEMOBJ_STRING ){ /* Callback,dup it's name */ SyBlobDup(&pEntry->sCallback.sBlob,&sVal.sBlob); }else if( pEntry->sCallback.iFlags & MEMOBJ_HASHMAP ){ SyBlobAppend(&sVal.sBlob,"Class Method",sizeof("Class Method")-1); }else{ SyBlobAppend(&sVal.sBlob,"default output handler",sizeof("default output handler")-1); } sVal.iFlags = MEMOBJ_STRING; /* Perform the insertion */ ph7_array_add_elem(pArray,0/* Automatic index assign */,&sVal /* Will make it's own copy */); } PH7_MemObjRelease(&sVal); /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * Section: * Random numbers/string generators. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * Generate a random 32-bit unsigned integer. * PH7 use it's own private PRNG which is based on the one * used by te SQLite3 library. */ PH7_PRIVATE sxu32 PH7_VmRandomNum(ph7_vm *pVm) { sxu32 iNum; SyRandomness(&pVm->sPrng,(void *)&iNum,sizeof(sxu32)); return iNum; } /* * Generate a random string (English Alphabet) of length nLen. * Note that the generated string is NOT null terminated. * PH7 use it's own private PRNG which is based on the one used * by te SQLite3 library. */ PH7_PRIVATE void PH7_VmRandomString(ph7_vm *pVm,char *zBuf,int nLen) { static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */ int i; /* Generate a binary string first */ SyRandomness(&pVm->sPrng,zBuf,(sxu32)nLen); /* Turn the binary string into english based alphabet */ for( i = 0 ; i < nLen ; ++i ){ zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)]; } } /* * int rand() * int mt_rand() * int rand(int $min,int $max) * int mt_rand(int $min,int $max) * Generate a random (unsigned 32-bit) integer. * Parameter * $min * The lowest value to return (default: 0) * $max * The highest value to return (default: getrandmax()) * Return * A pseudo random value between min (or 0) and max (or getrandmax(), inclusive). * Note: * PH7 use it's own private PRNG which is based on the one used * by te SQLite3 library. */ static int vm_builtin_rand(ph7_context *pCtx,int nArg,ph7_value **apArg) { sxu32 iNum; /* Generate the random number */ iNum = PH7_VmRandomNum(pCtx->pVm); if( nArg > 1 ){ sxu32 iMin,iMax; iMin = (sxu32)ph7_value_to_int(apArg[0]); iMax = (sxu32)ph7_value_to_int(apArg[1]); if( iMin < iMax ){ sxu32 iDiv = iMax+1-iMin; if( iDiv > 0 ){ iNum = (iNum % iDiv)+iMin; } }else if(iMax > 0 ){ iNum %= iMax; } } /* Return the number */ ph7_result_int64(pCtx,(ph7_int64)iNum); return SXRET_OK; } /* * int getrandmax(void) * int mt_getrandmax(void) * int rc4_getrandmax(void) * Show largest possible random value * Return * The largest possible random value returned by rand() which is in * this implementation 0xFFFFFFFF. * Note: * PH7 use it's own private PRNG which is based on the one used * by te SQLite3 library. */ static int vm_builtin_getrandmax(ph7_context *pCtx,int nArg,ph7_value **apArg) { SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_int64(pCtx,SXU32_HIGH); return SXRET_OK; } /* * string rand_str() * string rand_str(int $len) * Generate a random string (English alphabet). * Parameter * $len * Length of the desired string (default: 16,Min: 1,Max: 1024) * Return * A pseudo random string. * Note: * PH7 use it's own private PRNG which is based on the one used * by te SQLite3 library. * This function is a symisc extension. */ static int vm_builtin_rand_str(ph7_context *pCtx,int nArg,ph7_value **apArg) { char zString[1024]; int iLen = 0x10; if( nArg > 0 ){ /* Get the desired length */ iLen = ph7_value_to_int(apArg[0]); if( iLen < 1 || iLen > 1024 ){ /* Default length */ iLen = 0x10; } } /* Generate the random string */ PH7_VmRandomString(pCtx->pVm,zString,iLen); /* Return the generated string */ ph7_result_string(pCtx,zString,iLen); /* Will make it's own copy */ return SXRET_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC #if !defined(PH7_DISABLE_HASH_FUNC) /* Unique ID private data */ struct unique_id_data { ph7_context *pCtx; /* Call context */ int entropy; /* TRUE if the more_entropy flag is set */ }; /* * Binary to hex consumer callback. * This callback is the default consumer used by [uniqid()] function * defined below. */ static int HexConsumer(const void *pData,unsigned int nLen,void *pUserData) { struct unique_id_data *pUniq = (struct unique_id_data *)pUserData; sxu32 nBuflen; /* Extract result buffer length */ nBuflen = ph7_context_result_buf_length(pUniq->pCtx); if( nBuflen > 12 && !pUniq->entropy ){ /* * If the more_entropy flag is not set,then the returned * string will be 13 characters long */ return SXERR_ABORT; } if( nBuflen > 22 ){ return SXERR_ABORT; } /* Safely Consume the hex stream */ ph7_result_string(pUniq->pCtx,(const char *)pData,(int)nLen); return SXRET_OK; } /* * string uniqid([string $prefix = "" [, bool $more_entropy = false]]) * Generate a unique ID * Parameter * $prefix * Append this prefix to the generated unique ID. * With an empty prefix, the returned string will be 13 characters long. * If more_entropy is TRUE, it will be 23 characters. * $more_entropy * If set to TRUE, uniqid() will add additional entropy which increases the likelihood * that the result will be unique. * Return * Returns the unique identifier, as a string. */ static int vm_builtin_uniqid(ph7_context *pCtx,int nArg,ph7_value **apArg) { struct unique_id_data sUniq; unsigned char zDigest[20]; ph7_vm *pVm = pCtx->pVm; const char *zPrefix; SHA1Context sCtx; char zRandom[7]; int nPrefix; int entropy; /* Generate a random string first */ PH7_VmRandomString(pVm,zRandom,(int)sizeof(zRandom)); /* Initialize fields */ zPrefix = 0; nPrefix = 0; entropy = 0; if( nArg > 0 ){ /* Append this prefix to the generated unqiue ID */ zPrefix = ph7_value_to_string(apArg[0],&nPrefix); if( nArg > 1 ){ entropy = ph7_value_to_bool(apArg[1]); } } SHA1Init(&sCtx); /* Generate the random ID */ if( nPrefix > 0 ){ SHA1Update(&sCtx,(const unsigned char *)zPrefix,(unsigned int)nPrefix); } /* Append the random ID */ SHA1Update(&sCtx,(const unsigned char *)&pVm->unique_id,sizeof(int)); /* Append the random string */ SHA1Update(&sCtx,(const unsigned char *)zRandom,sizeof(zRandom)); /* Increment the number */ pVm->unique_id++; SHA1Final(&sCtx,zDigest); /* Hexify the digest */ sUniq.pCtx = pCtx; sUniq.entropy = entropy; SyBinToHexConsumer((const void *)zDigest,sizeof(zDigest),HexConsumer,&sUniq); /* All done */ return PH7_OK; } #endif /* PH7_DISABLE_HASH_FUNC */ #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * Section: * Language construct implementation as foreign functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * void echo($string...) * Output one or more messages. * Parameters * $string * Message to output. * Return * NULL. */ static int vm_builtin_echo(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zData; int nDataLen = 0; ph7_vm *pVm; int i,rc; /* Point to the target VM */ pVm = pCtx->pVm; /* Output */ for( i = 0 ; i < nArg ; ++i ){ zData = ph7_value_to_string(apArg[i],&nDataLen); if( nDataLen > 0 ){ rc = pVm->sVmConsumer.xConsumer((const void *)zData,(unsigned int)nDataLen,pVm->sVmConsumer.pUserData); if( pVm->sVmConsumer.xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += nDataLen; } if( rc == SXERR_ABORT ){ /* Output consumer callback request an operation abort */ return PH7_ABORT; } } } return SXRET_OK; } /* * int print($string...) * Output one or more messages. * Parameters * $string * Message to output. * Return * 1 always. */ static int vm_builtin_print(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zData; int nDataLen = 0; ph7_vm *pVm; int i,rc; /* Point to the target VM */ pVm = pCtx->pVm; /* Output */ for( i = 0 ; i < nArg ; ++i ){ zData = ph7_value_to_string(apArg[i],&nDataLen); if( nDataLen > 0 ){ rc = pVm->sVmConsumer.xConsumer((const void *)zData,(unsigned int)nDataLen,pVm->sVmConsumer.pUserData); if( pVm->sVmConsumer.xConsumer != VmObConsumer ){ /* Increment output length */ pVm->nOutputLen += nDataLen; } if( rc == SXERR_ABORT ){ /* Output consumer callback request an operation abort */ return PH7_ABORT; } } } /* Return 1 */ ph7_result_int(pCtx,1); return SXRET_OK; } /* * void exit(string $msg) * void exit(int $status) * void die(string $ms) * void die(int $status) * Output a message and terminate program execution. * Parameter * If status is a string, this function prints the status just before exiting. * If status is an integer, that value will be used as the exit status * and not printed * Return * NULL */ static int vm_builtin_exit(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg > 0 ){ if( ph7_value_is_string(apArg[0]) ){ const char *zData; int iLen = 0; /* Print exit message */ zData = ph7_value_to_string(apArg[0],&iLen); ph7_context_output(pCtx,zData,iLen); }else if(ph7_value_is_int(apArg[0]) ){ sxi32 iExitStatus; /* Record exit status code */ iExitStatus = ph7_value_to_int(apArg[0]); pCtx->pVm->iExitStatus = iExitStatus; } } /* Abort processing immediately */ return PH7_ABORT; } /* * bool isset($var,...) * Finds out whether a variable is set. * Parameters * One or more variable to check. * Return * 1 if var exists and has value other than NULL, 0 otherwise. */ static int vm_builtin_isset(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pObj; int res = 0; int i; if( nArg < 1 ){ /* Missing arguments,return false */ ph7_result_bool(pCtx,res); return SXRET_OK; } /* Iterate over available arguments */ for( i = 0 ; i < nArg ; ++i ){ pObj = apArg[i]; if( pObj->nIdx == SXU32_HIGH ){ if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){ /* Not so fatal,Throw a warning */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a variable not a constant"); } } res = (pObj->iFlags & MEMOBJ_NULL) ? 0 : 1; if( !res ){ /* Variable not set,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } } /* All given variable are set,return TRUE */ ph7_result_bool(pCtx,1); return SXRET_OK; } /* * Unset a memory object [i.e: a ph7_value],remove it from the current * frame,the reference table and discard it's contents. * This function never fail and always return SXRET_OK. */ PH7_PRIVATE sxi32 PH7_VmUnsetMemObj(ph7_vm *pVm,sxu32 nObjIdx,int bForce) { ph7_value *pObj; VmRefObj *pRef; pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nObjIdx); if( pObj ){ /* Release the object */ PH7_MemObjRelease(pObj); } /* Remove old reference links */ pRef = VmRefObjExtract(&(*pVm),nObjIdx); if( pRef ){ sxi32 iFlags = pRef->iFlags; /* Unlink from the reference table */ VmRefObjUnlink(&(*pVm),pRef); if( (bForce == TRUE) || (iFlags & VM_REF_IDX_KEEP) == 0 ){ VmSlot sFree; /* Restore to the free list */ sFree.nIdx = nObjIdx; sFree.pUserData = 0; SySetPut(&pVm->aFreeObj,(const void *)&sFree); } } return SXRET_OK; } /* * void unset($var,...) * Unset one or more given variable. * Parameters * One or more variable to unset. * Return * Nothing. */ static int vm_builtin_unset(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pObj; ph7_vm *pVm; int i; /* Point to the target VM */ pVm = pCtx->pVm; /* Iterate and unset */ for( i = 0 ; i < nArg ; ++i ){ pObj = apArg[i]; if( pObj->nIdx == SXU32_HIGH ){ if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){ /* Throw an error */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a variable not a constant"); } }else{ sxu32 nIdx = pObj->nIdx; /* TICKET 1433-35: Protect the $GLOBALS array from deletion */ if( nIdx != pVm->nGlobalIdx ){ PH7_VmUnsetMemObj(&(*pVm),nIdx,FALSE); } } } return SXRET_OK; } /* * Hash walker callback used by the [get_defined_vars()] function. */ static sxi32 VmHashVarWalker(SyHashEntry *pEntry,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; ph7_vm *pVm = pArray->pVm; ph7_value *pObj; sxu32 nIdx; /* Extract the memory object */ nIdx = SX_PTR_TO_INT(pEntry->pUserData); pObj = (ph7_value *)SySetAt(&pVm->aMemObj,nIdx); if( pObj ){ if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 || (ph7_hashmap *)pObj->x.pOther != pVm->pGlobal ){ if( pEntry->nKeyLen > 0 ){ SyString sName; ph7_value sKey; /* Perform the insertion */ SyStringInitFromBuf(&sName,pEntry->pKey,pEntry->nKeyLen); PH7_MemObjInitFromString(pVm,&sKey,&sName); ph7_array_add_elem(pArray,&sKey/*Will make it's own copy*/,pObj); PH7_MemObjRelease(&sKey); } } } return SXRET_OK; } /* * array get_defined_vars(void) * Returns an array of all defined variables. * Parameter * None * Return * An array with all the variables defined in the current scope. */ static int vm_builtin_get_defined_vars(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pArray; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Superglobals first */ SyHashForEach(&pVm->hSuper,VmHashVarWalker,pArray); /* Then variable defined in the current frame */ SyHashForEach(&pVm->pFrame->hVar,VmHashVarWalker,pArray); /* Finally,return the created array */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * bool gettype($var) * Get the type of a variable * Parameters * $var * The variable being type checked. * Return * String representation of the given variable type. */ static int vm_builtin_gettype(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zType = "Empty"; if( nArg > 0 ){ zType = PH7_MemObjTypeDump(apArg[0]); } /* Return the variable type */ ph7_result_string(pCtx,zType,-1/*Compute length automatically*/); return SXRET_OK; } /* * string get_resource_type(resource $handle) * This function gets the type of the given resource. * Parameters * $handle * The evaluated resource handle. * Return * If the given handle is a resource, this function will return a string * representing its type. If the type is not identified by this function * the return value will be the string Unknown. * This function will return FALSE and generate an error if handle * is not a resource. */ static int vm_builtin_get_resource_type(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE*/ ph7_result_bool(pCtx,0); return PH7_OK; } ph7_result_string_format(pCtx,"resID_%#x",apArg[0]->x.pOther); return SXRET_OK; } /* * void var_dump(expression,....) * var_dump � Dumps information about a variable * Parameters * One or more expression to dump. * Returns * Nothing. */ static int vm_builtin_var_dump(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyBlob sDump; /* Generated dump is stored here */ int i; SyBlobInit(&sDump,&pCtx->pVm->sAllocator); /* Dump one or more expressions */ for( i = 0 ; i < nArg ; i++ ){ ph7_value *pObj = apArg[i]; /* Reset the working buffer */ SyBlobReset(&sDump); /* Dump the given expression */ PH7_MemObjDump(&sDump,pObj,TRUE,0,0,0); /* Output */ if( SyBlobLength(&sDump) > 0 ){ ph7_context_output(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); } } /* Release the working buffer */ SyBlobRelease(&sDump); return SXRET_OK; } /* * string/bool print_r(expression,[bool $return = FALSE]) * print-r - Prints human-readable information about a variable * Parameters * expression: Expression to dump * return : If you would like to capture the output of print_r() use * the return parameter. When this parameter is set to TRUE * print_r() will return the information rather than print it. * Return * When the return parameter is TRUE, this function will return a string. * Otherwise, the return value is TRUE. */ static int vm_builtin_print_r(ph7_context *pCtx,int nArg,ph7_value **apArg) { int ret_string = 0; SyBlob sDump; if( nArg < 1 ){ /* Nothing to output,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } SyBlobInit(&sDump,&pCtx->pVm->sAllocator); if ( nArg > 1 ){ /* Where to redirect output */ ret_string = ph7_value_to_bool(apArg[1]); } /* Generate dump */ PH7_MemObjDump(&sDump,apArg[0],FALSE,0,0,0); if( !ret_string ){ /* Output dump */ ph7_context_output(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); /* Return true */ ph7_result_bool(pCtx,1); }else{ /* Generated dump as return value */ ph7_result_string(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); } /* Release the working buffer */ SyBlobRelease(&sDump); return SXRET_OK; } /* * string/null var_export(expression,[bool $return = FALSE]) * Same job as print_r. (see coment above) */ static int vm_builtin_var_export(ph7_context *pCtx,int nArg,ph7_value **apArg) { int ret_string = 0; SyBlob sDump; /* Dump is stored in this BLOB */ if( nArg < 1 ){ /* Nothing to output,return FALSE */ ph7_result_bool(pCtx,0); return SXRET_OK; } SyBlobInit(&sDump,&pCtx->pVm->sAllocator); if ( nArg > 1 ){ /* Where to redirect output */ ret_string = ph7_value_to_bool(apArg[1]); } /* Generate dump */ PH7_MemObjDump(&sDump,apArg[0],FALSE,0,0,0); if( !ret_string ){ /* Output dump */ ph7_context_output(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); /* Return NULL */ ph7_result_null(pCtx); }else{ /* Generated dump as return value */ ph7_result_string(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); } /* Release the working buffer */ SyBlobRelease(&sDump); return SXRET_OK; } /* * int/bool assert_options(int $what [, mixed $value ]) * Set/get the various assert flags. * Parameter * $what * ASSERT_ACTIVE Enable assert() evaluation * ASSERT_WARNING Issue a warning for each failed assertion * ASSERT_BAIL Terminate execution on failed assertions * ASSERT_QUIET_EVAL Not used * ASSERT_CALLBACK Callback to call on failed assertions * $value * An optional new value for the option. * Return * Old setting on success or FALSE on failure. */ static int vm_builtin_assert_options(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; int iOld,iNew,iValue; if( nArg < 1 || !ph7_value_is_int(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Save old assertion flags */ iOld = pVm->iAssertFlags; /* Extract the new flags */ iNew = ph7_value_to_int(apArg[0]); if( iNew == PH7_ASSERT_DISABLE ){ pVm->iAssertFlags &= ~PH7_ASSERT_DISABLE; if( nArg > 1 ){ iValue = !ph7_value_to_bool(apArg[1]); if( iValue ){ /* Disable assertion */ pVm->iAssertFlags |= PH7_ASSERT_DISABLE; } } }else if( iNew == PH7_ASSERT_WARNING ){ pVm->iAssertFlags &= ~PH7_ASSERT_WARNING; if( nArg > 1 ){ iValue = ph7_value_to_bool(apArg[1]); if( iValue ){ /* Issue a warning for each failed assertion */ pVm->iAssertFlags |= PH7_ASSERT_WARNING; } } }else if( iNew == PH7_ASSERT_BAIL ){ pVm->iAssertFlags &= ~PH7_ASSERT_BAIL; if( nArg > 1 ){ iValue = ph7_value_to_bool(apArg[1]); if( iValue ){ /* Terminate execution on failed assertions */ pVm->iAssertFlags |= PH7_ASSERT_BAIL; } } }else if( iNew == PH7_ASSERT_CALLBACK ){ pVm->iAssertFlags &= ~PH7_ASSERT_CALLBACK; if( nArg > 1 && ph7_value_is_callable(apArg[1]) ){ /* Callback to call on failed assertions */ PH7_MemObjStore(apArg[1],&pVm->sAssertCallback); pVm->iAssertFlags |= PH7_ASSERT_CALLBACK; } } /* Return the old flags */ ph7_result_int(pCtx,iOld); return PH7_OK; } /* * bool assert(mixed $assertion) * Checks if assertion is FALSE. * Parameter * $assertion * The assertion to test. * Return * FALSE if the assertion is false, TRUE otherwise. */ static int vm_builtin_assert(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pAssert; int iFlags,iResult; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } iFlags = pVm->iAssertFlags; if( iFlags & PH7_ASSERT_DISABLE ){ /* Assertion is disabled,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } pAssert = apArg[0]; iResult = 1; /* cc warning */ if( pAssert->iFlags & MEMOBJ_STRING ){ SyString sChunk; SyStringInitFromBuf(&sChunk,SyBlobData(&pAssert->sBlob),SyBlobLength(&pAssert->sBlob)); if( sChunk.nByte > 0 ){ VmEvalChunk(pVm,pCtx,&sChunk,PH7_PHP_ONLY|PH7_PHP_EXPR,FALSE); /* Extract evaluation result */ iResult = ph7_value_to_bool(pCtx->pRet); }else{ iResult = 0; } }else{ /* Perform a boolean cast */ iResult = ph7_value_to_bool(apArg[0]); } if( !iResult ){ /* Assertion failed */ if( iFlags & PH7_ASSERT_CALLBACK ){ static const SyString sFileName = { ":Memory", sizeof(":Memory") - 1}; ph7_value sFile,sLine; ph7_value *apCbArg[3]; SyString *pFile; /* Extract the processed script */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile == 0 ){ pFile = (SyString *)&sFileName; } /* Invoke the callback */ PH7_MemObjInitFromString(pVm,&sFile,pFile); PH7_MemObjInitFromInt(pVm,&sLine,0); apCbArg[0] = &sFile; apCbArg[1] = &sLine; apCbArg[2] = pAssert; PH7_VmCallUserFunction(pVm,&pVm->sAssertCallback,3,apCbArg,0); /* Clean-up the mess left behind */ PH7_MemObjRelease(&sFile); PH7_MemObjRelease(&sLine); } if( iFlags & PH7_ASSERT_WARNING ){ /* Emit a warning */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Assertion failed"); } if( iFlags & PH7_ASSERT_BAIL ){ /* Abort VM execution immediately */ return PH7_ABORT; } } /* Assertion result */ ph7_result_bool(pCtx,iResult); return PH7_OK; } /* * Section: * Error reporting functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * bool trigger_error(string $error_msg[,int $error_type = E_USER_NOTICE ]) * Generates a user-level error/warning/notice message. * Parameters * $error_msg * The designated error message for this error. It's limited to 1024 characters * in length. Any additional characters beyond 1024 will be truncated. * $error_type * The designated error type for this error. It only works with the E_USER family * of constants, and will default to E_USER_NOTICE. * Return * This function returns FALSE if wrong error_type is specified, TRUE otherwise. */ static int vm_builtin_trigger_error(ph7_context *pCtx,int nArg,ph7_value **apArg) { int nErr = PH7_CTX_NOTICE; int rc = PH7_OK; if( nArg > 0 ){ const char *zErr; int nLen; /* Extract the error message */ zErr = ph7_value_to_string(apArg[0],&nLen); if( nArg > 1 ){ /* Extract the error type */ nErr = ph7_value_to_int(apArg[1]); switch( nErr ){ case 1: /* E_ERROR */ case 16: /* E_CORE_ERROR */ case 64: /* E_COMPILE_ERROR */ case 256: /* E_USER_ERROR */ nErr = PH7_CTX_ERR; rc = PH7_ABORT; /* Abort processing immediately */ break; case 2: /* E_WARNING */ case 32: /* E_CORE_WARNING */ case 123: /* E_COMPILE_WARNING */ case 512: /* E_USER_WARNING */ nErr = PH7_CTX_WARNING; break; default: nErr = PH7_CTX_NOTICE; break; } } /* Report error */ ph7_context_throw_error_format(pCtx,nErr,"%.*s",nLen,zErr); /* Return true */ ph7_result_bool(pCtx,1); }else{ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); } return rc; } /* * int error_reporting([int $level]) * Sets which PHP errors are reported. * Parameters * $level * The new error_reporting level. It takes on either a bitmask, or named constants. * Using named constants is strongly encouraged to ensure compatibility for future versions. * As error levels are added, the range of integers increases, so older integer-based error * levels will not always behave as expected. * The available error level constants and the actual meanings of these error levels are described * in the predefined constants. * Return * Returns the old error_reporting level or the current level if no level * parameter is given. */ static int vm_builtin_error_reporting(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; int nOld; /* Extract the old reporting level */ nOld = pVm->bErrReport ? 32767 /* E_ALL */ : 0; if( nArg > 0 ){ int nNew; /* Extract the desired error reporting level */ nNew = ph7_value_to_int(apArg[0]); if( !nNew ){ /* Do not report errors at all */ pVm->bErrReport = 0; }else{ /* Report all errors */ pVm->bErrReport = 1; } } /* Return the old level */ ph7_result_int(pCtx,nOld); return PH7_OK; } /* * bool error_log(string $message[,int $message_type = 0 [,string $destination[,string $extra_headers]]]) * Send an error message somewhere. * Parameter * $message * The error message that should be logged. * $message_type * Says where the error should go. The possible message types are as follows: * 0 message is sent to PHP's system logger, using the Operating System's system logging mechanism * or a file, depending on what the error_log configuration directive is set to. * This is the default option. * 1 message is sent by email to the address in the destination parameter. * This is the only message type where the fourth parameter, extra_headers is used. * 2 No longer an option. * 3 message is appended to the file destination. A newline is not automatically added * to the end of the message string. * 4 message is sent directly to the SAPI logging handler. * $destination * The destination. Its meaning depends on the message_type parameter as described above. * $extra_headers * The extra headers. It's used when the message_type parameter is set to 1 * Return * TRUE on success or FALSE on failure. * NOTE: * Actually,PH7 does not care about the given parameters,all this function does * is to invoke any user callback registered using the PH7_VM_CONFIG_ERR_LOG_HANDLER * configuration directive (refer to the official documentation for more information). * Otherwise this function is no-op. */ static int vm_builtin_error_log(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zMessage,*zDest,*zHeader; ph7_vm *pVm = pCtx->pVm; int iType = 0; if( nArg < 1 ){ /* Missing log message,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( pVm->xErrLog ){ /* Invoke the user callback */ zMessage = ph7_value_to_string(apArg[0],0); zDest = zHeader = ""; /* Empty string */ if( nArg > 1 ){ iType = ph7_value_to_int(apArg[1]); if( nArg > 2 ){ zDest = ph7_value_to_string(apArg[2],0); if( nArg > 3 ){ zHeader = ph7_value_to_string(apArg[3],0); } } } pVm->xErrLog(zMessage,iType,zDest,zHeader); } /* Retun TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool restore_exception_handler(void) * Restores the previously defined exception handler function. * Parameter * None * Return * TRUE if the exception handler is restored.FALSE otherwise */ static int vm_builtin_restore_exception_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pOld,*pNew; /* Point to the old and the new handler */ pOld = &pVm->aExceptionCB[0]; pNew = &pVm->aExceptionCB[1]; if( pOld->iFlags & MEMOBJ_NULL ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* No installed handler,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Copy the old handler */ PH7_MemObjStore(pOld,pNew); PH7_MemObjRelease(pOld); /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * callable set_exception_handler(callable $exception_handler) * Sets a user-defined exception handler function. * Sets the default exception handler if an exception is not caught within a try/catch block. * NOTE * Execution will NOT stop after the exception_handler calls for example die/exit unlike * the satndard PHP engine. * Parameters * $exception_handler * Name of the function to be called when an uncaught exception occurs. * This handler function needs to accept one parameter, which will be the exception object * that was thrown. * Note: * NULL may be passed instead, to reset this handler to its default state. * Return * Returns the name of the previously defined exception handler, or NULL on error. * If no previous handler was defined, NULL is also returned. If NULL is passed * resetting the handler to its default state, TRUE is returned. */ static int vm_builtin_set_exception_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pOld,*pNew; /* Point to the old and the new handler */ pOld = &pVm->aExceptionCB[0]; pNew = &pVm->aExceptionCB[1]; /* Return the old handler */ ph7_result_value(pCtx,pOld); /* Will make it's own copy */ if( nArg > 0 ){ if( !ph7_value_is_callable(apArg[0])) { /* Not callable,return TRUE (As requested by the PHP specification) */ PH7_MemObjRelease(pNew); ph7_result_bool(pCtx,1); }else{ PH7_MemObjStore(pNew,pOld); /* Install the new handler */ PH7_MemObjStore(apArg[0],pNew); } } return PH7_OK; } /* * bool restore_error_handler(void) * THIS FUNCTION IS A NO-OP IN THE CURRENT RELEASE OF THE PH7 ENGINE. * Parameters: * None. * Return * Always TRUE. */ static int vm_builtin_restore_error_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pOld,*pNew; /* Point to the old and the new handler */ pOld = &pVm->aErrCB[0]; pNew = &pVm->aErrCB[1]; if( pOld->iFlags & MEMOBJ_NULL ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* No installed callback,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Copy the old callback */ PH7_MemObjStore(pOld,pNew); PH7_MemObjRelease(pOld); /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * value set_error_handler(callable $error_handler) * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * THIS FUNCTION IS DISABLED IN THE CURRENT RELEASE OF THE PH7 ENGINE. * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * Sets a user-defined error handler function. * This function can be used for defining your own way of handling errors during * runtime, for example in applications in which you need to do cleanup of data/files * when a critical error happens, or when you need to trigger an error under certain * conditions (using trigger_error()). * Parameters * $error_handler * The user function needs to accept two parameters: the error code, and a string * describing the error. * Then there are three optional parameters that may be supplied: the filename in which * the error occurred, the line number in which the error occurred, and the context in which * the error occurred (an array that points to the active symbol table at the point the error occurred). * The function can be shown as: * handler ( int $errno , string $errstr [, string $errfile]) * errno * The first parameter, errno, contains the level of the error raised, as an integer. * errstr * The second parameter, errstr, contains the error message, as a string. * errfile * The third parameter is optional, errfile, which contains the filename that the error * was raised in, as a string. * Note: * NULL may be passed instead, to reset this handler to its default state. * Return * Returns the name of the previously defined error handler, or NULL on error. * If no previous handler was defined, NULL is also returned. If NULL is passed * resetting the handler to its default state, TRUE is returned. */ static int vm_builtin_set_error_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pOld,*pNew; /* Point to the old and the new handler */ pOld = &pVm->aErrCB[0]; pNew = &pVm->aErrCB[1]; /* Return the old handler */ ph7_result_value(pCtx,pOld); /* Will make it's own copy */ if( nArg > 0 ){ if( !ph7_value_is_callable(apArg[0])) { /* Not callable,return TRUE (As requested by the PHP specification) */ PH7_MemObjRelease(pNew); ph7_result_bool(pCtx,1); }else{ PH7_MemObjStore(pNew,pOld); /* Install the new handler */ PH7_MemObjStore(apArg[0],pNew); } } ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "This function is disabled in the current release of the PH7(%s) engine", ph7_lib_version() ); return PH7_OK; } /* * array debug_backtrace([ int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT [, int $limit = 0 ]] ) * Generates a backtrace. * Paramaeter * $options * DEBUG_BACKTRACE_PROVIDE_OBJECT: Whether or not to populate the "object" index. * DEBUG_BACKTRACE_IGNORE_ARGS Whether or not to omit the "args" index, and thus * all the function/method arguments, to save memory. * $limit * (Not Used) * Return * An array.The possible returned elements are as follows: * Possible returned elements from debug_backtrace() * Name Type Description * ------ ------ ----------- * function string The current function name. See also __FUNCTION__. * line integer The current line number. See also __LINE__. * file string The current file name. See also __FILE__. * class string The current class name. See also __CLASS__ * object object The current object. * args array If inside a function, this lists the functions arguments. * If inside an included file, this lists the included file name(s). */ static int vm_builtin_debug_backtrace(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; ph7_value *pArray; ph7_class *pClass; ph7_value *pValue; SyString *pFile; /* Create a new array */ pArray = ph7_context_new_array(pCtx); pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ /* Out of memory,return NULL */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_null(pCtx); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Dump running function name and it's arguments */ if( pVm->pFrame->pParent ){ VmFrame *pFrame = pVm->pFrame; ph7_vm_func *pFunc; ph7_value *pArg; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } pFunc = (ph7_vm_func *)pFrame->pUserData; if( pFrame->pParent && pFunc ){ ph7_value_string(pValue,pFunc->sName.zString,(int)pFunc->sName.nByte); ph7_array_add_strkey_elem(pArray,"function",pValue); ph7_value_reset_string_cursor(pValue); } /* Function arguments */ pArg = ph7_context_new_array(pCtx); if( pArg ){ ph7_value *pObj; VmSlot *aSlot; sxu32 n; /* Start filling the array with the given arguments */ aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){ pObj = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj,aSlot[n].nIdx); if( pObj ){ ph7_array_add_elem(pArg,0/* Automatic index assign*/,pObj); } } /* Save the array */ ph7_array_add_strkey_elem(pArray,"args",pArg); } } ph7_value_int(pValue,1); /* Append the current line (which is always 1 since PH7 does not track * line numbers at run-time. ) */ ph7_array_add_strkey_elem(pArray,"line",pValue); /* Current processed script */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile ){ ph7_value_string(pValue,pFile->zString,(int)pFile->nByte); ph7_array_add_strkey_elem(pArray,"file",pValue); ph7_value_reset_string_cursor(pValue); } /* Top class */ pClass = PH7_VmPeekTopClass(pVm); if( pClass ){ ph7_value_reset_string_cursor(pValue); ph7_value_string(pValue,pClass->sName.zString,(int)pClass->sName.nByte); ph7_array_add_strkey_elem(pArray,"class",pValue); } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory, everything will be released automatically * as soon we return from this function. */ return PH7_OK; } /* * Generate a small backtrace. * Store the generated dump in the given BLOB */ static int VmMiniBacktrace( ph7_vm *pVm, /* Target VM */ SyBlob *pOut /* Store Dump here */ ) { VmFrame *pFrame = pVm->pFrame; ph7_vm_func *pFunc; ph7_class *pClass; SyString *pFile; /* Called function */ while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } pFunc = (ph7_vm_func *)pFrame->pUserData; SyBlobAppend(pOut,"[",sizeof(char)); if( pFrame->pParent && pFunc ){ SyBlobAppend(pOut,"Called function: ",sizeof("Called function: ")-1); SyBlobAppend(pOut,pFunc->sName.zString,pFunc->sName.nByte); }else{ SyBlobAppend(pOut,"Global scope",sizeof("Global scope") - 1); } SyBlobAppend(pOut,"]",sizeof(char)); /* Current processed script */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile ){ SyBlobAppend(pOut,"[",sizeof(char)); SyBlobAppend(pOut,"Processed file: ",sizeof("Processed file: ")-1); SyBlobAppend(pOut,pFile->zString,pFile->nByte); SyBlobAppend(pOut,"]",sizeof(char)); } /* Top class */ pClass = PH7_VmPeekTopClass(pVm); if( pClass ){ SyBlobAppend(pOut,"[",sizeof(char)); SyBlobAppend(pOut,"Class: ",sizeof("Class: ")-1); SyBlobAppend(pOut,pClass->sName.zString,pClass->sName.nByte); SyBlobAppend(pOut,"]",sizeof(char)); } SyBlobAppend(pOut,"\n",sizeof(char)); /* All done */ return SXRET_OK; } /* * void debug_print_backtrace() * Prints a backtrace * Parameters * None * Return * NULL */ static int vm_builtin_debug_print_backtrace(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; SyBlob sDump; SyBlobInit(&sDump,&pVm->sAllocator); /* Generate the backtrace */ VmMiniBacktrace(pVm,&sDump); /* Output backtrace */ ph7_context_output(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); /* All done,cleanup */ SyBlobRelease(&sDump); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* * string debug_string_backtrace() * Generate a backtrace * Parameters * None * Return * A mini backtrace(). * Note that this is a symisc extension. */ static int vm_builtin_debug_string_backtrace(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; SyBlob sDump; SyBlobInit(&sDump,&pVm->sAllocator); /* Generate the backtrace */ VmMiniBacktrace(pVm,&sDump); /* Return the backtrace */ ph7_result_string(pCtx,(const char *)SyBlobData(&sDump),(int)SyBlobLength(&sDump)); /* Will make it's own copy */ /* All done,cleanup */ SyBlobRelease(&sDump); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* * The following routine is invoked by the engine when an uncaught * exception is triggered. */ static sxi32 VmUncaughtException( ph7_vm *pVm, /* Target VM */ ph7_class_instance *pThis /* Exception class instance [i.e: Exception $e] */ ) { ph7_value *apArg[2],sArg; int nArg = 1; sxi32 rc; if( pVm->nExceptDepth > 15 ){ /* Nesting limit reached */ return SXRET_OK; } /* Call any exception handler if available */ PH7_MemObjInit(pVm,&sArg); if( pThis ){ /* Load the exception instance */ sArg.x.pOther = pThis; pThis->iRef++; MemObjSetType(&sArg,MEMOBJ_OBJ); }else{ nArg = 0; } apArg[0] = &sArg; /* Call the exception handler if available */ pVm->nExceptDepth++; rc = PH7_VmCallUserFunction(&(*pVm),&pVm->aExceptionCB[1],1,apArg,0); pVm->nExceptDepth--; if( rc != SXRET_OK ){ SyString sName = { "Exception" , sizeof("Exception") - 1 }; SyString sFuncName = { "Global",sizeof("Global") - 1 }; VmFrame *pFrame = pVm->pFrame; /* No available handler,generate a fatal error */ if( pThis ){ SyStringDupPtr(&sName,&pThis->pClass->sName); } while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Ignore exception frames */ pFrame = pFrame->pParent; } if( pFrame->pParent ){ if( pFrame->iFlags & VM_FRAME_CATCH ){ SyStringInitFromBuf(&sFuncName,"Catch_block",sizeof("Catch_block")-1); }else{ ph7_vm_func *pFunc = (ph7_vm_func *)pFrame->pUserData; if( pFunc ){ SyStringDupPtr(&sFuncName,&pFunc->sName); } } } /* Generate a listing */ VmErrorFormat(&(*pVm),PH7_CTX_ERR, "Uncaught exception '%z' in the '%z' frame context", &sName,&sFuncName); /* Tell the upper layer to stop VM execution immediately */ rc = SXERR_ABORT; } PH7_MemObjRelease(&sArg); return rc; } /* * Throw an user exception. */ static sxi32 VmThrowException( ph7_vm *pVm, /* Target VM */ ph7_class_instance *pThis /* Exception class instance [i.e: Exception $e] */ ) { ph7_exception_block *pCatch; /* Catch block to execute */ ph7_exception **apException; ph7_exception *pException; /* Point to the stack of loaded exceptions */ apException = (ph7_exception **)SySetBasePtr(&pVm->aException); pException = 0; pCatch = 0; if( SySetUsed(&pVm->aException) > 0 ){ ph7_exception_block *aCatch; ph7_class *pClass; sxu32 j; /* Locate the appropriate block to execute */ pException = apException[SySetUsed(&pVm->aException) - 1]; (void)SySetPop(&pVm->aException); aCatch = (ph7_exception_block *)SySetBasePtr(&pException->sEntry); for( j = 0 ; j < SySetUsed(&pException->sEntry) ; ++j ){ SyString *pName = &aCatch[j].sClass; /* Extract the target class */ pClass = PH7_VmExtractClass(&(*pVm),pName->zString,pName->nByte,TRUE,0); if( pClass == 0 ){ /* No such class */ continue; } if( VmInstanceOf(pThis->pClass,pClass) ){ /* Catch block found,break immeditaley */ pCatch = &aCatch[j]; break; } } } /* Execute the cached block if available */ if( pCatch == 0 ){ sxi32 rc; rc = VmUncaughtException(&(*pVm),pThis); if( rc == SXRET_OK && pException ){ VmFrame *pFrame = pVm->pFrame; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pException->pFrame == pFrame ){ /* Tell the upper layer that the exception was caught */ pFrame->iFlags &= ~VM_FRAME_THROW; } } return rc; }else{ VmFrame *pFrame = pVm->pFrame; sxi32 rc; while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pException->pFrame == pFrame ){ /* Tell the upper layer that the exception was caught */ pFrame->iFlags &= ~VM_FRAME_THROW; } /* Create a private frame first */ rc = VmEnterFrame(&(*pVm),0,0,&pFrame); if( rc == SXRET_OK ){ /* Mark as catch frame */ ph7_value *pObj = VmExtractMemObj(&(*pVm),&pCatch->sThis,FALSE,TRUE); pFrame->iFlags |= VM_FRAME_CATCH; if( pObj ){ /* Install the exception instance */ pThis->iRef++; /* Increment reference count */ pObj->x.pOther = pThis; MemObjSetType(pObj,MEMOBJ_OBJ); } /* Exceute the block */ VmLocalExec(&(*pVm),&pCatch->sByteCode,0); /* Leave the frame */ VmLeaveFrame(&(*pVm)); } } /* TICKET 1433-60: Do not release the 'pException' pointer since it may * be used again if a 'goto' statement is executed. */ return SXRET_OK; } /* * Section: * Version,Credits and Copyright related functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * string ph7version(void) * Returns the running version of the PH7 version. * Parameters * None * Return * Current PH7 version. */ static int vm_builtin_ph7_version(ph7_context *pCtx,int nArg,ph7_value **apArg) { SXUNUSED(nArg); SXUNUSED(apArg); /* cc warning */ /* Current engine version */ ph7_result_string(pCtx,PH7_VERSION,sizeof(PH7_VERSION) - 1); return PH7_OK; } /* * PH7 release information HTML page used by the ph7info() and ph7credits() functions. */ #define PH7_HTML_PAGE_HEADER ""\ ""\ ""\ "PH7 engine credits"\ ""\ "
"\ "

PH7 Engine Credits

"\ "
"\ "

"\ "Symisc PH7 

"\ "

"\ "A highly efficient embeddable bytecode compiler and a Virtual Machine for the PHP(5) Programming Language.

"\ "

Copyright (C) Symisc Systems.

"\ "

Engine Version:

"\ "

" #define PH7_HTML_PAGE_FORMAT "%s

"\ "

Engine ID:

"\ "

%s %s

"\ "

Underlying VFS:

"\ "

%s

"\ "

Total Built-in Functions:

"\ "

%d

"\ "

Total Built-in Classes:

"\ "

%d

"\ "

Host Operating System:

"\ "

%s

"\ "

"\ "

Licensed To: <Public Release Under The "\ "Symisc Public License (SPL)>

" #define PH7_HTML_PAGE_FOOTER "

/*
"\ " * Copyright (C) 2011, 2012 Symisc Systems. All rights reserved.
"\ " *
"\ " * Redistribution and use in source and binary forms, with or without
"\ " * modification, are permitted provided that the following conditions
"\ " * are met:
"\ " * 1. Redistributions of source code must retain the above copyright
"\ " *    notice, this list of conditions and the following disclaimer.
"\ " * 2. Redistributions in binary form must reproduce the above copyright
"\ " *    notice, this list of conditions and the following disclaimer in the
"\ " *    documentation and/or other materials provided with the distribution.
"\ " * 3. Redistributions in any form must be accompanied by information on
"\ " *    how to obtain complete source code for the PH7 engine and any
"\ " *    accompanying software that uses the PH7 engine software.
"\ " *    The source code must either be included in the distribution
"\ " *    or be available for no more than the cost of distribution plus
"\ " *    a nominal fee, and must be freely redistributable under reasonable
"\ " *    conditions. For an executable file, complete source code means
"\ " *    the source code for all modules it contains.It does not include
"\ " *    source code for modules or files that typically accompany the major
"\ " *    components of the operating system on which the executable file runs.
"\ " *
"\ " * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
"\ " * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
"\ " * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
"\ " * NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO EVENT SHALL SYMISC SYSTEMS
"\ " * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
"\ " * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
"\ " * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
"\ " * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
"\ " * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
"\ " * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
"\ " * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"\ " */
"\ "

"\ "

Copyright (C) Symisc Systems"\ "

" /* * bool ph7credits(void) * bool ph7info(void) * bool ph7copyright(void) * Prints out the credits for PH7 engine * Parameters * None * Return * Always TRUE */ static int vm_builtin_ph7_credits(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; /* Point to the underlying VM */ /* Expand the HTML page above*/ ph7_context_output(pCtx,PH7_HTML_PAGE_HEADER,(int)sizeof(PH7_HTML_PAGE_HEADER)-1); ph7_context_output_format( pCtx, PH7_HTML_PAGE_FORMAT, ph7_lib_version(), /* Engine version */ ph7_lib_signature(), /* Engine signature */ ph7_lib_ident(), /* Engine ID */ pVm->pEngine->pVfs ? pVm->pEngine->pVfs->zName : "null_vfs", SyHashTotalEntry(&pVm->hFunction) + SyHashTotalEntry(&pVm->hHostFunction),/* # built-in functions */ SyHashTotalEntry(&pVm->hClass), #ifdef __WINNT__ "Windows NT" #elif defined(__UNIXES__) "UNIX-Like" #else "Other OS" #endif ); ph7_context_output(pCtx,PH7_HTML_PAGE_FOOTER,(int)sizeof(PH7_HTML_PAGE_FOOTER)-1); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return TRUE */ //ph7_result_bool(pCtx,1); return PH7_OK; } /* * Section: * URL related routines. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* Forward declaration */ static sxi32 VmHttpSplitURI(SyhttpUri *pOut,const char *zUri,sxu32 nLen); /* * value parse_url(string $url [, int $component = -1 ]) * Parse a URL and return its fields. * Parameters * $url * The URL to parse. * $component * Specify one of PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_USER * PHP_URL_PASS, PHP_URL_PATH, PHP_URL_QUERY or PHP_URL_FRAGMENT to retrieve * just a specific URL component as a string (except when PHP_URL_PORT is given * in which case the return value will be an integer). * Return * If the component parameter is omitted, an associative array is returned. * At least one element will be present within the array. Potential keys within * this array are: * scheme - e.g. http * host * port * user * pass * path * query - after the question mark ? * fragment - after the hashmark # * Note: * FALSE is returned on failure. * This function work with relative URL unlike the one shipped * with the standard PHP engine. */ static int vm_builtin_parse_url(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zStr; /* Input string */ SyString *pComp; /* Pointer to the URI component */ SyhttpUri sURI; /* Parse of the given URI */ int nLen; sxi32 rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the given URI */ zStr = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Nothing to process,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Get a parse */ rc = VmHttpSplitURI(&sURI,zStr,(sxu32)nLen); if( rc != SXRET_OK ){ /* Malformed input,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ int nComponent = ph7_value_to_int(apArg[1]); /* Refer to constant.c for constants values */ switch(nComponent){ case 1: /* PHP_URL_SCHEME */ pComp = &sURI.sScheme; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 2: /* PHP_URL_HOST */ pComp = &sURI.sHost; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 3: /* PHP_URL_PORT */ pComp = &sURI.sPort; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ int iPort = 0; /* Cast the value to integer */ SyStrToInt32(pComp->zString,pComp->nByte,(void *)&iPort,0); ph7_result_int(pCtx,iPort); } break; case 4: /* PHP_URL_USER */ pComp = &sURI.sUser; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 5: /* PHP_URL_PASS */ pComp = &sURI.sPass; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 7: /* PHP_URL_QUERY */ pComp = &sURI.sQuery; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 8: /* PHP_URL_FRAGMENT */ pComp = &sURI.sFragment; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; case 6: /* PHP_URL_PATH */ pComp = &sURI.sPath; if( pComp->nByte < 1 ){ /* No available value,return NULL */ ph7_result_null(pCtx); }else{ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); } break; default: /* No such entry,return NULL */ ph7_result_null(pCtx); break; } }else{ ph7_value *pArray,*pValue; /* Return an associative array */ pArray = ph7_context_new_array(pCtx); /* Empty array */ pValue = ph7_context_new_scalar(pCtx); /* Array value */ if( pArray == 0 || pValue == 0 ){ /* Out of memory */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 engine is running out of memory"); /* Return false */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Fill the array */ pComp = &sURI.sScheme; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"scheme",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sHost; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"host",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sPort; if( pComp->nByte > 0 ){ int iPort = 0;/* cc warning */ /* Convert to integer */ SyStrToInt32(pComp->zString,pComp->nByte,(void *)&iPort,0); ph7_value_int(pValue,iPort); ph7_array_add_strkey_elem(pArray,"port",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sUser; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"user",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sPass; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"pass",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sPath; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"path",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sQuery; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"query",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); pComp = &sURI.sFragment; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); ph7_array_add_strkey_elem(pArray,"fragment",pValue); /* Will make it's own copy */ } /* Return the created array */ ph7_result_value(pCtx,pArray); /* NOTE: * Don't worry about freeing 'pValue',everything will be released * automatically as soon we return from this function. */ } /* All done */ return PH7_OK; } /* * Section: * Array related routines. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. * Note 2012-5-21 01:04:15: * Array related functions that need access to the underlying * virtual machine are implemented here rather than 'hashmap.c' */ /* * The [compact()] function store it's state information in an instance * of the following structure. */ struct compact_data { ph7_value *pArray; /* Target array */ int nRecCount; /* Recursion count */ }; /* * Walker callback for the [compact()] function defined below. */ static int VmCompactCallback(ph7_value *pKey,ph7_value *pValue,void *pUserData) { struct compact_data *pData = (struct compact_data *)pUserData; ph7_value *pArray = (ph7_value *)pData->pArray; ph7_vm *pVm = pArray->pVm; /* Act according to the hashmap value */ if( ph7_value_is_string(pValue) ){ SyString sVar; SyStringInitFromBuf(&sVar,SyBlobData(&pValue->sBlob),SyBlobLength(&pValue->sBlob)); if( sVar.nByte > 0 ){ /* Query the current frame */ pKey = VmExtractMemObj(pVm,&sVar,FALSE,FALSE); /* ^ * | Avoid wasting variable and use 'pKey' instead */ if( pKey ){ /* Perform the insertion */ ph7_array_add_elem(pArray,pValue/* Variable name*/,pKey/* Variable value */); } } }else if( ph7_value_is_array(pValue) && pData->nRecCount < 32) { int rc; /* Recursively traverse this array */ pData->nRecCount++; rc = PH7_HashmapWalk((ph7_hashmap *)pValue->x.pOther,VmCompactCallback,pUserData); pData->nRecCount--; return rc; } return SXRET_OK; } /* * array compact(mixed $varname [, mixed $... ]) * Create array containing variables and their values. * For each of these, compact() looks for a variable with that name * in the current symbol table and adds it to the output array such * that the variable name becomes the key and the contents of the variable * become the value for that key. In short, it does the opposite of extract(). * Any strings that are not set will simply be skipped. * Parameters * $varname * compact() takes a variable number of parameters. Each parameter can be either * a string containing the name of the variable, or an array of variable names. * The array can contain other arrays of variable names inside it; compact() handles * it recursively. * Return * The output array with all the variables added to it or NULL on failure */ static int vm_builtin_compact(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pObj; ph7_vm *pVm = pCtx->pVm; const char *zName; SyString sVar; int i,nLen; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create the array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ /* Out of memory */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 engine is running out of memory"); /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ for( i = 0 ; i < nArg ; i++ ){ if( !ph7_value_is_string(apArg[i]) ){ if( ph7_value_is_array(apArg[i]) ){ struct compact_data sData; ph7_hashmap *pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Recursively walk the array */ sData.nRecCount = 0; sData.pArray = pArray; PH7_HashmapWalk(pMap,VmCompactCallback,&sData); } }else{ /* Extract variable name */ zName = ph7_value_to_string(apArg[i],&nLen); if( nLen > 0 ){ SyStringInitFromBuf(&sVar,zName,nLen); /* Check if the variable is available in the current frame */ pObj = VmExtractMemObj(pVm,&sVar,FALSE,FALSE); if( pObj ){ ph7_array_add_elem(pArray,apArg[i]/*Variable name*/,pObj/* Variable value */); } } } } /* Return the array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * The [extract()] function store it's state information in an instance * of the following structure. */ typedef struct extract_aux_data extract_aux_data; struct extract_aux_data { ph7_vm *pVm; /* VM that own this instance */ int iCount; /* Number of variables successfully imported */ const char *zPrefix; /* Prefix name */ int Prefixlen; /* Prefix length */ int iFlags; /* Control flags */ char zWorker[1024]; /* Working buffer */ }; /* Forward declaration */ static int VmExtractCallback(ph7_value *pKey,ph7_value *pValue,void *pUserData); /* * int extract(array &$var_array[,int $extract_type = EXTR_OVERWRITE[,string $prefix = NULL ]]) * Import variables into the current symbol table from an array. * Parameters * $var_array * An associative array. This function treats keys as variable names and values * as variable values. For each key/value pair it will create a variable in the current symbol * table, subject to extract_type and prefix parameters. * You must use an associative array; a numerically indexed array will not produce results * unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID. * $extract_type * The way invalid/numeric keys and collisions are treated is determined by the extract_type. * It can be one of the following values: * EXTR_OVERWRITE * If there is a collision, overwrite the existing variable. * EXTR_SKIP * If there is a collision, don't overwrite the existing variable. * EXTR_PREFIX_SAME * If there is a collision, prefix the variable name with prefix. * EXTR_PREFIX_ALL * Prefix all variable names with prefix. * EXTR_PREFIX_INVALID * Only prefix invalid/numeric variable names with prefix. * EXTR_IF_EXISTS * Only overwrite the variable if it already exists in the current symbol table * otherwise do nothing. * This is useful for defining a list of valid variables and then extracting only those * variables you have defined out of $_REQUEST, for example. * EXTR_PREFIX_IF_EXISTS * Only create prefixed variable names if the non-prefixed version of the same variable exists in * the current symbol table. * $prefix * Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL * EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name * it is not imported into the symbol table. Prefixes are automatically separated from the array key by an * underscore character. * Return * Returns the number of variables successfully imported into the symbol table. */ static int vm_builtin_extract(ph7_context *pCtx,int nArg,ph7_value **apArg) { extract_aux_data sAux; ph7_hashmap *pMap; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Point to the target hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry < 1 ){ /* Empty map,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Prepare the aux data */ SyZero(&sAux,sizeof(extract_aux_data)-sizeof(sAux.zWorker)); if( nArg > 1 ){ sAux.iFlags = ph7_value_to_int(apArg[1]); if( nArg > 2 ){ sAux.zPrefix = ph7_value_to_string(apArg[2],&sAux.Prefixlen); } } sAux.pVm = pCtx->pVm; /* Invoke the worker callback */ PH7_HashmapWalk(pMap,VmExtractCallback,&sAux); /* Number of variables successfully imported */ ph7_result_int(pCtx,sAux.iCount); return PH7_OK; } /* * Worker callback for the [extract()] function defined * below. */ static int VmExtractCallback(ph7_value *pKey,ph7_value *pValue,void *pUserData) { extract_aux_data *pAux = (extract_aux_data *)pUserData; int iFlags = pAux->iFlags; ph7_vm *pVm = pAux->pVm; ph7_value *pObj; SyString sVar; if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){ iFlags |= 0x08; /*EXTR_PREFIX_ALL*/ } /* Perform a string cast */ PH7_MemObjToString(pKey); if( SyBlobLength(&pKey->sBlob) < 1 ){ /* Unavailable variable name */ return SXRET_OK; } sVar.nByte = 0; /* cc warning */ if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){ sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker,sizeof(pAux->zWorker),"%.*s_%.*s", pAux->Prefixlen,pAux->zPrefix, SyBlobLength(&pKey->sBlob),SyBlobData(&pKey->sBlob) ); }else{ sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob),pAux->zWorker, SXMIN(SyBlobLength(&pKey->sBlob),sizeof(pAux->zWorker))); } sVar.zString = pAux->zWorker; /* Try to extract the variable */ pObj = VmExtractMemObj(pVm,&sVar,TRUE,FALSE); if( pObj ){ /* Collision */ if( iFlags & 0x02 /* EXTR_SKIP */ ){ return SXRET_OK; } if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){ if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){ /* Already prefixed */ return SXRET_OK; } sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker,sizeof(pAux->zWorker),"%.*s_%.*s", pAux->Prefixlen,pAux->zPrefix, SyBlobLength(&pKey->sBlob),SyBlobData(&pKey->sBlob) ); pObj = VmExtractMemObj(pVm,&sVar,TRUE,TRUE); } }else{ /* Create the variable */ pObj = VmExtractMemObj(pVm,&sVar,TRUE,TRUE); } if( pObj ){ /* Overwrite the old value */ PH7_MemObjStore(pValue,pObj); /* Increment counter */ pAux->iCount++; } return SXRET_OK; } /* * Worker callback for the [import_request_variables()] function * defined below. */ static int VmImportRequestCallback(ph7_value *pKey,ph7_value *pValue,void *pUserData) { extract_aux_data *pAux = (extract_aux_data *)pUserData; ph7_vm *pVm = pAux->pVm; ph7_value *pObj; SyString sVar; /* Perform a string cast */ PH7_MemObjToString(pKey); if( SyBlobLength(&pKey->sBlob) < 1 ){ /* Unavailable variable name */ return SXRET_OK; } sVar.nByte = 0; /* cc warning */ if( pAux->Prefixlen > 0 ){ sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker,sizeof(pAux->zWorker),"%.*s%.*s", pAux->Prefixlen,pAux->zPrefix, SyBlobLength(&pKey->sBlob),SyBlobData(&pKey->sBlob) ); }else{ sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob),pAux->zWorker, SXMIN(SyBlobLength(&pKey->sBlob),sizeof(pAux->zWorker))); } sVar.zString = pAux->zWorker; /* Extract the variable */ pObj = VmExtractMemObj(pVm,&sVar,TRUE,TRUE); if( pObj ){ PH7_MemObjStore(pValue,pObj); } return SXRET_OK; } /* * bool import_request_variables(string $types[,string $prefix]) * Import GET/POST/Cookie variables into the global scope. * Parameters * $types * Using the types parameter, you can specify which request variables to import. * You can use 'G', 'P' and 'C' characters respectively for GET, POST and Cookie. * These characters are not case sensitive, so you can also use any combination of 'g', 'p' and 'c'. * POST includes the POST uploaded file information. * Note: * Note that the order of the letters matters, as when using "GP", the POST variables will overwrite * GET variables with the same name. Any other letters than GPC are discarded. * $prefix * Variable name prefix, prepended before all variable's name imported into the global scope. * So if you have a GET value named "userid", and provide a prefix "pref_", then you'll get a global * variable named $pref_userid. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_import_request_variables(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPrefix,*zEnd,*zImport; extract_aux_data sAux; int nLen,nPrefixLen; ph7_value *pSuper; ph7_vm *pVm; /* By default import only $_GET variables */ zImport = "G"; nLen = (int)sizeof(char); zPrefix = 0; nPrefixLen = 0; if( nArg > 0 ){ if( ph7_value_is_string(apArg[0]) ){ zImport = ph7_value_to_string(apArg[0],&nLen); } if( nArg > 1 && ph7_value_is_string(apArg[1]) ){ zPrefix = ph7_value_to_string(apArg[1],&nPrefixLen); } } /* Point to the underlying VM */ pVm = pCtx->pVm; /* Initialize the aux data */ SyZero(&sAux,sizeof(sAux)-sizeof(sAux.zWorker)); sAux.zPrefix = zPrefix; sAux.Prefixlen = nPrefixLen; sAux.pVm = pVm; /* Extract */ zEnd = &zImport[nLen]; while( zImport < zEnd ){ int c = zImport[0]; pSuper = 0; if( c == 'G' || c == 'g' ){ /* Import $_GET variables */ pSuper = VmExtractSuper(pVm,"_GET",sizeof("_GET")-1); }else if( c == 'P' || c == 'p' ){ /* Import $_POST variables */ pSuper = VmExtractSuper(pVm,"_POST",sizeof("_POST")-1); }else if( c == 'c' || c == 'C' ){ /* Import $_COOKIE variables */ pSuper = VmExtractSuper(pVm,"_COOKIE",sizeof("_COOKIE")-1); } if( pSuper ){ /* Iterate throw array entries */ ph7_array_walk(pSuper,VmImportRequestCallback,&sAux); } /* Advance the cursor */ zImport++; } /* All done,return TRUE*/ ph7_result_bool(pCtx,0); return PH7_OK; } /* * Compile and evaluate a PHP chunk at run-time. * Refer to the eval() language construct implementation for more * information. */ static sxi32 VmEvalChunk( ph7_vm *pVm, /* Underlying Virtual Machine */ ph7_context *pCtx, /* Call Context */ SyString *pChunk, /* PHP chunk to evaluate */ int iFlags, /* Compile flag */ int bTrueReturn /* TRUE to return execution result */ ) { SySet *pByteCode,aByteCode; ProcConsumer xErr = 0; void *pErrData = 0; /* Initialize bytecode container */ SySetInit(&aByteCode,&pVm->sAllocator,sizeof(VmInstr)); SySetAlloc(&aByteCode,0x20); /* Reset the code generator */ if( bTrueReturn ){ /* Included file,log compile-time errors */ xErr = pVm->pEngine->xConf.xErr; pErrData = pVm->pEngine->xConf.pErrData; } PH7_ResetCodeGenerator(pVm,xErr,pErrData); /* Swap bytecode container */ pByteCode = pVm->pByteContainer; pVm->pByteContainer = &aByteCode; /* Compile the chunk */ PH7_CompileScript(pVm,pChunk,iFlags); if( pVm->sCodeGen.nErr > 0 ){ /* Compilation error,return false */ if( pCtx ){ ph7_result_bool(pCtx,0); } }else{ ph7_value sResult; /* Return value */ if( SXRET_OK != PH7_VmEmitInstr(pVm,PH7_OP_DONE,0,0,0,0) ){ /* Out of memory */ if( pCtx ){ ph7_result_bool(pCtx,0); } goto Cleanup; } if( bTrueReturn ){ /* Assume a boolean true return value */ PH7_MemObjInitFromBool(pVm,&sResult,1); }else{ /* Assume a null return value */ PH7_MemObjInit(pVm,&sResult); } /* Execute the compiled chunk */ VmLocalExec(pVm,&aByteCode,&sResult); if( pCtx ){ /* Set the execution result */ ph7_result_value(pCtx,&sResult); } PH7_MemObjRelease(&sResult); } Cleanup: /* Cleanup the mess left behind */ pVm->pByteContainer = pByteCode; SySetRelease(&aByteCode); return SXRET_OK; } /* * value eval(string $code) * Evaluate a string as PHP code. * Parameter * code: PHP code to evaluate. * Return * eval() returns NULL unless return is called in the evaluated code, in which case * the value passed to return is returned. If there is a parse error in the evaluated * code, eval() returns FALSE and execution of the following code continues normally. */ static int vm_builtin_eval(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sChunk; /* Chunk to evaluate */ if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Chunk to evaluate */ sChunk.zString = ph7_value_to_string(apArg[0],(int *)&sChunk.nByte); if( sChunk.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Eval the chunk */ VmEvalChunk(pCtx->pVm,&(*pCtx),&sChunk,PH7_PHP_ONLY,FALSE); return SXRET_OK; } /* * Check if a file path is already included. */ static int VmIsIncludedFile(ph7_vm *pVm,SyString *pFile) { SyString *aEntries; sxu32 n; aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded); /* Perform a linear search */ for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){ if( SyStringCmp(pFile,&aEntries[n],SyMemcmp) == 0 ){ /* Already included */ return TRUE; } } return FALSE; } /* * Push a file path in the appropriate VM container. */ PH7_PRIVATE sxi32 PH7_VmPushFilePath(ph7_vm *pVm,const char *zPath,int nLen,sxu8 bMain,sxi32 *pNew) { SyString sPath; char *zDup; #ifdef __WINNT__ char *zCur; #endif sxi32 rc; if( nLen < 0 ){ nLen = SyStrlen(zPath); } /* Duplicate the file path first */ zDup = SyMemBackendStrDup(&pVm->sAllocator,zPath,nLen); if( zDup == 0 ){ return SXERR_MEM; } #ifdef __WINNT__ /* Normalize path on windows * Example: * Path/To/File.php * becomes * path\to\file.php */ zCur = zDup; while( zCur[0] != 0 ){ if( zCur[0] == '/' ){ zCur[0] = '\\'; }else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){ int c = SyToLower(zCur[0]); zCur[0] = (char)c; /* MSVC stupidity */ } zCur++; } #endif /* Install the file path */ SyStringInitFromBuf(&sPath,zDup,nLen); if( !bMain ){ if( VmIsIncludedFile(&(*pVm),&sPath) ){ /* Already included */ *pNew = 0; }else{ /* Insert in the corresponding container */ rc = SySetPut(&pVm->aIncluded,(const void *)&sPath); if( rc != SXRET_OK ){ SyMemBackendFree(&pVm->sAllocator,zDup); return rc; } *pNew = 1; } } SySetPut(&pVm->aFiles,(const void *)&sPath); return SXRET_OK; } /* * Compile and Execute a PHP script at run-time. * SXRET_OK is returned on sucessful evaluation.Any other return values * indicates failure. * Note that the PHP script to evaluate can be a local or remote file.In * either cases the [PH7_StreamReadWholeFile()] function handle all the underlying * operations. * If the [PH7_DISABLE_BUILTIN_FUNC] compile-time directive is defined,then * this function is a no-op. * Refer to the implementation of the include(),include_once() language * constructs for more information. */ static sxi32 VmExecIncludedFile( ph7_context *pCtx, /* Call Context */ SyString *pPath, /* Script path or URL*/ int IncludeOnce /* TRUE if called from include_once() or require_once() */ ) { sxi32 rc; #ifndef PH7_DISABLE_BUILTIN_FUNC const ph7_io_stream *pStream; SyBlob sContents; void *pHandle; ph7_vm *pVm; int isNew; /* Initialize fields */ pVm = pCtx->pVm; SyBlobInit(&sContents,&pVm->sAllocator); isNew = 0; /* Extract the associated stream */ pStream = PH7_VmGetStreamDevice(pVm,&pPath->zString,pPath->nByte); /* * Open the file or the URL [i.e: http://ph7.symisc.net/example/hello.php"] * in a read-only mode. */ pHandle = PH7_StreamOpenHandle(pVm,pStream,pPath->zString,PH7_IO_OPEN_RDONLY,TRUE,0,TRUE,&isNew); if( pHandle == 0 ){ return SXERR_IO; } rc = SXRET_OK; /* Stupid cc warning */ if( IncludeOnce && !isNew ){ /* Already included */ rc = SXERR_EXISTS; }else{ /* Read the whole file contents */ rc = PH7_StreamReadWholeFile(pHandle,pStream,&sContents); if( rc == SXRET_OK ){ SyString sScript; /* Compile and execute the script */ SyStringInitFromBuf(&sScript,SyBlobData(&sContents),SyBlobLength(&sContents)); VmEvalChunk(pCtx->pVm,&(*pCtx),&sScript,0,TRUE); } } /* Pop from the set of included file */ (void)SySetPop(&pVm->aFiles); /* Close the handle */ PH7_StreamCloseHandle(pStream,pHandle); /* Release the working buffer */ SyBlobRelease(&sContents); #else pCtx = 0; /* cc warning */ pPath = 0; IncludeOnce = 0; rc = SXERR_IO; #endif /* PH7_DISABLE_BUILTIN_FUNC */ return rc; } /* * string get_include_path(void) * Gets the current include_path configuration option. * Parameter * None * Return * Included paths as a string */ static int vm_builtin_get_include_path(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; SyString *aEntry; int dir_sep; sxu32 n; #ifdef __WINNT__ dir_sep = ';'; #else /* Assume UNIX path separator */ dir_sep = ':'; #endif SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Point to the list of import paths */ aEntry = (SyString *)SySetBasePtr(&pVm->aPaths); for( n = 0 ; n < SySetUsed(&pVm->aPaths) ; n++ ){ SyString *pEntry = &aEntry[n]; if( n > 0 ){ /* Append dir seprator */ ph7_result_string(pCtx,(const char *)&dir_sep,sizeof(char)); } /* Append path */ ph7_result_string(pCtx,pEntry->zString,(int)pEntry->nByte); } return PH7_OK; } /* * string get_get_included_files(void) * Gets the current include_path configuration option. * Parameter * None * Return * Included paths as a string */ static int vm_builtin_get_included_files(ph7_context *pCtx,int nArg,ph7_value **apArg) { SySet *pFiles = &pCtx->pVm->aFiles; ph7_value *pArray,*pWorker; SyString *pEntry; int c,d; /* Create an array and a working value */ pArray = ph7_context_new_array(pCtx); pWorker = ph7_context_new_scalar(pCtx); if( pArray == 0 || pWorker == 0 ){ /* Out of memory,return null */ ph7_result_null(pCtx); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } c = d = '/'; #ifdef __WINNT__ d = '\\'; #endif /* Iterate throw entries */ SySetResetCursor(pFiles); while( SXRET_OK == SySetGetNextEntry(pFiles,(void **)&pEntry) ){ const char *zBase,*zEnd; int iLen; /* reset the string cursor */ ph7_value_reset_string_cursor(pWorker); /* Extract base name */ zEnd = &pEntry->zString[pEntry->nByte - 1]; /* Ignore trailing '/' */ while( zEnd > pEntry->zString && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){ zEnd--; } iLen = (int)(&zEnd[1]-pEntry->zString); while( zEnd > pEntry->zString && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ zEnd--; } zBase = (zEnd > pEntry->zString) ? &zEnd[1] : pEntry->zString; zEnd = &pEntry->zString[iLen]; /* Copy entry name */ ph7_value_string(pWorker,zBase,(int)(zEnd-zBase)); /* Perform the insertion */ ph7_array_add_elem(pArray,0/* Automatic index assign*/,pWorker); /* Will make it's own copy */ } /* All done,return the created array */ ph7_result_value(pCtx,pArray); /* Note that 'pWorker' will be automatically destroyed * by the engine as soon we return from this foreign * function. */ return PH7_OK; } /* * include: * According to the PHP reference manual. * The include() function includes and evaluates the specified file. * Files are included based on the file path given or, if none is given * the include_path specified.If the file isn't found in the include_path * include() will finally check in the calling script's own directory * and the current working directory before failing. The include() * construct will emit a warning if it cannot find a file; this is different * behavior from require(), which will emit a fatal error. * If a path is defined � whether absolute (starting with a drive letter * or \ on Windows, or / on Unix/Linux systems) or relative to the current * directory (starting with . or ..) � the include_path will be ignored altogether. * For example, if a filename begins with ../, the parser will look in the parent * directory to find the requested file. * When a file is included, the code it contains inherits the variable scope * of the line on which the include occurs. Any variables available at that line * in the calling file will be available within the called file, from that point forward. * However, all functions and classes defined in the included file have the global scope. */ static int vm_builtin_include(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sFile; sxi32 rc; if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* File to include */ sFile.zString = ph7_value_to_string(apArg[0],(int *)&sFile.nByte); if( sFile.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Open,compile and execute the desired script */ rc = VmExecIncludedFile(&(*pCtx),&sFile,FALSE); if( rc != SXRET_OK ){ /* Emit a warning and return false */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING,"IO error while importing: '%z'",&sFile); ph7_result_bool(pCtx,0); } return SXRET_OK; } /* * include_once: * According to the PHP reference manual. * The include_once() statement includes and evaluates the specified file during * the execution of the script. This is a behavior similar to the include() * statement, with the only difference being that if the code from a file has already * been included, it will not be included again. As the name suggests, it will be included * just once. */ static int vm_builtin_include_once(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sFile; sxi32 rc; if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* File to include */ sFile.zString = ph7_value_to_string(apArg[0],(int *)&sFile.nByte); if( sFile.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Open,compile and execute the desired script */ rc = VmExecIncludedFile(&(*pCtx),&sFile,TRUE); if( rc == SXERR_EXISTS ){ /* File already included,return TRUE */ ph7_result_bool(pCtx,1); return SXRET_OK; } if( rc != SXRET_OK ){ /* Emit a warning and return false */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING,"IO error while importing: '%z'",&sFile); ph7_result_bool(pCtx,0); } return SXRET_OK; } /* * require. * According to the PHP reference manual. * require() is identical to include() except upon failure it will * also produce a fatal level error. * In other words, it will halt the script whereas include() only * emits a warning which allows the script to continue. */ static int vm_builtin_require(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sFile; sxi32 rc; if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* File to include */ sFile.zString = ph7_value_to_string(apArg[0],(int *)&sFile.nByte); if( sFile.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Open,compile and execute the desired script */ rc = VmExecIncludedFile(&(*pCtx),&sFile,FALSE); if( rc != SXRET_OK ){ /* Fatal,abort VM execution immediately */ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"Fatal IO error while importing: '%z'",&sFile); ph7_result_bool(pCtx,0); return PH7_ABORT; } return SXRET_OK; } /* * require_once: * According to the PHP reference manual. * The require_once() statement is identical to require() except PHP will check * if the file has already been included, and if so, not include (require) it again. * See the include_once() documentation for information about the _once behaviour * and how it differs from its non _once siblings. */ static int vm_builtin_require_once(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sFile; sxi32 rc; if( nArg < 1 ){ /* Nothing to evaluate,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* File to include */ sFile.zString = ph7_value_to_string(apArg[0],(int *)&sFile.nByte); if( sFile.nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return SXRET_OK; } /* Open,compile and execute the desired script */ rc = VmExecIncludedFile(&(*pCtx),&sFile,TRUE); if( rc == SXERR_EXISTS ){ /* File already included,return TRUE */ ph7_result_bool(pCtx,1); return SXRET_OK; } if( rc != SXRET_OK ){ /* Fatal,abort VM execution immediately */ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"Fatal IO error while importing: '%z'",&sFile); ph7_result_bool(pCtx,0); return PH7_ABORT; } return SXRET_OK; } /* * Section: * Command line arguments processing. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * Check if a short option argument [i.e: -c] is available in the command * line string. Return a pointer to the start of the stream on success. * NULL otherwise. */ static const char * VmFindShortOpt(int c,const char *zIn,const char *zEnd) { while( zIn < zEnd ){ if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){ /* Got one */ return &zIn[1]; } /* Advance the cursor */ zIn++; } /* No such option */ return 0; } /* * Check if a long option argument [i.e: --opt] is available in the command * line string. Return a pointer to the start of the stream on success. * NULL otherwise. */ static const char * VmFindLongOpt(const char *zLong,int nByte,const char *zIn,const char *zEnd) { const char *zOpt; while( zIn < zEnd ){ if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){ zIn += 2; zOpt = zIn; while( zIn < zEnd && !SyisSpace(zIn[0]) ){ if( zIn[0] == '=' /* --opt=val */){ break; } zIn++; } /* Test */ if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt,zLong,nByte) == 0 ){ /* Got one,return it's value */ return zIn; } }else{ zIn++; } } /* No such option */ return 0; } /* * Long option [i.e: --opt] arguments private data structure. */ struct getopt_long_opt { const char *zArgIn,*zArgEnd; /* Command line arguments */ ph7_value *pWorker; /* Worker variable*/ ph7_value *pArray; /* getopt() return value */ ph7_context *pCtx; /* Call Context */ }; /* Forward declaration */ static int VmProcessLongOpt(ph7_value *pKey,ph7_value *pValue,void *pUserData); /* * Extract short or long argument option values. */ static void VmExtractOptArgValue( ph7_value *pArray, /* getopt() return value */ ph7_value *pWorker, /* Worker variable */ const char *zArg, /* Argument stream */ const char *zArgEnd,/* End of the argument stream */ int need_val, /* TRUE to fetch option argument */ ph7_context *pCtx, /* Call Context */ const char *zName /* Option name */) { ph7_value_bool(pWorker,0); if( !need_val ){ /* * Option does not need arguments. * Insert the option name and a boolean FALSE. */ ph7_array_add_strkey_elem(pArray,(const char *)zName,pWorker); /* Will make it's own copy */ }else{ const char *zCur; /* Extract option argument */ zArg++; if( zArg < zArgEnd && zArg[0] == '=' ){ zArg++; } while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ zArg++; } if( zArg >= zArgEnd || zArg[0] == '-' ){ /* * Argument not found. * Insert the option name and a boolean FALSE. */ ph7_array_add_strkey_elem(pArray,(const char *)zName,pWorker); /* Will make it's own copy */ return; } /* Delimit the value */ zCur = zArg; if( zArg[0] == '\'' || zArg[0] == '"' ){ int d = zArg[0]; /* Delimt the argument */ zArg++; zCur = zArg; while( zArg < zArgEnd ){ if( zArg[0] == d && zArg[-1] != '\\' ){ /* Delimiter found,exit the loop */ break; } zArg++; } /* Save the value */ ph7_value_string(pWorker,zCur,(int)(zArg-zCur)); if( zArg < zArgEnd ){ zArg++; } }else{ while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){ zArg++; } /* Save the value */ ph7_value_string(pWorker,zCur,(int)(zArg-zCur)); } /* * Check if we are dealing with multiple values. * If so,create an array to hold them,rather than a scalar variable. */ while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ zArg++; } if( zArg < zArgEnd && zArg[0] != '-' ){ ph7_value *pOptArg; /* Array of option arguments */ pOptArg = ph7_context_new_array(pCtx); if( pOptArg == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); }else{ /* Insert the first value */ ph7_array_add_elem(pOptArg,0,pWorker); /* Will make it's own copy */ for(;;){ if( zArg >= zArgEnd || zArg[0] == '-' ){ /* No more value */ break; } /* Delimit the value */ zCur = zArg; if( zArg < zArgEnd && zArg[0] == '\\' ){ zArg++; zCur = zArg; } while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){ zArg++; } /* Reset the string cursor */ ph7_value_reset_string_cursor(pWorker); /* Save the value */ ph7_value_string(pWorker,zCur,(int)(zArg-zCur)); /* Insert */ ph7_array_add_elem(pOptArg,0,pWorker); /* Will make it's own copy */ /* Jump trailing white spaces */ while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){ zArg++; } } /* Insert the option arg array */ ph7_array_add_strkey_elem(pArray,(const char *)zName,pOptArg); /* Will make it's own copy */ /* Safely release */ ph7_context_release_value(pCtx,pOptArg); } }else{ /* Single value */ ph7_array_add_strkey_elem(pArray,(const char *)zName,pWorker); /* Will make it's own copy */ } } } /* * array getopt(string $options[,array $longopts ]) * Gets options from the command line argument list. * Parameters * $options * Each character in this string will be used as option characters * and matched against options passed to the script starting with * a single hyphen (-). For example, an option string "x" recognizes * an option -x. Only a-z, A-Z and 0-9 are allowed. * $longopts * An array of options. Each element in this array will be used as option * strings and matched against options passed to the script starting with * two hyphens (--). For example, an longopts element "opt" recognizes an * option --opt. * Return * This function will return an array of option / argument pairs or FALSE * on failure. */ static int vm_builtin_getopt(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn,*zEnd,*zArg,*zArgIn,*zArgEnd; struct getopt_long_opt sLong; ph7_value *pArray,*pWorker; SyBlob *pArg; int nByte; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Missing/Invalid option arguments"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract option arguments */ zIn = ph7_value_to_string(apArg[0],&nByte); zEnd = &zIn[nByte]; /* Point to the string representation of the $argv[] array */ pArg = &pCtx->pVm->sArgv; /* Create a new empty array and a worker variable */ pArray = ph7_context_new_array(pCtx); pWorker = ph7_context_new_scalar(pCtx); if( pArray == 0 || pWorker == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } if( SyBlobLength(pArg) < 1 ){ /* Empty command line,return the empty array*/ ph7_result_value(pCtx,pArray); /* Everything will be released automatically when we return * from this function. */ return PH7_OK; } zArgIn = (const char *)SyBlobData(pArg); zArgEnd = &zArgIn[SyBlobLength(pArg)]; /* Fill the long option structure */ sLong.pArray = pArray; sLong.pWorker = pWorker; sLong.zArgIn = zArgIn; sLong.zArgEnd = zArgEnd; sLong.pCtx = pCtx; /* Start processing */ while( zIn < zEnd ){ int c = zIn[0]; int need_val = 0; /* Advance the stream cursor */ zIn++; /* Ignore non-alphanum characters */ if( !SyisAlphaNum(c) ){ continue; } if( zIn < zEnd && zIn[0] == ':' ){ zIn++; need_val = 1; if( zIn < zEnd && zIn[0] == ':' ){ zIn++; } } /* Find option */ zArg = VmFindShortOpt(c,zArgIn,zArgEnd); if( zArg == 0 ){ /* No such option */ continue; } /* Extract option argument value */ VmExtractOptArgValue(pArray,pWorker,zArg,zArgEnd,need_val,pCtx,(const char *)&c); } if( nArg > 1 && ph7_value_is_array(apArg[1]) && ph7_array_count(apArg[1]) > 0 ){ /* Process long options */ ph7_array_walk(apArg[1],VmProcessLongOpt,&sLong); } /* Return the option array */ ph7_result_value(pCtx,pArray); /* * Don't worry about freeing memory, everything will be released * automatically as soon we return from this foreign function. */ return PH7_OK; } /* * Array walker callback used for processing long options values. */ static int VmProcessLongOpt(ph7_value *pKey,ph7_value *pValue,void *pUserData) { struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData; const char *zArg,*zOpt,*zEnd; int need_value = 0; int nByte; /* Value must be of type string */ if( !ph7_value_is_string(pValue) ){ /* Simply ignore */ return PH7_OK; } zOpt = ph7_value_to_string(pValue,&nByte); if( nByte < 1 ){ /* Empty string,ignore */ return PH7_OK; } zEnd = &zOpt[nByte - 1]; if( zEnd[0] == ':' ){ char *zTerm; /* Try to extract a value */ need_value = 1; while( zEnd >= zOpt && zEnd[0] == ':' ){ zEnd--; } if( zOpt >= zEnd ){ /* Empty string,ignore */ SXUNUSED(pKey); return PH7_OK; } zEnd++; zTerm = (char *)zEnd; zTerm[0] = 0; }else{ zEnd = &zOpt[nByte]; } /* Find the option */ zArg = VmFindLongOpt(zOpt,(int)(zEnd-zOpt),pOpt->zArgIn,pOpt->zArgEnd); if( zArg == 0 ){ /* No such option,return immediately */ return PH7_OK; } /* Try to extract a value */ VmExtractOptArgValue(pOpt->pArray,pOpt->pWorker,zArg,pOpt->zArgEnd,need_value,pOpt->pCtx,zOpt); return PH7_OK; } /* * Section: * JSON encoding/decoding routines. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Devel. */ /* Forward reference */ static int VmJsonArrayEncode(ph7_value *pKey,ph7_value *pValue,void *pUserData); static int VmJsonObjectEncode(const char *zAttr,ph7_value *pValue,void *pUserData); /* * JSON encoder state is stored in an instance * of the following structure. */ typedef struct json_private_data json_private_data; struct json_private_data { ph7_context *pCtx; /* Call context */ int isFirst; /* True if first encoded entry */ int iFlags; /* JSON encoding flags */ int nRecCount; /* Recursion count */ }; /* * Returns the JSON representation of a value.In other word perform a JSON encoding operation. * According to wikipedia * JSON's basic types are: * Number (double precision floating-point format in JavaScript, generally depends on implementation) * String (double-quoted Unicode, with backslash escaping) * Boolean (true or false) * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values * do not need to be of the same type) * Object (an unordered collection of key:value pairs with the ':' character separating the key * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should * be distinct from each other) * null (empty) * Non-significant white space may be added freely around the "structural characters" * (i.e. the brackets "[{]}", colon ":" and comma ","). */ static sxi32 VmJsonEncode( ph7_value *pIn, /* Encode this value */ json_private_data *pData /* Context data */ ){ ph7_context *pCtx = pData->pCtx; int iFlags = pData->iFlags; int nByte; if( ph7_value_is_null(pIn) || ph7_value_is_resource(pIn)){ /* null */ ph7_result_string(pCtx,"null",(int)sizeof("null")-1); }else if( ph7_value_is_bool(pIn) ){ int iBool = ph7_value_to_bool(pIn); int iLen; /* true/false */ iLen = iBool ? (int)sizeof("true") : (int)sizeof("false"); ph7_result_string(pCtx,iBool ? "true" : "false",iLen-1); }else if( ph7_value_is_numeric(pIn) && !ph7_value_is_string(pIn) ){ const char *zNum; /* Get a string representation of the number */ zNum = ph7_value_to_string(pIn,&nByte); ph7_result_string(pCtx,zNum,nByte); }else if( ph7_value_is_string(pIn) ){ if( (iFlags & JSON_NUMERIC_CHECK) && ph7_value_is_numeric(pIn) ){ const char *zNum; /* Encodes numeric strings as numbers. */ PH7_MemObjToReal(pIn); /* Force a numeric cast */ /* Get a string representation of the number */ zNum = ph7_value_to_string(pIn,&nByte); ph7_result_string(pCtx,zNum,nByte); }else{ const char *zIn,*zEnd; int c; /* Encode the string */ zIn = ph7_value_to_string(pIn,&nByte); zEnd = &zIn[nByte]; /* Append the double quote */ ph7_result_string(pCtx,"\"",(int)sizeof(char)); for(;;){ if( zIn >= zEnd ){ /* No more input to process */ break; } c = zIn[0]; /* Advance the stream cursor */ zIn++; if( (c == '<' || c == '>') && (iFlags & JSON_HEX_TAG) ){ /* All < and > are converted to \u003C and \u003E */ if( c == '<' ){ ph7_result_string(pCtx,"\\u003C",(int)sizeof("\\u003C")-1); }else{ ph7_result_string(pCtx,"\\u003E",(int)sizeof("\\u003E")-1); } continue; }else if( c == '&' && (iFlags & JSON_HEX_AMP) ){ /* All &s are converted to \u0026. */ ph7_result_string(pCtx,"\\u0026",(int)sizeof("\\u0026")-1); continue; }else if( c == '\'' && (iFlags & JSON_HEX_APOS) ){ /* All ' are converted to \u0027. */ ph7_result_string(pCtx,"\\u0027",(int)sizeof("\\u0027")-1); continue; }else if( c == '"' && (iFlags & JSON_HEX_QUOT) ){ /* All " are converted to \u0022. */ ph7_result_string(pCtx,"\\u0022",(int)sizeof("\\u0022")-1); continue; } if( c == '"' || (c == '\\' && ((iFlags & JSON_UNESCAPED_SLASHES)==0)) ){ /* Unescape the character */ ph7_result_string(pCtx,"\\",(int)sizeof(char)); } /* Append character verbatim */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); } /* Append the double quote */ ph7_result_string(pCtx,"\"",(int)sizeof(char)); } }else if( ph7_value_is_array(pIn) ){ int c = '[',d = ']'; /* Encode the array */ pData->isFirst = 1; if( iFlags & JSON_FORCE_OBJECT ){ /* Outputs an object rather than an array */ c = '{'; d = '}'; } /* Append the square bracket or curly braces */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); /* Iterate throw array entries */ ph7_array_walk(pIn,VmJsonArrayEncode,pData); /* Append the closing square bracket or curly braces */ ph7_result_string(pCtx,(const char *)&d,(int)sizeof(char)); }else if( ph7_value_is_object(pIn) ){ /* Encode the class instance */ pData->isFirst = 1; /* Append the curly braces */ ph7_result_string(pCtx,"{",(int)sizeof(char)); /* Iterate throw class attribute */ ph7_object_walk(pIn,VmJsonObjectEncode,pData); /* Append the closing curly braces */ ph7_result_string(pCtx,"}",(int)sizeof(char)); }else{ /* Can't happen */ ph7_result_string(pCtx,"null",(int)sizeof("null")-1); } /* All done */ return PH7_OK; } /* * The following walker callback is invoked each time we need * to encode an array to JSON. */ static int VmJsonArrayEncode(ph7_value *pKey,ph7_value *pValue,void *pUserData) { json_private_data *pJson = (json_private_data *)pUserData; if( pJson->nRecCount > 31 ){ /* Recursion limit reached,return immediately */ return PH7_OK; } if( !pJson->isFirst ){ /* Append the colon first */ ph7_result_string(pJson->pCtx,",",(int)sizeof(char)); } if( pJson->iFlags & JSON_FORCE_OBJECT ){ /* Outputs an object rather than an array */ const char *zKey; int nByte; /* Extract a string representation of the key */ zKey = ph7_value_to_string(pKey,&nByte); /* Append the key and the double colon */ ph7_result_string_format(pJson->pCtx,"\"%.*s\":",nByte,zKey); } /* Encode the value */ pJson->nRecCount++; VmJsonEncode(pValue,pJson); pJson->nRecCount--; pJson->isFirst = 0; return PH7_OK; } /* * The following walker callback is invoked each time we need to encode * a class instance [i.e: Object in the PHP jargon] to JSON. */ static int VmJsonObjectEncode(const char *zAttr,ph7_value *pValue,void *pUserData) { json_private_data *pJson = (json_private_data *)pUserData; if( pJson->nRecCount > 31 ){ /* Recursion limit reached,return immediately */ return PH7_OK; } if( !pJson->isFirst ){ /* Append the colon first */ ph7_result_string(pJson->pCtx,",",(int)sizeof(char)); } /* Append the attribute name and the double colon first */ ph7_result_string_format(pJson->pCtx,"\"%s\":",zAttr); /* Encode the value */ pJson->nRecCount++; VmJsonEncode(pValue,pJson); pJson->nRecCount--; pJson->isFirst = 0; return PH7_OK; } /* * string json_encode(mixed $value [, int $options = 0 ]) * Returns a string containing the JSON representation of value. * Parameters * $value * The value being encoded. Can be any type except a resource. * $options * Bitmask consisting of: * JSON_HEX_TAG All < and > are converted to \u003C and \u003E. * JSON_HEX_AMP All &s are converted to \u0026. * JSON_HEX_APOS All ' are converted to \u0027. * JSON_HEX_QUOT All " are converted to \u0022. * JSON_FORCE_OBJECT Outputs an object rather than an array. * JSON_NUMERIC_CHECK Encodes numeric strings as numbers. * JSON_BIGINT_AS_STRING Not used * JSON_PRETTY_PRINT Use whitespace in returned data to format it. * JSON_UNESCAPED_SLASHES Don't escape '/' * JSON_UNESCAPED_UNICODE Not used. * Return * Returns a JSON encoded string on success. FALSE otherwise */ static int vm_builtin_json_encode(ph7_context *pCtx,int nArg,ph7_value **apArg) { json_private_data sJson; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Prepare the JSON data */ sJson.nRecCount = 0; sJson.pCtx = pCtx; sJson.isFirst = 1; sJson.iFlags = 0; if( nArg > 1 && ph7_value_is_int(apArg[1]) ){ /* Extract option flags */ sJson.iFlags = ph7_value_to_int(apArg[1]); } /* Perform the encoding operation */ VmJsonEncode(apArg[0],&sJson); /* All done */ return PH7_OK; } /* * int json_last_error(void) * Returns the last error (if any) occurred during the last JSON encoding/decoding. * Parameters * None * Return * Returns an integer, the value can be one of the following constants: * JSON_ERROR_NONE No error has occurred. * JSON_ERROR_DEPTH The maximum stack depth has been exceeded. * JSON_ERROR_STATE_MISMATCH Invalid or malformed JSON. * JSON_ERROR_CTRL_CHAR Control character error, possibly incorrectly encoded. * JSON_ERROR_SYNTAX Syntax error. * JSON_ERROR_UTF8_CHECK Malformed UTF-8 characters. */ static int vm_builtin_json_last_error(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; /* Return the error code */ ph7_result_int(pCtx,pVm->json_rc); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Possible tokens from the JSON tokenization process */ #define JSON_TK_TRUE 0x001 /* Boolean true */ #define JSON_TK_FALSE 0x002 /* Boolean false */ #define JSON_TK_STR 0x004 /* String enclosed in double quotes */ #define JSON_TK_NULL 0x008 /* null */ #define JSON_TK_NUM 0x010 /* Numeric */ #define JSON_TK_OCB 0x020 /* Open curly braces '{' */ #define JSON_TK_CCB 0x040 /* Closing curly braces '}' */ #define JSON_TK_OSB 0x080 /* Open square bracke '[' */ #define JSON_TK_CSB 0x100 /* Closing square bracket ']' */ #define JSON_TK_COLON 0x200 /* Single colon ':' */ #define JSON_TK_COMMA 0x400 /* Single comma ',' */ #define JSON_TK_INVALID 0x800 /* Unexpected token */ /* * Tokenize an entire JSON input. * Get a single low-level token from the input file. * Update the stream pointer so that it points to the first * character beyond the extracted token. */ static sxi32 VmJsonTokenize(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData) { int *pJsonErr = (int *)pUserData; SyString *pStr; int c; /* Ignore leading white spaces */ while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){ /* Advance the stream cursor */ if( pStream->zText[0] == '\n' ){ /* Update line counter */ pStream->nLine++; } pStream->zText++; } if( pStream->zText >= pStream->zEnd ){ /* End of input reached */ SXUNUSED(pCtxData); /* cc warning */ return SXERR_EOF; } /* Record token starting position and line */ pToken->nLine = pStream->nLine; pToken->pUserData = 0; pStr = &pToken->sData; SyStringInitFromBuf(pStr,pStream->zText,0); if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']' || pStream->zText[0] == ':' || pStream->zText[0] == ',' ){ /* Single character */ c = pStream->zText[0]; /* Set token type */ switch(c){ case '[': pToken->nType = JSON_TK_OSB; break; case '{': pToken->nType = JSON_TK_OCB; break; case '}': pToken->nType = JSON_TK_CCB; break; case ']': pToken->nType = JSON_TK_CSB; break; case ':': pToken->nType = JSON_TK_COLON; break; case ',': pToken->nType = JSON_TK_COMMA; break; default: break; } /* Advance the stream cursor */ pStream->zText++; }else if( pStream->zText[0] == '"') { /* JSON string */ pStream->zText++; pStr->zString++; /* Delimit the string */ while( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){ break; } if( pStream->zText[0] == '\n' ){ /* Update line counter */ pStream->nLine++; } pStream->zText++; } if( pStream->zText >= pStream->zEnd ){ /* Missing closing '"' */ pToken->nType = JSON_TK_INVALID; *pJsonErr = JSON_ERROR_SYNTAX; }else{ pToken->nType = JSON_TK_STR; pStream->zText++; /* Jump the closing double quotes */ } }else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ /* Number */ pStream->zText++; pToken->nType = JSON_TK_NUM; while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c == '.' ){ /* Real number */ pStream->zText++; while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c=='e' || c=='E' ){ pStream->zText++; if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c =='+' || c=='-' ){ pStream->zText++; } while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } } } } }else if( c=='e' || c=='E' ){ /* Real number */ pStream->zText++; if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c =='+' || c=='-' ){ pStream->zText++; } while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } } } } }else if( XLEX_IN_LEN(pStream) >= sizeof("true") -1 && SyStrnicmp((const char *)pStream->zText,"true",sizeof("true")-1) == 0 ){ /* boolean true */ pToken->nType = JSON_TK_TRUE; /* Advance the stream cursor */ pStream->zText += sizeof("true")-1; }else if( XLEX_IN_LEN(pStream) >= sizeof("false") -1 && SyStrnicmp((const char *)pStream->zText,"false",sizeof("false")-1) == 0 ){ /* boolean false */ pToken->nType = JSON_TK_FALSE; /* Advance the stream cursor */ pStream->zText += sizeof("false")-1; }else if( XLEX_IN_LEN(pStream) >= sizeof("null") -1 && SyStrnicmp((const char *)pStream->zText,"null",sizeof("null")-1) == 0 ){ /* NULL */ pToken->nType = JSON_TK_NULL; /* Advance the stream cursor */ pStream->zText += sizeof("null")-1; }else{ /* Unexpected token */ pToken->nType = JSON_TK_INVALID; /* Advance the stream cursor */ pStream->zText++; *pJsonErr = JSON_ERROR_SYNTAX; /* Abort processing immediatley */ return SXERR_ABORT; } /* record token length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); if( pToken->nType == JSON_TK_STR ){ pStr->nByte--; } /* Return to the lexer */ return SXRET_OK; } /* * JSON decoded input consumer callback signature. */ typedef int (*ProcJsonConsumer)(ph7_context *,ph7_value *,ph7_value *,void *); /* * JSON decoder state is kept in the following structure. */ typedef struct json_decoder json_decoder; struct json_decoder { ph7_context *pCtx; /* Call context */ ProcJsonConsumer xConsumer; /* Consumer callback */ void *pUserData; /* Last argument to xConsumer() */ int iFlags; /* Configuration flags */ SyToken *pIn; /* Token stream */ SyToken *pEnd; /* End of the token stream */ int rec_depth; /* Recursion limit */ int rec_count; /* Current nesting level */ int *pErr; /* JSON decoding error if any */ }; #define JSON_DECODE_ASSOC 0x01 /* Decode a JSON object as an associative array */ /* Forward declaration */ static int VmJsonArrayDecoder(ph7_context *pCtx,ph7_value *pKey,ph7_value *pWorker,void *pUserData); /* * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store * the result in the given ph7_value. */ static void VmJsonDequoteString(const SyString *pStr,ph7_value *pWorker) { const char *zIn = pStr->zString; const char *zEnd = &pStr->zString[pStr->nByte]; const char *zCur; int c; /* Mark the value as a string */ ph7_value_string(pWorker,"",0); /* Empty string */ for(;;){ zCur = zIn; while( zIn < zEnd && zIn[0] != '\\' ){ zIn++; } if( zIn > zCur ){ /* Append chunk verbatim */ ph7_value_string(pWorker,zCur,(int)(zIn-zCur)); } zIn++; if( zIn >= zEnd ){ /* End of the input reached */ break; } c = zIn[0]; /* Unescape the character */ switch(c){ case '"': ph7_value_string(pWorker,(const char *)&c,(int)sizeof(char)); break; case '\\': ph7_value_string(pWorker,(const char *)&c,(int)sizeof(char)); break; case 'n': ph7_value_string(pWorker,"\n",(int)sizeof(char)); break; case 'r': ph7_value_string(pWorker,"\r",(int)sizeof(char)); break; case 't': ph7_value_string(pWorker,"\t",(int)sizeof(char)); break; case 'f': ph7_value_string(pWorker,"\f",(int)sizeof(char)); break; default: ph7_value_string(pWorker,(const char *)&c,(int)sizeof(char)); break; } /* Advance the stream cursor */ zIn++; } } /* * Returns a ph7_value holding the image of a JSON string. In other word perform a JSON decoding operation. * According to wikipedia * JSON's basic types are: * Number (double precision floating-point format in JavaScript, generally depends on implementation) * String (double-quoted Unicode, with backslash escaping) * Boolean (true or false) * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values * do not need to be of the same type) * Object (an unordered collection of key:value pairs with the ':' character separating the key * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should * be distinct from each other) * null (empty) * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ","). */ static sxi32 VmJsonDecode( json_decoder *pDecoder, /* JSON decoder */ ph7_value *pArrayKey /* Key for the decoded array */ ){ ph7_value *pWorker; /* Worker variable */ sxi32 rc; /* Check if we do not nest to much */ if( pDecoder->rec_count >= pDecoder->rec_depth ){ /* Nesting limit reached,abort decoding immediately */ *pDecoder->pErr = JSON_ERROR_DEPTH; return SXERR_ABORT; } if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){ /* Scalar value */ pWorker = ph7_context_new_scalar(pDecoder->pCtx); if( pWorker == 0 ){ ph7_context_throw_error(pDecoder->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Reflect the JSON image */ if( pDecoder->pIn->nType & JSON_TK_NULL ){ /* Nullify the value.*/ ph7_value_null(pWorker); }else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){ /* Boolean value */ ph7_value_bool(pWorker,(pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 ); }else if( pDecoder->pIn->nType & JSON_TK_NUM ){ SyString *pStr = &pDecoder->pIn->sData; /* * Numeric value. * Get a string representation first then try to get a numeric * value. */ ph7_value_string(pWorker,pStr->zString,(int)pStr->nByte); /* Obtain a numeric representation */ PH7_MemObjToNumeric(pWorker); }else{ /* Dequote the string */ VmJsonDequoteString(&pDecoder->pIn->sData,pWorker); } /* Invoke the consumer callback */ rc = pDecoder->xConsumer(pDecoder->pCtx,pArrayKey,pWorker,pDecoder->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* All done,advance the stream cursor */ pDecoder->pIn++; }else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) { ProcJsonConsumer xOld; void *pOld; /* Array representation*/ pDecoder->pIn++; /* Create a working array */ pWorker = ph7_context_new_array(pDecoder->pCtx); if( pWorker == 0 ){ ph7_context_throw_error(pDecoder->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Save the old consumer */ xOld = pDecoder->xConsumer; pOld = pDecoder->pUserData; /* Set the new consumer */ pDecoder->xConsumer = VmJsonArrayDecoder; pDecoder->pUserData = pWorker; /* Decode the array */ for(;;){ /* Jump trailing comma. Note that the standard PHP engine will not let you * do this. */ while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){ pDecoder->pIn++; } if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){ if( pDecoder->pIn < pDecoder->pEnd ){ pDecoder->pIn++; /* Jump the trailing ']' */ } break; } /* Recurse and decode the entry */ pDecoder->rec_count++; rc = VmJsonDecode(pDecoder,0); pDecoder->rec_count--; if( rc == SXERR_ABORT ){ /* Abort processing immediately */ return SXERR_ABORT; } /*The cursor is automatically advanced by the VmJsonDecode() function */ if( (pDecoder->pIn < pDecoder->pEnd) && ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){ /* Unexpected token,abort immediatley */ *pDecoder->pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } } /* Restore the old consumer */ pDecoder->xConsumer = xOld; pDecoder->pUserData = pOld; /* Invoke the old consumer on the decoded array */ xOld(pDecoder->pCtx,pArrayKey,pWorker,pOld); }else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) { ProcJsonConsumer xOld; ph7_value *pKey; void *pOld; /* Object representation*/ pDecoder->pIn++; /* Return the object as an associative array */ if( (pDecoder->iFlags & JSON_DECODE_ASSOC) == 0 ){ ph7_context_throw_error(pDecoder->pCtx,PH7_CTX_WARNING, "JSON Objects are always returned as an associative array" ); } /* Create a working array */ pWorker = ph7_context_new_array(pDecoder->pCtx); pKey = ph7_context_new_scalar(pDecoder->pCtx); if( pWorker == 0 || pKey == 0){ ph7_context_throw_error(pDecoder->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Abort the decoding operation immediately */ return SXERR_ABORT; } /* Save the old consumer */ xOld = pDecoder->xConsumer; pOld = pDecoder->pUserData; /* Set the new consumer */ pDecoder->xConsumer = VmJsonArrayDecoder; pDecoder->pUserData = pWorker; /* Decode the object */ for(;;){ /* Jump trailing comma. Note that the standard PHP engine will not let you * do this. */ while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){ pDecoder->pIn++; } if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){ if( pDecoder->pIn < pDecoder->pEnd ){ pDecoder->pIn++; /* Jump the trailing ']' */ } break; } if( (pDecoder->pIn->nType & JSON_TK_STR) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){ /* Syntax error,return immediately */ *pDecoder->pErr = JSON_ERROR_SYNTAX; return SXERR_ABORT; } /* Dequote the key */ VmJsonDequoteString(&pDecoder->pIn->sData,pKey); /* Jump the key and the colon */ pDecoder->pIn += 2; /* Recurse and decode the value */ pDecoder->rec_count++; rc = VmJsonDecode(pDecoder,pKey); pDecoder->rec_count--; if( rc == SXERR_ABORT ){ /* Abort processing immediately */ return SXERR_ABORT; } /* Reset the internal buffer of the key */ ph7_value_reset_string_cursor(pKey); /*The cursor is automatically advanced by the VmJsonDecode() function */ } /* Restore the old consumer */ pDecoder->xConsumer = xOld; pDecoder->pUserData = pOld; /* Invoke the old consumer on the decoded object*/ xOld(pDecoder->pCtx,pArrayKey,pWorker,pOld); /* Release the key */ ph7_context_release_value(pDecoder->pCtx,pKey); }else{ /* Unexpected token */ return SXERR_ABORT; /* Abort immediately */ } /* Release the worker variable */ ph7_context_release_value(pDecoder->pCtx,pWorker); return SXRET_OK; } /* * The following JSON decoder callback is invoked each time * a JSON array representation [i.e: [15,"hello",FALSE] ] * is being decoded. */ static int VmJsonArrayDecoder(ph7_context *pCtx,ph7_value *pKey,ph7_value *pWorker,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; /* Insert the entry */ ph7_array_add_elem(pArray,pKey,pWorker); /* Will make it's own copy */ SXUNUSED(pCtx); /* cc warning */ /* All done */ return SXRET_OK; } /* * Standard JSON decoder callback. */ static int VmJsonDefaultDecoder(ph7_context *pCtx,ph7_value *pKey,ph7_value *pWorker,void *pUserData) { /* Return the value directly */ ph7_result_value(pCtx,pWorker); /* Will make it's own copy */ SXUNUSED(pKey); /* cc warning */ SXUNUSED(pUserData); /* All done */ return SXRET_OK; } /* * mixed json_decode(string $json[,bool $assoc = false[,int $depth = 32[,int $options = 0 ]]]) * Takes a JSON encoded string and converts it into a PHP variable. * Parameters * $json * The json string being decoded. * $assoc * When TRUE, returned objects will be converted into associative arrays. * $depth * User specified recursion depth. * $options * Bitmask of JSON decode options. Currently only JSON_BIGINT_AS_STRING is supported * (default is to cast large integers as floats) * Return * The value encoded in json in appropriate PHP type. Values true, false and null (case-insensitive) * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded * or if the encoded data is deeper than the recursion limit. */ static int vm_builtin_json_decode(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vm *pVm = pCtx->pVm; json_decoder sDecoder; const char *zIn; SySet sToken; SyLex sLex; int nByte; sxi32 rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments, return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the JSON string */ zIn = ph7_value_to_string(apArg[0],&nByte); if( nByte < 1 ){ /* Empty string,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Clear JSON error code */ pVm->json_rc = JSON_ERROR_NONE; /* Tokenize the input */ SySetInit(&sToken,&pVm->sAllocator,sizeof(SyToken)); SyLexInit(&sLex,&sToken,VmJsonTokenize,&pVm->json_rc); SyLexTokenizeInput(&sLex,zIn,(sxu32)nByte,0,0,0); if( pVm->json_rc != JSON_ERROR_NONE ){ /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */ SyLexRelease(&sLex); SySetRelease(&sToken); /* return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the decoder */ sDecoder.pCtx = pCtx; sDecoder.pErr = &pVm->json_rc; sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken); sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)]; sDecoder.iFlags = 0; if( nArg > 1 && ph7_value_to_bool(apArg[1]) != 0 ){ /* Returned objects will be converted into associative arrays */ sDecoder.iFlags |= JSON_DECODE_ASSOC; } sDecoder.rec_depth = 32; if( nArg > 2 && ph7_value_is_int(apArg[2]) ){ int nDepth = ph7_value_to_int(apArg[2]); if( nDepth > 1 && nDepth < 32 ){ sDecoder.rec_depth = nDepth; } } sDecoder.rec_count = 0; /* Set a default consumer */ sDecoder.xConsumer = VmJsonDefaultDecoder; sDecoder.pUserData = 0; /* Decode the raw JSON input */ rc = VmJsonDecode(&sDecoder,0); if( rc == SXERR_ABORT || pVm->json_rc != JSON_ERROR_NONE ){ /* * Something goes wrong while decoding JSON input.Return NULL. */ ph7_result_null(pCtx); } /* Clean-up the mess left behind */ SyLexRelease(&sLex); SySetRelease(&sToken); /* All done */ return PH7_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC /* * XML processing Functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Devel. */ enum ph7_xml_handler_id{ PH7_XML_START_TAG = 0, /* Start element handlers ID */ PH7_XML_END_TAG, /* End element handler ID*/ PH7_XML_CDATA, /* Character data handler ID*/ PH7_XML_PI, /* Processing instruction (PI) handler ID*/ PH7_XML_DEF, /* Default handler ID */ PH7_XML_UNPED, /* Unparsed entity declaration handler */ PH7_XML_ND, /* Notation declaration handler ID*/ PH7_XML_EER, /* External entity reference handler */ PH7_XML_NS_START, /* Start namespace declaration handler */ PH7_XML_NS_END /* End namespace declaration handler */ }; #define XML_TOTAL_HANDLER (PH7_XML_NS_END + 1) /* An instance of the following structure describe a working * XML engine instance. */ typedef struct ph7_xml_engine ph7_xml_engine; struct ph7_xml_engine { ph7_vm *pVm; /* VM that own this instance */ ph7_context *pCtx; /* Call context */ SyXMLParser sParser; /* Underlying XML parser */ ph7_value aCB[XML_TOTAL_HANDLER]; /* User-defined callbacks */ ph7_value sParserValue; /* ph7_value holding this instance which is forwarded * as the first argument to the user callbacks. */ int ns_sep; /* Namespace separator */ SyBlob sErr; /* Error message consumer */ sxi32 iErrCode; /* Last error code */ sxi32 iNest; /* Nesting level */ sxu32 nLine; /* Last processed line */ sxu32 nMagic; /* Magic number so that we avoid misuse */ }; #define XML_ENGINE_MAGIC 0x851EFC52 #define IS_INVALID_XML_ENGINE(XML) (XML == 0 || (XML)->nMagic != XML_ENGINE_MAGIC) /* * Allocate and initialize an XML engine. */ static ph7_xml_engine * VmCreateXMLEngine(ph7_context *pCtx,int process_ns,int ns_sep) { ph7_xml_engine *pEngine; ph7_vm *pVm = pCtx->pVm; ph7_value *pValue; sxu32 n; /* Allocate a new instance */ pEngine = (ph7_xml_engine *)SyMemBackendAlloc(&pVm->sAllocator,sizeof(ph7_xml_engine)); if( pEngine == 0 ){ /* Out of memory */ return 0; } /* Zero the structure */ SyZero(pEngine,sizeof(ph7_xml_engine)); /* Initialize fields */ pEngine->pVm = pVm; pEngine->pCtx = 0; pEngine->ns_sep = ns_sep; SyXMLParserInit(&pEngine->sParser,&pVm->sAllocator,process_ns ? SXML_ENABLE_NAMESPACE : 0); SyBlobInit(&pEngine->sErr,&pVm->sAllocator); PH7_MemObjInit(pVm,&pEngine->sParserValue); for( n = 0 ; n < SX_ARRAYSIZE(pEngine->aCB) ; ++n ){ pValue = &pEngine->aCB[n]; /* NULLIFY the array entries,until someone register an event handler */ PH7_MemObjInit(&(*pVm),pValue); } ph7_value_resource(&pEngine->sParserValue,pEngine); pEngine->iErrCode = SXML_ERROR_NONE; /* Finally set the magic number */ pEngine->nMagic = XML_ENGINE_MAGIC; return pEngine; } /* * Release an XML engine. */ static void VmReleaseXMLEngine(ph7_xml_engine *pEngine) { ph7_vm *pVm = pEngine->pVm; ph7_value *pValue; sxu32 n; /* Release fields */ SyBlobRelease(&pEngine->sErr); SyXMLParserRelease(&pEngine->sParser); PH7_MemObjRelease(&pEngine->sParserValue); for( n = 0 ; n < SX_ARRAYSIZE(pEngine->aCB) ; ++n ){ pValue = &pEngine->aCB[n]; PH7_MemObjRelease(pValue); } pEngine->nMagic = 0x2621; /* Finally,release the whole instance */ SyMemBackendFree(&pVm->sAllocator,pEngine); } /* * resource xml_parser_create([ string $encoding ]) * Create an UTF-8 XML parser. * Parameter * $encoding * (Only UTF-8 encoding is used) * Return * Returns a resource handle for the new XML parser. */ static int vm_builtin_xml_parser_create(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; /* Allocate a new instance */ pEngine = VmCreateXMLEngine(&(*pCtx),0,':'); if( pEngine == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Return null */ ph7_result_null(pCtx); SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); return PH7_OK; } /* Return the engine as a resource */ ph7_result_resource(pCtx,pEngine); return PH7_OK; } /* * resource xml_parser_create_ns([ string $encoding[,string $separator = ':']]) * Create an UTF-8 XML parser with namespace support. * Parameter * $encoding * (Only UTF-8 encoding is supported) * $separtor * Namespace separator (a single character) * Return * Returns a resource handle for the new XML parser. */ static int vm_builtin_xml_parser_create_ns(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; int ns_sep = ':'; if( nArg > 1 && ph7_value_is_string(apArg[1]) ){ const char *zSep = ph7_value_to_string(apArg[1],0); if( zSep[0] != 0 ){ ns_sep = zSep[0]; } } /* Allocate a new instance */ pEngine = VmCreateXMLEngine(&(*pCtx),TRUE,ns_sep); if( pEngine == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Return null */ ph7_result_null(pCtx); return PH7_OK; } /* Return the engine as a resource */ ph7_result_resource(pCtx,pEngine); return PH7_OK; } /* * bool xml_parser_free(resource $parser) * Release an XML engine. * Parameter * $parser * A reference to the XML parser to free. * Return * This function returns FALSE if parser does not refer * to a valid parser, or else it frees the parser and returns TRUE. */ static int vm_builtin_xml_parser_free(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Safely release the engine */ VmReleaseXMLEngine(pEngine); /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_element_handler(resource $parser,callback $start_element_handler,[callback $end_element_handler]) * Sets the element handler functions for the XML parser. start_element_handler and end_element_handler * are strings containing the names of functions. * Parameters * $parser * A reference to the XML parser to set up start and end element handler functions. * $start_element_handler * The function named by start_element_handler must accept three parameters: * start_element_handler(resource $parser,string $name,array $attribs) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $name * The second parameter, name, contains the name of the element for which this handler * is called.If case-folding is in effect for this parser, the element name will be in uppercase letters. * $attribs * The third parameter, attribs, contains an associative array with the element's attributes (if any). * The keys of this array are the attribute names, the values are the attribute values. * Attribute names are case-folded on the same criteria as element names.Attribute values are not case-folded. * The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). * The first key in the array was the first attribute, and so on. * Note: Instead of a function name, an array containing an object reference and a method name can also be supplied. * $end_element_handler * The function named by end_element_handler must accept two parameters: * end_element_handler(resource $parser,string $name) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $name * The second parameter, name, contains the name of the element for which this handler * is called.If case-folding is in effect for this parser, the element name will be in uppercase * letters. * If a handler function is set to an empty string, or FALSE, the handler in question is disabled. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_element_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the start_element_handler callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_START_TAG]); if( nArg > 2 ){ /* Save the end_element_handler callback for later invocation */ PH7_MemObjStore(apArg[2]/* User callback*/,&pEngine->aCB[PH7_XML_END_TAG]); } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_character_data_handler(resource $parser,callback $handler) * Sets the character data handler function for the XML parser parser. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept two parameters: * handler(resource $parser,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $data * The second parameter, data, contains the character data as a string. * Character data handler is called for every piece of a text in the XML document. * It can be called multiple times inside each fragment (e.g. for non-ASCII strings). * If a handler function is set to an empty string, or FALSE, the handler in question is disabled. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_character_data_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_CDATA]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_default_handler(resource $parser,callback $handler) * Set up default handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept two parameters: * handler(resource $parser,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $data * The second parameter, data, contains the character data.This may be the XML declaration * document type declaration, entities or other data for which no other handler exists. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_default_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_DEF]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_end_namespace_decl_handler(resource $parser,callback $handler) * Set up end namespace declaration handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept two parameters: * handler(resource $parser,string $prefix) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $prefix * The prefix is a string used to reference the namespace within an XML object. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_end_namespace_decl_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_NS_END]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_start_namespace_decl_handler(resource $parser,callback $handler) * Set up start namespace declaration handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept two parameters: * handler(resource $parser,string $prefix,string $uri) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $prefix * The prefix is a string used to reference the namespace within an XML object. * $uri * Uniform Resource Identifier (URI) of namespace. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_start_namespace_decl_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_NS_START]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_processing_instruction_handler(resource $parser,callback $handler) * Set up processing instruction (PI) handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept three parameters: * handler(resource $parser,string $target,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $target * The second parameter, target, contains the PI target. * $data The third parameter, data, contains the PI data. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_processing_instruction_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_PI]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_unparsed_entity_decl_handler(resource $parser,callback $handler) * Set up unparsed entity declaration handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept six parameters: * handler(resource $parser,string $entity_name,string $base,string $system_id,string $public_id,string $notation_name) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $entity_name * The name of the entity that is about to be defined. * $base * This is the base for resolving the system identifier (systemId) of the external entity. * Currently this parameter will always be set to an empty string. * $system_id * System identifier for the external entity. * $public_id * Public identifier for the external entity. * $notation_name * Name of the notation of this entity (see xml_set_notation_decl_handler()). * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_unparsed_entity_decl_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_UNPED]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_notation_decl_handler(resource $parser,callback $handler) * Set up notation declaration handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept five parameters: * handler(resource $parser,string $entity_name,string $base,string $system_id,string $public_id) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $entity_name * The name of the entity that is about to be defined. * $base * This is the base for resolving the system identifier (systemId) of the external entity. * Currently this parameter will always be set to an empty string. * $system_id * System identifier for the external entity. * $public_id * Public identifier for the external entity. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_notation_decl_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_ND]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool xml_set_external_entity_ref_handler(resource $parser,callback $handler) * Set up external entity reference handler. * Parameters * $parser * A reference to the XML parser to set up character data handler function. * $handler * handler is a string containing the name of the callback. * The function named by handler must accept five parameters: * handler(resource $parser,string $open_entity_names,string $base,string $system_id,string $public_id) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $open_entity_names * The second parameter, open_entity_names, is a space-separated list of the names * of the entities that are open for the parse of this entity (including the name of the referenced entity). * $base * This is the base for resolving the system identifier (system_id) of the external entity. * Currently this parameter will always be set to an empty string. * $system_id * The fourth parameter, system_id, is the system identifier as specified in the entity declaration. * $public_id * The fifth parameter, public_id, is the public identifier as specified in the entity declaration * or an empty string if none was specified; the whitespace in the public identifier will have been * normalized as required by the XML spec. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. * Return * TRUE on success or FALSE on failure. */ static int vm_builtin_xml_set_external_entity_ref_handler(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ /* Save the user callback for later invocation */ PH7_MemObjStore(apArg[1]/* User callback*/,&pEngine->aCB[PH7_XML_EER]); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * int xml_get_current_line_number(resource $parser) * Gets the current line number for the given XML parser. * Parameters * $parser * A reference to the XML parser. * Return * This function returns FALSE if parser does not refer * to a valid parser, or else it returns which line the parser * is currently at in its data buffer. */ static int vm_builtin_xml_get_current_line_number(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the line number */ ph7_result_int(pCtx,(int)pEngine->nLine); return PH7_OK; } /* * int xml_get_current_byte_index(resource $parser) * Gets the current byte index of the given XML parser. * Parameters * $parser * A reference to the XML parser. * Return * This function returns FALSE if parser does not refer to a valid * parser, or else it returns which byte index the parser is currently * at in its data buffer (starting at 0). */ static int vm_builtin_xml_get_current_byte_index(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; SyStream *pStream; SyToken *pToken; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the current processed token */ pToken = (SyToken *)SySetPeekCurrentEntry(&pEngine->sParser.sToken); if( pToken == 0 ){ /* Stream not yet processed */ ph7_result_int(pCtx,0); return 0; } /* Point to the input stream */ pStream = &pEngine->sParser.sLex.sStream; /* Return the byte index */ ph7_result_int64(pCtx,(ph7_int64)(pToken->sData.zString-(const char *)pStream->zInput)); return PH7_OK; } /* * bool xml_set_object(resource $parser,object &$object) * Use XML Parser within an object. * NOTE * This function is depreceated and is a no-op. * Parameters * $parser * A reference to the XML parser. * $object * The object where to use the XML parser. * Return * Always FALSE. */ static int vm_builtin_xml_set_object(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) || !ph7_value_is_object(apArg[1]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Throw a notice and return */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"This function is depreceated and is a no-op." "In order to mimic this behaviour,you can supply instead of a function name an array " "containing an object reference and a method name." ); /* Return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * int xml_get_current_column_number(resource $parser) * Gets the current column number of the given XML parser. * Parameters * $parser * A reference to the XML parser. * Return * This function returns FALSE if parser does not refer to a valid parser, or else it returns * which column on the current line (as given by xml_get_current_line_number()) the parser * is currently at. */ static int vm_builtin_xml_get_current_column_number(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; SyStream *pStream; SyToken *pToken; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the current processed token */ pToken = (SyToken *)SySetPeekCurrentEntry(&pEngine->sParser.sToken); if( pToken == 0 ){ /* Stream not yet processed */ ph7_result_int(pCtx,0); return 0; } /* Point to the input stream */ pStream = &pEngine->sParser.sLex.sStream; /* Return the byte index */ ph7_result_int64(pCtx,(ph7_int64)(pToken->sData.zString-(const char *)pStream->zInput)/80); return PH7_OK; } /* * int xml_get_error_code(resource $parser) * Get XML parser error code. * Parameters * $parser * A reference to the XML parser. * Return * This function returns FALSE if parser does not refer to a valid * parser, or else it returns one of the error codes listed in the error * codes section. */ static int vm_builtin_xml_get_error_code(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the error code if any */ ph7_result_int(pCtx,pEngine->iErrCode); return PH7_OK; } /* * XML parser event callbacks * Each time the unserlying XML parser extract a single token * from the input,one of the following callbacks are invoked. * IMP-XML-ENGINE-07-07-2012 22:02 FreeBSD [chm@symisc.net] */ /* * Create a scalar ph7_value holding the value * of an XML tag/attribute/CDATA and so on. */ static ph7_value * VmXMLValue(ph7_xml_engine *pEngine,SyXMLRawStr *pXML,SyXMLRawStr *pNsUri) { ph7_value *pValue; /* Allocate a new scalar variable */ pValue = ph7_context_new_scalar(pEngine->pCtx); if( pValue == 0 ){ ph7_context_throw_error(pEngine->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); return 0; } if( pNsUri && pNsUri->nByte > 0 ){ /* Append namespace URI and the separator */ ph7_value_string_format(pValue,"%.*s%c",pNsUri->nByte,pNsUri->zString,pEngine->ns_sep); } /* Copy the tag value */ ph7_value_string(pValue,pXML->zString,(int)pXML->nByte); return pValue; } /* * Create a 'ph7_value' of type array holding the values * of an XML tag attributes. */ static ph7_value * VmXMLAttrValue(ph7_xml_engine *pEngine,SyXMLRawStr *aAttr,sxu32 nAttr) { ph7_value *pArray; /* Create an empty array */ pArray = ph7_context_new_array(pEngine->pCtx); if( pArray == 0 ){ ph7_context_throw_error(pEngine->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); return 0; } if( nAttr > 0 ){ ph7_value *pKey,*pValue; sxu32 n; /* Create worker variables */ pKey = ph7_context_new_scalar(pEngine->pCtx); pValue = ph7_context_new_scalar(pEngine->pCtx); if( pKey == 0 || pValue == 0 ){ ph7_context_throw_error(pEngine->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); return 0; } /* Copy attributes */ for( n = 0 ; n < nAttr ; n += 2 ){ /* Reset string cursors */ ph7_value_reset_string_cursor(pKey); ph7_value_reset_string_cursor(pValue); /* Copy attribute name and it's associated value */ ph7_value_string(pKey,aAttr[n].zString,(int)aAttr[n].nByte); /* Attribute name */ ph7_value_string(pValue,aAttr[n+1].zString,(int)aAttr[n+1].nByte); /* Attribute value */ /* Insert in the array */ ph7_array_add_elem(pArray,pKey,pValue); /* Will make it's own copy */ } /* Release the worker variables */ ph7_context_release_value(pEngine->pCtx,pKey); ph7_context_release_value(pEngine->pCtx,pValue); } /* Return the freshly created array */ return pArray; } /* * Start element handler. * The user defined callback must accept three parameters: * start_element_handler(resource $parser,string $name,array $attribs ) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $name * The second parameter, name, contains the name of the element for which this handler * is called.If case-folding is in effect for this parser, the element name will be in uppercase letters. * $attribs * The third parameter, attribs, contains an associative array with the element's attributes (if any). * The keys of this array are the attribute names, the values are the attribute values. * Attribute names are case-folded on the same criteria as element names.Attribute values are not case-folded. * The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). * The first key in the array was the first attribute, and so on. * Note: Instead of a function name, an array containing an object reference and a method name can also be supplied. */ static sxi32 VmXMLStartElementHandler(SyXMLRawStr *pStart,SyXMLRawStr *pNS,sxu32 nAttr,SyXMLRawStr *aAttr,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pTag,*pAttr; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_START_TAG]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Create a ph7_value holding the tag name */ pTag = VmXMLValue(pEngine,pStart,pNS); /* Create a ph7_value holding the tag attributes */ pAttr = VmXMLAttrValue(pEngine,aAttr,nAttr); if( pTag == 0 || pAttr == 0 ){ SXUNUSED(pNS); /* cc warning */ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pTag,pAttr,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pTag); ph7_context_release_value(pEngine->pCtx,pAttr); return SXRET_OK; } /* * End element handler. * The user defined callback must accept two parameters: * end_element_handler(resource $parser,string $name) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $name * The second parameter, name, contains the name of the element for which this handler is called. * If case-folding is in effect for this parser, the element name will be in uppercase letters. * Note: Instead of a function name, an array containing an object reference and a method name * can also be supplied. */ static sxi32 VmXMLEndElementHandler(SyXMLRawStr *pEnd,SyXMLRawStr *pNS,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pTag; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_END_TAG]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Create a ph7_value holding the tag name */ pTag = VmXMLValue(pEngine,pEnd,pNS); if( pTag == 0 ){ SXUNUSED(pNS); /* cc warning */ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pTag,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pTag); return SXRET_OK; } /* * Character data handler. * The user defined callback must accept two parameters: * handler(resource $parser,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $data * The second parameter, data, contains the character data as a string. * Character data handler is called for every piece of a text in the XML document. * It can be called multiple times inside each fragment (e.g. for non-ASCII strings). * If a handler function is set to an empty string, or FALSE, the handler in question is disabled. * Note: Instead of a function name, an array containing an object reference and a method name can also be supplied. */ static sxi32 VmXMLTextHandler(SyXMLRawStr *pText,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pData; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_CDATA]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Create a ph7_value holding the data */ pData = VmXMLValue(pEngine,&(*pText),0); if( pData == 0 ){ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pData,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pData); return SXRET_OK; } /* * Processing instruction (PI) handler. * The user defined callback must accept two parameters: * handler(resource $parser,string $target,string $data) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $target * The second parameter, target, contains the PI target. * $data * The third parameter, data, contains the PI data. * Note: Instead of a function name, an array containing an object reference * and a method name can also be supplied. */ static sxi32 VmXMLPIHandler(SyXMLRawStr *pTargetStr,SyXMLRawStr *pDataStr,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pTarget,*pData; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_PI]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Get a ph7_value holding the data */ pTarget = VmXMLValue(pEngine,&(*pTargetStr),0); pData = VmXMLValue(pEngine,&(*pDataStr),0); if( pTarget == 0 || pData == 0 ){ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pTarget,pData,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pTarget); ph7_context_release_value(pEngine->pCtx,pData); return SXRET_OK; } /* * Namespace declaration handler. * The user defined callback must accept two parameters: * handler(resource $parser,string $prefix,string $uri) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $prefix * The prefix is a string used to reference the namespace within an XML object. * $uri * Uniform Resource Identifier (URI) of namespace. * Note: Instead of a function name, an array containing an object reference * and a method name can also be supplied. */ static sxi32 VmXMLNSStartHandler(SyXMLRawStr *pUriStr,SyXMLRawStr *pPrefixStr,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pUri,*pPrefix; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_NS_START]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Get a ph7_value holding the PREFIX/URI */ pUri = VmXMLValue(pEngine,pUriStr,0); pPrefix = VmXMLValue(pEngine,pPrefixStr,0); if( pUri == 0 || pPrefix == 0 ){ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pUri,pPrefix,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pUri); ph7_context_release_value(pEngine->pCtx,pPrefix); return SXRET_OK; } /* * Namespace end declaration handler. * The user defined callback must accept two parameters: * handler(resource $parser,string $prefix) * $parser * The first parameter, parser, is a reference to the XML parser calling the handler. * $prefix * The prefix is a string used to reference the namespace within an XML object. * Note: Instead of a function name, an array containing an object reference * and a method name can also be supplied. */ static sxi32 VmXMLNSEndHandler(SyXMLRawStr *pPrefixStr,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; ph7_value *pCallback,*pPrefix; /* Point to the target user defined callback */ pCallback = &pEngine->aCB[PH7_XML_NS_END]; /* Make sure the given callback is callable */ if( !PH7_VmIsCallable(pEngine->pVm,pCallback,0) ){ /* Not callable,return immediately*/ return SXRET_OK; } /* Get a ph7_value holding the prefix */ pPrefix = VmXMLValue(pEngine,pPrefixStr,0); if( pPrefix == 0 ){ /* Out of mem,return immediately */ return SXRET_OK; } /* Invoke the user callback */ PH7_VmCallUserFunctionAp(pEngine->pVm,pCallback,0,&pEngine->sParserValue,pPrefix,0); /* Clean-up the mess left behind */ ph7_context_release_value(pEngine->pCtx,pPrefix); return SXRET_OK; } /* * Error Message consumer handler. * Each time the XML parser encounter a syntaxt error or any other error * related to XML processing,the following callback is invoked by the * underlying XML parser. */ static sxi32 VmXMLErrorHandler(const char *zMessage,sxi32 iErrCode,SyToken *pToken,void *pUserData) { ph7_xml_engine *pEngine = (ph7_xml_engine *)pUserData; /* Save the error code */ pEngine->iErrCode = iErrCode; SXUNUSED(zMessage); /* cc warning */ if( pToken ){ pEngine->nLine = pToken->nLine; } /* Abort XML processing immediately */ return SXERR_ABORT; } /* * int xml_parse(resource $parser,string $data[,bool $is_final = false ]) * Parses an XML document. The handlers for the configured events are called * as many times as necessary. * Parameters * $parser * A reference to the XML parser. * $data * Chunk of data to parse. A document may be parsed piece-wise by calling * xml_parse() several times with new data, as long as the is_final parameter * is set and TRUE when the last data is parsed. * $is_final * NOT USED. This implementation require that all the processed input be * entirely loaded in memory. * Return * Returns 1 on success or 0 on failure. */ static int vm_builtin_xml_parse(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; SyXMLParser *pParser; const char *zData; int nByte; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) || !ph7_value_is_string(apArg[1]) ){ /* Missing/Ivalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( pEngine->iNest > 0 ){ /* This can happen when the user callback call xml_parse() again * in it's body which is forbidden. */ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR, "Recursive call to %s,PH7 is returning false", ph7_function_name(pCtx) ); /* Return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } pEngine->pCtx = pCtx; /* Point to the underlying XML parser */ pParser = &pEngine->sParser; /* Register elements handler */ SyXMLParserSetEventHandler(pParser,pEngine, VmXMLStartElementHandler, VmXMLTextHandler, VmXMLErrorHandler, 0, VmXMLEndElementHandler, VmXMLPIHandler, 0, 0, VmXMLNSStartHandler, VmXMLNSEndHandler ); pEngine->iErrCode = SXML_ERROR_NONE; /* Extract the raw XML input */ zData = ph7_value_to_string(apArg[1],&nByte); /* Start the parse process */ pEngine->iNest++; SyXMLProcess(pParser,zData,(sxu32)nByte); pEngine->iNest--; /* Return the parse result */ ph7_result_int(pCtx,pEngine->iErrCode == SXML_ERROR_NONE ? 1 : 0); return PH7_OK; } /* * bool xml_parser_set_option(resource $parser,int $option,mixed $value) * Sets an option in an XML parser. * Parameters * $parser * A reference to the XML parser to set an option in. * $option * Which option to set. See below. * The following options are available: * XML_OPTION_CASE_FOLDING integer Controls whether case-folding is enabled for this XML parser. * XML_OPTION_SKIP_TAGSTART integer Specify how many characters should be skipped in the beginning of a tag name. * XML_OPTION_SKIP_WHITE integer Whether to skip values consisting of whitespace characters. * XML_OPTION_TARGET_ENCODING string Sets which target encoding to use in this XML parser. * $value * The option's new value. * Return * Returns 1 on success or 0 on failure. * Note: * Well,none of these options have meaning under the built-in XML parser so a call to this * function is a no-op. */ static int vm_builtin_xml_parser_set_option(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Always return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * mixed xml_parser_get_option(resource $parser,int $option) * Get options from an XML parser. * Parameters * $parser * A reference to the XML parser to set an option in. * $option * Which option to fetch. * Return * This function returns FALSE if parser does not refer to a valid parser * or if option isn't valid.Else the option's value is returned. */ static int vm_builtin_xml_parser_get_option(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_xml_engine *pEngine; int nOp; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Ivalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the XML engine */ pEngine = (ph7_xml_engine *)ph7_value_to_resource(apArg[0]); if( IS_INVALID_XML_ENGINE(pEngine) ){ /* Corrupt engine,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the option */ nOp = ph7_value_to_int(apArg[1]); switch(nOp){ case SXML_OPTION_SKIP_TAGSTART: case SXML_OPTION_SKIP_WHITE: case SXML_OPTION_CASE_FOLDING: ph7_result_int(pCtx,0); break; case SXML_OPTION_TARGET_ENCODING: ph7_result_string(pCtx,"UTF-8",(int)sizeof("UTF-8")-1); break; default: /* Unknown option,return FALSE*/ ph7_result_bool(pCtx,0); break; } return PH7_OK; } /* * string xml_error_string(int $code) * Gets the XML parser error string associated with the given code. * Parameters * $code * An error code from xml_get_error_code(). * Return * Returns a string with a textual description of the error * code, or FALSE if no description was found. */ static int vm_builtin_xml_error_string(ph7_context *pCtx,int nArg,ph7_value **apArg) { int nErr = -1; if( nArg > 0 ){ nErr = ph7_value_to_int(apArg[0]); } switch(nErr){ case SXML_ERROR_DUPLICATE_ATTRIBUTE: ph7_result_string(pCtx,"Duplicate attribute",-1/*Compute length automatically*/); break; case SXML_ERROR_INCORRECT_ENCODING: ph7_result_string(pCtx,"Incorrect encoding",-1); break; case SXML_ERROR_INVALID_TOKEN: ph7_result_string(pCtx,"Unexpected token",-1); break; case SXML_ERROR_MISPLACED_XML_PI: ph7_result_string(pCtx,"Misplaced processing instruction",-1); break; case SXML_ERROR_NO_MEMORY: ph7_result_string(pCtx,"Out of memory",-1); break; case SXML_ERROR_NONE: ph7_result_string(pCtx,"Not an error",-1); break; case SXML_ERROR_TAG_MISMATCH: ph7_result_string(pCtx,"Tag mismatch",-1); break; case -1: ph7_result_string(pCtx,"Unknown error code",-1); break; default: ph7_result_string(pCtx,"Syntax error",-1); break; } return PH7_OK; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * int utf8_encode(string $input) * UTF-8 encoding. * This function encodes the string data to UTF-8, and returns the encoded version. * UTF-8 is a standard mechanism used by Unicode for encoding wide character values * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized * (meaning it is possible for a program to figure out where in the bytestream characters start) * and can be used with normal string comparison functions for sorting and such. * Notes on UTF-8 (According to SQLite3 authors): * Byte-0 Byte-1 Byte-2 Byte-3 Value * 0xxxxxxx 00000000 00000000 0xxxxxxx * 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx * 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx * 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx * Parameters * $input * String to encode or NULL on failure. * Return * An UTF-8 encoded string. */ static int vm_builtin_utf8_encode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nByte,c,e; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nByte); if( nByte < 1 ){ /* Empty string,return null */ ph7_result_null(pCtx); return PH7_OK; } zEnd = &zIn[nByte]; /* Start the encoding process */ for(;;){ if( zIn >= zEnd ){ /* End of input */ break; } c = zIn[0]; /* Advance the stream cursor */ zIn++; /* Encode */ if( c<0x00080 ){ e = (c&0xFF); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); }else if( c<0x00800 ){ e = 0xC0 + ((c>>6)&0x1F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + (c & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); }else if( c<0x10000 ){ e = 0xE0 + ((c>>12)&0x0F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + ((c>>6) & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + (c & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); }else{ e = 0xF0 + ((c>>18) & 0x07); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + ((c>>12) & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + ((c>>6) & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); e = 0x80 + (c & 0x3F); ph7_result_string(pCtx,(const char *)&e,(int)sizeof(char)); } } /* All done */ return PH7_OK; } /* * UTF-8 decoding routine extracted from the sqlite3 source tree. * Original author: D. Richard Hipp (http://www.sqlite.org) * Status: Public Domain */ /* ** This lookup table is used to help decode the first byte of ** a multi-byte UTF8 character. */ static const unsigned char UtfTrans1[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, }; /* ** Translate a single UTF-8 character. Return the unicode value. ** ** During translation, assume that the byte that zTerm points ** is a 0x00. ** ** Write a pointer to the next unread byte back into *pzNext. ** ** Notes On Invalid UTF-8: ** ** * This routine never allows a 7-bit character (0x00 through 0x7f) to ** be encoded as a multi-byte character. Any multi-byte character that ** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. ** ** * This routine never allows a UTF16 surrogate value to be encoded. ** If a multi-byte character attempts to encode a value between ** 0xd800 and 0xe000 then it is rendered as 0xfffd. ** ** * Bytes in the range of 0x80 through 0xbf which occur as the first ** byte of a character are interpreted as single-byte characters ** and rendered as themselves even though they are technically ** invalid characters. ** ** * This routine accepts an infinite number of different UTF8 encodings ** for unicode values 0x80 and greater. It do not change over-length ** encodings to 0xfffd as some systems recommend. */ #define READ_UTF8(zIn, zTerm, c) \ c = *(zIn++); \ if( c>=0xc0 ){ \ c = UtfTrans1[c-0xc0]; \ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ c = (c<<6) + (0x3f & *(zIn++)); \ } \ if( c<0x80 \ || (c&0xFFFFF800)==0xD800 \ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ } PH7_PRIVATE int PH7_Utf8Read( const unsigned char *z, /* First byte of UTF-8 character */ const unsigned char *zTerm, /* Pretend this byte is 0x00 */ const unsigned char **pzNext /* Write first byte past UTF-8 char here */ ){ int c; READ_UTF8(z, zTerm, c); *pzNext = z; return c; } /* * string utf8_decode(string $data) * This function decodes data, assumed to be UTF-8 encoded, to unicode. * Parameters * data * An UTF-8 encoded string. * Return * Unicode decoded string or NULL on failure. */ static int vm_builtin_utf8_decode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nByte,c; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nByte); if( nByte < 1 ){ /* Empty string,return null */ ph7_result_null(pCtx); return PH7_OK; } zEnd = &zIn[nByte]; /* Start the decoding process */ while( zIn < zEnd ){ c = PH7_Utf8Read(zIn,zEnd,&zIn); if( c == 0x0 ){ break; } ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); } return PH7_OK; } /* Table of built-in VM functions. */ static const ph7_builtin_func aVmFunc[] = { { "func_num_args" , vm_builtin_func_num_args }, { "func_get_arg" , vm_builtin_func_get_arg }, { "func_get_args" , vm_builtin_func_get_args }, { "func_get_args_byref" , vm_builtin_func_get_args_byref }, { "function_exists", vm_builtin_func_exists }, { "is_callable" , vm_builtin_is_callable }, { "get_defined_functions", vm_builtin_get_defined_func }, { "register_shutdown_function",vm_builtin_register_shutdown_function }, { "call_user_func", vm_builtin_call_user_func }, { "call_user_func_array", vm_builtin_call_user_func_array }, { "forward_static_call", vm_builtin_call_user_func }, { "forward_static_call_array",vm_builtin_call_user_func_array }, /* Constants management */ { "defined", vm_builtin_defined }, { "define", vm_builtin_define }, { "constant", vm_builtin_constant }, { "get_defined_constants", vm_builtin_get_defined_constants }, /* Class/Object functions */ { "class_alias", vm_builtin_class_alias }, { "class_exists", vm_builtin_class_exists }, { "property_exists", vm_builtin_property_exists }, { "method_exists", vm_builtin_method_exists }, { "interface_exists",vm_builtin_interface_exists }, { "get_class", vm_builtin_get_class }, { "get_parent_class",vm_builtin_get_parent_class }, { "get_called_class",vm_builtin_get_called_class }, { "get_declared_classes", vm_builtin_get_declared_classes }, { "get_defined_classes", vm_builtin_get_declared_classes }, { "get_declared_interfaces", vm_builtin_get_declared_interfaces}, { "get_class_methods", vm_builtin_get_class_methods }, { "get_class_vars", vm_builtin_get_class_vars }, { "get_object_vars", vm_builtin_get_object_vars }, { "is_subclass_of", vm_builtin_is_subclass_of }, { "is_a", vm_builtin_is_a }, /* Random numbers/strings generators */ { "rand", vm_builtin_rand }, { "mt_rand", vm_builtin_rand }, { "rand_str", vm_builtin_rand_str }, { "getrandmax", vm_builtin_getrandmax }, { "mt_getrandmax", vm_builtin_getrandmax }, #ifndef PH7_DISABLE_BUILTIN_FUNC #if !defined(PH7_DISABLE_HASH_FUNC) { "uniqid", vm_builtin_uniqid }, #endif /* PH7_DISABLE_HASH_FUNC */ #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* Language constructs functions */ { "echo", vm_builtin_echo }, { "print", vm_builtin_print }, { "exit", vm_builtin_exit }, { "die", vm_builtin_exit }, { "eval", vm_builtin_eval }, /* Variable handling functions */ { "get_defined_vars",vm_builtin_get_defined_vars}, { "gettype", vm_builtin_gettype }, { "get_resource_type", vm_builtin_get_resource_type}, { "isset", vm_builtin_isset }, { "unset", vm_builtin_unset }, { "var_dump", vm_builtin_var_dump }, { "print_r", vm_builtin_print_r }, { "var_export",vm_builtin_var_export }, /* Ouput control functions */ { "flush", vm_builtin_ob_flush }, { "ob_clean", vm_builtin_ob_clean }, { "ob_end_clean", vm_builtin_ob_end_clean }, { "ob_end_flush", vm_builtin_ob_end_flush }, { "ob_flush", vm_builtin_ob_flush }, { "ob_get_clean", vm_builtin_ob_get_clean }, { "ob_get_contents", vm_builtin_ob_get_contents}, { "ob_get_flush", vm_builtin_ob_get_clean }, { "ob_get_length", vm_builtin_ob_get_length }, { "ob_get_level", vm_builtin_ob_get_level }, { "ob_implicit_flush", vm_builtin_ob_implicit_flush}, { "ob_get_level", vm_builtin_ob_get_level }, { "ob_list_handlers", vm_builtin_ob_list_handlers }, { "ob_start", vm_builtin_ob_start }, /* Assertion functions */ { "assert_options", vm_builtin_assert_options }, { "assert", vm_builtin_assert }, /* Error reporting functions */ { "trigger_error",vm_builtin_trigger_error }, { "user_error", vm_builtin_trigger_error }, { "error_reporting",vm_builtin_error_reporting }, { "error_log", vm_builtin_error_log }, { "restore_exception_handler", vm_builtin_restore_exception_handler }, { "set_exception_handler", vm_builtin_set_exception_handler }, { "restore_error_handler", vm_builtin_restore_error_handler }, { "set_error_handler",vm_builtin_set_error_handler }, { "debug_backtrace", vm_builtin_debug_backtrace}, { "error_get_last" , vm_builtin_debug_backtrace }, { "debug_print_backtrace", vm_builtin_debug_print_backtrace }, { "debug_string_backtrace",vm_builtin_debug_string_backtrace }, /* Release info */ {"ph7version", vm_builtin_ph7_version }, {"ph7credits", vm_builtin_ph7_credits }, {"ph7info", vm_builtin_ph7_credits }, {"ph7_info", vm_builtin_ph7_credits }, {"phpinfo", vm_builtin_ph7_credits }, {"ph7copyright", vm_builtin_ph7_credits }, /* hashmap */ {"compact", vm_builtin_compact }, {"extract", vm_builtin_extract }, {"import_request_variables", vm_builtin_import_request_variables}, /* URL related function */ {"parse_url", vm_builtin_parse_url }, /* Refer to 'builtin.c' for others string processing functions. */ #ifndef PH7_DISABLE_BUILTIN_FUNC /* XML processing functions */ {"xml_parser_create", vm_builtin_xml_parser_create }, {"xml_parser_create_ns", vm_builtin_xml_parser_create_ns}, {"xml_parser_free", vm_builtin_xml_parser_free }, {"xml_set_element_handler", vm_builtin_xml_set_element_handler}, {"xml_set_character_data_handler", vm_builtin_xml_set_character_data_handler}, {"xml_set_default_handler", vm_builtin_xml_set_default_handler }, {"xml_set_end_namespace_decl_handler", vm_builtin_xml_set_end_namespace_decl_handler}, {"xml_set_start_namespace_decl_handler",vm_builtin_xml_set_start_namespace_decl_handler}, {"xml_set_processing_instruction_handler",vm_builtin_xml_set_processing_instruction_handler}, {"xml_set_unparsed_entity_decl_handler",vm_builtin_xml_set_unparsed_entity_decl_handler}, {"xml_set_notation_decl_handler",vm_builtin_xml_set_notation_decl_handler}, {"xml_set_external_entity_ref_handler",vm_builtin_xml_set_external_entity_ref_handler}, {"xml_get_current_line_number", vm_builtin_xml_get_current_line_number}, {"xml_get_current_byte_index", vm_builtin_xml_get_current_byte_index }, {"xml_set_object", vm_builtin_xml_set_object}, {"xml_get_current_column_number",vm_builtin_xml_get_current_column_number}, {"xml_get_error_code", vm_builtin_xml_get_error_code }, {"xml_parse", vm_builtin_xml_parse }, {"xml_parser_set_option", vm_builtin_xml_parser_set_option}, {"xml_parser_get_option", vm_builtin_xml_parser_get_option}, {"xml_error_string", vm_builtin_xml_error_string }, #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* UTF-8 encoding/decoding */ {"utf8_encode", vm_builtin_utf8_encode}, {"utf8_decode", vm_builtin_utf8_decode}, /* Command line processing */ {"getopt", vm_builtin_getopt }, /* JSON encoding/decoding */ {"json_encode", vm_builtin_json_encode }, {"json_last_error",vm_builtin_json_last_error}, {"json_decode", vm_builtin_json_decode }, {"serialize", vm_builtin_json_encode }, {"unserialize", vm_builtin_json_decode }, /* Files/URI inclusion facility */ { "get_include_path", vm_builtin_get_include_path }, { "get_included_files",vm_builtin_get_included_files}, { "include", vm_builtin_include }, { "include_once", vm_builtin_include_once }, { "require", vm_builtin_require }, { "require_once", vm_builtin_require_once }, }; /* * Register the built-in VM functions defined above. */ static sxi32 VmRegisterSpecialFunction(ph7_vm *pVm) { sxi32 rc; sxu32 n; for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){ /* Note that these special functions have access * to the underlying virtual machine as their * private data. */ rc = ph7_create_function(&(*pVm),aVmFunc[n].zName,aVmFunc[n].xFunc,&(*pVm)); if( rc != SXRET_OK ){ return rc; } } return SXRET_OK; } /* * Check if the given name refer to an installed class. * Return a pointer to that class on success. NULL on failure. */ PH7_PRIVATE ph7_class * PH7_VmExtractClass( ph7_vm *pVm, /* Target VM */ const char *zName, /* Name of the target class */ sxu32 nByte, /* zName length */ sxi32 iLoadable, /* TRUE to return only loadable class * [i.e: no abstract classes or interfaces] */ sxi32 iNest /* Nesting level (Not used) */ ) { SyHashEntry *pEntry; ph7_class *pClass; /* Perform a hash lookup */ pEntry = SyHashGet(&pVm->hClass,(const void *)zName,nByte); if( pEntry == 0 ){ /* No such entry,return NULL */ iNest = 0; /* cc warning */ return 0; } pClass = (ph7_class *)pEntry->pUserData; if( !iLoadable ){ /* Return the first class seen */ return pClass; }else{ /* Check the collision list */ while(pClass){ if( (pClass->iFlags & (PH7_CLASS_INTERFACE|PH7_CLASS_ABSTRACT)) == 0 ){ /* Class is loadable */ return pClass; } /* Point to the next entry */ pClass = pClass->pNextName; } } /* No such loadable class */ return 0; } /* * Reference Table Implementation * Status: stable * Intro * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ /* * Allocate a new reference entry. */ static VmRefObj * VmNewRefObj(ph7_vm *pVm,sxu32 nIdx) { VmRefObj *pRef; /* Allocate a new instance */ pRef = (VmRefObj *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(VmRefObj)); if( pRef == 0 ){ return 0; } /* Zero the structure */ SyZero(pRef,sizeof(VmRefObj)); /* Initialize fields */ SySetInit(&pRef->aReference,&pVm->sAllocator,sizeof(SyHashEntry *)); SySetInit(&pRef->aArrEntries,&pVm->sAllocator,sizeof(ph7_hashmap_node *)); pRef->nIdx = nIdx; return pRef; } /* * Default hash function used by the reference table * for lookup/insertion operations. */ static sxu32 VmRefHash(sxu32 nIdx) { /* Calculate the hash based on the memory object index */ return nIdx ^ (nIdx << 8) ^ (nIdx >> 8); } /* * Check if a memory object [i.e: a variable] is already installed * in the reference table. * Return a pointer to the entry (VmRefObj instance) on success.NULL * otherwise. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ static VmRefObj * VmRefObjExtract(ph7_vm *pVm,sxu32 nObjIdx) { VmRefObj *pRef; sxu32 nBucket; /* Point to the appropriate bucket */ nBucket = VmRefHash(nObjIdx) & (pVm->nRefSize - 1); /* Perform the lookup */ pRef = pVm->apRefObj[nBucket]; for(;;){ if( pRef == 0 ){ break; } if( pRef->nIdx == nObjIdx ){ /* Entry found */ return pRef; } /* Point to the next entry */ pRef = pRef->pNextCollide; } /* No such entry,return NULL */ return 0; } /* * Install a memory object [i.e: a variable] in the reference table. * * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ static sxi32 VmRefObjInsert(ph7_vm *pVm,VmRefObj *pRef) { sxu32 nBucket; if( pVm->nRefUsed * 3 >= pVm->nRefSize ){ VmRefObj **apNew; sxu32 nNew; /* Allocate a larger table */ nNew = pVm->nRefSize << 1; apNew = (VmRefObj **)SyMemBackendAlloc(&pVm->sAllocator,sizeof(VmRefObj *) * nNew); if( apNew ){ VmRefObj *pEntry = pVm->pRefList; sxu32 n; /* Zero the structure */ SyZero((void *)apNew,nNew * sizeof(VmRefObj *)); /* Rehash all referenced entries */ for( n = 0 ; n < pVm->nRefUsed ; ++n ){ /* Remove old collision links */ pEntry->pNextCollide = pEntry->pPrevCollide = 0; /* Point to the appropriate bucket */ nBucket = VmRefHash(pEntry->nIdx) & (nNew - 1); /* Insert the entry */ pEntry->pNextCollide = apNew[nBucket]; if( apNew[nBucket] ){ apNew[nBucket]->pPrevCollide = pEntry; } apNew[nBucket] = pEntry; /* Point to the next entry */ pEntry = pEntry->pNext; } /* Release the old table */ SyMemBackendFree(&pVm->sAllocator,pVm->apRefObj); /* Install the new one */ pVm->apRefObj = apNew; pVm->nRefSize = nNew; } } /* Point to the appropriate bucket */ nBucket = VmRefHash(pRef->nIdx) & (pVm->nRefSize - 1); /* Insert the entry */ pRef->pNextCollide = pVm->apRefObj[nBucket]; if( pVm->apRefObj[nBucket] ){ pVm->apRefObj[nBucket]->pPrevCollide = pRef; } pVm->apRefObj[nBucket] = pRef; MACRO_LD_PUSH(pVm->pRefList,pRef); pVm->nRefUsed++; return SXRET_OK; } /* * Destroy a memory object [i.e: a variable] and remove it from * the reference table. * This function is invoked when the user perform an unset * call [i.e: unset($var); ]. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ static sxi32 VmRefObjUnlink(ph7_vm *pVm,VmRefObj *pRef) { ph7_hashmap_node **apNode; SyHashEntry **apEntry; sxu32 n; /* Point to the reference table */ apNode = (ph7_hashmap_node **)SySetBasePtr(&pRef->aArrEntries); apEntry = (SyHashEntry **)SySetBasePtr(&pRef->aReference); /* Unlink the entry from the reference table */ for( n = 0 ; n < SySetUsed(&pRef->aReference) ; n++ ){ if( apEntry[n] ){ SyHashDeleteEntry2(apEntry[n]); } } for(n = 0 ; n < SySetUsed(&pRef->aArrEntries) ; ++n ){ if( apNode[n] ){ PH7_HashmapUnlinkNode(apNode[n],FALSE); } } if( pRef->pPrevCollide ){ pRef->pPrevCollide->pNextCollide = pRef->pNextCollide; }else{ pVm->apRefObj[VmRefHash(pRef->nIdx) & (pVm->nRefSize - 1)] = pRef->pNextCollide; } if( pRef->pNextCollide ){ pRef->pNextCollide->pPrevCollide = pRef->pPrevCollide; } MACRO_LD_REMOVE(pVm->pRefList,pRef); /* Release the node */ SySetRelease(&pRef->aReference); SySetRelease(&pRef->aArrEntries); SyMemBackendPoolFree(&pVm->sAllocator,pRef); pVm->nRefUsed--; return SXRET_OK; } /* * Install a memory object [i.e: a variable] in the reference table. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ PH7_PRIVATE sxi32 PH7_VmRefObjInstall( ph7_vm *pVm, /* Target VM */ sxu32 nIdx, /* Memory object index in the global object pool */ SyHashEntry *pEntry, /* Hash entry of this object */ ph7_hashmap_node *pMapEntry, /* != NULL if the memory object is an array entry */ sxi32 iFlags /* Control flags */ ) { VmFrame *pFrame = pVm->pFrame; VmRefObj *pRef; /* Check if the referenced object already exists */ pRef = VmRefObjExtract(&(*pVm),nIdx); if( pRef == 0 ){ /* Create a new entry */ pRef = VmNewRefObj(&(*pVm),nIdx); if( pRef == 0 ){ return SXERR_MEM; } pRef->iFlags = iFlags; /* Install the entry */ VmRefObjInsert(&(*pVm),pRef); } while( pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION) ){ /* Safely ignore the exception frame */ pFrame = pFrame->pParent; } if( pFrame->pParent != 0 && pEntry ){ VmSlot sRef; /* Local frame,record referenced entry so that it can * be deleted when we leave this frame. */ sRef.nIdx = nIdx; sRef.pUserData = pEntry; if( SXRET_OK != SySetPut(&pFrame->sRef,(const void *)&sRef)) { pEntry = 0; /* Do not record this entry */ } } if( pEntry ){ /* Address of the hash-entry */ SySetPut(&pRef->aReference,(const void *)&pEntry); } if( pMapEntry ){ /* Address of the hashmap node [i.e: Array entry] */ SySetPut(&pRef->aArrEntries,(const void *)&pMapEntry); } return SXRET_OK; } /* * Remove a memory object [i.e: a variable] from the reference table. * The implementation of the reference mechanism in the PH7 engine * differ greatly from the one used by the zend engine. That is, * the reference implementation is consistent,solid and it's * behavior resemble the C++ reference mechanism. * Refer to the official for more information on this powerful * extension. */ PH7_PRIVATE sxi32 PH7_VmRefObjRemove( ph7_vm *pVm, /* Target VM */ sxu32 nIdx, /* Memory object index in the global object pool */ SyHashEntry *pEntry, /* Hash entry of this object */ ph7_hashmap_node *pMapEntry /* != NULL if the memory object is an array entry */ ) { VmRefObj *pRef; sxu32 n; /* Check if the referenced object already exists */ pRef = VmRefObjExtract(&(*pVm),nIdx); if( pRef == 0 ){ /* Not such entry */ return SXERR_NOTFOUND; } /* Remove the desired entry */ if( pEntry ){ SyHashEntry **apEntry; apEntry = (SyHashEntry **)SySetBasePtr(&pRef->aReference); for( n = 0 ; n < SySetUsed(&pRef->aReference) ; n++ ){ if( apEntry[n] == pEntry ){ /* Nullify the entry */ apEntry[n] = 0; /* * NOTE: * In future releases,think to add a free pool of entries,so that * we avoid wasting spaces. */ } } } if( pMapEntry ){ ph7_hashmap_node **apNode; apNode = (ph7_hashmap_node **)SySetBasePtr(&pRef->aArrEntries); for(n = 0 ; n < SySetUsed(&pRef->aArrEntries) ; n++ ){ if( apNode[n] == pMapEntry ){ /* nullify the entry */ apNode[n] = 0; } } } return SXRET_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC /* * Extract the IO stream device associated with a given scheme. * Return a pointer to an instance of ph7_io_stream when the scheme * have an associated IO stream registered with it. NULL otherwise. * If no scheme:// is avalilable then the file:// scheme is assumed. * For more information on how to register IO stream devices,please * refer to the official documentation. */ PH7_PRIVATE const ph7_io_stream * PH7_VmGetStreamDevice( ph7_vm *pVm, /* Target VM */ const char **pzDevice, /* Full path,URI,... */ int nByte /* *pzDevice length*/ ) { const char *zIn,*zEnd,*zCur,*zNext; ph7_io_stream **apStream,*pStream; SyString sDev,sCur; sxu32 n,nEntry; int rc; /* Check if a scheme [i.e: file://,http://,zip://...] is available */ zNext = zCur = zIn = *pzDevice; zEnd = &zIn[nByte]; while( zIn < zEnd ){ if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){ /* Got one */ zNext = &zIn[sizeof("://")-1]; break; } /* Advance the cursor */ zIn++; } if( zIn >= zEnd ){ /* No such scheme,return the default stream */ return pVm->pDefStream; } SyStringInitFromBuf(&sDev,zCur,zIn-zCur); /* Remove leading and trailing white spaces */ SyStringFullTrim(&sDev); /* Perform a linear lookup on the installed stream devices */ apStream = (ph7_io_stream **)SySetBasePtr(&pVm->aIOstream); nEntry = SySetUsed(&pVm->aIOstream); for( n = 0 ; n < nEntry ; n++ ){ pStream = apStream[n]; SyStringInitFromBuf(&sCur,pStream->zName,SyStrlen(pStream->zName)); /* Perfrom a case-insensitive comparison */ rc = SyStringCmp(&sDev,&sCur,SyStrnicmp); if( rc == 0 ){ /* Stream device found */ *pzDevice = zNext; return pStream; } } /* No such stream,return NULL */ return 0; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * Section: * HTTP/URI related routines. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * URI Parser: Split an URI into components [i.e: Host,Path,Query,...]. * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document] * This almost, but not quite, RFC1738 URI syntax. * This routine is not a validator,it does not check for validity * nor decode URI parts,the only thing this routine does is splitting * the input to its fields. * Upper layer are responsible of decoding and validating URI parts. * On success,this function populate the "SyhttpUri" structure passed * as the first argument. Otherwise SXERR_* is returned when a malformed * input is encountered. */ static sxi32 VmHttpSplitURI(SyhttpUri *pOut,const char *zUri,sxu32 nLen) { const char *zEnd = &zUri[nLen]; sxu8 bHostOnly = FALSE; sxu8 bIPv6 = FALSE ; const char *zCur; SyString *pComp; sxu32 nPos = 0; sxi32 rc; /* Zero the structure first */ SyZero(pOut,sizeof(SyhttpUri)); /* Remove leading and trailing white spaces */ SyStringInitFromBuf(&pOut->sRaw,zUri,nLen); SyStringFullTrim(&pOut->sRaw); /* Find the first '/' separator */ rc = SyByteFind(zUri,(sxu32)(zEnd - zUri),'/',&nPos); if( rc != SXRET_OK ){ /* Assume a host name only */ zCur = zEnd; bHostOnly = TRUE; goto ProcessHost; } zCur = &zUri[nPos]; if( zUri != zCur && zCur[-1] == ':' ){ /* Extract a scheme: * Not that we can get an invalid scheme here. * Fortunately the caller can discard any URI by comparing this scheme with its * registered schemes and will report the error as soon as his comparison function * fail. */ pComp = &pOut->sScheme; SyStringInitFromBuf(pComp,zUri,(sxu32)(zCur - zUri - 1)); SyStringLeftTrim(pComp); } if( zCur[1] != '/' ){ if( zCur == zUri || zCur[-1] == ':' ){ /* No authority */ goto PathSplit; } /* There is something here , we will assume its an authority * and someone has forgot the two prefix slashes "//", * sooner or later we will detect if we are dealing with a malicious * user or not,but now assume we are dealing with an authority * and let the caller handle all the validation process. */ goto ProcessHost; } zUri = &zCur[2]; zCur = zEnd; rc = SyByteFind(zUri,(sxu32)(zEnd - zUri),'/',&nPos); if( rc == SXRET_OK ){ zCur = &zUri[nPos]; } ProcessHost: /* Extract user information if present */ rc = SyByteFind(zUri,(sxu32)(zCur - zUri),'@',&nPos); if( rc == SXRET_OK ){ if( nPos > 0 ){ sxu32 nPassOfft; /* Password offset */ pComp = &pOut->sUser; SyStringInitFromBuf(pComp,zUri,nPos); /* Extract the password if available */ rc = SyByteFind(zUri,(sxu32)(zCur - zUri),':',&nPassOfft); if( rc == SXRET_OK && nPassOfft < nPos){ pComp->nByte = nPassOfft; pComp = &pOut->sPass; pComp->zString = &zUri[nPassOfft+sizeof(char)]; pComp->nByte = nPos - nPassOfft - 1; } /* Update the cursor */ zUri = &zUri[nPos+1]; }else{ zUri++; } } pComp = &pOut->sHost; while( zUri < zCur && SyisSpace(zUri[0])){ zUri++; } SyStringInitFromBuf(pComp,zUri,(sxu32)(zCur - zUri)); if( pComp->zString[0] == '[' ){ /* An IPv6 Address: Make a simple naive test */ zUri++; pComp->zString++; pComp->nByte = 0; while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){ zUri++; pComp->nByte++; } if( zUri[0] != ']' ){ return SXERR_CORRUPT; /* Malformed IPv6 address */ } zUri++; bIPv6 = TRUE; } /* Extract a port number if available */ rc = SyByteFind(zUri,(sxu32)(zCur - zUri),':',&nPos); if( rc == SXRET_OK ){ if( bIPv6 == FALSE ){ pComp->nByte = (sxu32)(&zUri[nPos] - zUri); } pComp = &pOut->sPort; SyStringInitFromBuf(pComp,&zUri[nPos+1],(sxu32)(zCur - &zUri[nPos+1])); } if( bHostOnly == TRUE ){ return SXRET_OK; } PathSplit: zUri = zCur; pComp = &pOut->sPath; SyStringInitFromBuf(pComp,zUri,(sxu32)(zEnd-zUri)); if( pComp->nByte == 0 ){ return SXRET_OK; /* Empty path */ } if( SXRET_OK == SyByteFind(zUri,(sxu32)(zEnd-zUri),'?',&nPos) ){ pComp->nByte = nPos; /* Update path length */ pComp = &pOut->sQuery; SyStringInitFromBuf(pComp,&zUri[nPos+1],(sxu32)(zEnd-&zUri[nPos+1])); } if( SXRET_OK == SyByteFind(zUri,(sxu32)(zEnd-zUri),'#',&nPos) ){ /* Update path or query length */ if( pComp == &pOut->sPath ){ pComp->nByte = nPos; }else{ if( &zUri[nPos] < (char *)SyStringData(pComp) ){ /* Malformed syntax : Query must be present before fragment */ return SXERR_SYNTAX; } pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]); } pComp = &pOut->sFragment; SyStringInitFromBuf(pComp,&zUri[nPos+1],(sxu32)(zEnd-&zUri[nPos+1])) } return SXRET_OK; } /* * Extract a single line from a raw HTTP request. * Return SXRET_OK on success,SXERR_EOF when end of input * and SXERR_MORE when more input is needed. */ static sxi32 VmGetNextLine(SyString *pCursor,SyString *pCurrent) { const char *zIn; sxu32 nPos; /* Jump leading white spaces */ SyStringLeftTrim(pCursor); if( pCursor->nByte < 1 ){ SyStringInitFromBuf(pCurrent,0,0); return SXERR_EOF; /* End of input */ } zIn = SyStringData(pCursor); if( SXRET_OK != SyByteListFind(pCursor->zString,pCursor->nByte,"\r\n",&nPos) ){ /* Line not found,tell the caller to read more input from source */ SyStringDupPtr(pCurrent,pCursor); return SXERR_MORE; } pCurrent->zString = zIn; pCurrent->nByte = nPos; /* advance the cursor so we can call this routine again */ pCursor->zString = &zIn[nPos]; pCursor->nByte -= nPos; return SXRET_OK; } /* * Split a single MIME header into a name value pair. * This function return SXRET_OK,SXERR_CONTINUE on success. * Otherwise SXERR_NEXT is returned when a malformed header * is encountered. * Note: This function handle also mult-line headers. */ static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr,SyhttpHeader *pLast,const char *zLine,sxu32 nLen) { SyString *pName; sxu32 nPos; sxi32 rc; if( nLen < 1 ){ return SXERR_NEXT; } /* Check for multi-line header */ if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){ SyString *pTmp = &pLast->sValue; SyStringFullTrim(pTmp); if( pTmp->nByte == 0 ){ SyStringInitFromBuf(pTmp,zLine,nLen); }else{ /* Update header value length */ pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString); } /* Simply tell the caller to reset its states and get another line */ return SXERR_CONTINUE; } /* Split the header */ pName = &pHdr->sName; rc = SyByteFind(zLine,nLen,':',&nPos); if(rc != SXRET_OK ){ return SXERR_NEXT; /* Malformed header;Check the next entry */ } SyStringInitFromBuf(pName,zLine,nPos); SyStringFullTrim(pName); /* Extract a header value */ SyStringInitFromBuf(&pHdr->sValue,&zLine[nPos + 1],nLen - nPos - 1); /* Remove leading and trailing whitespaces */ SyStringFullTrim(&pHdr->sValue); return SXRET_OK; } /* * Extract all MIME headers associated with a HTTP request. * After processing the first line of a HTTP request,the following * routine is called in order to extract MIME headers. * This function return SXRET_OK on success,SXERR_MORE when it needs * more inputs. * Note: Any malformed header is simply discarded. */ static sxi32 VmHttpExtractHeaders(SyString *pRequest,SySet *pOut) { SyhttpHeader *pLast = 0; SyString sCurrent; SyhttpHeader sHdr; sxu8 bEol; sxi32 rc; if( SySetUsed(pOut) > 0 ){ pLast = (SyhttpHeader *)SySetAt(pOut,SySetUsed(pOut)-1); } bEol = FALSE; for(;;){ SyZero(&sHdr,sizeof(SyhttpHeader)); /* Extract a single line from the raw HTTP request */ rc = VmGetNextLine(pRequest,&sCurrent); if(rc != SXRET_OK ){ if( sCurrent.nByte < 1 ){ break; } bEol = TRUE; } /* Process the header */ if( SXRET_OK == VmHttpProcessOneHeader(&sHdr,pLast,sCurrent.zString,sCurrent.nByte)){ if( SXRET_OK != SySetPut(pOut,(const void *)&sHdr) ){ break; } /* Retrieve the last parsed header so we can handle multi-line header * in case we face one of them. */ pLast = (SyhttpHeader *)SySetPeek(pOut); } if( bEol ){ break; } } /* for(;;) */ return SXRET_OK; } /* * Process the first line of a HTTP request. * This routine perform the following operations * 1) Extract the HTTP method. * 2) Split the request URI to it's fields [ie: host,path,query,...]. * 3) Extract the HTTP protocol version. */ static sxi32 VmHttpProcessFirstLine( SyString *pRequest, /* Raw HTTP request */ sxi32 *pMethod, /* OUT: HTTP method */ SyhttpUri *pUri, /* OUT: Parse of the URI */ sxi32 *pProto /* OUT: HTTP protocol */ ) { static const char *azMethods[] = { "get","post","head","put"}; static const sxi32 aMethods[] = { HTTP_METHOD_GET,HTTP_METHOD_POST,HTTP_METHOD_HEAD,HTTP_METHOD_PUT}; const char *zIn,*zEnd,*zPtr; SyString sLine; sxu32 nLen; sxi32 rc; /* Extract the first line and update the pointer */ rc = VmGetNextLine(pRequest,&sLine); if( rc != SXRET_OK ){ return rc; } if ( sLine.nByte < 1 ){ /* Empty HTTP request */ return SXERR_EMPTY; } /* Delimit the line and ignore trailing and leading white spaces */ zIn = sLine.zString; zEnd = &zIn[sLine.nByte]; while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } /* Extract the HTTP method */ zPtr = zIn; while( zIn < zEnd && !SyisSpace(zIn[0]) ){ zIn++; } *pMethod = HTTP_METHOD_OTHR; if( zIn > zPtr ){ sxu32 i; nLen = (sxu32)(zIn-zPtr); for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){ if( SyStrnicmp(azMethods[i],zPtr,nLen) == 0 ){ *pMethod = aMethods[i]; break; } } } /* Jump trailing white spaces */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } /* Extract the request URI */ zPtr = zIn; while( zIn < zEnd && !SyisSpace(zIn[0]) ){ zIn++; } if( zIn > zPtr ){ nLen = (sxu32)(zIn-zPtr); /* Split raw URI to it's fields */ VmHttpSplitURI(pUri,zPtr,nLen); } /* Jump trailing white spaces */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } /* Extract the HTTP version */ zPtr = zIn; while( zIn < zEnd && !SyisSpace(zIn[0]) ){ zIn++; } *pProto = HTTP_PROTO_11; /* HTTP/1.1 */ rc = 1; if( zIn > zPtr ){ rc = SyStrnicmp(zPtr,"http/1.0",(sxu32)(zIn-zPtr)); } if( !rc ){ *pProto = HTTP_PROTO_10; /* HTTP/1.0 */ } return SXRET_OK; } /* * Tokenize,decode and split a raw query encoded as: "x-www-form-urlencoded" * into a name value pair. * Note that this encoding is implicit in GET based requests. * After the tokenization process,register the decoded queries * in the $_GET/$_POST/$_REQUEST superglobals arrays. */ static sxi32 VmHttpSplitEncodedQuery( ph7_vm *pVm, /* Target VM */ SyString *pQuery, /* Raw query to decode */ SyBlob *pWorker, /* Working buffer */ int is_post /* TRUE if we are dealing with a POST request */ ) { const char *zEnd = &pQuery->zString[pQuery->nByte]; const char *zIn = pQuery->zString; ph7_value *pGet,*pRequest; SyString sName,sValue; const char *zPtr; sxu32 nBlobOfft; /* Extract superglobals */ if( is_post ){ /* $_POST superglobal */ pGet = VmExtractSuper(&(*pVm),"_POST",sizeof("_POST")-1); }else{ /* $_GET superglobal */ pGet = VmExtractSuper(&(*pVm),"_GET",sizeof("_GET")-1); } pRequest = VmExtractSuper(&(*pVm),"_REQUEST",sizeof("_REQUEST")-1); /* Split up the raw query */ for(;;){ /* Jump leading white spaces */ while(zIn < zEnd && SyisSpace(zIn[0]) ){ zIn++; } if( zIn >= zEnd ){ break; } zPtr = zIn; while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){ zPtr++; } /* Reset the working buffer */ SyBlobReset(pWorker); /* Decode the entry */ SyUriDecode(zIn,(sxu32)(zPtr-zIn),PH7_VmBlobConsumer,pWorker,TRUE); /* Save the entry */ sName.nByte = SyBlobLength(pWorker); sValue.zString = 0; sValue.nByte = 0; if( zPtr < zEnd && zPtr[0] == '=' ){ zPtr++; zIn = zPtr; /* Store field value */ while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){ zPtr++; } if( zPtr > zIn ){ /* Decode the value */ nBlobOfft = SyBlobLength(pWorker); SyUriDecode(zIn,(sxu32)(zPtr-zIn),PH7_VmBlobConsumer,pWorker,TRUE); sValue.zString = (const char *)SyBlobDataAt(pWorker,nBlobOfft); sValue.nByte = SyBlobLength(pWorker) - nBlobOfft; } /* Synchronize pointers */ zIn = zPtr; } sName.zString = (const char *)SyBlobData(pWorker); /* Install the decoded query in the $_GET/$_REQUEST array */ if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){ VmHashmapInsert((ph7_hashmap *)pGet->x.pOther, sName.zString,(int)sName.nByte, sValue.zString,(int)sValue.nByte ); } if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){ VmHashmapInsert((ph7_hashmap *)pRequest->x.pOther, sName.zString,(int)sName.nByte, sValue.zString,(int)sValue.nByte ); } /* Advance the pointer */ zIn = &zPtr[1]; } /* All done*/ return SXRET_OK; } /* * Extract MIME header value from the given set. * Return header value on success. NULL otherwise. */ static SyString * VmHttpExtractHeaderValue(SySet *pSet,const char *zMime,sxu32 nByte) { SyhttpHeader *aMime,*pMime; SyString sMime; sxu32 n; SyStringInitFromBuf(&sMime,zMime,nByte); /* Point to the MIME entries */ aMime = (SyhttpHeader *)SySetBasePtr(pSet); /* Perform the lookup */ for( n = 0 ; n < SySetUsed(pSet) ; ++n ){ pMime = &aMime[n]; if( SyStringCmp(&sMime,&pMime->sName,SyStrnicmp) == 0 ){ /* Header found,return it's associated value */ return &pMime->sValue; } } /* No such MIME header */ return 0; } /* * Tokenize and decode a raw "Cookie:" MIME header into a name value pair * and insert it's fields [i.e name,value] in the $_COOKIE superglobal. */ static sxi32 VmHttpPorcessCookie(ph7_vm *pVm,SyBlob *pWorker,const char *zIn,sxu32 nByte) { const char *zPtr,*zDelimiter,*zEnd = &zIn[nByte]; SyString sName,sValue; ph7_value *pCookie; sxu32 nOfft; /* Make sure the $_COOKIE superglobal is available */ pCookie = VmExtractSuper(&(*pVm),"_COOKIE",sizeof("_COOKIE")-1); if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* $_COOKIE superglobal not available */ return SXERR_NOTFOUND; } for(;;){ /* Jump leading white spaces */ while( zIn < zEnd && SyisSpace(zIn[0]) ){ zIn++; } if( zIn >= zEnd ){ break; } /* Reset the working buffer */ SyBlobReset(pWorker); zDelimiter = zIn; /* Delimit the name[=value]; pair */ while( zDelimiter < zEnd && zDelimiter[0] != ';' ){ zDelimiter++; } zPtr = zIn; while( zPtr < zDelimiter && zPtr[0] != '=' ){ zPtr++; } /* Decode the cookie */ SyUriDecode(zIn,(sxu32)(zPtr-zIn),PH7_VmBlobConsumer,pWorker,TRUE); sName.nByte = SyBlobLength(pWorker); zPtr++; sValue.zString = 0; sValue.nByte = 0; if( zPtr < zDelimiter ){ /* Got a Cookie value */ nOfft = SyBlobLength(pWorker); SyUriDecode(zPtr,(sxu32)(zDelimiter-zPtr),PH7_VmBlobConsumer,pWorker,TRUE); SyStringInitFromBuf(&sValue,SyBlobDataAt(pWorker,nOfft),SyBlobLength(pWorker)-nOfft); } /* Synchronize pointers */ zIn = &zDelimiter[1]; /* Perform the insertion */ sName.zString = (const char *)SyBlobData(pWorker); VmHashmapInsert((ph7_hashmap *)pCookie->x.pOther, sName.zString,(int)sName.nByte, sValue.zString,(int)sValue.nByte ); } return SXRET_OK; } /* * Process a full HTTP request and populate the appropriate arrays * such as $_SERVER,$_GET,$_POST,$_COOKIE,$_REQUEST,... with the information * extracted from the raw HTTP request. As an extension Symisc introduced * the $_HEADER array which hold a copy of the processed HTTP MIME headers * and their associated values. [i.e: $_HEADER['Server'],$_HEADER['User-Agent'],...]. * This function return SXRET_OK on success. Any other return value indicates * a malformed HTTP request. */ static sxi32 VmHttpProcessRequest(ph7_vm *pVm,const char *zRequest,int nByte) { SyString *pName,*pValue,sRequest; /* Raw HTTP request */ ph7_value *pHeaderArray; /* $_HEADER superglobal (Symisc eXtension to the PHP specification)*/ SyhttpHeader *pHeader; /* MIME header */ SyhttpUri sUri; /* Parse of the raw URI*/ SyBlob sWorker; /* General purpose working buffer */ SySet sHeader; /* MIME headers set */ sxi32 iMethod; /* HTTP method [i.e: GET,POST,HEAD...]*/ sxi32 iVer; /* HTTP protocol version */ sxi32 rc; SyStringInitFromBuf(&sRequest,zRequest,nByte); SySetInit(&sHeader,&pVm->sAllocator,sizeof(SyhttpHeader)); SyBlobInit(&sWorker,&pVm->sAllocator); /* Ignore leading and trailing white spaces*/ SyStringFullTrim(&sRequest); /* Process the first line */ rc = VmHttpProcessFirstLine(&sRequest,&iMethod,&sUri,&iVer); if( rc != SXRET_OK ){ return rc; } /* Process MIME headers */ VmHttpExtractHeaders(&sRequest,&sHeader); /* * Setup $_SERVER environments */ /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "SERVER_PROTOCOL", iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1", sizeof("HTTP/1.1")-1 ); /* 'REQUEST_METHOD': Which request method was used to access the page */ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "REQUEST_METHOD", iMethod == HTTP_METHOD_GET ? "GET" : (iMethod == HTTP_METHOD_POST ? "POST": (iMethod == HTTP_METHOD_PUT ? "PUT" : (iMethod == HTTP_METHOD_HEAD ? "HEAD" : "OTHER"))), -1 /* Compute attribute length automatically */ ); if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){ pValue = &sUri.sQuery; /* 'QUERY_STRING': The query string, if any, via which the page was accessed */ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "QUERY_STRING", pValue->zString, pValue->nByte ); /* Decoded the raw query */ VmHttpSplitEncodedQuery(&(*pVm),pValue,&sWorker,FALSE); } /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */ pValue = &sUri.sRaw; ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "REQUEST_URI", pValue->zString, pValue->nByte ); /* * 'PATH_INFO' * 'ORIG_PATH_INFO' * Contains any client-provided pathname information trailing the actual script filename but preceding * the query string, if available. For instance, if the current script was accessed via the URL * http://www.example.com/php/path_info.php/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain * /some/stuff. */ pValue = &sUri.sPath; ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "PATH_INFO", pValue->zString, pValue->nByte ); ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "ORIG_PATH_INFO", pValue->zString, pValue->nByte ); /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */ pValue = VmHttpExtractHeaderValue(&sHeader,"Accept",sizeof("Accept")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_ACCEPT", pValue->zString, pValue->nByte ); } /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Accept-Charset",sizeof("Accept-Charset")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_ACCEPT_CHARSET", pValue->zString, pValue->nByte ); } /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Accept-Encoding",sizeof("Accept-Encoding")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_ACCEPT_ENCODING", pValue->zString, pValue->nByte ); } /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */ pValue = VmHttpExtractHeaderValue(&sHeader,"Accept-Language",sizeof("Accept-Language")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_ACCEPT_LANGUAGE", pValue->zString, pValue->nByte ); } /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Connection",sizeof("Connection")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_CONNECTION", pValue->zString, pValue->nByte ); } /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Host",sizeof("Host")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_HOST", pValue->zString, pValue->nByte ); } /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"Referer",sizeof("Referer")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_REFERER", pValue->zString, pValue->nByte ); } /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */ pValue = VmHttpExtractHeaderValue(&sHeader,"User-Agent",sizeof("User-Agent")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "HTTP_USER_AGENT", pValue->zString, pValue->nByte ); } /* 'PHP_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization' * header sent by the client (which you should then use to make the appropriate validation). */ pValue = VmHttpExtractHeaderValue(&sHeader,"Authorization",sizeof("Authorization")-1); if( pValue ){ ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "PHP_AUTH_DIGEST", pValue->zString, pValue->nByte ); ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR, "PHP_AUTH", pValue->zString, pValue->nByte ); } /* Install all clients HTTP headers in the $_HEADER superglobal */ pHeaderArray = VmExtractSuper(&(*pVm),"_HEADER",sizeof("_HEADER")-1); /* Iterate throw the available MIME headers*/ SySetResetCursor(&sHeader); pHeader = 0; /* stupid cc warning */ while( SXRET_OK == SySetGetNextEntry(&sHeader,(void **)&pHeader) ){ pName = &pHeader->sName; pValue = &pHeader->sValue; if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){ /* Insert the MIME header and it's associated value */ VmHashmapInsert((ph7_hashmap *)pHeaderArray->x.pOther, pName->zString,(int)pName->nByte, pValue->zString,(int)pValue->nByte ); } if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString,"Cookie",sizeof("Cookie")-1) == 0 && pValue->nByte > 0){ /* Process the name=value pair and insert them in the $_COOKIE superglobal array */ VmHttpPorcessCookie(&(*pVm),&sWorker,pValue->zString,pValue->nByte); } } if( iMethod == HTTP_METHOD_POST ){ /* Extract raw POST data */ pValue = VmHttpExtractHeaderValue(&sHeader,"Content-Type",sizeof("Content-Type") - 1); if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 && SyMemcmp("application/x-www-form-urlencoded",pValue->zString,pValue->nByte) == 0 ){ /* Extract POST data length */ pValue = VmHttpExtractHeaderValue(&sHeader,"Content-Length",sizeof("Content-Length") - 1); if( pValue ){ sxi32 iLen = 0; /* POST data length */ SyStrToInt32(pValue->zString,pValue->nByte,(void *)&iLen,0); if( iLen > 0 ){ /* Remove leading and trailing white spaces */ SyStringFullTrim(&sRequest); if( (int)sRequest.nByte > iLen ){ sRequest.nByte = (sxu32)iLen; } /* Decode POST data now */ VmHttpSplitEncodedQuery(&(*pVm),&sRequest,&sWorker,TRUE); } } } } /* All done,clean-up the mess left behind */ SySetRelease(&sHeader); SyBlobRelease(&sWorker); return SXRET_OK; } /* * ---------------------------------------------------------- * File: vfs.c * MD5: bfe61218da83cefd9f25149de0610d3b * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: vfs.c v2.1 Win7 2012-05-24 01:18 devel $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* * This file implement a virtual file systems (VFS) for the PH7 engine. */ /* * Given a string containing the path of a file or directory, this function * return the parent directory's path. */ PH7_PRIVATE const char * PH7_ExtractDirName(const char *zPath,int nByte,int *pLen) { const char *zEnd = &zPath[nByte - 1]; int c,d; c = d = '/'; #ifdef __WINNT__ d = '\\'; #endif while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ zEnd--; } *pLen = (int)(zEnd-zPath); #ifdef __WINNT__ if( (*pLen) == (int)sizeof(char) && zPath[0] == '/' ){ /* Normalize path on windows */ return "\\"; } #endif if( zEnd == zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d) ){ /* No separator,return "." as the current directory */ *pLen = sizeof(char); return "."; } if( (*pLen) == 0 ){ *pLen = sizeof(char); #ifdef __WINNT__ return "\\"; #else return "/"; #endif } return zPath; } /* * Omit the vfs layer implementation from the built if the PH7_DISABLE_BUILTIN_FUNC directive is defined. */ #ifndef PH7_DISABLE_BUILTIN_FUNC /* * bool chdir(string $directory) * Change the current directory. * Parameters * $directory * The new current directory * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_chdir(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xChdir == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xChdir(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool chroot(string $directory) * Change the root directory. * Parameters * $directory * The path to change the root directory to * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_chroot(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xChroot == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xChroot(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * string getcwd(void) * Gets the current working directory. * Parameters * None * Return * Returns the current working directory on success, or FALSE on failure. */ static int PH7_vfs_getcwd(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vfs *pVfs; int rc; /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xGetcwd == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } ph7_result_string(pCtx,"",0); /* Perform the requested operation */ rc = pVfs->xGetcwd(pCtx); if( rc != PH7_OK ){ /* Error,return FALSE */ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * bool rmdir(string $directory) * Removes directory. * Parameters * $directory * The path to the directory * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_rmdir(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xRmdir == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xRmdir(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool is_dir(string $filename) * Tells whether the given filename is a directory. * Parameters * $filename * Path to the file. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_is_dir(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xIsdir == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xIsdir(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool mkdir(string $pathname[,int $mode = 0777 [,bool $recursive = false]) * Make a directory. * Parameters * $pathname * The directory path. * $mode * The mode is 0777 by default, which means the widest possible access. * Note: * mode is ignored on Windows. * Note that you probably want to specify the mode as an octal number, which means * it should have a leading zero. The mode is also modified by the current umask * which you can change using umask(). * $recursive * Allows the creation of nested directories specified in the pathname. * Defaults to FALSE. (Not used) * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_mkdir(ph7_context *pCtx,int nArg,ph7_value **apArg) { int iRecursive = 0; const char *zPath; ph7_vfs *pVfs; int iMode,rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xMkdir == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); #ifdef __WINNT__ iMode = 0; #else /* Assume UNIX */ iMode = 0777; #endif if( nArg > 1 ){ iMode = ph7_value_to_int(apArg[1]); if( nArg > 2 ){ iRecursive = ph7_value_to_bool(apArg[2]); } } /* Perform the requested operation */ rc = pVfs->xMkdir(zPath,iMode,iRecursive); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool rename(string $oldname,string $newname) * Attempts to rename oldname to newname. * Parameters * $oldname * Old name. * $newname * New name. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_rename(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zOld,*zNew; ph7_vfs *pVfs; int rc; if( nArg < 2 || !ph7_value_is_string(apArg[0]) || !ph7_value_is_string(apArg[1]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xRename == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ zOld = ph7_value_to_string(apArg[0],0); zNew = ph7_value_to_string(apArg[1],0); rc = pVfs->xRename(zOld,zNew); /* IO result */ ph7_result_bool(pCtx,rc == PH7_OK ); return PH7_OK; } /* * string realpath(string $path) * Returns canonicalized absolute pathname. * Parameters * $path * Target path. * Return * Canonicalized absolute pathname on success. or FALSE on failure. */ static int PH7_vfs_realpath(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xRealpath == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Set an empty string untnil the underlying OS interface change that */ ph7_result_string(pCtx,"",0); /* Perform the requested operation */ zPath = ph7_value_to_string(apArg[0],0); rc = pVfs->xRealpath(zPath,pCtx); if( rc != PH7_OK ){ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * int sleep(int $seconds) * Delays the program execution for the given number of seconds. * Parameters * $seconds * Halt time in seconds. * Return * Zero on success or FALSE on failure. */ static int PH7_vfs_sleep(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vfs *pVfs; int rc,nSleep; if( nArg < 1 || !ph7_value_is_int(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xSleep == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Amount to sleep */ nSleep = ph7_value_to_int(apArg[0]); if( nSleep < 0 ){ /* Invalid value,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation (Microseconds) */ rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC)); if( rc != PH7_OK ){ /* Return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Return zero */ ph7_result_int(pCtx,0); } return PH7_OK; } /* * void usleep(int $micro_seconds) * Delays program execution for the given number of micro seconds. * Parameters * $micro_seconds * Halt time in micro seconds. A micro second is one millionth of a second. * Return * None. */ static int PH7_vfs_usleep(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vfs *pVfs; int nSleep; if( nArg < 1 || !ph7_value_is_int(apArg[0]) ){ /* Missing/Invalid argument,return immediately */ return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xSleep == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS", ph7_function_name(pCtx) ); return PH7_OK; } /* Amount to sleep */ nSleep = ph7_value_to_int(apArg[0]); if( nSleep < 0 ){ /* Invalid value,return immediately */ return PH7_OK; } /* Perform the requested operation (Microseconds) */ pVfs->xSleep((unsigned int)nSleep); return PH7_OK; } /* * bool unlink (string $filename) * Delete a file. * Parameters * $filename * Path to the file. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_unlink(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xUnlink == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xUnlink(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool chmod(string $filename,int $mode) * Attempts to change the mode of the specified file to that given in mode. * Parameters * $filename * Path to the file. * $mode * Mode (Must be an integer) * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_chmod(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int iMode; int rc; if( nArg < 2 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xChmod == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Extract the mode */ iMode = ph7_value_to_int(apArg[1]); /* Perform the requested operation */ rc = pVfs->xChmod(zPath,iMode); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool chown(string $filename,string $user) * Attempts to change the owner of the file filename to user user. * Parameters * $filename * Path to the file. * $user * Username. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_chown(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath,*zUser; ph7_vfs *pVfs; int rc; if( nArg < 2 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xChown == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Extract the user */ zUser = ph7_value_to_string(apArg[1],0); /* Perform the requested operation */ rc = pVfs->xChown(zPath,zUser); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool chgrp(string $filename,string $group) * Attempts to change the group of the file filename to group. * Parameters * $filename * Path to the file. * $group * groupname. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_chgrp(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath,*zGroup; ph7_vfs *pVfs; int rc; if( nArg < 2 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xChgrp == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Extract the user */ zGroup = ph7_value_to_string(apArg[1],0); /* Perform the requested operation */ rc = pVfs->xChgrp(zPath,zGroup); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * int64 disk_free_space(string $directory) * Returns available space on filesystem or disk partition. * Parameters * $directory * A directory of the filesystem or disk partition. * Return * Returns the number of available bytes as a 64-bit integer or FALSE on failure. */ static int PH7_vfs_disk_free_space(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_int64 iSize; ph7_vfs *pVfs; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xFreeSpace == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ iSize = pVfs->xFreeSpace(zPath); /* IO return value */ ph7_result_int64(pCtx,iSize); return PH7_OK; } /* * int64 disk_total_space(string $directory) * Returns the total size of a filesystem or disk partition. * Parameters * $directory * A directory of the filesystem or disk partition. * Return * Returns the number of available bytes as a 64-bit integer or FALSE on failure. */ static int PH7_vfs_disk_total_space(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_int64 iSize; ph7_vfs *pVfs; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xTotalSpace == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ iSize = pVfs->xTotalSpace(zPath); /* IO return value */ ph7_result_int64(pCtx,iSize); return PH7_OK; } /* * bool file_exists(string $filename) * Checks whether a file or directory exists. * Parameters * $filename * Path to the file. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_file_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xFileExists == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xFileExists(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * int64 file_size(string $filename) * Gets the size for the given file. * Parameters * $filename * Path to the file. * Return * File size on success or FALSE on failure. */ static int PH7_vfs_file_size(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_int64 iSize; ph7_vfs *pVfs; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xFileSize == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ iSize = pVfs->xFileSize(zPath); /* IO return value */ ph7_result_int64(pCtx,iSize); return PH7_OK; } /* * int64 fileatime(string $filename) * Gets the last access time of the given file. * Parameters * $filename * Path to the file. * Return * File atime on success or FALSE on failure. */ static int PH7_vfs_file_atime(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_int64 iTime; ph7_vfs *pVfs; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xFileAtime == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ iTime = pVfs->xFileAtime(zPath); /* IO return value */ ph7_result_int64(pCtx,iTime); return PH7_OK; } /* * int64 filemtime(string $filename) * Gets file modification time. * Parameters * $filename * Path to the file. * Return * File mtime on success or FALSE on failure. */ static int PH7_vfs_file_mtime(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_int64 iTime; ph7_vfs *pVfs; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xFileMtime == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ iTime = pVfs->xFileMtime(zPath); /* IO return value */ ph7_result_int64(pCtx,iTime); return PH7_OK; } /* * int64 filectime(string $filename) * Gets inode change time of file. * Parameters * $filename * Path to the file. * Return * File ctime on success or FALSE on failure. */ static int PH7_vfs_file_ctime(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_int64 iTime; ph7_vfs *pVfs; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xFileCtime == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ iTime = pVfs->xFileCtime(zPath); /* IO return value */ ph7_result_int64(pCtx,iTime); return PH7_OK; } /* * bool is_file(string $filename) * Tells whether the filename is a regular file. * Parameters * $filename * Path to the file. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_is_file(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xIsfile == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xIsfile(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool is_link(string $filename) * Tells whether the filename is a symbolic link. * Parameters * $filename * Path to the file. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_is_link(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xIslink == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xIslink(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool is_readable(string $filename) * Tells whether a file exists and is readable. * Parameters * $filename * Path to the file. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_is_readable(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xReadable == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xReadable(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool is_writable(string $filename) * Tells whether the filename is writable. * Parameters * $filename * Path to the file. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_is_writable(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xWritable == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xWritable(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool is_executable(string $filename) * Tells whether the filename is executable. * Parameters * $filename * Path to the file. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_is_executable(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xExecutable == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xExecutable(zPath); /* IO return value */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * string filetype(string $filename) * Gets file type. * Parameters * $filename * Path to the file. * Return * The type of the file. Possible values are fifo, char, dir, block, link * file, socket and unknown. */ static int PH7_vfs_filetype(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; ph7_vfs *pVfs; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return 'unknown' */ ph7_result_string(pCtx,"unknown",sizeof("unknown")-1); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xFiletype == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the desired directory */ zPath = ph7_value_to_string(apArg[0],0); /* Set the empty string as the default return value */ ph7_result_string(pCtx,"",0); /* Perform the requested operation */ pVfs->xFiletype(zPath,pCtx); return PH7_OK; } /* * array stat(string $filename) * Gives information about a file. * Parameters * $filename * Path to the file. * Return * An associative array on success holding the following entries on success * 0 dev device number * 1 ino inode number (zero on windows) * 2 mode inode protection mode * 3 nlink number of links * 4 uid userid of owner (zero on windows) * 5 gid groupid of owner (zero on windows) * 6 rdev device type, if inode device * 7 size size in bytes * 8 atime time of last access (Unix timestamp) * 9 mtime time of last modification (Unix timestamp) * 10 ctime time of last inode change (Unix timestamp) * 11 blksize blocksize of filesystem IO (zero on windows) * 12 blocks number of 512-byte blocks allocated. * Note: * FALSE is returned on failure. */ static int PH7_vfs_stat(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pValue; const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xStat == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Create the array and the working value */ pArray = ph7_context_new_array(pCtx); pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xStat(zPath,pArray,pValue); if( rc != PH7_OK ){ /* IO error,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Return the associative array */ ph7_result_value(pCtx,pArray); } /* Don't worry about freeing memory here,everything will be released * automatically as soon we return from this function. */ return PH7_OK; } /* * array lstat(string $filename) * Gives information about a file or symbolic link. * Parameters * $filename * Path to the file. * Return * An associative array on success holding the following entries on success * 0 dev device number * 1 ino inode number (zero on windows) * 2 mode inode protection mode * 3 nlink number of links * 4 uid userid of owner (zero on windows) * 5 gid groupid of owner (zero on windows) * 6 rdev device type, if inode device * 7 size size in bytes * 8 atime time of last access (Unix timestamp) * 9 mtime time of last modification (Unix timestamp) * 10 ctime time of last inode change (Unix timestamp) * 11 blksize blocksize of filesystem IO (zero on windows) * 12 blocks number of 512-byte blocks allocated. * Note: * FALSE is returned on failure. */ static int PH7_vfs_lstat(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pValue; const char *zPath; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xlStat == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Create the array and the working value */ pArray = ph7_context_new_array(pCtx); pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zPath = ph7_value_to_string(apArg[0],0); /* Perform the requested operation */ rc = pVfs->xlStat(zPath,pArray,pValue); if( rc != PH7_OK ){ /* IO error,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Return the associative array */ ph7_result_value(pCtx,pArray); } /* Don't worry about freeing memory here,everything will be released * automatically as soon we return from this function. */ return PH7_OK; } /* * string getenv(string $varname) * Gets the value of an environment variable. * Parameters * $varname * The variable name. * Return * Returns the value of the environment variable varname, or FALSE if the environment * variable varname does not exist. */ static int PH7_vfs_getenv(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zEnv; ph7_vfs *pVfs; int iLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xGetenv == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the environment variable */ zEnv = ph7_value_to_string(apArg[0],&iLen); /* Set a boolean FALSE as the default return value */ ph7_result_bool(pCtx,0); if( iLen < 1 ){ /* Empty string */ return PH7_OK; } /* Perform the requested operation */ pVfs->xGetenv(zEnv,pCtx); return PH7_OK; } /* * bool putenv(string $settings) * Set the value of an environment variable. * Parameters * $setting * The setting, like "FOO=BAR" * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_putenv(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zName,*zValue; char *zSettings,*zEnd; ph7_vfs *pVfs; int iLen,rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the setting variable */ zSettings = (char *)ph7_value_to_string(apArg[0],&iLen); if( iLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Parse the setting */ zEnd = &zSettings[iLen]; zValue = 0; zName = zSettings; while( zSettings < zEnd ){ if( zSettings[0] == '=' ){ /* Null terminate the name */ zSettings[0] = 0; zValue = &zSettings[1]; break; } zSettings++; } /* Install the environment variable in the $_Env array */ if( zValue == 0 || zName[0] == 0 || zValue >= zEnd || zName >= zValue ){ /* Invalid settings,retun FALSE */ ph7_result_bool(pCtx,0); if( zSettings < zEnd ){ zSettings[0] = '='; } return PH7_OK; } ph7_vm_config(pCtx->pVm,PH7_VM_CONFIG_ENV_ATTR,zName,zValue,(int)(zEnd-zValue)); /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xSetenv == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); zSettings[0] = '='; return PH7_OK; } /* Perform the requested operation */ rc = pVfs->xSetenv(zName,zValue); ph7_result_bool(pCtx,rc == PH7_OK ); zSettings[0] = '='; return PH7_OK; } /* * bool touch(string $filename[,int64 $time = time()[,int64 $atime]]) * Sets access and modification time of file. * Note: On windows * If the file does not exists,it will not be created. * Parameters * $filename * The name of the file being touched. * $time * The touch time. If time is not supplied, the current system time is used. * $atime * If present, the access time of the given filename is set to the value of atime. * Otherwise, it is set to the value passed to the time parameter. If neither are * present, the current system time is used. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_touch(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_int64 nTime,nAccess; const char *zFile; ph7_vfs *pVfs; int rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xTouch == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ nTime = nAccess = -1; zFile = ph7_value_to_string(apArg[0],0); if( nArg > 1 ){ nTime = ph7_value_to_int64(apArg[1]); if( nArg > 2 ){ nAccess = ph7_value_to_int64(apArg[1]); }else{ nAccess = nTime; } } rc = pVfs->xTouch(zFile,nTime,nAccess); /* IO result */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * Path processing functions that do not need access to the VFS layer * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * string dirname(string $path) * Returns parent directory's path. * Parameters * $path * Target path. * On Windows, both slash (/) and backslash (\) are used as directory separator character. * In other environments, it is the forward slash (/). * Return * The path of the parent directory. If there are no slashes in path, a dot ('.') * is returned, indicating the current directory. */ static int PH7_builtin_dirname(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath,*zDir; int iLen,iDirlen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Point to the target path */ zPath = ph7_value_to_string(apArg[0],&iLen); if( iLen < 1 ){ /* Reuturn "." */ ph7_result_string(pCtx,".",sizeof(char)); return PH7_OK; } /* Perform the requested operation */ zDir = PH7_ExtractDirName(zPath,iLen,&iDirlen); /* Return directory name */ ph7_result_string(pCtx,zDir,iDirlen); return PH7_OK; } /* * string basename(string $path[, string $suffix ]) * Returns trailing name component of path. * Parameters * $path * Target path. * On Windows, both slash (/) and backslash (\) are used as directory separator character. * In other environments, it is the forward slash (/). * $suffix * If the name component ends in suffix this will also be cut off. * Return * The base name of the given path. */ static int PH7_builtin_basename(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath,*zBase,*zEnd; int c,d,iLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } c = d = '/'; #ifdef __WINNT__ d = '\\'; #endif /* Point to the target path */ zPath = ph7_value_to_string(apArg[0],&iLen); if( iLen < 1 ){ /* Empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Perform the requested operation */ zEnd = &zPath[iLen - 1]; /* Ignore trailing '/' */ while( zEnd > zPath && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){ zEnd--; } iLen = (int)(&zEnd[1]-zPath); while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ zEnd--; } zBase = (zEnd > zPath) ? &zEnd[1] : zPath; zEnd = &zPath[iLen]; if( nArg > 1 && ph7_value_is_string(apArg[1]) ){ const char *zSuffix; int nSuffix; /* Strip suffix */ zSuffix = ph7_value_to_string(apArg[1],&nSuffix); if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix],zSuffix,nSuffix) == 0 ){ zEnd -= nSuffix; } } /* Store the basename */ ph7_result_string(pCtx,zBase,(int)(zEnd-zBase)); return PH7_OK; } /* * value pathinfo(string $path [,int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ]) * Returns information about a file path. * Parameter * $path * The path to be parsed. * $options * If present, specifies a specific element to be returned; one of * PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION or PATHINFO_FILENAME. * Return * If the options parameter is not passed, an associative array containing the following * elements is returned: dirname, basename, extension (if any), and filename. * If options is present, returns a string containing the requested element. */ typedef struct path_info path_info; struct path_info { SyString sDir; /* Directory [i.e: /var/www] */ SyString sBasename; /* Basename [i.e httpd.conf] */ SyString sExtension; /* File extension [i.e xml,pdf..] */ SyString sFilename; /* Filename */ }; /* * Extract path fields. */ static sxi32 ExtractPathInfo(const char *zPath,int nByte,path_info *pOut) { const char *zPtr,*zEnd = &zPath[nByte - 1]; SyString *pCur; int c,d; c = d = '/'; #ifdef __WINNT__ d = '\\'; #endif /* Zero the structure */ SyZero(pOut,sizeof(path_info)); /* Handle special case */ if( nByte == sizeof(char) && ( (int)zPath[0] == c || (int)zPath[0] == d ) ){ #ifdef __WINNT__ SyStringInitFromBuf(&pOut->sDir,"\\",sizeof(char)); #else SyStringInitFromBuf(&pOut->sDir,"/",sizeof(char)); #endif return SXRET_OK; } /* Extract the basename */ while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ zEnd--; } zPtr = (zEnd > zPath) ? &zEnd[1] : zPath; zEnd = &zPath[nByte]; /* dirname */ pCur = &pOut->sDir; SyStringInitFromBuf(pCur,zPath,zPtr-zPath); if( pCur->nByte > 1 ){ SyStringTrimTrailingChar(pCur,'/'); #ifdef __WINNT__ SyStringTrimTrailingChar(pCur,'\\'); #endif }else if( (int)zPath[0] == c || (int)zPath[0] == d ){ #ifdef __WINNT__ SyStringInitFromBuf(&pOut->sDir,"\\",sizeof(char)); #else SyStringInitFromBuf(&pOut->sDir,"/",sizeof(char)); #endif } /* basename/filename */ pCur = &pOut->sBasename; SyStringInitFromBuf(pCur,zPtr,zEnd-zPtr); SyStringTrimLeadingChar(pCur,'/'); #ifdef __WINNT__ SyStringTrimLeadingChar(pCur,'\\'); #endif SyStringDupPtr(&pOut->sFilename,pCur); if( pCur->nByte > 0 ){ /* extension */ zEnd--; while( zEnd > pCur->zString /*basename*/ && zEnd[0] != '.' ){ zEnd--; } if( zEnd > pCur->zString ){ zEnd++; /* Jump leading dot */ SyStringInitFromBuf(&pOut->sExtension,zEnd,&zPath[nByte]-zEnd); /* Fix filename */ pCur = &pOut->sFilename; if( pCur->nByte > SyStringLength(&pOut->sExtension) ){ pCur->nByte -= 1 + SyStringLength(&pOut->sExtension); } } } return SXRET_OK; } /* * value pathinfo(string $path [,int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ]) * See block comment above. */ static int PH7_builtin_pathinfo(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zPath; path_info sInfo; SyString *pComp; int iLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Point to the target path */ zPath = ph7_value_to_string(apArg[0],&iLen); if( iLen < 1 ){ /* Empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract path info */ ExtractPathInfo(zPath,iLen,&sInfo); if( nArg > 1 && ph7_value_is_int(apArg[1]) ){ /* Return path component */ int nComp = ph7_value_to_int(apArg[1]); switch(nComp){ case 1: /* PATHINFO_DIRNAME */ pComp = &sInfo.sDir; if( pComp->nByte > 0 ){ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); }else{ /* Expand the empty string */ ph7_result_string(pCtx,"",0); } break; case 2: /*PATHINFO_BASENAME*/ pComp = &sInfo.sBasename; if( pComp->nByte > 0 ){ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); }else{ /* Expand the empty string */ ph7_result_string(pCtx,"",0); } break; case 3: /*PATHINFO_EXTENSION*/ pComp = &sInfo.sExtension; if( pComp->nByte > 0 ){ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); }else{ /* Expand the empty string */ ph7_result_string(pCtx,"",0); } break; case 4: /*PATHINFO_FILENAME*/ pComp = &sInfo.sFilename; if( pComp->nByte > 0 ){ ph7_result_string(pCtx,pComp->zString,(int)pComp->nByte); }else{ /* Expand the empty string */ ph7_result_string(pCtx,"",0); } break; default: /* Expand the empty string */ ph7_result_string(pCtx,"",0); break; } }else{ /* Return an associative array */ ph7_value *pArray,*pValue; pArray = ph7_context_new_array(pCtx); pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ /* Out of mem,return NULL */ ph7_result_bool(pCtx,0); return PH7_OK; } /* dirname */ pComp = &sInfo.sDir; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); /* Perform the insertion */ ph7_array_add_strkey_elem(pArray,"dirname",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); /* basername */ pComp = &sInfo.sBasename; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); /* Perform the insertion */ ph7_array_add_strkey_elem(pArray,"basename",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); /* extension */ pComp = &sInfo.sExtension; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); /* Perform the insertion */ ph7_array_add_strkey_elem(pArray,"extension",pValue); /* Will make it's own copy */ } /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); /* filename */ pComp = &sInfo.sFilename; if( pComp->nByte > 0 ){ ph7_value_string(pValue,pComp->zString,(int)pComp->nByte); /* Perform the insertion */ ph7_array_add_strkey_elem(pArray,"filename",pValue); /* Will make it's own copy */ } /* Return the created array */ ph7_result_value(pCtx,pArray); /* Don't worry about freeing memory, everything will be released * automatically as soon we return from this foreign function. */ } return PH7_OK; } /* * Globbing implementation extracted from the sqlite3 source tree. * Original author: D. Richard Hipp (http://www.sqlite.org) * Status: Public Domain */ typedef unsigned char u8; /* An array to map all upper-case characters into their corresponding ** lower-case character. ** ** SQLite only considers US-ASCII (or EBCDIC) characters. We do not ** handle case conversions for the UTF character set since the tables ** involved are nearly as big or bigger than SQLite itself. */ static const unsigned char sqlite3UpperToLower[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, 252,253,254,255 }; #define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; } /* ** Assuming zIn points to the first byte of a UTF-8 character, ** advance zIn to point to the first byte of the next UTF-8 character. */ #define SQLITE_SKIP_UTF8(zIn) { \ if( (*(zIn++))>=0xc0 ){ \ while( (*zIn & 0xc0)==0x80 ){ zIn++; } \ } \ } /* ** Compare two UTF-8 strings for equality where the first string can ** potentially be a "glob" expression. Return true (1) if they ** are the same and false (0) if they are different. ** ** Globbing rules: ** ** '*' Matches any sequence of zero or more characters. ** ** '?' Matches exactly one character. ** ** [...] Matches one character from the enclosed list of ** characters. ** ** [^...] Matches one character not in the enclosed list. ** ** With the [...] and [^...] matching, a ']' character can be included ** in the list by making it the first character after '[' or '^'. A ** range of characters can be specified using '-'. Example: ** "[a-z]" matches any single lower-case letter. To match a '-', make ** it the last character in the list. ** ** This routine is usually quick, but can be N**2 in the worst case. ** ** Hints: to match '*' or '?', put them in "[]". Like this: ** ** abc[*]xyz Matches "abc*xyz" only */ static int patternCompare( const u8 *zPattern, /* The glob pattern */ const u8 *zString, /* The string to compare against the glob */ const int esc, /* The escape character */ int noCase ){ int c, c2; int invert; int seen; u8 matchOne = '?'; u8 matchAll = '*'; u8 matchSet = '['; int prevEscape = 0; /* True if the previous character was 'escape' */ if( !zPattern || !zString ) return 0; while( (c = PH7_Utf8Read(zPattern,0,&zPattern))!=0 ){ if( !prevEscape && c==matchAll ){ while( (c=PH7_Utf8Read(zPattern,0,&zPattern)) == matchAll || c == matchOne ){ if( c==matchOne && PH7_Utf8Read(zString, 0, &zString)==0 ){ return 0; } } if( c==0 ){ return 1; }else if( c==esc ){ c = PH7_Utf8Read(zPattern, 0, &zPattern); if( c==0 ){ return 0; } }else if( c==matchSet ){ if( (esc==0) || (matchSet<0x80) ) return 0; while( *zString && patternCompare(&zPattern[-1],zString,esc,noCase)==0 ){ SQLITE_SKIP_UTF8(zString); } return *zString!=0; } while( (c2 = PH7_Utf8Read(zString,0,&zString))!=0 ){ if( noCase ){ GlogUpperToLower(c2); GlogUpperToLower(c); while( c2 != 0 && c2 != c ){ c2 = PH7_Utf8Read(zString, 0, &zString); GlogUpperToLower(c2); } }else{ while( c2 != 0 && c2 != c ){ c2 = PH7_Utf8Read(zString, 0, &zString); } } if( c2==0 ) return 0; if( patternCompare(zPattern,zString,esc,noCase) ) return 1; } return 0; }else if( !prevEscape && c==matchOne ){ if( PH7_Utf8Read(zString, 0, &zString)==0 ){ return 0; } }else if( c==matchSet ){ int prior_c = 0; if( esc == 0 ) return 0; seen = 0; invert = 0; c = PH7_Utf8Read(zString, 0, &zString); if( c==0 ) return 0; c2 = PH7_Utf8Read(zPattern, 0, &zPattern); if( c2=='^' ){ invert = 1; c2 = PH7_Utf8Read(zPattern, 0, &zPattern); } if( c2==']' ){ if( c==']' ) seen = 1; c2 = PH7_Utf8Read(zPattern, 0, &zPattern); } while( c2 && c2!=']' ){ if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){ c2 = PH7_Utf8Read(zPattern, 0, &zPattern); if( c>=prior_c && c<=c2 ) seen = 1; prior_c = 0; }else{ if( c==c2 ){ seen = 1; } prior_c = c2; } c2 = PH7_Utf8Read(zPattern, 0, &zPattern); } if( c2==0 || (seen ^ invert)==0 ){ return 0; } }else if( esc==c && !prevEscape ){ prevEscape = 1; }else{ c2 = PH7_Utf8Read(zString, 0, &zString); if( noCase ){ GlogUpperToLower(c); GlogUpperToLower(c2); } if( c!=c2 ){ return 0; } prevEscape = 0; } } return *zString==0; } /* * Wrapper around patternCompare() defined above. * See block comment above for more information. */ static int Glob(const unsigned char *zPattern,const unsigned char *zString,int iEsc,int CaseCompare) { int rc; if( iEsc < 0 ){ iEsc = '\\'; } rc = patternCompare(zPattern,zString,iEsc,CaseCompare); return rc; } /* * bool fnmatch(string $pattern,string $string[,int $flags = 0 ]) * Match filename against a pattern. * Parameters * $pattern * The shell wildcard pattern. * $string * The tested string. * $flags * A list of possible flags: * FNM_NOESCAPE Disable backslash escaping. * FNM_PATHNAME Slash in string only matches slash in the given pattern. * FNM_PERIOD Leading period in string must be exactly matched by period in the given pattern. * FNM_CASEFOLD Caseless match. * Return * TRUE if there is a match, FALSE otherwise. */ static int PH7_builtin_fnmatch(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zPattern; int iEsc = '\\'; int noCase = 0; int rc; if( nArg < 2 || !ph7_value_is_string(apArg[0]) || !ph7_value_is_string(apArg[1]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the pattern and the string */ zPattern = ph7_value_to_string(apArg[0],0); zString = ph7_value_to_string(apArg[1],0); /* Extract the flags if avaialble */ if( nArg > 2 && ph7_value_is_int(apArg[2]) ){ rc = ph7_value_to_int(apArg[2]); if( rc & 0x01 /*FNM_NOESCAPE*/){ iEsc = 0; } if( rc & 0x08 /*FNM_CASEFOLD*/){ noCase = 1; } } /* Go globbing */ rc = Glob((const unsigned char *)zPattern,(const unsigned char *)zString,iEsc,noCase); /* Globbing result */ ph7_result_bool(pCtx,rc); return PH7_OK; } /* * bool strglob(string $pattern,string $string) * Match string against a pattern. * Parameters * $pattern * The shell wildcard pattern. * $string * The tested string. * Return * TRUE if there is a match, FALSE otherwise. * Note that this a symisc eXtension. */ static int PH7_builtin_strglob(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zPattern; int iEsc = '\\'; int rc; if( nArg < 2 || !ph7_value_is_string(apArg[0]) || !ph7_value_is_string(apArg[1]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the pattern and the string */ zPattern = ph7_value_to_string(apArg[0],0); zString = ph7_value_to_string(apArg[1],0); /* Go globbing */ rc = Glob((const unsigned char *)zPattern,(const unsigned char *)zString,iEsc,0); /* Globbing result */ ph7_result_bool(pCtx,rc); return PH7_OK; } /* * bool link(string $target,string $link) * Create a hard link. * Parameters * $target * Target of the link. * $link * The link name. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_link(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zTarget,*zLink; ph7_vfs *pVfs; int rc; if( nArg < 2 || !ph7_value_is_string(apArg[0]) || !ph7_value_is_string(apArg[1]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xLink == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the given arguments */ zTarget = ph7_value_to_string(apArg[0],0); zLink = ph7_value_to_string(apArg[1],0); /* Perform the requested operation */ rc = pVfs->xLink(zTarget,zLink,0/*Not a symbolic link */); /* IO result */ ph7_result_bool(pCtx,rc == PH7_OK ); return PH7_OK; } /* * bool symlink(string $target,string $link) * Creates a symbolic link. * Parameters * $target * Target of the link. * $link * The link name. * Return * TRUE on success or FALSE on failure. */ static int PH7_vfs_symlink(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zTarget,*zLink; ph7_vfs *pVfs; int rc; if( nArg < 2 || !ph7_value_is_string(apArg[0]) || !ph7_value_is_string(apArg[1]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xLink == 0 ){ /* IO routine not implemented,return NULL */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS,PH7 is returning FALSE", ph7_function_name(pCtx) ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the given arguments */ zTarget = ph7_value_to_string(apArg[0],0); zLink = ph7_value_to_string(apArg[1],0); /* Perform the requested operation */ rc = pVfs->xLink(zTarget,zLink,1/*A symbolic link */); /* IO result */ ph7_result_bool(pCtx,rc == PH7_OK ); return PH7_OK; } /* * int umask([ int $mask ]) * Changes the current umask. * Parameters * $mask * The new umask. * Return * umask() without arguments simply returns the current umask. * Otherwise the old umask is returned. */ static int PH7_vfs_umask(ph7_context *pCtx,int nArg,ph7_value **apArg) { int iOld,iNew; ph7_vfs *pVfs; /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xUmask == 0 ){ /* IO routine not implemented,return -1 */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS", ph7_function_name(pCtx) ); ph7_result_int(pCtx,0); return PH7_OK; } iNew = 0; if( nArg > 0 ){ iNew = ph7_value_to_int(apArg[0]); } /* Perform the requested operation */ iOld = pVfs->xUmask(iNew); /* Old mask */ ph7_result_int(pCtx,iOld); return PH7_OK; } /* * string sys_get_temp_dir() * Returns directory path used for temporary files. * Parameters * None * Return * Returns the path of the temporary directory. */ static int PH7_vfs_sys_get_temp_dir(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vfs *pVfs; /* Set the empty string as the default return value */ ph7_result_string(pCtx,"",0); /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xTempDir == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* IO routine not implemented,return "" */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS", ph7_function_name(pCtx) ); return PH7_OK; } /* Perform the requested operation */ pVfs->xTempDir(pCtx); return PH7_OK; } /* * string get_current_user() * Returns the name of the current working user. * Parameters * None * Return * Returns the name of the current working user. */ static int PH7_vfs_get_current_user(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vfs *pVfs; /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xUsername == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* IO routine not implemented */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS", ph7_function_name(pCtx) ); /* Set a dummy username */ ph7_result_string(pCtx,"unknown",sizeof("unknown")-1); return PH7_OK; } /* Perform the requested operation */ pVfs->xUsername(pCtx); return PH7_OK; } /* * int64 getmypid() * Gets process ID. * Parameters * None * Return * Returns the process ID. */ static int PH7_vfs_getmypid(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_int64 nProcessId; ph7_vfs *pVfs; /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xProcessId == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* IO routine not implemented,return -1 */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS", ph7_function_name(pCtx) ); ph7_result_int(pCtx,-1); return PH7_OK; } /* Perform the requested operation */ nProcessId = (ph7_int64)pVfs->xProcessId(); /* Set the result */ ph7_result_int64(pCtx,nProcessId); return PH7_OK; } /* * int getmyuid() * Get user ID. * Parameters * None * Return * Returns the user ID. */ static int PH7_vfs_getmyuid(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vfs *pVfs; int nUid; /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xUid == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* IO routine not implemented,return -1 */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS", ph7_function_name(pCtx) ); ph7_result_int(pCtx,-1); return PH7_OK; } /* Perform the requested operation */ nUid = pVfs->xUid(); /* Set the result */ ph7_result_int(pCtx,nUid); return PH7_OK; } /* * int getmygid() * Get group ID. * Parameters * None * Return * Returns the group ID. */ static int PH7_vfs_getmygid(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_vfs *pVfs; int nGid; /* Point to the underlying vfs */ pVfs = (ph7_vfs *)ph7_context_user_data(pCtx); if( pVfs == 0 || pVfs->xGid == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* IO routine not implemented,return -1 */ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying VFS", ph7_function_name(pCtx) ); ph7_result_int(pCtx,-1); return PH7_OK; } /* Perform the requested operation */ nGid = pVfs->xGid(); /* Set the result */ ph7_result_int(pCtx,nGid); return PH7_OK; } #ifdef __WINNT__ #include #elif defined(__UNIXES__) #include #endif /* * string php_uname([ string $mode = "a" ]) * Returns information about the host operating system. * Parameters * $mode * mode is a single character that defines what information is returned: * 'a': This is the default. Contains all modes in the sequence "s n r v m". * 's': Operating system name. eg. FreeBSD. * 'n': Host name. eg. localhost.example.com. * 'r': Release name. eg. 5.1.2-RELEASE. * 'v': Version information. Varies a lot between operating systems. * 'm': Machine type. eg. i386. * Return * OS description as a string. */ static int PH7_vfs_ph7_uname(ph7_context *pCtx,int nArg,ph7_value **apArg) { #if defined(__WINNT__) const char *zName = "Microsoft Windows"; OSVERSIONINFOW sVer; #elif defined(__UNIXES__) struct utsname sName; #endif const char *zMode = "a"; if( nArg > 0 && ph7_value_is_string(apArg[0]) ){ /* Extract the desired mode */ zMode = ph7_value_to_string(apArg[0],0); } #if defined(__WINNT__) sVer.dwOSVersionInfoSize = sizeof(sVer); if( TRUE != GetVersionExW(&sVer)){ ph7_result_string(pCtx,zName,-1); return PH7_OK; } if( sVer.dwPlatformId == VER_PLATFORM_WIN32_NT ){ if( sVer.dwMajorVersion <= 4 ){ zName = "Microsoft Windows NT"; }else if( sVer.dwMajorVersion == 5 ){ switch(sVer.dwMinorVersion){ case 0: zName = "Microsoft Windows 2000"; break; case 1: zName = "Microsoft Windows XP"; break; case 2: zName = "Microsoft Windows Server 2003"; break; } }else if( sVer.dwMajorVersion == 6){ switch(sVer.dwMinorVersion){ case 0: zName = "Microsoft Windows Vista"; break; case 1: zName = "Microsoft Windows 7"; break; case 2: zName = "Microsoft Windows Server 2008"; break; case 3: zName = "Microsoft Windows 8"; break; default: break; } } } switch(zMode[0]){ case 's': /* Operating system name */ ph7_result_string(pCtx,zName,-1/* Compute length automatically*/); break; case 'n': /* Host name */ ph7_result_string(pCtx,"localhost",(int)sizeof("localhost")-1); break; case 'r': case 'v': /* Version information. */ ph7_result_string_format(pCtx,"%u.%u build %u", sVer.dwMajorVersion,sVer.dwMinorVersion,sVer.dwBuildNumber ); break; case 'm': /* Machine name */ ph7_result_string(pCtx,"x86",(int)sizeof("x86")-1); break; default: ph7_result_string_format(pCtx,"%s localhost %u.%u build %u x86", zName, sVer.dwMajorVersion,sVer.dwMinorVersion,sVer.dwBuildNumber ); break; } #elif defined(__UNIXES__) if( uname(&sName) != 0 ){ ph7_result_string(pCtx,"Unix",(int)sizeof("Unix")-1); return PH7_OK; } switch(zMode[0]){ case 's': /* Operating system name */ ph7_result_string(pCtx,sName.sysname,-1/* Compute length automatically*/); break; case 'n': /* Host name */ ph7_result_string(pCtx,sName.nodename,-1/* Compute length automatically*/); break; case 'r': /* Release information */ ph7_result_string(pCtx,sName.release,-1/* Compute length automatically*/); break; case 'v': /* Version information. */ ph7_result_string(pCtx,sName.version,-1/* Compute length automatically*/); break; case 'm': /* Machine name */ ph7_result_string(pCtx,sName.machine,-1/* Compute length automatically*/); break; default: ph7_result_string_format(pCtx, "%s %s %s %s %s", sName.sysname, sName.release, sName.version, sName.nodename, sName.machine ); break; } #else ph7_result_string(pCtx,"Unknown Operating System",(int)sizeof("Unknown Operating System")-1); #endif return PH7_OK; } /* * Section: * IO stream implementation. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ typedef struct io_private io_private; struct io_private { const ph7_io_stream *pStream; /* Underlying IO device */ void *pHandle; /* IO handle */ /* Unbuffered IO */ SyBlob sBuffer; /* Working buffer */ sxu32 nOfft; /* Current read offset */ sxu32 iMagic; /* Sanity check to avoid misuse */ }; #define IO_PRIVATE_MAGIC 0xFEAC14 /* Make sure we are dealing with a valid io_private instance */ #define IO_PRIVATE_INVALID(IO) ( IO == 0 || IO->iMagic != IO_PRIVATE_MAGIC ) /* Forward declaration */ static void ResetIOPrivate(io_private *pDev); /* * bool ftruncate(resource $handle,int64 $size) * Truncates a file to a given length. * Parameters * $handle * The file pointer. * Note: * The handle must be open for writing. * $size * The size to truncate to. * Return * TRUE on success or FALSE on failure. */ static int PH7_builtin_ftruncate(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; int rc; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xTrunc == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ rc = pStream->xTrunc(pDev->pHandle,ph7_value_to_int64(apArg[1])); if( rc == PH7_OK ){ /* Discard buffered data */ ResetIOPrivate(pDev); } /* IO result */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * int fseek(resource $handle,int $offset[,int $whence = SEEK_SET ]) * Seeks on a file pointer. * Parameters * $handle * A file system pointer resource that is typically created using fopen(). * $offset * The offset. * To move to a position before the end-of-file, you need to pass a negative * value in offset and set whence to SEEK_END. * whence * whence values are: * SEEK_SET - Set position equal to offset bytes. * SEEK_CUR - Set position to current location plus offset. * SEEK_END - Set position to end-of-file plus offset. * Return * 0 on success,-1 on failure */ static int PH7_builtin_fseek(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; ph7_int64 iOfft; int whence; int rc; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_int(pCtx,-1); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_int(pCtx,-1); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xSeek == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_int(pCtx,-1); return PH7_OK; } /* Extract the offset */ iOfft = ph7_value_to_int64(apArg[1]); whence = 0;/* SEEK_SET */ if( nArg > 2 && ph7_value_is_int(apArg[2]) ){ whence = ph7_value_to_int(apArg[2]); } /* Perform the requested operation */ rc = pStream->xSeek(pDev->pHandle,iOfft,whence); if( rc == PH7_OK ){ /* Ignore buffered data */ ResetIOPrivate(pDev); } /* IO result */ ph7_result_int(pCtx,rc == PH7_OK ? 0 : - 1); return PH7_OK; } /* * int64 ftell(resource $handle) * Returns the current position of the file read/write pointer. * Parameters * $handle * The file pointer. * Return * Returns the position of the file pointer referenced by handle * as an integer; i.e., its offset into the file stream. * FALSE is returned on failure. */ static int PH7_builtin_ftell(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; ph7_int64 iOfft; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xTell == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ iOfft = pStream->xTell(pDev->pHandle); /* IO result */ ph7_result_int64(pCtx,iOfft); return PH7_OK; } /* * bool rewind(resource $handle) * Rewind the position of a file pointer. * Parameters * $handle * The file pointer. * Return * TRUE on success or FALSE on failure. */ static int PH7_builtin_rewind(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; int rc; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xSeek == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ rc = pStream->xSeek(pDev->pHandle,0,0/*SEEK_SET*/); if( rc == PH7_OK ){ /* Ignore buffered data */ ResetIOPrivate(pDev); } /* IO result */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool fflush(resource $handle) * Flushes the output to a file. * Parameters * $handle * The file pointer. * Return * TRUE on success or FALSE on failure. */ static int PH7_builtin_fflush(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; int rc; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xSync == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ rc = pStream->xSync(pDev->pHandle); /* IO result */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * bool feof(resource $handle) * Tests for end-of-file on a file pointer. * Parameters * $handle * The file pointer. * Return * Returns TRUE if the file pointer is at EOF.FALSE otherwise */ static int PH7_builtin_feof(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; int rc; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,1); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,1); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,1); return PH7_OK; } rc = SXERR_EOF; /* Perform the requested operation */ if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ /* Data is available */ rc = PH7_OK; }else{ char zBuf[4096]; ph7_int64 n; /* Perform a buffered read */ n = pStream->xRead(pDev->pHandle,zBuf,sizeof(zBuf)); if( n > 0 ){ /* Copy buffered data */ SyBlobAppend(&pDev->sBuffer,zBuf,(sxu32)n); rc = PH7_OK; } } /* EOF or not */ ph7_result_bool(pCtx,rc == SXERR_EOF); return PH7_OK; } /* * Read n bytes from the underlying IO stream device. * Return total numbers of bytes readen on success. A number < 1 on failure * [i.e: IO error ] or EOF. */ static ph7_int64 StreamRead(io_private *pDev,void *pBuf,ph7_int64 nLen) { const ph7_io_stream *pStream = pDev->pStream; char *zBuf = (char *)pBuf; ph7_int64 n,nRead; n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; if( n > 0 ){ if( n > nLen ){ n = nLen; } /* Copy the buffered data */ SyMemcpy(SyBlobDataAt(&pDev->sBuffer,pDev->nOfft),pBuf,(sxu32)n); /* Update the read offset */ pDev->nOfft += (sxu32)n; if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){ /* Reset the working buffer so that we avoid excessive memory allocation */ SyBlobReset(&pDev->sBuffer); pDev->nOfft = 0; } nLen -= n; if( nLen < 1 ){ /* All done */ return n; } /* Advance the cursor */ zBuf += n; } /* Read without buffering */ nRead = pStream->xRead(pDev->pHandle,zBuf,nLen); if( nRead > 0 ){ n += nRead; }else if( n < 1 ){ /* EOF or IO error */ return nRead; } return n; } /* * Extract a single line from the buffered input. */ static sxi32 GetLine(io_private *pDev,ph7_int64 *pLen,const char **pzLine) { const char *zIn,*zEnd,*zPtr; zIn = (const char *)SyBlobDataAt(&pDev->sBuffer,pDev->nOfft); zEnd = &zIn[SyBlobLength(&pDev->sBuffer)-pDev->nOfft]; zPtr = zIn; while( zIn < zEnd ){ if( zIn[0] == '\n' ){ /* Line found */ zIn++; /* Include the line ending as requested by the PHP specification */ *pLen = (ph7_int64)(zIn-zPtr); *pzLine = zPtr; return SXRET_OK; } zIn++; } /* No line were found */ return SXERR_NOTFOUND; } /* * Read a single line from the underlying IO stream device. */ static ph7_int64 StreamReadLine(io_private *pDev,const char **pzData,ph7_int64 nMaxLen) { const ph7_io_stream *pStream = pDev->pStream; char zBuf[8192]; ph7_int64 n; sxi32 rc; n = 0; if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){ /* Reset the working buffer so that we avoid excessive memory allocation */ SyBlobReset(&pDev->sBuffer); pDev->nOfft = 0; } if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ /* Check if there is a line */ rc = GetLine(pDev,&n,pzData); if( rc == SXRET_OK ){ /* Got line,update the cursor */ pDev->nOfft += (sxu32)n; return n; } } /* Perform the read operation until a new line is extracted or length * limit is reached. */ for(;;){ n = pStream->xRead(pDev->pHandle,zBuf,(nMaxLen > 0 && nMaxLen < sizeof(zBuf)) ? nMaxLen : sizeof(zBuf)); if( n < 1 ){ /* EOF or IO error */ break; } /* Append the data just read */ SyBlobAppend(&pDev->sBuffer,zBuf,(sxu32)n); /* Try to extract a line */ rc = GetLine(pDev,&n,pzData); if( rc == SXRET_OK ){ /* Got one,return immediately */ pDev->nOfft += (sxu32)n; return n; } if( nMaxLen > 0 && (SyBlobLength(&pDev->sBuffer) - pDev->nOfft >= nMaxLen) ){ /* Read limit reached,return the available data */ *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer,pDev->nOfft); n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; /* Reset the working buffer */ SyBlobReset(&pDev->sBuffer); pDev->nOfft = 0; return n; } } if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ /* Read limit reached,return the available data */ *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer,pDev->nOfft); n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; /* Reset the working buffer */ SyBlobReset(&pDev->sBuffer); pDev->nOfft = 0; } return n; } /* * Open an IO stream handle. * Notes on stream: * According to the PHP reference manual. * In its simplest definition, a stream is a resource object which exhibits streamable behavior. * That is, it can be read from or written to in a linear fashion, and may be able to fseek() * to an arbitrary locations within the stream. * A wrapper is additional code which tells the stream how to handle specific protocols/encodings. * For example, the http wrapper knows how to translate a URL into an HTTP/1.0 request for a file * on a remote server. * A stream is referenced as: scheme://target * scheme(string) - The name of the wrapper to be used. Examples include: file, http... * If no wrapper is specified, the function default is used (typically file://). * target - Depends on the wrapper used. For filesystem related streams this is typically a path * and filename of the desired file. For network related streams this is typically a hostname, often * with a path appended. * * Note that PH7 IO streams looks like PHP streams but their implementation differ greately. * Please refer to the official documentation for a full discussion. * This function return a handle on success. Otherwise null. */ PH7_PRIVATE void * PH7_StreamOpenHandle(ph7_vm *pVm,const ph7_io_stream *pStream,const char *zFile, int iFlags,int use_include,ph7_value *pResource,int bPushInclude,int *pNew) { void *pHandle = 0; /* cc warning */ SyString sFile; int rc; if( pStream == 0 ){ /* No such stream device */ return 0; } SyStringInitFromBuf(&sFile,zFile,SyStrlen(zFile)); if( use_include ){ if( sFile.zString[0] == '/' || #ifdef __WINNT__ (sFile.nByte > 2 && sFile.zString[1] == ':' && (sFile.zString[2] == '\\' || sFile.zString[2] == '/') ) || #endif (sFile.nByte > 1 && sFile.zString[0] == '.' && sFile.zString[1] == '/') || (sFile.nByte > 2 && sFile.zString[0] == '.' && sFile.zString[1] == '.' && sFile.zString[2] == '/') ){ /* Open the file directly */ rc = pStream->xOpen(zFile,iFlags,pResource,&pHandle); }else{ SyString *pPath; SyBlob sWorker; #ifdef __WINNT__ static const int c = '\\'; #else static const int c = '/'; #endif /* Init the path builder working buffer */ SyBlobInit(&sWorker,&pVm->sAllocator); /* Build a path from the set of include path */ SySetResetCursor(&pVm->aPaths); rc = SXERR_IO; while( SXRET_OK == SySetGetNextEntry(&pVm->aPaths,(void **)&pPath) ){ /* Build full path */ SyBlobFormat(&sWorker,"%z%c%z",pPath,c,&sFile); /* Append null terminator */ if( SXRET_OK != SyBlobNullAppend(&sWorker) ){ continue; } /* Try to open the file */ rc = pStream->xOpen((const char *)SyBlobData(&sWorker),iFlags,pResource,&pHandle); if( rc == PH7_OK ){ if( bPushInclude ){ /* Mark as included */ PH7_VmPushFilePath(pVm,(const char *)SyBlobData(&sWorker),SyBlobLength(&sWorker),FALSE,pNew); } break; } /* Reset the working buffer */ SyBlobReset(&sWorker); /* Check the next path */ } SyBlobRelease(&sWorker); } if( rc == PH7_OK ){ if( bPushInclude ){ /* Mark as included */ PH7_VmPushFilePath(pVm,sFile.zString,sFile.nByte,FALSE,pNew); } } }else{ /* Open the URI direcly */ rc = pStream->xOpen(zFile,iFlags,pResource,&pHandle); } if( rc != PH7_OK ){ /* IO error */ return 0; } /* Return the file handle */ return pHandle; } /* * Read the whole contents of an open IO stream handle [i.e local file/URL..] * Store the read data in the given BLOB (last argument). * The read operation is stopped when he hit the EOF or an IO error occurs. */ PH7_PRIVATE sxi32 PH7_StreamReadWholeFile(void *pHandle,const ph7_io_stream *pStream,SyBlob *pOut) { ph7_int64 nRead; char zBuf[8192]; /* 8K */ int rc; /* Perform the requested operation */ for(;;){ nRead = pStream->xRead(pHandle,zBuf,sizeof(zBuf)); if( nRead < 1 ){ /* EOF or IO error */ break; } /* Append contents */ rc = SyBlobAppend(pOut,zBuf,(sxu32)nRead); if( rc != SXRET_OK ){ break; } } return SyBlobLength(pOut) > 0 ? SXRET_OK : -1; } /* * Close an open IO stream handle [i.e local file/URI..]. */ PH7_PRIVATE void PH7_StreamCloseHandle(const ph7_io_stream *pStream,void *pHandle) { if( pStream->xClose ){ pStream->xClose(pHandle); } } /* * string fgetc(resource $handle) * Gets a character from the given file pointer. * Parameters * $handle * The file pointer. * Return * Returns a string containing a single character read from the file * pointed to by handle. Returns FALSE on EOF. * WARNING * This operation is extremely slow.Avoid using it. */ static int PH7_builtin_fgetc(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; int c,n; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ n = (int)StreamRead(pDev,(void *)&c,sizeof(char)); /* IO result */ if( n < 1 ){ /* EOF or error,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Return the string holding the character */ ph7_result_string(pCtx,(const char *)&c,sizeof(char)); } return PH7_OK; } /* * string fgets(resource $handle[,int64 $length ]) * Gets line from file pointer. * Parameters * $handle * The file pointer. * $length * Reading ends when length - 1 bytes have been read, on a newline * (which is included in the return value), or on EOF (whichever comes first). * If no length is specified, it will keep reading from the stream until it reaches * the end of the line. * Return * Returns a string of up to length - 1 bytes read from the file pointed to by handle. * If there is no more data to read in the file pointer, then FALSE is returned. * If an error occurs, FALSE is returned. */ static int PH7_builtin_fgets(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; const char *zLine; io_private *pDev; ph7_int64 n,nLen; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } nLen = -1; if( nArg > 1 ){ /* Maximum data to read */ nLen = ph7_value_to_int64(apArg[1]); } /* Perform the requested operation */ n = StreamReadLine(pDev,&zLine,nLen); if( n < 1 ){ /* EOF or IO error,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Return the freshly extracted line */ ph7_result_string(pCtx,zLine,(int)n); } return PH7_OK; } /* * string fread(resource $handle,int64 $length) * Binary-safe file read. * Parameters * $handle * The file pointer. * $length * Up to length number of bytes read. * Return * The data readen on success or FALSE on failure. */ static int PH7_builtin_fread(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; ph7_int64 nRead; void *pBuf; int nLen; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } nLen = 4096; if( nArg > 1 ){ nLen = ph7_value_to_int(apArg[1]); if( nLen < 1 ){ /* Invalid length,set a default length */ nLen = 4096; } } /* Allocate enough buffer */ pBuf = ph7_context_alloc_chunk(pCtx,(unsigned int)nLen,FALSE,FALSE); if( pBuf == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ nRead = StreamRead(pDev,pBuf,(ph7_int64)nLen); if( nRead < 1 ){ /* Nothing read,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Make a copy of the data just read */ ph7_result_string(pCtx,(const char *)pBuf,(int)nRead); } /* Release the buffer */ ph7_context_free_chunk(pCtx,pBuf); return PH7_OK; } /* * array fgetcsv(resource $handle [, int $length = 0 * [,string $delimiter = ','[,string $enclosure = '"'[,string $escape='\\']]]]) * Gets line from file pointer and parse for CSV fields. * Parameters * $handle * The file pointer. * $length * Reading ends when length - 1 bytes have been read, on a newline * (which is included in the return value), or on EOF (whichever comes first). * If no length is specified, it will keep reading from the stream until it reaches * the end of the line. * $delimiter * Set the field delimiter (one character only). * $enclosure * Set the field enclosure character (one character only). * $escape * Set the escape character (one character only). Defaults as a backslash (\) * Return * Returns a string of up to length - 1 bytes read from the file pointed to by handle. * If there is no more data to read in the file pointer, then FALSE is returned. * If an error occurs, FALSE is returned. */ static int PH7_builtin_fgetcsv(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; const char *zLine; io_private *pDev; ph7_int64 n,nLen; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } nLen = -1; if( nArg > 1 ){ /* Maximum data to read */ nLen = ph7_value_to_int64(apArg[1]); } /* Perform the requested operation */ n = StreamReadLine(pDev,&zLine,nLen); if( n < 1 ){ /* EOF or IO error,return FALSE */ ph7_result_bool(pCtx,0); }else{ ph7_value *pArray; int delim = ','; /* Delimiter */ int encl = '"' ; /* Enclosure */ int escape = '\\'; /* Escape character */ if( nArg > 2 ){ const char *zPtr; int i; if( ph7_value_is_string(apArg[2]) ){ /* Extract the delimiter */ zPtr = ph7_value_to_string(apArg[2],&i); if( i > 0 ){ delim = zPtr[0]; } } if( nArg > 3 ){ if( ph7_value_is_string(apArg[3]) ){ /* Extract the enclosure */ zPtr = ph7_value_to_string(apArg[3],&i); if( i > 0 ){ encl = zPtr[0]; } } if( nArg > 4 ){ if( ph7_value_is_string(apArg[4]) ){ /* Extract the escape character */ zPtr = ph7_value_to_string(apArg[4],&i); if( i > 0 ){ escape = zPtr[0]; } } } } } /* Create our array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_null(pCtx); return PH7_OK; } /* Parse the raw input */ PH7_ProcessCsv(zLine,(int)n,delim,encl,escape,PH7_CsvConsumer,pArray); /* Return the freshly created array */ ph7_result_value(pCtx,pArray); } return PH7_OK; } /* * string fgetss(resource $handle [,int $length [,string $allowable_tags ]]) * Gets line from file pointer and strip HTML tags. * Parameters * $handle * The file pointer. * $length * Reading ends when length - 1 bytes have been read, on a newline * (which is included in the return value), or on EOF (whichever comes first). * If no length is specified, it will keep reading from the stream until it reaches * the end of the line. * $allowable_tags * You can use the optional second parameter to specify tags which should not be stripped. * Return * Returns a string of up to length - 1 bytes read from the file pointed to by * handle, with all HTML and PHP code stripped. If an error occurs, returns FALSE. */ static int PH7_builtin_fgetss(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; const char *zLine; io_private *pDev; ph7_int64 n,nLen; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } nLen = -1; if( nArg > 1 ){ /* Maximum data to read */ nLen = ph7_value_to_int64(apArg[1]); } /* Perform the requested operation */ n = StreamReadLine(pDev,&zLine,nLen); if( n < 1 ){ /* EOF or IO error,return FALSE */ ph7_result_bool(pCtx,0); }else{ const char *zTaglist = 0; int nTaglen = 0; if( nArg > 2 && ph7_value_is_string(apArg[2]) ){ /* Allowed tag */ zTaglist = ph7_value_to_string(apArg[2],&nTaglen); } /* Process data just read */ PH7_StripTagsFromString(pCtx,zLine,(int)n,zTaglist,nTaglen); } return PH7_OK; } /* * string readdir(resource $dir_handle) * Read entry from directory handle. * Parameter * $dir_handle * The directory handle resource previously opened with opendir(). * Return * Returns the filename on success or FALSE on failure. */ static int PH7_builtin_readdir(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; int rc; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xReadDir == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } ph7_result_bool(pCtx,0); /* Perform the requested operation */ rc = pStream->xReadDir(pDev->pHandle,pCtx); if( rc != PH7_OK ){ /* Return FALSE */ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * void rewinddir(resource $dir_handle) * Rewind directory handle. * Parameter * $dir_handle * The directory handle resource previously opened with opendir(). * Return * FALSE on failure. */ static int PH7_builtin_rewinddir(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xRewindDir == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ pStream->xRewindDir(pDev->pHandle); return PH7_OK; } /* Forward declaration */ static void InitIOPrivate(ph7_vm *pVm,const ph7_io_stream *pStream,io_private *pOut); static void ReleaseIOPrivate(ph7_context *pCtx,io_private *pDev); /* * void closedir(resource $dir_handle) * Close directory handle. * Parameter * $dir_handle * The directory handle resource previously opened with opendir(). * Return * FALSE on failure. */ static int PH7_builtin_closedir(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xCloseDir == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ pStream->xCloseDir(pDev->pHandle); /* Release the private stucture */ ReleaseIOPrivate(pCtx,pDev); PH7_MemObjRelease(apArg[0]); return PH7_OK; } /* * resource opendir(string $path[,resource $context]) * Open directory handle. * Parameters * $path * The directory path that is to be opened. * $context * A context stream resource. * Return * A directory handle resource on success,or FALSE on failure. */ static int PH7_builtin_opendir(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; const char *zPath; io_private *pDev; int iLen,rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a directory path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target path */ zPath = ph7_value_to_string(apArg[0],&iLen); /* Try to extract a stream */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zPath,iLen); if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "No stream device is associated with the given path(%s)",zPath); ph7_result_bool(pCtx,0); return PH7_OK; } if( pStream->xOpenDir == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device", ph7_function_name(pCtx),pStream->zName ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Allocate a new IO private instance */ pDev = (io_private *)ph7_context_alloc_chunk(pCtx,sizeof(io_private),TRUE,FALSE); if( pDev == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Initialize the structure */ InitIOPrivate(pCtx->pVm,pStream,pDev); /* Open the target directory */ rc = pStream->xOpenDir(zPath,nArg > 1 ? apArg[1] : 0,&pDev->pHandle); if( rc != PH7_OK ){ /* IO error,return FALSE */ ReleaseIOPrivate(pCtx,pDev); ph7_result_bool(pCtx,0); }else{ /* Return the handle as a resource */ ph7_result_resource(pCtx,pDev); } return PH7_OK; } /* * int readfile(string $filename[,bool $use_include_path = false [,resource $context ]]) * Reads a file and writes it to the output buffer. * Parameters * $filename * The filename being read. * $use_include_path * You can use the optional second parameter and set it to * TRUE, if you want to search for the file in the include_path, too. * $context * A context stream resource. * Return * The number of bytes read from the file on success or FALSE on failure. */ static int PH7_builtin_readfile(ph7_context *pCtx,int nArg,ph7_value **apArg) { int use_include = FALSE; const ph7_io_stream *pStream; ph7_int64 n,nRead; const char *zFile; char zBuf[8192]; void *pHandle; int rc,nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pStream == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ use_include = ph7_value_to_bool(apArg[1]); } /* Try to open the file in read-only mode */ pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zFile,PH7_IO_OPEN_RDONLY, use_include,nArg > 2 ? apArg[2] : 0,FALSE,0); if( pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zFile); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ nRead = 0; for(;;){ n = pStream->xRead(pHandle,zBuf,sizeof(zBuf)); if( n < 1 ){ /* EOF or IO error,break immediately */ break; } /* Output data */ rc = ph7_context_output(pCtx,zBuf,(int)n); if( rc == PH7_ABORT ){ break; } /* Increment counter */ nRead += n; } /* Close the stream */ PH7_StreamCloseHandle(pStream,pHandle); /* Total number of bytes readen */ ph7_result_int64(pCtx,nRead); return PH7_OK; } /* * string file_get_contents(string $filename[,bool $use_include_path = false * [, resource $context [, int $offset = -1 [, int $maxlen ]]]]) * Reads entire file into a string. * Parameters * $filename * The filename being read. * $use_include_path * You can use the optional second parameter and set it to * TRUE, if you want to search for the file in the include_path, too. * $context * A context stream resource. * $offset * The offset where the reading starts on the original stream. * $maxlen * Maximum length of data read. The default is to read until end of file * is reached. Note that this parameter is applied to the stream processed by the filters. * Return * The function returns the read data or FALSE on failure. */ static int PH7_builtin_file_get_contents(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; ph7_int64 n,nRead,nMaxlen; int use_include = FALSE; const char *zFile; char zBuf[8192]; void *pHandle; int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pStream == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } nMaxlen = -1; if( nArg > 1 ){ use_include = ph7_value_to_bool(apArg[1]); } /* Try to open the file in read-only mode */ pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zFile,PH7_IO_OPEN_RDONLY,use_include,nArg > 2 ? apArg[2] : 0,FALSE,0); if( pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zFile); ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 3 ){ /* Extract the offset */ n = ph7_value_to_int64(apArg[3]); if( n > 0 ){ if( pStream->xSeek ){ /* Seek to the desired offset */ pStream->xSeek(pHandle,n,0/*SEEK_SET*/); } } if( nArg > 4 ){ /* Maximum data to read */ nMaxlen = ph7_value_to_int64(apArg[4]); } } /* Perform the requested operation */ nRead = 0; for(;;){ n = pStream->xRead(pHandle,zBuf, (nMaxlen > 0 && (nMaxlen < sizeof(zBuf))) ? nMaxlen : sizeof(zBuf)); if( n < 1 ){ /* EOF or IO error,break immediately */ break; } /* Append data */ ph7_result_string(pCtx,zBuf,(int)n); /* Increment read counter */ nRead += n; if( nMaxlen > 0 && nRead >= nMaxlen ){ /* Read limit reached */ break; } } /* Close the stream */ PH7_StreamCloseHandle(pStream,pHandle); /* Check if we have read something */ if( ph7_context_result_buf_length(pCtx) < 1 ){ /* Nothing read,return FALSE */ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * int file_put_contents(string $filename,mixed $data[,int $flags = 0[,resource $context]]) * Write a string to a file. * Parameters * $filename * Path to the file where to write the data. * $data * The data to write(Must be a string). * $flags * The value of flags can be any combination of the following * flags, joined with the binary OR (|) operator. * FILE_USE_INCLUDE_PATH Search for filename in the include directory. See include_path for more information. * FILE_APPEND If file filename already exists, append the data to the file instead of overwriting it. * LOCK_EX Acquire an exclusive lock on the file while proceeding to the writing. * context * A context stream resource. * Return * The function returns the number of bytes that were written to the file, or FALSE on failure. */ static int PH7_builtin_file_put_contents(ph7_context *pCtx,int nArg,ph7_value **apArg) { int use_include = FALSE; const ph7_io_stream *pStream; const char *zFile; const char *zData; int iOpenFlags; void *pHandle; int iFlags; int nLen; if( nArg < 2 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pStream == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Data to write */ zData = ph7_value_to_string(apArg[1],&nLen); if( nLen < 1 ){ /* Nothing to write,return immediately */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Try to open the file in read-write mode */ iOpenFlags = PH7_IO_OPEN_CREATE|PH7_IO_OPEN_RDWR|PH7_IO_OPEN_TRUNC; /* Extract the flags */ iFlags = 0; if( nArg > 2 ){ iFlags = ph7_value_to_int(apArg[2]); if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/){ use_include = TRUE; } if( iFlags & 0x08 /* FILE_APPEND */){ /* If the file already exists, append the data to the file * instead of overwriting it. */ iOpenFlags &= ~PH7_IO_OPEN_TRUNC; /* Append mode */ iOpenFlags |= PH7_IO_OPEN_APPEND; } } pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zFile,iOpenFlags,use_include, nArg > 3 ? apArg[3] : 0,FALSE,FALSE); if( pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zFile); ph7_result_bool(pCtx,0); return PH7_OK; } if( pStream->xWrite ){ ph7_int64 n; if( (iFlags & 0x01/* LOCK_EX */) && pStream->xLock ){ /* Try to acquire an exclusive lock */ pStream->xLock(pHandle,1/* LOCK_EX */); } /* Perform the write operation */ n = pStream->xWrite(pHandle,(const void *)zData,nLen); if( n < 1 ){ /* IO error,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Total number of bytes written */ ph7_result_int64(pCtx,n); } }else{ /* Read-only stream */ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR, "Read-only stream(%s): Cannot perform write operation", pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); } /* Close the handle */ PH7_StreamCloseHandle(pStream,pHandle); return PH7_OK; } /* * array file(string $filename[,int $flags = 0[,resource $context]]) * Reads entire file into an array. * Parameters * $filename * The filename being read. * $flags * The optional parameter flags can be one, or more, of the following constants: * FILE_USE_INCLUDE_PATH * Search for the file in the include_path. * FILE_IGNORE_NEW_LINES * Do not add newline at the end of each array element * FILE_SKIP_EMPTY_LINES * Skip empty lines * $context * A context stream resource. * Return * The function returns the read data or FALSE on failure. */ static int PH7_builtin_file(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zFile,*zPtr,*zEnd,*zBuf; ph7_value *pArray,*pLine; const ph7_io_stream *pStream; int use_include = 0; io_private *pDev; ph7_int64 n; int iFlags; int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pStream == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Allocate a new IO private instance */ pDev = (io_private *)ph7_context_alloc_chunk(pCtx,sizeof(io_private),TRUE,FALSE); if( pDev == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Initialize the structure */ InitIOPrivate(pCtx->pVm,pStream,pDev); iFlags = 0; if( nArg > 1 ){ iFlags = ph7_value_to_int(apArg[1]); } if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){ use_include = TRUE; } /* Create the array and the working value */ pArray = ph7_context_new_array(pCtx); pLine = ph7_context_new_scalar(pCtx); if( pArray == 0 || pLine == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Try to open the file in read-only mode */ pDev->pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zFile,PH7_IO_OPEN_RDONLY,use_include,nArg > 2 ? apArg[2] : 0,FALSE,0); if( pDev->pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zFile); ph7_result_bool(pCtx,0); /* Don't worry about freeing memory, everything will be released automatically * as soon we return from this function. */ return PH7_OK; } /* Perform the requested operation */ for(;;){ /* Try to extract a line */ n = StreamReadLine(pDev,&zBuf,-1); if( n < 1 ){ /* EOF or IO error */ break; } /* Reset the cursor */ ph7_value_reset_string_cursor(pLine); /* Remove line ending if requested by the caller */ zPtr = zBuf; zEnd = &zBuf[n]; if( iFlags & 0x02 /* FILE_IGNORE_NEW_LINES */ ){ /* Ignore trailig lines */ while( zPtr < zEnd && (zEnd[-1] == '\n' #ifdef __WINNT__ || zEnd[-1] == '\r' #endif )){ n--; zEnd--; } } if( iFlags & 0x04 /* FILE_SKIP_EMPTY_LINES */ ){ /* Ignore empty lines */ while( zPtr < zEnd && (unsigned char)zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) ){ zPtr++; } if( zPtr >= zEnd ){ /* Empty line */ continue; } } ph7_value_string(pLine,zBuf,(int)(zEnd-zBuf)); /* Insert line */ ph7_array_add_elem(pArray,0/* Automatic index assign*/,pLine); } /* Close the stream */ PH7_StreamCloseHandle(pStream,pDev->pHandle); /* Release the io_private instance */ ReleaseIOPrivate(pCtx,pDev); /* Return the created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * bool copy(string $source,string $dest[,resource $context ] ) * Makes a copy of the file source to dest. * Parameters * $source * Path to the source file. * $dest * The destination path. If dest is a URL, the copy operation * may fail if the wrapper does not support overwriting of existing files. * $context * A context stream resource. * Return * TRUE on success or FALSE on failure. */ static int PH7_builtin_copy(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pSin,*pSout; const char *zFile; char zBuf[8192]; void *pIn,*pOut; ph7_int64 n; int nLen; if( nArg < 2 || !ph7_value_is_string(apArg[0]) || !ph7_value_is_string(apArg[1])){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a source and a destination path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the source name */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pSin = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pSin == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Try to open the source file in a read-only mode */ pIn = PH7_StreamOpenHandle(pCtx->pVm,pSin,zFile,PH7_IO_OPEN_RDONLY,FALSE,nArg > 2 ? apArg[2] : 0,FALSE,0); if( pIn == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening source: '%s'",zFile); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the destination name */ zFile = ph7_value_to_string(apArg[1],&nLen); /* Point to the target IO stream device */ pSout = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pSout == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); PH7_StreamCloseHandle(pSin,pIn); return PH7_OK; } if( pSout->xWrite == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pSin->zName ); ph7_result_bool(pCtx,0); PH7_StreamCloseHandle(pSin,pIn); return PH7_OK; } /* Try to open the destination file in a read-write mode */ pOut = PH7_StreamOpenHandle(pCtx->pVm,pSout,zFile, PH7_IO_OPEN_CREATE|PH7_IO_OPEN_TRUNC|PH7_IO_OPEN_RDWR,FALSE,nArg > 2 ? apArg[2] : 0,FALSE,0); if( pOut == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening destination: '%s'",zFile); ph7_result_bool(pCtx,0); PH7_StreamCloseHandle(pSin,pIn); return PH7_OK; } /* Perform the requested operation */ for(;;){ /* Read from source */ n = pSin->xRead(pIn,zBuf,sizeof(zBuf)); if( n < 1 ){ /* EOF or IO error,break immediately */ break; } /* Write to dest */ n = pSout->xWrite(pOut,zBuf,n); if( n < 1 ){ /* IO error,break immediately */ break; } } /* Close the streams */ PH7_StreamCloseHandle(pSin,pIn); PH7_StreamCloseHandle(pSout,pOut); /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * array fstat(resource $handle) * Gets information about a file using an open file pointer. * Parameters * $handle * The file pointer. * Return * Returns an array with the statistics of the file or FALSE on failure. */ static int PH7_builtin_fstat(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pValue; const ph7_io_stream *pStream; io_private *pDev; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /* Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xStat == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Create the array and the working value */ pArray = ph7_context_new_array(pCtx); pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ pStream->xStat(pDev->pHandle,pArray,pValue); /* Return the freshly created array */ ph7_result_value(pCtx,pArray); /* Don't worry about freeing memory here,everything will be * released automatically as soon we return from this function. */ return PH7_OK; } /* * int fwrite(resource $handle,string $string[,int $length]) * Writes the contents of string to the file stream pointed to by handle. * Parameters * $handle * The file pointer. * $string * The string that is to be written. * $length * If the length argument is given, writing will stop after length bytes have been written * or the end of string is reached, whichever comes first. * Return * Returns the number of bytes written, or FALSE on error. */ static int PH7_builtin_fwrite(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; const char *zString; io_private *pDev; int nLen,n; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /* Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xWrite == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the data to write */ zString = ph7_value_to_string(apArg[1],&nLen); if( nArg > 2 ){ /* Maximum data length to write */ n = ph7_value_to_int(apArg[2]); if( n >= 0 && n < nLen ){ nLen = n; } } if( nLen < 1 ){ /* Nothing to write */ ph7_result_int(pCtx,0); return PH7_OK; } /* Perform the requested operation */ n = (int)pStream->xWrite(pDev->pHandle,(const void *)zString,nLen); if( n < 0 ){ /* IO error,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* #Bytes written */ ph7_result_int(pCtx,n); } return PH7_OK; } /* * bool flock(resource $handle,int $operation) * Portable advisory file locking. * Parameters * $handle * The file pointer. * $operation * operation is one of the following: * LOCK_SH to acquire a shared lock (reader). * LOCK_EX to acquire an exclusive lock (writer). * LOCK_UN to release a lock (shared or exclusive). * Return * Returns TRUE on success or FALSE on failure. */ static int PH7_builtin_flock(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; int nLock; int rc; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xLock == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Requested lock operation */ nLock = ph7_value_to_int(apArg[1]); /* Lock operation */ rc = pStream->xLock(pDev->pHandle,nLock); /* IO result */ ph7_result_bool(pCtx,rc == PH7_OK); return PH7_OK; } /* * int fpassthru(resource $handle) * Output all remaining data on a file pointer. * Parameters * $handle * The file pointer. * Return * Total number of characters read from handle and passed through * to the output on success or FALSE on failure. */ static int PH7_builtin_fpassthru(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; ph7_int64 n,nRead; char zBuf[8192]; int rc; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ nRead = 0; for(;;){ n = StreamRead(pDev,zBuf,sizeof(zBuf)); if( n < 1 ){ /* Error or EOF */ break; } /* Increment the read counter */ nRead += n; /* Output data */ rc = ph7_context_output(pCtx,zBuf,(int)nRead /* FIXME: 64-bit issues */); if( rc == PH7_ABORT ){ /* Consumer callback request an operation abort */ break; } } /* Total number of bytes readen */ ph7_result_int64(pCtx,nRead); return PH7_OK; } /* CSV reader/writer private data */ struct csv_data { int delimiter; /* Delimiter. Default ',' */ int enclosure; /* Enclosure. Default '"'*/ io_private *pDev; /* Open stream handle */ int iCount; /* Counter */ }; /* * The following callback is used by the fputcsv() function inorder to iterate * throw array entries and output CSV data based on the current key and it's * associated data. */ static int csv_write_callback(ph7_value *pKey,ph7_value *pValue,void *pUserData) { struct csv_data *pData = (struct csv_data *)pUserData; const char *zData; int nLen,c2; sxu32 n; /* Point to the raw data */ zData = ph7_value_to_string(pValue,&nLen); if( nLen < 1 ){ /* Nothing to write */ return PH7_OK; } if( pData->iCount > 0 ){ /* Write the delimiter */ pData->pDev->pStream->xWrite(pData->pDev->pHandle,(const void *)&pData->delimiter,sizeof(char)); } n = 1; c2 = 0; if( SyByteFind(zData,(sxu32)nLen,pData->delimiter,0) == SXRET_OK || SyByteFind(zData,(sxu32)nLen,pData->enclosure,&n) == SXRET_OK ){ c2 = 1; if( n == 0 ){ c2 = 2; } /* Write the enclosure */ pData->pDev->pStream->xWrite(pData->pDev->pHandle,(const void *)&pData->enclosure,sizeof(char)); if( c2 > 1 ){ pData->pDev->pStream->xWrite(pData->pDev->pHandle,(const void *)&pData->enclosure,sizeof(char)); } } /* Write the data */ if( pData->pDev->pStream->xWrite(pData->pDev->pHandle,(const void *)zData,(ph7_int64)nLen) < 1 ){ SXUNUSED(pKey); /* cc warning */ return PH7_ABORT; } if( c2 > 0 ){ /* Write the enclosure */ pData->pDev->pStream->xWrite(pData->pDev->pHandle,(const void *)&pData->enclosure,sizeof(char)); if( c2 > 1 ){ pData->pDev->pStream->xWrite(pData->pDev->pHandle,(const void *)&pData->enclosure,sizeof(char)); } } pData->iCount++; return PH7_OK; } /* * int fputcsv(resource $handle,array $fields[,string $delimiter = ','[,string $enclosure = '"' ]]) * Format line as CSV and write to file pointer. * Parameters * $handle * Open file handle. * $fields * An array of values. * $delimiter * The optional delimiter parameter sets the field delimiter (one character only). * $enclosure * The optional enclosure parameter sets the field enclosure (one character only). */ static int PH7_builtin_fputcsv(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; struct csv_data sCsv; io_private *pDev; char *zEol; int eolen; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) || !ph7_value_is_array(apArg[1]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Missing/Invalid arguments"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 || pStream->xWrite == 0){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Set default csv separator */ sCsv.delimiter = ','; sCsv.enclosure = '"'; sCsv.pDev = pDev; sCsv.iCount = 0; if( nArg > 2 ){ /* User delimiter */ const char *z; int n; z = ph7_value_to_string(apArg[2],&n); if( n > 0 ){ sCsv.delimiter = z[0]; } if( nArg > 3 ){ z = ph7_value_to_string(apArg[3],&n); if( n > 0 ){ sCsv.enclosure = z[0]; } } } /* Iterate throw array entries and write csv data */ ph7_array_walk(apArg[1],csv_write_callback,&sCsv); /* Write a line ending */ #ifdef __WINNT__ zEol = "\r\n"; eolen = (int)sizeof("\r\n")-1; #else /* Assume UNIX LF */ zEol = "\n"; eolen = (int)sizeof(char); #endif pDev->pStream->xWrite(pDev->pHandle,(const void *)zEol,eolen); return PH7_OK; } /* * fprintf,vfprintf private data. * An instance of the following structure is passed to the formatted * input consumer callback defined below. */ typedef struct fprintf_data fprintf_data; struct fprintf_data { io_private *pIO; /* IO stream */ ph7_int64 nCount; /* Total number of bytes written */ }; /* * Callback [i.e: Formatted input consumer] for the fprintf function. */ static int fprintfConsumer(ph7_context *pCtx,const char *zInput,int nLen,void *pUserData) { fprintf_data *pFdata = (fprintf_data *)pUserData; ph7_int64 n; /* Write the formatted data */ n = pFdata->pIO->pStream->xWrite(pFdata->pIO->pHandle,(const void *)zInput,nLen); if( n < 1 ){ SXUNUSED(pCtx); /* cc warning */ /* IO error,abort immediately */ return SXERR_ABORT; } /* Increment counter */ pFdata->nCount += n; return PH7_OK; } /* * int fprintf(resource $handle,string $format[,mixed $args [, mixed $... ]]) * Write a formatted string to a stream. * Parameters * $handle * The file pointer. * $format * String format (see sprintf()). * Return * The length of the written string. */ static int PH7_builtin_fprintf(ph7_context *pCtx,int nArg,ph7_value **apArg) { fprintf_data sFdata; const char *zFormat; io_private *pDev; int nLen; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) || !ph7_value_is_string(apArg[1]) ){ /* Missing/Invalid arguments,return zero */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Invalid arguments"); ph7_result_int(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_int(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device", ph7_function_name(pCtx),pDev->pStream ? pDev->pStream->zName : "null_stream" ); ph7_result_int(pCtx,0); return PH7_OK; } /* Extract the string format */ zFormat = ph7_value_to_string(apArg[1],&nLen); if( nLen < 1 ){ /* Empty string,return zero */ ph7_result_int(pCtx,0); return PH7_OK; } /* Prepare our private data */ sFdata.nCount = 0; sFdata.pIO = pDev; /* Format the string */ PH7_InputFormat(fprintfConsumer,pCtx,zFormat,nLen,nArg - 1,&apArg[1],(void *)&sFdata,FALSE); /* Return total number of bytes written */ ph7_result_int64(pCtx,sFdata.nCount); return PH7_OK; } /* * int vfprintf(resource $handle,string $format,array $args) * Write a formatted string to a stream. * Parameters * $handle * The file pointer. * $format * String format (see sprintf()). * $args * User arguments. * Return * The length of the written string. */ static int PH7_builtin_vfprintf(ph7_context *pCtx,int nArg,ph7_value **apArg) { fprintf_data sFdata; const char *zFormat; ph7_hashmap *pMap; io_private *pDev; SySet sArg; int n,nLen; if( nArg < 3 || !ph7_value_is_resource(apArg[0]) || !ph7_value_is_string(apArg[1]) || !ph7_value_is_array(apArg[2]) ){ /* Missing/Invalid arguments,return zero */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Invalid arguments"); ph7_result_int(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_int(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device", ph7_function_name(pCtx),pDev->pStream ? pDev->pStream->zName : "null_stream" ); ph7_result_int(pCtx,0); return PH7_OK; } /* Extract the string format */ zFormat = ph7_value_to_string(apArg[1],&nLen); if( nLen < 1 ){ /* Empty string,return zero */ ph7_result_int(pCtx,0); return PH7_OK; } /* Point to hashmap */ pMap = (ph7_hashmap *)apArg[2]->x.pOther; /* Extract arguments from the hashmap */ n = PH7_HashmapValuesToSet(pMap,&sArg); /* Prepare our private data */ sFdata.nCount = 0; sFdata.pIO = pDev; /* Format the string */ PH7_InputFormat(fprintfConsumer,pCtx,zFormat,nLen,n,(ph7_value **)SySetBasePtr(&sArg),(void *)&sFdata,TRUE); /* Return total number of bytes written*/ ph7_result_int64(pCtx,sFdata.nCount); SySetRelease(&sArg); return PH7_OK; } /* * Convert open modes (string passed to the fopen() function) [i.e: 'r','w+','a',...] into PH7 flags. * According to the PHP reference manual: * The mode parameter specifies the type of access you require to the stream. It may be any of the following * 'r' Open for reading only; place the file pointer at the beginning of the file. * 'r+' Open for reading and writing; place the file pointer at the beginning of the file. * 'w' Open for writing only; place the file pointer at the beginning of the file and truncate the file * to zero length. If the file does not exist, attempt to create it. * 'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate * the file to zero length. If the file does not exist, attempt to create it. * 'a' Open for writing only; place the file pointer at the end of the file. If the file does not * exist, attempt to create it. * 'a+' Open for reading and writing; place the file pointer at the end of the file. If the file does * not exist, attempt to create it. * 'x' Create and open for writing only; place the file pointer at the beginning of the file. If the file * already exists, * the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file * does not exist attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for * the underlying open(2) system call. * 'x+' Create and open for reading and writing; otherwise it has the same behavior as 'x'. * 'c' Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated * (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer * is positioned on the beginning of the file. * This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file * as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can * be used after the lock is requested). * 'c+' Open the file for reading and writing; otherwise it has the same behavior as 'c'. */ static int StrModeToFlags(ph7_context *pCtx,const char *zMode,int nLen) { const char *zEnd = &zMode[nLen]; int iFlag = 0; int c; if( nLen < 1 ){ /* Open in a read-only mode */ return PH7_IO_OPEN_RDONLY; } c = zMode[0]; if( c == 'r' || c == 'R' ){ /* Read-only access */ iFlag = PH7_IO_OPEN_RDONLY; zMode++; /* Advance */ if( zMode < zEnd ){ c = zMode[0]; if( c == '+' || c == 'w' || c == 'W' ){ /* Read+Write access */ iFlag = PH7_IO_OPEN_RDWR; } } }else if( c == 'w' || c == 'W' ){ /* Overwrite mode. * If the file does not exists,try to create it */ iFlag = PH7_IO_OPEN_WRONLY|PH7_IO_OPEN_TRUNC|PH7_IO_OPEN_CREATE; zMode++; /* Advance */ if( zMode < zEnd ){ c = zMode[0]; if( c == '+' || c == 'r' || c == 'R' ){ /* Read+Write access */ iFlag &= ~PH7_IO_OPEN_WRONLY; iFlag |= PH7_IO_OPEN_RDWR; } } }else if( c == 'a' || c == 'A' ){ /* Append mode (place the file pointer at the end of the file). * Create the file if it does not exists. */ iFlag = PH7_IO_OPEN_WRONLY|PH7_IO_OPEN_APPEND|PH7_IO_OPEN_CREATE; zMode++; /* Advance */ if( zMode < zEnd ){ c = zMode[0]; if( c == '+' ){ /* Read-Write access */ iFlag &= ~PH7_IO_OPEN_WRONLY; iFlag |= PH7_IO_OPEN_RDWR; } } }else if( c == 'x' || c == 'X' ){ /* Exclusive access. * If the file already exists,return immediately with a failure code. * Otherwise create a new file. */ iFlag = PH7_IO_OPEN_WRONLY|PH7_IO_OPEN_EXCL; zMode++; /* Advance */ if( zMode < zEnd ){ c = zMode[0]; if( c == '+' || c == 'r' || c == 'R' ){ /* Read-Write access */ iFlag &= ~PH7_IO_OPEN_WRONLY; iFlag |= PH7_IO_OPEN_RDWR; } } }else if( c == 'c' || c == 'C' ){ /* Overwrite mode.Create the file if it does not exists.*/ iFlag = PH7_IO_OPEN_WRONLY|PH7_IO_OPEN_CREATE; zMode++; /* Advance */ if( zMode < zEnd ){ c = zMode[0]; if( c == '+' ){ /* Read-Write access */ iFlag &= ~PH7_IO_OPEN_WRONLY; iFlag |= PH7_IO_OPEN_RDWR; } } }else{ /* Invalid mode. Assume a read only open */ ph7_context_throw_error(pCtx,PH7_CTX_NOTICE,"Invalid open mode,PH7 is assuming a Read-Only open"); iFlag = PH7_IO_OPEN_RDONLY; } while( zMode < zEnd ){ c = zMode[0]; if( c == 'b' || c == 'B' ){ iFlag &= ~PH7_IO_OPEN_TEXT; iFlag |= PH7_IO_OPEN_BINARY; }else if( c == 't' || c == 'T' ){ iFlag &= ~PH7_IO_OPEN_BINARY; iFlag |= PH7_IO_OPEN_TEXT; } zMode++; } return iFlag; } /* * Initialize the IO private structure. */ static void InitIOPrivate(ph7_vm *pVm,const ph7_io_stream *pStream,io_private *pOut) { pOut->pStream = pStream; SyBlobInit(&pOut->sBuffer,&pVm->sAllocator); pOut->nOfft = 0; /* Set the magic number */ pOut->iMagic = IO_PRIVATE_MAGIC; } /* * Release the IO private structure. */ static void ReleaseIOPrivate(ph7_context *pCtx,io_private *pDev) { SyBlobRelease(&pDev->sBuffer); pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */ /* Release the whole structure */ ph7_context_free_chunk(pCtx,pDev); } /* * Reset the IO private structure. */ static void ResetIOPrivate(io_private *pDev) { SyBlobReset(&pDev->sBuffer); pDev->nOfft = 0; } /* Forward declaration */ static int is_php_stream(const ph7_io_stream *pStream); /* * resource fopen(string $filename,string $mode [,bool $use_include_path = false[,resource $context ]]) * Open a file,a URL or any other IO stream. * Parameters * $filename * If filename is of the form "scheme://...", it is assumed to be a URL and PHP will search * for a protocol handler (also known as a wrapper) for that scheme. If no scheme is given * then a regular file is assumed. * $mode * The mode parameter specifies the type of access you require to the stream * See the block comment associated with the StrModeToFlags() for the supported * modes. * $use_include_path * You can use the optional second parameter and set it to * TRUE, if you want to search for the file in the include_path, too. * $context * A context stream resource. * Return * File handle on success or FALSE on failure. */ static int PH7_builtin_fopen(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; const char *zUri,*zMode; ph7_value *pResource; io_private *pDev; int iLen,imLen; int iOpenFlags; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path or URL"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the URI and the desired access mode */ zUri = ph7_value_to_string(apArg[0],&iLen); if( nArg > 1 ){ zMode = ph7_value_to_string(apArg[1],&imLen); }else{ /* Set a default read-only mode */ zMode = "r"; imLen = (int)sizeof(char); } /* Try to extract a stream */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zUri,iLen); if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "No stream device is associated with the given URI(%s)",zUri); ph7_result_bool(pCtx,0); return PH7_OK; } /* Allocate a new IO private instance */ pDev = (io_private *)ph7_context_alloc_chunk(pCtx,sizeof(io_private),TRUE,FALSE); if( pDev == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } pResource = 0; if( nArg > 3 ){ pResource = apArg[3]; }else if( is_php_stream(pStream) ){ /* TICKET 1433-80: The php:// stream need a ph7_value to access the underlying * virtual machine. */ pResource = apArg[0]; } /* Initialize the structure */ InitIOPrivate(pCtx->pVm,pStream,pDev); /* Convert open mode to PH7 flags */ iOpenFlags = StrModeToFlags(pCtx,zMode,imLen); /* Try to get a handle */ pDev->pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zUri,iOpenFlags, nArg > 2 ? ph7_value_to_bool(apArg[2]) : FALSE,pResource,FALSE,0); if( pDev->pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zUri); ph7_result_bool(pCtx,0); ph7_context_free_chunk(pCtx,pDev); return PH7_OK; } /* All done,return the io_private instance as a resource */ ph7_result_resource(pCtx,pDev); return PH7_OK; } /* * bool fclose(resource $handle) * Closes an open file pointer * Parameters * $handle * The file pointer. * Return * TRUE on success or FALSE on failure. */ static int PH7_builtin_fclose(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; io_private *pDev; ph7_vm *pVm; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract our private data */ pDev = (io_private *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid io_private instance */ if( IO_PRIVATE_INVALID(pDev) ){ /*Expecting an IO handle */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting an IO handle"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target IO stream device */ pStream = pDev->pStream; if( pStream == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_WARNING, "IO routine(%s) not implemented in the underlying stream(%s) device,PH7 is returning FALSE", ph7_function_name(pCtx),pStream ? pStream->zName : "null_stream" ); ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the VM that own this context */ pVm = pCtx->pVm; /* TICKET 1433-62: Keep the STDIN/STDOUT/STDERR handles open */ if( pDev != pVm->pStdin && pDev != pVm->pStdout && pDev != pVm->pStderr ){ /* Perform the requested operation */ PH7_StreamCloseHandle(pStream,pDev->pHandle); /* Release the IO private structure */ ReleaseIOPrivate(pCtx,pDev); /* Invalidate the resource handle */ ph7_value_release(apArg[0]); } /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } #if !defined(PH7_DISABLE_HASH_FUNC) /* * MD5/SHA1 digest consumer. */ static int vfsHashConsumer(const void *pData,unsigned int nLen,void *pUserData) { /* Append hex chunk verbatim */ ph7_result_string((ph7_context *)pUserData,(const char *)pData,(int)nLen); return SXRET_OK; } /* * string md5_file(string $uri[,bool $raw_output = false ]) * Calculates the md5 hash of a given file. * Parameters * $uri * Target URI (file(/path/to/something) or URL(http://www.symisc.net/)) * $raw_output * When TRUE, returns the digest in raw binary format with a length of 16. * Return * Return the MD5 digest on success or FALSE on failure. */ static int PH7_builtin_md5_file(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; unsigned char zDigest[16]; int raw_output = FALSE; const char *zFile; MD5Context sCtx; char zBuf[8192]; void *pHandle; ph7_int64 n; int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pStream == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ raw_output = ph7_value_to_bool(apArg[1]); } /* Try to open the file in read-only mode */ pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zFile,PH7_IO_OPEN_RDONLY,FALSE,0,FALSE,0); if( pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zFile); ph7_result_bool(pCtx,0); return PH7_OK; } /* Init the MD5 context */ MD5Init(&sCtx); /* Perform the requested operation */ for(;;){ n = pStream->xRead(pHandle,zBuf,sizeof(zBuf)); if( n < 1 ){ /* EOF or IO error,break immediately */ break; } MD5Update(&sCtx,(const unsigned char *)zBuf,(unsigned int)n); } /* Close the stream */ PH7_StreamCloseHandle(pStream,pHandle); /* Extract the digest */ MD5Final(zDigest,&sCtx); if( raw_output ){ /* Output raw digest */ ph7_result_string(pCtx,(const char *)zDigest,sizeof(zDigest)); }else{ /* Perform a binary to hex conversion */ SyBinToHexConsumer((const void *)zDigest,sizeof(zDigest),vfsHashConsumer,pCtx); } return PH7_OK; } /* * string sha1_file(string $uri[,bool $raw_output = false ]) * Calculates the SHA1 hash of a given file. * Parameters * $uri * Target URI (file(/path/to/something) or URL(http://www.symisc.net/)) * $raw_output * When TRUE, returns the digest in raw binary format with a length of 20. * Return * Return the SHA1 digest on success or FALSE on failure. */ static int PH7_builtin_sha1_file(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; unsigned char zDigest[20]; int raw_output = FALSE; const char *zFile; SHA1Context sCtx; char zBuf[8192]; void *pHandle; ph7_int64 n; int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pStream == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 1 ){ raw_output = ph7_value_to_bool(apArg[1]); } /* Try to open the file in read-only mode */ pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zFile,PH7_IO_OPEN_RDONLY,FALSE,0,FALSE,0); if( pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zFile); ph7_result_bool(pCtx,0); return PH7_OK; } /* Init the SHA1 context */ SHA1Init(&sCtx); /* Perform the requested operation */ for(;;){ n = pStream->xRead(pHandle,zBuf,sizeof(zBuf)); if( n < 1 ){ /* EOF or IO error,break immediately */ break; } SHA1Update(&sCtx,(const unsigned char *)zBuf,(unsigned int)n); } /* Close the stream */ PH7_StreamCloseHandle(pStream,pHandle); /* Extract the digest */ SHA1Final(&sCtx,zDigest); if( raw_output ){ /* Output raw digest */ ph7_result_string(pCtx,(const char *)zDigest,sizeof(zDigest)); }else{ /* Perform a binary to hex conversion */ SyBinToHexConsumer((const void *)zDigest,sizeof(zDigest),vfsHashConsumer,pCtx); } return PH7_OK; } #endif /* PH7_DISABLE_HASH_FUNC */ /* * array parse_ini_file(string $filename[, bool $process_sections = false [, int $scanner_mode = INI_SCANNER_NORMAL ]] ) * Parse a configuration file. * Parameters * $filename * The filename of the ini file being parsed. * $process_sections * By setting the process_sections parameter to TRUE, you get a multidimensional array * with the section names and settings included. * The default for process_sections is FALSE. * $scanner_mode * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. * If INI_SCANNER_RAW is supplied, then option values will not be parsed. * Return * The settings are returned as an associative array on success. * Otherwise is returned. */ static int PH7_builtin_parse_ini_file(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; const char *zFile; SyBlob sContents; void *pHandle; int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pStream == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Try to open the file in read-only mode */ pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zFile,PH7_IO_OPEN_RDONLY,FALSE,0,FALSE,0); if( pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zFile); ph7_result_bool(pCtx,0); return PH7_OK; } SyBlobInit(&sContents,&pCtx->pVm->sAllocator); /* Read the whole file */ PH7_StreamReadWholeFile(pHandle,pStream,&sContents); if( SyBlobLength(&sContents) < 1 ){ /* Empty buffer,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Process the raw INI buffer */ PH7_ParseIniString(pCtx,(const char *)SyBlobData(&sContents),SyBlobLength(&sContents), nArg > 1 ? ph7_value_to_bool(apArg[1]) : 0); } /* Close the stream */ PH7_StreamCloseHandle(pStream,pHandle); /* Release the working buffer */ SyBlobRelease(&sContents); return PH7_OK; } /* * Section: * ZIP archive processing. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ typedef struct zip_raw_data zip_raw_data; struct zip_raw_data { int iType; /* Where the raw data is stored */ union raw_data{ struct mmap_data{ void *pMap; /* Memory mapped data */ ph7_int64 nSize; /* Map size */ const ph7_vfs *pVfs; /* Underlying vfs */ }mmap; SyBlob sBlob; /* Memory buffer */ }raw; }; #define ZIP_RAW_DATA_MMAPED 1 /* Memory mapped ZIP raw data */ #define ZIP_RAW_DATA_MEMBUF 2 /* ZIP raw data stored in a dynamically * allocated memory chunk. */ /* * mixed zip_open(string $filename) * Opens a new zip archive for reading. * Parameters * $filename * The file name of the ZIP archive to open. * Return * A resource handle for later use with zip_read() and zip_close() or FALSE on failure. */ static int PH7_builtin_zip_open(ph7_context *pCtx,int nArg,ph7_value **apArg) { const ph7_io_stream *pStream; SyArchive *pArchive; zip_raw_data *pRaw; const char *zFile; SyBlob *pContents; void *pHandle; int nLen; sxi32 rc; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Expecting a file path"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the file path */ zFile = ph7_value_to_string(apArg[0],&nLen); /* Point to the target IO stream device */ pStream = PH7_VmGetStreamDevice(pCtx->pVm,&zFile,nLen); if( pStream == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"No such stream device,PH7 is returning FALSE"); ph7_result_bool(pCtx,0); return PH7_OK; } /* Create an in-memory archive */ pArchive = (SyArchive *)ph7_context_alloc_chunk(pCtx,sizeof(SyArchive)+sizeof(zip_raw_data),TRUE,FALSE); if( pArchive == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"PH7 is running out of memory"); ph7_result_bool(pCtx,0); return PH7_OK; } pRaw = (zip_raw_data *)&pArchive[1]; /* Initialize the archive */ SyArchiveInit(pArchive,&pCtx->pVm->sAllocator,0,0); /* Extract the default stream */ if( pStream == pCtx->pVm->pDefStream /* file:// stream*/){ const ph7_vfs *pVfs; /* Try to get a memory view of the whole file since ZIP files * tends to be very big this days,this is a huge performance win. */ pVfs = PH7_ExportBuiltinVfs(); if( pVfs && pVfs->xMmap ){ rc = pVfs->xMmap(zFile,&pRaw->raw.mmap.pMap,&pRaw->raw.mmap.nSize); if( rc == PH7_OK ){ /* Nice,Extract the whole archive */ rc = SyZipExtractFromBuf(pArchive,(const char *)pRaw->raw.mmap.pMap,(sxu32)pRaw->raw.mmap.nSize); if( rc != SXRET_OK ){ if( pVfs->xUnmap ){ pVfs->xUnmap(pRaw->raw.mmap.pMap,pRaw->raw.mmap.nSize); } /* Release the allocated chunk */ ph7_context_free_chunk(pCtx,pArchive); /* Something goes wrong with this ZIP archive,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Archive successfully opened */ pRaw->iType = ZIP_RAW_DATA_MMAPED; pRaw->raw.mmap.pVfs = pVfs; goto success; } } /* FALL THROUGH */ } /* Try to open the file in read-only mode */ pHandle = PH7_StreamOpenHandle(pCtx->pVm,pStream,zFile,PH7_IO_OPEN_RDONLY,FALSE,0,FALSE,0); if( pHandle == 0 ){ ph7_context_throw_error_format(pCtx,PH7_CTX_ERR,"IO error while opening '%s'",zFile); ph7_result_bool(pCtx,0); return PH7_OK; } pContents = &pRaw->raw.sBlob; SyBlobInit(pContents,&pCtx->pVm->sAllocator); /* Read the whole file */ PH7_StreamReadWholeFile(pHandle,pStream,pContents); /* Assume an invalid ZIP file */ rc = SXERR_INVALID; if( SyBlobLength(pContents) > 0 ){ /* Extract archive entries */ rc = SyZipExtractFromBuf(pArchive,(const char *)SyBlobData(pContents),SyBlobLength(pContents)); } pRaw->iType = ZIP_RAW_DATA_MEMBUF; /* Close the stream */ PH7_StreamCloseHandle(pStream,pHandle); if( rc != SXRET_OK ){ /* Release the working buffer */ SyBlobRelease(pContents); /* Release the allocated chunk */ ph7_context_free_chunk(pCtx,pArchive); /* Something goes wrong with this ZIP archive,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } success: /* Reset the loop cursor */ SyArchiveResetLoopCursor(pArchive); /* Return the in-memory archive as a resource handle */ ph7_result_resource(pCtx,pArchive); return PH7_OK; } /* * void zip_close(resource $zip) * Close an in-memory ZIP archive. * Parameters * $zip * A ZIP file previously opened with zip_open(). * Return * null. */ static int PH7_builtin_zip_close(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchive *pArchive; zip_raw_data *pRaw; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive"); return PH7_OK; } /* Point to the in-memory archive */ pArchive = (SyArchive *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid ZIP archive */ if( SXARCH_INVALID(pArchive) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive"); return PH7_OK; } /* Release the archive */ SyArchiveRelease(pArchive); pRaw = (zip_raw_data *)&pArchive[1]; if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){ SyBlobRelease(&pRaw->raw.sBlob); }else{ const ph7_vfs *pVfs = pRaw->raw.mmap.pVfs; if( pVfs->xUnmap ){ /* Unmap the memory view */ pVfs->xUnmap(pRaw->raw.mmap.pMap,pRaw->raw.mmap.nSize); } } /* Release the memory chunk */ ph7_context_free_chunk(pCtx,pArchive); return PH7_OK; } /* * mixed zip_read(resource $zip) * Reads the next entry from an in-memory ZIP archive. * Parameters * $zip * A ZIP file previously opened with zip_open(). * Return * A directory entry resource for later use with the zip_entry_... functions * or FALSE if there are no more entries to read, or an error code if an error occurred. */ static int PH7_builtin_zip_read(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pNext = 0; /* cc warning */ SyArchive *pArchive; sxi32 rc; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the in-memory archive */ pArchive = (SyArchive *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid ZIP archive */ if( SXARCH_INVALID(pArchive) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the next entry */ rc = SyArchiveGetNextEntry(pArchive,&pNext); if( rc != SXRET_OK ){ /* No more entries in the central directory,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Return as a resource handle */ ph7_result_resource(pCtx,pNext); /* Point to the ZIP raw data */ pNext->pUserData = (void *)&pArchive[1]; } return PH7_OK; } /* * bool zip_entry_open(resource $zip,resource $zip_entry[,string $mode ]) * Open a directory entry for reading * Parameters * $zip * A ZIP file previously opened with zip_open(). * $zip_entry * A directory entry returned by zip_read(). * $mode * Not used * Return * A directory entry resource for later use with the zip_entry_... functions * or FALSE if there are no more entries to read, or an error code if an error occurred. */ static int PH7_builtin_zip_entry_open(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pEntry; SyArchive *pArchive; if( nArg < 2 || !ph7_value_is_resource(apArg[0]) || !ph7_value_is_resource(apArg[1]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the in-memory archive */ pArchive = (SyArchive *)ph7_value_to_resource(apArg[0]); /* Make sure we are dealing with a valid ZIP archive */ if( SXARCH_INVALID(pArchive) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid ZIP archive entry */ pEntry = (SyArchiveEntry *)ph7_value_to_resource(apArg[1]); if( SXARCH_ENTRY_INVALID(pEntry) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* All done. Actually this function is a no-op,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool zip_entry_close(resource $zip_entry) * Close a directory entry. * Parameters * $zip_entry * A directory entry returned by zip_read(). * Return * Returns TRUE on success or FALSE on failure. */ static int PH7_builtin_zip_entry_close(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pEntry; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid ZIP archive entry */ pEntry = (SyArchiveEntry *)ph7_value_to_resource(apArg[0]); if( SXARCH_ENTRY_INVALID(pEntry) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Reset the read cursor */ pEntry->nReadCount = 0; /*All done. Actually this function is a no-op,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * string zip_entry_name(resource $zip_entry) * Retrieve the name of a directory entry. * Parameters * $zip_entry * A directory entry returned by zip_read(). * Return * The name of the directory entry. */ static int PH7_builtin_zip_entry_name(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pEntry; SyString *pName; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid ZIP archive entry */ pEntry = (SyArchiveEntry *)ph7_value_to_resource(apArg[0]); if( SXARCH_ENTRY_INVALID(pEntry) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return entry name */ pName = &pEntry->sFileName; ph7_result_string(pCtx,pName->zString,(int)pName->nByte); return PH7_OK; } /* * int64 zip_entry_filesize(resource $zip_entry) * Retrieve the actual file size of a directory entry. * Parameters * $zip_entry * A directory entry returned by zip_read(). * Return * The size of the directory entry. */ static int PH7_builtin_zip_entry_filesize(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pEntry; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid ZIP archive entry */ pEntry = (SyArchiveEntry *)ph7_value_to_resource(apArg[0]); if( SXARCH_ENTRY_INVALID(pEntry) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return entry size */ ph7_result_int64(pCtx,(ph7_int64)pEntry->nByte); return PH7_OK; } /* * int64 zip_entry_compressedsize(resource $zip_entry) * Retrieve the compressed size of a directory entry. * Parameters * $zip_entry * A directory entry returned by zip_read(). * Return * The compressed size. */ static int PH7_builtin_zip_entry_compressedsize(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pEntry; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid ZIP archive entry */ pEntry = (SyArchiveEntry *)ph7_value_to_resource(apArg[0]); if( SXARCH_ENTRY_INVALID(pEntry) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return entry compressed size */ ph7_result_int64(pCtx,(ph7_int64)pEntry->nByteCompr); return PH7_OK; } /* * string zip_entry_read(resource $zip_entry[,int $length]) * Reads from an open directory entry. * Parameters * $zip_entry * A directory entry returned by zip_read(). * $length * The number of bytes to return. If not specified, this function * will attempt to read 1024 bytes. * Return * Returns the data read, or FALSE if the end of the file is reached. */ static int PH7_builtin_zip_entry_read(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pEntry; zip_raw_data *pRaw; const char *zData; int iLength; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid ZIP archive entry */ pEntry = (SyArchiveEntry *)ph7_value_to_resource(apArg[0]); if( SXARCH_ENTRY_INVALID(pEntry) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } zData = 0; if( pEntry->nReadCount >= pEntry->nByteCompr ){ /* No more data to read,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Set a default read length */ iLength = 1024; if( nArg > 1 ){ iLength = ph7_value_to_int(apArg[1]); if( iLength < 1 ){ iLength = 1024; } } if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){ iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount); } /* Return the entry contents */ pRaw = (zip_raw_data *)pEntry->pUserData; if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){ zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob,(pEntry->nOfft+pEntry->nReadCount)); }else{ const char *zMap = (const char *)pRaw->raw.mmap.pMap; /* Memory mmaped chunk */ zData = &zMap[pEntry->nOfft+pEntry->nReadCount]; } /* Increment the read counter */ pEntry->nReadCount += iLength; /* Return the raw data */ ph7_result_string(pCtx,zData,iLength); return PH7_OK; } /* * bool zip_entry_reset_read_cursor(resource $zip_entry) * Reset the read cursor of an open directory entry. * Parameters * $zip_entry * A directory entry returned by zip_read(). * Return * TRUE on success,FALSE on failure. * Note that this is a symisc eXtension. */ static int PH7_builtin_zip_entry_reset_read_cursor(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pEntry; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid ZIP archive entry */ pEntry = (SyArchiveEntry *)ph7_value_to_resource(apArg[0]); if( SXARCH_ENTRY_INVALID(pEntry) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Reset the cursor */ pEntry->nReadCount = 0; /* Return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * string zip_entry_compressionmethod(resource $zip_entry) * Retrieve the compression method of a directory entry. * Parameters * $zip_entry * A directory entry returned by zip_read(). * Return * The compression method on success or FALSE on failure. */ static int PH7_builtin_zip_entry_compressionmethod(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyArchiveEntry *pEntry; if( nArg < 1 || !ph7_value_is_resource(apArg[0]) ){ /* Missing/Invalid arguments */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid ZIP archive entry */ pEntry = (SyArchiveEntry *)ph7_value_to_resource(apArg[0]); if( SXARCH_ENTRY_INVALID(pEntry) ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"Expecting a ZIP archive entry"); /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } switch(pEntry->nComprMeth){ case 0: /* No compression;entry is stored */ ph7_result_string(pCtx,"stored",(int)sizeof("stored")-1); break; case 8: /* Entry is deflated (Default compression algorithm) */ ph7_result_string(pCtx,"deflate",(int)sizeof("deflate")-1); break; /* Exotic compression algorithms */ case 1: ph7_result_string(pCtx,"shrunk",(int)sizeof("shrunk")-1); break; case 2: case 3: case 4: case 5: /* Entry is reduced */ ph7_result_string(pCtx,"reduced",(int)sizeof("reduced")-1); break; case 6: /* Entry is imploded */ ph7_result_string(pCtx,"implode",(int)sizeof("implode")-1); break; default: ph7_result_string(pCtx,"unknown",(int)sizeof("unknown")-1); break; } return PH7_OK; } #endif /* #ifndef PH7_DISABLE_BUILTIN_FUNC*/ /* NULL VFS [i.e: a no-op VFS]*/ static const ph7_vfs null_vfs = { "null_vfs", PH7_VFS_VERSION, 0, /* int (*xChdir)(const char *) */ 0, /* int (*xChroot)(const char *); */ 0, /* int (*xGetcwd)(ph7_context *) */ 0, /* int (*xMkdir)(const char *,int,int) */ 0, /* int (*xRmdir)(const char *) */ 0, /* int (*xIsdir)(const char *) */ 0, /* int (*xRename)(const char *,const char *) */ 0, /*int (*xRealpath)(const char *,ph7_context *)*/ 0, /* int (*xSleep)(unsigned int) */ 0, /* int (*xUnlink)(const char *) */ 0, /* int (*xFileExists)(const char *) */ 0, /*int (*xChmod)(const char *,int)*/ 0, /*int (*xChown)(const char *,const char *)*/ 0, /*int (*xChgrp)(const char *,const char *)*/ 0, /* ph7_int64 (*xFreeSpace)(const char *) */ 0, /* ph7_int64 (*xTotalSpace)(const char *) */ 0, /* ph7_int64 (*xFileSize)(const char *) */ 0, /* ph7_int64 (*xFileAtime)(const char *) */ 0, /* ph7_int64 (*xFileMtime)(const char *) */ 0, /* ph7_int64 (*xFileCtime)(const char *) */ 0, /* int (*xStat)(const char *,ph7_value *,ph7_value *) */ 0, /* int (*xlStat)(const char *,ph7_value *,ph7_value *) */ 0, /* int (*xIsfile)(const char *) */ 0, /* int (*xIslink)(const char *) */ 0, /* int (*xReadable)(const char *) */ 0, /* int (*xWritable)(const char *) */ 0, /* int (*xExecutable)(const char *) */ 0, /* int (*xFiletype)(const char *,ph7_context *) */ 0, /* int (*xGetenv)(const char *,ph7_context *) */ 0, /* int (*xSetenv)(const char *,const char *) */ 0, /* int (*xTouch)(const char *,ph7_int64,ph7_int64) */ 0, /* int (*xMmap)(const char *,void **,ph7_int64 *) */ 0, /* void (*xUnmap)(void *,ph7_int64); */ 0, /* int (*xLink)(const char *,const char *,int) */ 0, /* int (*xUmask)(int) */ 0, /* void (*xTempDir)(ph7_context *) */ 0, /* unsigned int (*xProcessId)(void) */ 0, /* int (*xUid)(void) */ 0, /* int (*xGid)(void) */ 0, /* void (*xUsername)(ph7_context *) */ 0 /* int (*xExec)(const char *,ph7_context *) */ }; #ifndef PH7_DISABLE_BUILTIN_FUNC #ifndef PH7_DISABLE_DISK_IO #ifdef __WINNT__ /* * Windows VFS implementation for the PH7 engine. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* What follows here is code that is specific to windows systems. */ #include /* ** Convert a UTF-8 string to microsoft unicode (UTF-16?). ** ** Space to hold the returned string is obtained from HeapAlloc(). ** Taken from the sqlite3 source tree ** status: Public Domain */ static WCHAR *utf8ToUnicode(const char *zFilename){ int nChar; WCHAR *zWideFilename; nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0); zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nChar*sizeof(zWideFilename[0])); if( zWideFilename == 0 ){ return 0; } nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); if( nChar==0 ){ HeapFree(GetProcessHeap(),0,zWideFilename); return 0; } return zWideFilename; } /* ** Convert a UTF-8 filename into whatever form the underlying ** operating system wants filenames in.Space to hold the result ** is obtained from HeapAlloc() and must be freed by the calling ** function. ** Taken from the sqlite3 source tree ** status: Public Domain */ static void *convertUtf8Filename(const char *zFilename){ void *zConverted; zConverted = utf8ToUnicode(zFilename); return zConverted; } /* ** Convert microsoft unicode to UTF-8. Space to hold the returned string is ** obtained from HeapAlloc(). ** Taken from the sqlite3 source tree ** status: Public Domain */ static char *unicodeToUtf8(const WCHAR *zWideFilename){ char *zFilename; int nByte; nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); zFilename = (char *)HeapAlloc(GetProcessHeap(),0,nByte); if( zFilename == 0 ){ return 0; } nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,0, 0); if( nByte == 0 ){ HeapFree(GetProcessHeap(),0,zFilename); return 0; } return zFilename; } /* int (*xchdir)(const char *) */ static int WinVfs_chdir(const char *zPath) { void * pConverted; BOOL rc; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } rc = SetCurrentDirectoryW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); return rc ? PH7_OK : -1; } /* int (*xGetcwd)(ph7_context *) */ static int WinVfs_getcwd(ph7_context *pCtx) { WCHAR zDir[2048]; char *zConverted; DWORD rc; /* Get the current directory */ rc = GetCurrentDirectoryW(sizeof(zDir),zDir); if( rc < 1 ){ return -1; } zConverted = unicodeToUtf8(zDir); if( zConverted == 0 ){ return -1; } ph7_result_string(pCtx,zConverted,-1/*Compute length automatically*/); /* Will make it's own copy */ HeapFree(GetProcessHeap(),0,zConverted); return PH7_OK; } /* int (*xMkdir)(const char *,int,int) */ static int WinVfs_mkdir(const char *zPath,int mode,int recursive) { void * pConverted; BOOL rc; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } mode= 0; /* MSVC warning */ recursive = 0; rc = CreateDirectoryW((LPCWSTR)pConverted,0); HeapFree(GetProcessHeap(),0,pConverted); return rc ? PH7_OK : -1; } /* int (*xRmdir)(const char *) */ static int WinVfs_rmdir(const char *zPath) { void * pConverted; BOOL rc; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } rc = RemoveDirectoryW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); return rc ? PH7_OK : -1; } /* int (*xIsdir)(const char *) */ static int WinVfs_isdir(const char *zPath) { void * pConverted; DWORD dwAttr; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } dwAttr = GetFileAttributesW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( dwAttr == INVALID_FILE_ATTRIBUTES ){ return -1; } return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? PH7_OK : -1; } /* int (*xRename)(const char *,const char *) */ static int WinVfs_Rename(const char *zOld,const char *zNew) { void *pOld,*pNew; BOOL rc = 0; pOld = convertUtf8Filename(zOld); if( pOld == 0 ){ return -1; } pNew = convertUtf8Filename(zNew); if( pNew ){ rc = MoveFileW((LPCWSTR)pOld,(LPCWSTR)pNew); } HeapFree(GetProcessHeap(),0,pOld); if( pNew ){ HeapFree(GetProcessHeap(),0,pNew); } return rc ? PH7_OK : - 1; } /* int (*xRealpath)(const char *,ph7_context *) */ static int WinVfs_Realpath(const char *zPath,ph7_context *pCtx) { WCHAR zTemp[2048]; void *pPath; char *zReal; DWORD n; pPath = convertUtf8Filename(zPath); if( pPath == 0 ){ return -1; } n = GetFullPathNameW((LPCWSTR)pPath,0,0,0); if( n > 0 ){ if( n >= sizeof(zTemp) ){ n = sizeof(zTemp) - 1; } GetFullPathNameW((LPCWSTR)pPath,n,zTemp,0); } HeapFree(GetProcessHeap(),0,pPath); if( !n ){ return -1; } zReal = unicodeToUtf8(zTemp); if( zReal == 0 ){ return -1; } ph7_result_string(pCtx,zReal,-1); /* Will make it's own copy */ HeapFree(GetProcessHeap(),0,zReal); return PH7_OK; } /* int (*xSleep)(unsigned int) */ static int WinVfs_Sleep(unsigned int uSec) { Sleep(uSec/1000/*uSec per Millisec */); return PH7_OK; } /* int (*xUnlink)(const char *) */ static int WinVfs_unlink(const char *zPath) { void * pConverted; BOOL rc; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } rc = DeleteFileW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); return rc ? PH7_OK : - 1; } /* ph7_int64 (*xFreeSpace)(const char *) */ static ph7_int64 WinVfs_DiskFreeSpace(const char *zPath) { #ifdef _WIN32_WCE /* GetDiskFreeSpace is not supported under WINCE */ SXUNUSED(zPath); return 0; #else DWORD dwSectPerClust,dwBytesPerSect,dwFreeClusters,dwTotalClusters; void * pConverted; WCHAR *p; BOOL rc; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return 0; } p = (WCHAR *)pConverted; for(;*p;p++){ if( *p == '\\' || *p == '/'){ *p = '\0'; break; } } rc = GetDiskFreeSpaceW((LPCWSTR)pConverted,&dwSectPerClust,&dwBytesPerSect,&dwFreeClusters,&dwTotalClusters); if( !rc ){ return 0; } return (ph7_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect; #endif } /* ph7_int64 (*xTotalSpace)(const char *) */ static ph7_int64 WinVfs_DiskTotalSpace(const char *zPath) { #ifdef _WIN32_WCE /* GetDiskFreeSpace is not supported under WINCE */ SXUNUSED(zPath); return 0; #else DWORD dwSectPerClust,dwBytesPerSect,dwFreeClusters,dwTotalClusters; void * pConverted; WCHAR *p; BOOL rc; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return 0; } p = (WCHAR *)pConverted; for(;*p;p++){ if( *p == '\\' || *p == '/'){ *p = '\0'; break; } } rc = GetDiskFreeSpaceW((LPCWSTR)pConverted,&dwSectPerClust,&dwBytesPerSect,&dwFreeClusters,&dwTotalClusters); if( !rc ){ return 0; } return (ph7_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect; #endif } /* int (*xFileExists)(const char *) */ static int WinVfs_FileExists(const char *zPath) { void * pConverted; DWORD dwAttr; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } dwAttr = GetFileAttributesW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( dwAttr == INVALID_FILE_ATTRIBUTES ){ return -1; } return PH7_OK; } /* Open a file in a read-only mode */ static HANDLE OpenReadOnly(LPCWSTR pPath) { DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; DWORD dwAccess = GENERIC_READ; DWORD dwCreate = OPEN_EXISTING; HANDLE pHandle; pHandle = CreateFileW(pPath,dwAccess,dwShare,0,dwCreate,dwType,0); if( pHandle == INVALID_HANDLE_VALUE){ return 0; } return pHandle; } /* ph7_int64 (*xFileSize)(const char *) */ static ph7_int64 WinVfs_FileSize(const char *zPath) { DWORD dwLow,dwHigh; void * pConverted; ph7_int64 nSize; HANDLE pHandle; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } /* Open the file in read-only mode */ pHandle = OpenReadOnly((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( pHandle ){ dwLow = GetFileSize(pHandle,&dwHigh); nSize = dwHigh; nSize <<= 32; nSize += dwLow; CloseHandle(pHandle); }else{ nSize = -1; } return nSize; } #define TICKS_PER_SECOND 10000000 #define EPOCH_DIFFERENCE 11644473600LL /* Convert Windows timestamp to UNIX timestamp */ static ph7_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime) { ph7_int64 input,temp; input = pTime->dwHighDateTime; input <<= 32; input += pTime->dwLowDateTime; temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/ temp = temp - EPOCH_DIFFERENCE; /*subtract number of seconds between epochs*/ return temp; } /* Convert UNIX timestamp to Windows timestamp */ static void convertUnixTimeToWindowsTime(ph7_int64 nUnixtime,LPFILETIME pOut) { ph7_int64 result = EPOCH_DIFFERENCE; result += nUnixtime; result *= 10000000LL; pOut->dwHighDateTime = (DWORD)(nUnixtime>>32); pOut->dwLowDateTime = (DWORD)nUnixtime; } /* int (*xTouch)(const char *,ph7_int64,ph7_int64) */ static int WinVfs_Touch(const char *zPath,ph7_int64 touch_time,ph7_int64 access_time) { FILETIME sTouch,sAccess; void *pConverted; void *pHandle; BOOL rc = 0; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } pHandle = OpenReadOnly((LPCWSTR)pConverted); if( pHandle ){ if( touch_time < 0 ){ GetSystemTimeAsFileTime(&sTouch); }else{ convertUnixTimeToWindowsTime(touch_time,&sTouch); } if( access_time < 0 ){ /* Use the touch time */ sAccess = sTouch; /* Structure assignment */ }else{ convertUnixTimeToWindowsTime(access_time,&sAccess); } rc = SetFileTime(pHandle,&sTouch,&sAccess,0); /* Close the handle */ CloseHandle(pHandle); } HeapFree(GetProcessHeap(),0,pConverted); return rc ? PH7_OK : -1; } /* ph7_int64 (*xFileAtime)(const char *) */ static ph7_int64 WinVfs_FileAtime(const char *zPath) { BY_HANDLE_FILE_INFORMATION sInfo; void * pConverted; ph7_int64 atime; HANDLE pHandle; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } /* Open the file in read-only mode */ pHandle = OpenReadOnly((LPCWSTR)pConverted); if( pHandle ){ BOOL rc; rc = GetFileInformationByHandle(pHandle,&sInfo); if( rc ){ atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime); }else{ atime = -1; } CloseHandle(pHandle); }else{ atime = -1; } HeapFree(GetProcessHeap(),0,pConverted); return atime; } /* ph7_int64 (*xFileMtime)(const char *) */ static ph7_int64 WinVfs_FileMtime(const char *zPath) { BY_HANDLE_FILE_INFORMATION sInfo; void * pConverted; ph7_int64 mtime; HANDLE pHandle; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } /* Open the file in read-only mode */ pHandle = OpenReadOnly((LPCWSTR)pConverted); if( pHandle ){ BOOL rc; rc = GetFileInformationByHandle(pHandle,&sInfo); if( rc ){ mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime); }else{ mtime = -1; } CloseHandle(pHandle); }else{ mtime = -1; } HeapFree(GetProcessHeap(),0,pConverted); return mtime; } /* ph7_int64 (*xFileCtime)(const char *) */ static ph7_int64 WinVfs_FileCtime(const char *zPath) { BY_HANDLE_FILE_INFORMATION sInfo; void * pConverted; ph7_int64 ctime; HANDLE pHandle; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } /* Open the file in read-only mode */ pHandle = OpenReadOnly((LPCWSTR)pConverted); if( pHandle ){ BOOL rc; rc = GetFileInformationByHandle(pHandle,&sInfo); if( rc ){ ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime); }else{ ctime = -1; } CloseHandle(pHandle); }else{ ctime = -1; } HeapFree(GetProcessHeap(),0,pConverted); return ctime; } /* int (*xStat)(const char *,ph7_value *,ph7_value *) */ /* int (*xlStat)(const char *,ph7_value *,ph7_value *) */ static int WinVfs_Stat(const char *zPath,ph7_value *pArray,ph7_value *pWorker) { BY_HANDLE_FILE_INFORMATION sInfo; void *pConverted; HANDLE pHandle; BOOL rc; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } /* Open the file in read-only mode */ pHandle = OpenReadOnly((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( pHandle == 0 ){ return -1; } rc = GetFileInformationByHandle(pHandle,&sInfo); CloseHandle(pHandle); if( !rc ){ return -1; } /* dev */ ph7_value_int64(pWorker,(ph7_int64)sInfo.dwVolumeSerialNumber); ph7_array_add_strkey_elem(pArray,"dev",pWorker); /* Will make it's own copy */ /* ino */ ph7_value_int64(pWorker,(ph7_int64)(((ph7_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow)); ph7_array_add_strkey_elem(pArray,"ino",pWorker); /* Will make it's own copy */ /* mode */ ph7_value_int(pWorker,0); ph7_array_add_strkey_elem(pArray,"mode",pWorker); /* nlink */ ph7_value_int(pWorker,(int)sInfo.nNumberOfLinks); ph7_array_add_strkey_elem(pArray,"nlink",pWorker); /* Will make it's own copy */ /* uid,gid,rdev */ ph7_value_int(pWorker,0); ph7_array_add_strkey_elem(pArray,"uid",pWorker); ph7_array_add_strkey_elem(pArray,"gid",pWorker); ph7_array_add_strkey_elem(pArray,"rdev",pWorker); /* size */ ph7_value_int64(pWorker,(ph7_int64)(((ph7_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow)); ph7_array_add_strkey_elem(pArray,"size",pWorker); /* Will make it's own copy */ /* atime */ ph7_value_int64(pWorker,convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime)); ph7_array_add_strkey_elem(pArray,"atime",pWorker); /* Will make it's own copy */ /* mtime */ ph7_value_int64(pWorker,convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime)); ph7_array_add_strkey_elem(pArray,"mtime",pWorker); /* Will make it's own copy */ /* ctime */ ph7_value_int64(pWorker,convertWindowsTimeToUnixTime(&sInfo.ftCreationTime)); ph7_array_add_strkey_elem(pArray,"ctime",pWorker); /* Will make it's own copy */ /* blksize,blocks */ ph7_value_int(pWorker,0); ph7_array_add_strkey_elem(pArray,"blksize",pWorker); ph7_array_add_strkey_elem(pArray,"blocks",pWorker); return PH7_OK; } /* int (*xIsfile)(const char *) */ static int WinVfs_isfile(const char *zPath) { void * pConverted; DWORD dwAttr; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } dwAttr = GetFileAttributesW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( dwAttr == INVALID_FILE_ATTRIBUTES ){ return -1; } return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? PH7_OK : -1; } /* int (*xIslink)(const char *) */ static int WinVfs_islink(const char *zPath) { void * pConverted; DWORD dwAttr; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } dwAttr = GetFileAttributesW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( dwAttr == INVALID_FILE_ATTRIBUTES ){ return -1; } return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? PH7_OK : -1; } /* int (*xWritable)(const char *) */ static int WinVfs_iswritable(const char *zPath) { void * pConverted; DWORD dwAttr; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } dwAttr = GetFileAttributesW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( dwAttr == INVALID_FILE_ATTRIBUTES ){ return -1; } if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){ /* Not a regular file */ return -1; } if( dwAttr & FILE_ATTRIBUTE_READONLY ){ /* Read-only file */ return -1; } /* File is writable */ return PH7_OK; } /* int (*xExecutable)(const char *) */ static int WinVfs_isexecutable(const char *zPath) { void * pConverted; DWORD dwAttr; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } dwAttr = GetFileAttributesW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( dwAttr == INVALID_FILE_ATTRIBUTES ){ return -1; } if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){ /* Not a regular file */ return -1; } /* File is executable */ return PH7_OK; } /* int (*xFiletype)(const char *,ph7_context *) */ static int WinVfs_Filetype(const char *zPath,ph7_context *pCtx) { void * pConverted; DWORD dwAttr; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ /* Expand 'unknown' */ ph7_result_string(pCtx,"unknown",sizeof("unknown")-1); return -1; } dwAttr = GetFileAttributesW((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( dwAttr == INVALID_FILE_ATTRIBUTES ){ /* Expand 'unknown' */ ph7_result_string(pCtx,"unknown",sizeof("unknown")-1); return -1; } if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){ ph7_result_string(pCtx,"file",sizeof("file")-1); }else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){ ph7_result_string(pCtx,"dir",sizeof("dir")-1); }else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){ ph7_result_string(pCtx,"link",sizeof("link")-1); }else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){ ph7_result_string(pCtx,"block",sizeof("block")-1); }else{ ph7_result_string(pCtx,"unknown",sizeof("unknown")-1); } return PH7_OK; } /* int (*xGetenv)(const char *,ph7_context *) */ static int WinVfs_Getenv(const char *zVar,ph7_context *pCtx) { char zValue[1024]; DWORD n; /* * According to MSDN * If lpBuffer is not large enough to hold the data, the return * value is the buffer size, in characters, required to hold the * string and its terminating null character and the contents * of lpBuffer are undefined. */ n = sizeof(zValue); SyMemcpy("Undefined",zValue,sizeof("Undefined")-1); /* Extract the environment value */ n = GetEnvironmentVariableA(zVar,zValue,sizeof(zValue)); if( !n ){ /* No such variable*/ return -1; } ph7_result_string(pCtx,zValue,(int)n); return PH7_OK; } /* int (*xSetenv)(const char *,const char *) */ static int WinVfs_Setenv(const char *zName,const char *zValue) { BOOL rc; rc = SetEnvironmentVariableA(zName,zValue); return rc ? PH7_OK : -1; } /* int (*xMmap)(const char *,void **,ph7_int64 *) */ static int WinVfs_Mmap(const char *zPath,void **ppMap,ph7_int64 *pSize) { DWORD dwSizeLow,dwSizeHigh; HANDLE pHandle,pMapHandle; void *pConverted,*pView; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } pHandle = OpenReadOnly((LPCWSTR)pConverted); HeapFree(GetProcessHeap(),0,pConverted); if( pHandle == 0 ){ return -1; } /* Get the file size */ dwSizeLow = GetFileSize(pHandle,&dwSizeHigh); /* Create the mapping */ pMapHandle = CreateFileMappingW(pHandle,0,PAGE_READONLY,dwSizeHigh,dwSizeLow,0); if( pMapHandle == 0 ){ CloseHandle(pHandle); return -1; } *pSize = ((ph7_int64)dwSizeHigh << 32) | dwSizeLow; /* Obtain the view */ pView = MapViewOfFile(pMapHandle,FILE_MAP_READ,0,0,(SIZE_T)(*pSize)); if( pView ){ /* Let the upper layer point to the view */ *ppMap = pView; } /* Close the handle * According to MSDN it's OK the close the HANDLES. */ CloseHandle(pMapHandle); CloseHandle(pHandle); return pView ? PH7_OK : -1; } /* void (*xUnmap)(void *,ph7_int64) */ static void WinVfs_Unmap(void *pView,ph7_int64 nSize) { nSize = 0; /* Compiler warning */ UnmapViewOfFile(pView); } /* void (*xTempDir)(ph7_context *) */ static void WinVfs_TempDir(ph7_context *pCtx) { CHAR zTemp[1024]; DWORD n; n = GetTempPathA(sizeof(zTemp),zTemp); if( n < 1 ){ /* Assume the default windows temp directory */ ph7_result_string(pCtx,"C:\\Windows\\Temp",-1/*Compute length automatically*/); }else{ ph7_result_string(pCtx,zTemp,(int)n); } } /* unsigned int (*xProcessId)(void) */ static unsigned int WinVfs_ProcessId(void) { DWORD nID = 0; #ifndef __MINGW32__ nID = GetProcessId(GetCurrentProcess()); #endif /* __MINGW32__ */ return (unsigned int)nID; } /* void (*xUsername)(ph7_context *) */ static void WinVfs_Username(ph7_context *pCtx) { WCHAR zUser[1024]; DWORD nByte; BOOL rc; nByte = sizeof(zUser); rc = GetUserNameW(zUser,&nByte); if( !rc ){ /* Set a dummy name */ ph7_result_string(pCtx,"Unknown",sizeof("Unknown")-1); }else{ char *zName; zName = unicodeToUtf8(zUser); if( zName == 0 ){ ph7_result_string(pCtx,"Unknown",sizeof("Unknown")-1); }else{ ph7_result_string(pCtx,zName,-1/*Compute length automatically*/); /* Will make it's own copy */ HeapFree(GetProcessHeap(),0,zName); } } } /* Export the windows vfs */ static const ph7_vfs sWinVfs = { "Windows_vfs", PH7_VFS_VERSION, WinVfs_chdir, /* int (*xChdir)(const char *) */ 0, /* int (*xChroot)(const char *); */ WinVfs_getcwd, /* int (*xGetcwd)(ph7_context *) */ WinVfs_mkdir, /* int (*xMkdir)(const char *,int,int) */ WinVfs_rmdir, /* int (*xRmdir)(const char *) */ WinVfs_isdir, /* int (*xIsdir)(const char *) */ WinVfs_Rename, /* int (*xRename)(const char *,const char *) */ WinVfs_Realpath, /*int (*xRealpath)(const char *,ph7_context *)*/ WinVfs_Sleep, /* int (*xSleep)(unsigned int) */ WinVfs_unlink, /* int (*xUnlink)(const char *) */ WinVfs_FileExists, /* int (*xFileExists)(const char *) */ 0, /*int (*xChmod)(const char *,int)*/ 0, /*int (*xChown)(const char *,const char *)*/ 0, /*int (*xChgrp)(const char *,const char *)*/ WinVfs_DiskFreeSpace,/* ph7_int64 (*xFreeSpace)(const char *) */ WinVfs_DiskTotalSpace,/* ph7_int64 (*xTotalSpace)(const char *) */ WinVfs_FileSize, /* ph7_int64 (*xFileSize)(const char *) */ WinVfs_FileAtime,/* ph7_int64 (*xFileAtime)(const char *) */ WinVfs_FileMtime,/* ph7_int64 (*xFileMtime)(const char *) */ WinVfs_FileCtime,/* ph7_int64 (*xFileCtime)(const char *) */ WinVfs_Stat, /* int (*xStat)(const char *,ph7_value *,ph7_value *) */ WinVfs_Stat, /* int (*xlStat)(const char *,ph7_value *,ph7_value *) */ WinVfs_isfile, /* int (*xIsfile)(const char *) */ WinVfs_islink, /* int (*xIslink)(const char *) */ WinVfs_isfile, /* int (*xReadable)(const char *) */ WinVfs_iswritable, /* int (*xWritable)(const char *) */ WinVfs_isexecutable, /* int (*xExecutable)(const char *) */ WinVfs_Filetype, /* int (*xFiletype)(const char *,ph7_context *) */ WinVfs_Getenv, /* int (*xGetenv)(const char *,ph7_context *) */ WinVfs_Setenv, /* int (*xSetenv)(const char *,const char *) */ WinVfs_Touch, /* int (*xTouch)(const char *,ph7_int64,ph7_int64) */ WinVfs_Mmap, /* int (*xMmap)(const char *,void **,ph7_int64 *) */ WinVfs_Unmap, /* void (*xUnmap)(void *,ph7_int64); */ 0, /* int (*xLink)(const char *,const char *,int) */ 0, /* int (*xUmask)(int) */ WinVfs_TempDir, /* void (*xTempDir)(ph7_context *) */ WinVfs_ProcessId, /* unsigned int (*xProcessId)(void) */ 0, /* int (*xUid)(void) */ 0, /* int (*xGid)(void) */ WinVfs_Username, /* void (*xUsername)(ph7_context *) */ 0 /* int (*xExec)(const char *,ph7_context *) */ }; /* Windows file IO */ #ifndef INVALID_SET_FILE_POINTER # define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif /* int (*xOpen)(const char *,int,ph7_value *,void **) */ static int WinFile_Open(const char *zPath,int iOpenMode,ph7_value *pResource,void **ppHandle) { DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; DWORD dwAccess = GENERIC_READ; DWORD dwShare,dwCreate; void *pConverted; HANDLE pHandle; pConverted = convertUtf8Filename(zPath); if( pConverted == 0 ){ return -1; } /* Set the desired flags according to the open mode */ if( iOpenMode & PH7_IO_OPEN_CREATE ){ /* Open existing file, or create if it doesn't exist */ dwCreate = OPEN_ALWAYS; if( iOpenMode & PH7_IO_OPEN_TRUNC ){ /* If the specified file exists and is writable, the function overwrites the file */ dwCreate = CREATE_ALWAYS; } }else if( iOpenMode & PH7_IO_OPEN_EXCL ){ /* Creates a new file, only if it does not already exist. * If the file exists, it fails. */ dwCreate = CREATE_NEW; }else if( iOpenMode & PH7_IO_OPEN_TRUNC ){ /* Opens a file and truncates it so that its size is zero bytes * The file must exist. */ dwCreate = TRUNCATE_EXISTING; }else{ /* Opens a file, only if it exists. */ dwCreate = OPEN_EXISTING; } if( iOpenMode & PH7_IO_OPEN_RDWR ){ /* Read+Write access */ dwAccess |= GENERIC_WRITE; }else if( iOpenMode & PH7_IO_OPEN_WRONLY ){ /* Write only access */ dwAccess = GENERIC_WRITE; } if( iOpenMode & PH7_IO_OPEN_APPEND ){ /* Append mode */ dwAccess = FILE_APPEND_DATA; } if( iOpenMode & PH7_IO_OPEN_TEMP ){ /* File is temporary */ dwType = FILE_ATTRIBUTE_TEMPORARY; } dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; pHandle = CreateFileW((LPCWSTR)pConverted,dwAccess,dwShare,0,dwCreate,dwType,0); HeapFree(GetProcessHeap(),0,pConverted); if( pHandle == INVALID_HANDLE_VALUE){ SXUNUSED(pResource); /* MSVC warning */ return -1; } /* Make the handle accessible to the upper layer */ *ppHandle = (void *)pHandle; return PH7_OK; } /* An instance of the following structure is used to record state information * while iterating throw directory entries. */ typedef struct WinDir_Info WinDir_Info; struct WinDir_Info { HANDLE pDirHandle; void *pPath; WIN32_FIND_DATAW sInfo; int rc; }; /* int (*xOpenDir)(const char *,ph7_value *,void **) */ static int WinDir_Open(const char *zPath,ph7_value *pResource,void **ppHandle) { WinDir_Info *pDirInfo; void *pConverted; char *zPrep; sxu32 n; /* Prepare the path */ n = SyStrlen(zPath); zPrep = (char *)HeapAlloc(GetProcessHeap(),0,n+sizeof("\\*")+4); if( zPrep == 0 ){ return -1; } SyMemcpy((const void *)zPath,zPrep,n); zPrep[n] = '\\'; zPrep[n+1] = '*'; zPrep[n+2] = 0; pConverted = convertUtf8Filename(zPrep); HeapFree(GetProcessHeap(),0,zPrep); if( pConverted == 0 ){ return -1; } /* Allocate a new instance */ pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(),0,sizeof(WinDir_Info)); if( pDirInfo == 0 ){ pResource = 0; /* Compiler warning */ return -1; } pDirInfo->rc = SXRET_OK; pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted,&pDirInfo->sInfo); if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){ /* Cannot open directory */ HeapFree(GetProcessHeap(),0,pConverted); HeapFree(GetProcessHeap(),0,pDirInfo); return -1; } /* Save the path */ pDirInfo->pPath = pConverted; /* Save our structure */ *ppHandle = pDirInfo; return PH7_OK; } /* void (*xCloseDir)(void *) */ static void WinDir_Close(void *pUserData) { WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){ FindClose(pDirInfo->pDirHandle); } HeapFree(GetProcessHeap(),0,pDirInfo->pPath); HeapFree(GetProcessHeap(),0,pDirInfo); } /* void (*xClose)(void *); */ static void WinFile_Close(void *pUserData) { HANDLE pHandle = (HANDLE)pUserData; CloseHandle(pHandle); } /* int (*xReadDir)(void *,ph7_context *) */ static int WinDir_Read(void *pUserData,ph7_context *pCtx) { WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; LPWIN32_FIND_DATAW pData; char *zName; BOOL rc; sxu32 n; if( pDirInfo->rc != SXRET_OK ){ /* No more entry to process */ return -1; } pData = &pDirInfo->sInfo; for(;;){ zName = unicodeToUtf8(pData->cFileName); if( zName == 0 ){ /* Out of memory */ return -1; } n = SyStrlen(zName); /* Ignore '.' && '..' */ if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){ break; } HeapFree(GetProcessHeap(),0,zName); rc = FindNextFileW(pDirInfo->pDirHandle,&pDirInfo->sInfo); if( !rc ){ return -1; } } /* Return the current file name */ ph7_result_string(pCtx,zName,-1); HeapFree(GetProcessHeap(),0,zName); /* Point to the next entry */ rc = FindNextFileW(pDirInfo->pDirHandle,&pDirInfo->sInfo); if( !rc ){ pDirInfo->rc = SXERR_EOF; } return PH7_OK; } /* void (*xRewindDir)(void *) */ static void WinDir_RewindDir(void *pUserData) { WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; FindClose(pDirInfo->pDirHandle); pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath,&pDirInfo->sInfo); if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){ pDirInfo->rc = SXERR_EOF; }else{ pDirInfo->rc = SXRET_OK; } } /* ph7_int64 (*xRead)(void *,void *,ph7_int64); */ static ph7_int64 WinFile_Read(void *pOS,void *pBuffer,ph7_int64 nDatatoRead) { HANDLE pHandle = (HANDLE)pOS; DWORD nRd; BOOL rc; rc = ReadFile(pHandle,pBuffer,(DWORD)nDatatoRead,&nRd,0); if( !rc ){ /* EOF or IO error */ return -1; } return (ph7_int64)nRd; } /* ph7_int64 (*xWrite)(void *,const void *,ph7_int64); */ static ph7_int64 WinFile_Write(void *pOS,const void *pBuffer,ph7_int64 nWrite) { const char *zData = (const char *)pBuffer; HANDLE pHandle = (HANDLE)pOS; ph7_int64 nCount; DWORD nWr; BOOL rc; nWr = 0; nCount = 0; for(;;){ if( nWrite < 1 ){ break; } rc = WriteFile(pHandle,zData,(DWORD)nWrite,&nWr,0); if( !rc ){ /* IO error */ break; } nWrite -= nWr; nCount += nWr; zData += nWr; } if( nWrite > 0 ){ return -1; } return nCount; } /* int (*xSeek)(void *,ph7_int64,int) */ static int WinFile_Seek(void *pUserData,ph7_int64 iOfft,int whence) { HANDLE pHandle = (HANDLE)pUserData; DWORD dwMove,dwNew; LONG nHighOfft; switch(whence){ case 1:/*SEEK_CUR*/ dwMove = FILE_CURRENT; break; case 2: /* SEEK_END */ dwMove = FILE_END; break; case 0: /* SEEK_SET */ default: dwMove = FILE_BEGIN; break; } nHighOfft = (LONG)(iOfft >> 32); dwNew = SetFilePointer(pHandle,(LONG)iOfft,&nHighOfft,dwMove); if( dwNew == INVALID_SET_FILE_POINTER ){ return -1; } return PH7_OK; } /* int (*xLock)(void *,int) */ static int WinFile_Lock(void *pUserData,int lock_type) { HANDLE pHandle = (HANDLE)pUserData; static DWORD dwLo = 0,dwHi = 0; /* xx: MT-SAFE */ OVERLAPPED sDummy; BOOL rc; SyZero(&sDummy,sizeof(sDummy)); /* Get the file size */ if( lock_type < 1 ){ /* Unlock the file */ rc = UnlockFileEx(pHandle,0,dwLo,dwHi,&sDummy); }else{ DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/ /* Lock the file */ if( lock_type == 1 /* LOCK_EXCL */ ){ dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; } dwLo = GetFileSize(pHandle,&dwHi); rc = LockFileEx(pHandle,dwFlags,0,dwLo,dwHi,&sDummy); } return rc ? PH7_OK : -1 /* Lock error */; } /* ph7_int64 (*xTell)(void *) */ static ph7_int64 WinFile_Tell(void *pUserData) { HANDLE pHandle = (HANDLE)pUserData; DWORD dwNew; dwNew = SetFilePointer(pHandle,0,0,FILE_CURRENT/* SEEK_CUR */); if( dwNew == INVALID_SET_FILE_POINTER ){ return -1; } return (ph7_int64)dwNew; } /* int (*xTrunc)(void *,ph7_int64) */ static int WinFile_Trunc(void *pUserData,ph7_int64 nOfft) { HANDLE pHandle = (HANDLE)pUserData; LONG HighOfft; DWORD dwNew; BOOL rc; HighOfft = (LONG)(nOfft >> 32); dwNew = SetFilePointer(pHandle,(LONG)nOfft,&HighOfft,FILE_BEGIN); if( dwNew == INVALID_SET_FILE_POINTER ){ return -1; } rc = SetEndOfFile(pHandle); return rc ? PH7_OK : -1; } /* int (*xSync)(void *); */ static int WinFile_Sync(void *pUserData) { HANDLE pHandle = (HANDLE)pUserData; BOOL rc; rc = FlushFileBuffers(pHandle); return rc ? PH7_OK : - 1; } /* int (*xStat)(void *,ph7_value *,ph7_value *) */ static int WinFile_Stat(void *pUserData,ph7_value *pArray,ph7_value *pWorker) { BY_HANDLE_FILE_INFORMATION sInfo; HANDLE pHandle = (HANDLE)pUserData; BOOL rc; rc = GetFileInformationByHandle(pHandle,&sInfo); if( !rc ){ return -1; } /* dev */ ph7_value_int64(pWorker,(ph7_int64)sInfo.dwVolumeSerialNumber); ph7_array_add_strkey_elem(pArray,"dev",pWorker); /* Will make it's own copy */ /* ino */ ph7_value_int64(pWorker,(ph7_int64)(((ph7_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow)); ph7_array_add_strkey_elem(pArray,"ino",pWorker); /* Will make it's own copy */ /* mode */ ph7_value_int(pWorker,0); ph7_array_add_strkey_elem(pArray,"mode",pWorker); /* nlink */ ph7_value_int(pWorker,(int)sInfo.nNumberOfLinks); ph7_array_add_strkey_elem(pArray,"nlink",pWorker); /* Will make it's own copy */ /* uid,gid,rdev */ ph7_value_int(pWorker,0); ph7_array_add_strkey_elem(pArray,"uid",pWorker); ph7_array_add_strkey_elem(pArray,"gid",pWorker); ph7_array_add_strkey_elem(pArray,"rdev",pWorker); /* size */ ph7_value_int64(pWorker,(ph7_int64)(((ph7_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow)); ph7_array_add_strkey_elem(pArray,"size",pWorker); /* Will make it's own copy */ /* atime */ ph7_value_int64(pWorker,convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime)); ph7_array_add_strkey_elem(pArray,"atime",pWorker); /* Will make it's own copy */ /* mtime */ ph7_value_int64(pWorker,convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime)); ph7_array_add_strkey_elem(pArray,"mtime",pWorker); /* Will make it's own copy */ /* ctime */ ph7_value_int64(pWorker,convertWindowsTimeToUnixTime(&sInfo.ftCreationTime)); ph7_array_add_strkey_elem(pArray,"ctime",pWorker); /* Will make it's own copy */ /* blksize,blocks */ ph7_value_int(pWorker,0); ph7_array_add_strkey_elem(pArray,"blksize",pWorker); ph7_array_add_strkey_elem(pArray,"blocks",pWorker); return PH7_OK; } /* Export the file:// stream */ static const ph7_io_stream sWinFileStream = { "file", /* Stream name */ PH7_IO_STREAM_VERSION, WinFile_Open, /* xOpen */ WinDir_Open, /* xOpenDir */ WinFile_Close, /* xClose */ WinDir_Close, /* xCloseDir */ WinFile_Read, /* xRead */ WinDir_Read, /* xReadDir */ WinFile_Write, /* xWrite */ WinFile_Seek, /* xSeek */ WinFile_Lock, /* xLock */ WinDir_RewindDir, /* xRewindDir */ WinFile_Tell, /* xTell */ WinFile_Trunc, /* xTrunc */ WinFile_Sync, /* xSeek */ WinFile_Stat /* xStat */ }; #elif defined(__UNIXES__) /* * UNIX VFS implementation for the PH7 engine. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* int (*xchdir)(const char *) */ static int UnixVfs_chdir(const char *zPath) { int rc; rc = chdir(zPath); return rc == 0 ? PH7_OK : -1; } /* int (*xGetcwd)(ph7_context *) */ static int UnixVfs_getcwd(ph7_context *pCtx) { char zBuf[4096]; char *zDir; /* Get the current directory */ zDir = getcwd(zBuf,sizeof(zBuf)); if( zDir == 0 ){ return -1; } ph7_result_string(pCtx,zDir,-1/*Compute length automatically*/); return PH7_OK; } /* int (*xMkdir)(const char *,int,int) */ static int UnixVfs_mkdir(const char *zPath,int mode,int recursive) { int rc; rc = mkdir(zPath,mode); recursive = 0; /* cc warning */ return rc == 0 ? PH7_OK : -1; } /* int (*xRmdir)(const char *) */ static int UnixVfs_rmdir(const char *zPath) { int rc; rc = rmdir(zPath); return rc == 0 ? PH7_OK : -1; } /* int (*xIsdir)(const char *) */ static int UnixVfs_isdir(const char *zPath) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ return -1; } rc = S_ISDIR(st.st_mode); return rc ? PH7_OK : -1 ; } /* int (*xRename)(const char *,const char *) */ static int UnixVfs_Rename(const char *zOld,const char *zNew) { int rc; rc = rename(zOld,zNew); return rc == 0 ? PH7_OK : -1; } /* int (*xRealpath)(const char *,ph7_context *) */ static int UnixVfs_Realpath(const char *zPath,ph7_context *pCtx) { #ifndef PH7_UNIX_OLD_LIBC char *zReal; zReal = realpath(zPath,0); if( zReal == 0 ){ return -1; } ph7_result_string(pCtx,zReal,-1/*Compute length automatically*/); /* Release the allocated buffer */ free(zReal); return PH7_OK; #else zPath = 0; /* cc warning */ pCtx = 0; return -1; #endif } /* int (*xSleep)(unsigned int) */ static int UnixVfs_Sleep(unsigned int uSec) { usleep(uSec); return PH7_OK; } /* int (*xUnlink)(const char *) */ static int UnixVfs_unlink(const char *zPath) { int rc; rc = unlink(zPath); return rc == 0 ? PH7_OK : -1 ; } /* int (*xFileExists)(const char *) */ static int UnixVfs_FileExists(const char *zPath) { int rc; rc = access(zPath,F_OK); return rc == 0 ? PH7_OK : -1; } /* ph7_int64 (*xFileSize)(const char *) */ static ph7_int64 UnixVfs_FileSize(const char *zPath) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ return -1; } return (ph7_int64)st.st_size; } /* int (*xTouch)(const char *,ph7_int64,ph7_int64) */ static int UnixVfs_Touch(const char *zPath,ph7_int64 touch_time,ph7_int64 access_time) { struct utimbuf ut; int rc; ut.actime = (time_t)access_time; ut.modtime = (time_t)touch_time; rc = utime(zPath,&ut); if( rc != 0 ){ return -1; } return PH7_OK; } /* ph7_int64 (*xFileAtime)(const char *) */ static ph7_int64 UnixVfs_FileAtime(const char *zPath) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ return -1; } return (ph7_int64)st.st_atime; } /* ph7_int64 (*xFileMtime)(const char *) */ static ph7_int64 UnixVfs_FileMtime(const char *zPath) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ return -1; } return (ph7_int64)st.st_mtime; } /* ph7_int64 (*xFileCtime)(const char *) */ static ph7_int64 UnixVfs_FileCtime(const char *zPath) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ return -1; } return (ph7_int64)st.st_ctime; } /* int (*xStat)(const char *,ph7_value *,ph7_value *) */ static int UnixVfs_Stat(const char *zPath,ph7_value *pArray,ph7_value *pWorker) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ return -1; } /* dev */ ph7_value_int64(pWorker,(ph7_int64)st.st_dev); ph7_array_add_strkey_elem(pArray,"dev",pWorker); /* Will make it's own copy */ /* ino */ ph7_value_int64(pWorker,(ph7_int64)st.st_ino); ph7_array_add_strkey_elem(pArray,"ino",pWorker); /* Will make it's own copy */ /* mode */ ph7_value_int(pWorker,(int)st.st_mode); ph7_array_add_strkey_elem(pArray,"mode",pWorker); /* nlink */ ph7_value_int(pWorker,(int)st.st_nlink); ph7_array_add_strkey_elem(pArray,"nlink",pWorker); /* Will make it's own copy */ /* uid,gid,rdev */ ph7_value_int(pWorker,(int)st.st_uid); ph7_array_add_strkey_elem(pArray,"uid",pWorker); ph7_value_int(pWorker,(int)st.st_gid); ph7_array_add_strkey_elem(pArray,"gid",pWorker); ph7_value_int(pWorker,(int)st.st_rdev); ph7_array_add_strkey_elem(pArray,"rdev",pWorker); /* size */ ph7_value_int64(pWorker,(ph7_int64)st.st_size); ph7_array_add_strkey_elem(pArray,"size",pWorker); /* Will make it's own copy */ /* atime */ ph7_value_int64(pWorker,(ph7_int64)st.st_atime); ph7_array_add_strkey_elem(pArray,"atime",pWorker); /* Will make it's own copy */ /* mtime */ ph7_value_int64(pWorker,(ph7_int64)st.st_mtime); ph7_array_add_strkey_elem(pArray,"mtime",pWorker); /* Will make it's own copy */ /* ctime */ ph7_value_int64(pWorker,(ph7_int64)st.st_ctime); ph7_array_add_strkey_elem(pArray,"ctime",pWorker); /* Will make it's own copy */ /* blksize,blocks */ ph7_value_int(pWorker,(int)st.st_blksize); ph7_array_add_strkey_elem(pArray,"blksize",pWorker); ph7_value_int(pWorker,(int)st.st_blocks); ph7_array_add_strkey_elem(pArray,"blocks",pWorker); return PH7_OK; } /* int (*xlStat)(const char *,ph7_value *,ph7_value *) */ static int UnixVfs_lStat(const char *zPath,ph7_value *pArray,ph7_value *pWorker) { struct stat st; int rc; rc = lstat(zPath,&st); if( rc != 0 ){ return -1; } /* dev */ ph7_value_int64(pWorker,(ph7_int64)st.st_dev); ph7_array_add_strkey_elem(pArray,"dev",pWorker); /* Will make it's own copy */ /* ino */ ph7_value_int64(pWorker,(ph7_int64)st.st_ino); ph7_array_add_strkey_elem(pArray,"ino",pWorker); /* Will make it's own copy */ /* mode */ ph7_value_int(pWorker,(int)st.st_mode); ph7_array_add_strkey_elem(pArray,"mode",pWorker); /* nlink */ ph7_value_int(pWorker,(int)st.st_nlink); ph7_array_add_strkey_elem(pArray,"nlink",pWorker); /* Will make it's own copy */ /* uid,gid,rdev */ ph7_value_int(pWorker,(int)st.st_uid); ph7_array_add_strkey_elem(pArray,"uid",pWorker); ph7_value_int(pWorker,(int)st.st_gid); ph7_array_add_strkey_elem(pArray,"gid",pWorker); ph7_value_int(pWorker,(int)st.st_rdev); ph7_array_add_strkey_elem(pArray,"rdev",pWorker); /* size */ ph7_value_int64(pWorker,(ph7_int64)st.st_size); ph7_array_add_strkey_elem(pArray,"size",pWorker); /* Will make it's own copy */ /* atime */ ph7_value_int64(pWorker,(ph7_int64)st.st_atime); ph7_array_add_strkey_elem(pArray,"atime",pWorker); /* Will make it's own copy */ /* mtime */ ph7_value_int64(pWorker,(ph7_int64)st.st_mtime); ph7_array_add_strkey_elem(pArray,"mtime",pWorker); /* Will make it's own copy */ /* ctime */ ph7_value_int64(pWorker,(ph7_int64)st.st_ctime); ph7_array_add_strkey_elem(pArray,"ctime",pWorker); /* Will make it's own copy */ /* blksize,blocks */ ph7_value_int(pWorker,(int)st.st_blksize); ph7_array_add_strkey_elem(pArray,"blksize",pWorker); ph7_value_int(pWorker,(int)st.st_blocks); ph7_array_add_strkey_elem(pArray,"blocks",pWorker); return PH7_OK; } /* int (*xChmod)(const char *,int) */ static int UnixVfs_Chmod(const char *zPath,int mode) { int rc; rc = chmod(zPath,(mode_t)mode); return rc == 0 ? PH7_OK : - 1; } /* int (*xChown)(const char *,const char *) */ static int UnixVfs_Chown(const char *zPath,const char *zUser) { #ifndef PH7_UNIX_STATIC_BUILD struct passwd *pwd; uid_t uid; int rc; pwd = getpwnam(zUser); /* Try getting UID for username */ if (pwd == 0) { return -1; } uid = pwd->pw_uid; rc = chown(zPath,uid,-1); return rc == 0 ? PH7_OK : -1; #else SXUNUSED(zPath); SXUNUSED(zUser); return -1; #endif /* PH7_UNIX_STATIC_BUILD */ } /* int (*xChgrp)(const char *,const char *) */ static int UnixVfs_Chgrp(const char *zPath,const char *zGroup) { #ifndef PH7_UNIX_STATIC_BUILD struct group *group; gid_t gid; int rc; group = getgrnam(zGroup); if (group == 0) { return -1; } gid = group->gr_gid; rc = chown(zPath,-1,gid); return rc == 0 ? PH7_OK : -1; #else SXUNUSED(zPath); SXUNUSED(zGroup); return -1; #endif /* PH7_UNIX_STATIC_BUILD */ } /* int (*xIsfile)(const char *) */ static int UnixVfs_isfile(const char *zPath) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ return -1; } rc = S_ISREG(st.st_mode); return rc ? PH7_OK : -1 ; } /* int (*xIslink)(const char *) */ static int UnixVfs_islink(const char *zPath) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ return -1; } rc = S_ISLNK(st.st_mode); return rc ? PH7_OK : -1 ; } /* int (*xReadable)(const char *) */ static int UnixVfs_isreadable(const char *zPath) { int rc; rc = access(zPath,R_OK); return rc == 0 ? PH7_OK : -1; } /* int (*xWritable)(const char *) */ static int UnixVfs_iswritable(const char *zPath) { int rc; rc = access(zPath,W_OK); return rc == 0 ? PH7_OK : -1; } /* int (*xExecutable)(const char *) */ static int UnixVfs_isexecutable(const char *zPath) { int rc; rc = access(zPath,X_OK); return rc == 0 ? PH7_OK : -1; } /* int (*xFiletype)(const char *,ph7_context *) */ static int UnixVfs_Filetype(const char *zPath,ph7_context *pCtx) { struct stat st; int rc; rc = stat(zPath,&st); if( rc != 0 ){ /* Expand 'unknown' */ ph7_result_string(pCtx,"unknown",sizeof("unknown")-1); return -1; } if(S_ISREG(st.st_mode) ){ ph7_result_string(pCtx,"file",sizeof("file")-1); }else if(S_ISDIR(st.st_mode)){ ph7_result_string(pCtx,"dir",sizeof("dir")-1); }else if(S_ISLNK(st.st_mode)){ ph7_result_string(pCtx,"link",sizeof("link")-1); }else if(S_ISBLK(st.st_mode)){ ph7_result_string(pCtx,"block",sizeof("block")-1); }else if(S_ISSOCK(st.st_mode)){ ph7_result_string(pCtx,"socket",sizeof("socket")-1); }else if(S_ISFIFO(st.st_mode)){ ph7_result_string(pCtx,"fifo",sizeof("fifo")-1); }else{ ph7_result_string(pCtx,"unknown",sizeof("unknown")-1); } return PH7_OK; } /* int (*xGetenv)(const char *,ph7_context *) */ static int UnixVfs_Getenv(const char *zVar,ph7_context *pCtx) { char *zEnv; zEnv = getenv(zVar); if( zEnv == 0 ){ return -1; } ph7_result_string(pCtx,zEnv,-1/*Compute length automatically*/); return PH7_OK; } /* int (*xSetenv)(const char *,const char *) */ static int UnixVfs_Setenv(const char *zName,const char *zValue) { int rc; rc = setenv(zName,zValue,1); return rc == 0 ? PH7_OK : -1; } /* int (*xMmap)(const char *,void **,ph7_int64 *) */ static int UnixVfs_Mmap(const char *zPath,void **ppMap,ph7_int64 *pSize) { struct stat st; void *pMap; int fd; int rc; /* Open the file in a read-only mode */ fd = open(zPath,O_RDONLY); if( fd < 0 ){ return -1; } /* stat the handle */ fstat(fd,&st); /* Obtain a memory view of the whole file */ pMap = mmap(0,st.st_size,PROT_READ,MAP_PRIVATE|MAP_FILE,fd,0); rc = PH7_OK; if( pMap == MAP_FAILED ){ rc = -1; }else{ /* Point to the memory view */ *ppMap = pMap; *pSize = (ph7_int64)st.st_size; } close(fd); return rc; } /* void (*xUnmap)(void *,ph7_int64) */ static void UnixVfs_Unmap(void *pView,ph7_int64 nSize) { munmap(pView,(size_t)nSize); } /* void (*xTempDir)(ph7_context *) */ static void UnixVfs_TempDir(ph7_context *pCtx) { static const char *azDirs[] = { "/var/tmp", "/usr/tmp", "/usr/local/tmp" }; unsigned int i; struct stat buf; const char *zDir; zDir = getenv("TMPDIR"); if( zDir && zDir[0] != 0 && !access(zDir,07) ){ ph7_result_string(pCtx,zDir,-1); return; } for(i=0; ipw_name,-1); #else ph7_result_string(pCtx,"Unknown",-1); #endif /* PH7_UNIX_STATIC_BUILD */ return; } /* int (*xLink)(const char *,const char *,int) */ static int UnixVfs_link(const char *zSrc,const char *zTarget,int is_sym) { int rc; if( is_sym ){ /* Symbolic link */ rc = symlink(zSrc,zTarget); }else{ /* Hard link */ rc = link(zSrc,zTarget); } return rc == 0 ? PH7_OK : -1; } /* int (*xChroot)(const char *) */ static int UnixVfs_chroot(const char *zRootDir) { int rc; rc = chroot(zRootDir); return rc == 0 ? PH7_OK : -1; } /* Export the UNIX vfs */ static const ph7_vfs sUnixVfs = { "Unix_vfs", PH7_VFS_VERSION, UnixVfs_chdir, /* int (*xChdir)(const char *) */ UnixVfs_chroot, /* int (*xChroot)(const char *); */ UnixVfs_getcwd, /* int (*xGetcwd)(ph7_context *) */ UnixVfs_mkdir, /* int (*xMkdir)(const char *,int,int) */ UnixVfs_rmdir, /* int (*xRmdir)(const char *) */ UnixVfs_isdir, /* int (*xIsdir)(const char *) */ UnixVfs_Rename, /* int (*xRename)(const char *,const char *) */ UnixVfs_Realpath, /*int (*xRealpath)(const char *,ph7_context *)*/ UnixVfs_Sleep, /* int (*xSleep)(unsigned int) */ UnixVfs_unlink, /* int (*xUnlink)(const char *) */ UnixVfs_FileExists, /* int (*xFileExists)(const char *) */ UnixVfs_Chmod, /*int (*xChmod)(const char *,int)*/ UnixVfs_Chown, /*int (*xChown)(const char *,const char *)*/ UnixVfs_Chgrp, /*int (*xChgrp)(const char *,const char *)*/ 0, /* ph7_int64 (*xFreeSpace)(const char *) */ 0, /* ph7_int64 (*xTotalSpace)(const char *) */ UnixVfs_FileSize, /* ph7_int64 (*xFileSize)(const char *) */ UnixVfs_FileAtime,/* ph7_int64 (*xFileAtime)(const char *) */ UnixVfs_FileMtime,/* ph7_int64 (*xFileMtime)(const char *) */ UnixVfs_FileCtime,/* ph7_int64 (*xFileCtime)(const char *) */ UnixVfs_Stat, /* int (*xStat)(const char *,ph7_value *,ph7_value *) */ UnixVfs_lStat, /* int (*xlStat)(const char *,ph7_value *,ph7_value *) */ UnixVfs_isfile, /* int (*xIsfile)(const char *) */ UnixVfs_islink, /* int (*xIslink)(const char *) */ UnixVfs_isreadable, /* int (*xReadable)(const char *) */ UnixVfs_iswritable, /* int (*xWritable)(const char *) */ UnixVfs_isexecutable,/* int (*xExecutable)(const char *) */ UnixVfs_Filetype, /* int (*xFiletype)(const char *,ph7_context *) */ UnixVfs_Getenv, /* int (*xGetenv)(const char *,ph7_context *) */ UnixVfs_Setenv, /* int (*xSetenv)(const char *,const char *) */ UnixVfs_Touch, /* int (*xTouch)(const char *,ph7_int64,ph7_int64) */ UnixVfs_Mmap, /* int (*xMmap)(const char *,void **,ph7_int64 *) */ UnixVfs_Unmap, /* void (*xUnmap)(void *,ph7_int64); */ UnixVfs_link, /* int (*xLink)(const char *,const char *,int) */ UnixVfs_Umask, /* int (*xUmask)(int) */ UnixVfs_TempDir, /* void (*xTempDir)(ph7_context *) */ UnixVfs_ProcessId, /* unsigned int (*xProcessId)(void) */ UnixVfs_uid, /* int (*xUid)(void) */ UnixVfs_gid, /* int (*xGid)(void) */ UnixVfs_Username, /* void (*xUsername)(ph7_context *) */ 0 /* int (*xExec)(const char *,ph7_context *) */ }; /* UNIX File IO */ #define PH7_UNIX_OPEN_MODE 0640 /* Default open mode */ /* int (*xOpen)(const char *,int,ph7_value *,void **) */ static int UnixFile_Open(const char *zPath,int iOpenMode,ph7_value *pResource,void **ppHandle) { int iOpen = O_RDONLY; int fd; /* Set the desired flags according to the open mode */ if( iOpenMode & PH7_IO_OPEN_CREATE ){ /* Open existing file, or create if it doesn't exist */ iOpen = O_CREAT; if( iOpenMode & PH7_IO_OPEN_TRUNC ){ /* If the specified file exists and is writable, the function overwrites the file */ iOpen |= O_TRUNC; SXUNUSED(pResource); /* cc warning */ } }else if( iOpenMode & PH7_IO_OPEN_EXCL ){ /* Creates a new file, only if it does not already exist. * If the file exists, it fails. */ iOpen = O_CREAT|O_EXCL; }else if( iOpenMode & PH7_IO_OPEN_TRUNC ){ /* Opens a file and truncates it so that its size is zero bytes * The file must exist. */ iOpen = O_RDWR|O_TRUNC; } if( iOpenMode & PH7_IO_OPEN_RDWR ){ /* Read+Write access */ iOpen &= ~O_RDONLY; iOpen |= O_RDWR; }else if( iOpenMode & PH7_IO_OPEN_WRONLY ){ /* Write only access */ iOpen &= ~O_RDONLY; iOpen |= O_WRONLY; } if( iOpenMode & PH7_IO_OPEN_APPEND ){ /* Append mode */ iOpen |= O_APPEND; } #ifdef O_TEMP if( iOpenMode & PH7_IO_OPEN_TEMP ){ /* File is temporary */ iOpen |= O_TEMP; } #endif /* Open the file now */ fd = open(zPath,iOpen,PH7_UNIX_OPEN_MODE); if( fd < 0 ){ /* IO error */ return -1; } /* Save the handle */ *ppHandle = SX_INT_TO_PTR(fd); return PH7_OK; } /* int (*xOpenDir)(const char *,ph7_value *,void **) */ static int UnixDir_Open(const char *zPath,ph7_value *pResource,void **ppHandle) { DIR *pDir; /* Open the target directory */ pDir = opendir(zPath); if( pDir == 0 ){ pResource = 0; /* Compiler warning */ return -1; } /* Save our structure */ *ppHandle = pDir; return PH7_OK; } /* void (*xCloseDir)(void *) */ static void UnixDir_Close(void *pUserData) { closedir((DIR *)pUserData); } /* void (*xClose)(void *); */ static void UnixFile_Close(void *pUserData) { close(SX_PTR_TO_INT(pUserData)); } /* int (*xReadDir)(void *,ph7_context *) */ static int UnixDir_Read(void *pUserData,ph7_context *pCtx) { DIR *pDir = (DIR *)pUserData; struct dirent *pEntry; char *zName = 0; /* cc warning */ sxu32 n = 0; for(;;){ pEntry = readdir(pDir); if( pEntry == 0 ){ /* No more entries to process */ return -1; } zName = pEntry->d_name; n = SyStrlen(zName); /* Ignore '.' && '..' */ if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){ break; } /* Next entry */ } /* Return the current file name */ ph7_result_string(pCtx,zName,(int)n); return PH7_OK; } /* void (*xRewindDir)(void *) */ static void UnixDir_Rewind(void *pUserData) { rewinddir((DIR *)pUserData); } /* ph7_int64 (*xRead)(void *,void *,ph7_int64); */ static ph7_int64 UnixFile_Read(void *pUserData,void *pBuffer,ph7_int64 nDatatoRead) { ssize_t nRd; nRd = read(SX_PTR_TO_INT(pUserData),pBuffer,(size_t)nDatatoRead); if( nRd < 1 ){ /* EOF or IO error */ return -1; } return (ph7_int64)nRd; } /* ph7_int64 (*xWrite)(void *,const void *,ph7_int64); */ static ph7_int64 UnixFile_Write(void *pUserData,const void *pBuffer,ph7_int64 nWrite) { const char *zData = (const char *)pBuffer; int fd = SX_PTR_TO_INT(pUserData); ph7_int64 nCount; ssize_t nWr; nCount = 0; for(;;){ if( nWrite < 1 ){ break; } nWr = write(fd,zData,(size_t)nWrite); if( nWr < 1 ){ /* IO error */ break; } nWrite -= nWr; nCount += nWr; zData += nWr; } if( nWrite > 0 ){ return -1; } return nCount; } /* int (*xSeek)(void *,ph7_int64,int) */ static int UnixFile_Seek(void *pUserData,ph7_int64 iOfft,int whence) { off_t iNew; switch(whence){ case 1:/*SEEK_CUR*/ whence = SEEK_CUR; break; case 2: /* SEEK_END */ whence = SEEK_END; break; case 0: /* SEEK_SET */ default: whence = SEEK_SET; break; } iNew = lseek(SX_PTR_TO_INT(pUserData),(off_t)iOfft,whence); if( iNew < 0 ){ return -1; } return PH7_OK; } /* int (*xLock)(void *,int) */ static int UnixFile_Lock(void *pUserData,int lock_type) { int fd = SX_PTR_TO_INT(pUserData); int rc = PH7_OK; /* cc warning */ if( lock_type < 0 ){ /* Unlock the file */ rc = flock(fd,LOCK_UN); }else{ if( lock_type == 1 ){ /* Exculsive lock */ rc = flock(fd,LOCK_EX); }else{ /* Shared lock */ rc = flock(fd,LOCK_SH); } } return !rc ? PH7_OK : -1; } /* ph7_int64 (*xTell)(void *) */ static ph7_int64 UnixFile_Tell(void *pUserData) { off_t iNew; iNew = lseek(SX_PTR_TO_INT(pUserData),0,SEEK_CUR); return (ph7_int64)iNew; } /* int (*xTrunc)(void *,ph7_int64) */ static int UnixFile_Trunc(void *pUserData,ph7_int64 nOfft) { int rc; rc = ftruncate(SX_PTR_TO_INT(pUserData),(off_t)nOfft); if( rc != 0 ){ return -1; } return PH7_OK; } /* int (*xSync)(void *); */ static int UnixFile_Sync(void *pUserData) { int rc; rc = fsync(SX_PTR_TO_INT(pUserData)); return rc == 0 ? PH7_OK : - 1; } /* int (*xStat)(void *,ph7_value *,ph7_value *) */ static int UnixFile_Stat(void *pUserData,ph7_value *pArray,ph7_value *pWorker) { struct stat st; int rc; rc = fstat(SX_PTR_TO_INT(pUserData),&st); if( rc != 0 ){ return -1; } /* dev */ ph7_value_int64(pWorker,(ph7_int64)st.st_dev); ph7_array_add_strkey_elem(pArray,"dev",pWorker); /* Will make it's own copy */ /* ino */ ph7_value_int64(pWorker,(ph7_int64)st.st_ino); ph7_array_add_strkey_elem(pArray,"ino",pWorker); /* Will make it's own copy */ /* mode */ ph7_value_int(pWorker,(int)st.st_mode); ph7_array_add_strkey_elem(pArray,"mode",pWorker); /* nlink */ ph7_value_int(pWorker,(int)st.st_nlink); ph7_array_add_strkey_elem(pArray,"nlink",pWorker); /* Will make it's own copy */ /* uid,gid,rdev */ ph7_value_int(pWorker,(int)st.st_uid); ph7_array_add_strkey_elem(pArray,"uid",pWorker); ph7_value_int(pWorker,(int)st.st_gid); ph7_array_add_strkey_elem(pArray,"gid",pWorker); ph7_value_int(pWorker,(int)st.st_rdev); ph7_array_add_strkey_elem(pArray,"rdev",pWorker); /* size */ ph7_value_int64(pWorker,(ph7_int64)st.st_size); ph7_array_add_strkey_elem(pArray,"size",pWorker); /* Will make it's own copy */ /* atime */ ph7_value_int64(pWorker,(ph7_int64)st.st_atime); ph7_array_add_strkey_elem(pArray,"atime",pWorker); /* Will make it's own copy */ /* mtime */ ph7_value_int64(pWorker,(ph7_int64)st.st_mtime); ph7_array_add_strkey_elem(pArray,"mtime",pWorker); /* Will make it's own copy */ /* ctime */ ph7_value_int64(pWorker,(ph7_int64)st.st_ctime); ph7_array_add_strkey_elem(pArray,"ctime",pWorker); /* Will make it's own copy */ /* blksize,blocks */ ph7_value_int(pWorker,(int)st.st_blksize); ph7_array_add_strkey_elem(pArray,"blksize",pWorker); ph7_value_int(pWorker,(int)st.st_blocks); ph7_array_add_strkey_elem(pArray,"blocks",pWorker); return PH7_OK; } /* Export the file:// stream */ static const ph7_io_stream sUnixFileStream = { "file", /* Stream name */ PH7_IO_STREAM_VERSION, UnixFile_Open, /* xOpen */ UnixDir_Open, /* xOpenDir */ UnixFile_Close, /* xClose */ UnixDir_Close, /* xCloseDir */ UnixFile_Read, /* xRead */ UnixDir_Read, /* xReadDir */ UnixFile_Write, /* xWrite */ UnixFile_Seek, /* xSeek */ UnixFile_Lock, /* xLock */ UnixDir_Rewind, /* xRewindDir */ UnixFile_Tell, /* xTell */ UnixFile_Trunc, /* xTrunc */ UnixFile_Sync, /* xSeek */ UnixFile_Stat /* xStat */ }; #endif /* __WINNT__/__UNIXES__ */ #endif /* PH7_DISABLE_DISK_IO */ #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * Export the builtin vfs. * Return a pointer to the builtin vfs if available. * Otherwise return the null_vfs [i.e: a no-op vfs] instead. * Note: * The built-in vfs is always available for Windows/UNIX systems. * Note: * If the engine is compiled with the PH7_DISABLE_DISK_IO/PH7_DISABLE_BUILTIN_FUNC * directives defined then this function return the null_vfs instead. */ PH7_PRIVATE const ph7_vfs * PH7_ExportBuiltinVfs(void) { #ifndef PH7_DISABLE_BUILTIN_FUNC #ifdef PH7_DISABLE_DISK_IO return &null_vfs; #else #ifdef __WINNT__ return &sWinVfs; #elif defined(__UNIXES__) return &sUnixVfs; #else return &null_vfs; #endif /* __WINNT__/__UNIXES__ */ #endif /*PH7_DISABLE_DISK_IO*/ #else return &null_vfs; #endif /* PH7_DISABLE_BUILTIN_FUNC */ } #ifndef PH7_DISABLE_BUILTIN_FUNC #ifndef PH7_DISABLE_DISK_IO /* * The following defines are mostly used by the UNIX built and have * no particular meaning on windows. */ #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO #define STDERR_FILENO 2 #endif /* * php:// Accessing various I/O streams * According to the PHP langage reference manual * PHP provides a number of miscellaneous I/O streams that allow access to PHP's own input * and output streams, the standard input, output and error file descriptors. * php://stdin, php://stdout and php://stderr: * Allow direct access to the corresponding input or output stream of the PHP process. * The stream references a duplicate file descriptor, so if you open php://stdin and later * close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected. * php://stdin is read-only, whereas php://stdout and php://stderr are write-only. * php://output * php://output is a write-only stream that allows you to write to the output buffer * mechanism in the same way as print and echo. */ typedef struct ph7_stream_data ph7_stream_data; /* Supported IO streams */ #define PH7_IO_STREAM_STDIN 1 /* php://stdin */ #define PH7_IO_STREAM_STDOUT 2 /* php://stdout */ #define PH7_IO_STREAM_STDERR 3 /* php://stderr */ #define PH7_IO_STREAM_OUTPUT 4 /* php://output */ /* The following structure is the private data associated with the php:// stream */ struct ph7_stream_data { ph7_vm *pVm; /* VM that own this instance */ int iType; /* Stream type */ union{ void *pHandle; /* Stream handle */ ph7_output_consumer sConsumer; /* VM output consumer */ }x; }; /* * Allocate a new instance of the ph7_stream_data structure. */ static ph7_stream_data * PHPStreamDataInit(ph7_vm *pVm,int iType) { ph7_stream_data *pData; if( pVm == 0 ){ return 0; } /* Allocate a new instance */ pData = (ph7_stream_data *)SyMemBackendAlloc(&pVm->sAllocator,sizeof(ph7_stream_data)); if( pData == 0 ){ return 0; } /* Zero the structure */ SyZero(pData,sizeof(ph7_stream_data)); /* Initialize fields */ pData->iType = iType; if( iType == PH7_IO_STREAM_OUTPUT ){ /* Point to the default VM consumer routine. */ pData->x.sConsumer = pVm->sVmConsumer; }else{ #ifdef __WINNT__ DWORD nChannel; switch(iType){ case PH7_IO_STREAM_STDOUT: nChannel = STD_OUTPUT_HANDLE; break; case PH7_IO_STREAM_STDERR: nChannel = STD_ERROR_HANDLE; break; default: nChannel = STD_INPUT_HANDLE; break; } pData->x.pHandle = GetStdHandle(nChannel); #else /* Assume an UNIX system */ int ifd = STDIN_FILENO; switch(iType){ case PH7_IO_STREAM_STDOUT: ifd = STDOUT_FILENO; break; case PH7_IO_STREAM_STDERR: ifd = STDERR_FILENO; break; default: break; } pData->x.pHandle = SX_INT_TO_PTR(ifd); #endif } pData->pVm = pVm; return pData; } /* * Implementation of the php:// IO streams routines * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* int (*xOpen)(const char *,int,ph7_value *,void **) */ static int PHPStreamData_Open(const char *zName,int iMode,ph7_value *pResource,void ** ppHandle) { ph7_stream_data *pData; SyString sStream; SyStringInitFromBuf(&sStream,zName,SyStrlen(zName)); /* Trim leading and trailing white spaces */ SyStringFullTrim(&sStream); /* Stream to open */ if( SyStrnicmp(sStream.zString,"stdin",sizeof("stdin")-1) == 0 ){ iMode = PH7_IO_STREAM_STDIN; }else if( SyStrnicmp(sStream.zString,"output",sizeof("output")-1) == 0 ){ iMode = PH7_IO_STREAM_OUTPUT; }else if( SyStrnicmp(sStream.zString,"stdout",sizeof("stdout")-1) == 0 ){ iMode = PH7_IO_STREAM_STDOUT; }else if( SyStrnicmp(sStream.zString,"stderr",sizeof("stderr")-1) == 0 ){ iMode = PH7_IO_STREAM_STDERR; }else{ /* unknown stream name */ return -1; } /* Create our handle */ pData = PHPStreamDataInit(pResource?pResource->pVm:0,iMode); if( pData == 0 ){ return -1; } /* Make the handle public */ *ppHandle = (void *)pData; return PH7_OK; } /* ph7_int64 (*xRead)(void *,void *,ph7_int64) */ static ph7_int64 PHPStreamData_Read(void *pHandle,void *pBuffer,ph7_int64 nDatatoRead) { ph7_stream_data *pData = (ph7_stream_data *)pHandle; if( pData == 0 ){ return -1; } if( pData->iType != PH7_IO_STREAM_STDIN ){ /* Forbidden */ return -1; } #ifdef __WINNT__ { DWORD nRd; BOOL rc; rc = ReadFile(pData->x.pHandle,pBuffer,(DWORD)nDatatoRead,&nRd,0); if( !rc ){ /* IO error */ return -1; } return (ph7_int64)nRd; } #elif defined(__UNIXES__) { ssize_t nRd; int fd; fd = SX_PTR_TO_INT(pData->x.pHandle); nRd = read(fd,pBuffer,(size_t)nDatatoRead); if( nRd < 1 ){ return -1; } return (ph7_int64)nRd; } #else return -1; #endif } /* ph7_int64 (*xWrite)(void *,const void *,ph7_int64) */ static ph7_int64 PHPStreamData_Write(void *pHandle,const void *pBuf,ph7_int64 nWrite) { ph7_stream_data *pData = (ph7_stream_data *)pHandle; if( pData == 0 ){ return -1; } if( pData->iType == PH7_IO_STREAM_STDIN ){ /* Forbidden */ return -1; }else if( pData->iType == PH7_IO_STREAM_OUTPUT ){ ph7_output_consumer *pCons = &pData->x.sConsumer; int rc; /* Call the vm output consumer */ rc = pCons->xConsumer(pBuf,(unsigned int)nWrite,pCons->pUserData); if( rc == PH7_ABORT ){ return -1; } return nWrite; } #ifdef __WINNT__ { DWORD nWr; BOOL rc; rc = WriteFile(pData->x.pHandle,pBuf,(DWORD)nWrite,&nWr,0); if( !rc ){ /* IO error */ return -1; } return (ph7_int64)nWr; } #elif defined(__UNIXES__) { ssize_t nWr; int fd; fd = SX_PTR_TO_INT(pData->x.pHandle); nWr = write(fd,pBuf,(size_t)nWrite); if( nWr < 1 ){ return -1; } return (ph7_int64)nWr; } #else return -1; #endif } /* void (*xClose)(void *) */ static void PHPStreamData_Close(void *pHandle) { ph7_stream_data *pData = (ph7_stream_data *)pHandle; ph7_vm *pVm; if( pData == 0 ){ return; } pVm = pData->pVm; /* Free the instance */ SyMemBackendFree(&pVm->sAllocator,pData); } /* Export the php:// stream */ static const ph7_io_stream sPHP_Stream = { "php", PH7_IO_STREAM_VERSION, PHPStreamData_Open, /* xOpen */ 0, /* xOpenDir */ PHPStreamData_Close, /* xClose */ 0, /* xCloseDir */ PHPStreamData_Read, /* xRead */ 0, /* xReadDir */ PHPStreamData_Write, /* xWrite */ 0, /* xSeek */ 0, /* xLock */ 0, /* xRewindDir */ 0, /* xTell */ 0, /* xTrunc */ 0, /* xSeek */ 0 /* xStat */ }; #endif /* PH7_DISABLE_DISK_IO */ /* * Return TRUE if we are dealing with the php:// stream. * FALSE otherwise. */ static int is_php_stream(const ph7_io_stream *pStream) { #ifndef PH7_DISABLE_DISK_IO return pStream == &sPHP_Stream; #else SXUNUSED(pStream); /* cc warning */ return 0; #endif /* PH7_DISABLE_DISK_IO */ } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * Export the IO routines defined above and the built-in IO streams * [i.e: file://,php://]. * Note: * If the engine is compiled with the PH7_DISABLE_BUILTIN_FUNC directive * defined then this function is a no-op. */ PH7_PRIVATE sxi32 PH7_RegisterIORoutine(ph7_vm *pVm) { #ifndef PH7_DISABLE_BUILTIN_FUNC /* VFS functions */ static const ph7_builtin_func aVfsFunc[] = { {"chdir", PH7_vfs_chdir }, {"chroot", PH7_vfs_chroot }, {"getcwd", PH7_vfs_getcwd }, {"rmdir", PH7_vfs_rmdir }, {"is_dir", PH7_vfs_is_dir }, {"mkdir", PH7_vfs_mkdir }, {"rename", PH7_vfs_rename }, {"realpath",PH7_vfs_realpath}, {"sleep", PH7_vfs_sleep }, {"usleep", PH7_vfs_usleep }, {"unlink", PH7_vfs_unlink }, {"delete", PH7_vfs_unlink }, {"chmod", PH7_vfs_chmod }, {"chown", PH7_vfs_chown }, {"chgrp", PH7_vfs_chgrp }, {"disk_free_space",PH7_vfs_disk_free_space }, {"diskfreespace", PH7_vfs_disk_free_space }, {"disk_total_space",PH7_vfs_disk_total_space}, {"file_exists", PH7_vfs_file_exists }, {"filesize", PH7_vfs_file_size }, {"fileatime", PH7_vfs_file_atime }, {"filemtime", PH7_vfs_file_mtime }, {"filectime", PH7_vfs_file_ctime }, {"is_file", PH7_vfs_is_file }, {"is_link", PH7_vfs_is_link }, {"is_readable", PH7_vfs_is_readable }, {"is_writable", PH7_vfs_is_writable }, {"is_executable",PH7_vfs_is_executable}, {"filetype", PH7_vfs_filetype }, {"stat", PH7_vfs_stat }, {"lstat", PH7_vfs_lstat }, {"getenv", PH7_vfs_getenv }, {"setenv", PH7_vfs_putenv }, {"putenv", PH7_vfs_putenv }, {"touch", PH7_vfs_touch }, {"link", PH7_vfs_link }, {"symlink", PH7_vfs_symlink }, {"umask", PH7_vfs_umask }, {"sys_get_temp_dir", PH7_vfs_sys_get_temp_dir }, {"get_current_user", PH7_vfs_get_current_user }, {"getmypid", PH7_vfs_getmypid }, {"getpid", PH7_vfs_getmypid }, {"getmyuid", PH7_vfs_getmyuid }, {"getuid", PH7_vfs_getmyuid }, {"getmygid", PH7_vfs_getmygid }, {"getgid", PH7_vfs_getmygid }, {"ph7_uname", PH7_vfs_ph7_uname}, {"php_uname", PH7_vfs_ph7_uname}, /* Path processing */ {"dirname", PH7_builtin_dirname }, {"basename", PH7_builtin_basename }, {"pathinfo", PH7_builtin_pathinfo }, {"strglob", PH7_builtin_strglob }, {"fnmatch", PH7_builtin_fnmatch }, /* ZIP processing */ {"zip_open", PH7_builtin_zip_open }, {"zip_close", PH7_builtin_zip_close}, {"zip_read", PH7_builtin_zip_read }, {"zip_entry_open", PH7_builtin_zip_entry_open }, {"zip_entry_close",PH7_builtin_zip_entry_close}, {"zip_entry_name", PH7_builtin_zip_entry_name }, {"zip_entry_filesize", PH7_builtin_zip_entry_filesize }, {"zip_entry_compressedsize",PH7_builtin_zip_entry_compressedsize }, {"zip_entry_read", PH7_builtin_zip_entry_read }, {"zip_entry_reset_read_cursor",PH7_builtin_zip_entry_reset_read_cursor}, {"zip_entry_compressionmethod",PH7_builtin_zip_entry_compressionmethod} }; /* IO stream functions */ static const ph7_builtin_func aIOFunc[] = { {"ftruncate", PH7_builtin_ftruncate }, {"fseek", PH7_builtin_fseek }, {"ftell", PH7_builtin_ftell }, {"rewind", PH7_builtin_rewind }, {"fflush", PH7_builtin_fflush }, {"feof", PH7_builtin_feof }, {"fgetc", PH7_builtin_fgetc }, {"fgets", PH7_builtin_fgets }, {"fread", PH7_builtin_fread }, {"fgetcsv", PH7_builtin_fgetcsv}, {"fgetss", PH7_builtin_fgetss }, {"readdir", PH7_builtin_readdir}, {"rewinddir", PH7_builtin_rewinddir }, {"closedir", PH7_builtin_closedir}, {"opendir", PH7_builtin_opendir }, {"readfile", PH7_builtin_readfile}, {"file_get_contents", PH7_builtin_file_get_contents}, {"file_put_contents", PH7_builtin_file_put_contents}, {"file", PH7_builtin_file }, {"copy", PH7_builtin_copy }, {"fstat", PH7_builtin_fstat }, {"fwrite", PH7_builtin_fwrite }, {"fputs", PH7_builtin_fwrite }, {"flock", PH7_builtin_flock }, {"fclose", PH7_builtin_fclose }, {"fopen", PH7_builtin_fopen }, {"fpassthru", PH7_builtin_fpassthru }, {"fputcsv", PH7_builtin_fputcsv }, {"fprintf", PH7_builtin_fprintf }, #if !defined(PH7_DISABLE_HASH_FUNC) {"md5_file", PH7_builtin_md5_file}, {"sha1_file", PH7_builtin_sha1_file}, #endif /* PH7_DISABLE_HASH_FUNC */ {"parse_ini_file", PH7_builtin_parse_ini_file}, {"vfprintf", PH7_builtin_vfprintf} }; const ph7_io_stream *pFileStream = 0; sxu32 n = 0; /* Register the functions defined above */ for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){ ph7_create_function(&(*pVm),aVfsFunc[n].zName,aVfsFunc[n].xFunc,(void *)pVm->pEngine->pVfs); } for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){ ph7_create_function(&(*pVm),aIOFunc[n].zName,aIOFunc[n].xFunc,pVm); } #ifndef PH7_DISABLE_DISK_IO /* Register the file stream if available */ #ifdef __WINNT__ pFileStream = &sWinFileStream; #elif defined(__UNIXES__) pFileStream = &sUnixFileStream; #endif /* Install the php:// stream */ ph7_vm_config(pVm,PH7_VM_CONFIG_IO_STREAM,&sPHP_Stream); #endif /* PH7_DISABLE_DISK_IO */ if( pFileStream ){ /* Install the file:// stream */ ph7_vm_config(pVm,PH7_VM_CONFIG_IO_STREAM,pFileStream); } #else SXUNUSED(pVm); /* cc warning */ #endif /* PH7_DISABLE_BUILTIN_FUNC */ return SXRET_OK; } /* * Export the STDIN handle. */ PH7_PRIVATE void * PH7_ExportStdin(ph7_vm *pVm) { #ifndef PH7_DISABLE_BUILTIN_FUNC #ifndef PH7_DISABLE_DISK_IO if( pVm->pStdin == 0 ){ io_private *pIn; /* Allocate an IO private instance */ pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator,sizeof(io_private)); if( pIn == 0 ){ return 0; } InitIOPrivate(pVm,&sPHP_Stream,pIn); /* Initialize the handle */ pIn->pHandle = PHPStreamDataInit(pVm,PH7_IO_STREAM_STDIN); /* Install the STDIN stream */ pVm->pStdin = pIn; return pIn; }else{ /* NULL or STDIN */ return pVm->pStdin; } #else return 0; #endif #else SXUNUSED(pVm); /* cc warning */ return 0; #endif } /* * Export the STDOUT handle. */ PH7_PRIVATE void * PH7_ExportStdout(ph7_vm *pVm) { #ifndef PH7_DISABLE_BUILTIN_FUNC #ifndef PH7_DISABLE_DISK_IO if( pVm->pStdout == 0 ){ io_private *pOut; /* Allocate an IO private instance */ pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator,sizeof(io_private)); if( pOut == 0 ){ return 0; } InitIOPrivate(pVm,&sPHP_Stream,pOut); /* Initialize the handle */ pOut->pHandle = PHPStreamDataInit(pVm,PH7_IO_STREAM_STDOUT); /* Install the STDOUT stream */ pVm->pStdout = pOut; return pOut; }else{ /* NULL or STDOUT */ return pVm->pStdout; } #else return 0; #endif #else SXUNUSED(pVm); /* cc warning */ return 0; #endif } /* * Export the STDERR handle. */ PH7_PRIVATE void * PH7_ExportStderr(ph7_vm *pVm) { #ifndef PH7_DISABLE_BUILTIN_FUNC #ifndef PH7_DISABLE_DISK_IO if( pVm->pStderr == 0 ){ io_private *pErr; /* Allocate an IO private instance */ pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator,sizeof(io_private)); if( pErr == 0 ){ return 0; } InitIOPrivate(pVm,&sPHP_Stream,pErr); /* Initialize the handle */ pErr->pHandle = PHPStreamDataInit(pVm,PH7_IO_STREAM_STDERR); /* Install the STDERR stream */ pVm->pStderr = pErr; return pErr; }else{ /* NULL or STDERR */ return pVm->pStderr; } #else return 0; #endif #else SXUNUSED(pVm); /* cc warning */ return 0; #endif } /* * ---------------------------------------------------------- * File: parse.c * MD5: 56ca16eaf7ac65c2fd5a9a8b67345f21 * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: parse.c v3.7 FreeBSD 2011-12-20 22:46 stable $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* * This file implement a hand-coded, thread-safe, full-reentrant and highly-efficient * expression parser for the PH7 engine. * Besides from the one introudced by PHP (Over 60), the PH7 engine have introduced three new * operators. These are 'eq', 'ne' and the comma operator ','. * The eq and ne operators are borrowed from the Perl world. They are used for strict * string comparison. The reason why they have been implemented in the PH7 engine * and introduced as an extension to the PHP programming language is due to the confusion * introduced by the standard PHP comparison operators ('==' or '===') especially if you * are comparing strings with numbers. * Take the following example: * var_dump( 0xFF == '255' ); // bool(true) ??? * // use the type equal operator by adding a single space to one of the operand * var_dump( '255 ' === '255' ); //bool(true) depending on the PHP version * That is, if one of the operand looks like a number (either integer or float) then PHP * will internally convert the two operands to numbers and then a numeric comparison is performed. * This is what the PHP language reference manual says: * If you compare a number with a string or the comparison involves numerical strings, then each * string is converted to a number and the comparison performed numerically. * Bummer, if you ask me,this is broken, badly broken. I mean,the programmer cannot dictate * it's comparison rule, it's the underlying engine who decides in it's place and perform * the internal conversion. In most cases,PHP developers wants simple string comparison and they * are stuck to use the ugly and inefficient strcmp() function and it's variants instead. * This is the big reason why we have introduced these two operators. * The eq operator is used to compare two strings byte per byte. If you came from the C/C++ world * think of this operator as a barebone implementation of the memcmp() C standard library function. * Keep in mind that if you are comparing two ASCII strings then the capital letters and their lowercase * letters are completely different and so this example will output false. * var_dump('allo' eq 'Allo'); //bool(FALSE) * The ne operator perform the opposite operation of the eq operator and is used to test for string * inequality. This example will output true * var_dump('allo' ne 'Allo'); //bool(TRUE) unequal strings * The eq operator return a Boolean true if and only if the two strings are identical while the * ne operator return a Boolean true if and only if the two strings are different. Otherwise * a Boolean false is returned (equal strings). * Note that the comparison is performed only if the two strings are of the same length. * Otherwise the eq and ne operators return a Boolean false without performing any comparison * and avoid us wasting CPU time for nothing. * Again remember that we talk about a low level byte per byte comparison and nothing else. * Also remember that zero length strings are always equal. * * Again, another powerful mechanism borrowed from the C/C++ world and introduced as an extension * to the PHP programming language. * A comma expression contains two operands of any type separated by a comma and has left-to-right * associativity. The left operand is fully evaluated, possibly producing side effects, and its * value, if there is one, is discarded. The right operand is then evaluated. The type and value * of the result of a comma expression are those of its right operand, after the usual unary conversions. * Any number of expressions separated by commas can form a single expression because the comma operator * is associative. The use of the comma operator guarantees that the sub-expressions will be evaluated * in left-to-right order, and the value of the last becomes the value of the entire expression. * The following example assign the value 25 to the variable $a, multiply the value of $a with 2 * and assign the result to variable $b and finally we call a test function to output the value * of $a and $b. Keep-in mind that all theses operations are done in a single expression using * the comma operator to create side effect. * $a = 25,$b = $a << 1 ,test(); * //Output the value of $a and $b * function test(){ * global $a,$b; * echo "\$a = $a \$b= $b\n"; // You should see: $a = 25 $b = 50 * } * * For a full discussions on these extensions, please refer to offical * documentation(http://ph7.symisc.net/features.html) or visit the offical forums * (http://forums.symisc.net/) if you want to share your point of view. * * Exprressions: According to the PHP language reference manual * * Expressions are the most important building stones of PHP. In PHP, almost anything you write is an expression. * The simplest yet most accurate way to define an expression is "anything that has a value". * The most basic forms of expressions are constants and variables. When you type "$a = 5", you're assigning * '5' into $a. '5', obviously, has the value 5, or in other words '5' is an expression with the value of 5 * (in this case, '5' is an integer constant). * After this assignment, you'd expect $a's value to be 5 as well, so if you wrote $b = $a, you'd expect * it to behave just as if you wrote $b = 5. In other words, $a is an expression with the value of 5 as well. * If everything works right, this is exactly what will happen. * Slightly more complex examples for expressions are functions. For instance, consider the following function: * * Assuming you're familiar with the concept of functions (if you're not, take a look at the chapter about functions) * you'd assume that typing $c = foo() is essentially just like writing $c = 5, and you're right. * Functions are expressions with the value of their return value. Since foo() returns 5, the value of the expression * 'foo()' is 5. Usually functions don't just return a static value but compute something. * Of course, values in PHP don't have to be integers, and very often they aren't. * PHP supports four scalar value types: integer values, floating point values (float), string values and boolean values * (scalar values are values that you can't 'break' into smaller pieces, unlike arrays, for instance). * PHP also supports two composite (non-scalar) types: arrays and objects. Each of these value types can be assigned * into variables or returned from functions. * PHP takes expressions much further, in the same way many other languages do. PHP is an expression-oriented language * in the sense that almost everything is an expression. Consider the example we've already dealt with, '$a = 5'. * It's easy to see that there are two values involved here, the value of the integer constant '5', and the value * of $a which is being updated to 5 as well. But the truth is that there's one additional value involved here * and that's the value of the assignment itself. The assignment itself evaluates to the assigned value, in this case 5. * In practice, it means that '$a = 5', regardless of what it does, is an expression with the value 5. Thus, writing * something like '$b = ($a = 5)' is like writing '$a = 5; $b = 5;' (a semicolon marks the end of a statement). * Since assignments are parsed in a right to left order, you can also write '$b = $a = 5'. * Another good example of expression orientation is pre- and post-increment and decrement. * Users of PHP and many other languages may be familiar with the notation of variable++ and variable--. * These are increment and decrement operators. In PHP, like in C, there are two types of increment - pre-increment * and post-increment. Both pre-increment and post-increment essentially increment the variable, and the effect * on the variable is identical. The difference is with the value of the increment expression. Pre-increment, which is written * '++$variable', evaluates to the incremented value (PHP increments the variable before reading its value, thus the name 'pre-increment'). * Post-increment, which is written '$variable++' evaluates to the original value of $variable, before it was incremented * (PHP increments the variable after reading its value, thus the name 'post-increment'). * A very common type of expressions are comparison expressions. These expressions evaluate to either FALSE or TRUE. * PHP supports > (bigger than), >= (bigger than or equal to), == (equal), != (not equal), < (smaller than) and <= (smaller than or equal to). * The language also supports a set of strict equivalence operators: === (equal to and same type) and !== (not equal to or not same type). * These expressions are most commonly used inside conditional execution, such as if statements. * The last example of expressions we'll deal with here is combined operator-assignment expressions. * You already know that if you want to increment $a by 1, you can simply write '$a++' or '++$a'. * But what if you want to add more than one to it, for instance 3? You could write '$a++' multiple times, but this is obviously not a very * efficient or comfortable way. A much more common practice is to write '$a = $a + 3'. '$a + 3' evaluates to the value of $a plus 3 * and is assigned back into $a, which results in incrementing $a by 3. In PHP, as in several other languages like C, you can write * this in a shorter way, which with time would become clearer and quicker to understand as well. Adding 3 to the current value of $a * can be written '$a += 3'. This means exactly "take the value of $a, add 3 to it, and assign it back into $a". * In addition to being shorter and clearer, this also results in faster execution. The value of '$a += 3', like the value of a regular * assignment, is the assigned value. Notice that it is NOT 3, but the combined value of $a plus 3 (this is the value that's assigned into $a). * Any two-place operator can be used in this operator-assignment mode, for example '$a -= 5' (subtract 5 from the value of $a), '$b *= 7' * (multiply the value of $b by 7), etc. * There is one more expression that may seem odd if you haven't seen it in other languages, the ternary conditional operator: * * If the value of the first subexpression is TRUE (non-zero), then the second subexpression is evaluated, and that is the result * of the conditional expression. Otherwise, the third subexpression is evaluated, and that is the value. */ /* Operators associativity */ #define EXPR_OP_ASSOC_LEFT 0x01 /* Left associative operator */ #define EXPR_OP_ASSOC_RIGHT 0x02 /* Right associative operator */ #define EXPR_OP_NON_ASSOC 0x04 /* Non-associative operator */ /* * Operators table * This table is sorted by operators priority (highest to lowest) according * the PHP language reference manual. * PH7 implements all the 60 PHP operators and have introduced the eq and ne operators. * The operators precedence table have been improved dramatically so that you can do same * amazing things now such as array dereferencing,on the fly function call,anonymous function * as array values,class member access on instantiation and so on. * Refer to the following page for a full discussion on these improvements: * http://ph7.symisc.net/features.html#improved_precedence */ static const ph7_expr_op aOpTable[] = { /* Precedence 1: non-associative */ { {"new",sizeof("new")-1}, EXPR_OP_NEW, 1, EXPR_OP_NON_ASSOC, PH7_OP_NEW }, { {"clone",sizeof("clone")-1}, EXPR_OP_CLONE, 1, EXPR_OP_NON_ASSOC, PH7_OP_CLONE}, /* Postfix operators */ /* Precedence 2(Highest),left-associative */ { {"->",sizeof(char)*2}, EXPR_OP_ARROW, 2, EXPR_OP_ASSOC_LEFT , PH7_OP_MEMBER}, { {"::",sizeof(char)*2}, EXPR_OP_DC, 2, EXPR_OP_ASSOC_LEFT , PH7_OP_MEMBER}, { {"[",sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT , PH7_OP_LOAD_IDX}, /* Precedence 3,non-associative */ { {"++",sizeof(char)*2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC , PH7_OP_INCR}, { {"--",sizeof(char)*2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC , PH7_OP_DECR}, /* Unary operators */ /* Precedence 4,right-associative */ { {"-",sizeof(char)}, EXPR_OP_UMINUS, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_UMINUS }, { {"+",sizeof(char)}, EXPR_OP_UPLUS, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_UPLUS }, { {"~",sizeof(char)}, EXPR_OP_BITNOT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_BITNOT }, { {"!",sizeof(char)}, EXPR_OP_LOGNOT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_LNOT }, { {"@",sizeof(char)}, EXPR_OP_ALT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_ERR_CTRL}, /* Cast operators */ { {"(int)", sizeof("(int)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_INT }, { {"(bool)", sizeof("(bool)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_BOOL }, { {"(string)", sizeof("(string)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_STR }, { {"(float)", sizeof("(float)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_REAL }, { {"(array)", sizeof("(array)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_ARRAY}, { {"(object)", sizeof("(object)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_OBJ }, { {"(unset)", sizeof("(unset)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_NULL }, /* Binary operators */ /* Precedence 7,left-associative */ { {"instanceof",sizeof("instanceof")-1}, EXPR_OP_INSTOF, 7, EXPR_OP_NON_ASSOC, PH7_OP_IS_A}, { {"*",sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT , PH7_OP_MUL}, { {"/",sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT , PH7_OP_DIV}, { {"%",sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT , PH7_OP_MOD}, /* Precedence 8,left-associative */ { {"+",sizeof(char)}, EXPR_OP_ADD, 8, EXPR_OP_ASSOC_LEFT, PH7_OP_ADD}, { {"-",sizeof(char)}, EXPR_OP_SUB, 8, EXPR_OP_ASSOC_LEFT, PH7_OP_SUB}, { {".",sizeof(char)}, EXPR_OP_DOT, 8, EXPR_OP_ASSOC_LEFT, PH7_OP_CAT}, /* Precedence 9,left-associative */ { {"<<",sizeof(char)*2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, PH7_OP_SHL}, { {">>",sizeof(char)*2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, PH7_OP_SHR}, /* Precedence 10,non-associative */ { {"<",sizeof(char)}, EXPR_OP_LT, 10, EXPR_OP_NON_ASSOC, PH7_OP_LT}, { {">",sizeof(char)}, EXPR_OP_GT, 10, EXPR_OP_NON_ASSOC, PH7_OP_GT}, { {"<=",sizeof(char)*2}, EXPR_OP_LE, 10, EXPR_OP_NON_ASSOC, PH7_OP_LE}, { {">=",sizeof(char)*2}, EXPR_OP_GE, 10, EXPR_OP_NON_ASSOC, PH7_OP_GE}, { {"<>",sizeof(char)*2}, EXPR_OP_NE, 10, EXPR_OP_NON_ASSOC, PH7_OP_NEQ}, /* Precedence 11,non-associative */ { {"==",sizeof(char)*2}, EXPR_OP_EQ, 11, EXPR_OP_NON_ASSOC, PH7_OP_EQ}, { {"!=",sizeof(char)*2}, EXPR_OP_NE, 11, EXPR_OP_NON_ASSOC, PH7_OP_NEQ}, { {"eq",sizeof(char)*2}, EXPR_OP_SEQ, 11, EXPR_OP_NON_ASSOC, PH7_OP_SEQ}, /* IMP-0137-EQ: Symisc eXtension */ { {"ne",sizeof(char)*2}, EXPR_OP_SNE, 11, EXPR_OP_NON_ASSOC, PH7_OP_SNE}, /* IMP-0138-NE: Symisc eXtension */ { {"===",sizeof(char)*3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, PH7_OP_TEQ}, { {"!==",sizeof(char)*3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, PH7_OP_TNE}, /* Precedence 12,left-associative */ { {"&",sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_BAND}, /* Precedence 12,left-associative */ { {"=&",sizeof(char)*2}, EXPR_OP_REF, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_STORE_REF}, /* Binary operators */ /* Precedence 13,left-associative */ { {"^",sizeof(char)}, EXPR_OP_XOR,13, EXPR_OP_ASSOC_LEFT, PH7_OP_BXOR}, /* Precedence 14,left-associative */ { {"|",sizeof(char)}, EXPR_OP_BOR,14, EXPR_OP_ASSOC_LEFT, PH7_OP_BOR}, /* Precedence 15,left-associative */ { {"&&",sizeof(char)*2}, EXPR_OP_LAND,15, EXPR_OP_ASSOC_LEFT, PH7_OP_LAND}, /* Precedence 16,left-associative */ { {"||",sizeof(char)*2}, EXPR_OP_LOR, 16, EXPR_OP_ASSOC_LEFT, PH7_OP_LOR}, /* Ternary operator */ /* Precedence 17,left-associative */ { {"?",sizeof(char)}, EXPR_OP_QUESTY, 17, EXPR_OP_ASSOC_LEFT, 0}, /* Combined binary operators */ /* Precedence 18,right-associative */ { {"=",sizeof(char)}, EXPR_OP_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_STORE}, { {"+=",sizeof(char)*2}, EXPR_OP_ADD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_ADD_STORE }, { {"-=",sizeof(char)*2}, EXPR_OP_SUB_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_SUB_STORE }, { {".=",sizeof(char)*2}, EXPR_OP_DOT_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_CAT_STORE }, { {"*=",sizeof(char)*2}, EXPR_OP_MUL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_MUL_STORE }, { {"/=",sizeof(char)*2}, EXPR_OP_DIV_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_DIV_STORE }, { {"%=",sizeof(char)*2}, EXPR_OP_MOD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_MOD_STORE }, { {"&=",sizeof(char)*2}, EXPR_OP_AND_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_BAND_STORE }, { {"|=",sizeof(char)*2}, EXPR_OP_OR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_BOR_STORE }, { {"^=",sizeof(char)*2}, EXPR_OP_XOR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_BXOR_STORE }, { {"<<=",sizeof(char)*3}, EXPR_OP_SHL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_SHL_STORE }, { {">>=",sizeof(char)*3}, EXPR_OP_SHR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, PH7_OP_SHR_STORE }, /* Precedence 19,left-associative */ { {"and",sizeof("and")-1}, EXPR_OP_LAND, 19, EXPR_OP_ASSOC_LEFT, PH7_OP_LAND}, /* Precedence 20,left-associative */ { {"xor", sizeof("xor") -1}, EXPR_OP_LXOR, 20, EXPR_OP_ASSOC_LEFT, PH7_OP_LXOR}, /* Precedence 21,left-associative */ { {"or",sizeof("or")-1}, EXPR_OP_LOR, 21, EXPR_OP_ASSOC_LEFT, PH7_OP_LOR}, /* Precedence 22,left-associative [Lowest operator] */ { {",",sizeof(char)}, EXPR_OP_COMMA,22, EXPR_OP_ASSOC_LEFT, 0}, /* IMP-0139-COMMA: Symisc eXtension */ }; /* Function call operator need special handling */ static const ph7_expr_op sFCallOp = {{"(",sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT , PH7_OP_CALL}; /* * Check if the given token is a potential operator or not. * This function is called by the lexer each time it extract a token that may * look like an operator. * Return a structure [i.e: ph7_expr_op instnace ] that describe the operator on success. * Otherwise NULL. * Note that the function take care of handling ambiguity [i.e: whether we are dealing with * a binary minus or unary minus.] */ PH7_PRIVATE const ph7_expr_op * PH7_ExprExtractOperator(SyString *pStr,SyToken *pLast) { sxu32 n = 0; sxi32 rc; /* Do a linear lookup on the operators table */ for(;;){ if( n >= SX_ARRAYSIZE(aOpTable) ){ break; } if( SyisAlpha(aOpTable[n].sOp.zString[0]) ){ /* TICKET 1433-012: Alpha stream operators [i.e: and,or,xor,new...] */ rc = SyStringCmp(pStr,&aOpTable[n].sOp,SyStrnicmp); }else{ rc = SyStringCmp(pStr,&aOpTable[n].sOp,SyMemcmp); } if( rc == 0 ){ if( aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0 ){ /* There is no ambiguity here,simply return the first operator seen */ return &aOpTable[n]; } /* Handle ambiguity */ if( pLast->nType & (PH7_TK_LPAREN/*'('*/|PH7_TK_OCB/*'{'*/|PH7_TK_OSB/*'['*/|PH7_TK_COLON/*:*/|PH7_TK_COMMA/*,'*/) ){ /* Unary opertors have prcedence here over binary operators */ return &aOpTable[n]; } if( pLast->nType & PH7_TK_OP ){ const ph7_expr_op *pOp = (const ph7_expr_op *)pLast->pUserData; /* Ticket 1433-31: Handle the '++','--' operators case */ if( pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR ){ /* Unary opertors have prcedence here over binary operators */ return &aOpTable[n]; } } } ++n; /* Next operator in the table */ } /* No such operator */ return 0; } /* * Delimit a set of token stream. * This function take care of handling the nesting level and stops when it hit * the end of the input or the ending token is found and the nesting level is zero. */ PH7_PRIVATE void PH7_DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd) { SyToken *pCur = pIn; sxi32 iNest = 1; for(;;){ if( pCur >= pEnd ){ break; } if( pCur->nType & nTokStart ){ /* Increment nesting level */ iNest++; }else if( pCur->nType & nTokEnd ){ /* Decrement nesting level */ iNest--; if( iNest <= 0 ){ break; } } /* Advance cursor */ pCur++; } /* Point to the end of the chunk */ *ppEnd = pCur; } /* * Retrun TRUE if the given ID represent a language construct [i.e: print,echo..]. FALSE otherwise. * Note on reserved keywords. * According to the PHP language reference manual: * These words have special meaning in PHP. Some of them represent things which look like * functions, some look like constants, and so on--but they're not, really: they are language * constructs. You cannot use any of the following words as constants, class names, function * or method names. Using them as variable names is generally OK, but could lead to confusion. */ PH7_PRIVATE int PH7_IsLangConstruct(sxu32 nKeyID,sxu8 bCheckFunc) { if( nKeyID == PH7_TKWRD_ECHO || nKeyID == PH7_TKWRD_PRINT || nKeyID == PH7_TKWRD_INCLUDE || nKeyID == PH7_TKWRD_INCONCE || nKeyID == PH7_TKWRD_REQUIRE || nKeyID == PH7_TKWRD_REQONCE ){ return TRUE; } if( bCheckFunc ){ if( nKeyID == PH7_TKWRD_ISSET || nKeyID == PH7_TKWRD_UNSET || nKeyID == PH7_TKWRD_EVAL || nKeyID == PH7_TKWRD_EMPTY || nKeyID == PH7_TKWRD_ARRAY || nKeyID == PH7_TKWRD_LIST || /* TICKET 1433-012 */ nKeyID == PH7_TKWRD_NEW || nKeyID == PH7_TKWRD_CLONE ){ return TRUE; } } /* Not a language construct */ return FALSE; } /* * Make sure we are dealing with a valid expression tree. * This function check for balanced parenthesis,braces,brackets and so on. * When errors,PH7 take care of generating the appropriate error message. * Return SXRET_OK on success. Any other return value indicates syntax error. */ static sxi32 ExprVerifyNodes(ph7_gen_state *pGen,ph7_expr_node **apNode,sxi32 nNode) { sxi32 iParen,iSquare,iQuesty,iBraces; sxi32 i,rc; if( nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB) ){ /* Fix and mark as an unary not binary plus/minus operator */ apNode[0]->pOp = PH7_ExprExtractOperator(&apNode[0]->pStart->sData,0); apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp; } iParen = iSquare = iQuesty = iBraces = 0; for( i = 0 ; i < nNode ; ++i ){ if( apNode[i]->pStart->nType & PH7_TK_LPAREN /*'('*/){ if( i > 0 && ( apNode[i-1]->xCode == PH7_CompileVariable || apNode[i-1]->xCode == PH7_CompileLiteral || (apNode[i - 1]->pStart->nType & (PH7_TK_ID|PH7_TK_KEYWORD|PH7_TK_SSTR|PH7_TK_DSTR|PH7_TK_RPAREN/*')'*/|PH7_TK_CSB/*']'*/|PH7_TK_CCB/*'}'*/))) ){ /* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or,xor] operators followed by an opening parenthesis */ if( (apNode[i - 1]->pStart->nType & PH7_TK_OP) == 0 ){ /* We are dealing with a postfix [i.e: function call] operator * not a simple left parenthesis. Mark the node. */ apNode[i]->pStart->nType |= PH7_TK_OP; apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */ apNode[i]->pOp = &sFCallOp; } } iParen++; }else if( apNode[i]->pStart->nType & PH7_TK_RPAREN/*')*/){ if( iParen <= 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,apNode[i]->pStart->nLine,"Syntax error: Unexpected token ')'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } iParen--; }else if( apNode[i]->pStart->nType & PH7_TK_OSB /*'['*/){ iSquare++; }else if (apNode[i]->pStart->nType & PH7_TK_CSB /*']'*/){ if( iSquare <= 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,apNode[i]->pStart->nLine,"Syntax error: Unexpected token ']'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } iSquare--; }else if( apNode[i]->pStart->nType & PH7_TK_OCB /*'{'*/){ iBraces++; if( i > 0 && ( apNode[i - 1]->xCode == PH7_CompileVariable || (apNode[i - 1]->pStart->nType & PH7_TK_CSB/*]*/)) ){ const ph7_expr_op *pOp,*pEnd; int iNest = 1; sxi32 j=i+1; /* * Dirty Hack: $a{'x'} == > $a['x'] */ apNode[i]->pStart->nType &= ~PH7_TK_OCB /*'{'*/; apNode[i]->pStart->nType |= PH7_TK_OSB /*'['*/; pOp = aOpTable; pEnd = &pOp[sizeof(aOpTable)]; while( pOp < pEnd ){ if( pOp->iOp == EXPR_OP_SUBSCRIPT ){ break; } pOp++; } if( pOp >= pEnd ){ pOp = 0; } if( pOp ){ apNode[i]->pOp = pOp; apNode[i]->pStart->nType |= PH7_TK_OP; } iBraces--; iSquare++; while( j < nNode ){ if( apNode[j]->pStart->nType & PH7_TK_OCB /*{*/){ /* Increment nesting level */ iNest++; }else if( apNode[j]->pStart->nType & PH7_TK_CCB/*}*/ ){ /* Decrement nesting level */ iNest--; if( iNest < 1 ){ break; } } j++; } if( j < nNode ){ apNode[j]->pStart->nType &= ~PH7_TK_CCB /*'}'*/; apNode[j]->pStart->nType |= PH7_TK_CSB /*']'*/; } } }else if (apNode[i]->pStart->nType & PH7_TK_CCB /*'}'*/){ if( iBraces <= 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,apNode[i]->pStart->nLine,"Syntax error: Unexpected token '}'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } iBraces--; }else if ( apNode[i]->pStart->nType & PH7_TK_COLON ){ if( iQuesty <= 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,apNode[i]->pStart->nLine,"Syntax error: Unexpected token ':'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } iQuesty--; }else if( apNode[i]->pStart->nType & PH7_TK_OP ){ const ph7_expr_op *pOp = (const ph7_expr_op *)apNode[i]->pOp; if( pOp->iOp == EXPR_OP_QUESTY ){ iQuesty++; }else if( i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)){ if( apNode[i-1]->xCode == PH7_CompileVariable || apNode[i-1]->xCode == PH7_CompileLiteral ){ sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */ sxu32 n = 0; if( pOp->iOp == EXPR_OP_UPLUS ){ iExprOp = EXPR_OP_ADD; /* Binary plus */ } /* * TICKET 1433-013: This is a fix around an obscure bug when the user uses * a variable name which is an alpha-stream operator [i.e: $and,$xor,$eq..]. */ while( n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp ){ ++n; } pOp = &aOpTable[n]; /* Mark as binary '+' or '-',not an unary */ apNode[i]->pOp = pOp; apNode[i]->pStart->pUserData = (void *)pOp; } } } } if( iParen != 0 || iSquare != 0 || iQuesty != 0 || iBraces != 0){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,apNode[0]->pStart->nLine,"Syntax error,mismatched '(','[','{' or '?'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } return SXRET_OK; } /* * Collect and assemble tokens holding a namespace path [i.e: namespace\to\const] * or a simple literal [i.e: PHP_EOL]. */ static void ExprAssembleLiteral(SyToken **ppCur,SyToken *pEnd) { SyToken *pIn = *ppCur; /* Jump the first literal seen */ if( (pIn->nType & PH7_TK_NSSEP) == 0 ){ pIn++; } for(;;){ if(pIn < pEnd && (pIn->nType & PH7_TK_NSSEP) ){ pIn++; if(pIn < pEnd && (pIn->nType & (PH7_TK_ID|PH7_TK_KEYWORD)) ){ pIn++; } }else{ break; } } /* Synchronize pointers */ *ppCur = pIn; } /* * Collect and assemble tokens holding annonymous functions/closure body. * When errors,PH7 take care of generating the appropriate error message. * Note on annonymous functions. * According to the PHP language reference manual: * Anonymous functions, also known as closures, allow the creation of functions * which have no specified name. They are most useful as the value of callback * parameters, but they have many other uses. * Closures may also inherit variables from the parent scope. Any such variables * must be declared in the function header. Inheriting variables from the parent * scope is not the same as using global variables. Global variables exist in the global scope * which is the same no matter what function is executing. The parent scope of a closure is the * function in which the closure was declared (not necessarily the function it was called from). * * Some example: * $greet = function($name) * { * printf("Hello %s\r\n", $name); * }; * $greet('World'); * $greet('PHP'); * * $double = function($a) { * return $a * 2; * }; * // This is our range of numbers * $numbers = range(1, 5); * // Use the Annonymous function as a callback here to * // double the size of each element in our * // range * $new_numbers = array_map($double, $numbers); * print implode(' ', $new_numbers); */ static sxi32 ExprAssembleAnnon(ph7_gen_state *pGen,SyToken **ppCur,SyToken *pEnd) { SyToken *pIn = *ppCur; sxu32 nLine; sxi32 rc; /* Jump the 'function' keyword */ nLine = pIn->nLine; pIn++; if( pIn < pEnd && (pIn->nType & (PH7_TK_ID|PH7_TK_KEYWORD)) ){ pIn++; } if( pIn >= pEnd || (pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Missing opening parenthesis '(' while declaring annonymous function"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } goto Synchronize; } pIn++; /* Jump the leading parenthesis '(' */ PH7_DelimitNestedTokens(pIn,pEnd,PH7_TK_LPAREN/*'('*/,PH7_TK_RPAREN/*')'*/,&pIn); if( pIn >= pEnd || &pIn[1] >= pEnd ){ /* Syntax error */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Syntax error while declaring annonymous function"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } goto Synchronize; } pIn++; /* Jump the trailing parenthesis */ if( pIn->nType & PH7_TK_KEYWORD ){ sxu32 nKey = SX_PTR_TO_INT(pIn->pUserData); /* Check if we are dealing with a closure */ if( nKey == PH7_TKWRD_USE ){ pIn++; /* Jump the 'use' keyword */ if( pIn >= pEnd || (pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Syntax error while declaring annonymous function"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } goto Synchronize; } pIn++; /* Jump the leading parenthesis '(' */ PH7_DelimitNestedTokens(pIn,pEnd,PH7_TK_LPAREN/*'('*/,PH7_TK_RPAREN/*')'*/,&pIn); if( pIn >= pEnd || &pIn[1] >= pEnd ){ /* Syntax error */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Syntax error while declaring annonymous function"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } goto Synchronize; } pIn++; }else{ /* Syntax error */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Syntax error while declaring annonymous function"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } goto Synchronize; } } if( pIn->nType & PH7_TK_OCB /*'{'*/ ){ pIn++; /* Jump the leading curly '{' */ PH7_DelimitNestedTokens(pIn,pEnd,PH7_TK_OCB/*'{'*/,PH7_TK_CCB/*'}'*/,&pIn); if( pIn < pEnd ){ pIn++; } }else{ /* Syntax error */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Syntax error while declaring annonymous function,missing '{'"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } rc = SXRET_OK; Synchronize: /* Synchronize pointers */ *ppCur = pIn; return rc; } /* * Extract a single expression node from the input. * On success store the freshly extractd node in ppNode. * When errors,PH7 take care of generating the appropriate error message. * An expression node can be a variable [i.e: $var],an operator [i.e: ++] * an annonymous function [i.e: function(){ return "Hello"; }, a double/single * quoted string, a heredoc/nowdoc,a literal [i.e: PHP_EOL],a namespace path * [i.e: namespaces\path\to..],a array/list [i.e: array(4,5,6)] and so on. */ static sxi32 ExprExtractNode(ph7_gen_state *pGen,ph7_expr_node **ppNode) { ph7_expr_node *pNode; SyToken *pCur; sxi32 rc; /* Allocate a new node */ pNode = (ph7_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator,sizeof(ph7_expr_node)); if( pNode == 0 ){ /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ return SXERR_MEM; } /* Zero the structure */ SyZero(pNode,sizeof(ph7_expr_node)); SySetInit(&pNode->aNodeArgs,&pGen->pVm->sAllocator,sizeof(ph7_expr_node **)); /* Point to the head of the token stream */ pCur = pNode->pStart = pGen->pIn; /* Start collecting tokens */ if( pCur->nType & PH7_TK_OP ){ /* Point to the instance that describe this operator */ pNode->pOp = (const ph7_expr_op *)pCur->pUserData; /* Advance the stream cursor */ pCur++; }else if( pCur->nType & PH7_TK_DOLLAR ){ /* Isolate variable */ while( pCur < pGen->pEnd && (pCur->nType & PH7_TK_DOLLAR) ){ pCur++; /* Variable variable */ } if( pCur < pGen->pEnd ){ if (pCur->nType & (PH7_TK_ID|PH7_TK_KEYWORD) ){ /* Variable name */ pCur++; }else if( pCur->nType & PH7_TK_OCB /* '{' */ ){ pCur++; /* Dynamic variable name,Collect until the next non nested '}' */ PH7_DelimitNestedTokens(pCur,pGen->pEnd,PH7_TK_OCB, PH7_TK_CCB,&pCur); if( pCur < pGen->pEnd ){ pCur++; }else{ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"Syntax error: Missing closing brace '}'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } SyMemBackendPoolFree(&pGen->pVm->sAllocator,pNode); return rc; } } } pNode->xCode = PH7_CompileVariable; }else if( pCur->nType & PH7_TK_KEYWORD ){ sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pCur->pUserData); if( nKeyword == PH7_TKWRD_ARRAY || nKeyword == PH7_TKWRD_LIST ){ /* List/Array node */ if( &pCur[1] >= pGen->pEnd || (pCur[1].nType & PH7_TK_LPAREN) == 0 ){ /* Assume a literal */ ExprAssembleLiteral(&pCur,pGen->pEnd); pNode->xCode = PH7_CompileLiteral; }else{ pCur += 2; /* Collect array/list tokens */ PH7_DelimitNestedTokens(pCur,pGen->pEnd,PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */,&pCur); if( pCur < pGen->pEnd ){ pCur++; }else{ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine, "%s: Missing closing parenthesis ')'",nKeyword == PH7_TKWRD_LIST ? "list" : "array"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } SyMemBackendPoolFree(&pGen->pVm->sAllocator,pNode); return rc; } pNode->xCode = (nKeyword == PH7_TKWRD_LIST) ? PH7_CompileList : PH7_CompileArray; if( pNode->xCode == PH7_CompileList ){ ph7_expr_op *pOp = (pCur < pGen->pEnd) ? (ph7_expr_op *)pCur->pUserData : 0; if( pCur >= pGen->pEnd || (pCur->nType & PH7_TK_OP) == 0 || pOp == 0 || pOp->iVmOp != PH7_OP_STORE /*'='*/){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"list(): expecting '=' after construct"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } SyMemBackendPoolFree(&pGen->pVm->sAllocator,pNode); return rc; } } } }else if( nKeyword == PH7_TKWRD_FUNCTION ){ /* Annonymous function */ if( &pCur[1] >= pGen->pEnd ){ /* Assume a literal */ ExprAssembleLiteral(&pCur,pGen->pEnd); pNode->xCode = PH7_CompileLiteral; }else{ /* Assemble annonymous functions body */ rc = ExprAssembleAnnon(&(*pGen),&pCur,pGen->pEnd); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pGen->pVm->sAllocator,pNode); return rc; } pNode->xCode = PH7_CompileAnnonFunc; } }else if( PH7_IsLangConstruct(nKeyword,FALSE) == TRUE && &pCur[1] < pGen->pEnd ){ /* Language constructs [i.e: print,echo,die...] require special handling */ PH7_DelimitNestedTokens(pCur,pGen->pEnd,PH7_TK_LPAREN|PH7_TK_OCB|PH7_TK_OSB, PH7_TK_RPAREN|PH7_TK_CCB|PH7_TK_CSB,&pCur); pNode->xCode = PH7_CompileLangConstruct; }else{ /* Assume a literal */ ExprAssembleLiteral(&pCur,pGen->pEnd); pNode->xCode = PH7_CompileLiteral; } }else if( pCur->nType & (PH7_TK_NSSEP|PH7_TK_ID) ){ /* Constants,function name,namespace path,class name... */ ExprAssembleLiteral(&pCur,pGen->pEnd); pNode->xCode = PH7_CompileLiteral; }else{ if( (pCur->nType & (PH7_TK_LPAREN|PH7_TK_RPAREN|PH7_TK_COMMA|PH7_TK_COLON|PH7_TK_CSB|PH7_TK_OCB|PH7_TK_CCB)) == 0 ){ /* Point to the code generator routine */ pNode->xCode = PH7_GetNodeHandler(pCur->nType); if( pNode->xCode == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"Syntax error: Unexpected token '%z'",&pNode->pStart->sData); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } SyMemBackendPoolFree(&pGen->pVm->sAllocator,pNode); return rc; } } /* Advance the stream cursor */ pCur++; } /* Point to the end of the token stream */ pNode->pEnd = pCur; /* Save the node for later processing */ *ppNode = pNode; /* Synchronize cursors */ pGen->pIn = pCur; return SXRET_OK; } /* * Point to the next expression that should be evaluated shortly. * The cursor stops when it hit a comma ',' or a semi-colon and the nesting * level is zero. */ PH7_PRIVATE sxi32 PH7_GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext) { SyToken *pCur = pStart; sxi32 iNest = 0; if( pCur >= pEnd || (pCur->nType & PH7_TK_SEMI/*';'*/) ){ /* Last expression */ return SXERR_EOF; } while( pCur < pEnd ){ if( (pCur->nType & (PH7_TK_COMMA/*','*/|PH7_TK_SEMI/*';'*/)) && iNest <= 0){ break; } if( pCur->nType & (PH7_TK_LPAREN/*'('*/|PH7_TK_OSB/*'['*/|PH7_TK_OCB/*'{'*/) ){ iNest++; }else if( pCur->nType & (PH7_TK_RPAREN/*')'*/|PH7_TK_CSB/*']'*/|PH7_TK_CCB/*'}*/) ){ iNest--; } pCur++; } *ppNext = pCur; return SXRET_OK; } /* * Free an expression tree. */ static void ExprFreeTree(ph7_gen_state *pGen,ph7_expr_node *pNode) { if( pNode->pLeft ){ /* Release the left tree */ ExprFreeTree(&(*pGen),pNode->pLeft); } if( pNode->pRight ){ /* Release the right tree */ ExprFreeTree(&(*pGen),pNode->pRight); } if( pNode->pCond ){ /* Release the conditional tree used by the ternary operator */ ExprFreeTree(&(*pGen),pNode->pCond); } if( SySetUsed(&pNode->aNodeArgs) > 0 ){ ph7_expr_node **apArg; sxu32 n; /* Release node arguments */ apArg = (ph7_expr_node **)SySetBasePtr(&pNode->aNodeArgs); for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){ ExprFreeTree(&(*pGen),apArg[n]); } SySetRelease(&pNode->aNodeArgs); } /* Finally,release this node */ SyMemBackendPoolFree(&pGen->pVm->sAllocator,pNode); } /* * Free an expression tree. * This function is a wrapper around ExprFreeTree() defined above. */ PH7_PRIVATE sxi32 PH7_ExprFreeTree(ph7_gen_state *pGen,SySet *pNodeSet) { ph7_expr_node **apNode; sxu32 n; apNode = (ph7_expr_node **)SySetBasePtr(pNodeSet); for( n = 0 ; n < SySetUsed(pNodeSet) ; ++n ){ if( apNode[n] ){ ExprFreeTree(&(*pGen),apNode[n]); } } return SXRET_OK; } /* * Check if the given node is a modifialbe l/r-value. * Return TRUE if modifiable.FALSE otherwise. */ static int ExprIsModifiableValue(ph7_expr_node *pNode,sxu8 bFunc) { sxi32 iExprOp; if( pNode->pOp == 0 ){ return pNode->xCode == PH7_CompileVariable ? TRUE : FALSE; } iExprOp = pNode->pOp->iOp; if( iExprOp == EXPR_OP_ARROW /*'->' */ || iExprOp == EXPR_OP_DC /*'::'*/ ){ return TRUE; } if( iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/ ){ if( pNode->pLeft->pOp ) { if( pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_ARROW /*'->'*/ && pNode->pLeft->pOp->iOp != EXPR_OP_DC /*'::'*/){ return FALSE; } }else if( pNode->pLeft->xCode != PH7_CompileVariable ){ return FALSE; } return TRUE; } if( bFunc && iExprOp == EXPR_OP_FUNC_CALL ){ return TRUE; } /* Not a modifiable l or r-value */ return FALSE; } /* Forward declaration */ static sxi32 ExprMakeTree(ph7_gen_state *pGen,ph7_expr_node **apNode,sxi32 nToken); /* Macro to check if the given node is a terminal */ #define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft )) /* * Buid an expression tree for each given function argument. * When errors,PH7 take care of generating the appropriate error message. */ static sxi32 ExprProcessFuncArguments(ph7_gen_state *pGen,ph7_expr_node *pOp,ph7_expr_node **apNode,sxi32 nToken) { sxi32 iNest,iCur,iNode; sxi32 rc; /* Process function arguments from left to right */ iCur = 0; for(;;){ if( iCur >= nToken ){ /* No more arguments to process */ break; } iNode = iCur; iNest = 0; while( iCur < nToken ){ if( apNode[iCur] ){ if( (apNode[iCur]->pStart->nType & PH7_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0 ){ break; }else if( apNode[iCur]->pStart->nType & (PH7_TK_LPAREN|PH7_TK_OSB|PH7_TK_OCB) ){ iNest++; }else if( apNode[iCur]->pStart->nType & (PH7_TK_RPAREN|PH7_TK_CCB|PH7_TK_CSB) ){ iNest--; } } iCur++; } if( iCur > iNode ){ if( apNode[iNode] && (apNode[iNode]->pStart->nType & PH7_TK_AMPER /*'&'*/) && ((iCur - iNode) == 2) && apNode[iNode+1]->xCode == PH7_CompileVariable ){ PH7_GenCompileError(&(*pGen),E_WARNING,apNode[iNode]->pStart->nLine, "call-time pass-by-reference is depreceated"); ExprFreeTree(&(*pGen),apNode[iNode]); apNode[iNode] = 0; } ExprMakeTree(&(*pGen),&apNode[iNode],iCur-iNode); if( apNode[iNode] ){ /* Put a pointer to the root of the tree in the arguments set */ SySetPut(&pOp->aNodeArgs,(const void *)&apNode[iNode]); }else{ /* Empty function argument */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pOp->pStart->nLine,"Empty function argument"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } }else{ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pOp->pStart->nLine,"Missing function argument"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } /* Jump trailing comma */ if( iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & PH7_TK_COMMA) ){ iCur++; if( iCur >= nToken ){ /* missing function argument */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pOp->pStart->nLine,"Missing function argument"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } } } return SXRET_OK; } /* * Create an expression tree from an array of tokens. * If successful, the root of the tree is stored in apNode[0]. * When errors,PH7 take care of generating the appropriate error message. */ static sxi32 ExprMakeTree(ph7_gen_state *pGen,ph7_expr_node **apNode,sxi32 nToken) { sxi32 i,iLeft,iRight; ph7_expr_node *pNode; sxi32 iCur; sxi32 rc; if( nToken <= 0 || (nToken == 1 && apNode[0]->xCode) ){ /* TICKET 1433-17: self evaluating node */ return SXRET_OK; } /* Process expressions enclosed in parenthesis first */ for( iCur = 0 ; iCur < nToken ; ++iCur ){ sxi32 iNest; /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator * since the LPAREN token can also be an operator [i.e: Function call]. */ if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != PH7_TK_LPAREN ){ continue; } iNest = 1; iLeft = iCur; /* Find the closing parenthesis */ iCur++; while( iCur < nToken ){ if( apNode[iCur] ){ if( apNode[iCur]->pStart->nType & PH7_TK_RPAREN /* ')' */){ /* Decrement nesting level */ iNest--; if( iNest <= 0 ){ break; } }else if( apNode[iCur]->pStart->nType & PH7_TK_LPAREN /* '(' */ ){ /* Increment nesting level */ iNest++; } } iCur++; } if( iCur - iLeft > 1 ){ /* Recurse and process this expression */ rc = ExprMakeTree(&(*pGen),&apNode[iLeft + 1],iCur - iLeft - 1); if( rc != SXRET_OK ){ return rc; } } /* Free the left and right nodes */ ExprFreeTree(&(*pGen),apNode[iLeft]); ExprFreeTree(&(*pGen),apNode[iCur]); apNode[iLeft] = 0; apNode[iCur] = 0; } /* Process expressions enclosed in braces */ for( iCur = 0 ; iCur < nToken ; ++iCur ){ sxi32 iNest; /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator * since the OCB '{' token can also be an operator [i.e: subscripting]. */ if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != PH7_TK_OCB ){ continue; } iNest = 1; iLeft = iCur; /* Find the closing parenthesis */ iCur++; while( iCur < nToken ){ if( apNode[iCur] ){ if( apNode[iCur]->pStart->nType & PH7_TK_CCB/*'}'*/){ /* Decrement nesting level */ iNest--; if( iNest <= 0 ){ break; } }else if( apNode[iCur]->pStart->nType & PH7_TK_OCB /*'{'*/ ){ /* Increment nesting level */ iNest++; } } iCur++; } if( iCur - iLeft > 1 ){ /* Recurse and process this expression */ rc = ExprMakeTree(&(*pGen),&apNode[iLeft + 1],iCur - iLeft - 1); if( rc != SXRET_OK ){ return rc; } } /* Free the left and right nodes */ ExprFreeTree(&(*pGen),apNode[iLeft]); ExprFreeTree(&(*pGen),apNode[iCur]); apNode[iLeft] = 0; apNode[iCur] = 0; } /* Handle postfix [i.e: function call,subscripting,member access] operators with precedence 2 */ iLeft = -1; for( iCur = 0 ; iCur < nToken ; ++iCur ){ if( apNode[iCur] == 0 ){ continue; } pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0 ){ if( pNode->pOp->iOp == EXPR_OP_FUNC_CALL ){ /* Collect function arguments */ sxi32 iPtr = 0; sxi32 nFuncTok = 0; while( nFuncTok + iCur < nToken ){ if( apNode[nFuncTok+iCur] ){ if( apNode[nFuncTok+iCur]->pStart->nType & PH7_TK_LPAREN /*'('*/ ){ iPtr++; }else if ( apNode[nFuncTok+iCur]->pStart->nType & PH7_TK_RPAREN /*')'*/){ iPtr--; if( iPtr <= 0 ){ break; } } } nFuncTok++; } if( nFuncTok + iCur >= nToken ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"Missing right parenthesis ')'"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } if( iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/ ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"Invalid function name"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } if( nFuncTok > 1 ){ /* Process function arguments */ rc = ExprProcessFuncArguments(&(*pGen),pNode,&apNode[iCur+1],nFuncTok-1); if( rc != SXRET_OK ){ return rc; } } /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; apNode[iLeft] = 0; for( iPtr = 1; iPtr <= nFuncTok ; iPtr++ ){ apNode[iCur+iPtr] = 0; } }else if (pNode->pOp->iOp == EXPR_OP_SUBSCRIPT ){ /* Subscripting */ sxi32 iArrTok = iCur + 1; sxi32 iNest = 1; if( iLeft < 0 || apNode[iLeft] == 0 || (apNode[iLeft]->pOp == 0 && (apNode[iLeft]->xCode != PH7_CompileVariable && apNode[iLeft]->xCode != PH7_CompileSimpleString && apNode[iLeft]->xCode != PH7_CompileString ) ) || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* postfix */) ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"Invalid array name"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } /* Collect index tokens */ while( iArrTok < nToken ){ if( apNode[iArrTok] ){ if( apNode[iArrTok]->pOp && apNode[iArrTok]->pOp->iOp == EXPR_OP_SUBSCRIPT && apNode[iArrTok]->pLeft == 0){ /* Increment nesting level */ iNest++; }else if( apNode[iArrTok]->pStart->nType & PH7_TK_CSB /*']'*/){ /* Decrement nesting level */ iNest--; if( iNest <= 0 ){ break; } } } ++iArrTok; } if( iArrTok > iCur + 1 ){ /* Recurse and process this expression */ rc = ExprMakeTree(&(*pGen),&apNode[iCur+1],iArrTok - iCur - 1); if( rc != SXRET_OK ){ return rc; } /* Link the node to it's index */ SySetPut(&pNode->aNodeArgs,(const void *)&apNode[iCur+1]); } /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; pNode->pRight = 0; apNode[iLeft] = 0; for( iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest ){ apNode[iNest] = 0; } }else{ /* Member access operators [i.e: '->','::'] */ iRight = iCur + 1; while( iRight < nToken && apNode[iRight] == 0 ){ iRight++; } if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Missing/Invalid member name",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; if( pNode->pOp->iOp == EXPR_OP_ARROW /*'->'*/ && pNode->pLeft->pOp == 0 && pNode->pLeft->xCode != PH7_CompileVariable ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine, "'%z': Expecting a variable as left operand",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } pNode->pRight = apNode[iRight]; apNode[iLeft] = apNode[iRight] = 0; } } iLeft = iCur; } /* Handle left associative (new, clone) operators */ for( iCur = 0 ; iCur < nToken ; ++iCur ){ if( apNode[iCur] == 0 ){ continue; } pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iPrec == 1 && pNode->pLeft == 0 ){ SyToken *pToken; /* Get the left node */ iLeft = iCur + 1; while( iLeft < nToken && apNode[iLeft] == 0 ){ iLeft++; } if( iLeft >= nToken || !NODE_ISTERM(iLeft) ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Expecting class constructor call", &pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } /* Make sure the operand are of a valid type */ if( pNode->pOp->iOp == EXPR_OP_CLONE ){ /* Clone: * Symisc eXtension: 'clone' accepts now as it's left operand: * ++ function call (including annonymous) * ++ array member * ++ 'new' operator * Example: * clone $pObj; * clone obj(); // function obj(){ return new Class(); } * clone $a['object']; // $a = array('object' => new Class()); */ if( apNode[iLeft]->pOp == 0 ){ if( apNode[iLeft]->xCode != PH7_CompileVariable ){ pToken = apNode[iLeft]->pStart; rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Unexpected token '%z'", &pNode->pOp->sOp,&pToken->sData); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } } }else{ /* New */ if( apNode[iLeft]->pOp == 0 ){ ProcNodeConstruct xCons = apNode[iLeft]->xCode; if( xCons != PH7_CompileVariable && xCons != PH7_CompileLiteral && xCons != PH7_CompileSimpleString){ pToken = apNode[iLeft]->pStart; /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine, "'%z': Unexpected token '%z', expecting literal, variable or constructor call", &pNode->pOp->sOp,&pToken->sData); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } } } /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; apNode[iLeft] = 0; pNode->pRight = 0; /* Paranoid */ } } /* Handle post/pre icrement/decrement [i.e: ++/--] operators with precedence 3 */ iLeft = -1; for( iCur = 0 ; iCur < nToken ; ++iCur ){ if( apNode[iCur] == 0 ){ continue; } pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){ if( iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */) || apNode[iLeft]->xCode == PH7_CompileVariable) ){ /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; apNode[iLeft] = 0; } } iLeft = iCur; } iLeft = -1; for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){ if( apNode[iCur] == 0 ){ continue; } pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){ if( iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != PH7_CompileVariable) || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */) ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z' operator needs l-value",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; apNode[iLeft] = 0; /* Mark as pre-increment/decrement node */ pNode->iFlags |= EXPR_NODE_PRE_INCR; } iLeft = iCur; } /* Handle right associative unary and cast operators [i.e: !,(string),~...] with precedence 4*/ iLeft = 0; for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){ if( apNode[iCur] ){ pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0){ if( iLeft > 0 ){ /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; apNode[iLeft] = 0; if( pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4 ){ if( pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pLeft->pStart->nLine,"'%z': Missing operand",&pNode->pLeft->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } } }else{ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Missing operand",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } } /* Save terminal position */ iLeft = iCur; } } /* Process left and non-associative binary operators [i.e: *,/,&&,||...]*/ for( i = 7 ; i < 17 ; i++ ){ iLeft = -1; for( iCur = 0 ; iCur < nToken ; ++iCur ){ if( apNode[iCur] == 0 ){ continue; } pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){ /* Get the right node */ iRight = iCur + 1; while( iRight < nToken && apNode[iRight] == 0 ){ iRight++; } if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Missing/Invalid operand",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } if( pNode->pOp->iOp == EXPR_OP_REF ){ sxi32 iTmp; /* Reference operator [i.e: '&=' ]*/ if( ExprIsModifiableValue(apNode[iLeft],FALSE) == FALSE || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iVmOp == PH7_OP_MEMBER /*->,::*/) ){ /* Left operand must be a modifiable l-value */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'&': Left operand must be a modifiable l-value"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } if( apNode[iLeft]->pOp == 0 || apNode[iLeft]->pOp->iOp != EXPR_OP_SUBSCRIPT /*$a[] =& 14*/) { if( ExprIsModifiableValue(apNode[iRight],TRUE) == FALSE ){ if( apNode[iRight]->pOp == 0 || (apNode[iRight]->pOp->iOp != EXPR_OP_NEW /* new */ && apNode[iRight]->pOp->iOp != EXPR_OP_CLONE /* clone */) ){ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine, "Reference operator '&' require a variable not a constant expression as it's right operand"); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } } } /* Swap operands */ iTmp = iRight; iRight = iLeft; iLeft = iTmp; } /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; pNode->pRight = apNode[iRight]; apNode[iLeft] = apNode[iRight] = 0; } iLeft = iCur; } } /* Handle the ternary operator. (expr1) ? (expr2) : (expr3) * Note that we do not need a precedence loop here since * we are dealing with a single operator. */ iLeft = -1; for( iCur = 0 ; iCur < nToken ; ++iCur ){ if( apNode[iCur] == 0 ){ continue; } pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0 ){ sxi32 iNest = 1; if( iLeft < 0 || !NODE_ISTERM(iLeft) ){ /* Missing condition */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Syntax error",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } /* Get the right node */ iRight = iCur + 1; while( iRight < nToken ){ if( apNode[iRight] ){ if( apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0){ /* Increment nesting level */ ++iNest; }else if( apNode[iRight]->pStart->nType & PH7_TK_COLON /*:*/ ){ /* Decrement nesting level */ --iNest; if( iNest <= 0 ){ break; } } } iRight++; } if( iRight > iCur + 1 ){ /* Recurse and process the then expression */ rc = ExprMakeTree(&(*pGen),&apNode[iCur + 1],iRight - iCur - 1); if( rc != SXRET_OK ){ return rc; } /* Link the node to the tree */ pNode->pLeft = apNode[iCur + 1]; }else{ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Missing 'then' expression",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } apNode[iCur + 1] = 0; if( iRight + 1 < nToken ){ /* Recurse and process the else expression */ rc = ExprMakeTree(&(*pGen),&apNode[iRight + 1],nToken - iRight - 1); if( rc != SXRET_OK ){ return rc; } /* Link the node to the tree */ pNode->pRight = apNode[iRight + 1]; apNode[iRight + 1] = apNode[iRight] = 0; }else{ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Missing 'else' expression",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } /* Point to the condition */ pNode->pCond = apNode[iLeft]; apNode[iLeft] = 0; break; } iLeft = iCur; } /* Process right associative binary operators [i.e: '=','+=','/='] * Note: All right associative binary operators have precedence 18 * so there is no need for a precedence loop here. */ iRight = -1; for( iCur = nToken - 1 ; iCur >= 0 ; iCur--){ if( apNode[iCur] == 0 ){ continue; } pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iPrec == 18 && pNode->pLeft == 0 ){ /* Get the left node */ iLeft = iCur - 1; while( iLeft >= 0 && apNode[iLeft] == 0 ){ iLeft--; } if( iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Missing/Invalid operand",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } if( ExprIsModifiableValue(apNode[iLeft],FALSE) == FALSE ){ if( pNode->pOp->iVmOp != PH7_OP_STORE || apNode[iLeft]->xCode != PH7_CompileList ){ /* Left operand must be a modifiable l-value */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine, "'%z': Left operand must be a modifiable l-value",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } } /* Link the node to the tree (Reverse) */ pNode->pLeft = apNode[iRight]; pNode->pRight = apNode[iLeft]; apNode[iLeft] = apNode[iRight] = 0; } iRight = iCur; } /* Process left associative binary operators that have the lowest precedence [i.e: and,or,xor] */ for( i = 19 ; i < 23 ; i++ ){ iLeft = -1; for( iCur = 0 ; iCur < nToken ; ++iCur ){ if( apNode[iCur] == 0 ){ continue; } pNode = apNode[iCur]; if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){ /* Get the right node */ iRight = iCur + 1; while( iRight < nToken && apNode[iRight] == 0 ){ iRight++; } if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pNode->pStart->nLine,"'%z': Missing/Invalid operand",&pNode->pOp->sOp); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } /* Link the node to the tree */ pNode->pLeft = apNode[iLeft]; pNode->pRight = apNode[iRight]; apNode[iLeft] = apNode[iRight] = 0; } iLeft = iCur; } } /* Point to the root of the expression tree */ for( iCur = 1 ; iCur < nToken ; ++iCur ){ if( apNode[iCur] ){ if( (apNode[iCur]->pOp || apNode[iCur]->xCode ) && apNode[0] != 0){ rc = PH7_GenCompileError(pGen,E_ERROR,apNode[iCur]->pStart->nLine,"Unexpected token '%z'",&apNode[iCur]->pStart->sData); if( rc != SXERR_ABORT ){ rc = SXERR_SYNTAX; } return rc; } apNode[0] = apNode[iCur]; apNode[iCur] = 0; } } return SXRET_OK; } /* * Build an expression tree from the freshly extracted raw tokens. * If successful, the root of the tree is stored in ppRoot. * When errors,PH7 take care of generating the appropriate error message. * This is the public interface used by the most code generator routines. */ PH7_PRIVATE sxi32 PH7_ExprMakeTree(ph7_gen_state *pGen,SySet *pExprNode,ph7_expr_node **ppRoot) { ph7_expr_node **apNode; ph7_expr_node *pNode; sxi32 rc; /* Reset node container */ SySetReset(pExprNode); pNode = 0; /* Prevent compiler warning */ /* Extract nodes one after one until we hit the end of the input */ while( pGen->pIn < pGen->pEnd ){ rc = ExprExtractNode(&(*pGen),&pNode); if( rc != SXRET_OK ){ return rc; } /* Save the extracted node */ SySetPut(pExprNode,(const void *)&pNode); } if( SySetUsed(pExprNode) < 1 ){ /* Empty expression [i.e: A semi-colon;] */ *ppRoot = 0; return SXRET_OK; } apNode = (ph7_expr_node **)SySetBasePtr(pExprNode); /* Make sure we are dealing with valid nodes */ rc = ExprVerifyNodes(&(*pGen),apNode,(sxi32)SySetUsed(pExprNode)); if( rc != SXRET_OK ){ /* Don't worry about freeing memory,upper layer will * cleanup the mess left behind. */ *ppRoot = 0; return rc; } /* Build the tree */ rc = ExprMakeTree(&(*pGen),apNode,(sxi32)SySetUsed(pExprNode)); if( rc != SXRET_OK ){ /* Something goes wrong [i.e: Syntax error] */ *ppRoot = 0; return rc; } /* Point to the root of the tree */ *ppRoot = apNode[0]; return SXRET_OK; } /* * ---------------------------------------------------------- * File: oo.c * MD5: 4b7cc68a49eca23fc71ff7f103f5dfc7 * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: oo.c v1.9 FeeBSD 2012-07-17 03:44 devel $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* * This file implement an Object Oriented (OO) subsystem for the PH7 engine. */ /* * Create an empty class. * Return a pointer to a raw class (ph7_class instance) on success. NULL otherwise. */ PH7_PRIVATE ph7_class * PH7_NewRawClass(ph7_vm *pVm,const SyString *pName,sxu32 nLine) { ph7_class *pClass; char *zName; /* Allocate a new instance */ pClass = (ph7_class *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_class)); if( pClass == 0 ){ return 0; } /* Zero the structure */ SyZero(pClass,sizeof(ph7_class)); /* Duplicate class name */ zName = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte); if( zName == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pClass); return 0; } /* Initialize fields */ SyStringInitFromBuf(&pClass->sName,zName,pName->nByte); SyHashInit(&pClass->hMethod,&pVm->sAllocator,0,0); SyHashInit(&pClass->hAttr,&pVm->sAllocator,0,0); SyHashInit(&pClass->hDerived,&pVm->sAllocator,0,0); SySetInit(&pClass->aInterface,&pVm->sAllocator,sizeof(ph7_class *)); pClass->nLine = nLine; /* All done */ return pClass; } /* * Allocate and initialize a new class attribute. * Return a pointer to the class attribute on success. NULL otherwise. */ PH7_PRIVATE ph7_class_attr * PH7_NewClassAttr(ph7_vm *pVm,const SyString *pName,sxu32 nLine,sxi32 iProtection,sxi32 iFlags) { ph7_class_attr *pAttr; char *zName; pAttr = (ph7_class_attr *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_class_attr)); if( pAttr == 0 ){ return 0; } /* Zero the structure */ SyZero(pAttr,sizeof(ph7_class_attr)); /* Duplicate attribute name */ zName = SyMemBackendStrDup(&pVm->sAllocator,pName->zString,pName->nByte); if( zName == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pAttr); return 0; } /* Initialize fields */ SySetInit(&pAttr->aByteCode,&pVm->sAllocator,sizeof(VmInstr)); SyStringInitFromBuf(&pAttr->sName,zName,pName->nByte); pAttr->iProtection = iProtection; pAttr->nIdx = SXU32_HIGH; pAttr->iFlags = iFlags; pAttr->nLine = nLine; return pAttr; } /* * Allocate and initialize a new class method. * Return a pointer to the class method on success. NULL otherwise * This function associate with the newly created method an automatically generated * random unique name. */ PH7_PRIVATE ph7_class_method * PH7_NewClassMethod(ph7_vm *pVm,ph7_class *pClass,const SyString *pName,sxu32 nLine, sxi32 iProtection,sxi32 iFlags,sxi32 iFuncFlags) { ph7_class_method *pMeth; SyHashEntry *pEntry; SyString *pNamePtr; char zSalt[10]; char *zName; sxu32 nByte; /* Allocate a new class method instance */ pMeth = (ph7_class_method *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_class_method)); if( pMeth == 0 ){ return 0; } /* Zero the structure */ SyZero(pMeth,sizeof(ph7_class_method)); /* Check for an already installed method with the same name */ pEntry = SyHashGet(&pClass->hMethod,(const void *)pName->zString,pName->nByte); if( pEntry == 0 ){ /* Associate an unique VM name to this method */ nByte = sizeof(zSalt) + pName->nByte + SyStringLength(&pClass->sName)+sizeof(char)*7/*[[__'\0'*/; zName = (char *)SyMemBackendAlloc(&pVm->sAllocator,nByte); if( zName == 0 ){ SyMemBackendPoolFree(&pVm->sAllocator,pMeth); return 0; } pNamePtr = &pMeth->sVmName; /* Generate a random string */ PH7_VmRandomString(&(*pVm),zSalt,sizeof(zSalt)); pNamePtr->nByte = SyBufferFormat(zName,nByte,"[__%z@%z_%.*s]",&pClass->sName,pName,sizeof(zSalt),zSalt); pNamePtr->zString = zName; }else{ /* Method is condidate for 'overloading' */ ph7_class_method *pCurrent = (ph7_class_method *)pEntry->pUserData; pNamePtr = &pMeth->sVmName; /* Use the same VM name */ SyStringDupPtr(pNamePtr,&pCurrent->sVmName); zName = (char *)pNamePtr->zString; } if( iProtection != PH7_CLASS_PROT_PUBLIC ){ if( (pName->nByte == sizeof("__construct") - 1 && SyMemcmp(pName->zString,"__construct",sizeof("__construct") - 1 ) == 0) || (pName->nByte == sizeof("__destruct") - 1 && SyMemcmp(pName->zString,"__destruct",sizeof("__destruct") - 1 ) == 0) || SyStringCmp(pName,&pClass->sName,SyMemcmp) == 0 ){ /* Switch to public visibility when dealing with constructor/destructor */ iProtection = PH7_CLASS_PROT_PUBLIC; } } /* Initialize method fields */ pMeth->iProtection = iProtection; pMeth->iFlags = iFlags; pMeth->nLine = nLine; PH7_VmInitFuncState(&(*pVm),&pMeth->sFunc,&zName[sizeof(char)*4/*[__@*/+SyStringLength(&pClass->sName)], pName->nByte,iFuncFlags|VM_FUNC_CLASS_METHOD,pClass); return pMeth; } /* * Check if the given name have a class method associated with it. * Return the desired method [i.e: ph7_class_method instance] on success. NULL otherwise. */ PH7_PRIVATE ph7_class_method * PH7_ClassExtractMethod(ph7_class *pClass,const char *zName,sxu32 nByte) { SyHashEntry *pEntry; /* Perform a hash lookup */ pEntry = SyHashGet(&pClass->hMethod,(const void *)zName,nByte); if( pEntry == 0 ){ /* No such entry */ return 0; } /* Point to the desired method */ return (ph7_class_method *)pEntry->pUserData; } /* * Check if the given name is a class attribute. * Return the desired attribute [i.e: ph7_class_attr instance] on success.NULL otherwise. */ PH7_PRIVATE ph7_class_attr * PH7_ClassExtractAttribute(ph7_class *pClass,const char *zName,sxu32 nByte) { SyHashEntry *pEntry; /* Perform a hash lookup */ pEntry = SyHashGet(&pClass->hAttr,(const void *)zName,nByte); if( pEntry == 0 ){ /* No such entry */ return 0; } /* Point to the desierd method */ return (ph7_class_attr *)pEntry->pUserData; } /* * Install a class attribute in the corresponding container. * Return SXRET_OK on success. Any other return value indicates failure. */ PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_class *pClass,ph7_class_attr *pAttr) { SyString *pName = &pAttr->sName; sxi32 rc; rc = SyHashInsert(&pClass->hAttr,(const void *)pName->zString,pName->nByte,pAttr); return rc; } /* * Install a class method in the corresponding container. * Return SXRET_OK on success. Any other return value indicates failure. */ PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass,ph7_class_method *pMeth) { SyString *pName = &pMeth->sFunc.sName; sxi32 rc; rc = SyHashInsert(&pClass->hMethod,(const void *)pName->zString,pName->nByte,pMeth); return rc; } /* * Perform an inheritance operation. * According to the PHP language reference manual * When you extend a class, the subclass inherits all of the public and protected methods * from the parent class. Unless a class Overwrites those methods, they will retain their original * functionality. * This is useful for defining and abstracting functionality, and permits the implementation * of additional functionality in similar objects without the need to reimplement all of the shared * functionality. * Example #1 Inheritance Example * printItem('baz'); // Output: 'Foo: baz' * $foo->printPHP(); // Output: 'PHP is great' * $bar->printItem('baz'); // Output: 'Bar: baz' * $bar->printPHP(); // Output: 'PHP is great' * * This function return SXRET_OK if the inheritance operation was successfully performed. * Any other return value indicates failure and the upper layer must generate an appropriate * error message. */ PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_gen_state *pGen,ph7_class *pSub,ph7_class *pBase) { ph7_class_method *pMeth; ph7_class_attr *pAttr; SyHashEntry *pEntry; SyString *pName; sxi32 rc; /* Install in the derived hashtable */ rc = SyHashInsert(&pBase->hDerived,(const void *)SyStringData(&pSub->sName),SyStringLength(&pSub->sName),pSub); if( rc != SXRET_OK ){ return rc; } /* Copy public/protected attributes from the base class */ SyHashResetLoopCursor(&pBase->hAttr); while((pEntry = SyHashGetNextEntry(&pBase->hAttr)) != 0 ){ /* Make sure the private attributes are not redeclared in the subclass */ pAttr = (ph7_class_attr *)pEntry->pUserData; pName = &pAttr->sName; if( (pEntry = SyHashGet(&pSub->hAttr,(const void *)pName->zString,pName->nByte)) != 0 ){ if( pAttr->iProtection == PH7_CLASS_PROT_PRIVATE && ((ph7_class_attr *)pEntry->pUserData)->iProtection != PH7_CLASS_PROT_PUBLIC ){ /* Cannot redeclare private attribute */ PH7_GenCompileError(&(*pGen),E_WARNING,((ph7_class_attr *)pEntry->pUserData)->nLine, "Private attribute '%z::%z' redeclared inside child class '%z'", &pBase->sName,pName,&pSub->sName); } continue; } /* Install the attribute */ if( pAttr->iProtection != PH7_CLASS_PROT_PRIVATE ){ rc = SyHashInsert(&pSub->hAttr,(const void *)pName->zString,pName->nByte,pAttr); if( rc != SXRET_OK ){ return rc; } } } SyHashResetLoopCursor(&pBase->hMethod); while((pEntry = SyHashGetNextEntry(&pBase->hMethod)) != 0 ){ /* Make sure the private/final methods are not redeclared in the subclass */ pMeth = (ph7_class_method *)pEntry->pUserData; pName = &pMeth->sFunc.sName; if( (pEntry = SyHashGet(&pSub->hMethod,(const void *)pName->zString,pName->nByte)) != 0 ){ if( pMeth->iFlags & PH7_CLASS_ATTR_FINAL ){ /* Cannot Overwrite final method */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,((ph7_class_method *)pEntry->pUserData)->nLine, "Cannot Overwrite final method '%z:%z' inside child class '%z'", &pBase->sName,pName,&pSub->sName); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } continue; }else{ if( pMeth->iFlags & PH7_CLASS_ATTR_ABSTRACT ){ /* Abstract method must be defined in the child class */ PH7_GenCompileError(&(*pGen),E_WARNING,pMeth->nLine, "Abstract method '%z:%z' must be defined inside child class '%z'", &pBase->sName,pName,&pSub->sName); continue; } } /* Install the method */ if( pMeth->iProtection != PH7_CLASS_PROT_PRIVATE ){ rc = SyHashInsert(&pSub->hMethod,(const void *)pName->zString,pName->nByte,pMeth); if( rc != SXRET_OK ){ return rc; } } } /* Mark as subclass */ pSub->pBase = pBase; /* All done */ return SXRET_OK; } /* * Inherit an object interface from another object interface. * According to the PHP language reference manual. * Object interfaces allow you to create code which specifies which methods a class * must implement, without having to define how these methods are handled. * Interfaces are defined using the interface keyword, in the same way as a standard * class, but without any of the methods having their contents defined. * All methods declared in an interface must be public, this is the nature of an interface. * * This function return SXRET_OK if the interface inheritance operation was successfully performed. * Any other return value indicates failure and the upper layer must generate an appropriate * error message. */ PH7_PRIVATE sxi32 PH7_ClassInterfaceInherit(ph7_class *pSub,ph7_class *pBase) { ph7_class_method *pMeth; ph7_class_attr *pAttr; SyHashEntry *pEntry; SyString *pName; sxi32 rc; /* Install in the derived hashtable */ SyHashInsert(&pBase->hDerived,(const void *)SyStringData(&pSub->sName),SyStringLength(&pSub->sName),pSub); SyHashResetLoopCursor(&pBase->hAttr); /* Copy constants */ while((pEntry = SyHashGetNextEntry(&pBase->hAttr)) != 0 ){ /* Make sure the constants are not redeclared in the subclass */ pAttr = (ph7_class_attr *)pEntry->pUserData; pName = &pAttr->sName; if( SyHashGet(&pSub->hAttr,(const void *)pName->zString,pName->nByte) == 0 ){ /* Install the constant in the subclass */ rc = SyHashInsert(&pSub->hAttr,(const void *)pName->zString,pName->nByte,pAttr); if( rc != SXRET_OK ){ return rc; } } } SyHashResetLoopCursor(&pBase->hMethod); /* Copy methods signature */ while((pEntry = SyHashGetNextEntry(&pBase->hMethod)) != 0 ){ /* Make sure the method are not redeclared in the subclass */ pMeth = (ph7_class_method *)pEntry->pUserData; pName = &pMeth->sFunc.sName; if( SyHashGet(&pSub->hMethod,(const void *)pName->zString,pName->nByte) == 0 ){ /* Install the method */ rc = SyHashInsert(&pSub->hMethod,(const void *)pName->zString,pName->nByte,pMeth); if( rc != SXRET_OK ){ return rc; } } } /* Mark as subclass */ pSub->pBase = pBase; /* All done */ return SXRET_OK; } /* * Implements an object interface in the given main class. * According to the PHP language reference manual. * Object interfaces allow you to create code which specifies which methods a class * must implement, without having to define how these methods are handled. * Interfaces are defined using the interface keyword, in the same way as a standard * class, but without any of the methods having their contents defined. * All methods declared in an interface must be public, this is the nature of an interface. * * This function return SXRET_OK if the interface was successfully implemented. * Any other return value indicates failure and the upper layer must generate an appropriate * error message. */ PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_class *pMain,ph7_class *pInterface) { ph7_class_attr *pAttr; SyHashEntry *pEntry; SyString *pName; sxi32 rc; /* First off,copy all constants declared inside the interface */ SyHashResetLoopCursor(&pInterface->hAttr); while((pEntry = SyHashGetNextEntry(&pInterface->hAttr)) != 0 ){ /* Point to the constant declaration */ pAttr = (ph7_class_attr *)pEntry->pUserData; pName = &pAttr->sName; /* Make sure the attribute is not redeclared in the main class */ if( SyHashGet(&pMain->hAttr,pName->zString,pName->nByte) == 0 ){ /* Install the attribute */ rc = SyHashInsert(&pMain->hAttr,pName->zString,pName->nByte,pAttr); if( rc != SXRET_OK ){ return rc; } } } /* Install in the interface container */ SySetPut(&pMain->aInterface,(const void *)&pInterface); /* TICKET 1433-49/1: Symisc eXtension * A class may not implemnt all declared interface methods,so there * is no need for a method installer loop here. */ return SXRET_OK; } /* * Create a class instance [i.e: Object in the PHP jargon] at run-time. * The following function is called when an object is created at run-time * typically when the PH7_OP_NEW/PH7_OP_CLONE instructions are executed. * Notes on object creation. * * According to PHP language reference manual. * To create an instance of a class, the new keyword must be used. An object will always * be created unless the object has a constructor defined that throws an exception on error. * Classes should be defined before instantiation (and in some cases this is a requirement). * If a string containing the name of a class is used with new, a new instance of that class * will be created. If the class is in a namespace, its fully qualified name must be used when * doing this. * Example #3 Creating an instance * * In the class context, it is possible to create a new object by new self and new parent. * When assigning an already created instance of a class to a new variable, the new variable * will access the same instance as the object that was assigned. This behaviour is the same * when passing instances to a function. A copy of an already created object can be made by * cloning it. * Example #4 Object Assignment * var = '$assigned will have this value'; * $instance = null; // $instance and $reference become null * var_dump($instance); * var_dump($reference); * var_dump($assigned); * ?> * The above example will output: * NULL * NULL * object(SimpleClass)#1 (1) { * ["var"]=> * string(30) "$assigned will have this value" * } * Example #5 Creating new objects * * The above example will output: * bool(true) * bool(true) * bool(true) * Note that Symisc Systems have introduced powerfull extension to * OO subsystem. For example a class attribute may have any complex * expression associated with it when declaring the attribute unlike * the standard PHP engine which would allow a single value. * Example: * class myClass{ * public $var = 25<<1+foo()/bar(); * }; * Refer to the official documentation for more information. */ static ph7_class_instance * NewClassInstance(ph7_vm *pVm,ph7_class *pClass) { ph7_class_instance *pThis; /* Allocate a new instance */ pThis = (ph7_class_instance *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_class_instance)); if( pThis == 0 ){ return 0; } /* Zero the structure */ SyZero(pThis,sizeof(ph7_class_instance)); /* Initialize fields */ pThis->iRef = 1; pThis->pVm = pVm; pThis->pClass = pClass; SyHashInit(&pThis->hAttr,&pVm->sAllocator,0,0); return pThis; } /* * Wrapper around the NewClassInstance() function defined above. * See the block comment above for more information. */ PH7_PRIVATE ph7_class_instance * PH7_NewClassInstance(ph7_vm *pVm,ph7_class *pClass) { ph7_class_instance *pNew; sxi32 rc; pNew = NewClassInstance(&(*pVm),&(*pClass)); if( pNew == 0 ){ return 0; } /* Associate a private VM frame with this class instance */ rc = PH7_VmCreateClassInstanceFrame(&(*pVm),pNew); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pVm->sAllocator,pNew); return 0; } return pNew; } /* * Extract the value of a class instance [i.e: Object in the PHP jargon] attribute. * This function never fail. */ static ph7_value * ExtractClassAttrValue(ph7_vm *pVm,VmClassAttr *pAttr) { /* Extract the value */ ph7_value *pValue; pValue = (ph7_value *)SySetAt(&pVm->aMemObj,pAttr->nIdx); return pValue; } /* * Perform a clone operation on a class instance [i.e: Object in the PHP jargon]. * The following function is called when an object is cloned at run-time * typically when the PH7_OP_CLONE instruction is executed. * Notes on object cloning. * * According to PHP language reference manual. * Creating a copy of an object with fully replicated properties is not always the wanted behavior. * A good example of the need for copy constructors. Another example is if your object holds a reference * to another object which it uses and when you replicate the parent object you want to create * a new instance of this other object so that the replica has its own separate copy. * An object copy is created by using the clone keyword (which calls the object's __clone() method if possible). * An object's __clone() method cannot be called directly. * $copy_of_object = clone $object; * When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties. * Any properties that are references to other variables, will remain references. * Once the cloning is complete, if a __clone() method is defined, then the newly created object's __clone() method * will be called, to allow any necessary properties that need to be changed. * Example #1 Cloning an object * instance = ++self::$instances; * } * * public function __clone() { * $this->instance = ++self::$instances; * } * } * * class MyCloneable * { * public $object1; * public $object2; * * function __clone() * { * // Force a copy of this->object, otherwise * // it will point to same object. * $this->object1 = clone $this->object1; * } * } * $obj = new MyCloneable(); * $obj->object1 = new SubObject(); * $obj->object2 = new SubObject(); * $obj2 = clone $obj; * print("Original Object:\n"); * print_r($obj); * print("Cloned Object:\n"); * print_r($obj2); * ?> * The above example will output: * Original Object: * MyCloneable Object * ( * [object1] => SubObject Object * ( * [instance] => 1 * ) * * [object2] => SubObject Object * ( * [instance] => 2 * ) * * ) * Cloned Object: * MyCloneable Object * ( * [object1] => SubObject Object * ( * [instance] => 3 * ) * * [object2] => SubObject Object * ( * [instance] => 2 * ) * ) */ PH7_PRIVATE ph7_class_instance * PH7_CloneClassInstance(ph7_class_instance *pSrc) { ph7_class_instance *pClone; ph7_class_method *pMethod; SyHashEntry *pEntry2; SyHashEntry *pEntry; ph7_vm *pVm; sxi32 rc; /* Allocate a new instance */ pVm = pSrc->pVm; pClone = NewClassInstance(pVm,pSrc->pClass); if( pClone == 0 ){ return 0; } /* Associate a private VM frame with this class instance */ rc = PH7_VmCreateClassInstanceFrame(pVm,pClone); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pVm->sAllocator,pClone); return 0; } /* Duplicate object values */ SyHashResetLoopCursor(&pSrc->hAttr); SyHashResetLoopCursor(&pClone->hAttr); while((pEntry = SyHashGetNextEntry(&pSrc->hAttr)) != 0 && (pEntry2 = SyHashGetNextEntry(&pClone->hAttr)) != 0 ){ VmClassAttr *pSrcAttr = (VmClassAttr *)pEntry->pUserData; VmClassAttr *pDestAttr = (VmClassAttr *)pEntry2->pUserData; /* Duplicate non-static attribute */ if( (pSrcAttr->pAttr->iFlags & (PH7_CLASS_ATTR_STATIC|PH7_CLASS_ATTR_CONSTANT)) == 0 ){ ph7_value *pvSrc,*pvDest; pvSrc = ExtractClassAttrValue(pVm,pSrcAttr); pvDest = ExtractClassAttrValue(pVm,pDestAttr); if( pvSrc && pvDest ){ PH7_MemObjStore(pvSrc,pvDest); } } } /* call the __clone method on the cloned object if available */ pMethod = PH7_ClassExtractMethod(pClone->pClass,"__clone",sizeof("__clone")-1); if( pMethod ){ if( pMethod->iCloneDepth < 16 ){ pMethod->iCloneDepth++; PH7_VmCallClassMethod(pVm,pClone,pMethod,0,0,0); }else{ /* Nesting limit reached */ PH7_VmThrowError(pVm,0,PH7_CTX_ERR,"Object clone limit reached,no more call to __clone()"); } /* Reset the cursor */ pMethod->iCloneDepth = 0; } /* Return the cloned object */ return pClone; } #define CLASS_INSTANCE_DESTROYED 0x001 /* Instance is released */ /* * Release a class instance [i.e: Object in the PHP jargon] and invoke any defined destructor. * This routine is invoked as soon as there are no other references to a particular * class instance. */ static void PH7_ClassInstanceRelease(ph7_class_instance *pThis) { ph7_class_method *pDestr; SyHashEntry *pEntry; ph7_class *pClass; ph7_vm *pVm; if( pThis->iFlags & CLASS_INSTANCE_DESTROYED ){ /* * Already destroyed,return immediately. * This could happend if someone perform unset($this) in the destructor body. */ return; } /* Mark as destroyed */ pThis->iFlags |= CLASS_INSTANCE_DESTROYED; /* Invoke any defined destructor if available */ pVm = pThis->pVm; pClass = pThis->pClass; pDestr = PH7_ClassExtractMethod(pClass,"__destruct",sizeof("__destruct")-1); if( pDestr ){ /* Invoke the destructor */ pThis->iRef = 2; /* Prevent garbage collection */ PH7_VmCallClassMethod(pVm,pThis,pDestr,0,0,0); } /* Release non-static attributes */ SyHashResetLoopCursor(&pThis->hAttr); while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){ VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData; if( (pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_STATIC|PH7_CLASS_ATTR_CONSTANT)) == 0 ){ PH7_VmUnsetMemObj(pVm,pVmAttr->nIdx,TRUE); } SyMemBackendPoolFree(&pVm->sAllocator,pVmAttr); } /* Release the whole structure */ SyHashRelease(&pThis->hAttr); SyMemBackendPoolFree(&pVm->sAllocator,pThis); } /* * Decrement the reference count of a class instance [i.e Object in the PHP jargon]. * If the reference count reaches zero,release the whole instance. */ PH7_PRIVATE void PH7_ClassInstanceUnref(ph7_class_instance *pThis) { pThis->iRef--; if( pThis->iRef < 1 ){ /* No more reference to this instance */ PH7_ClassInstanceRelease(&(*pThis)); } } /* * Compare two class instances [i.e: Objects in the PHP jargon] * Note on objects comparison: * According to the PHP langauge reference manual * When using the comparison operator (==), object variables are compared in a simple manner * namely: Two object instances are equal if they have the same attributes and values, and are * instances of the same class. * On the other hand, when using the identity operator (===), object variables are identical * if and only if they refer to the same instance of the same class. * An example will clarify these rules. * Example #1 Example of object comparison * flag = $flag; * } * } * * class OtherFlag * { * public $flag; * * function OtherFlag($flag = true) { * $this->flag = $flag; * } * } * * $o = new Flag(); * $p = new Flag(); * $q = $o; * $r = new OtherFlag(); * * echo "Two instances of the same class\n"; * compareObjects($o, $p); * echo "\nTwo references to the same instance\n"; * compareObjects($o, $q); * echo "\nInstances of two different classes\n"; * compareObjects($o, $r); * ?> * The above example will output: * Two instances of the same class * o1 == o2 : TRUE * o1 != o2 : FALSE * o1 === o2 : FALSE * o1 !== o2 : TRUE * Two references to the same instance * o1 == o2 : TRUE * o1 != o2 : FALSE * o1 === o2 : TRUE * o1 !== o2 : FALSE * Instances of two different classes * o1 == o2 : FALSE * o1 != o2 : TRUE * o1 === o2 : FALSE * o1 !== o2 : TRUE * * This function return 0 if the objects are equals according to the comprison rules defined above. * Any other return values indicates difference. */ PH7_PRIVATE sxi32 PH7_ClassInstanceCmp(ph7_class_instance *pLeft,ph7_class_instance *pRight,int bStrict,int iNest) { SyHashEntry *pEntry,*pEntry2; ph7_value sV1,sV2; sxi32 rc; if( iNest > 31 ){ /* Nesting limit reached */ PH7_VmThrowError(pLeft->pVm,0,PH7_CTX_ERR,"Nesting limit reached: Infinite recursion?"); return 1; } /* Comparison is performed only if the objects are instance of the same class */ if( pLeft->pClass != pRight->pClass ){ return 1; } if( bStrict ){ /* * According to the PHP language reference manual: * when using the identity operator (===), object variables * are identical if and only if they refer to the same instance * of the same class. */ return !(pLeft == pRight); } /* * Attribute comparison. * According to the PHP reference manual: * When using the comparison operator (==), object variables are compared * in a simple manner, namely: Two object instances are equal if they have * the same attributes and values, and are instances of the same class. */ if( pLeft == pRight ){ /* Same instance,don't bother processing,object are equals */ return 0; } SyHashResetLoopCursor(&pLeft->hAttr); SyHashResetLoopCursor(&pRight->hAttr); PH7_MemObjInit(pLeft->pVm,&sV1); PH7_MemObjInit(pLeft->pVm,&sV2); sV1.nIdx = sV2.nIdx = SXU32_HIGH; while((pEntry = SyHashGetNextEntry(&pLeft->hAttr)) != 0 && (pEntry2 = SyHashGetNextEntry(&pRight->hAttr)) != 0 ){ VmClassAttr *p1 = (VmClassAttr *)pEntry->pUserData; VmClassAttr *p2 = (VmClassAttr *)pEntry2->pUserData; /* Compare only non-static attribute */ if( (p1->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC)) == 0 ){ ph7_value *pL,*pR; pL = ExtractClassAttrValue(pLeft->pVm,p1); pR = ExtractClassAttrValue(pRight->pVm,p2); if( pL && pR ){ PH7_MemObjLoad(pL,&sV1); PH7_MemObjLoad(pR,&sV2); /* Compare the two values now */ rc = PH7_MemObjCmp(&sV1,&sV2,bStrict,iNest+1); PH7_MemObjRelease(&sV1); PH7_MemObjRelease(&sV2); if( rc != 0 ){ /* Not equals */ return rc; } } } } /* Object are equals */ return 0; } /* * Dump a class instance and the store the dump in the BLOB given * as the first argument. * Note that only non-static/non-constants attribute are dumped. * This function is typically invoked when the user issue a call * to [var_dump(),var_export(),print_r(),...]. * This function SXRET_OK on success. Any other return value including * SXERR_LIMIT(infinite recursion) indicates failure. */ PH7_PRIVATE sxi32 PH7_ClassInstanceDump(SyBlob *pOut,ph7_class_instance *pThis,int ShowType,int nTab,int nDepth) { SyHashEntry *pEntry; ph7_value *pValue; sxi32 rc; int i; if( nDepth > 31 ){ static const char zInfinite[] = "Nesting limit reached: Infinite recursion?"; /* Nesting limit reached..halt immediately*/ SyBlobAppend(&(*pOut),zInfinite,sizeof(zInfinite)-1); if( ShowType ){ SyBlobAppend(&(*pOut),")",sizeof(char)); } return SXERR_LIMIT; } rc = SXRET_OK; if( !ShowType ){ SyBlobAppend(&(*pOut),"Object(",sizeof("Object(")-1); } /* Append class name */ SyBlobFormat(&(*pOut),"%z) {",&pThis->pClass->sName); #ifdef __WINNT__ SyBlobAppend(&(*pOut),"\r\n",sizeof("\r\n")-1); #else SyBlobAppend(&(*pOut),"\n",sizeof(char)); #endif /* Dump object attributes */ SyHashResetLoopCursor(&pThis->hAttr); while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0){ VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData; if((pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC)) == 0 ){ /* Dump non-static/constant attribute only */ for( i = 0 ; i < nTab ; i++ ){ SyBlobAppend(&(*pOut)," ",sizeof(char)); } pValue = ExtractClassAttrValue(pThis->pVm,pVmAttr); if( pValue ){ SyBlobFormat(&(*pOut),"['%z'] =>",&pVmAttr->pAttr->sName); #ifdef __WINNT__ SyBlobAppend(&(*pOut),"\r\n",sizeof("\r\n")-1); #else SyBlobAppend(&(*pOut),"\n",sizeof(char)); #endif rc = PH7_MemObjDump(&(*pOut),pValue,ShowType,nTab+1,nDepth,0); if( rc == SXERR_LIMIT ){ break; } } } } for( i = 0 ; i < nTab ; i++ ){ SyBlobAppend(&(*pOut)," ",sizeof(char)); } SyBlobAppend(&(*pOut),"}",sizeof(char)); return rc; } /* * Call a magic method [i.e: __toString(),__toBool(),__Invoke()...] * Return SXRET_OK on successfull call. Any other return value indicates failure. * Notes on magic methods. * According to the PHP language reference manual. * The function names __construct(), __destruct(), __call(), __callStatic() * __get(), __toString(), __invoke(), __clone() are magical in PHP classes. * You cannot have functions with these names in any of your classes unless * you want the magic functionality associated with them. * Example of magical methods: * __toString() * The __toString() method allows a class to decide how it will react when it is treated like * a string. For example, what echo $obj; will print. This method must return a string. * Example #2 Simple example * foo = $foo; * } * * public function __toString() * { * return $this->foo; * } * } * $class = new TestClass('Hello'); * echo $class; * ?> * The above example will output: * Hello * * Note that PH7 does not support all the magical method and introudces __toFloat(),__toInt() * which have the same behaviour as __toString() but for float and integer types * respectively. * Refer to the official documentation for more information. */ PH7_PRIVATE sxi32 PH7_ClassInstanceCallMagicMethod( ph7_vm *pVm, /* VM that own all this stuff */ ph7_class *pClass, /* Target class */ ph7_class_instance *pThis, /* Target object */ const char *zMethod, /* Magic method name [i.e: __toString()]*/ sxu32 nByte, /* zMethod length*/ const SyString *pAttrName /* Attribute name */ ) { ph7_value *apArg[2] = { 0 , 0 }; ph7_class_method *pMeth; ph7_value sAttr; /* cc warning */ sxi32 rc; int nArg; /* Make sure the magic method is available */ pMeth = PH7_ClassExtractMethod(&(*pClass),zMethod,nByte); if( pMeth == 0 ){ /* No such method,return immediately */ return SXERR_NOTFOUND; } nArg = 0; /* Copy arguments */ if( pAttrName ){ PH7_MemObjInitFromString(pVm,&sAttr,pAttrName); sAttr.nIdx = SXU32_HIGH; /* Mark as constant */ apArg[0] = &sAttr; nArg = 1; } /* Call the magic method now */ rc = PH7_VmCallClassMethod(pVm,&(*pThis),pMeth,0,nArg,apArg); /* Clean up */ if( pAttrName ){ PH7_MemObjRelease(&sAttr); } return rc; } /* * Extract the value of a class instance [i.e: Object in the PHP jargon]. * This function is simply a wrapper on ExtractClassAttrValue(). */ PH7_PRIVATE ph7_value * PH7_ClassInstanceExtractAttrValue(ph7_class_instance *pThis,VmClassAttr *pAttr) { /* Extract the attribute value */ ph7_value *pValue; pValue = ExtractClassAttrValue(pThis->pVm,pAttr); return pValue; } /* * Convert a class instance [i.e: Object in the PHP jargon] into a hashmap [i.e: array in the PHP jargon]. * Return SXRET_OK on success. Any other value indicates failure. * Note on object conversion to array: * Acccording to the PHP language reference manual * If an object is converted to an array, the result is an array whose elements are the object's properties. * The keys are the member variable names. * * The following example: * class Test { * public $A = 25<<1; // 50 * public $c = rand_str(3); // Random string of length 3 * public $d = rand() & 1023; // Random number between 0..1023 * } * var_dump((array) new Test()); * Will output: * array(3) { * [A] => * int(50) * [c] => * string(3 'aps') * [d] => * int(991) * } * You have noticed that PH7 allow class attributes [i.e: $a,$c,$d in the example above] * have any complex expression (even function calls/Annonymous functions) as their default * value unlike the standard PHP engine. * This is a very powerful feature that you have to look at. */ PH7_PRIVATE sxi32 PH7_ClassInstanceToHashmap(ph7_class_instance *pThis,ph7_hashmap *pMap) { SyHashEntry *pEntry; SyString *pAttrName; VmClassAttr *pAttr; ph7_value *pValue; ph7_value sName; /* Reset the loop cursor */ SyHashResetLoopCursor(&pThis->hAttr); PH7_MemObjInitFromString(pThis->pVm,&sName,0); while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){ /* Point to the current attribute */ pAttr = (VmClassAttr *)pEntry->pUserData; /* Extract attribute value */ pValue = ExtractClassAttrValue(pThis->pVm,pAttr); if( pValue ){ /* Build attribute name */ pAttrName = &pAttr->pAttr->sName; PH7_MemObjStringAppend(&sName,pAttrName->zString,pAttrName->nByte); /* Perform the insertion */ PH7_HashmapInsert(pMap,&sName,pValue); /* Reset the string cursor */ SyBlobReset(&sName.sBlob); } } PH7_MemObjRelease(&sName); return SXRET_OK; } /* * Iterate throw class attributes and invoke the given callback [i.e: xWalk()] for each * retrieved attribute. * Note that argument are passed to the callback by copy. That is,any modification to * the attribute value in the callback body will not alter the real attribute value. * If the callback wishes to abort processing [i.e: it's invocation] it must return * a value different from PH7_OK. * Refer to [ph7_object_walk()] for more information. */ PH7_PRIVATE sxi32 PH7_ClassInstanceWalk( ph7_class_instance *pThis, /* Target object */ int (*xWalk)(const char *,ph7_value *,void *), /* Walker callback */ void *pUserData /* Last argument to xWalk() */ ) { SyHashEntry *pEntry; /* Hash entry */ VmClassAttr *pAttr; /* Pointer to the attribute */ ph7_value *pValue; /* Attribute value */ ph7_value sValue; /* Copy of the attribute value */ int rc; /* Reset the loop cursor */ SyHashResetLoopCursor(&pThis->hAttr); PH7_MemObjInit(pThis->pVm,&sValue); /* Start the walk process */ while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0 ){ /* Point to the current attribute */ pAttr = (VmClassAttr *)pEntry->pUserData; /* Extract attribute value */ pValue = ExtractClassAttrValue(pThis->pVm,pAttr); if( pValue ){ PH7_MemObjLoad(pValue,&sValue); /* Invoke the supplied callback */ rc = xWalk(SyStringData(&pAttr->pAttr->sName),&sValue,pUserData); PH7_MemObjRelease(&sValue); if( rc != PH7_OK){ /* User callback request an operation abort */ return SXERR_ABORT; } } } /* All done */ return SXRET_OK; } /* * Extract a class atrribute value. * Return a pointer to the attribute value on success. Otherwise NULL. * Note: * Access to static and constant attribute is not allowed. That is,the function * will return NULL in case someone (host-application code) try to extract * a static/constant attribute. */ PH7_PRIVATE ph7_value * PH7_ClassInstanceFetchAttr(ph7_class_instance *pThis,const SyString *pName) { SyHashEntry *pEntry; VmClassAttr *pAttr; /* Query the attribute hashtable */ pEntry = SyHashGet(&pThis->hAttr,(const void *)pName->zString,pName->nByte); if( pEntry == 0 ){ /* No such attribute */ return 0; } /* Point to the class atrribute */ pAttr = (VmClassAttr *)pEntry->pUserData; /* Check if we are dealing with a static/constant attribute */ if( pAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT|PH7_CLASS_ATTR_STATIC) ){ /* Access is forbidden */ return 0; } /* Return the attribute value */ return ExtractClassAttrValue(pThis->pVm,pAttr); } /* * ---------------------------------------------------------- * File: memobj.c * MD5: 02f3ad7da7ab382b1d5bf779013b4d7b * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* This file handle low-level stuff related to indexed memory objects [i.e: ph7_value] */ /* * Notes on memory objects [i.e: ph7_value]. * Internally, the PH7 virtual machine manipulates nearly all PHP values * [i.e: string,int,float,resource,object,bool,null..] as ph7_values structures. * Each ph7_values struct may cache multiple representations (string, * integer etc.) of the same value. */ /* * Convert a 64-bit IEEE double into a 64-bit signed integer. * If the double is too large, return 0x8000000000000000. * * Most systems appear to do this simply by assigning ariables and without * the extra range tests. * But there are reports that windows throws an expection if the floating * point value is out of range. */ static sxi64 MemObjRealToInt(ph7_value *pObj) { #ifdef PH7_OMIT_FLOATING_POINT /* Real and 64bit integer are the same when floating point arithmetic * is omitted from the build. */ return pObj->rVal; #else /* ** Many compilers we encounter do not define constants for the ** minimum and maximum 64-bit integers, or they define them ** inconsistently. And many do not understand the "LL" notation. ** So we define our own static constants here using nothing ** larger than a 32-bit integer constant. */ static const sxi64 maxInt = LARGEST_INT64; static const sxi64 minInt = SMALLEST_INT64; ph7_real r = pObj->rVal; if( r<(ph7_real)minInt ){ return minInt; }else if( r>(ph7_real)maxInt ){ /* minInt is correct here - not maxInt. It turns out that assigning ** a very large positive number to an integer results in a very large ** negative integer. This makes no sense, but it is what x86 hardware ** does so for compatibility we will do the same in software. */ return minInt; }else{ return (sxi64)r; } #endif } /* * Convert a raw token value typically a stream of digit [i.e: hex,octal,binary or decimal] * to a 64-bit integer. */ PH7_PRIVATE sxi64 PH7_TokenValueToInt64(SyString *pVal) { sxi64 iVal = 0; if( pVal->nByte <= 0 ){ return 0; } if( pVal->zString[0] == '0' ){ sxi32 c; if( pVal->nByte == sizeof(char) ){ return 0; } c = pVal->zString[1]; if( c == 'x' || c == 'X' ){ /* Hex digit stream */ SyHexStrToInt64(pVal->zString,pVal->nByte,(void *)&iVal,0); }else if( c == 'b' || c == 'B' ){ /* Binary digit stream */ SyBinaryStrToInt64(pVal->zString,pVal->nByte,(void *)&iVal,0); }else{ /* Octal digit stream */ SyOctalStrToInt64(pVal->zString,pVal->nByte,(void *)&iVal,0); } }else{ /* Decimal digit stream */ SyStrToInt64(pVal->zString,pVal->nByte,(void *)&iVal,0); } return iVal; } /* * Return some kind of 64-bit integer value which is the best we can * do at representing the value that pObj describes as a string * representation. */ static sxi64 MemObjStringToInt(ph7_value *pObj) { SyString sVal; SyStringInitFromBuf(&sVal,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob)); return PH7_TokenValueToInt64(&sVal); } /* * Call a magic class method [i.e: __toString(),__toInt(),...] * Return SXRET_OK if the magic method is available and have been * successfully called. Any other return value indicates failure. */ static sxi32 MemObjCallClassCastMethod( ph7_vm *pVm, /* VM that trigger the invocation */ ph7_class_instance *pThis, /* Target class instance [i.e: Object] */ const char *zMethod, /* Magic method name [i.e: __toString] */ sxu32 nLen, /* Method name length */ ph7_value *pResult /* OUT: Store the return value of the magic method here */ ) { ph7_class_method *pMethod; /* Check if the method is available */ pMethod = PH7_ClassExtractMethod(pThis->pClass,zMethod,nLen); if( pMethod == 0 ){ /* No such method */ return SXERR_NOTFOUND; } /* Invoke the desired method */ PH7_VmCallClassMethod(&(*pVm),&(*pThis),pMethod,&(*pResult),0,0); /* Method successfully called,pResult should hold the return value */ return SXRET_OK; } /* * Return some kind of integer value which is the best we can * do at representing the value that pObj describes as an integer. * If pObj is an integer, then the value is exact. If pObj is * a floating-point then the value returned is the integer part. * If pObj is a string, then we make an attempt to convert it into * a integer and return that. * If pObj represents a NULL value, return 0. */ static sxi64 MemObjIntValue(ph7_value *pObj) { sxi32 iFlags; iFlags = pObj->iFlags; if (iFlags & MEMOBJ_REAL ){ return MemObjRealToInt(&(*pObj)); }else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ return pObj->x.iVal; }else if (iFlags & MEMOBJ_STRING) { return MemObjStringToInt(&(*pObj)); }else if( iFlags & MEMOBJ_NULL ){ return 0; }else if( iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther; sxu32 n = pMap->nEntry; PH7_HashmapUnref(pMap); /* Return total number of entries in the hashmap */ return n; }else if( iFlags & MEMOBJ_OBJ ){ ph7_value sResult; sxi64 iVal = 1; sxi32 rc; /* Invoke the [__toInt()] magic method if available [note that this is a symisc extension] */ PH7_MemObjInit(pObj->pVm,&sResult); rc = MemObjCallClassCastMethod(pObj->pVm,(ph7_class_instance *)pObj->x.pOther, "__toInt",sizeof("__toInt")-1,&sResult); if( rc == SXRET_OK && (sResult.iFlags & MEMOBJ_INT) ){ /* Extract method return value */ iVal = sResult.x.iVal; } PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther); PH7_MemObjRelease(&sResult); return iVal; }else if(iFlags & MEMOBJ_RES ){ return pObj->x.pOther != 0; } /* CANT HAPPEN */ return 0; } /* * Return some kind of real value which is the best we can * do at representing the value that pObj describes as a real. * If pObj is a real, then the value is exact.If pObj is an * integer then the integer is promoted to real and that value * is returned. * If pObj is a string, then we make an attempt to convert it * into a real and return that. * If pObj represents a NULL value, return 0.0 */ static ph7_real MemObjRealValue(ph7_value *pObj) { sxi32 iFlags; iFlags = pObj->iFlags; if( iFlags & MEMOBJ_REAL ){ return pObj->rVal; }else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ return (ph7_real)pObj->x.iVal; }else if (iFlags & MEMOBJ_STRING){ SyString sString; #ifdef PH7_OMIT_FLOATING_POINT ph7_real rVal = 0; #else ph7_real rVal = 0.0; #endif SyStringInitFromBuf(&sString,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob)); if( SyBlobLength(&pObj->sBlob) > 0 ){ /* Convert as much as we can */ #ifdef PH7_OMIT_FLOATING_POINT rVal = MemObjStringToInt(&(*pObj)); #else SyStrToReal(sString.zString,sString.nByte,(void *)&rVal,0); #endif } return rVal; }else if( iFlags & MEMOBJ_NULL ){ #ifdef PH7_OMIT_FLOATING_POINT return 0; #else return 0.0; #endif }else if( iFlags & MEMOBJ_HASHMAP ){ /* Return the total number of entries in the hashmap */ ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther; ph7_real n = (ph7_real)pMap->nEntry; PH7_HashmapUnref(pMap); return n; }else if( iFlags & MEMOBJ_OBJ ){ ph7_value sResult; ph7_real rVal = 1; sxi32 rc; /* Invoke the [__toFloat()] magic method if available [note that this is a symisc extension] */ PH7_MemObjInit(pObj->pVm,&sResult); rc = MemObjCallClassCastMethod(pObj->pVm,(ph7_class_instance *)pObj->x.pOther, "__toFloat",sizeof("__toFloat")-1,&sResult); if( rc == SXRET_OK && (sResult.iFlags & MEMOBJ_REAL) ){ /* Extract method return value */ rVal = sResult.rVal; } PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther); PH7_MemObjRelease(&sResult); return rVal; }else if(iFlags & MEMOBJ_RES ){ return (ph7_real)(pObj->x.pOther != 0); } /* NOT REACHED */ return 0; } /* * Return the string representation of a given ph7_value. * This function never fail and always return SXRET_OK. */ static sxi32 MemObjStringValue(SyBlob *pOut,ph7_value *pObj,sxu8 bStrictBool) { if( pObj->iFlags & MEMOBJ_REAL ){ SyBlobFormat(&(*pOut),"%.15g",pObj->rVal); }else if( pObj->iFlags & MEMOBJ_INT ){ SyBlobFormat(&(*pOut),"%qd",pObj->x.iVal); /* %qd (BSD quad) is equivalent to %lld in the libc printf */ }else if( pObj->iFlags & MEMOBJ_BOOL ){ if( pObj->x.iVal ){ SyBlobAppend(&(*pOut),"TRUE",sizeof("TRUE")-1); }else{ if( !bStrictBool ){ SyBlobAppend(&(*pOut),"FALSE",sizeof("FALSE")-1); } } }else if( pObj->iFlags & MEMOBJ_HASHMAP ){ SyBlobAppend(&(*pOut),"Array",sizeof("Array")-1); PH7_HashmapUnref((ph7_hashmap *)pObj->x.pOther); }else if( pObj->iFlags & MEMOBJ_OBJ ){ ph7_value sResult; sxi32 rc; /* Invoke the __toString() method if available */ PH7_MemObjInit(pObj->pVm,&sResult); rc = MemObjCallClassCastMethod(pObj->pVm,(ph7_class_instance *)pObj->x.pOther, "__toString",sizeof("__toString")-1,&sResult); if( rc == SXRET_OK && (sResult.iFlags & MEMOBJ_STRING) && SyBlobLength(&sResult.sBlob) > 0){ /* Expand method return value */ SyBlobDup(&sResult.sBlob,pOut); }else{ /* Expand "Object" as requested by the PHP language reference manual */ SyBlobAppend(&(*pOut),"Object",sizeof("Object")-1); } PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther); PH7_MemObjRelease(&sResult); }else if(pObj->iFlags & MEMOBJ_RES ){ SyBlobFormat(&(*pOut),"ResourceID_%#x",pObj->x.pOther); } return SXRET_OK; } /* * Return some kind of boolean value which is the best we can do * at representing the value that pObj describes as a boolean. * When converting to boolean, the following values are considered FALSE: * NULL * the boolean FALSE itself. * the integer 0 (zero). * the real 0.0 (zero). * the empty string,a stream of zero [i.e: "0","00","000",...] and the string * "false". * an array with zero elements. */ static sxi32 MemObjBooleanValue(ph7_value *pObj) { sxi32 iFlags; iFlags = pObj->iFlags; if (iFlags & MEMOBJ_REAL ){ #ifdef PH7_OMIT_FLOATING_POINT return pObj->rVal ? 1 : 0; #else return pObj->rVal != 0.0 ? 1 : 0; #endif }else if( iFlags & MEMOBJ_INT ){ return pObj->x.iVal ? 1 : 0; }else if (iFlags & MEMOBJ_STRING) { SyString sString; SyStringInitFromBuf(&sString,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob)); if( sString.nByte == 0 ){ /* Empty string */ return 0; }else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString,"true",sizeof("true")-1) == 0) || (sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString,"on",sizeof("on")-1) == 0) || (sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString,"yes",sizeof("yes")-1) == 0) ){ return 1; }else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString,"false",sizeof("false")-1) == 0 ){ return 0; }else{ const char *zIn,*zEnd; zIn = sString.zString; zEnd = &zIn[sString.nByte]; while( zIn < zEnd && zIn[0] == '0' ){ zIn++; } return zIn >= zEnd ? 0 : 1; } }else if( iFlags & MEMOBJ_NULL ){ return 0; }else if( iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther; sxu32 n = pMap->nEntry; PH7_HashmapUnref(pMap); return n > 0 ? TRUE : FALSE; }else if( iFlags & MEMOBJ_OBJ ){ ph7_value sResult; sxi32 iVal = 1; sxi32 rc; /* Invoke the __toBool() method if available [note that this is a symisc extension] */ PH7_MemObjInit(pObj->pVm,&sResult); rc = MemObjCallClassCastMethod(pObj->pVm,(ph7_class_instance *)pObj->x.pOther, "__toBool",sizeof("__toBool")-1,&sResult); if( rc == SXRET_OK && (sResult.iFlags & (MEMOBJ_INT|MEMOBJ_BOOL)) ){ /* Extract method return value */ iVal = (sxi32)(sResult.x.iVal != 0); /* Stupid cc warning -W -Wall -O6 */ } PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther); PH7_MemObjRelease(&sResult); return iVal; }else if(iFlags & MEMOBJ_RES ){ return pObj->x.pOther != 0; } /* NOT REACHED */ return 0; } /* * If the ph7_value is of type real,try to make it an integer also. */ static sxi32 MemObjTryIntger(ph7_value *pObj) { pObj->x.iVal = MemObjRealToInt(&(*pObj)); /* Only mark the value as an integer if ** ** (1) the round-trip conversion real->int->real is a no-op, and ** (2) The integer is neither the largest nor the smallest ** possible integer ** ** The second and third terms in the following conditional enforces ** the second condition under the assumption that addition overflow causes ** values to wrap around. On x86 hardware, the third term is always ** true and could be omitted. But we leave it in because other ** architectures might behave differently. */ if( pObj->rVal ==(ph7_real)pObj->x.iVal && pObj->x.iVal>SMALLEST_INT64 && pObj->x.iValiFlags |= MEMOBJ_INT; } return SXRET_OK; } /* * Convert a ph7_value to type integer.Invalidate any prior representations. */ PH7_PRIVATE sxi32 PH7_MemObjToInteger(ph7_value *pObj) { if( (pObj->iFlags & MEMOBJ_INT) == 0 ){ /* Preform the conversion */ pObj->x.iVal = MemObjIntValue(&(*pObj)); /* Invalidate any prior representations */ SyBlobRelease(&pObj->sBlob); MemObjSetType(pObj,MEMOBJ_INT); } return SXRET_OK; } /* * Convert a ph7_value to type real (Try to get an integer representation also). * Invalidate any prior representations */ PH7_PRIVATE sxi32 PH7_MemObjToReal(ph7_value *pObj) { if((pObj->iFlags & MEMOBJ_REAL) == 0 ){ /* Preform the conversion */ pObj->rVal = MemObjRealValue(&(*pObj)); /* Invalidate any prior representations */ SyBlobRelease(&pObj->sBlob); MemObjSetType(pObj,MEMOBJ_REAL); /* Try to get an integer representation */ MemObjTryIntger(&(*pObj)); } return SXRET_OK; } /* * Convert a ph7_value to type boolean.Invalidate any prior representations. */ PH7_PRIVATE sxi32 PH7_MemObjToBool(ph7_value *pObj) { if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){ /* Preform the conversion */ pObj->x.iVal = MemObjBooleanValue(&(*pObj)); /* Invalidate any prior representations */ SyBlobRelease(&pObj->sBlob); MemObjSetType(pObj,MEMOBJ_BOOL); } return SXRET_OK; } /* * Convert a ph7_value to type string.Prior representations are NOT invalidated. */ PH7_PRIVATE sxi32 PH7_MemObjToString(ph7_value *pObj) { sxi32 rc = SXRET_OK; if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ /* Perform the conversion */ SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */ rc = MemObjStringValue(&pObj->sBlob,&(*pObj),TRUE); MemObjSetType(pObj,MEMOBJ_STRING); } return rc; } /* * Nullify a ph7_value.In other words invalidate any prior * representation. */ PH7_PRIVATE sxi32 PH7_MemObjToNull(ph7_value *pObj) { return PH7_MemObjRelease(pObj); } /* * Convert a ph7_value to type array.Invalidate any prior representations. * According to the PHP language reference manual. * For any of the types: integer, float, string, boolean converting a value * to an array results in an array with a single element with index zero * and the value of the scalar which was converted. */ PH7_PRIVATE sxi32 PH7_MemObjToHashmap(ph7_value *pObj) { if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){ ph7_hashmap *pMap; /* Allocate a new hashmap instance */ pMap = PH7_NewHashmap(pObj->pVm,0,0); if( pMap == 0 ){ return SXERR_MEM; } if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){ /* * According to the PHP language reference manual. * For any of the types: integer, float, string, boolean converting a value * to an array results in an array with a single element with index zero * and the value of the scalar which was converted. */ if( pObj->iFlags & MEMOBJ_OBJ ){ /* Object cast */ PH7_ClassInstanceToHashmap((ph7_class_instance *)pObj->x.pOther,pMap); }else{ /* Insert a single element */ PH7_HashmapInsert(pMap,0/* Automatic index assign */,&(*pObj)); } SyBlobRelease(&pObj->sBlob); } /* Invalidate any prior representation */ MemObjSetType(pObj,MEMOBJ_HASHMAP); pObj->x.pOther = pMap; } return SXRET_OK; } /* * Convert a ph7_value to type object.Invalidate any prior representations. * The new object is instantiated from the builtin stdClass(). * The stdClass() class have a single attribute which is '$value'. This attribute * hold a copy of the converted ph7_value. * The internal of the stdClass is as follows: * class stdClass{ * public $value; * public function __toInt(){ return (int)$this->value; } * public function __toBool(){ return (bool)$this->value; } * public function __toFloat(){ return (float)$this->value; } * public function __toString(){ return (string)$this->value; } * function __construct($v){ $this->value = $v; }" * } * Refer to the official documentation for more information. */ PH7_PRIVATE sxi32 PH7_MemObjToObject(ph7_value *pObj) { if( (pObj->iFlags & MEMOBJ_OBJ) == 0 ){ ph7_class_instance *pStd; ph7_class_method *pCons; ph7_class *pClass; ph7_vm *pVm; /* Point to the underlying VM */ pVm = pObj->pVm; /* Point to the stdClass() */ pClass = PH7_VmExtractClass(pVm,"stdClass",sizeof("stdClass")-1,0,0); if( pClass == 0 ){ /* Can't happen,load null instead */ PH7_MemObjRelease(pObj); return SXRET_OK; } /* Instanciate a new stdClass() object */ pStd = PH7_NewClassInstance(pVm,pClass); if( pStd == 0 ){ /* Out of memory */ PH7_MemObjRelease(pObj); return SXRET_OK; } /* Check if a constructor is available */ pCons = PH7_ClassExtractMethod(pClass,"__construct",sizeof("__construct")-1); if( pCons ){ ph7_value *apArg[2]; /* Invoke the constructor with one argument */ apArg[0] = pObj; PH7_VmCallClassMethod(pVm,pStd,pCons,0,1,apArg); if( pStd->iRef < 1 ){ pStd->iRef = 1; } } /* Invalidate any prior representation */ PH7_MemObjRelease(pObj); /* Save the new instance */ pObj->x.pOther = pStd; MemObjSetType(pObj,MEMOBJ_OBJ); } return SXRET_OK; } /* * Return a pointer to the appropriate convertion method associated * with the given type. * Note on type juggling. * Accoding to the PHP language reference manual * PHP does not require (or support) explicit type definition in variable * declaration; a variable's type is determined by the context in which * the variable is used. That is to say, if a string value is assigned * to variable $var, $var becomes a string. If an integer value is then * assigned to $var, it becomes an integer. */ PH7_PRIVATE ProcMemObjCast PH7_MemObjCastMethod(sxi32 iFlags) { if( iFlags & MEMOBJ_STRING ){ return PH7_MemObjToString; }else if( iFlags & MEMOBJ_INT ){ return PH7_MemObjToInteger; }else if( iFlags & MEMOBJ_REAL ){ return PH7_MemObjToReal; }else if( iFlags & MEMOBJ_BOOL ){ return PH7_MemObjToBool; }else if( iFlags & MEMOBJ_HASHMAP ){ return PH7_MemObjToHashmap; }else if( iFlags & MEMOBJ_OBJ ){ return PH7_MemObjToObject; } /* NULL cast */ return PH7_MemObjToNull; } /* * Check whether the ph7_value is numeric [i.e: int/float/bool] or looks * like a numeric number [i.e: if the ph7_value is of type string.]. * Return TRUE if numeric.FALSE otherwise. */ PH7_PRIVATE sxi32 PH7_MemObjIsNumeric(ph7_value *pObj) { if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){ return TRUE; }else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES) ){ return FALSE; }else if( pObj->iFlags & MEMOBJ_STRING ){ SyString sStr; sxi32 rc; SyStringInitFromBuf(&sStr,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob)); if( sStr.nByte <= 0 ){ /* Empty string */ return FALSE; } /* Check if the string representation looks like a numeric number */ rc = SyStrIsNumeric(sStr.zString,sStr.nByte,0,0); return rc == SXRET_OK ? TRUE : FALSE; } /* NOT REACHED */ return FALSE; } /* * Check whether the ph7_value is empty.Return TRUE if empty. * FALSE otherwise. * An ph7_value is considered empty if the following are true: * NULL value. * Boolean FALSE. * Integer/Float with a 0 (zero) value. * An empty string or a stream of 0 (zero) [i.e: "0","00","000",...]. * An empty array. * NOTE * OBJECT VALUE MUST NOT BE MODIFIED. */ PH7_PRIVATE sxi32 PH7_MemObjIsEmpty(ph7_value *pObj) { if( pObj->iFlags & MEMOBJ_NULL ){ return TRUE; }else if( pObj->iFlags & MEMOBJ_INT ){ return pObj->x.iVal == 0 ? TRUE : FALSE; }else if( pObj->iFlags & MEMOBJ_REAL ){ return pObj->rVal == (ph7_real)0 ? TRUE : FALSE; }else if( pObj->iFlags & MEMOBJ_BOOL ){ return !pObj->x.iVal; }else if( pObj->iFlags & MEMOBJ_STRING ){ if( SyBlobLength(&pObj->sBlob) <= 0 ){ return TRUE; }else{ const char *zIn,*zEnd; zIn = (const char *)SyBlobData(&pObj->sBlob); zEnd = &zIn[SyBlobLength(&pObj->sBlob)]; while( zIn < zEnd ){ if( zIn[0] != '0' ){ break; } zIn++; } return zIn >= zEnd ? TRUE : FALSE; } }else if( pObj->iFlags & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther; return pMap->nEntry == 0 ? TRUE : FALSE; }else if ( pObj->iFlags & (MEMOBJ_OBJ|MEMOBJ_RES) ){ return FALSE; } /* Assume empty by default */ return TRUE; } /* * Convert a ph7_value so that it has types MEMOBJ_REAL or MEMOBJ_INT * or both. * Invalidate any prior representations. Every effort is made to force * the conversion, even if the input is a string that does not look * completely like a number.Convert as much of the string as we can * and ignore the rest. */ PH7_PRIVATE sxi32 PH7_MemObjToNumeric(ph7_value *pObj) { if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){ if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){ if( pObj->iFlags & MEMOBJ_NULL ){ pObj->x.iVal = 0; } MemObjSetType(pObj,MEMOBJ_INT); } /* Already numeric */ return SXRET_OK; } if( pObj->iFlags & MEMOBJ_STRING ){ sxi32 rc = SXERR_INVALID; sxu8 bReal = FALSE; SyString sString; SyStringInitFromBuf(&sString,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob)); /* Check if the given string looks like a numeric number */ if( sString.nByte > 0 ){ rc = SyStrIsNumeric(sString.zString,sString.nByte,&bReal,0); } if( bReal ){ PH7_MemObjToReal(&(*pObj)); }else{ if( rc != SXRET_OK ){ /* The input does not look at all like a number,set the value to 0 */ pObj->x.iVal = 0; }else{ /* Convert as much as we can */ pObj->x.iVal = MemObjStringToInt(&(*pObj)); } MemObjSetType(pObj,MEMOBJ_INT); SyBlobRelease(&pObj->sBlob); } }else if(pObj->iFlags & (MEMOBJ_OBJ|MEMOBJ_HASHMAP|MEMOBJ_RES)){ PH7_MemObjToInteger(pObj); }else{ /* Perform a blind cast */ PH7_MemObjToReal(&(*pObj)); } return SXRET_OK; } /* * Try a get an integer representation of the given ph7_value. * If the ph7_value is not of type real,this function is a no-op. */ PH7_PRIVATE sxi32 PH7_MemObjTryInteger(ph7_value *pObj) { if( pObj->iFlags & MEMOBJ_REAL ){ /* Work only with reals */ MemObjTryIntger(&(*pObj)); } return SXRET_OK; } /* * Initialize a ph7_value to the null type. */ PH7_PRIVATE sxi32 PH7_MemObjInit(ph7_vm *pVm,ph7_value *pObj) { /* Zero the structure */ SyZero(pObj,sizeof(ph7_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob,&pVm->sAllocator); /* Set the NULL type */ pObj->iFlags = MEMOBJ_NULL; return SXRET_OK; } /* * Initialize a ph7_value to the integer type. */ PH7_PRIVATE sxi32 PH7_MemObjInitFromInt(ph7_vm *pVm,ph7_value *pObj,sxi64 iVal) { /* Zero the structure */ SyZero(pObj,sizeof(ph7_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob,&pVm->sAllocator); /* Set the desired type */ pObj->x.iVal = iVal; pObj->iFlags = MEMOBJ_INT; return SXRET_OK; } /* * Initialize a ph7_value to the boolean type. */ PH7_PRIVATE sxi32 PH7_MemObjInitFromBool(ph7_vm *pVm,ph7_value *pObj,sxi32 iVal) { /* Zero the structure */ SyZero(pObj,sizeof(ph7_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob,&pVm->sAllocator); /* Set the desired type */ pObj->x.iVal = iVal ? 1 : 0; pObj->iFlags = MEMOBJ_BOOL; return SXRET_OK; } #if 0 /* * Initialize a ph7_value to the real type. */ PH7_PRIVATE sxi32 PH7_MemObjInitFromReal(ph7_vm *pVm,ph7_value *pObj,ph7_real rVal) { /* Zero the structure */ SyZero(pObj,sizeof(ph7_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob,&pVm->sAllocator); /* Set the desired type */ pObj->rVal = rVal; pObj->iFlags = MEMOBJ_REAL; return SXRET_OK; } #endif /* * Initialize a ph7_value to the array type. */ PH7_PRIVATE sxi32 PH7_MemObjInitFromArray(ph7_vm *pVm,ph7_value *pObj,ph7_hashmap *pArray) { /* Zero the structure */ SyZero(pObj,sizeof(ph7_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob,&pVm->sAllocator); /* Set the desired type */ pObj->iFlags = MEMOBJ_HASHMAP; pObj->x.pOther = pArray; return SXRET_OK; } /* * Initialize a ph7_value to the string type. */ PH7_PRIVATE sxi32 PH7_MemObjInitFromString(ph7_vm *pVm,ph7_value *pObj,const SyString *pVal) { /* Zero the structure */ SyZero(pObj,sizeof(ph7_value)); /* Initialize fields */ pObj->pVm = pVm; SyBlobInit(&pObj->sBlob,&pVm->sAllocator); if( pVal ){ /* Append contents */ SyBlobAppend(&pObj->sBlob,(const void *)pVal->zString,pVal->nByte); } /* Set the desired type */ pObj->iFlags = MEMOBJ_STRING; return SXRET_OK; } /* * Append some contents to the internal buffer of a given ph7_value. * If the given ph7_value is not of type string,this function * invalidate any prior representation and set the string type. * Then a simple append operation is performed. */ PH7_PRIVATE sxi32 PH7_MemObjStringAppend(ph7_value *pObj,const char *zData,sxu32 nLen) { sxi32 rc; if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ /* Invalidate any prior representation */ PH7_MemObjRelease(pObj); MemObjSetType(pObj,MEMOBJ_STRING); } /* Append contents */ rc = SyBlobAppend(&pObj->sBlob,zData,nLen); return rc; } #if 0 /* * Format and append some contents to the internal buffer of a given ph7_value. * If the given ph7_value is not of type string,this function invalidate * any prior representation and set the string type. * Then a simple format and append operation is performed. */ PH7_PRIVATE sxi32 PH7_MemObjStringFormat(ph7_value *pObj,const char *zFormat,va_list ap) { sxi32 rc; if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ /* Invalidate any prior representation */ PH7_MemObjRelease(pObj); MemObjSetType(pObj,MEMOBJ_STRING); } /* Format and append contents */ rc = SyBlobFormatAp(&pObj->sBlob,zFormat,ap); return rc; } #endif /* * Duplicate the contents of a ph7_value. */ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc,ph7_value *pDest) { ph7_class_instance *pObj = 0; ph7_hashmap *pMap = 0; sxi32 rc; if( pSrc->iFlags & MEMOBJ_HASHMAP ){ /* Increment reference count */ ((ph7_hashmap *)pSrc->x.pOther)->iRef++; }else if( pSrc->iFlags & MEMOBJ_OBJ ){ /* Increment reference count */ ((ph7_class_instance *)pSrc->x.pOther)->iRef++; } if( pDest->iFlags & MEMOBJ_HASHMAP ){ pMap = (ph7_hashmap *)pDest->x.pOther; }else if( pDest->iFlags & MEMOBJ_OBJ ){ pObj = (ph7_class_instance *)pDest->x.pOther; } SyMemcpy((const void *)&(*pSrc),&(*pDest),sizeof(ph7_value)-(sizeof(ph7_vm *)+sizeof(SyBlob)+sizeof(sxu32))); pDest->iFlags &= ~MEMOBJ_AUX; rc = SXRET_OK; if( SyBlobLength(&pSrc->sBlob) > 0 ){ SyBlobReset(&pDest->sBlob); rc = SyBlobDup(&pSrc->sBlob,&pDest->sBlob); }else{ if( SyBlobLength(&pDest->sBlob) > 0 ){ SyBlobRelease(&pDest->sBlob); } } if( pMap ){ PH7_HashmapUnref(pMap); }else if( pObj ){ PH7_ClassInstanceUnref(pObj); } return rc; } /* * Duplicate the contents of a ph7_value but do not copy internal * buffer contents,simply point to it. */ PH7_PRIVATE sxi32 PH7_MemObjLoad(ph7_value *pSrc,ph7_value *pDest) { SyMemcpy((const void *)&(*pSrc),&(*pDest), sizeof(ph7_value)-(sizeof(ph7_vm *)+sizeof(SyBlob)+sizeof(sxu32))); if( pSrc->iFlags & MEMOBJ_HASHMAP ){ /* Increment reference count */ ((ph7_hashmap *)pSrc->x.pOther)->iRef++; }else if( pSrc->iFlags & MEMOBJ_OBJ ){ /* Increment reference count */ ((ph7_class_instance *)pSrc->x.pOther)->iRef++; } if( SyBlobLength(&pDest->sBlob) > 0 ){ SyBlobRelease(&pDest->sBlob); } if( SyBlobLength(&pSrc->sBlob) > 0 ){ SyBlobReadOnly(&pDest->sBlob,SyBlobData(&pSrc->sBlob),SyBlobLength(&pSrc->sBlob)); } return SXRET_OK; } /* * Invalidate any prior representation of a given ph7_value. */ PH7_PRIVATE sxi32 PH7_MemObjRelease(ph7_value *pObj) { if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){ if( pObj->iFlags & MEMOBJ_HASHMAP ){ PH7_HashmapUnref((ph7_hashmap *)pObj->x.pOther); }else if( pObj->iFlags & MEMOBJ_OBJ ){ PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther); } /* Release the internal buffer */ SyBlobRelease(&pObj->sBlob); /* Invalidate any prior representation */ pObj->iFlags = MEMOBJ_NULL; } return SXRET_OK; } /* * Compare two ph7_values. * Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2 * or < 0 if pObj2 is greater than pObj1. * Type comparison table taken from the PHP language reference manual. * Comparisons of $x with PHP functions Expression * gettype() empty() is_null() isset() boolean : if($x) * $x = ""; string TRUE FALSE TRUE FALSE * $x = null NULL TRUE TRUE FALSE FALSE * var $x; NULL TRUE TRUE FALSE FALSE * $x is undefined NULL TRUE TRUE FALSE FALSE * $x = array(); array TRUE FALSE TRUE FALSE * $x = false; boolean TRUE FALSE TRUE FALSE * $x = true; boolean FALSE FALSE TRUE TRUE * $x = 1; integer FALSE FALSE TRUE TRUE * $x = 42; integer FALSE FALSE TRUE TRUE * $x = 0; integer TRUE FALSE TRUE FALSE * $x = -1; integer FALSE FALSE TRUE TRUE * $x = "1"; string FALSE FALSE TRUE TRUE * $x = "0"; string TRUE FALSE TRUE FALSE * $x = "-1"; string FALSE FALSE TRUE TRUE * $x = "php"; string FALSE FALSE TRUE TRUE * $x = "true"; string FALSE FALSE TRUE TRUE * $x = "false"; string FALSE FALSE TRUE TRUE * Loose comparisons with == * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "php" "" * TRUE TRUE FALSE TRUE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE * FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE TRUE * 1 TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE * 0 FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE * -1 TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE * "1" TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE * "0" FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE * "-1" TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE * NULL FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE * array() FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE * "php" TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE * "" FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE * Strict comparisons with === * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "php" "" * TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE * FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE * 1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE * 0 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE * -1 FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE * "1" FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE * "0" FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE * "-1" FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE * NULL FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE * array() FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE * "php" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE * "" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE */ PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1,ph7_value *pObj2,int bStrict,int iNest) { sxi32 iComb; sxi32 rc; if( bStrict ){ sxi32 iF1,iF2; /* Strict comparisons with === */ iF1 = pObj1->iFlags&~MEMOBJ_AUX; iF2 = pObj2->iFlags&~MEMOBJ_AUX; if( iF1 != iF2 ){ /* Not of the same type */ return 1; } } /* Combine flag together */ iComb = pObj1->iFlags|pObj2->iFlags; if( iComb & (MEMOBJ_NULL|MEMOBJ_RES|MEMOBJ_BOOL) ){ /* Convert to boolean: Keep in mind FALSE < TRUE */ if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pObj1); } if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){ PH7_MemObjToBool(pObj2); } return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0)); }else if ( iComb & MEMOBJ_HASHMAP ){ /* Hashmap aka 'array' comparison */ if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* Array is always greater */ return -1; } if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* Array is always greater */ return 1; } /* Perform the comparison */ rc = PH7_HashmapCmp((ph7_hashmap *)pObj1->x.pOther,(ph7_hashmap *)pObj2->x.pOther,bStrict); return rc; }else if(iComb & MEMOBJ_OBJ ){ /* Object comparison */ if( (pObj1->iFlags & MEMOBJ_OBJ) == 0 ){ /* Object is always greater */ return -1; } if( (pObj2->iFlags & MEMOBJ_OBJ) == 0 ){ /* Object is always greater */ return 1; } /* Perform the comparison */ rc = PH7_ClassInstanceCmp((ph7_class_instance *)pObj1->x.pOther,(ph7_class_instance *)pObj2->x.pOther,bStrict,iNest); return rc; }else if ( iComb & MEMOBJ_STRING ){ SyString s1,s2; if( !bStrict ){ /* * According to the PHP language reference manual: * * If you compare a number with a string or the comparison involves numerical * strings, then each string is converted to a number and the comparison * performed numerically. */ if( PH7_MemObjIsNumeric(pObj1) ){ /* Perform a numeric comparison */ goto Numeric; } if( PH7_MemObjIsNumeric(pObj2) ){ /* Perform a numeric comparison */ goto Numeric; } } /* Perform a strict string comparison.*/ if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pObj1); } if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(pObj2); } SyStringInitFromBuf(&s1,SyBlobData(&pObj1->sBlob),SyBlobLength(&pObj1->sBlob)); SyStringInitFromBuf(&s2,SyBlobData(&pObj2->sBlob),SyBlobLength(&pObj2->sBlob)); /* * Strings are compared using memcmp(). If one value is an exact prefix of the * other, then the shorter value is less than the longer value. */ rc = SyMemcmp((const void *)s1.zString,(const void *)s2.zString,SXMIN(s1.nByte,s2.nByte)); if( rc == 0 ){ if( s1.nByte != s2.nByte ){ rc = s1.nByte < s2.nByte ? -1 : 1; } } return rc; }else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){ Numeric: /* Perform a numeric comparison if one of the operand is numeric(integer or real) */ if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){ PH7_MemObjToNumeric(pObj1); } if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){ PH7_MemObjToNumeric(pObj2); } if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) { /* * Symisc eXtension to the PHP language: * Floating point comparison is introduced and works as expected. */ ph7_real r1,r2; /* Compare as reals */ if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pObj1); } r1 = pObj1->rVal; if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pObj2); } r2 = pObj2->rVal; if( r1 > r2 ){ return 1; }else if( r1 < r2 ){ return -1; } return 0; }else{ /* Integer comparison */ if( pObj1->x.iVal > pObj2->x.iVal ){ return 1; }else if( pObj1->x.iVal < pObj2->x.iVal ){ return -1; } return 0; } } /* NOT REACHED */ return 0; } /* * Perform an addition operation of two ph7_values. * The reason this function is implemented here rather than 'vm.c' * is that the '+' operator is overloaded. * That is,the '+' operator is used for arithmetic operation and also * used for operation on arrays [i.e: union]. When used with an array * The + operator returns the right-hand array appended to the left-hand array. * For keys that exist in both arrays, the elements from the left-hand array * will be used, and the matching elements from the right-hand array will * be ignored. * This function take care of handling all the scenarios. */ PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1,ph7_value *pObj2,int bAddStore) { if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){ /* Arithemtic operation */ PH7_MemObjToNumeric(pObj1); PH7_MemObjToNumeric(pObj2); if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){ /* Floating point arithmetic */ ph7_real a,b; if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pObj1); } if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){ PH7_MemObjToReal(pObj2); } a = pObj1->rVal; b = pObj2->rVal; pObj1->rVal = a+b; MemObjSetType(pObj1,MEMOBJ_REAL); /* Try to get an integer representation also */ MemObjTryIntger(&(*pObj1)); }else{ /* Integer arithmetic */ sxi64 a,b; a = pObj1->x.iVal; b = pObj2->x.iVal; pObj1->x.iVal = a+b; MemObjSetType(pObj1,MEMOBJ_INT); } }else{ if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){ ph7_hashmap *pMap; sxi32 rc; if( bAddStore ){ /* Do not duplicate the hashmap,use the left one since its an add&store operation. */ if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){ /* Force a hashmap cast */ rc = PH7_MemObjToHashmap(pObj1); if( rc != SXRET_OK ){ PH7_VmThrowError(pObj1->pVm,0,PH7_CTX_ERR,"PH7 is running out of memory while creating array"); return rc; } } /* Point to the structure that describe the hashmap */ pMap = (ph7_hashmap *)pObj1->x.pOther; }else{ /* Create a new hashmap */ pMap = PH7_NewHashmap(pObj1->pVm,0,0); if( pMap == 0){ PH7_VmThrowError(pObj1->pVm,0,PH7_CTX_ERR,"PH7 is running out of memory while creating array"); return SXERR_MEM; } } if( !bAddStore ){ if(pObj1->iFlags & MEMOBJ_HASHMAP ){ /* Perform a hashmap duplication */ PH7_HashmapDup((ph7_hashmap *)pObj1->x.pOther,pMap); }else{ if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){ /* Simple insertion */ PH7_HashmapInsert(pMap,0,pObj1); } } } /* Perform the union */ if(pObj2->iFlags & MEMOBJ_HASHMAP ){ PH7_HashmapUnion(pMap,(ph7_hashmap *)pObj2->x.pOther); }else{ if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){ /* Simple insertion */ PH7_HashmapInsert(pMap,0,pObj2); } } /* Reflect the change */ if( pObj1->iFlags & MEMOBJ_STRING ){ SyBlobRelease(&pObj1->sBlob); } pObj1->x.pOther = pMap; MemObjSetType(pObj1,MEMOBJ_HASHMAP); } } return SXRET_OK; } /* * Return a printable representation of the type of a given * ph7_value. */ PH7_PRIVATE const char * PH7_MemObjTypeDump(ph7_value *pVal) { const char *zType = ""; if( pVal->iFlags & MEMOBJ_NULL ){ zType = "null"; }else if( pVal->iFlags & MEMOBJ_INT ){ zType = "int"; }else if( pVal->iFlags & MEMOBJ_REAL ){ zType = "float"; }else if( pVal->iFlags & MEMOBJ_STRING ){ zType = "string"; }else if( pVal->iFlags & MEMOBJ_BOOL ){ zType = "bool"; }else if( pVal->iFlags & MEMOBJ_HASHMAP ){ zType = "array"; }else if( pVal->iFlags & MEMOBJ_OBJ ){ zType = "object"; }else if( pVal->iFlags & MEMOBJ_RES ){ zType = "resource"; } return zType; } /* * Dump a ph7_value [i.e: get a printable representation of it's type and contents.]. * Store the dump in the given blob. */ PH7_PRIVATE sxi32 PH7_MemObjDump( SyBlob *pOut, /* Store the dump here */ ph7_value *pObj, /* Dump this */ int ShowType, /* TRUE to output value type */ int nTab, /* # of Whitespace to insert */ int nDepth, /* Nesting level */ int isRef /* TRUE if referenced object */ ) { sxi32 rc = SXRET_OK; const char *zType; int i; for( i = 0 ; i < nTab ; i++ ){ SyBlobAppend(&(*pOut)," ",sizeof(char)); } if( ShowType ){ if( isRef ){ SyBlobAppend(&(*pOut),"&",sizeof(char)); } /* Get value type first */ zType = PH7_MemObjTypeDump(pObj); SyBlobAppend(&(*pOut),zType,SyStrlen(zType)); } if((pObj->iFlags & MEMOBJ_NULL) == 0 ){ if ( ShowType ){ SyBlobAppend(&(*pOut),"(",sizeof(char)); } if( pObj->iFlags & MEMOBJ_HASHMAP ){ /* Dump hashmap entries */ rc = PH7_HashmapDump(&(*pOut),(ph7_hashmap *)pObj->x.pOther,ShowType,nTab+1,nDepth+1); }else if(pObj->iFlags & MEMOBJ_OBJ ){ /* Dump class instance attributes */ rc = PH7_ClassInstanceDump(&(*pOut),(ph7_class_instance *)pObj->x.pOther,ShowType,nTab+1,nDepth+1); }else{ SyBlob *pContents = &pObj->sBlob; /* Get a printable representation of the contents */ if((pObj->iFlags & MEMOBJ_STRING) == 0 ){ MemObjStringValue(&(*pOut),&(*pObj),FALSE); }else{ /* Append length first */ if( ShowType ){ SyBlobFormat(&(*pOut),"%u '",SyBlobLength(&pObj->sBlob)); } if( SyBlobLength(pContents) > 0 ){ SyBlobAppend(&(*pOut),SyBlobData(pContents),SyBlobLength(pContents)); } if( ShowType ){ SyBlobAppend(&(*pOut),"'",sizeof(char)); } } } if( ShowType ){ if( (pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_OBJ)) == 0 ){ SyBlobAppend(&(*pOut),")",sizeof(char)); } } } #ifdef __WINNT__ SyBlobAppend(&(*pOut),"\r\n",sizeof("\r\n")-1); #else SyBlobAppend(&(*pOut),"\n",sizeof(char)); #endif return rc; } /* * ---------------------------------------------------------- * File: lib.c * MD5: 1d998050126fa9f31b5a52c757d498cb * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: lib.c v5.1 Win7 2012-08-08 04:19 stable $ */ /* * Symisc Run-Time API: A modern thread safe replacement of the standard libc * Copyright (C) Symisc Systems 2007-2012, http://www.symisc.net/ * * The Symisc Run-Time API is an independent project developed by symisc systems * internally as a secure replacement of the standard libc. * The library is re-entrant,thread-safe and platform independent. */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif #if defined(__WINNT__) #include #else #include #endif #if defined(PH7_ENABLE_THREADS) /* SyRunTimeApi: sxmutex.c */ #if defined(__WINNT__) struct SyMutex { CRITICAL_SECTION sMutex; sxu32 nType; /* Mutex type,one of SXMUTEX_TYPE_* */ }; /* Preallocated static mutex */ static SyMutex aStaticMutexes[] = { {{0},SXMUTEX_TYPE_STATIC_1}, {{0},SXMUTEX_TYPE_STATIC_2}, {{0},SXMUTEX_TYPE_STATIC_3}, {{0},SXMUTEX_TYPE_STATIC_4}, {{0},SXMUTEX_TYPE_STATIC_5}, {{0},SXMUTEX_TYPE_STATIC_6} }; static BOOL winMutexInit = FALSE; static LONG winMutexLock = 0; static sxi32 WinMutexGlobaInit(void) { LONG rc; rc = InterlockedCompareExchange(&winMutexLock,1,0); if ( rc == 0 ){ sxu32 n; for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){ InitializeCriticalSection(&aStaticMutexes[n].sMutex); } winMutexInit = TRUE; }else{ /* Someone else is doing this for us */ while( winMutexInit == FALSE ){ Sleep(1); } } return SXRET_OK; } static void WinMutexGlobalRelease(void) { LONG rc; rc = InterlockedCompareExchange(&winMutexLock,0,1); if( rc == 1 ){ /* The first to decrement to zero does the actual global release */ if( winMutexInit == TRUE ){ sxu32 n; for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){ DeleteCriticalSection(&aStaticMutexes[n].sMutex); } winMutexInit = FALSE; } } } static SyMutex * WinMutexNew(int nType) { SyMutex *pMutex = 0; if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){ /* Allocate a new mutex */ pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(),0,sizeof(SyMutex)); if( pMutex == 0 ){ return 0; } InitializeCriticalSection(&pMutex->sMutex); }else{ /* Use a pre-allocated static mutex */ if( nType > SXMUTEX_TYPE_STATIC_6 ){ nType = SXMUTEX_TYPE_STATIC_6; } pMutex = &aStaticMutexes[nType - 3]; } pMutex->nType = nType; return pMutex; } static void WinMutexRelease(SyMutex *pMutex) { if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){ DeleteCriticalSection(&pMutex->sMutex); HeapFree(GetProcessHeap(),0,pMutex); } } static void WinMutexEnter(SyMutex *pMutex) { EnterCriticalSection(&pMutex->sMutex); } static sxi32 WinMutexTryEnter(SyMutex *pMutex) { #ifdef _WIN32_WINNT BOOL rc; /* Only WindowsNT platforms */ rc = TryEnterCriticalSection(&pMutex->sMutex); if( rc ){ return SXRET_OK; }else{ return SXERR_BUSY; } #else return SXERR_NOTIMPLEMENTED; #endif } static void WinMutexLeave(SyMutex *pMutex) { LeaveCriticalSection(&pMutex->sMutex); } /* Export Windows mutex interfaces */ static const SyMutexMethods sWinMutexMethods = { WinMutexGlobaInit, /* xGlobalInit() */ WinMutexGlobalRelease, /* xGlobalRelease() */ WinMutexNew, /* xNew() */ WinMutexRelease, /* xRelease() */ WinMutexEnter, /* xEnter() */ WinMutexTryEnter, /* xTryEnter() */ WinMutexLeave /* xLeave() */ }; PH7_PRIVATE const SyMutexMethods * SyMutexExportMethods(void) { return &sWinMutexMethods; } #elif defined(__UNIXES__) #include struct SyMutex { pthread_mutex_t sMutex; sxu32 nType; }; static SyMutex * UnixMutexNew(int nType) { static SyMutex aStaticMutexes[] = { {PTHREAD_MUTEX_INITIALIZER,SXMUTEX_TYPE_STATIC_1}, {PTHREAD_MUTEX_INITIALIZER,SXMUTEX_TYPE_STATIC_2}, {PTHREAD_MUTEX_INITIALIZER,SXMUTEX_TYPE_STATIC_3}, {PTHREAD_MUTEX_INITIALIZER,SXMUTEX_TYPE_STATIC_4}, {PTHREAD_MUTEX_INITIALIZER,SXMUTEX_TYPE_STATIC_5}, {PTHREAD_MUTEX_INITIALIZER,SXMUTEX_TYPE_STATIC_6} }; SyMutex *pMutex; if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){ pthread_mutexattr_t sRecursiveAttr; /* Allocate a new mutex */ pMutex = (SyMutex *)malloc(sizeof(SyMutex)); if( pMutex == 0 ){ return 0; } if( nType == SXMUTEX_TYPE_RECURSIVE ){ pthread_mutexattr_init(&sRecursiveAttr); pthread_mutexattr_settype(&sRecursiveAttr,PTHREAD_MUTEX_RECURSIVE); } pthread_mutex_init(&pMutex->sMutex,nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0 ); if( nType == SXMUTEX_TYPE_RECURSIVE ){ pthread_mutexattr_destroy(&sRecursiveAttr); } }else{ /* Use a pre-allocated static mutex */ if( nType > SXMUTEX_TYPE_STATIC_6 ){ nType = SXMUTEX_TYPE_STATIC_6; } pMutex = &aStaticMutexes[nType - 3]; } pMutex->nType = nType; return pMutex; } static void UnixMutexRelease(SyMutex *pMutex) { if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){ pthread_mutex_destroy(&pMutex->sMutex); free(pMutex); } } static void UnixMutexEnter(SyMutex *pMutex) { pthread_mutex_lock(&pMutex->sMutex); } static void UnixMutexLeave(SyMutex *pMutex) { pthread_mutex_unlock(&pMutex->sMutex); } /* Export pthread mutex interfaces */ static const SyMutexMethods sPthreadMutexMethods = { 0, /* xGlobalInit() */ 0, /* xGlobalRelease() */ UnixMutexNew, /* xNew() */ UnixMutexRelease, /* xRelease() */ UnixMutexEnter, /* xEnter() */ 0, /* xTryEnter() */ UnixMutexLeave /* xLeave() */ }; PH7_PRIVATE const SyMutexMethods * SyMutexExportMethods(void) { return &sPthreadMutexMethods; } #else /* Host application must register their own mutex subsystem if the target * platform is not an UNIX-like or windows systems. */ struct SyMutex { sxu32 nType; }; static SyMutex * DummyMutexNew(int nType) { static SyMutex sMutex; SXUNUSED(nType); return &sMutex; } static void DummyMutexRelease(SyMutex *pMutex) { SXUNUSED(pMutex); } static void DummyMutexEnter(SyMutex *pMutex) { SXUNUSED(pMutex); } static void DummyMutexLeave(SyMutex *pMutex) { SXUNUSED(pMutex); } /* Export the dummy mutex interfaces */ static const SyMutexMethods sDummyMutexMethods = { 0, /* xGlobalInit() */ 0, /* xGlobalRelease() */ DummyMutexNew, /* xNew() */ DummyMutexRelease, /* xRelease() */ DummyMutexEnter, /* xEnter() */ 0, /* xTryEnter() */ DummyMutexLeave /* xLeave() */ }; PH7_PRIVATE const SyMutexMethods * SyMutexExportMethods(void) { return &sDummyMutexMethods; } #endif /* __WINNT__ */ #endif /* PH7_ENABLE_THREADS */ static void * SyOSHeapAlloc(sxu32 nByte) { void *pNew; #if defined(__WINNT__) pNew = HeapAlloc(GetProcessHeap(),0,nByte); #else pNew = malloc((size_t)nByte); #endif return pNew; } static void * SyOSHeapRealloc(void *pOld,sxu32 nByte) { void *pNew; #if defined(__WINNT__) pNew = HeapReAlloc(GetProcessHeap(),0,pOld,nByte); #else pNew = realloc(pOld,(size_t)nByte); #endif return pNew; } static void SyOSHeapFree(void *pPtr) { #if defined(__WINNT__) HeapFree(GetProcessHeap(),0,pPtr); #else free(pPtr); #endif } /* SyRunTimeApi:sxstr.c */ PH7_PRIVATE sxu32 SyStrlen(const char *zSrc) { register const char *zIn = zSrc; #if defined(UNTRUST) if( zIn == 0 ){ return 0; } #endif for(;;){ if( !zIn[0] ){ break; } zIn++; if( !zIn[0] ){ break; } zIn++; if( !zIn[0] ){ break; } zIn++; if( !zIn[0] ){ break; } zIn++; } return (sxu32)(zIn - zSrc); } PH7_PRIVATE sxi32 SyByteFind(const char *zStr,sxu32 nLen,sxi32 c,sxu32 *pPos) { const char *zIn = zStr; const char *zEnd; zEnd = &zIn[nLen]; for(;;){ if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++; if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++; if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++; if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++; } return SXERR_NOTFOUND; } #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyByteFind2(const char *zStr,sxu32 nLen,sxi32 c,sxu32 *pPos) { const char *zIn = zStr; const char *zEnd; zEnd = &zIn[nLen - 1]; for( ;; ){ if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--; if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--; if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--; if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--; } return SXERR_NOTFOUND; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ PH7_PRIVATE sxi32 SyByteListFind(const char *zSrc,sxu32 nLen,const char *zList,sxu32 *pFirstPos) { const char *zIn = zSrc; const char *zPtr; const char *zEnd; sxi32 c; zEnd = &zSrc[nLen]; for(;;){ if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++; if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++; if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++; if( zIn >= zEnd ){ break; } for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++; } return SXERR_NOTFOUND; } #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyStrncmp(const char *zLeft,const char *zRight,sxu32 nLen) { const unsigned char *zP = (const unsigned char *)zLeft; const unsigned char *zQ = (const unsigned char *)zRight; if( SX_EMPTY_STR(zP) || SX_EMPTY_STR(zQ) ){ return SX_EMPTY_STR(zP) ? (SX_EMPTY_STR(zQ) ? 0 : -1) :1; } if( nLen <= 0 ){ return 0; } for(;;){ if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--; if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--; if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--; if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--; } return (sxi32)(zP[0] - zQ[0]); } #endif PH7_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight,sxu32 SLen) { register unsigned char *p = (unsigned char *)zLeft; register unsigned char *q = (unsigned char *)zRight; if( SX_EMPTY_STR(p) || SX_EMPTY_STR(q) ){ return SX_EMPTY_STR(p)? SX_EMPTY_STR(q) ? 0 : -1 :1; } for(;;){ if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen; } return (sxi32)(SyCharToLower(p[0]) - SyCharToLower(q[0])); } PH7_PRIVATE sxi32 SyStrnmicmp(const void *pLeft, const void *pRight,sxu32 SLen) { return SyStrnicmp((const char *)pLeft,(const char *)pRight,SLen); } static sxu32 Systrcpy(char *zDest,sxu32 nDestLen,const char *zSrc,sxu32 nLen) { unsigned char *zBuf = (unsigned char *)zDest; unsigned char *zIn = (unsigned char *)zSrc; unsigned char *zEnd; #if defined(UNTRUST) if( zSrc == (const char *)zDest ){ return 0; } #endif if( nLen <= 0 ){ nLen = SyStrlen(zSrc); } zEnd = &zBuf[nDestLen - 1]; /* reserve a room for the null terminator */ for(;;){ if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--; if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--; if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--; if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--; } zBuf[0] = 0; return (sxu32)(zBuf-(unsigned char *)zDest); } /* SyRunTimeApi:sxmem.c */ PH7_PRIVATE void SyZero(void *pSrc,sxu32 nSize) { register unsigned char *zSrc = (unsigned char *)pSrc; unsigned char *zEnd; #if defined(UNTRUST) if( zSrc == 0 || nSize <= 0 ){ return ; } #endif zEnd = &zSrc[nSize]; for(;;){ if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++; if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++; if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++; if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++; } } PH7_PRIVATE sxi32 SyMemcmp(const void *pB1,const void *pB2,sxu32 nSize) { sxi32 rc; if( nSize <= 0 ){ return 0; } if( pB1 == 0 || pB2 == 0 ){ return pB1 != 0 ? 1 : (pB2 == 0 ? 0 : -1); } SX_MACRO_FAST_CMP(pB1,pB2,nSize,rc); return rc; } PH7_PRIVATE sxu32 SyMemcpy(const void *pSrc,void *pDest,sxu32 nLen) { #if defined(UNTRUST) if( pSrc == 0 || pDest == 0 ){ return 0; } #endif if( pSrc == (const void *)pDest ){ return nLen; } SX_MACRO_FAST_MEMCPY(pSrc,pDest,nLen); return nLen; } static void * MemOSAlloc(sxu32 nBytes) { sxu32 *pChunk; pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + sizeof(sxu32)); if( pChunk == 0 ){ return 0; } pChunk[0] = nBytes; return (void *)&pChunk[1]; } static void * MemOSRealloc(void *pOld,sxu32 nBytes) { sxu32 *pOldChunk; sxu32 *pChunk; pOldChunk = (sxu32 *)(((char *)pOld)-sizeof(sxu32)); if( pOldChunk[0] >= nBytes ){ return pOld; } pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk,nBytes + sizeof(sxu32)); if( pChunk == 0 ){ return 0; } pChunk[0] = nBytes; return (void *)&pChunk[1]; } static void MemOSFree(void *pBlock) { void *pChunk; pChunk = (void *)(((char *)pBlock)-sizeof(sxu32)); SyOSHeapFree(pChunk); } static sxu32 MemOSChunkSize(void *pBlock) { sxu32 *pChunk; pChunk = (sxu32 *)(((char *)pBlock)-sizeof(sxu32)); return pChunk[0]; } /* Export OS allocation methods */ static const SyMemMethods sOSAllocMethods = { MemOSAlloc, MemOSRealloc, MemOSFree, MemOSChunkSize, 0, 0, 0 }; static void * MemBackendAlloc(SyMemBackend *pBackend,sxu32 nByte) { SyMemBlock *pBlock; sxi32 nRetry = 0; /* Append an extra block so we can tracks allocated chunks and avoid memory * leaks. */ nByte += sizeof(SyMemBlock); for(;;){ pBlock = (SyMemBlock *)pBackend->pMethods->xAlloc(nByte); if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY || SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){ break; } nRetry++; } if( pBlock == 0 ){ return 0; } pBlock->pNext = pBlock->pPrev = 0; /* Link to the list of already tracked blocks */ MACRO_LD_PUSH(pBackend->pBlocks,pBlock); #if defined(UNTRUST) pBlock->nGuard = SXMEM_BACKEND_MAGIC; #endif pBackend->nBlock++; return (void *)&pBlock[1]; } PH7_PRIVATE void * SyMemBackendAlloc(SyMemBackend *pBackend,sxu32 nByte) { void *pChunk; #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) ){ return 0; } #endif if( pBackend->pMutexMethods ){ SyMutexEnter(pBackend->pMutexMethods,pBackend->pMutex); } pChunk = MemBackendAlloc(&(*pBackend),nByte); if( pBackend->pMutexMethods ){ SyMutexLeave(pBackend->pMutexMethods,pBackend->pMutex); } return pChunk; } static void * MemBackendRealloc(SyMemBackend *pBackend,void * pOld,sxu32 nByte) { SyMemBlock *pBlock,*pNew,*pPrev,*pNext; sxu32 nRetry = 0; if( pOld == 0 ){ return MemBackendAlloc(&(*pBackend),nByte); } pBlock = (SyMemBlock *)(((char *)pOld) - sizeof(SyMemBlock)); #if defined(UNTRUST) if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){ return 0; } #endif nByte += sizeof(SyMemBlock); pPrev = pBlock->pPrev; pNext = pBlock->pNext; for(;;){ pNew = (SyMemBlock *)pBackend->pMethods->xRealloc(pBlock,nByte); if( pNew != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY || SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){ break; } nRetry++; } if( pNew == 0 ){ return 0; } if( pNew != pBlock ){ if( pPrev == 0 ){ pBackend->pBlocks = pNew; }else{ pPrev->pNext = pNew; } if( pNext ){ pNext->pPrev = pNew; } #if defined(UNTRUST) pNew->nGuard = SXMEM_BACKEND_MAGIC; #endif } return (void *)&pNew[1]; } PH7_PRIVATE void * SyMemBackendRealloc(SyMemBackend *pBackend,void * pOld,sxu32 nByte) { void *pChunk; #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) ){ return 0; } #endif if( pBackend->pMutexMethods ){ SyMutexEnter(pBackend->pMutexMethods,pBackend->pMutex); } pChunk = MemBackendRealloc(&(*pBackend),pOld,nByte); if( pBackend->pMutexMethods ){ SyMutexLeave(pBackend->pMutexMethods,pBackend->pMutex); } return pChunk; } static sxi32 MemBackendFree(SyMemBackend *pBackend,void * pChunk) { SyMemBlock *pBlock; pBlock = (SyMemBlock *)(((char *)pChunk) - sizeof(SyMemBlock)); #if defined(UNTRUST) if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){ return SXERR_CORRUPT; } #endif /* Unlink from the list of active blocks */ if( pBackend->nBlock > 0 ){ /* Release the block */ #if defined(UNTRUST) /* Mark as stale block */ pBlock->nGuard = 0x635B; #endif MACRO_LD_REMOVE(pBackend->pBlocks,pBlock); pBackend->nBlock--; pBackend->pMethods->xFree(pBlock); } return SXRET_OK; } PH7_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend,void * pChunk) { sxi32 rc; #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) ){ return SXERR_CORRUPT; } #endif if( pChunk == 0 ){ return SXRET_OK; } if( pBackend->pMutexMethods ){ SyMutexEnter(pBackend->pMutexMethods,pBackend->pMutex); } rc = MemBackendFree(&(*pBackend),pChunk); if( pBackend->pMutexMethods ){ SyMutexLeave(pBackend->pMutexMethods,pBackend->pMutex); } return rc; } #if defined(PH7_ENABLE_THREADS) PH7_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend,const SyMutexMethods *pMethods) { SyMutex *pMutex; #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) || pMethods == 0 || pMethods->xNew == 0){ return SXERR_CORRUPT; } #endif pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST); if( pMutex == 0 ){ return SXERR_OS; } /* Attach the mutex to the memory backend */ pBackend->pMutex = pMutex; pBackend->pMutexMethods = pMethods; return SXRET_OK; } PH7_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend) { #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) ){ return SXERR_CORRUPT; } #endif if( pBackend->pMutex == 0 ){ /* There is no mutex subsystem at all */ return SXRET_OK; } SyMutexRelease(pBackend->pMutexMethods,pBackend->pMutex); pBackend->pMutexMethods = 0; pBackend->pMutex = 0; return SXRET_OK; } #endif /* * Memory pool allocator */ #define SXMEM_POOL_MAGIC 0xDEAD #define SXMEM_POOL_MAXALLOC (1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR)) #define SXMEM_POOL_MINALLOC (1<<(SXMEM_POOL_INCR)) static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend,sxu32 nBucket) { char *zBucket,*zBucketEnd; SyMemHeader *pHeader; sxu32 nBucketSize; /* Allocate one big block first */ zBucket = (char *)MemBackendAlloc(&(*pBackend),SXMEM_POOL_MAXALLOC); if( zBucket == 0 ){ return SXERR_MEM; } zBucketEnd = &zBucket[SXMEM_POOL_MAXALLOC]; /* Divide the big block into mini bucket pool */ nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR); pBackend->apPool[nBucket] = pHeader = (SyMemHeader *)zBucket; for(;;){ if( &zBucket[nBucketSize] >= zBucketEnd ){ break; } pHeader->pNext = (SyMemHeader *)&zBucket[nBucketSize]; /* Advance the cursor to the next available chunk */ pHeader = pHeader->pNext; zBucket += nBucketSize; } pHeader->pNext = 0; return SXRET_OK; } static void * MemBackendPoolAlloc(SyMemBackend *pBackend,sxu32 nByte) { SyMemHeader *pBucket,*pNext; sxu32 nBucketSize; sxu32 nBucket; if( nByte + sizeof(SyMemHeader) >= SXMEM_POOL_MAXALLOC ){ /* Allocate a big chunk directly */ pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend),nByte+sizeof(SyMemHeader)); if( pBucket == 0 ){ return 0; } /* Record as big block */ pBucket->nBucket = (sxu32)(SXMEM_POOL_MAGIC << 16) | SXU16_HIGH; return (void *)(pBucket+1); } /* Locate the appropriate bucket */ nBucket = 0; nBucketSize = SXMEM_POOL_MINALLOC; while( nByte + sizeof(SyMemHeader) > nBucketSize ){ nBucketSize <<= 1; nBucket++; } pBucket = pBackend->apPool[nBucket]; if( pBucket == 0 ){ sxi32 rc; rc = MemPoolBucketAlloc(&(*pBackend),nBucket); if( rc != SXRET_OK ){ return 0; } pBucket = pBackend->apPool[nBucket]; } /* Remove from the free list */ pNext = pBucket->pNext; pBackend->apPool[nBucket] = pNext; /* Record bucket&magic number */ pBucket->nBucket = (SXMEM_POOL_MAGIC << 16) | nBucket; return (void *)&pBucket[1]; } PH7_PRIVATE void * SyMemBackendPoolAlloc(SyMemBackend *pBackend,sxu32 nByte) { void *pChunk; #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) ){ return 0; } #endif if( pBackend->pMutexMethods ){ SyMutexEnter(pBackend->pMutexMethods,pBackend->pMutex); } pChunk = MemBackendPoolAlloc(&(*pBackend),nByte); if( pBackend->pMutexMethods ){ SyMutexLeave(pBackend->pMutexMethods,pBackend->pMutex); } return pChunk; } static sxi32 MemBackendPoolFree(SyMemBackend *pBackend,void * pChunk) { SyMemHeader *pHeader; sxu32 nBucket; /* Get the corresponding bucket */ pHeader = (SyMemHeader *)(((char *)pChunk) - sizeof(SyMemHeader)); /* Sanity check to avoid misuse */ if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){ return SXERR_CORRUPT; } nBucket = pHeader->nBucket & 0xFFFF; if( nBucket == SXU16_HIGH ){ /* Free the big block */ MemBackendFree(&(*pBackend),pHeader); }else{ /* Return to the free list */ pHeader->pNext = pBackend->apPool[nBucket & 0x0f]; pBackend->apPool[nBucket & 0x0f] = pHeader; } return SXRET_OK; } PH7_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend,void * pChunk) { sxi32 rc; #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) || pChunk == 0 ){ return SXERR_CORRUPT; } #endif if( pBackend->pMutexMethods ){ SyMutexEnter(pBackend->pMutexMethods,pBackend->pMutex); } rc = MemBackendPoolFree(&(*pBackend),pChunk); if( pBackend->pMutexMethods ){ SyMutexLeave(pBackend->pMutexMethods,pBackend->pMutex); } return rc; } #if 0 static void * MemBackendPoolRealloc(SyMemBackend *pBackend,void * pOld,sxu32 nByte) { sxu32 nBucket,nBucketSize; SyMemHeader *pHeader; void * pNew; if( pOld == 0 ){ /* Allocate a new pool */ pNew = MemBackendPoolAlloc(&(*pBackend),nByte); return pNew; } /* Get the corresponding bucket */ pHeader = (SyMemHeader *)(((char *)pOld) - sizeof(SyMemHeader)); /* Sanity check to avoid misuse */ if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){ return 0; } nBucket = pHeader->nBucket & 0xFFFF; if( nBucket == SXU16_HIGH ){ /* Big block */ return MemBackendRealloc(&(*pBackend),pHeader,nByte); } nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR); if( nBucketSize >= nByte + sizeof(SyMemHeader) ){ /* The old bucket can honor the requested size */ return pOld; } /* Allocate a new pool */ pNew = MemBackendPoolAlloc(&(*pBackend),nByte); if( pNew == 0 ){ return 0; } /* Copy the old data into the new block */ SyMemcpy(pOld,pNew,nBucketSize); /* Free the stale block */ MemBackendPoolFree(&(*pBackend),pOld); return pNew; } PH7_PRIVATE void * SyMemBackendPoolRealloc(SyMemBackend *pBackend,void * pOld,sxu32 nByte) { void *pChunk; #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) ){ return 0; } #endif if( pBackend->pMutexMethods ){ SyMutexEnter(pBackend->pMutexMethods,pBackend->pMutex); } pChunk = MemBackendPoolRealloc(&(*pBackend),pOld,nByte); if( pBackend->pMutexMethods ){ SyMutexLeave(pBackend->pMutexMethods,pBackend->pMutex); } return pChunk; } #endif PH7_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend,ProcMemError xMemErr,void * pUserData) { #if defined(UNTRUST) if( pBackend == 0 ){ return SXERR_EMPTY; } #endif /* Zero the allocator first */ SyZero(&(*pBackend),sizeof(SyMemBackend)); pBackend->xMemError = xMemErr; pBackend->pUserData = pUserData; /* Switch to the OS memory allocator */ pBackend->pMethods = &sOSAllocMethods; if( pBackend->pMethods->xInit ){ /* Initialize the backend */ if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){ return SXERR_ABORT; } } #if defined(UNTRUST) pBackend->nMagic = SXMEM_BACKEND_MAGIC; #endif return SXRET_OK; } PH7_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend,const SyMemMethods *pMethods,ProcMemError xMemErr,void * pUserData) { #if defined(UNTRUST) if( pBackend == 0 || pMethods == 0){ return SXERR_EMPTY; } #endif if( pMethods->xAlloc == 0 || pMethods->xRealloc == 0 || pMethods->xFree == 0 || pMethods->xChunkSize == 0 ){ /* mandatory methods are missing */ return SXERR_INVALID; } /* Zero the allocator first */ SyZero(&(*pBackend),sizeof(SyMemBackend)); pBackend->xMemError = xMemErr; pBackend->pUserData = pUserData; /* Switch to the host application memory allocator */ pBackend->pMethods = pMethods; if( pBackend->pMethods->xInit ){ /* Initialize the backend */ if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){ return SXERR_ABORT; } } #if defined(UNTRUST) pBackend->nMagic = SXMEM_BACKEND_MAGIC; #endif return SXRET_OK; } PH7_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,SyMemBackend *pParent) { sxu8 bInheritMutex; #if defined(UNTRUST) if( pBackend == 0 || SXMEM_BACKEND_CORRUPT(pParent) ){ return SXERR_CORRUPT; } #endif /* Zero the allocator first */ SyZero(&(*pBackend),sizeof(SyMemBackend)); pBackend->pMethods = pParent->pMethods; pBackend->xMemError = pParent->xMemError; pBackend->pUserData = pParent->pUserData; bInheritMutex = pParent->pMutexMethods ? TRUE : FALSE; if( bInheritMutex ){ pBackend->pMutexMethods = pParent->pMutexMethods; /* Create a private mutex */ pBackend->pMutex = pBackend->pMutexMethods->xNew(SXMUTEX_TYPE_FAST); if( pBackend->pMutex == 0){ return SXERR_OS; } } #if defined(UNTRUST) pBackend->nMagic = SXMEM_BACKEND_MAGIC; #endif return SXRET_OK; } static sxi32 MemBackendRelease(SyMemBackend *pBackend) { SyMemBlock *pBlock,*pNext; pBlock = pBackend->pBlocks; for(;;){ if( pBackend->nBlock == 0 ){ break; } pNext = pBlock->pNext; pBackend->pMethods->xFree(pBlock); pBlock = pNext; pBackend->nBlock--; /* LOOP ONE */ if( pBackend->nBlock == 0 ){ break; } pNext = pBlock->pNext; pBackend->pMethods->xFree(pBlock); pBlock = pNext; pBackend->nBlock--; /* LOOP TWO */ if( pBackend->nBlock == 0 ){ break; } pNext = pBlock->pNext; pBackend->pMethods->xFree(pBlock); pBlock = pNext; pBackend->nBlock--; /* LOOP THREE */ if( pBackend->nBlock == 0 ){ break; } pNext = pBlock->pNext; pBackend->pMethods->xFree(pBlock); pBlock = pNext; pBackend->nBlock--; /* LOOP FOUR */ } if( pBackend->pMethods->xRelease ){ pBackend->pMethods->xRelease(pBackend->pMethods->pUserData); } pBackend->pMethods = 0; pBackend->pBlocks = 0; #if defined(UNTRUST) pBackend->nMagic = 0x2626; #endif return SXRET_OK; } PH7_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend) { sxi32 rc; #if defined(UNTRUST) if( SXMEM_BACKEND_CORRUPT(pBackend) ){ return SXERR_INVALID; } #endif if( pBackend->pMutexMethods ){ SyMutexEnter(pBackend->pMutexMethods,pBackend->pMutex); } rc = MemBackendRelease(&(*pBackend)); if( pBackend->pMutexMethods ){ SyMutexLeave(pBackend->pMutexMethods,pBackend->pMutex); SyMutexRelease(pBackend->pMutexMethods,pBackend->pMutex); } return SXRET_OK; } PH7_PRIVATE void * SyMemBackendDup(SyMemBackend *pBackend,const void *pSrc,sxu32 nSize) { void *pNew; #if defined(UNTRUST) if( pSrc == 0 || nSize <= 0 ){ return 0; } #endif pNew = SyMemBackendAlloc(&(*pBackend),nSize); if( pNew ){ SyMemcpy(pSrc,pNew,nSize); } return pNew; } PH7_PRIVATE char * SyMemBackendStrDup(SyMemBackend *pBackend,const char *zSrc,sxu32 nSize) { char *zDest; zDest = (char *)SyMemBackendAlloc(&(*pBackend),nSize + 1); if( zDest ){ Systrcpy(zDest,nSize+1,zSrc,nSize); } return zDest; } PH7_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob,void *pBuffer,sxu32 nSize) { #if defined(UNTRUST) if( pBlob == 0 || pBuffer == 0 || nSize < 1 ){ return SXERR_EMPTY; } #endif pBlob->pBlob = pBuffer; pBlob->mByte = nSize; pBlob->nByte = 0; pBlob->pAllocator = 0; pBlob->nFlags = SXBLOB_LOCKED|SXBLOB_STATIC; return SXRET_OK; } PH7_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob,SyMemBackend *pAllocator) { #if defined(UNTRUST) if( pBlob == 0 ){ return SXERR_EMPTY; } #endif pBlob->pBlob = 0; pBlob->mByte = pBlob->nByte = 0; pBlob->pAllocator = &(*pAllocator); pBlob->nFlags = 0; return SXRET_OK; } PH7_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob,const void *pData,sxu32 nByte) { #if defined(UNTRUST) if( pBlob == 0 ){ return SXERR_EMPTY; } #endif pBlob->pBlob = (void *)pData; pBlob->nByte = nByte; pBlob->mByte = 0; pBlob->nFlags |= SXBLOB_RDONLY; return SXRET_OK; } #ifndef SXBLOB_MIN_GROWTH #define SXBLOB_MIN_GROWTH 16 #endif static sxi32 BlobPrepareGrow(SyBlob *pBlob,sxu32 *pByte) { sxu32 nByte; void *pNew; nByte = *pByte; if( pBlob->nFlags & (SXBLOB_LOCKED|SXBLOB_STATIC) ){ if ( SyBlobFreeSpace(pBlob) < nByte ){ *pByte = SyBlobFreeSpace(pBlob); if( (*pByte) == 0 ){ return SXERR_SHORT; } } return SXRET_OK; } if( pBlob->nFlags & SXBLOB_RDONLY ){ /* Make a copy of the read-only item */ if( pBlob->nByte > 0 ){ pNew = SyMemBackendDup(pBlob->pAllocator,pBlob->pBlob,pBlob->nByte); if( pNew == 0 ){ return SXERR_MEM; } pBlob->pBlob = pNew; pBlob->mByte = pBlob->nByte; }else{ pBlob->pBlob = 0; pBlob->mByte = 0; } /* Remove the read-only flag */ pBlob->nFlags &= ~SXBLOB_RDONLY; } if( SyBlobFreeSpace(pBlob) >= nByte ){ return SXRET_OK; } if( pBlob->mByte > 0 ){ nByte = nByte + pBlob->mByte * 2 + SXBLOB_MIN_GROWTH; }else if ( nByte < SXBLOB_MIN_GROWTH ){ nByte = SXBLOB_MIN_GROWTH; } pNew = SyMemBackendRealloc(pBlob->pAllocator,pBlob->pBlob,nByte); if( pNew == 0 ){ return SXERR_MEM; } pBlob->pBlob = pNew; pBlob->mByte = nByte; return SXRET_OK; } PH7_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob,const void *pData,sxu32 nSize) { sxu8 *zBlob; sxi32 rc; if( nSize < 1 ){ return SXRET_OK; } rc = BlobPrepareGrow(&(*pBlob),&nSize); if( SXRET_OK != rc ){ return rc; } if( pData ){ zBlob = (sxu8 *)pBlob->pBlob ; zBlob = &zBlob[pBlob->nByte]; pBlob->nByte += nSize; SX_MACRO_FAST_MEMCPY(pData,zBlob,nSize); } return SXRET_OK; } PH7_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob) { sxi32 rc; sxu32 n; n = pBlob->nByte; rc = SyBlobAppend(&(*pBlob),(const void *)"\0",sizeof(char)); if (rc == SXRET_OK ){ pBlob->nByte = n; } return rc; } PH7_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc,SyBlob *pDest) { sxi32 rc = SXRET_OK; #ifdef UNTRUST if( pSrc == 0 || pDest == 0 ){ return SXERR_EMPTY; } #endif if( pSrc->nByte > 0 ){ rc = SyBlobAppend(&(*pDest),pSrc->pBlob,pSrc->nByte); } return rc; } PH7_PRIVATE sxi32 SyBlobCmp(SyBlob *pLeft,SyBlob *pRight) { sxi32 rc; #ifdef UNTRUST if( pLeft == 0 || pRight == 0 ){ return pLeft ? 1 : -1; } #endif if( pLeft->nByte != pRight->nByte ){ /* Length differ */ return pLeft->nByte - pRight->nByte; } if( pLeft->nByte == 0 ){ return 0; } /* Perform a standard memcmp() operation */ rc = SyMemcmp(pLeft->pBlob,pRight->pBlob,pLeft->nByte); return rc; } PH7_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob) { pBlob->nByte = 0; if( pBlob->nFlags & SXBLOB_RDONLY ){ pBlob->pBlob = 0; pBlob->mByte = 0; pBlob->nFlags &= ~SXBLOB_RDONLY; } return SXRET_OK; } PH7_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob) { if( (pBlob->nFlags & (SXBLOB_STATIC|SXBLOB_RDONLY)) == 0 && pBlob->mByte > 0 ){ SyMemBackendFree(pBlob->pAllocator,pBlob->pBlob); } pBlob->pBlob = 0; pBlob->nByte = pBlob->mByte = 0; pBlob->nFlags = 0; return SXRET_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyBlobSearch(const void *pBlob,sxu32 nLen,const void *pPattern,sxu32 pLen,sxu32 *pOfft) { const char *zIn = (const char *)pBlob; const char *zEnd; sxi32 rc; if( pLen > nLen ){ return SXERR_NOTFOUND; } zEnd = &zIn[nLen-pLen]; for(;;){ if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn,pPattern,pLen,rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++; if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn,pPattern,pLen,rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++; if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn,pPattern,pLen,rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++; if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn,pPattern,pLen,rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++; } return SXERR_NOTFOUND; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* SyRunTimeApi:sxds.c */ PH7_PRIVATE sxi32 SySetInit(SySet *pSet,SyMemBackend *pAllocator,sxu32 ElemSize) { pSet->nSize = 0 ; pSet->nUsed = 0; pSet->nCursor = 0; pSet->eSize = ElemSize; pSet->pAllocator = pAllocator; pSet->pBase = 0; pSet->pUserData = 0; return SXRET_OK; } PH7_PRIVATE sxi32 SySetPut(SySet *pSet,const void *pItem) { unsigned char *zbase; if( pSet->nUsed >= pSet->nSize ){ void *pNew; if( pSet->pAllocator == 0 ){ return SXERR_LOCKED; } if( pSet->nSize <= 0 ){ pSet->nSize = 4; } pNew = SyMemBackendRealloc(pSet->pAllocator,pSet->pBase,pSet->eSize * pSet->nSize * 2); if( pNew == 0 ){ return SXERR_MEM; } pSet->pBase = pNew; pSet->nSize <<= 1; } zbase = (unsigned char *)pSet->pBase; SX_MACRO_FAST_MEMCPY(pItem,&zbase[pSet->nUsed * pSet->eSize],pSet->eSize); pSet->nUsed++; return SXRET_OK; } PH7_PRIVATE sxi32 SySetAlloc(SySet *pSet,sxi32 nItem) { if( pSet->nSize > 0 ){ return SXERR_LOCKED; } if( nItem < 8 ){ nItem = 8; } pSet->pBase = SyMemBackendAlloc(pSet->pAllocator,pSet->eSize * nItem); if( pSet->pBase == 0 ){ return SXERR_MEM; } pSet->nSize = nItem; return SXRET_OK; } PH7_PRIVATE sxi32 SySetReset(SySet *pSet) { pSet->nUsed = 0; pSet->nCursor = 0; return SXRET_OK; } PH7_PRIVATE sxi32 SySetResetCursor(SySet *pSet) { pSet->nCursor = 0; return SXRET_OK; } PH7_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet,void **ppEntry) { register unsigned char *zSrc; if( pSet->nCursor >= pSet->nUsed ){ /* Reset cursor */ pSet->nCursor = 0; return SXERR_EOF; } zSrc = (unsigned char *)SySetBasePtr(pSet); if( ppEntry ){ *ppEntry = (void *)&zSrc[pSet->nCursor * pSet->eSize]; } pSet->nCursor++; return SXRET_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE void * SySetPeekCurrentEntry(SySet *pSet) { register unsigned char *zSrc; if( pSet->nCursor >= pSet->nUsed ){ return 0; } zSrc = (unsigned char *)SySetBasePtr(pSet); return (void *)&zSrc[pSet->nCursor * pSet->eSize]; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ PH7_PRIVATE sxi32 SySetTruncate(SySet *pSet,sxu32 nNewSize) { if( nNewSize < pSet->nUsed ){ pSet->nUsed = nNewSize; } return SXRET_OK; } PH7_PRIVATE sxi32 SySetRelease(SySet *pSet) { sxi32 rc = SXRET_OK; if( pSet->pAllocator && pSet->pBase ){ rc = SyMemBackendFree(pSet->pAllocator,pSet->pBase); } pSet->pBase = 0; pSet->nUsed = 0; pSet->nCursor = 0; return rc; } PH7_PRIVATE void * SySetPeek(SySet *pSet) { const char *zBase; if( pSet->nUsed <= 0 ){ return 0; } zBase = (const char *)pSet->pBase; return (void *)&zBase[(pSet->nUsed - 1) * pSet->eSize]; } PH7_PRIVATE void * SySetPop(SySet *pSet) { const char *zBase; void *pData; if( pSet->nUsed <= 0 ){ return 0; } zBase = (const char *)pSet->pBase; pSet->nUsed--; pData = (void *)&zBase[pSet->nUsed * pSet->eSize]; return pData; } PH7_PRIVATE void * SySetAt(SySet *pSet,sxu32 nIdx) { const char *zBase; if( nIdx >= pSet->nUsed ){ /* Out of range */ return 0; } zBase = (const char *)pSet->pBase; return (void *)&zBase[nIdx * pSet->eSize]; } /* Private hash entry */ struct SyHashEntry_Pr { const void *pKey; /* Hash key */ sxu32 nKeyLen; /* Key length */ void *pUserData; /* User private data */ /* Private fields */ sxu32 nHash; SyHash *pHash; SyHashEntry_Pr *pNext,*pPrev; /* Next and previous entry in the list */ SyHashEntry_Pr *pNextCollide,*pPrevCollide; /* Collision list */ }; #define INVALID_HASH(H) ((H)->apBucket == 0) /* Forward declarartion */ static sxu32 SyBinHash(const void *pSrc,sxu32 nLen); PH7_PRIVATE sxi32 SyHashInit(SyHash *pHash,SyMemBackend *pAllocator,ProcHash xHash,ProcCmp xCmp) { SyHashEntry_Pr **apNew; #if defined(UNTRUST) if( pHash == 0 ){ return SXERR_EMPTY; } #endif /* Allocate a new table */ apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(&(*pAllocator),sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE); if( apNew == 0 ){ return SXERR_MEM; } SyZero((void *)apNew,sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE); pHash->pAllocator = &(*pAllocator); pHash->xHash = xHash ? xHash : SyBinHash; pHash->xCmp = xCmp ? xCmp : SyMemcmp; pHash->pCurrent = pHash->pList = 0; pHash->nEntry = 0; pHash->apBucket = apNew; pHash->nBucketSize = SXHASH_BUCKET_SIZE; return SXRET_OK; } PH7_PRIVATE sxi32 SyHashRelease(SyHash *pHash) { SyHashEntry_Pr *pEntry,*pNext; #if defined(UNTRUST) if( INVALID_HASH(pHash) ){ return SXERR_EMPTY; } #endif pEntry = pHash->pList; for(;;){ if( pHash->nEntry == 0 ){ break; } pNext = pEntry->pNext; SyMemBackendPoolFree(pHash->pAllocator,pEntry); pEntry = pNext; pHash->nEntry--; } if( pHash->apBucket ){ SyMemBackendFree(pHash->pAllocator,(void *)pHash->apBucket); } pHash->apBucket = 0; pHash->nBucketSize = 0; pHash->pAllocator = 0; return SXRET_OK; } static SyHashEntry_Pr * HashGetEntry(SyHash *pHash,const void *pKey,sxu32 nKeyLen) { SyHashEntry_Pr *pEntry; sxu32 nHash; nHash = pHash->xHash(pKey,nKeyLen); pEntry = pHash->apBucket[nHash & (pHash->nBucketSize - 1)]; for(;;){ if( pEntry == 0 ){ break; } if( pEntry->nHash == nHash && pEntry->nKeyLen == nKeyLen && pHash->xCmp(pEntry->pKey,pKey,nKeyLen) == 0 ){ return pEntry; } pEntry = pEntry->pNextCollide; } /* Entry not found */ return 0; } PH7_PRIVATE SyHashEntry * SyHashGet(SyHash *pHash,const void *pKey,sxu32 nKeyLen) { SyHashEntry_Pr *pEntry; #if defined(UNTRUST) if( INVALID_HASH(pHash) ){ return 0; } #endif if( pHash->nEntry < 1 || nKeyLen < 1 ){ /* Don't bother hashing,return immediately */ return 0; } pEntry = HashGetEntry(&(*pHash),pKey,nKeyLen); if( pEntry == 0 ){ return 0; } return (SyHashEntry *)pEntry; } static sxi32 HashDeleteEntry(SyHash *pHash,SyHashEntry_Pr *pEntry,void **ppUserData) { sxi32 rc; if( pEntry->pPrevCollide == 0 ){ pHash->apBucket[pEntry->nHash & (pHash->nBucketSize - 1)] = pEntry->pNextCollide; }else{ pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide; } if( pEntry->pNextCollide ){ pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide; } MACRO_LD_REMOVE(pHash->pList,pEntry); pHash->nEntry--; if( ppUserData ){ /* Write a pointer to the user data */ *ppUserData = pEntry->pUserData; } /* Release the entry */ rc = SyMemBackendPoolFree(pHash->pAllocator,pEntry); return rc; } PH7_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash,const void *pKey,sxu32 nKeyLen,void **ppUserData) { SyHashEntry_Pr *pEntry; sxi32 rc; #if defined(UNTRUST) if( INVALID_HASH(pHash) ){ return SXERR_CORRUPT; } #endif pEntry = HashGetEntry(&(*pHash),pKey,nKeyLen); if( pEntry == 0 ){ return SXERR_NOTFOUND; } rc = HashDeleteEntry(&(*pHash),pEntry,ppUserData); return rc; } PH7_PRIVATE sxi32 SyHashDeleteEntry2(SyHashEntry *pEntry) { SyHashEntry_Pr *pPtr = (SyHashEntry_Pr *)pEntry; sxi32 rc; #if defined(UNTRUST) if( pPtr == 0 || INVALID_HASH(pPtr->pHash) ){ return SXERR_CORRUPT; } #endif rc = HashDeleteEntry(pPtr->pHash,pPtr,0); return rc; } PH7_PRIVATE sxi32 SyHashResetLoopCursor(SyHash *pHash) { #if defined(UNTRUST) if( INVALID_HASH(pHash) ){ return SXERR_CORRUPT; } #endif pHash->pCurrent = pHash->pList; return SXRET_OK; } PH7_PRIVATE SyHashEntry * SyHashGetNextEntry(SyHash *pHash) { SyHashEntry_Pr *pEntry; #if defined(UNTRUST) if( INVALID_HASH(pHash) ){ return 0; } #endif if( pHash->pCurrent == 0 || pHash->nEntry <= 0 ){ pHash->pCurrent = pHash->pList; return 0; } pEntry = pHash->pCurrent; /* Advance the cursor */ pHash->pCurrent = pEntry->pNext; /* Return the current entry */ return (SyHashEntry *)pEntry; } PH7_PRIVATE sxi32 SyHashForEach(SyHash *pHash,sxi32 (*xStep)(SyHashEntry *,void *),void *pUserData) { SyHashEntry_Pr *pEntry; sxi32 rc; sxu32 n; #if defined(UNTRUST) if( INVALID_HASH(pHash) || xStep == 0){ return 0; } #endif pEntry = pHash->pList; for( n = 0 ; n < pHash->nEntry ; n++ ){ /* Invoke the callback */ rc = xStep((SyHashEntry *)pEntry,pUserData); if( rc != SXRET_OK ){ return rc; } /* Point to the next entry */ pEntry = pEntry->pNext; } return SXRET_OK; } static sxi32 HashGrowTable(SyHash *pHash) { sxu32 nNewSize = pHash->nBucketSize * 2; SyHashEntry_Pr *pEntry; SyHashEntry_Pr **apNew; sxu32 n,iBucket; /* Allocate a new larger table */ apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(pHash->pAllocator,nNewSize * sizeof(SyHashEntry_Pr *)); if( apNew == 0 ){ /* Not so fatal,simply a performance hit */ return SXRET_OK; } /* Zero the new table */ SyZero((void *)apNew,nNewSize * sizeof(SyHashEntry_Pr *)); /* Rehash all entries */ for( n = 0,pEntry = pHash->pList; n < pHash->nEntry ; n++ ){ pEntry->pNextCollide = pEntry->pPrevCollide = 0; /* Install in the new bucket */ iBucket = pEntry->nHash & (nNewSize - 1); pEntry->pNextCollide = apNew[iBucket]; if( apNew[iBucket] != 0 ){ apNew[iBucket]->pPrevCollide = pEntry; } apNew[iBucket] = pEntry; /* Point to the next entry */ pEntry = pEntry->pNext; } /* Release the old table and reflect the change */ SyMemBackendFree(pHash->pAllocator,(void *)pHash->apBucket); pHash->apBucket = apNew; pHash->nBucketSize = nNewSize; return SXRET_OK; } static sxi32 HashInsert(SyHash *pHash,SyHashEntry_Pr *pEntry) { sxu32 iBucket = pEntry->nHash & (pHash->nBucketSize - 1); /* Insert the entry in its corresponding bcuket */ pEntry->pNextCollide = pHash->apBucket[iBucket]; if( pHash->apBucket[iBucket] != 0 ){ pHash->apBucket[iBucket]->pPrevCollide = pEntry; } pHash->apBucket[iBucket] = pEntry; /* Link to the entry list */ MACRO_LD_PUSH(pHash->pList,pEntry); if( pHash->nEntry == 0 ){ pHash->pCurrent = pHash->pList; } pHash->nEntry++; return SXRET_OK; } PH7_PRIVATE sxi32 SyHashInsert(SyHash *pHash,const void *pKey,sxu32 nKeyLen,void *pUserData) { SyHashEntry_Pr *pEntry; sxi32 rc; #if defined(UNTRUST) if( INVALID_HASH(pHash) || pKey == 0 ){ return SXERR_CORRUPT; } #endif if( pHash->nEntry >= pHash->nBucketSize * SXHASH_FILL_FACTOR ){ rc = HashGrowTable(&(*pHash)); if( rc != SXRET_OK ){ return rc; } } /* Allocate a new hash entry */ pEntry = (SyHashEntry_Pr *)SyMemBackendPoolAlloc(pHash->pAllocator,sizeof(SyHashEntry_Pr)); if( pEntry == 0 ){ return SXERR_MEM; } /* Zero the entry */ SyZero(pEntry,sizeof(SyHashEntry_Pr)); pEntry->pHash = pHash; pEntry->pKey = pKey; pEntry->nKeyLen = nKeyLen; pEntry->pUserData = pUserData; pEntry->nHash = pHash->xHash(pEntry->pKey,pEntry->nKeyLen); /* Finally insert the entry in its corresponding bucket */ rc = HashInsert(&(*pHash),pEntry); return rc; } PH7_PRIVATE SyHashEntry * SyHashLastEntry(SyHash *pHash) { #if defined(UNTRUST) if( INVALID_HASH(pHash) ){ return 0; } #endif /* Last inserted entry */ return (SyHashEntry *)pHash->pList; } /* SyRunTimeApi:sxutils.c */ PH7_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc,sxu32 nLen,sxu8 *pReal,const char **pzTail) { const char *zCur,*zEnd; #ifdef UNTRUST if( SX_EMPTY_STR(zSrc) ){ return SXERR_EMPTY; } #endif zEnd = &zSrc[nLen]; /* Jump leading white spaces */ while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisSpace(zSrc[0]) ){ zSrc++; } if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){ zSrc++; } zCur = zSrc; if( pReal ){ *pReal = FALSE; } for(;;){ if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++; if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++; if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++; if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++; }; if( zSrc < zEnd && zSrc > zCur ){ int c = zSrc[0]; if( c == '.' ){ zSrc++; if( pReal ){ *pReal = TRUE; } if( pzTail ){ while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){ zSrc++; } if( zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E') ){ zSrc++; if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){ zSrc++; } while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){ zSrc++; } } } }else if( c == 'e' || c == 'E' ){ zSrc++; if( pReal ){ *pReal = TRUE; } if( pzTail ){ if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){ zSrc++; } while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){ zSrc++; } } } } if( pzTail ){ /* Point to the non numeric part */ *pzTail = zSrc; } return zSrc > zCur ? SXRET_OK /* String prefix is numeric */ : SXERR_INVALID /* Not a digit stream */; } #define SXINT32_MIN_STR "2147483648" #define SXINT32_MAX_STR "2147483647" #define SXINT64_MIN_STR "9223372036854775808" #define SXINT64_MAX_STR "9223372036854775807" PH7_PRIVATE sxi32 SyStrToInt32(const char *zSrc,sxu32 nLen,void * pOutVal,const char **zRest) { int isNeg = FALSE; const char *zEnd; sxi32 nVal = 0; sxi16 i; #if defined(UNTRUST) if( SX_EMPTY_STR(zSrc) ){ if( pOutVal ){ *(sxi32 *)pOutVal = 0; } return SXERR_EMPTY; } #endif zEnd = &zSrc[nLen]; while(zSrc < zEnd && SyisSpace(zSrc[0]) ){ zSrc++; } if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){ isNeg = (zSrc[0] == '-') ? TRUE :FALSE; zSrc++; } /* Skip leading zero */ while(zSrc < zEnd && zSrc[0] == '0' ){ zSrc++; } i = 10; if( (sxu32)(zEnd-zSrc) >= 10 ){ /* Handle overflow */ i = SyMemcmp(zSrc,(isNeg == TRUE) ? SXINT32_MIN_STR : SXINT32_MAX_STR,nLen) <= 0 ? 10 : 9; } for(;;){ if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; } /* Skip trailing spaces */ while(zSrc < zEnd && SyisSpace(zSrc[0])){ zSrc++; } if( zRest ){ *zRest = (char *)zSrc; } if( pOutVal ){ if( isNeg == TRUE && nVal != 0 ){ nVal = -nVal; } *(sxi32 *)pOutVal = nVal; } return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX; } PH7_PRIVATE sxi32 SyStrToInt64(const char *zSrc,sxu32 nLen,void * pOutVal,const char **zRest) { int isNeg = FALSE; const char *zEnd; sxi64 nVal; sxi16 i; #if defined(UNTRUST) if( SX_EMPTY_STR(zSrc) ){ if( pOutVal ){ *(sxi32 *)pOutVal = 0; } return SXERR_EMPTY; } #endif zEnd = &zSrc[nLen]; while(zSrc < zEnd && SyisSpace(zSrc[0]) ){ zSrc++; } if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){ isNeg = (zSrc[0] == '-') ? TRUE :FALSE; zSrc++; } /* Skip leading zero */ while(zSrc < zEnd && zSrc[0] == '0' ){ zSrc++; } i = 19; if( (sxu32)(zEnd-zSrc) >= 19 ){ i = SyMemcmp(zSrc,isNeg ? SXINT64_MIN_STR : SXINT64_MAX_STR,19) <= 0 ? 19 : 18 ; } nVal = 0; for(;;){ if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++; } /* Skip trailing spaces */ while(zSrc < zEnd && SyisSpace(zSrc[0])){ zSrc++; } if( zRest ){ *zRest = (char *)zSrc; } if( pOutVal ){ if( isNeg == TRUE && nVal != 0 ){ nVal = -nVal; } *(sxi64 *)pOutVal = nVal; } return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX; } PH7_PRIVATE sxi32 SyHexToint(sxi32 c) { switch(c){ case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case 'A': case 'a': return 10; case 'B': case 'b': return 11; case 'C': case 'c': return 12; case 'D': case 'd': return 13; case 'E': case 'e': return 14; case 'F': case 'f': return 15; } return -1; } PH7_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc,sxu32 nLen,void * pOutVal,const char **zRest) { const char *zIn,*zEnd; int isNeg = FALSE; sxi64 nVal = 0; #if defined(UNTRUST) if( SX_EMPTY_STR(zSrc) ){ if( pOutVal ){ *(sxi32 *)pOutVal = 0; } return SXERR_EMPTY; } #endif zEnd = &zSrc[nLen]; while( zSrc < zEnd && SyisSpace(zSrc[0]) ){ zSrc++; } if( zSrc < zEnd && ( *zSrc == '-' || *zSrc == '+' ) ){ isNeg = (zSrc[0] == '-') ? TRUE :FALSE; zSrc++; } if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'x' || zSrc[1] == 'X') ){ /* Bypass hex prefix */ zSrc += sizeof(char) * 2; } /* Skip leading zero */ while(zSrc < zEnd && zSrc[0] == '0' ){ zSrc++; } zIn = zSrc; for(;;){ if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ; if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ; if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ; if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ; } while( zSrc < zEnd && SyisSpace(zSrc[0]) ){ zSrc++; } if( zRest ){ *zRest = zSrc; } if( pOutVal ){ if( isNeg == TRUE && nVal != 0 ){ nVal = -nVal; } *(sxi64 *)pOutVal = nVal; } return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX; } PH7_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc,sxu32 nLen,void * pOutVal,const char **zRest) { const char *zIn,*zEnd; int isNeg = FALSE; sxi64 nVal = 0; int c; #if defined(UNTRUST) if( SX_EMPTY_STR(zSrc) ){ if( pOutVal ){ *(sxi32 *)pOutVal = 0; } return SXERR_EMPTY; } #endif zEnd = &zSrc[nLen]; while(zSrc < zEnd && SyisSpace(zSrc[0]) ){ zSrc++; } if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){ isNeg = (zSrc[0] == '-') ? TRUE :FALSE; zSrc++; } /* Skip leading zero */ while(zSrc < zEnd && zSrc[0] == '0' ){ zSrc++; } zIn = zSrc; for(;;){ if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++; if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++; if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++; if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++; } /* Skip trailing spaces */ while(zSrc < zEnd && SyisSpace(zSrc[0])){ zSrc++; } if( zRest ){ *zRest = zSrc; } if( pOutVal ){ if( isNeg == TRUE && nVal != 0 ){ nVal = -nVal; } *(sxi64 *)pOutVal = nVal; } return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX; } PH7_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc,sxu32 nLen,void * pOutVal,const char **zRest) { const char *zIn,*zEnd; int isNeg = FALSE; sxi64 nVal = 0; int c; #if defined(UNTRUST) if( SX_EMPTY_STR(zSrc) ){ if( pOutVal ){ *(sxi32 *)pOutVal = 0; } return SXERR_EMPTY; } #endif zEnd = &zSrc[nLen]; while(zSrc < zEnd && SyisSpace(zSrc[0]) ){ zSrc++; } if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){ isNeg = (zSrc[0] == '-') ? TRUE :FALSE; zSrc++; } if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'b' || zSrc[1] == 'B') ){ /* Bypass binary prefix */ zSrc += sizeof(char) * 2; } /* Skip leading zero */ while(zSrc < zEnd && zSrc[0] == '0' ){ zSrc++; } zIn = zSrc; for(;;){ if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++; if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++; if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++; if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++; } /* Skip trailing spaces */ while(zSrc < zEnd && SyisSpace(zSrc[0])){ zSrc++; } if( zRest ){ *zRest = zSrc; } if( pOutVal ){ if( isNeg == TRUE && nVal != 0 ){ nVal = -nVal; } *(sxi64 *)pOutVal = nVal; } return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX; } PH7_PRIVATE sxi32 SyStrToReal(const char *zSrc,sxu32 nLen,void * pOutVal,const char **zRest) { #define SXDBL_DIG 15 #define SXDBL_MAX_EXP 308 #define SXDBL_MIN_EXP_PLUS 307 static const sxreal aTab[] = { 10, 1.0e2, 1.0e4, 1.0e8, 1.0e16, 1.0e32, 1.0e64, 1.0e128, 1.0e256 }; sxu8 neg = FALSE; sxreal Val = 0.0; const char *zEnd; sxi32 Lim,exp; sxreal *p = 0; #ifdef UNTRUST if( SX_EMPTY_STR(zSrc) ){ if( pOutVal ){ *(sxreal *)pOutVal = 0.0; } return SXERR_EMPTY; } #endif zEnd = &zSrc[nLen]; while( zSrc < zEnd && SyisSpace(zSrc[0]) ){ zSrc++; } if( zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+' ) ){ neg = zSrc[0] == '-' ? TRUE : FALSE ; zSrc++; } Lim = SXDBL_DIG ; for(;;){ if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim; if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim; if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim; if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim; } if( zSrc < zEnd && ( zSrc[0] == '.' || zSrc[0] == ',' ) ){ sxreal dec = 1.0; zSrc++; for(;;){ if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim; if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim; if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim; if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim; } Val /= dec; } if( neg == TRUE && Val != 0.0 ) { Val = -Val ; } if( Lim <= 0 ){ /* jump overflow digit */ while( zSrc < zEnd ){ if( zSrc[0] == 'e' || zSrc[0] == 'E' ){ break; } zSrc++; } } neg = FALSE; if( zSrc < zEnd && ( zSrc[0] == 'e' || zSrc[0] == 'E' ) ){ zSrc++; if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+') ){ neg = zSrc[0] == '-' ? TRUE : FALSE ; zSrc++; } exp = 0; while( zSrc < zEnd && SyisDigit(zSrc[0]) && exp < SXDBL_MAX_EXP ){ exp = exp * 10 + (zSrc[0] - '0'); zSrc++; } if( neg ){ if( exp > SXDBL_MIN_EXP_PLUS ) exp = SXDBL_MIN_EXP_PLUS ; }else if ( exp > SXDBL_MAX_EXP ){ exp = SXDBL_MAX_EXP; } for( p = (sxreal *)aTab ; exp ; exp >>= 1 , p++ ){ if( exp & 01 ){ if( neg ){ Val /= *p ; }else{ Val *= *p; } } } } while( zSrc < zEnd && SyisSpace(zSrc[0]) ){ zSrc++; } if( zRest ){ *zRest = zSrc; } if( pOutVal ){ *(sxreal *)pOutVal = Val; } return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX; } /* SyRunTimeApi:sxlib.c */ static sxu32 SyBinHash(const void *pSrc,sxu32 nLen) { register unsigned char *zIn = (unsigned char *)pSrc; unsigned char *zEnd; sxu32 nH = 5381; zEnd = &zIn[nLen]; for(;;){ if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; } return nH; } PH7_PRIVATE sxu32 SyStrHash(const void *pSrc,sxu32 nLen) { register unsigned char *zIn = (unsigned char *)pSrc; unsigned char *zEnd; sxu32 nH = 5381; zEnd = &zIn[nLen]; for(;;){ if( zIn >= zEnd ){ break; } nH = nH * 33 + SyToLower(zIn[0]); zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + SyToLower(zIn[0]); zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + SyToLower(zIn[0]); zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + SyToLower(zIn[0]); zIn++; } return nH; } #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyBase64Encode(const char *zSrc,sxu32 nLen,ProcConsumer xConsumer,void *pUserData) { static const unsigned char zBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; unsigned char *zIn = (unsigned char *)zSrc; unsigned char z64[4]; sxu32 i; sxi32 rc; #if defined(UNTRUST) if( SX_EMPTY_STR(zSrc) || xConsumer == 0){ return SXERR_EMPTY; } #endif for(i = 0; i + 2 < nLen; i += 3){ z64[0] = zBase64[(zIn[i] >> 2) & 0x3F]; z64[1] = zBase64[( ((zIn[i] & 0x03) << 4) | (zIn[i+1] >> 4)) & 0x3F]; z64[2] = zBase64[( ((zIn[i+1] & 0x0F) << 2) | (zIn[i + 2] >> 6) ) & 0x3F]; z64[3] = zBase64[ zIn[i + 2] & 0x3F]; rc = xConsumer((const void *)z64,sizeof(z64),pUserData); if( rc != SXRET_OK ){return SXERR_ABORT;} } if ( i+1 < nLen ){ z64[0] = zBase64[(zIn[i] >> 2) & 0x3F]; z64[1] = zBase64[( ((zIn[i] & 0x03) << 4) | (zIn[i+1] >> 4)) & 0x3F]; z64[2] = zBase64[(zIn[i+1] & 0x0F) << 2 ]; z64[3] = '='; rc = xConsumer((const void *)z64,sizeof(z64),pUserData); if( rc != SXRET_OK ){return SXERR_ABORT;} }else if( i < nLen ){ z64[0] = zBase64[(zIn[i] >> 2) & 0x3F]; z64[1] = zBase64[(zIn[i] & 0x03) << 4]; z64[2] = '='; z64[3] = '='; rc = xConsumer((const void *)z64,sizeof(z64),pUserData); if( rc != SXRET_OK ){return SXERR_ABORT;} } return SXRET_OK; } PH7_PRIVATE sxi32 SyBase64Decode(const char *zB64,sxu32 nLen,ProcConsumer xConsumer,void *pUserData) { static const sxu32 aBase64Trans[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4, 5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27, 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,0,0, 0,0,0 }; sxu32 n,w,x,y,z; sxi32 rc; unsigned char zOut[10]; #if defined(UNTRUST) if( SX_EMPTY_STR(zB64) || xConsumer == 0 ){ return SXERR_EMPTY; } #endif while(nLen > 0 && zB64[nLen - 1] == '=' ){ nLen--; } for( n = 0 ; n+3>4) & 0x03); zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F); zOut[2] = ((y<<6) & 0xC0) | (z & 0x3F); rc = xConsumer((const void *)zOut,sizeof(unsigned char)*3,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT;} } if( n+2 < nLen ){ w = aBase64Trans[zB64[n] & 0x7F]; x = aBase64Trans[zB64[n+1] & 0x7F]; y = aBase64Trans[zB64[n+2] & 0x7F]; zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03); zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F); rc = xConsumer((const void *)zOut,sizeof(unsigned char)*2,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT;} }else if( n+1 < nLen ){ w = aBase64Trans[zB64[n] & 0x7F]; x = aBase64Trans[zB64[n+1] & 0x7F]; zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03); rc = xConsumer((const void *)zOut,sizeof(unsigned char)*1,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT;} } return SXRET_OK; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ #define INVALID_LEXER(LEX) ( LEX == 0 || LEX->xTokenizer == 0 ) PH7_PRIVATE sxi32 SyLexInit(SyLex *pLex,SySet *pSet,ProcTokenizer xTokenizer,void *pUserData) { SyStream *pStream; #if defined (UNTRUST) if ( pLex == 0 || xTokenizer == 0 ){ return SXERR_CORRUPT; } #endif pLex->pTokenSet = 0; /* Initialize lexer fields */ if( pSet ){ if ( SySetElemSize(pSet) != sizeof(SyToken) ){ return SXERR_INVALID; } pLex->pTokenSet = pSet; } pStream = &pLex->sStream; pLex->xTokenizer = xTokenizer; pLex->pUserData = pUserData; pStream->nLine = 1; pStream->nIgn = 0; pStream->zText = pStream->zEnd = 0; pStream->pSet = pSet; return SXRET_OK; } PH7_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex,const char *zInput,sxu32 nLen,void *pCtxData,ProcSort xSort,ProcCmp xCmp) { const unsigned char *zCur; SyStream *pStream; SyToken sToken; sxi32 rc; #if defined (UNTRUST) if ( INVALID_LEXER(pLex) || zInput == 0 ){ return SXERR_CORRUPT; } #endif pStream = &pLex->sStream; /* Point to the head of the input */ pStream->zText = pStream->zInput = (const unsigned char *)zInput; /* Point to the end of the input */ pStream->zEnd = &pStream->zInput[nLen]; for(;;){ if( pStream->zText >= pStream->zEnd ){ /* End of the input reached */ break; } zCur = pStream->zText; /* Call the tokenizer callback */ rc = pLex->xTokenizer(pStream,&sToken,pLex->pUserData,pCtxData); if( rc != SXRET_OK && rc != SXERR_CONTINUE ){ /* Tokenizer callback request an operation abort */ if( rc == SXERR_ABORT ){ return SXERR_ABORT; } break; } if( rc == SXERR_CONTINUE ){ /* Request to ignore this token */ pStream->nIgn++; }else if( pLex->pTokenSet ){ /* Put the token in the set */ rc = SySetPut(pLex->pTokenSet,(const void *)&sToken); if( rc != SXRET_OK ){ break; } } if( zCur >= pStream->zText ){ /* Automatic advance of the stream cursor */ pStream->zText = &zCur[1]; } } if( xSort && pLex->pTokenSet ){ SyToken *aToken = (SyToken *)SySetBasePtr(pLex->pTokenSet); /* Sort the extrated tokens */ if( xCmp == 0 ){ /* Use a default comparison function */ xCmp = SyMemcmp; } xSort(aToken,SySetUsed(pLex->pTokenSet),sizeof(SyToken),xCmp); } return SXRET_OK; } PH7_PRIVATE sxi32 SyLexRelease(SyLex *pLex) { sxi32 rc = SXRET_OK; #if defined (UNTRUST) if ( INVALID_LEXER(pLex) ){ return SXERR_CORRUPT; } #else SXUNUSED(pLex); /* Prevent compiler warning */ #endif return rc; } #ifndef PH7_DISABLE_BUILTIN_FUNC #define SAFE_HTTP(C) (SyisAlphaNum(c) || c == '_' || c == '-' || c == '$' || c == '.' ) PH7_PRIVATE sxi32 SyUriEncode(const char *zSrc,sxu32 nLen,ProcConsumer xConsumer,void *pUserData) { unsigned char *zIn = (unsigned char *)zSrc; unsigned char zHex[3] = { '%',0,0 }; unsigned char zOut[2]; unsigned char *zCur,*zEnd; sxi32 c; sxi32 rc; #ifdef UNTRUST if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){ return SXERR_EMPTY; } #endif rc = SXRET_OK; zEnd = &zIn[nLen]; zCur = zIn; for(;;){ if( zCur >= zEnd ){ if( zCur != zIn ){ rc = xConsumer(zIn,(sxu32)(zCur-zIn),pUserData); } break; } c = zCur[0]; if( SAFE_HTTP(c) ){ zCur++; continue; } if( zCur != zIn && SXRET_OK != (rc = xConsumer(zIn,(sxu32)(zCur-zIn),pUserData))){ break; } if( c == ' ' ){ zOut[0] = '+'; rc = xConsumer((const void *)zOut,sizeof(unsigned char),pUserData); }else{ zHex[1] = "0123456789ABCDEF"[(c >> 4) & 0x0F]; zHex[2] = "0123456789ABCDEF"[c & 0x0F]; rc = xConsumer(zHex,sizeof(zHex),pUserData); } if( SXRET_OK != rc ){ break; } zIn = &zCur[1]; zCur = zIn ; } return rc == SXRET_OK ? SXRET_OK : SXERR_ABORT; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ static sxi32 SyAsciiToHex(sxi32 c) { if( c >= 'a' && c <= 'f' ){ c += 10 - 'a'; return c; } if( c >= '0' && c <= '9' ){ c -= '0'; return c; } if( c >= 'A' && c <= 'F') { c += 10 - 'A'; return c; } return 0; } PH7_PRIVATE sxi32 SyUriDecode(const char *zSrc,sxu32 nLen,ProcConsumer xConsumer,void *pUserData,int bUTF8) { static const sxu8 Utf8Trans[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00 }; const char *zIn = zSrc; const char *zEnd; const char *zCur; sxu8 *zOutPtr; sxu8 zOut[10]; sxi32 c,d; sxi32 rc; #if defined(UNTRUST) if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){ return SXERR_EMPTY; } #endif rc = SXRET_OK; zEnd = &zSrc[nLen]; zCur = zIn; for(;;){ while(zCur < zEnd && zCur[0] != '%' && zCur[0] != '+' ){ zCur++; } if( zCur != zIn ){ /* Consume input */ rc = xConsumer(zIn,(unsigned int)(zCur-zIn),pUserData); if( rc != SXRET_OK ){ /* User consumer routine request an operation abort */ break; } } if( zCur >= zEnd ){ rc = SXRET_OK; break; } /* Decode unsafe HTTP characters */ zOutPtr = zOut; if( zCur[0] == '+' ){ *zOutPtr++ = ' '; zCur++; }else{ if( &zCur[2] >= zEnd ){ rc = SXERR_OVERFLOW; break; } c = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]); zCur += 3; if( c < 0x000C0 ){ *zOutPtr++ = (sxu8)c; }else{ c = Utf8Trans[c-0xC0]; while( zCur[0] == '%' ){ d = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]); if( (d&0xC0) != 0x80 ){ break; } c = (c<<6) + (0x3f & d); zCur += 3; } if( bUTF8 == FALSE ){ *zOutPtr++ = (sxu8)c; }else{ SX_WRITE_UTF8(zOutPtr,c); } } } /* Consume the decoded characters */ rc = xConsumer((const void *)zOut,(unsigned int)(zOutPtr-zOut),pUserData); if( rc != SXRET_OK ){ break; } /* Synchronize pointers */ zIn = zCur; } return rc; } #ifndef PH7_DISABLE_BUILTIN_FUNC static const char *zEngDay[] = { "Sunday","Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday" }; static const char *zEngMonth[] = { "January","February","March","April", "May","June","July","August", "September","October","November","December" }; static const char * GetDay(sxi32 i) { return zEngDay[ i % 7 ]; } static const char * GetMonth(sxi32 i) { return zEngMonth[ i % 12 ]; } PH7_PRIVATE const char * SyTimeGetDay(sxi32 iDay) { return GetDay(iDay); } PH7_PRIVATE const char * SyTimeGetMonth(sxi32 iMonth) { return GetMonth(iMonth); } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* SyRunTimeApi: sxfmt.c */ #define SXFMT_BUFSIZ 1024 /* Conversion buffer size */ /* ** Conversion types fall into various categories as defined by the ** following enumeration. */ #define SXFMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */ #define SXFMT_FLOAT 2 /* Floating point.%f */ #define SXFMT_EXP 3 /* Exponentional notation.%e and %E */ #define SXFMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */ #define SXFMT_SIZE 5 /* Total number of characters processed so far.%n */ #define SXFMT_STRING 6 /* Strings.%s */ #define SXFMT_PERCENT 7 /* Percent symbol.%% */ #define SXFMT_CHARX 8 /* Characters.%c */ #define SXFMT_ERROR 9 /* Used to indicate no such conversion type */ /* Extension by Symisc Systems */ #define SXFMT_RAWSTR 13 /* %z Pointer to raw string (SyString *) */ #define SXFMT_UNUSED 15 /* ** Allowed values for SyFmtInfo.flags */ #define SXFLAG_SIGNED 0x01 #define SXFLAG_UNSIGNED 0x02 /* Allowed values for SyFmtConsumer.nType */ #define SXFMT_CONS_PROC 1 /* Consumer is a procedure */ #define SXFMT_CONS_STR 2 /* Consumer is a managed string */ #define SXFMT_CONS_FILE 5 /* Consumer is an open File */ #define SXFMT_CONS_BLOB 6 /* Consumer is a BLOB */ /* ** Each builtin conversion character (ex: the 'd' in "%d") is described ** by an instance of the following structure */ typedef struct SyFmtInfo SyFmtInfo; struct SyFmtInfo { char fmttype; /* The format field code letter [i.e: 'd','s','x'] */ sxu8 base; /* The base for radix conversion */ int flags; /* One or more of SXFLAG_ constants below */ sxu8 type; /* Conversion paradigm */ char *charset; /* The character set for conversion */ char *prefix; /* Prefix on non-zero values in alt format */ }; typedef struct SyFmtConsumer SyFmtConsumer; struct SyFmtConsumer { sxu32 nLen; /* Total output length */ sxi32 nType; /* Type of the consumer see below */ sxi32 rc; /* Consumer return value;Abort processing if rc != SXRET_OK */ union{ struct{ ProcConsumer xUserConsumer; void *pUserData; }sFunc; SyBlob *pBlob; }uConsumer; }; #ifndef SX_OMIT_FLOATINGPOINT static int getdigit(sxlongreal *val,int *cnt) { sxlongreal d; int digit; if( (*cnt)++ >= 16 ){ return '0'; } digit = (int)*val; d = digit; *val = (*val - d)*10.0; return digit + '0' ; } #endif /* SX_OMIT_FLOATINGPOINT */ /* * The following routine was taken from the SQLITE2 source tree and was * extended by Symisc Systems to fit its need. * Status: Public Domain */ static sxi32 InternFormat(ProcConsumer xConsumer,void *pUserData,const char *zFormat,va_list ap) { /* * The following table is searched linearly, so it is good to put the most frequently * used conversion types first. */ static const SyFmtInfo aFmt[] = { { 'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789",0 }, { 's', 0, 0, SXFMT_STRING, 0, 0 }, { 'c', 0, 0, SXFMT_CHARX, 0, 0 }, { 'x', 16, 0, SXFMT_RADIX, "0123456789abcdef", "x0" }, { 'X', 16, 0, SXFMT_RADIX, "0123456789ABCDEF", "X0" }, /* -- Extensions by Symisc Systems -- */ { 'z', 0, 0, SXFMT_RAWSTR, 0, 0 }, /* Pointer to a raw string (SyString *) */ { 'B', 2, 0, SXFMT_RADIX, "01", "b0"}, /* -- End of Extensions -- */ { 'o', 8, 0, SXFMT_RADIX, "01234567", "0" }, { 'u', 10, 0, SXFMT_RADIX, "0123456789", 0 }, #ifndef SX_OMIT_FLOATINGPOINT { 'f', 0, SXFLAG_SIGNED, SXFMT_FLOAT, 0, 0 }, { 'e', 0, SXFLAG_SIGNED, SXFMT_EXP, "e", 0 }, { 'E', 0, SXFLAG_SIGNED, SXFMT_EXP, "E", 0 }, { 'g', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "e", 0 }, { 'G', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "E", 0 }, #endif { 'i', 10, SXFLAG_SIGNED, SXFMT_RADIX,"0123456789", 0 }, { 'n', 0, 0, SXFMT_SIZE, 0, 0 }, { '%', 0, 0, SXFMT_PERCENT, 0, 0 }, { 'p', 10, 0, SXFMT_RADIX, "0123456789", 0 } }; int c; /* Next character in the format string */ char *bufpt; /* Pointer to the conversion buffer */ int precision; /* Precision of the current field */ int length; /* Length of the field */ int idx; /* A general purpose loop counter */ int width; /* Width of the current field */ sxu8 flag_leftjustify; /* True if "-" flag is present */ sxu8 flag_plussign; /* True if "+" flag is present */ sxu8 flag_blanksign; /* True if " " flag is present */ sxu8 flag_alternateform; /* True if "#" flag is present */ sxu8 flag_zeropad; /* True if field width constant starts with zero */ sxu8 flag_long; /* True if "l" flag is present */ sxi64 longvalue; /* Value for integer types */ const SyFmtInfo *infop; /* Pointer to the appropriate info structure */ char buf[SXFMT_BUFSIZ]; /* Conversion buffer */ char prefix; /* Prefix character."+" or "-" or " " or '\0'.*/ sxu8 errorflag = 0; /* True if an error is encountered */ sxu8 xtype; /* Conversion paradigm */ char *zExtra; static char spaces[] = " "; #define etSPACESIZE ((int)sizeof(spaces)-1) #ifndef SX_OMIT_FLOATINGPOINT sxlongreal realvalue; /* Value for real types */ int exp; /* exponent of real numbers */ double rounder; /* Used for rounding floating point values */ sxu8 flag_dp; /* True if decimal point should be shown */ sxu8 flag_rtz; /* True if trailing zeros should be removed */ sxu8 flag_exp; /* True to force display of the exponent */ int nsd; /* Number of significant digits returned */ #endif int rc; length = 0; bufpt = 0; for(; (c=(*zFormat))!=0; ++zFormat){ if( c!='%' ){ unsigned int amt; bufpt = (char *)zFormat; amt = 1; while( (c=(*++zFormat))!='%' && c!=0 ) amt++; rc = xConsumer((const void *)bufpt,amt,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } if( c==0 ){ return errorflag > 0 ? SXERR_FORMAT : SXRET_OK; } } if( (c=(*++zFormat))==0 ){ errorflag = 1; rc = xConsumer("%",sizeof("%")-1,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } return errorflag > 0 ? SXERR_FORMAT : SXRET_OK; } /* Find out what flags are present */ flag_leftjustify = flag_plussign = flag_blanksign = flag_alternateform = flag_zeropad = 0; do{ switch( c ){ case '-': flag_leftjustify = 1; c = 0; break; case '+': flag_plussign = 1; c = 0; break; case ' ': flag_blanksign = 1; c = 0; break; case '#': flag_alternateform = 1; c = 0; break; case '0': flag_zeropad = 1; c = 0; break; default: break; } }while( c==0 && (c=(*++zFormat))!=0 ); /* Get the field width */ width = 0; if( c=='*' ){ width = va_arg(ap,int); if( width<0 ){ flag_leftjustify = 1; width = -width; } c = *++zFormat; }else{ while( c>='0' && c<='9' ){ width = width*10 + c - '0'; c = *++zFormat; } } if( width > SXFMT_BUFSIZ-10 ){ width = SXFMT_BUFSIZ-10; } /* Get the precision */ precision = -1; if( c=='.' ){ precision = 0; c = *++zFormat; if( c=='*' ){ precision = va_arg(ap,int); if( precision<0 ) precision = -precision; c = *++zFormat; }else{ while( c>='0' && c<='9' ){ precision = precision*10 + c - '0'; c = *++zFormat; } } } /* Get the conversion type modifier */ flag_long = 0; if( c=='l' || c == 'q' /* BSD quad (expect a 64-bit integer) */ ){ flag_long = (c == 'q') ? 2 : 1; c = *++zFormat; if( c == 'l' ){ /* Standard printf emulation 'lld' (expect a 64bit integer) */ flag_long = 2; } } /* Fetch the info entry for the field */ infop = 0; xtype = SXFMT_ERROR; for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){ if( c==aFmt[idx].fmttype ){ infop = &aFmt[idx]; xtype = infop->type; break; } } zExtra = 0; /* ** At this point, variables are initialized as follows: ** ** flag_alternateform TRUE if a '#' is present. ** flag_plussign TRUE if a '+' is present. ** flag_leftjustify TRUE if a '-' is present or if the ** field width was negative. ** flag_zeropad TRUE if the width began with 0. ** flag_long TRUE if the letter 'l' (ell) or 'q'(BSD quad) prefixed ** the conversion character. ** flag_blanksign TRUE if a ' ' is present. ** width The specified field width.This is ** always non-negative.Zero is the default. ** precision The specified precision.The default ** is -1. ** xtype The class of the conversion. ** infop Pointer to the appropriate info struct. */ switch( xtype ){ case SXFMT_RADIX: if( flag_long > 0 ){ if( flag_long > 1 ){ /* BSD quad: expect a 64-bit integer */ longvalue = va_arg(ap,sxi64); }else{ longvalue = va_arg(ap,sxlong); } }else{ if( infop->flags & SXFLAG_SIGNED ){ longvalue = va_arg(ap,sxi32); }else{ longvalue = va_arg(ap,sxu32); } } /* Limit the precision to prevent overflowing buf[] during conversion */ if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40; #if 1 /* For the format %#x, the value zero is printed "0" not "0x0". ** I think this is stupid.*/ if( longvalue==0 ) flag_alternateform = 0; #else /* More sensible: turn off the prefix for octal (to prevent "00"), ** but leave the prefix for hex.*/ if( longvalue==0 && infop->base==8 ) flag_alternateform = 0; #endif if( infop->flags & SXFLAG_SIGNED ){ if( longvalue<0 ){ longvalue = -longvalue; /* Ticket 1433-003 */ if( longvalue < 0 ){ /* Overflow */ longvalue= 0x7FFFFFFFFFFFFFFF; } prefix = '-'; }else if( flag_plussign ) prefix = '+'; else if( flag_blanksign ) prefix = ' '; else prefix = 0; }else{ if( longvalue<0 ){ longvalue = -longvalue; /* Ticket 1433-003 */ if( longvalue < 0 ){ /* Overflow */ longvalue= 0x7FFFFFFFFFFFFFFF; } } prefix = 0; } if( flag_zeropad && precisioncharset; base = infop->base; do{ /* Convert to ascii */ *(--bufpt) = cset[longvalue%base]; longvalue = longvalue/base; }while( longvalue>0 ); } length = &buf[SXFMT_BUFSIZ-1]-bufpt; for(idx=precision-length; idx>0; idx--){ *(--bufpt) = '0'; /* Zero pad */ } if( prefix ) *(--bufpt) = prefix; /* Add sign */ if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ char *pre, x; pre = infop->prefix; if( *bufpt!=pre[0] ){ for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x; } } length = &buf[SXFMT_BUFSIZ-1]-bufpt; break; case SXFMT_FLOAT: case SXFMT_EXP: case SXFMT_GENERIC: #ifndef SX_OMIT_FLOATINGPOINT realvalue = va_arg(ap,double); if( precision<0 ) precision = 6; /* Set default precision */ if( precision>SXFMT_BUFSIZ-40) precision = SXFMT_BUFSIZ-40; if( realvalue<0.0 ){ realvalue = -realvalue; prefix = '-'; }else{ if( flag_plussign ) prefix = '+'; else if( flag_blanksign ) prefix = ' '; else prefix = 0; } if( infop->type==SXFMT_GENERIC && precision>0 ) precision--; rounder = 0.0; #if 0 /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */ for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); #else /* It makes more sense to use 0.5 */ for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1); #endif if( infop->type==SXFMT_FLOAT ) realvalue += rounder; /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ exp = 0; if( realvalue>0.0 ){ while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } if( exp>350 || exp<-350 ){ bufpt = "NaN"; length = 3; break; } } bufpt = buf; /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ flag_exp = xtype==SXFMT_EXP; if( xtype!=SXFMT_FLOAT ){ realvalue += rounder; if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } } if( xtype==SXFMT_GENERIC ){ flag_rtz = !flag_alternateform; if( exp<-4 || exp>precision ){ xtype = SXFMT_EXP; }else{ precision = precision - exp; xtype = SXFMT_FLOAT; } }else{ flag_rtz = 0; } /* ** The "exp+precision" test causes output to be of type etEXP if ** the precision is too large to fit in buf[]. */ nsd = 0; if( xtype==SXFMT_FLOAT && exp+precision0 || flag_alternateform); if( prefix ) *(bufpt++) = prefix; /* Sign */ if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */ else for(; exp>=0; exp--) *(bufpt++) = (char)getdigit(&realvalue,&nsd); if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */ for(exp++; exp<0 && precision>0; precision--, exp++){ *(bufpt++) = '0'; } while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue,&nsd); *(bufpt--) = 0; /* Null terminate */ if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */ while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0; if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0; } bufpt++; /* point to next free slot */ }else{ /* etEXP or etGENERIC */ flag_dp = (precision>0 || flag_alternateform); if( prefix ) *(bufpt++) = prefix; /* Sign */ *(bufpt++) = (char)getdigit(&realvalue,&nsd); /* First digit */ if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */ while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue,&nsd); bufpt--; /* point to last digit */ if( flag_rtz && flag_dp ){ /* Remove tail zeros */ while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0; if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0; } bufpt++; /* point to next free slot */ if( exp || flag_exp ){ *(bufpt++) = infop->charset[0]; if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */ else { *(bufpt++) = '+'; } if( exp>=100 ){ *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */ exp %= 100; } *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */ *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */ } } /* The converted number is in buf[] and zero terminated.Output it. ** Note that the number is in the usual order, not reversed as with ** integer conversions.*/ length = bufpt-buf; bufpt = buf; /* Special case: Add leading zeros if the flag_zeropad flag is ** set and we are not left justified */ if( flag_zeropad && !flag_leftjustify && length < width){ int i; int nPad = width - length; for(i=width; i>=nPad; i--){ bufpt[i] = bufpt[i-nPad]; } i = prefix!=0; while( nPad-- ) bufpt[i++] = '0'; length = width; } #else bufpt = " "; length = (int)sizeof(" ") - 1; #endif /* SX_OMIT_FLOATINGPOINT */ break; case SXFMT_SIZE:{ int *pSize = va_arg(ap,int *); *pSize = ((SyFmtConsumer *)pUserData)->nLen; length = width = 0; } break; case SXFMT_PERCENT: buf[0] = '%'; bufpt = buf; length = 1; break; case SXFMT_CHARX: c = va_arg(ap,int); buf[0] = (char)c; /* Limit the precision to prevent overflowing buf[] during conversion */ if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40; if( precision>=0 ){ for(idx=1; idx=0 && precisionzString == 0 ){ bufpt = " "; length = (int)sizeof(char); break; } bufpt = (char *)pStr->zString; length = (int)pStr->nByte; break; } case SXFMT_ERROR: buf[0] = '?'; bufpt = buf; length = (int)sizeof(char); if( c==0 ) zFormat--; break; }/* End switch over the format type */ /* ** The text of the conversion is pointed to by "bufpt" and is ** "length" characters long.The field width is "width".Do ** the output. */ if( !flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ while( nspace>=etSPACESIZE ){ rc = xConsumer(spaces,etSPACESIZE,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } nspace -= etSPACESIZE; } if( nspace>0 ){ rc = xConsumer(spaces,(unsigned int)nspace,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } } } } if( length>0 ){ rc = xConsumer(bufpt,(unsigned int)length,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } } if( flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ while( nspace>=etSPACESIZE ){ rc = xConsumer(spaces,etSPACESIZE,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } nspace -= etSPACESIZE; } if( nspace>0 ){ rc = xConsumer(spaces,(unsigned int)nspace,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } } } } }/* End for loop over the format string */ return errorflag ? SXERR_FORMAT : SXRET_OK; } static sxi32 FormatConsumer(const void *pSrc,unsigned int nLen,void *pData) { SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData; sxi32 rc = SXERR_ABORT; switch(pConsumer->nType){ case SXFMT_CONS_PROC: /* User callback */ rc = pConsumer->uConsumer.sFunc.xUserConsumer(pSrc,nLen,pConsumer->uConsumer.sFunc.pUserData); break; case SXFMT_CONS_BLOB: /* Blob consumer */ rc = SyBlobAppend(pConsumer->uConsumer.pBlob,pSrc,(sxu32)nLen); break; default: /* Unknown consumer */ break; } /* Update total number of bytes consumed so far */ pConsumer->nLen += nLen; pConsumer->rc = rc; return rc; } static sxi32 FormatMount(sxi32 nType,void *pConsumer,ProcConsumer xUserCons,void *pUserData,sxu32 *pOutLen,const char *zFormat,va_list ap) { SyFmtConsumer sCons; sCons.nType = nType; sCons.rc = SXRET_OK; sCons.nLen = 0; if( pOutLen ){ *pOutLen = 0; } switch(nType){ case SXFMT_CONS_PROC: #if defined(UNTRUST) if( xUserCons == 0 ){ return SXERR_EMPTY; } #endif sCons.uConsumer.sFunc.xUserConsumer = xUserCons; sCons.uConsumer.sFunc.pUserData = pUserData; break; case SXFMT_CONS_BLOB: sCons.uConsumer.pBlob = (SyBlob *)pConsumer; break; default: return SXERR_UNKNOWN; } InternFormat(FormatConsumer,&sCons,zFormat,ap); if( pOutLen ){ *pOutLen = sCons.nLen; } return sCons.rc; } PH7_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer,void *pData,const char *zFormat,...) { va_list ap; sxi32 rc; #if defined(UNTRUST) if( SX_EMPTY_STR(zFormat) ){ return SXERR_EMPTY; } #endif va_start(ap,zFormat); rc = FormatMount(SXFMT_CONS_PROC,0,xConsumer,pData,0,zFormat,ap); va_end(ap); return rc; } PH7_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob,const char *zFormat,...) { va_list ap; sxu32 n; #if defined(UNTRUST) if( SX_EMPTY_STR(zFormat) ){ return 0; } #endif va_start(ap,zFormat); FormatMount(SXFMT_CONS_BLOB,&(*pBlob),0,0,&n,zFormat,ap); va_end(ap); return n; } PH7_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob,const char *zFormat,va_list ap) { sxu32 n = 0; /* cc warning */ #if defined(UNTRUST) if( SX_EMPTY_STR(zFormat) ){ return 0; } #endif FormatMount(SXFMT_CONS_BLOB,&(*pBlob),0,0,&n,zFormat,ap); return n; } PH7_PRIVATE sxu32 SyBufferFormat(char *zBuf,sxu32 nLen,const char *zFormat,...) { SyBlob sBlob; va_list ap; sxu32 n; #if defined(UNTRUST) if( SX_EMPTY_STR(zFormat) ){ return 0; } #endif if( SXRET_OK != SyBlobInitFromBuf(&sBlob,zBuf,nLen - 1) ){ return 0; } va_start(ap,zFormat); FormatMount(SXFMT_CONS_BLOB,&sBlob,0,0,0,zFormat,ap); va_end(ap); n = SyBlobLength(&sBlob); /* Append the null terminator */ sBlob.mByte++; SyBlobAppend(&sBlob,"\0",sizeof(char)); return n; } #ifndef PH7_DISABLE_BUILTIN_FUNC /* * Symisc XML Parser Engine (UTF-8) SAX(Event Driven) API * @author Mrad Chems Eddine * @started 08/03/2010 21:32 FreeBSD * @finished 07/04/2010 23:24 Win32[VS8] */ /* * An XML raw text,CDATA,tag name is parsed out and stored * in an instance of the following structure. */ typedef struct SyXMLRawStrNS SyXMLRawStrNS; struct SyXMLRawStrNS { /* Public field [Must match the SyXMLRawStr fields ] */ const char *zString; /* Raw text [UTF-8 ENCODED EXCEPT CDATA] [NOT NULL TERMINATED] */ sxu32 nByte; /* Text length */ sxu32 nLine; /* Line number this text occurs */ /* Private fields */ SySet sNSset; /* Namespace entries */ }; /* * Lexer token codes * The following set of constants are the token value recognized * by the lexer when processing XML input. */ #define SXML_TOK_INVALID 0xFFFF /* Invalid Token */ #define SXML_TOK_COMMENT 0x01 /* Comment */ #define SXML_TOK_PI 0x02 /* Processing instruction */ #define SXML_TOK_DOCTYPE 0x04 /* Doctype directive */ #define SXML_TOK_RAW 0x08 /* Raw text */ #define SXML_TOK_START_TAG 0x10 /* Starting tag */ #define SXML_TOK_CDATA 0x20 /* CDATA */ #define SXML_TOK_END_TAG 0x40 /* Ending tag */ #define SXML_TOK_START_END 0x80 /* Tag */ #define SXML_TOK_SPACE 0x100 /* Spaces (including new lines) */ #define IS_XML_DIRTY(c) \ ( c == '<' || c == '$'|| c == '"' || c == '\''|| c == '&'|| c == '(' || c == ')' || c == '*' ||\ c == '%' || c == '#' || c == '|' || c == '/'|| c == '~' || c == '{' || c == '}' ||\ c == '[' || c == ']' || c == '\\'|| c == ';'||c == '^' || c == '`' ) /* Tokenize an entire XML input */ static sxi32 XML_Tokenize(SyStream *pStream,SyToken *pToken,void *pUserData,void *pUnused2) { SyXMLParser *pParse = (SyXMLParser *)pUserData; SyString *pStr; sxi32 rc; int c; /* Jump leading white spaces */ while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){ /* Advance the stream cursor */ if( pStream->zText[0] == '\n' ){ /* Increment line counter */ pStream->nLine++; } pStream->zText++; } if( pStream->zText >= pStream->zEnd ){ SXUNUSED(pUnused2); /* End of input reached */ return SXERR_EOF; } /* Record token starting position and line */ pToken->nLine = pStream->nLine; pToken->pUserData = 0; pStr = &pToken->sData; SyStringInitFromBuf(pStr,pStream->zText,0); /* Extract the current token */ c = pStream->zText[0]; if( c == '<' ){ pStream->zText++; pStr->zString++; if( pStream->zText >= pStream->zEnd ){ if( pParse->xError ){ rc = pParse->xError("Illegal syntax,expecting valid start name character",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* End of input reached */ return SXERR_EOF; } c = pStream->zText[0]; if( c == '?' ){ /* Processing instruction */ pStream->zText++; pStr->zString++; pToken->nType = SXML_TOK_PI; while( XLEX_IN_LEN(pStream) >= sizeof("?>")-1 && SyMemcmp((const void *)pStream->zText,"?>",sizeof("?>")-1) != 0 ){ if( pStream->zText[0] == '\n' ){ /* Increment line counter */ pStream->nLine++; } pStream->zText++; } /* Record token length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); if( XLEX_IN_LEN(pStream) < sizeof("?>")-1 ){ if( pParse->xError ){ rc = pParse->xError("End of input found,but processing instruction was not found",SXML_ERROR_UNCLOSED_TOKEN,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_EOF; } pStream->zText += sizeof("?>")-1; }else if( c == '!' ){ pStream->zText++; if( XLEX_IN_LEN(pStream) >= sizeof("--")-1 && pStream->zText[0] == '-' && pStream->zText[1] == '-' ){ /* Comment */ pStream->zText += sizeof("--") - 1; while( XLEX_IN_LEN(pStream) >= sizeof("-->")-1 && SyMemcmp((const void *)pStream->zText,"-->",sizeof("-->")-1) != 0 ){ if( pStream->zText[0] == '\n' ){ /* Increment line counter */ pStream->nLine++; } pStream->zText++; } pStream->zText += sizeof("-->")-1; /* Tell the lexer to ignore this token */ return SXERR_CONTINUE; } if( XLEX_IN_LEN(pStream) >= sizeof("[CDATA[") - 1 && SyMemcmp((const void *)pStream->zText,"[CDATA[",sizeof("[CDATA[")-1) == 0 ){ /* CDATA */ pStream->zText += sizeof("[CDATA[") - 1; pStr->zString = (const char *)pStream->zText; while( XLEX_IN_LEN(pStream) >= sizeof("]]>")-1 && SyMemcmp((const void *)pStream->zText,"]]>",sizeof("]]>")-1) != 0 ){ if( pStream->zText[0] == '\n' ){ /* Increment line counter */ pStream->nLine++; } pStream->zText++; } /* Record token type and length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); pToken->nType = SXML_TOK_CDATA; if( XLEX_IN_LEN(pStream) < sizeof("]]>")-1 ){ if( pParse->xError ){ rc = pParse->xError("End of input found,but ]]> was not found",SXML_ERROR_UNCLOSED_TOKEN,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_EOF; } pStream->zText += sizeof("]]>")-1; return SXRET_OK; } if( XLEX_IN_LEN(pStream) >= sizeof("DOCTYPE") - 1 && SyMemcmp((const void *)pStream->zText,"DOCTYPE",sizeof("DOCTYPE")-1) == 0 ){ SyString sDelim = { ">" , sizeof(char) }; /* Default delimiter */ int c = 0; /* DOCTYPE */ pStream->zText += sizeof("DOCTYPE") - 1; pStr->zString = (const char *)pStream->zText; /* Check for element declaration */ while( pStream->zText < pStream->zEnd && pStream->zText[0] != '\n' ){ if( pStream->zText[0] >= 0xc0 || !SyisSpace(pStream->zText[0]) ){ c = pStream->zText[0]; if( c == '>' ){ break; } } pStream->zText++; } if( c == '[' ){ /* Change the delimiter */ SyStringInitFromBuf(&sDelim,"]>",sizeof("]>")-1); } if( c != '>' ){ while( XLEX_IN_LEN(pStream) >= sDelim.nByte && SyMemcmp((const void *)pStream->zText,sDelim.zString,sDelim.nByte) != 0 ){ if( pStream->zText[0] == '\n' ){ /* Increment line counter */ pStream->nLine++; } pStream->zText++; } } /* Record token type and length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); pToken->nType = SXML_TOK_DOCTYPE; if( XLEX_IN_LEN(pStream) < sDelim.nByte ){ if( pParse->xError ){ rc = pParse->xError("End of input found,but ]> or > was not found",SXML_ERROR_UNCLOSED_TOKEN,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_EOF; } pStream->zText += sDelim.nByte; return SXRET_OK; } }else{ int c; c = pStream->zText[0]; rc = SXRET_OK; pToken->nType = SXML_TOK_START_TAG; if( c == '/' ){ /* End tag */ pToken->nType = SXML_TOK_END_TAG; pStream->zText++; pStr->zString++; if( pStream->zText >= pStream->zEnd ){ if( pParse->xError ){ rc = pParse->xError("Illegal syntax,expecting valid start name character",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_EOF; } c = pStream->zText[0]; } if( c == '>' ){ /*<>*/ if( pParse->xError ){ rc = pParse->xError("Illegal syntax,expecting valid start name character",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Ignore the token */ return SXERR_CONTINUE; } if( c < 0xc0 && (SyisSpace(c) || SyisDigit(c) || c == '.' || c == '-' ||IS_XML_DIRTY(c) ) ){ if( pParse->xError ){ rc = pParse->xError("Illegal syntax,expecting valid start name character",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } rc = SXERR_INVALID; } pStream->zText++; /* Delimit the tag */ while( pStream->zText < pStream->zEnd && pStream->zText[0] != '>' ){ c = pStream->zText[0]; if( c >= 0xc0 ){ /* UTF-8 stream */ pStream->zText++; SX_JMP_UTF8(pStream->zText,pStream->zEnd); }else{ if( c == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '>' ){ pStream->zText++; if( pToken->nType != SXML_TOK_START_TAG ){ if( pParse->xError ){ rc = pParse->xError("Unexpected closing tag,expecting '>'", SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Ignore the token */ rc = SXERR_INVALID; }else{ pToken->nType = SXML_TOK_START_END; } break; } if( pStream->zText[0] == '\n' ){ /* Increment line counter */ pStream->nLine++; } /* Advance the stream cursor */ pStream->zText++; } } if( rc != SXRET_OK ){ /* Tell the lexer to ignore this token */ return SXERR_CONTINUE; } /* Record token length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); if( pToken->nType == SXML_TOK_START_END && pStr->nByte > 0){ pStr->nByte -= sizeof(char); } if ( pStream->zText < pStream->zEnd ){ pStream->zText++; }else{ if( pParse->xError ){ rc = pParse->xError("End of input found,but closing tag '>' was not found",SXML_ERROR_UNCLOSED_TOKEN,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } } } }else{ /* Raw input */ while( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c < 0xc0 ){ if( c == '<' ){ break; }else if( c == '\n' ){ /* Increment line counter */ pStream->nLine++; } /* Advance the stream cursor */ pStream->zText++; }else{ /* UTF-8 stream */ pStream->zText++; SX_JMP_UTF8(pStream->zText,pStream->zEnd); } } /* Record token type,length */ pToken->nType = SXML_TOK_RAW; pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); } /* Return to the lexer */ return SXRET_OK; } static int XMLCheckDuplicateAttr(SyXMLRawStr *aSet,sxu32 nEntry,SyXMLRawStr *pEntry) { sxu32 n; for( n = 0 ; n < nEntry ; n += 2 ){ SyXMLRawStr *pAttr = &aSet[n]; if( pAttr->nByte == pEntry->nByte && SyMemcmp(pAttr->zString,pEntry->zString,pEntry->nByte) == 0 ){ /* Attribute found */ return 1; } } /* No duplicates */ return 0; } static sxi32 XMLProcessNamesSpace(SyXMLParser *pParse,SyXMLRawStrNS *pTag,SyToken *pToken,SySet *pAttr) { SyXMLRawStr *pPrefix,*pUri; /* Namespace prefix/URI */ SyHashEntry *pEntry; SyXMLRawStr *pDup; sxi32 rc; /* Extract the URI first */ pUri = (SyXMLRawStr *)SySetPeek(pAttr); /* Extract the prefix */ pPrefix = (SyXMLRawStr *)SySetAt(pAttr,SySetUsed(pAttr) - 2); /* Prefix name */ if( pPrefix->nByte == sizeof("xmlns")-1 ){ /* Default namespace */ pPrefix->nByte = 0; pPrefix->zString = ""; /* Empty string */ }else{ pPrefix->nByte -= sizeof("xmlns")-1; pPrefix->zString += sizeof("xmlns")-1; if( pPrefix->zString[0] != ':' ){ return SXRET_OK; } pPrefix->nByte--; pPrefix->zString++; if( pPrefix->nByte < 1 ){ if( pParse->xError ){ rc = pParse->xError("Invalid namespace name",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* POP the last insertred two entries */ (void)SySetPop(pAttr); (void)SySetPop(pAttr); return SXERR_SYNTAX; } } /* Invoke the namespace callback if available */ if( pParse->xNameSpace ){ rc = pParse->xNameSpace(pPrefix,pUri,pParse->pUserData); if( rc == SXERR_ABORT ){ /* User callback request an operation abort */ return SXERR_ABORT; } } /* Duplicate structure */ pDup = (SyXMLRawStr *)SyMemBackendAlloc(pParse->pAllocator,sizeof(SyXMLRawStr)); if( pDup == 0 ){ if( pParse->xError ){ pParse->xError("Out of memory",SXML_ERROR_NO_MEMORY,pToken,pParse->pUserData); } /* Abort processing immediately */ return SXERR_ABORT; } *pDup = *pUri; /* Structure assignement */ /* Save the namespace */ if( pPrefix->nByte == 0 ){ pPrefix->zString = "Default"; pPrefix->nByte = sizeof("Default")-1; } SyHashInsert(&pParse->hns,(const void *)pPrefix->zString,pPrefix->nByte,pDup); /* Peek the last inserted entry */ pEntry = SyHashLastEntry(&pParse->hns); /* Store in the corresponding tag container*/ SySetPut(&pTag->sNSset,(const void *)&pEntry); /* POP the last insertred two entries */ (void)SySetPop(pAttr); (void)SySetPop(pAttr); return SXRET_OK; } static sxi32 XMLProcessStartTag(SyXMLParser *pParse,SyToken *pToken,SyXMLRawStrNS *pTag,SySet *pAttrSet,SySet *pTagStack) { SyString *pIn = &pToken->sData; const char *zIn,*zCur,*zEnd; SyXMLRawStr sEntry; sxi32 rc; int c; /* Reset the working set */ SySetReset(pAttrSet); /* Delimit the raw tag */ zIn = pIn->zString; zEnd = &zIn[pIn->nByte]; while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } /* Isolate tag name */ sEntry.nLine = pTag->nLine = pToken->nLine; zCur = zIn; while( zIn < zEnd ){ if( (unsigned char)zIn[0] >= 0xc0 ){ /* UTF-8 stream */ zIn++; SX_JMP_UTF8(zIn,zEnd); }else if( SyisSpace(zIn[0])){ break; }else{ if( IS_XML_DIRTY(zIn[0]) ){ if( pParse->xError ){ rc = pParse->xError("Illegal character in XML name",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } } zIn++; } } if( zCur >= zIn ){ if( pParse->xError ){ rc = pParse->xError("Invalid XML name",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_SYNTAX; } pTag->zString = zCur; pTag->nByte = (sxu32)(zIn-zCur); /* Process tag attribute */ for(;;){ int is_ns = 0; while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } if( zIn >= zEnd ){ break; } zCur = zIn; while( zIn < zEnd && zIn[0] != '=' ){ if( (unsigned char)zIn[0] >= 0xc0 ){ /* UTF-8 stream */ zIn++; SX_JMP_UTF8(zIn,zEnd); }else if( SyisSpace(zIn[0]) ){ break; }else{ zIn++; } } if( zCur >= zIn ){ if( pParse->xError ){ rc = pParse->xError("Missing attribute name",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_SYNTAX; } /* Store attribute name */ sEntry.zString = zCur; sEntry.nByte = (sxu32)(zIn-zCur); if( (pParse->nFlags & SXML_ENABLE_NAMESPACE) && sEntry.nByte >= sizeof("xmlns") - 1 && SyMemcmp(sEntry.zString,"xmlns",sizeof("xmlns") - 1) == 0 ){ is_ns = 1; } while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } if( zIn >= zEnd || zIn[0] != '=' ){ if( pParse->xError ){ rc = pParse->xError("Missing attribute value",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_SYNTAX; } while( sEntry.nByte > 0 && (unsigned char)zCur[sEntry.nByte - 1] < 0xc0 && SyisSpace(zCur[sEntry.nByte - 1])){ sEntry.nByte--; } /* Check for duplicates first */ if( XMLCheckDuplicateAttr((SyXMLRawStr *)SySetBasePtr(pAttrSet),SySetUsed(pAttrSet),&sEntry) ){ if( pParse->xError ){ rc = pParse->xError("Duplicate attribute",SXML_ERROR_DUPLICATE_ATTRIBUTE,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_SYNTAX; } if( SXRET_OK != SySetPut(pAttrSet,(const void *)&sEntry) ){ return SXERR_ABORT; } /* Extract attribute value */ zIn++; /* Jump the trailing '=' */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } if( zIn >= zEnd ){ if( pParse->xError ){ rc = pParse->xError("Missing attribute value",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } (void)SySetPop(pAttrSet); return SXERR_SYNTAX; } if( zIn[0] != '\'' && zIn[0] != '"' ){ if( pParse->xError ){ rc = pParse->xError("Missing quotes on attribute value",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } (void)SySetPop(pAttrSet); return SXERR_SYNTAX; } c = zIn[0]; zIn++; zCur = zIn; while( zIn < zEnd && zIn[0] != c ){ zIn++; } if( zIn >= zEnd ){ if( pParse->xError ){ rc = pParse->xError("Missing quotes on attribute value",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } (void)SySetPop(pAttrSet); return SXERR_SYNTAX; } /* Store attribute value */ sEntry.zString = zCur; sEntry.nByte = (sxu32)(zIn-zCur); if( SXRET_OK != SySetPut(pAttrSet,(const void *)&sEntry) ){ return SXERR_ABORT; } zIn++; if( is_ns ){ /* Process namespace declaration */ XMLProcessNamesSpace(pParse,pTag,pToken,pAttrSet); } } /* Store in the tag stack */ if( pToken->nType == SXML_TOK_START_TAG ){ rc = SySetPut(pTagStack,(const void *)pTag); } return SXRET_OK; } static void XMLExtactPI(SyToken *pToken,SyXMLRawStr *pTarget,SyXMLRawStr *pData,int *pXML) { SyString *pIn = &pToken->sData; const char *zIn,*zCur,*zEnd; pTarget->nLine = pData->nLine = pToken->nLine; /* Nullify the entries first */ pTarget->zString = pData->zString = 0; /* Ignore leading and traing white spaces */ SyStringFullTrim(pIn); /* Delimit the raw PI */ zIn = pIn->zString; zEnd = &zIn[pIn->nByte]; if( pXML ){ *pXML = 0; } /* Extract the target */ zCur = zIn; while( zIn < zEnd ){ if( (unsigned char)zIn[0] >= 0xc0 ){ /* UTF-8 stream */ zIn++; SX_JMP_UTF8(zIn,zEnd); }else if( SyisSpace(zIn[0])){ break; }else{ zIn++; } } if( zIn > zCur ){ pTarget->zString = zCur; pTarget->nByte = (sxu32)(zIn-zCur); if( pXML && pTarget->nByte == sizeof("xml")-1 && SyStrnicmp(pTarget->zString,"xml",sizeof("xml")-1) == 0 ){ *pXML = 1; } } /* Extract the PI data */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } if( zIn < zEnd ){ pData->zString = zIn; pData->nByte = (sxu32)(zEnd-zIn); } } static sxi32 XMLExtractEndTag(SyXMLParser *pParse,SyToken *pToken,SyXMLRawStrNS *pOut) { SyString *pIn = &pToken->sData; const char *zEnd = &pIn->zString[pIn->nByte]; const char *zIn = pIn->zString; /* Ignore leading white spaces */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } pOut->nLine = pToken->nLine; pOut->zString = zIn; pOut->nByte = (sxu32)(zEnd-zIn); /* Ignore trailing white spaces */ while( pOut->nByte > 0 && (unsigned char)pOut->zString[pOut->nByte - 1] < 0xc0 && SyisSpace(pOut->zString[pOut->nByte - 1]) ){ pOut->nByte--; } if( pOut->nByte < 1 ){ if( pParse->xError ){ sxi32 rc; rc = pParse->xError("Invalid end tag name",SXML_ERROR_INVALID_TOKEN,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_SYNTAX; } return SXRET_OK; } static void TokenToXMLString(SyToken *pTok,SyXMLRawStrNS *pOut) { /* Remove leading and trailing white spaces first */ SyStringFullTrim(&pTok->sData); pOut->zString = SyStringData(&pTok->sData); pOut->nByte = SyStringLength(&pTok->sData); } static sxi32 XMLExtractNS(SyXMLParser *pParse,SyToken *pToken,SyXMLRawStrNS *pTag,SyXMLRawStr *pnsUri) { SyXMLRawStr *pUri,sPrefix; SyHashEntry *pEntry; sxu32 nOfft; sxi32 rc; /* Extract a prefix if available */ rc = SyByteFind(pTag->zString,pTag->nByte,':',&nOfft); if( rc != SXRET_OK ){ /* Check if there is a default namespace */ pEntry = SyHashGet(&pParse->hns,"Default",sizeof("Default")-1); if( pEntry ){ /* Extract the ns URI */ pUri = (SyXMLRawStr *)pEntry->pUserData; /* Save the ns URI */ pnsUri->zString = pUri->zString; pnsUri->nByte = pUri->nByte; } return SXRET_OK; } if( nOfft < 1 ){ if( pParse->xError ){ rc = pParse->xError("Empty prefix is not allowed according to XML namespace specification", SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_SYNTAX; } sPrefix.zString = pTag->zString; sPrefix.nByte = nOfft; sPrefix.nLine = pTag->nLine; pTag->zString += nOfft + 1; pTag->nByte -= nOfft; if( pTag->nByte < 1 ){ if( pParse->xError ){ rc = pParse->xError("Missing tag name",SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_SYNTAX; } /* Check if the prefix is already registered */ pEntry = SyHashGet(&pParse->hns,sPrefix.zString,sPrefix.nByte); if( pEntry == 0 ){ if( pParse->xError ){ rc = pParse->xError("Namespace prefix is not defined",SXML_ERROR_SYNTAX, pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXERR_SYNTAX; } /* Extract the ns URI */ pUri = (SyXMLRawStr *)pEntry->pUserData; /* Save the ns URI */ pnsUri->zString = pUri->zString; pnsUri->nByte = pUri->nByte; /* All done */ return SXRET_OK; } static sxi32 XMLnsUnlink(SyXMLParser *pParse,SyXMLRawStrNS *pLast,SyToken *pToken) { SyHashEntry **apEntry,*pEntry; void *pUserData; sxu32 n; /* Release namespace entries */ apEntry = (SyHashEntry **)SySetBasePtr(&pLast->sNSset); for( n = 0 ; n < SySetUsed(&pLast->sNSset) ; ++n ){ pEntry = apEntry[n]; /* Invoke the end namespace declaration callback */ if( pParse->xNameSpaceEnd && (pParse->nFlags & SXML_ENABLE_NAMESPACE) && pToken ){ SyXMLRawStr sPrefix; sxi32 rc; sPrefix.zString = (const char *)pEntry->pKey; sPrefix.nByte = pEntry->nKeyLen; sPrefix.nLine = pToken->nLine; rc = pParse->xNameSpaceEnd(&sPrefix,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } pUserData = pEntry->pUserData; /* Remove from the namespace hashtable */ SyHashDeleteEntry2(pEntry); SyMemBackendFree(pParse->pAllocator,pUserData); } SySetRelease(&pLast->sNSset); return SXRET_OK; } /* Process XML tokens */ static sxi32 ProcessXML(SyXMLParser *pParse,SySet *pTagStack,SySet *pWorker) { SySet *pTokenSet = &pParse->sToken; SyXMLRawStrNS sEntry; SyXMLRawStr sNs; SyToken *pToken; int bGotTag; sxi32 rc; /* Initialize fields */ bGotTag = 0; /* Start processing */ if( pParse->xStartDoc && (SXERR_ABORT == pParse->xStartDoc(pParse->pUserData)) ){ /* User callback request an operation abort */ return SXERR_ABORT; } /* Reset the loop cursor */ SySetResetCursor(pTokenSet); /* Extract the current token */ while( SXRET_OK == (SySetGetNextEntry(&(*pTokenSet),(void **)&pToken)) ){ SyZero(&sEntry,sizeof(SyXMLRawStrNS)); SyZero(&sNs,sizeof(SyXMLRawStr)); SySetInit(&sEntry.sNSset,pParse->pAllocator,sizeof(SyHashEntry *)); sEntry.nLine = sNs.nLine = pToken->nLine; switch(pToken->nType){ case SXML_TOK_DOCTYPE: if( SySetUsed(pTagStack) > 1 || bGotTag ){ if( pParse->xError ){ rc = pParse->xError("DOCTYPE must be declared first",SXML_ERROR_MISPLACED_XML_PI,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; } /* Invoke the supplied callback if any */ if( pParse->xDoctype ){ TokenToXMLString(pToken,&sEntry); rc = pParse->xDoctype((SyXMLRawStr *)&sEntry,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; case SXML_TOK_CDATA: if( SySetUsed(pTagStack) < 1 ){ if( pParse->xError ){ rc = pParse->xError("CDATA without matching tag",SXML_ERROR_TAG_MISMATCH,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } } /* Invoke the supplied callback if any */ if( pParse->xRaw ){ TokenToXMLString(pToken,&sEntry); rc = pParse->xRaw((SyXMLRawStr *)&sEntry,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; case SXML_TOK_PI:{ SyXMLRawStr sTarget,sData; int isXML = 0; /* Extract the target and data */ XMLExtactPI(pToken,&sTarget,&sData,&isXML); if( isXML && SySetCursor(pTokenSet) - 1 > 0 ){ if( pParse->xError ){ rc = pParse->xError("Unexpected XML declaration. The XML declaration must be the first node in the document", SXML_ERROR_MISPLACED_XML_PI,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } }else if( pParse->xPi ){ /* Invoke the supplied callback*/ rc = pParse->xPi(&sTarget,&sData,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; } case SXML_TOK_RAW: if( SySetUsed(pTagStack) < 1 ){ if( pParse->xError ){ rc = pParse->xError("Text (Raw data) without matching tag",SXML_ERROR_TAG_MISMATCH,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; } /* Invoke the supplied callback if any */ if( pParse->xRaw ){ TokenToXMLString(pToken,&sEntry); rc = pParse->xRaw((SyXMLRawStr *)&sEntry,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; case SXML_TOK_END_TAG:{ SyXMLRawStrNS *pLast = 0; /* cc warning */ if( SySetUsed(pTagStack) < 1 ){ if( pParse->xError ){ rc = pParse->xError("Unexpected closing tag",SXML_ERROR_TAG_MISMATCH,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; } rc = XMLExtractEndTag(pParse,pToken,&sEntry); if( rc == SXRET_OK ){ /* Extract the last inserted entry */ pLast = (SyXMLRawStrNS *)SySetPeek(pTagStack); if( pLast == 0 || pLast->nByte != sEntry.nByte || SyMemcmp(pLast->zString,sEntry.zString,sEntry.nByte) != 0 ){ if( pParse->xError ){ rc = pParse->xError("Unexpected closing tag",SXML_ERROR_TAG_MISMATCH,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } }else{ /* Invoke the supllied callback if any */ if( pParse->xEndTag ){ rc = SXRET_OK; if( pParse->nFlags & SXML_ENABLE_NAMESPACE ){ /* Extract namespace URI */ rc = XMLExtractNS(pParse,pToken,&sEntry,&sNs); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } if( rc == SXRET_OK ){ rc = pParse->xEndTag((SyXMLRawStr *)&sEntry,&sNs,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } } } }else if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( pLast ){ rc = XMLnsUnlink(pParse,pLast,pToken); (void)SySetPop(pTagStack); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; } case SXML_TOK_START_TAG: case SXML_TOK_START_END: if( SySetUsed(pTagStack) < 1 && bGotTag ){ if( pParse->xError ){ rc = pParse->xError("XML document cannot contain multiple root level elements documents", SXML_ERROR_SYNTAX,pToken,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } break; } bGotTag = 1; /* Extract the tag and it's supplied attribute */ rc = XMLProcessStartTag(pParse,pToken,&sEntry,pWorker,pTagStack); if( rc == SXRET_OK ){ if( pParse->nFlags & SXML_ENABLE_NAMESPACE ){ /* Extract namespace URI */ rc = XMLExtractNS(pParse,pToken,&sEntry,&sNs); } } if( rc == SXRET_OK ){ /* Invoke the supplied callback */ if( pParse->xStartTag ){ rc = pParse->xStartTag((SyXMLRawStr *)&sEntry,&sNs,SySetUsed(pWorker), (SyXMLRawStr *)SySetBasePtr(pWorker),pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } if( pToken->nType == SXML_TOK_START_END ){ if ( pParse->xEndTag ){ rc = pParse->xEndTag((SyXMLRawStr *)&sEntry,&sNs,pParse->pUserData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } rc = XMLnsUnlink(pParse,&sEntry,pToken); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } }else if( rc == SXERR_ABORT ){ /* Abort processing immediately */ return SXERR_ABORT; } break; default: /* Can't happen */ break; } } if( SySetUsed(pTagStack) > 0 && pParse->xError){ pParse->xError("Missing closing tag",SXML_ERROR_SYNTAX, (SyToken *)SySetPeek(&pParse->sToken),pParse->pUserData); } if( pParse->xEndDoc ){ pParse->xEndDoc(pParse->pUserData); } return SXRET_OK; } PH7_PRIVATE sxi32 SyXMLParserInit(SyXMLParser *pParser,SyMemBackend *pAllocator,sxi32 iFlags) { /* Zero the structure first */ SyZero(pParser,sizeof(SyXMLParser)); /* Initilaize fields */ SySetInit(&pParser->sToken,pAllocator,sizeof(SyToken)); SyLexInit(&pParser->sLex,&pParser->sToken,XML_Tokenize,pParser); SyHashInit(&pParser->hns,pAllocator,0,0); pParser->pAllocator = pAllocator; pParser->nFlags = iFlags; return SXRET_OK; } PH7_PRIVATE sxi32 SyXMLParserSetEventHandler(SyXMLParser *pParser, void *pUserData, ProcXMLStartTagHandler xStartTag, ProcXMLTextHandler xRaw, ProcXMLSyntaxErrorHandler xErr, ProcXMLStartDocument xStartDoc, ProcXMLEndTagHandler xEndTag, ProcXMLPIHandler xPi, ProcXMLEndDocument xEndDoc, ProcXMLDoctypeHandler xDoctype, ProcXMLNameSpaceStart xNameSpace, ProcXMLNameSpaceEnd xNameSpaceEnd ){ /* Install user callbacks */ if( xErr ){ pParser->xError = xErr; } if( xStartDoc ){ pParser->xStartDoc = xStartDoc; } if( xStartTag ){ pParser->xStartTag = xStartTag; } if( xRaw ){ pParser->xRaw = xRaw; } if( xEndTag ){ pParser->xEndTag = xEndTag; } if( xPi ){ pParser->xPi = xPi; } if( xEndDoc ){ pParser->xEndDoc = xEndDoc; } if( xDoctype ){ pParser->xDoctype = xDoctype; } if( xNameSpace ){ pParser->xNameSpace = xNameSpace; } if( xNameSpaceEnd ){ pParser->xNameSpaceEnd = xNameSpaceEnd; } pParser->pUserData = pUserData; return SXRET_OK; } /* Process an XML chunk */ PH7_PRIVATE sxi32 SyXMLProcess(SyXMLParser *pParser,const char *zInput,sxu32 nByte) { SySet sTagStack; SySet sWorker; sxi32 rc; /* Initialize working sets */ SySetInit(&sWorker,pParser->pAllocator,sizeof(SyXMLRawStr)); /* Tag container */ SySetInit(&sTagStack,pParser->pAllocator,sizeof(SyXMLRawStrNS)); /* Tag stack */ /* Tokenize the entire input */ rc = SyLexTokenizeInput(&pParser->sLex,zInput,nByte,0,0,0); if( rc == SXERR_ABORT ){ /* Tokenize callback request an operation abort */ return SXERR_ABORT; } if( SySetUsed(&pParser->sToken) < 1 ){ /* Nothing to process [i.e: white spaces] */ rc = SXRET_OK; }else{ /* Process XML Tokens */ rc = ProcessXML(&(*pParser),&sTagStack,&sWorker); if( pParser->nFlags & SXML_ENABLE_NAMESPACE ){ if( SySetUsed(&sTagStack) > 0 ){ SyXMLRawStrNS *pEntry; SyHashEntry **apEntry; sxu32 n; SySetResetCursor(&sTagStack); while( SySetGetNextEntry(&sTagStack,(void **)&pEntry) == SXRET_OK ){ /* Release namespace entries */ apEntry = (SyHashEntry **)SySetBasePtr(&pEntry->sNSset); for( n = 0 ; n < SySetUsed(&pEntry->sNSset) ; ++n ){ SyMemBackendFree(pParser->pAllocator,apEntry[n]->pUserData); } SySetRelease(&pEntry->sNSset); } } } } /* Clean-up the mess left behind */ SySetRelease(&sWorker); SySetRelease(&sTagStack); /* Processing result */ return rc; } PH7_PRIVATE sxi32 SyXMLParserRelease(SyXMLParser *pParser) { SyLexRelease(&pParser->sLex); SySetRelease(&pParser->sToken); SyHashRelease(&pParser->hns); return SXRET_OK; } /* * Zip File Format: * * Byte order: Little-endian * * [Local file header + Compressed data [+ Extended local header]?]* * [Central directory]* * [End of central directory record] * * Local file header:* * Offset Length Contents * 0 4 bytes Local file header signature (0x04034b50) * 4 2 bytes Version needed to extract * 6 2 bytes General purpose bit flag * 8 2 bytes Compression method * 10 2 bytes Last mod file time * 12 2 bytes Last mod file date * 14 4 bytes CRC-32 * 18 4 bytes Compressed size (n) * 22 4 bytes Uncompressed size * 26 2 bytes Filename length (f) * 28 2 bytes Extra field length (e) * 30 (f)bytes Filename * (e)bytes Extra field * (n)bytes Compressed data * * Extended local header:* * Offset Length Contents * 0 4 bytes Extended Local file header signature (0x08074b50) * 4 4 bytes CRC-32 * 8 4 bytes Compressed size * 12 4 bytes Uncompressed size * * Extra field:?(if any) * Offset Length Contents * 0 2 bytes Header ID (0x001 until 0xfb4a) see extended appnote from Info-zip * 2 2 bytes Data size (g) * (g) bytes (g) bytes of extra field * * Central directory:* * Offset Length Contents * 0 4 bytes Central file header signature (0x02014b50) * 4 2 bytes Version made by * 6 2 bytes Version needed to extract * 8 2 bytes General purpose bit flag * 10 2 bytes Compression method * 12 2 bytes Last mod file time * 14 2 bytes Last mod file date * 16 4 bytes CRC-32 * 20 4 bytes Compressed size * 24 4 bytes Uncompressed size * 28 2 bytes Filename length (f) * 30 2 bytes Extra field length (e) * 32 2 bytes File comment length (c) * 34 2 bytes Disk number start * 36 2 bytes Internal file attributes * 38 4 bytes External file attributes * 42 4 bytes Relative offset of local header * 46 (f)bytes Filename * (e)bytes Extra field * (c)bytes File comment * * End of central directory record: * Offset Length Contents * 0 4 bytes End of central dir signature (0x06054b50) * 4 2 bytes Number of this disk * 6 2 bytes Number of the disk with the start of the central directory * 8 2 bytes Total number of entries in the central dir on this disk * 10 2 bytes Total number of entries in the central dir * 12 4 bytes Size of the central directory * 16 4 bytes Offset of start of central directory with respect to the starting disk number * 20 2 bytes zipfile comment length (c) * 22 (c)bytes zipfile comment * * compression method: (2 bytes) * 0 - The file is stored (no compression) * 1 - The file is Shrunk * 2 - The file is Reduced with compression factor 1 * 3 - The file is Reduced with compression factor 2 * 4 - The file is Reduced with compression factor 3 * 5 - The file is Reduced with compression factor 4 * 6 - The file is Imploded * 7 - Reserved for Tokenizing compression algorithm * 8 - The file is Deflated */ #define SXMAKE_ZIP_WORKBUF (SXU16_HIGH/2) /* 32KB Initial working buffer size */ #define SXMAKE_ZIP_EXTRACT_VER 0x000a /* Version needed to extract */ #define SXMAKE_ZIP_VER 0x003 /* Version made by */ #define SXZIP_CENTRAL_MAGIC 0x02014b50 #define SXZIP_END_CENTRAL_MAGIC 0x06054b50 #define SXZIP_LOCAL_MAGIC 0x04034b50 /*#define SXZIP_CRC32_START 0xdebb20e3*/ #define SXZIP_LOCAL_HDRSZ 30 /* Local header size */ #define SXZIP_LOCAL_EXT_HDRZ 16 /* Extended local header(footer) size */ #define SXZIP_CENTRAL_HDRSZ 46 /* Central directory header size */ #define SXZIP_END_CENTRAL_HDRSZ 22 /* End of central directory header size */ #define SXARCHIVE_HASH_SIZE 64 /* Starting hash table size(MUST BE POWER OF 2)*/ static sxi32 SyLittleEndianUnpack32(sxu32 *uNB,const unsigned char *buf,sxu32 Len) { if( Len < sizeof(sxu32) ){ return SXERR_SHORT; } *uNB = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); return SXRET_OK; } static sxi32 SyLittleEndianUnpack16(sxu16 *pOut,const unsigned char *zBuf,sxu32 nLen) { if( nLen < sizeof(sxu16) ){ return SXERR_SHORT; } *pOut = zBuf[0] + (zBuf[1] <<8); return SXRET_OK; } static sxi32 SyDosTimeFormat(sxu32 nDosDate,Sytm *pOut) { sxu16 nDate; sxu16 nTime; nDate = nDosDate >> 16; nTime = nDosDate & 0xFFFF; pOut->tm_isdst = 0; pOut->tm_year = 1980 + (nDate >> 9); pOut->tm_mon = (nDate % (1<<9))>>5; pOut->tm_mday = (nDate % (1<<9))&0x1F; pOut->tm_hour = nTime >> 11; pOut->tm_min = (nTime % (1<<11)) >> 5; pOut->tm_sec = ((nTime % (1<<11))& 0x1F )<<1; return SXRET_OK; } /* * Archive hashtable manager */ static sxi32 ArchiveHashGetEntry(SyArchive *pArch,const char *zName,sxu32 nLen,SyArchiveEntry **ppEntry) { SyArchiveEntry *pBucketEntry; SyString sEntry; sxu32 nHash; nHash = pArch->xHash(zName,nLen); pBucketEntry = pArch->apHash[nHash & (pArch->nSize - 1)]; SyStringInitFromBuf(&sEntry,zName,nLen); for(;;){ if( pBucketEntry == 0 ){ break; } if( nHash == pBucketEntry->nHash && pArch->xCmp(&sEntry,&pBucketEntry->sFileName) == 0 ){ if( ppEntry ){ *ppEntry = pBucketEntry; } return SXRET_OK; } pBucketEntry = pBucketEntry->pNextHash; } return SXERR_NOTFOUND; } static void ArchiveHashBucketInstall(SyArchiveEntry **apTable,sxu32 nBucket,SyArchiveEntry *pEntry) { pEntry->pNextHash = apTable[nBucket]; if( apTable[nBucket] != 0 ){ apTable[nBucket]->pPrevHash = pEntry; } apTable[nBucket] = pEntry; } static sxi32 ArchiveHashGrowTable(SyArchive *pArch) { sxu32 nNewSize = pArch->nSize * 2; SyArchiveEntry **apNew; SyArchiveEntry *pEntry; sxu32 n; /* Allocate a new table */ apNew = (SyArchiveEntry **)SyMemBackendAlloc(pArch->pAllocator,nNewSize * sizeof(SyArchiveEntry *)); if( apNew == 0 ){ return SXRET_OK; /* Not so fatal,simply a performance hit */ } SyZero(apNew,nNewSize * sizeof(SyArchiveEntry *)); /* Rehash old entries */ for( n = 0 , pEntry = pArch->pList ; n < pArch->nLoaded ; n++ , pEntry = pEntry->pNext ){ pEntry->pNextHash = pEntry->pPrevHash = 0; ArchiveHashBucketInstall(apNew,pEntry->nHash & (nNewSize - 1),pEntry); } /* Release the old table */ SyMemBackendFree(pArch->pAllocator,pArch->apHash); pArch->apHash = apNew; pArch->nSize = nNewSize; return SXRET_OK; } static sxi32 ArchiveHashInstallEntry(SyArchive *pArch,SyArchiveEntry *pEntry) { if( pArch->nLoaded > pArch->nSize * 3 ){ ArchiveHashGrowTable(&(*pArch)); } pEntry->nHash = pArch->xHash(SyStringData(&pEntry->sFileName),SyStringLength(&pEntry->sFileName)); /* Install the entry in its bucket */ ArchiveHashBucketInstall(pArch->apHash,pEntry->nHash & (pArch->nSize - 1),pEntry); MACRO_LD_PUSH(pArch->pList,pEntry); pArch->nLoaded++; return SXRET_OK; } /* * Parse the End of central directory and report status */ static sxi32 ParseEndOfCentralDirectory(SyArchive *pArch,const unsigned char *zBuf) { sxu32 nMagic = 0; /* cc -O6 warning */ sxi32 rc; /* Sanity check */ rc = SyLittleEndianUnpack32(&nMagic,zBuf,sizeof(sxu32)); if( /* rc != SXRET_OK || */nMagic != SXZIP_END_CENTRAL_MAGIC ){ return SXERR_CORRUPT; } /* # of entries */ rc = SyLittleEndianUnpack16((sxu16 *)&pArch->nEntry,&zBuf[8],sizeof(sxu16)); if( /* rc != SXRET_OK || */ pArch->nEntry > SXI16_HIGH /* SXU16_HIGH */ ){ return SXERR_CORRUPT; } /* Size of central directory */ rc = SyLittleEndianUnpack32(&pArch->nCentralSize,&zBuf[12],sizeof(sxu32)); if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){ return SXERR_CORRUPT; } /* Starting offset of central directory */ rc = SyLittleEndianUnpack32(&pArch->nCentralOfft,&zBuf[16],sizeof(sxu32)); if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){ return SXERR_CORRUPT; } return SXRET_OK; } /* * Fill the zip entry with the appropriate information from the central directory */ static sxi32 GetCentralDirectoryEntry(SyArchive *pArch,SyArchiveEntry *pEntry,const unsigned char *zCentral,sxu32 *pNextOffset) { SyString *pName = &pEntry->sFileName; /* File name */ sxu16 nDosDate,nDosTime; sxu16 nComment = 0 ; sxu32 nMagic = 0; /* cc -O6 warning */ sxi32 rc; nDosDate = nDosTime = 0; /* cc -O6 warning */ SXUNUSED(pArch); /* Sanity check */ rc = SyLittleEndianUnpack32(&nMagic,zCentral,sizeof(sxu32)); if( /* rc != SXRET_OK || */ nMagic != SXZIP_CENTRAL_MAGIC ){ rc = SXERR_CORRUPT; /* * Try to recover by examing the next central directory record. * Dont worry here,there is no risk of an infinite loop since * the buffer size is delimited. */ /* pName->nByte = 0; nComment = 0; pName->nExtra = 0 */ goto update; } /* * entry name length */ SyLittleEndianUnpack16((sxu16 *)&pName->nByte,&zCentral[28],sizeof(sxu16)); if( pName->nByte > SXI16_HIGH /* SXU16_HIGH */){ rc = SXERR_BIG; goto update; } /* Extra information */ SyLittleEndianUnpack16(&pEntry->nExtra,&zCentral[30],sizeof(sxu16)); /* Comment length */ SyLittleEndianUnpack16(&nComment,&zCentral[32],sizeof(sxu16)); /* Compression method 0 == stored / 8 == deflated */ rc = SyLittleEndianUnpack16(&pEntry->nComprMeth,&zCentral[10],sizeof(sxu16)); /* DOS Timestamp */ SyLittleEndianUnpack16(&nDosTime,&zCentral[12],sizeof(sxu16)); SyLittleEndianUnpack16(&nDosDate,&zCentral[14],sizeof(sxu16)); SyDosTimeFormat((nDosDate << 16 | nDosTime),&pEntry->sFmt); /* Little hack to fix month index */ pEntry->sFmt.tm_mon--; /* CRC32 */ rc = SyLittleEndianUnpack32(&pEntry->nCrc,&zCentral[16],sizeof(sxu32)); /* Content size before compression */ rc = SyLittleEndianUnpack32(&pEntry->nByte,&zCentral[24],sizeof(sxu32)); if( pEntry->nByte > SXI32_HIGH ){ rc = SXERR_BIG; goto update; } /* * Content size after compression. * Note that if the file is stored pEntry->nByte should be equal to pEntry->nByteCompr */ rc = SyLittleEndianUnpack32(&pEntry->nByteCompr,&zCentral[20],sizeof(sxu32)); if( pEntry->nByteCompr > SXI32_HIGH ){ rc = SXERR_BIG; goto update; } /* Finally grab the contents offset */ SyLittleEndianUnpack32(&pEntry->nOfft,&zCentral[42],sizeof(sxu32)); if( pEntry->nOfft > SXI32_HIGH ){ rc = SXERR_BIG; goto update; } rc = SXRET_OK; update: /* Update the offset to point to the next central directory record */ *pNextOffset = SXZIP_CENTRAL_HDRSZ + pName->nByte + pEntry->nExtra + nComment; return rc; /* Report failure or success */ } static sxi32 ZipFixOffset(SyArchiveEntry *pEntry,void *pSrc) { sxu16 nExtra,nNameLen; unsigned char *zHdr; nExtra = nNameLen = 0; zHdr = (unsigned char *)pSrc; zHdr = &zHdr[pEntry->nOfft]; if( SyMemcmp(zHdr,"PK\003\004",sizeof(sxu32)) != 0 ){ return SXERR_CORRUPT; } SyLittleEndianUnpack16(&nNameLen,&zHdr[26],sizeof(sxu16)); SyLittleEndianUnpack16(&nExtra,&zHdr[28],sizeof(sxu16)); /* Fix contents offset */ pEntry->nOfft += SXZIP_LOCAL_HDRSZ + nExtra + nNameLen; return SXRET_OK; } /* * Extract all valid entries from the central directory */ static sxi32 ZipExtract(SyArchive *pArch,const unsigned char *zCentral,sxu32 nLen,void *pSrc) { SyArchiveEntry *pEntry,*pDup; const unsigned char *zEnd ; /* End of central directory */ sxu32 nIncr,nOfft; /* Central Offset */ SyString *pName; /* Entry name */ char *zName; sxi32 rc; nOfft = nIncr = 0; zEnd = &zCentral[nLen]; for(;;){ if( &zCentral[nOfft] >= zEnd ){ break; } /* Add a new entry */ pEntry = (SyArchiveEntry *)SyMemBackendPoolAlloc(pArch->pAllocator,sizeof(SyArchiveEntry)); if( pEntry == 0 ){ break; } SyZero(pEntry,sizeof(SyArchiveEntry)); pEntry->nMagic = SXARCH_MAGIC; nIncr = 0; rc = GetCentralDirectoryEntry(&(*pArch),pEntry,&zCentral[nOfft],&nIncr); if( rc == SXRET_OK ){ /* Fix the starting record offset so we can access entry contents correctly */ rc = ZipFixOffset(pEntry,pSrc); } if(rc != SXRET_OK ){ sxu32 nJmp = 0; SyMemBackendPoolFree(pArch->pAllocator,pEntry); /* Try to recover by brute-forcing for a valid central directory record */ if( SXRET_OK == SyBlobSearch((const void *)&zCentral[nOfft + nIncr],(sxu32)(zEnd - &zCentral[nOfft + nIncr]), (const void *)"PK\001\002",sizeof(sxu32),&nJmp)){ nOfft += nIncr + nJmp; /* Check next entry */ continue; } break; /* Giving up,archive is hopelessly corrupted */ } pName = &pEntry->sFileName; pName->zString = (const char *)&zCentral[nOfft + SXZIP_CENTRAL_HDRSZ]; if( pName->nByte <= 0 || ( pEntry->nByte <= 0 && pName->zString[pName->nByte - 1] != '/') ){ /* Ignore zero length records (except folders) and records without names */ SyMemBackendPoolFree(pArch->pAllocator,pEntry); nOfft += nIncr; /* Check next entry */ continue; } zName = SyMemBackendStrDup(pArch->pAllocator,pName->zString,pName->nByte); if( zName == 0 ){ SyMemBackendPoolFree(pArch->pAllocator,pEntry); nOfft += nIncr; /* Check next entry */ continue; } pName->zString = (const char *)zName; /* Check for duplicates */ rc = ArchiveHashGetEntry(&(*pArch),pName->zString,pName->nByte,&pDup); if( rc == SXRET_OK ){ /* Another entry with the same name exists ; link them together */ pEntry->pNextName = pDup->pNextName; pDup->pNextName = pEntry; pDup->nDup++; }else{ /* Insert in hashtable */ ArchiveHashInstallEntry(pArch,pEntry); } nOfft += nIncr; /* Check next record */ } pArch->pCursor = pArch->pList; return pArch->nLoaded > 0 ? SXRET_OK : SXERR_EMPTY; } PH7_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch,const char *zBuf,sxu32 nLen) { const unsigned char *zCentral,*zEnd; sxi32 rc; #if defined(UNTRUST) if( SXARCH_INVALID(pArch) || zBuf == 0 ){ return SXERR_INVALID; } #endif /* The miminal size of a zip archive: * LOCAL_HDR_SZ + CENTRAL_HDR_SZ + END_OF_CENTRAL_HDR_SZ * 30 46 22 */ if( nLen < SXZIP_LOCAL_HDRSZ + SXZIP_CENTRAL_HDRSZ + SXZIP_END_CENTRAL_HDRSZ ){ return SXERR_CORRUPT; /* Don't bother processing return immediately */ } zEnd = (unsigned char *)&zBuf[nLen - SXZIP_END_CENTRAL_HDRSZ]; /* Find the end of central directory */ while( ((sxu32)((unsigned char *)&zBuf[nLen] - zEnd) < (SXZIP_END_CENTRAL_HDRSZ + SXI16_HIGH)) && zEnd > (unsigned char *)zBuf && SyMemcmp(zEnd,"PK\005\006",sizeof(sxu32)) != 0 ){ zEnd--; } /* Parse the end of central directory */ rc = ParseEndOfCentralDirectory(&(*pArch),zEnd); if( rc != SXRET_OK ){ return rc; } /* Find the starting offset of the central directory */ zCentral = &zEnd[-(sxi32)pArch->nCentralSize]; if( zCentral <= (unsigned char *)zBuf || SyMemcmp(zCentral,"PK\001\002",sizeof(sxu32)) != 0 ){ if( pArch->nCentralOfft >= nLen ){ /* Corrupted central directory offset */ return SXERR_CORRUPT; } zCentral = (unsigned char *)&zBuf[pArch->nCentralOfft]; if( SyMemcmp(zCentral,"PK\001\002",sizeof(sxu32)) != 0 ){ /* Corrupted zip archive */ return SXERR_CORRUPT; } /* Fall thru and extract all valid entries from the central directory */ } rc = ZipExtract(&(*pArch),zCentral,(sxu32)(zEnd - zCentral),(void *)zBuf); return rc; } /* * Default comparison function. */ static sxi32 ArchiveHashCmp(const SyString *pStr1,const SyString *pStr2) { sxi32 rc; rc = SyStringCmp(pStr1,pStr2,SyMemcmp); return rc; } PH7_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch,SyMemBackend *pAllocator,ProcHash xHash,ProcRawStrCmp xCmp) { SyArchiveEntry **apHash; #if defined(UNTRUST) if( pArch == 0 ){ return SXERR_EMPTY; } #endif SyZero(pArch,sizeof(SyArchive)); /* Allocate a new hashtable */ apHash = (SyArchiveEntry **)SyMemBackendAlloc(&(*pAllocator),SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *)); if( apHash == 0){ return SXERR_MEM; } SyZero(apHash,SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *)); pArch->apHash = apHash; pArch->xHash = xHash ? xHash : SyBinHash; pArch->xCmp = xCmp ? xCmp : ArchiveHashCmp; pArch->nSize = SXARCHIVE_HASH_SIZE; pArch->pAllocator = &(*pAllocator); pArch->nMagic = SXARCH_MAGIC; return SXRET_OK; } static sxi32 ArchiveReleaseEntry(SyMemBackend *pAllocator,SyArchiveEntry *pEntry) { SyArchiveEntry *pDup = pEntry->pNextName; SyArchiveEntry *pNextDup; /* Release duplicates first since there are not stored in the hashtable */ for(;;){ if( pEntry->nDup == 0 ){ break; } pNextDup = pDup->pNextName; pDup->nMagic = 0x2661; SyMemBackendFree(pAllocator,(void *)SyStringData(&pDup->sFileName)); SyMemBackendPoolFree(pAllocator,pDup); pDup = pNextDup; pEntry->nDup--; } pEntry->nMagic = 0x2661; SyMemBackendFree(pAllocator,(void *)SyStringData(&pEntry->sFileName)); SyMemBackendPoolFree(pAllocator,pEntry); return SXRET_OK; } PH7_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch) { SyArchiveEntry *pEntry,*pNext; pEntry = pArch->pList; for(;;){ if( pArch->nLoaded < 1 ){ break; } pNext = pEntry->pNext; MACRO_LD_REMOVE(pArch->pList,pEntry); ArchiveReleaseEntry(pArch->pAllocator,pEntry); pEntry = pNext; pArch->nLoaded--; } SyMemBackendFree(pArch->pAllocator,pArch->apHash); pArch->pCursor = 0; pArch->nMagic = 0x2626; return SXRET_OK; } PH7_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch) { pArch->pCursor = pArch->pList; return SXRET_OK; } PH7_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch,SyArchiveEntry **ppEntry) { SyArchiveEntry *pNext; if( pArch->pCursor == 0 ){ /* Rewind the cursor */ pArch->pCursor = pArch->pList; return SXERR_EOF; } *ppEntry = pArch->pCursor; pNext = pArch->pCursor->pNext; /* Advance the cursor to the next entry */ pArch->pCursor = pNext; return SXRET_OK; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * Psuedo Random Number Generator (PRNG) * @authors: SQLite authors * @status: Public Domain * NOTE: * Nothing in this file or anywhere else in the library does any kind of * encryption.The RC4 algorithm is being used as a PRNG (pseudo-random * number generator) not as an encryption device. */ #define SXPRNG_MAGIC 0x13C4 #ifdef __UNIXES__ #include #include #include #include #include #include #include #endif static sxi32 SyOSUtilRandomSeed(void *pBuf,sxu32 nLen,void *pUnused) { char *zBuf = (char *)pBuf; #ifdef __WINNT__ DWORD nProcessID; /* Yes,keep it uninitialized when compiling using the MinGW32 builds tools */ #elif defined(__UNIXES__) pid_t pid; int fd; #else char zGarbage[128]; /* Yes,keep this buffer uninitialized */ #endif SXUNUSED(pUnused); #ifdef __WINNT__ #ifndef __MINGW32__ nProcessID = GetProcessId(GetCurrentProcess()); #endif SyMemcpy((const void *)&nProcessID,zBuf,SXMIN(nLen,sizeof(DWORD))); if( (sxu32)(&zBuf[nLen] - &zBuf[sizeof(DWORD)]) >= sizeof(SYSTEMTIME) ){ GetSystemTime((LPSYSTEMTIME)&zBuf[sizeof(DWORD)]); } #elif defined(__UNIXES__) fd = open("/dev/urandom",O_RDONLY); if (fd >= 0 ){ if( read(fd,zBuf,nLen) > 0 ){ close(fd); return SXRET_OK; } /* FALL THRU */ } close(fd); pid = getpid(); SyMemcpy((const void *)&pid,zBuf,SXMIN(nLen,sizeof(pid_t))); if( &zBuf[nLen] - &zBuf[sizeof(pid_t)] >= (int)sizeof(struct timeval) ){ gettimeofday((struct timeval *)&zBuf[sizeof(pid_t)],0); } #else /* Fill with uninitialized data */ SyMemcpy(zGarbage,zBuf,SXMIN(nLen,sizeof(zGarbage))); #endif return SXRET_OK; } PH7_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx,ProcRandomSeed xSeed,void * pUserData) { char zSeed[256]; sxu8 t; sxi32 rc; sxu32 i; if( pCtx->nMagic == SXPRNG_MAGIC ){ return SXRET_OK; /* Already initialized */ } /* Initialize the state of the random number generator once, ** the first time this routine is called.The seed value does ** not need to contain a lot of randomness since we are not ** trying to do secure encryption or anything like that... */ if( xSeed == 0 ){ xSeed = SyOSUtilRandomSeed; } rc = xSeed(zSeed,sizeof(zSeed),pUserData); if( rc != SXRET_OK ){ return rc; } pCtx->i = pCtx->j = 0; for(i=0; i < SX_ARRAYSIZE(pCtx->s) ; i++){ pCtx->s[i] = (unsigned char)i; } for(i=0; i < sizeof(zSeed) ; i++){ pCtx->j += pCtx->s[i] + zSeed[i]; t = pCtx->s[pCtx->j]; pCtx->s[pCtx->j] = pCtx->s[i]; pCtx->s[i] = t; } pCtx->nMagic = SXPRNG_MAGIC; return SXRET_OK; } /* * Get a single 8-bit random value using the RC4 PRNG. */ static sxu8 randomByte(SyPRNGCtx *pCtx) { sxu8 t; /* Generate and return single random byte */ pCtx->i++; t = pCtx->s[pCtx->i]; pCtx->j += t; pCtx->s[pCtx->i] = pCtx->s[pCtx->j]; pCtx->s[pCtx->j] = t; t += pCtx->s[pCtx->i]; return pCtx->s[t]; } PH7_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx,void *pBuf,sxu32 nLen) { unsigned char *zBuf = (unsigned char *)pBuf; unsigned char *zEnd = &zBuf[nLen]; #if defined(UNTRUST) if( pCtx == 0 || pBuf == 0 || nLen <= 0 ){ return SXERR_EMPTY; } #endif if(pCtx->nMagic != SXPRNG_MAGIC ){ return SXERR_CORRUPT; } for(;;){ if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++; } return SXRET_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC #ifndef PH7_DISABLE_HASH_FUNC /* SyRunTimeApi: sxhash.c */ /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest.This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #define SX_MD5_BINSZ 16 #define SX_MD5_HEXSZ 32 /* * Note: this code is harmless on little-endian machines. */ static void byteReverse (unsigned char *buf, unsigned longs) { sxu32 t; do { t = (sxu32)((unsigned)buf[3]<<8 | buf[2]) << 16 | ((unsigned)buf[1]<<8 | buf[0]); *(sxu32*)buf = t; buf += 4; } while (--longs); } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #ifdef F1 #undef F1 #endif #ifdef F2 #undef F2 #endif #ifdef F3 #undef F3 #endif #ifdef F4 #undef F4 #endif #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm.*/ #define SX_MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data.MD5Update blocks * the data and converts bytes into longwords for this routine. */ static void MD5Transform(sxu32 buf[4], const sxu32 in[16]) { register sxu32 a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; SX_MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); SX_MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); SX_MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); SX_MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); SX_MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); SX_MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); SX_MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); SX_MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); SX_MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); SX_MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); SX_MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); SX_MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); SX_MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); SX_MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); SX_MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); SX_MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); SX_MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); SX_MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); SX_MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); SX_MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); SX_MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); SX_MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); SX_MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); SX_MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); SX_MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); SX_MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); SX_MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); SX_MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); SX_MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); SX_MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); SX_MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); SX_MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); SX_MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); SX_MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); SX_MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); SX_MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); SX_MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); SX_MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); SX_MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); SX_MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); SX_MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); SX_MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); SX_MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); SX_MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); SX_MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); SX_MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); SX_MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); SX_MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); SX_MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); SX_MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); SX_MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); SX_MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); SX_MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); SX_MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); SX_MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); SX_MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); SX_MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); SX_MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); SX_MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); SX_MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); SX_MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); SX_MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); SX_MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); SX_MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ PH7_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len) { sxu32 t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((sxu32)len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if ( t ) { unsigned char *p = (unsigned char *)ctx->in + t; t = 64-t; if (len < t) { SyMemcpy(buf,p,len); return; } SyMemcpy(buf,p,t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (sxu32*)ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { SyMemcpy(buf,ctx->in,64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (sxu32*)ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data.*/ SyMemcpy(buf,ctx->in,len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ PH7_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx){ unsigned count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80.This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ SyZero(p,count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (sxu32*)ctx->in); /* Now fill the next block with 56 bytes */ SyZero(ctx->in,56); } else { /* Pad block to 56 bytes */ SyZero(p,count-8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ ((sxu32*)ctx->in)[ 14 ] = ctx->bits[0]; ((sxu32*)ctx->in)[ 15 ] = ctx->bits[1]; MD5Transform(ctx->buf, (sxu32*)ctx->in); byteReverse((unsigned char *)ctx->buf, 4); SyMemcpy(ctx->buf,digest,0x10); SyZero(ctx,sizeof(ctx)); /* In case it's sensitive */ } #undef F1 #undef F2 #undef F3 #undef F4 PH7_PRIVATE sxi32 MD5Init(MD5Context *pCtx) { pCtx->buf[0] = 0x67452301; pCtx->buf[1] = 0xefcdab89; pCtx->buf[2] = 0x98badcfe; pCtx->buf[3] = 0x10325476; pCtx->bits[0] = 0; pCtx->bits[1] = 0; return SXRET_OK; } PH7_PRIVATE sxi32 SyMD5Compute(const void *pIn,sxu32 nLen,unsigned char zDigest[16]) { MD5Context sCtx; MD5Init(&sCtx); MD5Update(&sCtx,(const unsigned char *)pIn,nLen); MD5Final(zDigest,&sCtx); return SXRET_OK; } /* * SHA-1 in C * By Steve Reid * Status: Public Domain */ /* * blk0() and blk() perform the initial expand. * I got the idea of expanding during the round function from SSLeay * * blk0le() for little-endian and blk0be() for big-endian. */ #if __GNUC__ && (defined(__i386__) || defined(__x86_64__)) /* * GCC by itself only generates left rotates. Use right rotates if * possible to be kinder to dinky implementations with iterative rotate * instructions. */ #define SHA_ROT(op, x, k) \ ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; }) #define rol(x,k) SHA_ROT("roll", x, k) #define ror(x,k) SHA_ROT("rorl", x, k) #else /* Generic C equivalent */ #define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) #define rol(x,k) SHA_ROT(x,k,32-(k)) #define ror(x,k) SHA_ROT(x,32-(k),k) #endif #define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ |(rol(block[i],8)&0x00FF00FF)) #define blk0be(i) block[i] #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ ^block[(i+2)&15]^block[i&15],1)) /* * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 * * Rl0() for little-endian and Rb0() for big-endian. Endianness is * determined at run-time. */ #define Rl0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); #define Rb0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R1(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R2(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); #define R3(v,w,x,y,z,i) \ z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); #define R4(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); /* * Hash a single 512-bit block. This is the core of the algorithm. */ #define a qq[0] #define b qq[1] #define c qq[2] #define d qq[3] #define e qq[4] static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]) { unsigned int qq[5]; /* a, b, c, d, e; */ static int one = 1; unsigned int block[16]; SyMemcpy(buffer,(void *)block,64); SyMemcpy(state,qq,5*sizeof(unsigned int)); /* Copy context->state[] to working vars */ /* a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; */ /* 4 rounds of 20 operations each. Loop unrolled. */ if( 1 == *(unsigned char*)&one ){ Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); }else{ Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); } R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } #undef a #undef b #undef c #undef d #undef e /* * SHA1Init - Initialize new context */ PH7_PRIVATE void SHA1Init(SHA1Context *context){ /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* * Run your data through this. */ PH7_PRIVATE void SHA1Update(SHA1Context *context,const unsigned char *data,unsigned int len){ unsigned int i, j; j = context->count[0]; if ((context->count[0] += len << 3) < j) context->count[1] += (len>>29)+1; j = (j >> 3) & 63; if ((j + len) > 63) { (void)SyMemcpy(data,&context->buffer[j], (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) SHA1Transform(context->state, &data[i]); j = 0; } else { i = 0; } (void)SyMemcpy(&data[i],&context->buffer[j],len - i); } /* * Add padding and return the message digest. */ PH7_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]){ unsigned int i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1Update(context, (const unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) SHA1Update(context, (const unsigned char *)"\0", 1); SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ if (digest) { for (i = 0; i < 20; i++) digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } } #undef Rl0 #undef Rb0 #undef R1 #undef R2 #undef R3 #undef R4 PH7_PRIVATE sxi32 SySha1Compute(const void *pIn,sxu32 nLen,unsigned char zDigest[20]) { SHA1Context sCtx; SHA1Init(&sCtx); SHA1Update(&sCtx,(const unsigned char *)pIn,nLen); SHA1Final(&sCtx,zDigest); return SXRET_OK; } static const sxu32 crc32_table[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, }; #define CRC32C(c,d) (c = ( crc32_table[(c ^ (d)) & 0xFF] ^ (c>>8) ) ) static sxu32 SyCrc32Update(sxu32 crc32,const void *pSrc,sxu32 nLen) { register unsigned char *zIn = (unsigned char *)pSrc; unsigned char *zEnd; if( zIn == 0 ){ return crc32; } zEnd = &zIn[nLen]; for(;;){ if(zIn >= zEnd ){ break; } CRC32C(crc32,zIn[0]); zIn++; if(zIn >= zEnd ){ break; } CRC32C(crc32,zIn[0]); zIn++; if(zIn >= zEnd ){ break; } CRC32C(crc32,zIn[0]); zIn++; if(zIn >= zEnd ){ break; } CRC32C(crc32,zIn[0]); zIn++; } return crc32; } PH7_PRIVATE sxu32 SyCrc32(const void *pSrc,sxu32 nLen) { return SyCrc32Update(SXU32_HIGH,pSrc,nLen); } #endif /* PH7_DISABLE_HASH_FUNC */ #endif /* PH7_DISABLE_BUILTIN_FUNC */ #ifndef PH7_DISABLE_BUILTIN_FUNC PH7_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn,sxu32 nLen,ProcConsumer xConsumer,void *pConsumerData) { static const unsigned char zHexTab[] = "0123456789abcdef"; const unsigned char *zIn,*zEnd; unsigned char zOut[3]; sxi32 rc; #if defined(UNTRUST) if( pIn == 0 || xConsumer == 0 ){ return SXERR_EMPTY; } #endif zIn = (const unsigned char *)pIn; zEnd = &zIn[nLen]; for(;;){ if( zIn >= zEnd ){ break; } zOut[0] = zHexTab[zIn[0] >> 4]; zOut[1] = zHexTab[zIn[0] & 0x0F]; rc = xConsumer((const void *)zOut,sizeof(char)*2,pConsumerData); if( rc != SXRET_OK ){ return rc; } zIn++; } return SXRET_OK; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * ---------------------------------------------------------- * File: lex.c * MD5: c218c13068ed53acb1154762f9e6fd13 * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: lex.c v2.8 Ubuntu-linux 2012-07-13 01:21 stable $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* * This file implement an efficient hand-coded,thread-safe and full-reentrant * lexical analyzer/Tokenizer for the PH7 engine. */ /* Forward declaration */ static sxu32 KeywordCode(const char *z, int n); static sxi32 LexExtractHeredoc(SyStream *pStream,SyToken *pToken); /* * Tokenize a raw PHP input. * Get a single low-level token from the input file. Update the stream pointer so that * it points to the first character beyond the extracted token. */ static sxi32 TokenizePHP(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData) { SyString *pStr; sxi32 rc; /* Ignore leading white spaces */ while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){ /* Advance the stream cursor */ if( pStream->zText[0] == '\n' ){ /* Update line counter */ pStream->nLine++; } pStream->zText++; } if( pStream->zText >= pStream->zEnd ){ /* End of input reached */ return SXERR_EOF; } /* Record token starting position and line */ pToken->nLine = pStream->nLine; pToken->pUserData = 0; pStr = &pToken->sData; SyStringInitFromBuf(pStr,pStream->zText,0); if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){ /* The following code fragment is taken verbatim from the xPP source tree. * xPP is a modern embeddable macro processor with advanced features useful for * application seeking for a production quality,ready to use macro processor. * xPP is a widely used library developed and maintened by Symisc Systems. * You can reach the xPP home page by following this link: * http://xpp.symisc.net/ */ const unsigned char *zIn; sxu32 nKeyword; /* Isolate UTF-8 or alphanumeric stream */ if( pStream->zText[0] < 0xc0 ){ pStream->zText++; } for(;;){ zIn = pStream->zText; if( zIn[0] >= 0xc0 ){ zIn++; /* UTF-8 stream */ while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){ zIn++; } } /* Skip alphanumeric stream */ while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){ zIn++; } if( zIn == pStream->zText ){ /* Not an UTF-8 or alphanumeric stream */ break; } /* Synchronize pointers */ pStream->zText = zIn; } /* Record token length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); nKeyword = KeywordCode(pStr->zString,(int)pStr->nByte); if( nKeyword != PH7_TK_ID ){ if( nKeyword & (PH7_TKWRD_NEW|PH7_TKWRD_CLONE|PH7_TKWRD_AND|PH7_TKWRD_XOR|PH7_TKWRD_OR|PH7_TKWRD_INSTANCEOF|PH7_TKWRD_SEQ|PH7_TKWRD_SNE) ){ /* Alpha stream operators [i.e: new,clone,and,instanceof,eq,ne,or,xor],save the operator instance for later processing */ pToken->pUserData = (void *)PH7_ExprExtractOperator(pStr,0); /* Mark as an operator */ pToken->nType = PH7_TK_ID|PH7_TK_OP; }else{ /* We are dealing with a keyword [i.e: while,foreach,class...],save the keyword ID */ pToken->nType = PH7_TK_KEYWORD; pToken->pUserData = SX_INT_TO_PTR(nKeyword); } }else{ /* A simple identifier */ pToken->nType = PH7_TK_ID; } }else{ sxi32 c; /* Non-alpha stream */ if( pStream->zText[0] == '#' || ( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '/') ){ pStream->zText++; /* Inline comments */ while( pStream->zText < pStream->zEnd && pStream->zText[0] != '\n' ){ pStream->zText++; } /* Tell the upper-layer to ignore this token */ return SXERR_CONTINUE; }else if( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '*' ){ pStream->zText += 2; /* Block comment */ while( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '*' ){ if( &pStream->zText[1] >= pStream->zEnd || pStream->zText[1] == '/' ){ break; } } if( pStream->zText[0] == '\n' ){ pStream->nLine++; } pStream->zText++; } pStream->zText += 2; /* Tell the upper-layer to ignore this token */ return SXERR_CONTINUE; }else if( SyisDigit(pStream->zText[0]) ){ pStream->zText++; /* Decimal digit stream */ while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } /* Mark the token as integer until we encounter a real number */ pToken->nType = PH7_TK_INTEGER; if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c == '.' ){ /* Real number */ pStream->zText++; while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( c=='e' || c=='E' ){ pStream->zText++; if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){ pStream->zText++; } while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } } } } pToken->nType = PH7_TK_REAL; }else if( c=='e' || c=='E' ){ SXUNUSED(pUserData); /* Prevent compiler warning */ SXUNUSED(pCtxData); pStream->zText++; if( pStream->zText < pStream->zEnd ){ c = pStream->zText[0]; if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){ pStream->zText++; } while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ pStream->zText++; } } pToken->nType = PH7_TK_REAL; }else if( c == 'x' || c == 'X' ){ /* Hex digit stream */ pStream->zText++; while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisHex(pStream->zText[0]) ){ pStream->zText++; } }else if(c == 'b' || c == 'B' ){ /* Binary digit stream */ pStream->zText++; while( pStream->zText < pStream->zEnd && (pStream->zText[0] == '0' || pStream->zText[0] == '1') ){ pStream->zText++; } } } /* Record token length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); return SXRET_OK; } c = pStream->zText[0]; pStream->zText++; /* Advance the stream cursor */ /* Assume we are dealing with an operator*/ pToken->nType = PH7_TK_OP; switch(c){ case '$': pToken->nType = PH7_TK_DOLLAR; break; case '{': pToken->nType = PH7_TK_OCB; break; case '}': pToken->nType = PH7_TK_CCB; break; case '(': pToken->nType = PH7_TK_LPAREN; break; case '[': pToken->nType |= PH7_TK_OSB; break; /* Bitwise operation here,since the square bracket token '[' * is a potential operator [i.e: subscripting] */ case ']': pToken->nType = PH7_TK_CSB; break; case ')': { SySet *pTokSet = pStream->pSet; /* Assemble type cast operators [i.e: (int),(float),(bool)...] */ if( pTokSet->nUsed >= 2 ){ SyToken *pTmp; /* Peek the last recongnized token */ pTmp = (SyToken *)SySetPeek(pTokSet); if( pTmp->nType & PH7_TK_KEYWORD ){ sxi32 nID = SX_PTR_TO_INT(pTmp->pUserData); if( (sxu32)nID & (PH7_TKWRD_ARRAY|PH7_TKWRD_INT|PH7_TKWRD_FLOAT|PH7_TKWRD_STRING|PH7_TKWRD_OBJECT|PH7_TKWRD_BOOL|PH7_TKWRD_UNSET) ){ pTmp = (SyToken *)SySetAt(pTokSet,pTokSet->nUsed - 2); if( pTmp->nType & PH7_TK_LPAREN ){ /* Merge the three tokens '(' 'TYPE' ')' into a single one */ const char * zTypeCast = "(int)"; if( nID & PH7_TKWRD_FLOAT ){ zTypeCast = "(float)"; }else if( nID & PH7_TKWRD_BOOL ){ zTypeCast = "(bool)"; }else if( nID & PH7_TKWRD_STRING ){ zTypeCast = "(string)"; }else if( nID & PH7_TKWRD_ARRAY ){ zTypeCast = "(array)"; }else if( nID & PH7_TKWRD_OBJECT ){ zTypeCast = "(object)"; }else if( nID & PH7_TKWRD_UNSET ){ zTypeCast = "(unset)"; } /* Reflect the change */ pToken->nType = PH7_TK_OP; SyStringInitFromBuf(&pToken->sData,zTypeCast,SyStrlen(zTypeCast)); /* Save the instance associated with the type cast operator */ pToken->pUserData = (void *)PH7_ExprExtractOperator(&pToken->sData,0); /* Remove the two previous tokens */ pTokSet->nUsed -= 2; return SXRET_OK; } } } } pToken->nType = PH7_TK_RPAREN; break; } case '\'':{ /* Single quoted string */ pStr->zString++; while( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '\'' ){ if( pStream->zText[-1] != '\\' ){ break; }else{ const unsigned char *zPtr = &pStream->zText[-2]; sxi32 i = 1; while( zPtr > pStream->zInput && zPtr[0] == '\\' ){ zPtr--; i++; } if((i&1)==0){ break; } } } if( pStream->zText[0] == '\n' ){ pStream->nLine++; } pStream->zText++; } /* Record token length and type */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); pToken->nType = PH7_TK_SSTR; /* Jump the trailing single quote */ pStream->zText++; return SXRET_OK; } case '"':{ sxi32 iNest; /* Double quoted string */ pStr->zString++; while( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '{' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '$'){ iNest = 1; pStream->zText++; /* TICKET 1433-40: Hnadle braces'{}' in double quoted string where everything is allowed */ while(pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '{' ){ iNest++; }else if (pStream->zText[0] == '}' ){ iNest--; if( iNest <= 0 ){ pStream->zText++; break; } }else if( pStream->zText[0] == '\n' ){ pStream->nLine++; } pStream->zText++; } if( pStream->zText >= pStream->zEnd ){ break; } } if( pStream->zText[0] == '"' ){ if( pStream->zText[-1] != '\\' ){ break; }else{ const unsigned char *zPtr = &pStream->zText[-2]; sxi32 i = 1; while( zPtr > pStream->zInput && zPtr[0] == '\\' ){ zPtr--; i++; } if((i&1)==0){ break; } } } if( pStream->zText[0] == '\n' ){ pStream->nLine++; } pStream->zText++; } /* Record token length and type */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); pToken->nType = PH7_TK_DSTR; /* Jump the trailing quote */ pStream->zText++; return SXRET_OK; } case '`':{ /* Backtick quoted string */ pStr->zString++; while( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '`' && pStream->zText[-1] != '\\' ){ break; } if( pStream->zText[0] == '\n' ){ pStream->nLine++; } pStream->zText++; } /* Record token length and type */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); pToken->nType = PH7_TK_BSTR; /* Jump the trailing backtick */ pStream->zText++; return SXRET_OK; } case '\\': pToken->nType = PH7_TK_NSSEP; break; case ':': if( pStream->zText < pStream->zEnd && pStream->zText[0] == ':' ){ /* Current operator: '::' */ pStream->zText++; }else{ pToken->nType = PH7_TK_COLON; /* Single colon */ } break; case ',': pToken->nType |= PH7_TK_COMMA; break; /* Comma is also an operator */ case ';': pToken->nType = PH7_TK_SEMI; break; /* Handle combined operators [i.e: +=,===,!=== ...] */ case '=': pToken->nType |= PH7_TK_EQUAL; if( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '=' ){ pToken->nType &= ~PH7_TK_EQUAL; /* Current operator: == */ pStream->zText++; if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: === */ pStream->zText++; } }else if( pStream->zText[0] == '>' ){ /* Array operator: => */ pToken->nType = PH7_TK_ARRAY_OP; pStream->zText++; }else{ /* TICKET 1433-0010: Reference operator '=&' */ const unsigned char *zCur = pStream->zText; sxu32 nLine = 0; while( zCur < pStream->zEnd && zCur[0] < 0xc0 && SyisSpace(zCur[0]) ){ if( zCur[0] == '\n' ){ nLine++; } zCur++; } if( zCur < pStream->zEnd && zCur[0] == '&' ){ /* Current operator: =& */ pToken->nType &= ~PH7_TK_EQUAL; SyStringInitFromBuf(pStr,"=&",sizeof("=&")-1); /* Update token stream */ pStream->zText = &zCur[1]; pStream->nLine += nLine; } } } break; case '!': if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: != */ pStream->zText++; if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: !== */ pStream->zText++; } } break; case '&': pToken->nType |= PH7_TK_AMPER; if( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '&' ){ pToken->nType &= ~PH7_TK_AMPER; /* Current operator: && */ pStream->zText++; }else if( pStream->zText[0] == '=' ){ pToken->nType &= ~PH7_TK_AMPER; /* Current operator: &= */ pStream->zText++; } } break; case '|': if( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '|' ){ /* Current operator: || */ pStream->zText++; }else if( pStream->zText[0] == '=' ){ /* Current operator: |= */ pStream->zText++; } } break; case '+': if( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '+' ){ /* Current operator: ++ */ pStream->zText++; }else if( pStream->zText[0] == '=' ){ /* Current operator: += */ pStream->zText++; } } break; case '-': if( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '-' ){ /* Current operator: -- */ pStream->zText++; }else if( pStream->zText[0] == '=' ){ /* Current operator: -= */ pStream->zText++; }else if( pStream->zText[0] == '>' ){ /* Current operator: -> */ pStream->zText++; } } break; case '*': if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: *= */ pStream->zText++; } break; case '/': if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: /= */ pStream->zText++; } break; case '%': if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: %= */ pStream->zText++; } break; case '^': if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: ^= */ pStream->zText++; } break; case '.': if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: .= */ pStream->zText++; } break; case '<': if( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '<' ){ /* Current operator: << */ pStream->zText++; if( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '=' ){ /* Current operator: <<= */ pStream->zText++; }else if( pStream->zText[0] == '<' ){ /* Current Token: <<< */ pStream->zText++; /* This may be the beginning of a Heredoc/Nowdoc string,try to delimit it */ rc = LexExtractHeredoc(&(*pStream),&(*pToken)); if( rc == SXRET_OK ){ /* Here/Now doc successfuly extracted */ return SXRET_OK; } } } }else if( pStream->zText[0] == '>' ){ /* Current operator: <> */ pStream->zText++; }else if( pStream->zText[0] == '=' ){ /* Current operator: <= */ pStream->zText++; } } break; case '>': if( pStream->zText < pStream->zEnd ){ if( pStream->zText[0] == '>' ){ /* Current operator: >> */ pStream->zText++; if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){ /* Current operator: >>= */ pStream->zText++; } }else if( pStream->zText[0] == '=' ){ /* Current operator: >= */ pStream->zText++; } } break; default: break; } if( pStr->nByte <= 0 ){ /* Record token length */ pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); } if( pToken->nType & PH7_TK_OP ){ const ph7_expr_op *pOp; /* Check if the extracted token is an operator */ pOp = PH7_ExprExtractOperator(pStr,(SyToken *)SySetPeek(pStream->pSet)); if( pOp == 0 ){ /* Not an operator */ pToken->nType &= ~PH7_TK_OP; if( pToken->nType <= 0 ){ pToken->nType = PH7_TK_OTHER; } }else{ /* Save the instance associated with this operator for later processing */ pToken->pUserData = (void *)pOp; } } } /* Tell the upper-layer to save the extracted token for later processing */ return SXRET_OK; } /***** This file contains automatically generated code ****** ** ** The code in this file has been automatically generated by ** ** $Header: /sqlite/sqlite/tool/mkkeywordhash.c ** ** Sligthly modified by Chems mrad for the PH7 engine. ** ** The code in this file implements a function that determines whether ** or not a given identifier is really a PHP keyword. The same thing ** might be implemented more directly using a hand-written hash table. ** But by using this automatically generated code, the size of the code ** is substantially reduced. This is important for embedded applications ** on platforms with limited memory. */ /* Hash score: 103 */ static sxu32 KeywordCode(const char *z, int n){ /* zText[] encodes 532 bytes of keywords in 333 bytes */ /* extendswitchprintegerequire_oncenddeclareturnamespacechobject */ /* hrowbooleandefaultrycaselfinalistaticlonewconstringlobaluse */ /* lseifloatvarrayANDIEchoUSECHOabstractclasscontinuendifunction */ /* diendwhilevaldoexitgotoimplementsinclude_oncemptyinstanceof */ /* interfacendforeachissetparentprivateprotectedpublicatchunset */ /* xorARRAYASArrayEXITUNSETXORbreak */ static const char zText[332] = { 'e','x','t','e','n','d','s','w','i','t','c','h','p','r','i','n','t','e', 'g','e','r','e','q','u','i','r','e','_','o','n','c','e','n','d','d','e', 'c','l','a','r','e','t','u','r','n','a','m','e','s','p','a','c','e','c', 'h','o','b','j','e','c','t','h','r','o','w','b','o','o','l','e','a','n', 'd','e','f','a','u','l','t','r','y','c','a','s','e','l','f','i','n','a', 'l','i','s','t','a','t','i','c','l','o','n','e','w','c','o','n','s','t', 'r','i','n','g','l','o','b','a','l','u','s','e','l','s','e','i','f','l', 'o','a','t','v','a','r','r','a','y','A','N','D','I','E','c','h','o','U', 'S','E','C','H','O','a','b','s','t','r','a','c','t','c','l','a','s','s', 'c','o','n','t','i','n','u','e','n','d','i','f','u','n','c','t','i','o', 'n','d','i','e','n','d','w','h','i','l','e','v','a','l','d','o','e','x', 'i','t','g','o','t','o','i','m','p','l','e','m','e','n','t','s','i','n', 'c','l','u','d','e','_','o','n','c','e','m','p','t','y','i','n','s','t', 'a','n','c','e','o','f','i','n','t','e','r','f','a','c','e','n','d','f', 'o','r','e','a','c','h','i','s','s','e','t','p','a','r','e','n','t','p', 'r','i','v','a','t','e','p','r','o','t','e','c','t','e','d','p','u','b', 'l','i','c','a','t','c','h','u','n','s','e','t','x','o','r','A','R','R', 'A','Y','A','S','A','r','r','a','y','E','X','I','T','U','N','S','E','T', 'X','O','R','b','r','e','a','k' }; static const unsigned char aHash[151] = { 0, 0, 4, 83, 0, 61, 39, 12, 0, 33, 77, 0, 48, 0, 2, 65, 67, 0, 0, 0, 47, 0, 0, 40, 0, 15, 74, 0, 51, 0, 76, 0, 0, 20, 0, 0, 0, 50, 0, 80, 34, 0, 36, 0, 0, 64, 16, 0, 0, 17, 0, 1, 19, 84, 66, 0, 43, 45, 78, 0, 0, 53, 56, 0, 0, 0, 23, 49, 0, 0, 13, 31, 54, 7, 0, 0, 25, 0, 72, 14, 0, 71, 0, 38, 6, 0, 0, 0, 73, 0, 0, 3, 0, 41, 5, 52, 57, 32, 0, 60, 63, 0, 69, 82, 30, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 62, 0, 11, 0, 0, 58, 0, 0, 0, 0, 59, 75, 0, 0, 0, 0, 0, 0, 35, 27, 0 }; static const unsigned char aNext[84] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 18, 0, 0, 0, 0, 0, 0, 46, 0, 29, 0, 0, 0, 22, 0, 0, 0, 0, 26, 0, 21, 24, 0, 0, 68, 0, 0, 9, 37, 0, 0, 0, 42, 0, 0, 0, 70, 55 }; static const unsigned char aLen[84] = { 7, 9, 6, 5, 7, 12, 7, 2, 10, 7, 6, 9, 4, 6, 5, 7, 4, 3, 7, 3, 4, 4, 5, 4, 6, 5, 2, 3, 5, 6, 6, 3, 6, 4, 2, 5, 3, 5, 3, 3, 4, 3, 4, 8, 5, 2, 8, 5, 8, 3, 8, 5, 4, 2, 4, 4, 10, 12, 7, 5, 10, 9, 3, 6, 10, 3, 7, 2, 5, 6, 7, 9, 6, 5, 5, 3, 5, 2, 5, 4, 5, 3, 2, 5 }; static const sxu16 aOffset[84] = { 0, 3, 6, 12, 14, 20, 20, 21, 31, 34, 39, 44, 52, 55, 60, 65, 65, 70, 72, 78, 81, 83, 86, 90, 92, 97, 100, 100, 103, 106, 111, 117, 119, 119, 123, 124, 129, 130, 135, 137, 139, 143, 145, 149, 157, 159, 162, 169, 173, 181, 183, 186, 190, 194, 196, 200, 204, 214, 214, 225, 230, 240, 240, 248, 248, 251, 251, 252, 258, 263, 269, 276, 285, 290, 295, 300, 303, 308, 310, 315, 319, 324, 325, 327 }; static const sxu32 aCode[84] = { PH7_TKWRD_EXTENDS, PH7_TKWRD_ENDSWITCH, PH7_TKWRD_SWITCH, PH7_TKWRD_PRINT, PH7_TKWRD_INT, PH7_TKWRD_REQONCE, PH7_TKWRD_REQUIRE, PH7_TKWRD_SEQ, PH7_TKWRD_ENDDEC, PH7_TKWRD_DECLARE, PH7_TKWRD_RETURN, PH7_TKWRD_NAMESPACE, PH7_TKWRD_ECHO, PH7_TKWRD_OBJECT, PH7_TKWRD_THROW, PH7_TKWRD_BOOL, PH7_TKWRD_BOOL, PH7_TKWRD_AND, PH7_TKWRD_DEFAULT, PH7_TKWRD_TRY, PH7_TKWRD_CASE, PH7_TKWRD_SELF, PH7_TKWRD_FINAL, PH7_TKWRD_LIST, PH7_TKWRD_STATIC, PH7_TKWRD_CLONE, PH7_TKWRD_SNE, PH7_TKWRD_NEW, PH7_TKWRD_CONST, PH7_TKWRD_STRING, PH7_TKWRD_GLOBAL, PH7_TKWRD_USE, PH7_TKWRD_ELIF, PH7_TKWRD_ELSE, PH7_TKWRD_IF, PH7_TKWRD_FLOAT, PH7_TKWRD_VAR, PH7_TKWRD_ARRAY, PH7_TKWRD_AND, PH7_TKWRD_DIE, PH7_TKWRD_ECHO, PH7_TKWRD_USE, PH7_TKWRD_ECHO, PH7_TKWRD_ABSTRACT, PH7_TKWRD_CLASS, PH7_TKWRD_AS, PH7_TKWRD_CONTINUE, PH7_TKWRD_ENDIF, PH7_TKWRD_FUNCTION, PH7_TKWRD_DIE, PH7_TKWRD_ENDWHILE, PH7_TKWRD_WHILE, PH7_TKWRD_EVAL, PH7_TKWRD_DO, PH7_TKWRD_EXIT, PH7_TKWRD_GOTO, PH7_TKWRD_IMPLEMENTS, PH7_TKWRD_INCONCE, PH7_TKWRD_INCLUDE, PH7_TKWRD_EMPTY, PH7_TKWRD_INSTANCEOF,PH7_TKWRD_INTERFACE, PH7_TKWRD_INT, PH7_TKWRD_ENDFOR, PH7_TKWRD_END4EACH, PH7_TKWRD_FOR, PH7_TKWRD_FOREACH, PH7_TKWRD_OR, PH7_TKWRD_ISSET, PH7_TKWRD_PARENT, PH7_TKWRD_PRIVATE, PH7_TKWRD_PROTECTED, PH7_TKWRD_PUBLIC, PH7_TKWRD_CATCH, PH7_TKWRD_UNSET, PH7_TKWRD_XOR, PH7_TKWRD_ARRAY, PH7_TKWRD_AS, PH7_TKWRD_ARRAY, PH7_TKWRD_EXIT, PH7_TKWRD_UNSET, PH7_TKWRD_XOR, PH7_TKWRD_OR, PH7_TKWRD_BREAK }; int h, i; if( n<2 ) return PH7_TK_ID; h = (((int)z[0]*4) ^ ((int)z[n-1]*3) ^ n) % 151; for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){ if( (int)aLen[i]==n && SyMemcmp(&zText[aOffset[i]],z,n)==0 ){ /* PH7_TKWRD_EXTENDS */ /* PH7_TKWRD_ENDSWITCH */ /* PH7_TKWRD_SWITCH */ /* PH7_TKWRD_PRINT */ /* PH7_TKWRD_INT */ /* PH7_TKWRD_REQONCE */ /* PH7_TKWRD_REQUIRE */ /* PH7_TKWRD_SEQ */ /* PH7_TKWRD_ENDDEC */ /* PH7_TKWRD_DECLARE */ /* PH7_TKWRD_RETURN */ /* PH7_TKWRD_NAMESPACE */ /* PH7_TKWRD_ECHO */ /* PH7_TKWRD_OBJECT */ /* PH7_TKWRD_THROW */ /* PH7_TKWRD_BOOL */ /* PH7_TKWRD_BOOL */ /* PH7_TKWRD_AND */ /* PH7_TKWRD_DEFAULT */ /* PH7_TKWRD_TRY */ /* PH7_TKWRD_CASE */ /* PH7_TKWRD_SELF */ /* PH7_TKWRD_FINAL */ /* PH7_TKWRD_LIST */ /* PH7_TKWRD_STATIC */ /* PH7_TKWRD_CLONE */ /* PH7_TKWRD_SNE */ /* PH7_TKWRD_NEW */ /* PH7_TKWRD_CONST */ /* PH7_TKWRD_STRING */ /* PH7_TKWRD_GLOBAL */ /* PH7_TKWRD_USE */ /* PH7_TKWRD_ELIF */ /* PH7_TKWRD_ELSE */ /* PH7_TKWRD_IF */ /* PH7_TKWRD_FLOAT */ /* PH7_TKWRD_VAR */ /* PH7_TKWRD_ARRAY */ /* PH7_TKWRD_AND */ /* PH7_TKWRD_DIE */ /* PH7_TKWRD_ECHO */ /* PH7_TKWRD_USE */ /* PH7_TKWRD_ECHO */ /* PH7_TKWRD_ABSTRACT */ /* PH7_TKWRD_CLASS */ /* PH7_TKWRD_AS */ /* PH7_TKWRD_CONTINUE */ /* PH7_TKWRD_ENDIF */ /* PH7_TKWRD_FUNCTION */ /* PH7_TKWRD_DIE */ /* PH7_TKWRD_ENDWHILE */ /* PH7_TKWRD_WHILE */ /* PH7_TKWRD_EVAL */ /* PH7_TKWRD_DO */ /* PH7_TKWRD_EXIT */ /* PH7_TKWRD_GOTO */ /* PH7_TKWRD_IMPLEMENTS */ /* PH7_TKWRD_INCONCE */ /* PH7_TKWRD_INCLUDE */ /* PH7_TKWRD_EMPTY */ /* PH7_TKWRD_INSTANCEOF */ /* PH7_TKWRD_INTERFACE */ /* PH7_TKWRD_INT */ /* PH7_TKWRD_ENDFOR */ /* PH7_TKWRD_END4EACH */ /* PH7_TKWRD_FOR */ /* PH7_TKWRD_FOREACH */ /* PH7_TKWRD_OR */ /* PH7_TKWRD_ISSET */ /* PH7_TKWRD_PARENT */ /* PH7_TKWRD_PRIVATE */ /* PH7_TKWRD_PROTECTED */ /* PH7_TKWRD_PUBLIC */ /* PH7_TKWRD_CATCH */ /* PH7_TKWRD_UNSET */ /* PH7_TKWRD_XOR */ /* PH7_TKWRD_ARRAY */ /* PH7_TKWRD_AS */ /* PH7_TKWRD_ARRAY */ /* PH7_TKWRD_EXIT */ /* PH7_TKWRD_UNSET */ /* PH7_TKWRD_XOR */ /* PH7_TKWRD_OR */ /* PH7_TKWRD_BREAK */ return aCode[i]; } } return PH7_TK_ID; } /* --- End of Automatically generated code --- */ /* * Extract a heredoc/nowdoc text from a raw PHP input. * According to the PHP language reference manual: * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier * is provided, then a newline. The string itself follows, and then the same identifier again * to close the quotation. * The closing identifier must begin in the first column of the line. Also, the identifier must * follow the same naming rules as any other label in PHP: it must contain only alphanumeric * characters and underscores, and must start with a non-digit character or underscore. * Heredoc text behaves just like a double-quoted string, without the double quotes. * This means that quotes in a heredoc do not need to be escaped, but the escape codes listed * above can still be used. Variables are expanded, but the same care must be taken when expressing * complex variables inside a heredoc as with strings. * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings. * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc. * The construct is ideal for embedding PHP code or other large blocks of text without the need * for escaping. It shares some features in common with the SGML construct, in that * it declares a block of text which is not for parsing. * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier which follows * is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc identifiers also apply to nowdoc * identifiers, especially those regarding the appearance of the closing identifier. * Symisc Extension: * The closing delimiter can now start with a digit or undersocre or it can be an UTF-8 stream. * Example: * <<<123 * HEREDOC Here * 123 * or * <<<___ * HEREDOC Here * ___ */ static sxi32 LexExtractHeredoc(SyStream *pStream,SyToken *pToken) { const unsigned char *zIn = pStream->zText; const unsigned char *zEnd = pStream->zEnd; const unsigned char *zPtr; sxu8 bNowDoc = FALSE; SyString sDelim; SyString sStr; /* Jump leading white spaces */ while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){ zIn++; } if( zIn >= zEnd ){ /* A simple symbol,return immediately */ return SXERR_CONTINUE; } if( zIn[0] == '\'' || zIn[0] == '"' ){ /* Make sure we are dealing with a nowdoc */ bNowDoc = zIn[0] == '\'' ? TRUE : FALSE; zIn++; } if( zIn[0] < 0xc0 && !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){ /* Invalid delimiter,return immediately */ return SXERR_CONTINUE; } /* Isolate the identifier */ sDelim.zString = (const char *)zIn; for(;;){ zPtr = zIn; /* Skip alphanumeric stream */ while( zPtr < zEnd && zPtr[0] < 0xc0 && (SyisAlphaNum(zPtr[0]) || zPtr[0] == '_') ){ zPtr++; } if( zPtr < zEnd && zPtr[0] >= 0xc0 ){ zPtr++; /* UTF-8 stream */ while( zPtr < zEnd && ((zPtr[0] & 0xc0) == 0x80) ){ zPtr++; } } if( zPtr == zIn ){ /* Not an UTF-8 or alphanumeric stream */ break; } /* Synchronize pointers */ zIn = zPtr; } /* Get the identifier length */ sDelim.nByte = (sxu32)((const char *)zIn-sDelim.zString); if( zIn[0] == '"' || (bNowDoc && zIn[0] == '\'') ){ /* Jump the trailing single quote */ zIn++; } /* Jump trailing white spaces */ while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){ zIn++; } if( sDelim.nByte <= 0 || zIn >= zEnd || zIn[0] != '\n' ){ /* Invalid syntax */ return SXERR_CONTINUE; } pStream->nLine++; /* Increment line counter */ zIn++; /* Isolate the delimited string */ sStr.zString = (const char *)zIn; /* Go and found the closing delimiter */ for(;;){ /* Synchronize with the next line */ while( zIn < zEnd && zIn[0] != '\n' ){ zIn++; } if( zIn >= zEnd ){ /* End of the input reached, break immediately */ pStream->zText = pStream->zEnd; break; } pStream->nLine++; /* Increment line counter */ zIn++; if( (sxu32)(zEnd - zIn) >= sDelim.nByte && SyMemcmp((const void *)sDelim.zString,(const void *)zIn,sDelim.nByte) == 0 ){ zPtr = &zIn[sDelim.nByte]; while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){ zPtr++; } if( zPtr >= zEnd ){ /* End of input */ pStream->zText = zPtr; break; } if( zPtr[0] == ';' ){ const unsigned char *zCur = zPtr; zPtr++; while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){ zPtr++; } if( zPtr >= zEnd || zPtr[0] == '\n' ){ /* Closing delimiter found,break immediately */ pStream->zText = zCur; /* Keep the semi-colon */ break; } }else if( zPtr[0] == '\n' ){ /* Closing delimiter found,break immediately */ pStream->zText = zPtr; /* Synchronize with the stream cursor */ break; } /* Synchronize pointers and continue searching */ zIn = zPtr; } } /* For(;;) */ /* Get the delimited string length */ sStr.nByte = (sxu32)((const char *)zIn-sStr.zString); /* Record token type and length */ pToken->nType = bNowDoc ? PH7_TK_NOWDOC : PH7_TK_HEREDOC; SyStringDupPtr(&pToken->sData,&sStr); /* Remove trailing white spaces */ SyStringRightTrim(&pToken->sData); /* All done */ return SXRET_OK; } /* * Tokenize a raw PHP input. * This is the public tokenizer called by most code generator routines. */ PH7_PRIVATE sxi32 PH7_TokenizePHP(const char *zInput,sxu32 nLen,sxu32 nLineStart,SySet *pOut) { SyLex sLexer; sxi32 rc; /* Initialize the lexer */ rc = SyLexInit(&sLexer,&(*pOut),TokenizePHP,0); if( rc != SXRET_OK ){ return rc; } sLexer.sStream.nLine = nLineStart; /* Tokenize input */ rc = SyLexTokenizeInput(&sLexer,zInput,nLen,0,0,0); /* Release the lexer */ SyLexRelease(&sLexer); /* Tokenization result */ return rc; } /* * High level public tokenizer. * Tokenize the input into PHP tokens and raw tokens [i.e: HTML,XML,Raw text...]. * According to the PHP language reference manual * When PHP parses a file, it looks for opening and closing tags, which tell PHP * to start and stop interpreting the code between them. Parsing in this manner allows * PHP to be embedded in all sorts of different documents, as everything outside of a pair * of opening and closing tags is ignored by the PHP parser. Most of the time you will see * PHP embedded in HTML documents, as in this example. * *

This will also be ignored.

* You can also use more advanced structures: * Example #1 Advanced escaping * * This is true. * * This is false. * * This works as expected, because when PHP hits the ?> closing tags, it simply starts outputting * whatever it finds (except for an immediately following newline - see instruction separation ) until it hits * another opening tag. The example given here is contrived, of course, but for outputting large blocks of text * dropping out of PHP parsing mode is generally more efficient than sending all of the text through echo() or print(). * There are four different pairs of opening and closing tags which can be used in PHP. Three of those, * and are always available. The other two are short tags and ASP style * tags, and can be turned on and off from the php.ini configuration file. As such, while some people find short tags * and ASP style tags convenient, they are less portable, and generally not recommended. * Note: * Also note that if you are embedding PHP within XML or XHTML you will need to use the tags to remain * compliant with standards. * Example #2 PHP Opening and Closing Tags * 1. * 2. * * 3. * This is a shortcut for "" */ PH7_PRIVATE sxi32 PH7_TokenizeRawText(const char *zInput,sxu32 nLen,SySet *pOut) { const char *zEnd = &zInput[nLen]; const char *zIn = zInput; const char *zCur,*zCurEnd; SyString sCtag = { 0, 0 }; /* Closing tag */ SyToken sToken; SyString sDoc; sxu32 nLine; sxi32 iNest; sxi32 rc; /* Tokenize the input into PHP tokens and raw tokens */ nLine = 1; zCur = zCurEnd = 0; /* Prevent compiler warning */ sToken.pUserData = 0; iNest = 0; sDoc.nByte = 0; sDoc.zString = ""; /* cc warning */ for(;;){ if( zIn >= zEnd ){ /* End of input reached */ break; } sToken.nLine = nLine; zCur = zIn; zCurEnd = 0; while( zIn < zEnd ){ if( zIn[0] == '<' ){ const char *zTmp = zIn; /* End of raw input marker */ zIn++; if( zIn < zEnd ){ if( zIn[0] == '?' ){ zIn++; if( (sxu32)(zEnd - zIn) >= sizeof("php")-1 && SyStrnicmp(zIn,"php",sizeof("php")-1) == 0 ){ /* opening tag: ' */ SyStringInitFromBuf(&sCtag,"?>",sizeof("?>")-1); zCurEnd = zTmp; break; } } }else{ if( zIn[0] == '\n' ){ nLine++; } zIn++; } } /* While(zIn < zEnd) */ if( zCurEnd == 0 ){ zCurEnd = zIn; } /* Save the raw token */ SyStringInitFromBuf(&sToken.sData,zCur,zCurEnd - zCur); sToken.nType = PH7_TOKEN_RAW; rc = SySetPut(&(*pOut),(const void *)&sToken); if( rc != SXRET_OK ){ return rc; } if( zIn >= zEnd ){ break; } /* Ignore leading white space */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ if( zIn[0] == '\n' ){ nLine++; } zIn++; } /* Delimit the PHP chunk */ sToken.nLine = nLine; zCur = zIn; while( (sxu32)(zEnd - zIn) >= sCtag.nByte ){ const char *zPtr; if( SyMemcmp(zIn,sCtag.zString,sCtag.nByte) == 0 && iNest < 1 ){ break; } for(;;){ if( zIn[0] != '/' || (zIn[1] != '*' && zIn[1] != '/') /* && sCtag.nByte >= 2 */ ){ break; } zIn += 2; if( zIn[-1] == '/' ){ /* Inline comment */ while( zIn < zEnd && zIn[0] != '\n' ){ zIn++; } if( zIn >= zEnd ){ zIn--; } }else{ /* Block comment */ while( (sxu32)(zEnd-zIn) >= sizeof("*/") - 1 ){ if( zIn[0] == '*' && zIn[1] == '/' ){ zIn += 2; break; } if( zIn[0] == '\n' ){ nLine++; } zIn++; } } } if( zIn[0] == '\n' ){ nLine++; if( iNest > 0 ){ zIn++; while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){ zIn++; } zPtr = zIn; while( zIn < zEnd ){ if( (unsigned char)zIn[0] >= 0xc0 ){ /* UTF-8 stream */ zIn++; SX_JMP_UTF8(zIn,zEnd); }else if( !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){ break; }else{ zIn++; } } if( (sxu32)(zIn - zPtr) == sDoc.nByte && SyMemcmp(sDoc.zString,zPtr,sDoc.nByte) == 0 ){ iNest = 0; } continue; } }else if ( (sxu32)(zEnd - zIn) >= sizeof("<<<") && zIn[0] == '<' && zIn[1] == '<' && zIn[2] == '<' && iNest < 1){ zIn += sizeof("<<<")-1; while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){ zIn++; } if( zIn[0] == '"' || zIn[0] == '\'' ){ zIn++; } zPtr = zIn; while( zIn < zEnd ){ if( (unsigned char)zIn[0] >= 0xc0 ){ /* UTF-8 stream */ zIn++; SX_JMP_UTF8(zIn,zEnd); }else if( !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){ break; }else{ zIn++; } } SyStringInitFromBuf(&sDoc,zPtr,zIn-zPtr); SyStringFullTrim(&sDoc); if( sDoc.nByte > 0 ){ iNest++; } continue; } zIn++; if ( zIn >= zEnd ) break; } if( (sxu32)(zEnd - zIn) < sCtag.nByte ){ zIn = zEnd; } if( zCur < zIn ){ /* Save the PHP chunk for later processing */ sToken.nType = PH7_TOKEN_PHP; SyStringInitFromBuf(&sToken.sData,zCur,zIn-zCur); SyStringRightTrim(&sToken.sData); /* Trim trailing white spaces */ rc = SySetPut(&(*pOut),(const void *)&sToken); if( rc != SXRET_OK ){ return rc; } } if( zIn < zEnd ){ /* Jump the trailing closing tag */ zIn += sCtag.nByte; } } /* For(;;) */ return SXRET_OK; } /* * ---------------------------------------------------------- * File: hashmap.c * MD5: cf4287c2602a9c97df208364cb9be084 * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: hashmap.c v3.5 FreeBSD 2012-08-07 08:29 stable $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* This file implement generic hashmaps known as 'array' in the PHP world */ /* Allowed node types */ #define HASHMAP_INT_NODE 1 /* Node with an int [i.e: 64-bit integer] key */ #define HASHMAP_BLOB_NODE 2 /* Node with a string/BLOB key */ /* Node control flags */ #define HASHMAP_NODE_FOREIGN_OBJ 0x001 /* Node hold a reference to a foreign ph7_value * [i.e: array(&var)/$a[] =& $var ] */ /* * Default hash function for int [i.e; 64-bit integer] keys. */ static sxu32 IntHash(sxi64 iKey) { return (sxu32)(iKey ^ (iKey << 8) ^ (iKey >> 8)); } /* * Default hash function for string/BLOB keys. */ static sxu32 BinHash(const void *pSrc,sxu32 nLen) { register unsigned char *zIn = (unsigned char *)pSrc; unsigned char *zEnd; sxu32 nH = 5381; zEnd = &zIn[nLen]; for(;;){ if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++; } return nH; } /* * Return the total number of entries in a given hashmap. * If bRecurisve is set to TRUE then recurse on hashmap entries. * If the nesting limit is reached,this function abort immediately. */ static sxi64 HashmapCount(ph7_hashmap *pMap,int bRecursive,int iRecCount) { sxi64 iCount = 0; if( !bRecursive ){ iCount = pMap->nEntry; }else{ /* Recursive hashmap walk */ ph7_hashmap_node *pEntry = pMap->pLast; ph7_value *pElem; sxu32 n = 0; for(;;){ if( n >= pMap->nEntry ){ break; } /* Point to the element value */ pElem = (ph7_value *)SySetAt(&pMap->pVm->aMemObj,pEntry->nValIdx); if( pElem ){ if( pElem->iFlags & MEMOBJ_HASHMAP ){ if( iRecCount > 31 ){ /* Nesting limit reached */ return iCount; } /* Recurse */ iRecCount++; iCount += HashmapCount((ph7_hashmap *)pElem->x.pOther,TRUE,iRecCount); iRecCount--; } } /* Point to the next entry */ pEntry = pEntry->pNext; ++n; } /* Update count */ iCount += pMap->nEntry; } return iCount; } /* * Allocate a new hashmap node with a 64-bit integer key. * If something goes wrong [i.e: out of memory],this function return NULL. * Otherwise a fresh [ph7_hashmap_node] instance is returned. */ static ph7_hashmap_node * HashmapNewIntNode(ph7_hashmap *pMap,sxi64 iKey,sxu32 nHash,sxu32 nValIdx) { ph7_hashmap_node *pNode; /* Allocate a new node */ pNode = (ph7_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator,sizeof(ph7_hashmap_node)); if( pNode == 0 ){ return 0; } /* Zero the stucture */ SyZero(pNode,sizeof(ph7_hashmap_node)); /* Fill in the structure */ pNode->pMap = &(*pMap); pNode->iType = HASHMAP_INT_NODE; pNode->nHash = nHash; pNode->xKey.iKey = iKey; pNode->nValIdx = nValIdx; return pNode; } /* * Allocate a new hashmap node with a BLOB key. * If something goes wrong [i.e: out of memory],this function return NULL. * Otherwise a fresh [ph7_hashmap_node] instance is returned. */ static ph7_hashmap_node * HashmapNewBlobNode(ph7_hashmap *pMap,const void *pKey,sxu32 nKeyLen,sxu32 nHash,sxu32 nValIdx) { ph7_hashmap_node *pNode; /* Allocate a new node */ pNode = (ph7_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator,sizeof(ph7_hashmap_node)); if( pNode == 0 ){ return 0; } /* Zero the stucture */ SyZero(pNode,sizeof(ph7_hashmap_node)); /* Fill in the structure */ pNode->pMap = &(*pMap); pNode->iType = HASHMAP_BLOB_NODE; pNode->nHash = nHash; SyBlobInit(&pNode->xKey.sKey,&pMap->pVm->sAllocator); SyBlobAppend(&pNode->xKey.sKey,pKey,nKeyLen); pNode->nValIdx = nValIdx; return pNode; } /* * link a hashmap node to the given bucket index (last argument to this function). */ static void HashmapNodeLink(ph7_hashmap *pMap,ph7_hashmap_node *pNode,sxu32 nBucketIdx) { /* Link */ if( pMap->apBucket[nBucketIdx] != 0 ){ pNode->pNextCollide = pMap->apBucket[nBucketIdx]; pMap->apBucket[nBucketIdx]->pPrevCollide = pNode; } pMap->apBucket[nBucketIdx] = pNode; /* Link to the map list */ if( pMap->pFirst == 0 ){ pMap->pFirst = pMap->pLast = pNode; /* Point to the first inserted node */ pMap->pCur = pNode; }else{ MACRO_LD_PUSH(pMap->pLast,pNode); } ++pMap->nEntry; } /* * Unlink a node from the hashmap. * If the node count reaches zero then release the whole hash-bucket. */ PH7_PRIVATE void PH7_HashmapUnlinkNode(ph7_hashmap_node *pNode,int bRestore) { ph7_hashmap *pMap = pNode->pMap; ph7_vm *pVm = pMap->pVm; /* Unlink from the corresponding bucket */ if( pNode->pPrevCollide == 0 ){ pMap->apBucket[pNode->nHash & (pMap->nSize - 1)] = pNode->pNextCollide; }else{ pNode->pPrevCollide->pNextCollide = pNode->pNextCollide; } if( pNode->pNextCollide ){ pNode->pNextCollide->pPrevCollide = pNode->pPrevCollide; } if( pMap->pFirst == pNode ){ pMap->pFirst = pNode->pPrev; } if( pMap->pCur == pNode ){ /* Advance the node cursor */ pMap->pCur = pMap->pCur->pPrev; /* Reverse link */ } /* Unlink from the map list */ MACRO_LD_REMOVE(pMap->pLast,pNode); if( bRestore ){ /* Remove the ph7_value associated with this node from the reference table */ PH7_VmRefObjRemove(pVm,pNode->nValIdx,0,pNode); /* Restore to the freelist */ if( (pNode->iFlags & HASHMAP_NODE_FOREIGN_OBJ) == 0 ){ PH7_VmUnsetMemObj(pVm,pNode->nValIdx,FALSE); } } if( pNode->iType == HASHMAP_BLOB_NODE ){ SyBlobRelease(&pNode->xKey.sKey); } SyMemBackendPoolFree(&pVm->sAllocator,pNode); pMap->nEntry--; if( pMap->nEntry < 1 && pMap != pVm->pGlobal ){ /* Free the hash-bucket */ SyMemBackendFree(&pVm->sAllocator,pMap->apBucket); pMap->apBucket = 0; pMap->nSize = 0; pMap->pFirst = pMap->pLast = pMap->pCur = 0; } } #define HASHMAP_FILL_FACTOR 3 /* * Grow the hash-table and rehash all entries. */ static sxi32 HashmapGrowBucket(ph7_hashmap *pMap) { if( pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR ){ ph7_hashmap_node **apOld = pMap->apBucket; ph7_hashmap_node *pEntry,**apNew; sxu32 nNew = pMap->nSize << 1; sxu32 nBucket; sxu32 n; if( nNew < 1 ){ nNew = 16; } /* Allocate a new bucket */ apNew = (ph7_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator,nNew * sizeof(ph7_hashmap_node *)); if( apNew == 0 ){ if( pMap->nSize < 1 ){ return SXERR_MEM; /* Fatal */ } /* Not so fatal here,simply a performance hit */ return SXRET_OK; } /* Zero the table */ SyZero((void *)apNew,nNew * sizeof(ph7_hashmap_node *)); /* Reflect the change */ pMap->apBucket = apNew; pMap->nSize = nNew; if( apOld == 0 ){ /* First allocated table [i.e: no entry],return immediately */ return SXRET_OK; } /* Rehash old entries */ pEntry = pMap->pFirst; n = 0; for( ;; ){ if( n >= pMap->nEntry ){ break; } /* Clear the old collision link */ pEntry->pNextCollide = pEntry->pPrevCollide = 0; /* Link to the new bucket */ nBucket = pEntry->nHash & (nNew - 1); if( pMap->apBucket[nBucket] != 0 ){ pEntry->pNextCollide = pMap->apBucket[nBucket]; pMap->apBucket[nBucket]->pPrevCollide = pEntry; } pMap->apBucket[nBucket] = pEntry; /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n++; } /* Free the old table */ SyMemBackendFree(&pMap->pVm->sAllocator,(void *)apOld); } return SXRET_OK; } /* * Insert a 64-bit integer key and it's associated value (if any) in the given * hashmap. */ static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap,sxi64 iKey,ph7_value *pValue,sxu32 nRefIdx,int isForeign) { ph7_hashmap_node *pNode; sxu32 nIdx; sxu32 nHash; sxi32 rc; if( !isForeign ){ ph7_value *pObj; /* Reserve a ph7_value for the value */ pObj = PH7_ReserveMemObj(pMap->pVm); if( pObj == 0 ){ return SXERR_MEM; } if( pValue ){ /* Duplicate the value */ PH7_MemObjStore(pValue,pObj); } nIdx = pObj->nIdx; }else{ nIdx = nRefIdx; } /* Hash the key */ nHash = pMap->xIntHash(iKey); /* Allocate a new int node */ pNode = HashmapNewIntNode(&(*pMap),iKey,nHash,nIdx); if( pNode == 0 ){ return SXERR_MEM; } if( isForeign ){ /* Mark as a foregin entry */ pNode->iFlags |= HASHMAP_NODE_FOREIGN_OBJ; } /* Make sure the bucket is big enough to hold the new entry */ rc = HashmapGrowBucket(&(*pMap)); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pMap->pVm->sAllocator,pNode); return rc; } /* Perform the insertion */ HashmapNodeLink(&(*pMap),pNode,nHash & (pMap->nSize - 1)); /* Install in the reference table */ PH7_VmRefObjInstall(pMap->pVm,nIdx,0,pNode,0); /* All done */ return SXRET_OK; } /* * Insert a BLOB key and it's associated value (if any) in the given * hashmap. */ static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap,const void *pKey,sxu32 nKeyLen,ph7_value *pValue,sxu32 nRefIdx,int isForeign) { ph7_hashmap_node *pNode; sxu32 nHash; sxu32 nIdx; sxi32 rc; if( !isForeign ){ ph7_value *pObj; /* Reserve a ph7_value for the value */ pObj = PH7_ReserveMemObj(pMap->pVm); if( pObj == 0 ){ return SXERR_MEM; } if( pValue ){ /* Duplicate the value */ PH7_MemObjStore(pValue,pObj); } nIdx = pObj->nIdx; }else{ nIdx = nRefIdx; } /* Hash the key */ nHash = pMap->xBlobHash(pKey,nKeyLen); /* Allocate a new blob node */ pNode = HashmapNewBlobNode(&(*pMap),pKey,nKeyLen,nHash,nIdx); if( pNode == 0 ){ return SXERR_MEM; } if( isForeign ){ /* Mark as a foregin entry */ pNode->iFlags |= HASHMAP_NODE_FOREIGN_OBJ; } /* Make sure the bucket is big enough to hold the new entry */ rc = HashmapGrowBucket(&(*pMap)); if( rc != SXRET_OK ){ SyMemBackendPoolFree(&pMap->pVm->sAllocator,pNode); return rc; } /* Perform the insertion */ HashmapNodeLink(&(*pMap),pNode,nHash & (pMap->nSize - 1)); /* Install in the reference table */ PH7_VmRefObjInstall(pMap->pVm,nIdx,0,pNode,0); /* All done */ return SXRET_OK; } /* * Check if a given 64-bit integer key exists in the given hashmap. * Write a pointer to the target node on success. Otherwise * SXERR_NOTFOUND is returned on failure. */ static sxi32 HashmapLookupIntKey( ph7_hashmap *pMap, /* Target hashmap */ sxi64 iKey, /* lookup key */ ph7_hashmap_node **ppNode /* OUT: target node on success */ ) { ph7_hashmap_node *pNode; sxu32 nHash; if( pMap->nEntry < 1 ){ /* Don't bother hashing,there is no entry anyway */ return SXERR_NOTFOUND; } /* Hash the key first */ nHash = pMap->xIntHash(iKey); /* Point to the appropriate bucket */ pNode = pMap->apBucket[nHash & (pMap->nSize - 1)]; /* Perform the lookup */ for(;;){ if( pNode == 0 ){ break; } if( pNode->iType == HASHMAP_INT_NODE && pNode->nHash == nHash && pNode->xKey.iKey == iKey ){ /* Node found */ if( ppNode ){ *ppNode = pNode; } return SXRET_OK; } /* Follow the collision link */ pNode = pNode->pNextCollide; } /* No such entry */ return SXERR_NOTFOUND; } /* * Check if a given BLOB key exists in the given hashmap. * Write a pointer to the target node on success. Otherwise * SXERR_NOTFOUND is returned on failure. */ static sxi32 HashmapLookupBlobKey( ph7_hashmap *pMap, /* Target hashmap */ const void *pKey, /* Lookup key */ sxu32 nKeyLen, /* Key length in bytes */ ph7_hashmap_node **ppNode /* OUT: target node on success */ ) { ph7_hashmap_node *pNode; sxu32 nHash; if( pMap->nEntry < 1 ){ /* Don't bother hashing,there is no entry anyway */ return SXERR_NOTFOUND; } /* Hash the key first */ nHash = pMap->xBlobHash(pKey,nKeyLen); /* Point to the appropriate bucket */ pNode = pMap->apBucket[nHash & (pMap->nSize - 1)]; /* Perform the lookup */ for(;;){ if( pNode == 0 ){ break; } if( pNode->iType == HASHMAP_BLOB_NODE && pNode->nHash == nHash && SyBlobLength(&pNode->xKey.sKey) == nKeyLen && SyMemcmp(SyBlobData(&pNode->xKey.sKey),pKey,nKeyLen) == 0 ){ /* Node found */ if( ppNode ){ *ppNode = pNode; } return SXRET_OK; } /* Follow the collision link */ pNode = pNode->pNextCollide; } /* No such entry */ return SXERR_NOTFOUND; } /* * Check if the given BLOB key looks like a decimal number. * Retrurn TRUE on success.FALSE otherwise. */ static int HashmapIsIntKey(SyBlob *pKey) { const char *zIn = (const char *)SyBlobData(pKey); const char *zEnd = &zIn[SyBlobLength(pKey)]; if( (int)(zEnd-zIn) > 1 && zIn[0] == '0' ){ /* Octal not decimal number */ return FALSE; } if( (zIn[0] == '-' || zIn[0] == '+') && &zIn[1] < zEnd ){ zIn++; } for(;;){ if( zIn >= zEnd ){ return TRUE; } if( (unsigned char)zIn[0] >= 0xc0 /* UTF-8 stream */ || !SyisDigit(zIn[0]) ){ break; } zIn++; } /* Key does not look like a decimal number */ return FALSE; } /* * Check if a given key exists in the given hashmap. * Write a pointer to the target node on success. * Otherwise SXERR_NOTFOUND is returned on failure. */ static sxi32 HashmapLookup( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pKey, /* Lookup key */ ph7_hashmap_node **ppNode /* OUT: target node on success */ ) { ph7_hashmap_node *pNode = 0; /* cc -O6 warning */ sxi32 rc; if( pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES) ){ if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(&(*pKey)); } if( SyBlobLength(&pKey->sBlob) > 0 && !HashmapIsIntKey(&pKey->sBlob) ){ /* Perform a blob lookup */ rc = HashmapLookupBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&pNode); goto result; } } /* Perform an int lookup */ if((pKey->iFlags & MEMOBJ_INT) == 0 ){ /* Force an integer cast */ PH7_MemObjToInteger(pKey); } /* Perform an int lookup */ rc = HashmapLookupIntKey(&(*pMap),pKey->x.iVal,&pNode); result: if( rc == SXRET_OK ){ /* Node found */ if( ppNode ){ *ppNode = pNode; } return SXRET_OK; } /* No such entry */ return SXERR_NOTFOUND; } /* * Insert a given key and it's associated value (if any) in the given * hashmap. * If a node with the given key already exists in the database * then this function overwrite the old value. */ static sxi32 HashmapInsert( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pKey, /* Lookup key */ ph7_value *pVal /* Node value */ ) { ph7_hashmap_node *pNode = 0; sxi32 rc = SXRET_OK; if( pKey && pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES) ){ if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(&(*pKey)); } if( SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob) ){ if(SyBlobLength(&pKey->sBlob) < 1){ /* Automatic index assign */ pKey = 0; } goto IntKey; } if( SXRET_OK == HashmapLookupBlobKey(&(*pMap),SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob),&pNode) ){ /* Overwrite the old value */ ph7_value *pElem; pElem = (ph7_value *)SySetAt(&pMap->pVm->aMemObj,pNode->nValIdx); if( pElem ){ if( pVal ){ PH7_MemObjStore(pVal,pElem); }else{ /* Nullify the entry */ PH7_MemObjToNull(pElem); } } return SXRET_OK; } if( pMap == pMap->pVm->pGlobal ){ /* Forbidden */ PH7_VmThrowError(pMap->pVm,0,PH7_CTX_NOTICE,"$GLOBALS is a read-only array,insertion is forbidden"); return SXRET_OK; } /* Perform a blob-key insertion */ rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&(*pVal),0,FALSE); return rc; } IntKey: if( pKey ){ if((pKey->iFlags & MEMOBJ_INT) == 0 ){ /* Force an integer cast */ PH7_MemObjToInteger(pKey); } if( SXRET_OK == HashmapLookupIntKey(&(*pMap),pKey->x.iVal,&pNode) ){ /* Overwrite the old value */ ph7_value *pElem; pElem = (ph7_value *)SySetAt(&pMap->pVm->aMemObj,pNode->nValIdx); if( pElem ){ if( pVal ){ PH7_MemObjStore(pVal,pElem); }else{ /* Nullify the entry */ PH7_MemObjToNull(pElem); } } return SXRET_OK; } if( pMap == pMap->pVm->pGlobal ){ /* Forbidden */ PH7_VmThrowError(pMap->pVm,0,PH7_CTX_NOTICE,"$GLOBALS is a read-only array,insertion is forbidden"); return SXRET_OK; } /* Perform a 64-bit-int-key insertion */ rc = HashmapInsertIntKey(&(*pMap),pKey->x.iVal,&(*pVal),0,FALSE); if( rc == SXRET_OK ){ if( pKey->x.iVal >= pMap->iNextIdx ){ /* Increment the automatic index */ pMap->iNextIdx = pKey->x.iVal + 1; /* Make sure the automatic index is not reserved */ while( SXRET_OK == HashmapLookupIntKey(&(*pMap),pMap->iNextIdx,0) ){ pMap->iNextIdx++; } } } }else{ if( pMap == pMap->pVm->pGlobal ){ /* Forbidden */ PH7_VmThrowError(pMap->pVm,0,PH7_CTX_NOTICE,"$GLOBALS is a read-only array,insertion is forbidden"); return SXRET_OK; } /* Assign an automatic index */ rc = HashmapInsertIntKey(&(*pMap),pMap->iNextIdx,&(*pVal),0,FALSE); if( rc == SXRET_OK ){ ++pMap->iNextIdx; } } /* Insertion result */ return rc; } /* * Insert a given key and it's associated value (foreign index) in the given * hashmap. * This is insertion by reference so be careful to mark the node * with the HASHMAP_NODE_FOREIGN_OBJ flag being set. * The insertion by reference is triggered when the following * expression is encountered. * $var = 10; * $a = array(&var); * OR * $a[] =& $var; * That is,$var is a foreign ph7_value and the $a array have no control * over it's contents. * Note that the node that hold the foreign ph7_value is automatically * removed when the foreign ph7_value is unset. * Example: * $var = 10; * $a[] =& $var; * echo count($a).PHP_EOL; //1 * //Unset the foreign ph7_value now * unset($var); * echo count($a); //0 * Note that this is a PH7 eXtension. * Refer to the official documentation for more information. * If a node with the given key already exists in the database * then this function overwrite the old value. */ static sxi32 HashmapInsertByRef( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pKey, /* Lookup key */ sxu32 nRefIdx /* Foreign ph7_value index */ ) { ph7_hashmap_node *pNode = 0; sxi32 rc = SXRET_OK; if( pKey && pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES) ){ if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){ /* Force a string cast */ PH7_MemObjToString(&(*pKey)); } if( SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob) ){ if(SyBlobLength(&pKey->sBlob) < 1){ /* Automatic index assign */ pKey = 0; } goto IntKey; } if( SXRET_OK == HashmapLookupBlobKey(&(*pMap),SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob),&pNode) ){ /* Overwrite */ PH7_VmRefObjRemove(pMap->pVm,pNode->nValIdx,0,pNode); pNode->nValIdx = nRefIdx; /* Install in the reference table */ PH7_VmRefObjInstall(pMap->pVm,nRefIdx,0,pNode,0); return SXRET_OK; } /* Perform a blob-key insertion */ rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),0,nRefIdx,TRUE); return rc; } IntKey: if( pKey ){ if((pKey->iFlags & MEMOBJ_INT) == 0 ){ /* Force an integer cast */ PH7_MemObjToInteger(pKey); } if( SXRET_OK == HashmapLookupIntKey(&(*pMap),pKey->x.iVal,&pNode) ){ /* Overwrite */ PH7_VmRefObjRemove(pMap->pVm,pNode->nValIdx,0,pNode); pNode->nValIdx = nRefIdx; /* Install in the reference table */ PH7_VmRefObjInstall(pMap->pVm,nRefIdx,0,pNode,0); return SXRET_OK; } /* Perform a 64-bit-int-key insertion */ rc = HashmapInsertIntKey(&(*pMap),pKey->x.iVal,0,nRefIdx,TRUE); if( rc == SXRET_OK ){ if( pKey->x.iVal >= pMap->iNextIdx ){ /* Increment the automatic index */ pMap->iNextIdx = pKey->x.iVal + 1; /* Make sure the automatic index is not reserved */ while( SXRET_OK == HashmapLookupIntKey(&(*pMap),pMap->iNextIdx,0) ){ pMap->iNextIdx++; } } } }else{ /* Assign an automatic index */ rc = HashmapInsertIntKey(&(*pMap),pMap->iNextIdx,0,nRefIdx,TRUE); if( rc == SXRET_OK ){ ++pMap->iNextIdx; } } /* Insertion result */ return rc; } /* * Extract node value. */ static ph7_value * HashmapExtractNodeValue(ph7_hashmap_node *pNode) { /* Point to the desired object */ ph7_value *pObj; pObj = (ph7_value *)SySetAt(&pNode->pMap->pVm->aMemObj,pNode->nValIdx); return pObj; } /* * Insert a node in the given hashmap. * If a node with the given key already exists in the database * then this function overwrite the old value. */ static sxi32 HashmapInsertNode(ph7_hashmap *pMap,ph7_hashmap_node *pNode,int bPreserve) { ph7_value *pObj; sxi32 rc; /* Extract the node value */ pObj = HashmapExtractNodeValue(&(*pNode)); if( pObj == 0 ){ return SXERR_EMPTY; } /* Preserve key */ if( pNode->iType == HASHMAP_INT_NODE){ /* Int64 key */ if( !bPreserve ){ /* Assign an automatic index */ rc = HashmapInsert(&(*pMap),0,pObj); }else{ rc = HashmapInsertIntKey(&(*pMap),pNode->xKey.iKey,pObj,0,FALSE); } }else{ /* Blob key */ rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey),pObj,0,FALSE); } return rc; } /* * Compare two node values. * Return 0 if the node values are equals, > 0 if pLeft is greater than pRight * or < 0 if pRight is greater than pLeft. * For a full description on ph7_values comparison,refer to the implementation * of the [PH7_MemObjCmp()] function defined in memobj.c or the official * documenation. */ static sxi32 HashmapNodeCmp(ph7_hashmap_node *pLeft,ph7_hashmap_node *pRight,int bStrict) { ph7_value sObj1,sObj2; sxi32 rc; if( pLeft == pRight ){ /* * Same node.Refer to the sort() implementation defined * below for more information on this sceanario. */ return 0; } /* Do the comparison */ PH7_MemObjInit(pLeft->pMap->pVm,&sObj1); PH7_MemObjInit(pLeft->pMap->pVm,&sObj2); PH7_HashmapExtractNodeValue(pLeft,&sObj1,FALSE); PH7_HashmapExtractNodeValue(pRight,&sObj2,FALSE); rc = PH7_MemObjCmp(&sObj1,&sObj2,bStrict,0); PH7_MemObjRelease(&sObj1); PH7_MemObjRelease(&sObj2); return rc; } /* * Rehash a node with a 64-bit integer key. * Refer to [merge_sort(),array_shift()] implementations for more information. */ static void HashmapRehashIntNode(ph7_hashmap_node *pEntry) { ph7_hashmap *pMap = pEntry->pMap; sxu32 nBucket; /* Remove old collision links */ if( pEntry->pPrevCollide ){ pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide; }else{ pMap->apBucket[pEntry->nHash & (pMap->nSize - 1)] = pEntry->pNextCollide; } if( pEntry->pNextCollide ){ pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide; } pEntry->pNextCollide = pEntry->pPrevCollide = 0; /* Compute the new hash */ pEntry->nHash = pMap->xIntHash(pMap->iNextIdx); pEntry->xKey.iKey = pMap->iNextIdx; nBucket = pEntry->nHash & (pMap->nSize - 1); /* Link to the new bucket */ pEntry->pNextCollide = pMap->apBucket[nBucket]; if( pMap->apBucket[nBucket] ){ pMap->apBucket[nBucket]->pPrevCollide = pEntry; } pEntry->pNextCollide = pMap->apBucket[nBucket]; pMap->apBucket[nBucket] = pEntry; /* Increment the automatic index */ pMap->iNextIdx++; } /* * Perform a linear search on a given hashmap. * Write a pointer to the target node on success. * Otherwise SXERR_NOTFOUND is returned on failure. * Refer to [array_intersect(),array_diff(),in_array(),...] implementations * for more information. */ static int HashmapFindValue( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pNeedle, /* Lookup key */ ph7_hashmap_node **ppNode, /* OUT: target node on success */ int bStrict /* TRUE for strict comparison */ ) { ph7_hashmap_node *pEntry; ph7_value sVal,*pVal; ph7_value sNeedle; sxi32 rc; sxu32 n; /* Perform a linear search since we cannot sort the hashmap based on values */ pEntry = pMap->pFirst; n = pMap->nEntry; PH7_MemObjInit(pMap->pVm,&sVal); PH7_MemObjInit(pMap->pVm,&sNeedle); for(;;){ if( n < 1 ){ break; } /* Extract node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ if( (pVal->iFlags|pNeedle->iFlags) & MEMOBJ_NULL ){ sxi32 iF1 = pVal->iFlags&~MEMOBJ_AUX; sxi32 iF2 = pNeedle->iFlags&~MEMOBJ_AUX; if( iF1 == iF2 ){ /* NULL values are equals */ if( ppNode ){ *ppNode = pEntry; } return SXRET_OK; } }else{ /* Duplicate value */ PH7_MemObjLoad(pVal,&sVal); PH7_MemObjLoad(pNeedle,&sNeedle); rc = PH7_MemObjCmp(&sNeedle,&sVal,bStrict,0); PH7_MemObjRelease(&sVal); PH7_MemObjRelease(&sNeedle); if( rc == 0 ){ if( ppNode ){ *ppNode = pEntry; } /* Match found*/ return SXRET_OK; } } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* No such entry */ return SXERR_NOTFOUND; } /* * Perform a linear search on a given hashmap but use an user-defined callback * for values comparison. * Write a pointer to the target node on success. * Otherwise SXERR_NOTFOUND is returned on failure. * Refer to [array_uintersect(),array_udiff()...] implementations * for more information. */ static int HashmapFindValueByCallback( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pNeedle, /* Lookup key */ ph7_value *pCallback, /* User defined callback */ ph7_hashmap_node **ppNode /* OUT: target node on success */ ) { ph7_hashmap_node *pEntry; ph7_value sResult,*pVal; ph7_value *apArg[2]; /* Callback arguments */ sxi32 rc; sxu32 n; /* Perform a linear search since we cannot sort the array based on values */ pEntry = pMap->pFirst; n = pMap->nEntry; /* Store callback result here */ PH7_MemObjInit(pMap->pVm,&sResult); /* First argument to the callback */ apArg[0] = pNeedle; for(;;){ if( n < 1 ){ break; } /* Extract node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ /* Invoke the user callback */ apArg[1] = pVal; /* Second argument to the callback */ rc = PH7_VmCallUserFunction(pMap->pVm,pCallback,2,apArg,&sResult); if( rc == SXRET_OK ){ /* Extract callback result */ if( (sResult.iFlags & MEMOBJ_INT) == 0 ){ /* Perform an int cast */ PH7_MemObjToInteger(&sResult); } rc = (sxi32)sResult.x.iVal; PH7_MemObjRelease(&sResult); if( rc == 0 ){ /* Match found*/ if( ppNode ){ *ppNode = pEntry; } return SXRET_OK; } } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* No such entry */ return SXERR_NOTFOUND; } /* * Compare two hashmaps. * Return 0 if the hashmaps are equals.Any other value indicates inequality. * Note on array comparison operators. * According to the PHP language reference manual. * Array Operators Example Name Result * $a + $b Union Union of $a and $b. * $a == $b Equality TRUE if $a and $b have the same key/value pairs. * $a === $b Identity TRUE if $a and $b have the same key/value pairs in the same * order and of the same types. * $a != $b Inequality TRUE if $a is not equal to $b. * $a <> $b Inequality TRUE if $a is not equal to $b. * $a !== $b Non-identity TRUE if $a is not identical to $b. * The + operator returns the right-hand array appended to the left-hand array; * For keys that exist in both arrays, the elements from the left-hand array will be used * and the matching elements from the right-hand array will be ignored. * "apple", "b" => "banana"); * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry"); * $c = $a + $b; // Union of $a and $b * echo "Union of \$a and \$b: \n"; * var_dump($c); * $c = $b + $a; // Union of $b and $a * echo "Union of \$b and \$a: \n"; * var_dump($c); * ?> * When executed, this script will print the following: * Union of $a and $b: * array(3) { * ["a"]=> * string(5) "apple" * ["b"]=> * string(6) "banana" * ["c"]=> * string(6) "cherry" * } * Union of $b and $a: * array(3) { * ["a"]=> * string(4) "pear" * ["b"]=> * string(10) "strawberry" * ["c"]=> * string(6) "cherry" * } * Elements of arrays are equal for the comparison if they have the same key and value. */ PH7_PRIVATE sxi32 PH7_HashmapCmp( ph7_hashmap *pLeft, /* Left hashmap */ ph7_hashmap *pRight, /* Right hashmap */ int bStrict /* TRUE for strict comparison */ ) { ph7_hashmap_node *pLe,*pRe; sxi32 rc; sxu32 n; if( pLeft == pRight ){ /* Same hashmap instance. This can easily happen since hashmaps are passed by reference. * Unlike the zend engine. */ return 0; } if( pLeft->nEntry != pRight->nEntry ){ /* Must have the same number of entries */ return pLeft->nEntry > pRight->nEntry ? 1 : -1; } /* Point to the first inserted entry of the left hashmap */ pLe = pLeft->pFirst; pRe = 0; /* cc warning */ /* Perform the comparison */ n = pLeft->nEntry; for(;;){ if( n < 1 ){ break; } if( pLe->iType == HASHMAP_INT_NODE){ /* Int key */ rc = HashmapLookupIntKey(&(*pRight),pLe->xKey.iKey,&pRe); }else{ SyBlob *pKey = &pLe->xKey.sKey; /* Blob key */ rc = HashmapLookupBlobKey(&(*pRight),SyBlobData(pKey),SyBlobLength(pKey),&pRe); } if( rc != SXRET_OK ){ /* No such entry in the right side */ return 1; } rc = 0; if( bStrict ){ /* Make sure,the keys are of the same type */ if( pLe->iType != pRe->iType ){ rc = 1; } } if( !rc ){ /* Compare nodes */ rc = HashmapNodeCmp(pLe,pRe,bStrict); } if( rc != 0 ){ /* Nodes key/value differ */ return rc; } /* Point to the next entry */ pLe = pLe->pPrev; /* Reverse link */ n--; } return 0; /* Hashmaps are equals */ } /* * Merge two hashmaps. * Note on the merge process * According to the PHP language reference manual. * Merges the elements of two arrays together so that the values of one are appended * to the end of the previous one. It returns the resulting array (pDest). * If the input arrays have the same string keys, then the later value for that key * will overwrite the previous one. If, however, the arrays contain numeric keys * the later value will not overwrite the original value, but will be appended. * Values in the input array with numeric keys will be renumbered with incrementing * keys starting from zero in the result array. */ static sxi32 HashmapMerge(ph7_hashmap *pSrc,ph7_hashmap *pDest) { ph7_hashmap_node *pEntry; ph7_value sKey,*pVal; sxi32 rc; sxu32 n; if( pSrc == pDest ){ /* Same map. This can easily happen since hashmaps are passed by reference. * Unlike the zend engine. */ return SXRET_OK; } /* Point to the first inserted entry in the source */ pEntry = pSrc->pFirst; /* Perform the merge */ for( n = 0 ; n < pSrc->nEntry ; ++n ){ /* Extract the node value */ pVal = HashmapExtractNodeValue(pEntry); if( pEntry->iType == HASHMAP_BLOB_NODE ){ /* Blob key insertion */ PH7_MemObjInitFromString(pDest->pVm,&sKey,0); PH7_MemObjStringAppend(&sKey,(const char *)SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey)); rc = PH7_HashmapInsert(&(*pDest),&sKey,pVal); PH7_MemObjRelease(&sKey); }else{ rc = HashmapInsert(&(*pDest),0/* Automatic index assign */,pVal); } if( rc != SXRET_OK ){ return rc; } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } return SXRET_OK; } /* * Overwrite entries with the same key. * Refer to the [array_replace()] implementation for more information. * According to the PHP language reference manual. * array_replace() replaces the values of the first array with the same values * from all the following arrays. If a key from the first array exists in the second * array, its value will be replaced by the value from the second array. If the key * exists in the second array, and not the first, it will be created in the first array. * If a key only exists in the first array, it will be left as is. If several arrays * are passed for replacement, they will be processed in order, the later arrays * overwriting the previous values. * array_replace() is not recursive : it will replace values in the first array * by whatever type is in the second array. */ static sxi32 HashmapOverwrite(ph7_hashmap *pSrc,ph7_hashmap *pDest) { ph7_hashmap_node *pEntry; ph7_value sKey,*pVal; sxi32 rc; sxu32 n; if( pSrc == pDest ){ /* Same map. This can easily happen since hashmaps are passed by reference. * Unlike the zend engine. */ return SXRET_OK; } /* Point to the first inserted entry in the source */ pEntry = pSrc->pFirst; /* Perform the merge */ for( n = 0 ; n < pSrc->nEntry ; ++n ){ /* Extract the node value */ pVal = HashmapExtractNodeValue(pEntry); if( pEntry->iType == HASHMAP_BLOB_NODE ){ /* Blob key insertion */ PH7_MemObjInitFromString(pDest->pVm,&sKey,0); PH7_MemObjStringAppend(&sKey,(const char *)SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey)); }else{ /* Int key insertion */ PH7_MemObjInitFromInt(pDest->pVm,&sKey,pEntry->xKey.iKey); } rc = PH7_HashmapInsert(&(*pDest),&sKey,pVal); PH7_MemObjRelease(&sKey); if( rc != SXRET_OK ){ return rc; } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } return SXRET_OK; } /* * Duplicate the contents of a hashmap. Store the copy in pDest. * Refer to the [array_pad(),array_copy(),...] implementation for more information. */ PH7_PRIVATE sxi32 PH7_HashmapDup(ph7_hashmap *pSrc,ph7_hashmap *pDest) { ph7_hashmap_node *pEntry; ph7_value sKey,*pVal; sxi32 rc; sxu32 n; if( pSrc == pDest ){ /* Same map. This can easily happen since hashmaps are passed by reference. * Unlike the zend engine. */ return SXRET_OK; } /* Point to the first inserted entry in the source */ pEntry = pSrc->pFirst; /* Perform the duplication */ for( n = 0 ; n < pSrc->nEntry ; ++n ){ /* Extract the node value */ pVal = HashmapExtractNodeValue(pEntry); if( pEntry->iType == HASHMAP_BLOB_NODE ){ /* Blob key insertion */ PH7_MemObjInitFromString(pDest->pVm,&sKey,0); PH7_MemObjStringAppend(&sKey,(const char *)SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey)); rc = PH7_HashmapInsert(&(*pDest),&sKey,pVal); PH7_MemObjRelease(&sKey); }else{ /* Int key insertion */ rc = HashmapInsertIntKey(&(*pDest),pEntry->xKey.iKey,pVal,0,FALSE); } if( rc != SXRET_OK ){ return rc; } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } return SXRET_OK; } /* * Perform the union of two hashmaps. * This operation is performed only if the user uses the '+' operator * with a variable holding an array as follows: * "apple", "b" => "banana"); * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry"); * $c = $a + $b; // Union of $a and $b * echo "Union of \$a and \$b: \n"; * var_dump($c); * $c = $b + $a; // Union of $b and $a * echo "Union of \$b and \$a: \n"; * var_dump($c); * ?> * When executed, this script will print the following: * Union of $a and $b: * array(3) { * ["a"]=> * string(5) "apple" * ["b"]=> * string(6) "banana" * ["c"]=> * string(6) "cherry" * } * Union of $b and $a: * array(3) { * ["a"]=> * string(4) "pear" * ["b"]=> * string(10) "strawberry" * ["c"]=> * string(6) "cherry" * } * The + operator returns the right-hand array appended to the left-hand array; * For keys that exist in both arrays, the elements from the left-hand array will be used * and the matching elements from the right-hand array will be ignored. */ PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft,ph7_hashmap *pRight) { ph7_hashmap_node *pEntry; sxi32 rc = SXRET_OK; ph7_value *pObj; sxu32 n; if( pLeft == pRight ){ /* Same map. This can easily happen since hashmaps are passed by reference. * Unlike the zend engine. */ return SXRET_OK; } /* Perform the union */ pEntry = pRight->pFirst; for(n = 0 ; n < pRight->nEntry ; ++n ){ /* Make sure the given key does not exists in the left array */ if( pEntry->iType == HASHMAP_BLOB_NODE ){ /* BLOB key */ if( SXRET_OK != HashmapLookupBlobKey(&(*pLeft),SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey),0) ){ pObj = HashmapExtractNodeValue(pEntry); if( pObj ){ /* Perform the insertion */ rc = HashmapInsertBlobKey(&(*pLeft),SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey), pObj,0,FALSE); if( rc != SXRET_OK ){ return rc; } } } }else{ /* INT key */ if( SXRET_OK != HashmapLookupIntKey(&(*pLeft),pEntry->xKey.iKey,0) ){ pObj = HashmapExtractNodeValue(pEntry); if( pObj ){ /* Perform the insertion */ rc = HashmapInsertIntKey(&(*pLeft),pEntry->xKey.iKey,pObj,0,FALSE); if( rc != SXRET_OK ){ return rc; } } } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } return SXRET_OK; } /* * Allocate a new hashmap. * Return a pointer to the freshly allocated hashmap on success.NULL otherwise. */ PH7_PRIVATE ph7_hashmap * PH7_NewHashmap( ph7_vm *pVm, /* VM that trigger the hashmap creation */ sxu32 (*xIntHash)(sxi64), /* Hash function for int keys.NULL otherwise*/ sxu32 (*xBlobHash)(const void *,sxu32) /* Hash function for BLOB keys.NULL otherwise */ ) { ph7_hashmap *pMap; /* Allocate a new instance */ pMap = (ph7_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_hashmap)); if( pMap == 0 ){ return 0; } /* Zero the structure */ SyZero(pMap,sizeof(ph7_hashmap)); /* Fill in the structure */ pMap->pVm = &(*pVm); pMap->iRef = 1; /* Default hash functions */ pMap->xIntHash = xIntHash ? xIntHash : IntHash; pMap->xBlobHash = xBlobHash ? xBlobHash : BinHash; return pMap; } /* * Install superglobals in the given virtual machine. * Note on superglobals. * According to the PHP language reference manual. * Superglobals are built-in variables that are always available in all scopes. * Description * Several predefined variables in PHP are "superglobals", which means they * are available in all scopes throughout a script. There is no need to do * global $variable; to access them within functions or methods. * These superglobal variables are: * $GLOBALS * $_SERVER * $_GET * $_POST * $_FILES * $_COOKIE * $_SESSION * $_REQUEST * $_ENV */ PH7_PRIVATE sxi32 PH7_HashmapCreateSuper(ph7_vm *pVm) { static const char * azSuper[] = { "_SERVER", /* $_SERVER */ "_GET", /* $_GET */ "_POST", /* $_POST */ "_FILES", /* $_FILES */ "_COOKIE", /* $_COOKIE */ "_SESSION", /* $_SESSION */ "_REQUEST", /* $_REQUEST */ "_ENV", /* $_ENV */ "_HEADER", /* $_HEADER */ "argv" /* $argv */ }; ph7_hashmap *pMap; ph7_value *pObj; SyString *pFile; sxi32 rc; sxu32 n; /* Allocate a new hashmap for the $GLOBALS array */ pMap = PH7_NewHashmap(&(*pVm),0,0); if( pMap == 0 ){ return SXERR_MEM; } pVm->pGlobal = pMap; /* Reserve a ph7_value for the $GLOBALS array*/ pObj = PH7_ReserveMemObj(&(*pVm)); if( pObj == 0 ){ return SXERR_MEM; } PH7_MemObjInitFromArray(&(*pVm),pObj,pMap); /* Record object index */ pVm->nGlobalIdx = pObj->nIdx; /* Install the special $GLOBALS array */ rc = SyHashInsert(&pVm->hSuper,(const void *)"GLOBALS",sizeof("GLOBALS")-1,SX_INT_TO_PTR(pVm->nGlobalIdx)); if( rc != SXRET_OK ){ return rc; } /* Install superglobals now */ for( n = 0 ; n < SX_ARRAYSIZE(azSuper) ; n++ ){ ph7_value *pSuper; /* Request an empty array */ pSuper = ph7_new_array(&(*pVm)); if( pSuper == 0 ){ return SXERR_MEM; } /* Install */ rc = ph7_vm_config(&(*pVm),PH7_VM_CONFIG_CREATE_SUPER,azSuper[n]/* Super-global name*/,pSuper/* Super-global value */); if( rc != SXRET_OK ){ return rc; } /* Release the value now it have been installed */ ph7_release_value(&(*pVm),pSuper); } /* Set some $_SERVER entries */ pFile = (SyString *)SySetPeek(&pVm->aFiles); /* * 'SCRIPT_FILENAME' * The absolute pathname of the currently executing script. */ ph7_vm_config(pVm,PH7_VM_CONFIG_SERVER_ATTR, "SCRIPT_FILENAME", pFile ? pFile->zString : ":Memory:", pFile ? pFile->nByte : sizeof(":Memory:") - 1 ); /* All done,all super-global are installed now */ return SXRET_OK; } /* * Release a hashmap. */ PH7_PRIVATE sxi32 PH7_HashmapRelease(ph7_hashmap *pMap,int FreeDS) { ph7_hashmap_node *pEntry,*pNext; ph7_vm *pVm = pMap->pVm; sxu32 n; if( pMap == pVm->pGlobal ){ /* Cannot delete the $GLOBALS array */ PH7_VmThrowError(pMap->pVm,0,PH7_CTX_NOTICE,"$GLOBALS is a read-only array,deletion is forbidden"); return SXRET_OK; } /* Start the release process */ n = 0; pEntry = pMap->pFirst; for(;;){ if( n >= pMap->nEntry ){ break; } pNext = pEntry->pPrev; /* Reverse link */ /* Remove the reference from the foreign table */ PH7_VmRefObjRemove(pVm,pEntry->nValIdx,0,pEntry); if( (pEntry->iFlags & HASHMAP_NODE_FOREIGN_OBJ) == 0 ){ /* Restore the ph7_value to the free list */ PH7_VmUnsetMemObj(pVm,pEntry->nValIdx,FALSE); } /* Release the node */ if( pEntry->iType == HASHMAP_BLOB_NODE ){ SyBlobRelease(&pEntry->xKey.sKey); } SyMemBackendPoolFree(&pVm->sAllocator,pEntry); /* Point to the next entry */ pEntry = pNext; n++; } if( pMap->nEntry > 0 ){ /* Release the hash bucket */ SyMemBackendFree(&pVm->sAllocator,pMap->apBucket); } if( FreeDS ){ /* Free the whole instance */ SyMemBackendPoolFree(&pVm->sAllocator,pMap); }else{ /* Keep the instance but reset it's fields */ pMap->apBucket = 0; pMap->iNextIdx = 0; pMap->nEntry = pMap->nSize = 0; pMap->pFirst = pMap->pLast = pMap->pCur = 0; } return SXRET_OK; } /* * Decrement the reference count of a given hashmap. * If the count reaches zero which mean no more variables * are pointing to this hashmap,then release the whole instance. */ PH7_PRIVATE void PH7_HashmapUnref(ph7_hashmap *pMap) { ph7_vm *pVm = pMap->pVm; /* TICKET 1432-49: $GLOBALS is not subject to garbage collection */ pMap->iRef--; if( pMap->iRef < 1 && pMap != pVm->pGlobal){ PH7_HashmapRelease(pMap,TRUE); } } /* * Check if a given key exists in the given hashmap. * Write a pointer to the target node on success. * Otherwise SXERR_NOTFOUND is returned on failure. */ PH7_PRIVATE sxi32 PH7_HashmapLookup( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pKey, /* Lookup key */ ph7_hashmap_node **ppNode /* OUT: Target node on success */ ) { sxi32 rc; if( pMap->nEntry < 1 ){ /* TICKET 1433-25: Don't bother hashing,the hashmap is empty anyway. */ return SXERR_NOTFOUND; } rc = HashmapLookup(&(*pMap),&(*pKey),ppNode); return rc; } /* * Insert a given key and it's associated value (if any) in the given * hashmap. * If a node with the given key already exists in the database * then this function overwrite the old value. */ PH7_PRIVATE sxi32 PH7_HashmapInsert( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pKey, /* Lookup key */ ph7_value *pVal /* Node value.NULL otherwise */ ) { sxi32 rc; if( pVal && (pVal->iFlags & MEMOBJ_HASHMAP) && (ph7_hashmap *)pVal->x.pOther == pMap->pVm->pGlobal ){ /* * TICKET 1433-35: Insertion in the $GLOBALS array is forbidden. */ PH7_VmThrowError(pMap->pVm,0,PH7_CTX_ERR,"$GLOBALS is a read-only array,insertion is forbidden"); return SXRET_OK; } rc = HashmapInsert(&(*pMap),&(*pKey),&(*pVal)); return rc; } /* * Insert a given key and it's associated value (foreign index) in the given * hashmap. * This is insertion by reference so be careful to mark the node * with the HASHMAP_NODE_FOREIGN_OBJ flag being set. * The insertion by reference is triggered when the following * expression is encountered. * $var = 10; * $a = array(&var); * OR * $a[] =& $var; * That is,$var is a foreign ph7_value and the $a array have no control * over it's contents. * Note that the node that hold the foreign ph7_value is automatically * removed when the foreign ph7_value is unset. * Example: * $var = 10; * $a[] =& $var; * echo count($a).PHP_EOL; //1 * //Unset the foreign ph7_value now * unset($var); * echo count($a); //0 * Note that this is a PH7 eXtension. * Refer to the official documentation for more information. * If a node with the given key already exists in the database * then this function overwrite the old value. */ PH7_PRIVATE sxi32 PH7_HashmapInsertByRef( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pKey, /* Lookup key */ sxu32 nRefIdx /* Foreign ph7_value index */ ) { sxi32 rc; if( nRefIdx == pMap->pVm->nGlobalIdx ){ /* * TICKET 1433-35: Insertion in the $GLOBALS array is forbidden. */ PH7_VmThrowError(pMap->pVm,0,PH7_CTX_ERR,"$GLOBALS is a read-only array,insertion is forbidden"); return SXRET_OK; } rc = HashmapInsertByRef(&(*pMap),&(*pKey),nRefIdx); return rc; } /* * Reset the node cursor of a given hashmap. */ PH7_PRIVATE void PH7_HashmapResetLoopCursor(ph7_hashmap *pMap) { /* Reset the loop cursor */ pMap->pCur = pMap->pFirst; } /* * Return a pointer to the node currently pointed by the node cursor. * If the cursor reaches the end of the list,then this function * return NULL. * Note that the node cursor is automatically advanced by this function. */ PH7_PRIVATE ph7_hashmap_node * PH7_HashmapGetNextEntry(ph7_hashmap *pMap) { ph7_hashmap_node *pCur = pMap->pCur; if( pCur == 0 ){ /* End of the list,return null */ return 0; } /* Advance the node cursor */ pMap->pCur = pCur->pPrev; /* Reverse link */ return pCur; } /* * Extract a node value. */ PH7_PRIVATE void PH7_HashmapExtractNodeValue(ph7_hashmap_node *pNode,ph7_value *pValue,int bStore) { ph7_value *pEntry = HashmapExtractNodeValue(pNode); if( pEntry ){ if( bStore ){ PH7_MemObjStore(pEntry,pValue); }else{ PH7_MemObjLoad(pEntry,pValue); } }else{ PH7_MemObjRelease(pValue); } } /* * Extract a node key. */ PH7_PRIVATE void PH7_HashmapExtractNodeKey(ph7_hashmap_node *pNode,ph7_value *pKey) { /* Fill with the current key */ if( pNode->iType == HASHMAP_INT_NODE ){ if( SyBlobLength(&pKey->sBlob) > 0 ){ SyBlobRelease(&pKey->sBlob); } pKey->x.iVal = pNode->xKey.iKey; MemObjSetType(pKey,MEMOBJ_INT); }else{ SyBlobReset(&pKey->sBlob); SyBlobAppend(&pKey->sBlob,SyBlobData(&pNode->xKey.sKey),SyBlobLength(&pNode->xKey.sKey)); MemObjSetType(pKey,MEMOBJ_STRING); } } #ifndef PH7_DISABLE_BUILTIN_FUNC /* * Store the address of nodes value in the given container. * Refer to the [vfprintf(),vprintf(),vsprintf()] implementations * defined in 'builtin.c' for more information. */ PH7_PRIVATE int PH7_HashmapValuesToSet(ph7_hashmap *pMap,SySet *pOut) { ph7_hashmap_node *pEntry = pMap->pFirst; ph7_value *pValue; sxu32 n; /* Initialize the container */ SySetInit(pOut,&pMap->pVm->sAllocator,sizeof(ph7_value *)); for(n = 0 ; n < pMap->nEntry ; n++ ){ /* Extract node value */ pValue = HashmapExtractNodeValue(pEntry); if( pValue ){ SySetPut(pOut,(const void *)&pValue); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Total inserted entries */ return (int)SySetUsed(pOut); } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * Merge sort. * The merge sort implementation is based on the one found in the SQLite3 source tree. * Status: Public domain */ /* Node comparison callback signature */ typedef sxi32 (*ProcNodeCmp)(ph7_hashmap_node *,ph7_hashmap_node *,void *); /* ** Inputs: ** a: A sorted, null-terminated linked list. (May be null). ** b: A sorted, null-terminated linked list. (May be null). ** cmp: A pointer to the comparison function. ** ** Return Value: ** A pointer to the head of a sorted list containing the elements ** of both a and b. ** ** Side effects: ** The "next","prev" pointers for elements in the lists a and b are ** changed. */ static ph7_hashmap_node * HashmapNodeMerge(ph7_hashmap_node *pA,ph7_hashmap_node *pB,ProcNodeCmp xCmp,void *pCmpData) { ph7_hashmap_node result,*pTail; /* Prevent compiler warning */ result.pNext = result.pPrev = 0; pTail = &result; while( pA && pB ){ if( xCmp(pA,pB,pCmpData) < 0 ){ pTail->pPrev = pA; pA->pNext = pTail; pTail = pA; pA = pA->pPrev; }else{ pTail->pPrev = pB; pB->pNext = pTail; pTail = pB; pB = pB->pPrev; } } if( pA ){ pTail->pPrev = pA; pA->pNext = pTail; }else if( pB ){ pTail->pPrev = pB; pB->pNext = pTail; }else{ pTail->pPrev = pTail->pNext = 0; } return result.pPrev; } /* ** Inputs: ** Map: Input hashmap ** cmp: A comparison function. ** ** Return Value: ** Sorted hashmap. ** ** Side effects: ** The "next" pointers for elements in list are changed. */ #define N_SORT_BUCKET 32 static sxi32 HashmapMergeSort(ph7_hashmap *pMap,ProcNodeCmp xCmp,void *pCmpData) { ph7_hashmap_node *a[N_SORT_BUCKET], *p,*pIn; sxu32 i; SyZero(a,sizeof(a)); /* Point to the first inserted entry */ pIn = pMap->pFirst; while( pIn ){ p = pIn; pIn = p->pPrev; p->pPrev = 0; for(i=0; ipNext = 0; /* Reflect the change */ pMap->pFirst = p; /* Reset the loop cursor */ pMap->pCur = pMap->pFirst; return SXRET_OK; } /* * Node comparison callback. * used-by: [sort(),asort(),...] */ static sxi32 HashmapCmpCallback1(ph7_hashmap_node *pA,ph7_hashmap_node *pB,void *pCmpData) { ph7_value sA,sB; sxi32 iFlags; int rc; if( pCmpData == 0 ){ /* Perform a standard comparison */ rc = HashmapNodeCmp(pA,pB,FALSE); return rc; } iFlags = SX_PTR_TO_INT(pCmpData); /* Duplicate node values */ PH7_MemObjInit(pA->pMap->pVm,&sA); PH7_MemObjInit(pA->pMap->pVm,&sB); PH7_HashmapExtractNodeValue(pA,&sA,FALSE); PH7_HashmapExtractNodeValue(pB,&sB,FALSE); if( iFlags == 5 ){ /* String cast */ if( (sA.iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(&sA); } if( (sB.iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(&sB); } }else{ /* Numeric cast */ PH7_MemObjToNumeric(&sA); PH7_MemObjToNumeric(&sB); } /* Perform the comparison */ rc = PH7_MemObjCmp(&sA,&sB,FALSE,0); PH7_MemObjRelease(&sA); PH7_MemObjRelease(&sB); return rc; } /* * Node comparison callback: Compare nodes by keys only. * used-by: [ksort()] */ static sxi32 HashmapCmpCallback2(ph7_hashmap_node *pA,ph7_hashmap_node *pB,void *pCmpData) { sxi32 rc; SXUNUSED(pCmpData); /* cc warning */ if( pA->iType == HASHMAP_BLOB_NODE && pB->iType == HASHMAP_BLOB_NODE ){ /* Perform a string comparison */ rc = SyBlobCmp(&pA->xKey.sKey,&pB->xKey.sKey); }else{ SyString sStr; sxi64 iA,iB; /* Perform a numeric comparison */ if( pA->iType == HASHMAP_BLOB_NODE ){ /* Cast to 64-bit integer */ SyStringInitFromBuf(&sStr,SyBlobData(&pA->xKey.sKey),SyBlobLength(&pA->xKey.sKey)); if( sStr.nByte < 1 ){ iA = 0; }else{ SyStrToInt64(sStr.zString,sStr.nByte,(void *)&iA,0); } }else{ iA = pA->xKey.iKey; } if( pB->iType == HASHMAP_BLOB_NODE ){ /* Cast to 64-bit integer */ SyStringInitFromBuf(&sStr,SyBlobData(&pB->xKey.sKey),SyBlobLength(&pB->xKey.sKey)); if( sStr.nByte < 1 ){ iB = 0; }else{ SyStrToInt64(sStr.zString,sStr.nByte,(void *)&iB,0); } }else{ iB = pB->xKey.iKey; } rc = (sxi32)(iA-iB); } /* Comparison result */ return rc; } /* * Node comparison callback. * Used by: [rsort(),arsort()]; */ static sxi32 HashmapCmpCallback3(ph7_hashmap_node *pA,ph7_hashmap_node *pB,void *pCmpData) { ph7_value sA,sB; sxi32 iFlags; int rc; if( pCmpData == 0 ){ /* Perform a standard comparison */ rc = HashmapNodeCmp(pA,pB,FALSE); return -rc; } iFlags = SX_PTR_TO_INT(pCmpData); /* Duplicate node values */ PH7_MemObjInit(pA->pMap->pVm,&sA); PH7_MemObjInit(pA->pMap->pVm,&sB); PH7_HashmapExtractNodeValue(pA,&sA,FALSE); PH7_HashmapExtractNodeValue(pB,&sB,FALSE); if( iFlags == 5 ){ /* String cast */ if( (sA.iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(&sA); } if( (sB.iFlags & MEMOBJ_STRING) == 0 ){ PH7_MemObjToString(&sB); } }else{ /* Numeric cast */ PH7_MemObjToNumeric(&sA); PH7_MemObjToNumeric(&sB); } /* Perform the comparison */ rc = PH7_MemObjCmp(&sA,&sB,FALSE,0); PH7_MemObjRelease(&sA); PH7_MemObjRelease(&sB); return -rc; } /* * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison. * used-by: [usort(),uasort()] */ static sxi32 HashmapCmpCallback4(ph7_hashmap_node *pA,ph7_hashmap_node *pB,void *pCmpData) { ph7_value sResult,*pCallback; ph7_value *pV1,*pV2; ph7_value *apArg[2]; /* Callback arguments */ sxi32 rc; /* Point to the desired callback */ pCallback = (ph7_value *)pCmpData; /* initialize the result value */ PH7_MemObjInit(pA->pMap->pVm,&sResult); /* Extract nodes values */ pV1 = HashmapExtractNodeValue(pA); pV2 = HashmapExtractNodeValue(pB); apArg[0] = pV1; apArg[1] = pV2; /* Invoke the callback */ rc = PH7_VmCallUserFunction(pA->pMap->pVm,pCallback,2,apArg,&sResult); if( rc != SXRET_OK ){ /* An error occured while calling user defined function [i.e: not defined] */ rc = -1; /* Set a dummy result */ }else{ /* Extract callback result */ if((sResult.iFlags & MEMOBJ_INT) == 0 ){ /* Perform an int cast */ PH7_MemObjToInteger(&sResult); } rc = (sxi32)sResult.x.iVal; } PH7_MemObjRelease(&sResult); /* Callback result */ return rc; } /* * Node comparison callback: Compare nodes by keys only. * used-by: [krsort()] */ static sxi32 HashmapCmpCallback5(ph7_hashmap_node *pA,ph7_hashmap_node *pB,void *pCmpData) { sxi32 rc; SXUNUSED(pCmpData); /* cc warning */ if( pA->iType == HASHMAP_BLOB_NODE && pB->iType == HASHMAP_BLOB_NODE ){ /* Perform a string comparison */ rc = SyBlobCmp(&pA->xKey.sKey,&pB->xKey.sKey); }else{ SyString sStr; sxi64 iA,iB; /* Perform a numeric comparison */ if( pA->iType == HASHMAP_BLOB_NODE ){ /* Cast to 64-bit integer */ SyStringInitFromBuf(&sStr,SyBlobData(&pA->xKey.sKey),SyBlobLength(&pA->xKey.sKey)); if( sStr.nByte < 1 ){ iA = 0; }else{ SyStrToInt64(sStr.zString,sStr.nByte,(void *)&iA,0); } }else{ iA = pA->xKey.iKey; } if( pB->iType == HASHMAP_BLOB_NODE ){ /* Cast to 64-bit integer */ SyStringInitFromBuf(&sStr,SyBlobData(&pB->xKey.sKey),SyBlobLength(&pB->xKey.sKey)); if( sStr.nByte < 1 ){ iB = 0; }else{ SyStrToInt64(sStr.zString,sStr.nByte,(void *)&iB,0); } }else{ iB = pB->xKey.iKey; } rc = (sxi32)(iA-iB); } return -rc; /* Reverse result */ } /* * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison. * used-by: [uksort()] */ static sxi32 HashmapCmpCallback6(ph7_hashmap_node *pA,ph7_hashmap_node *pB,void *pCmpData) { ph7_value sResult,*pCallback; ph7_value *apArg[2]; /* Callback arguments */ ph7_value sK1,sK2; sxi32 rc; /* Point to the desired callback */ pCallback = (ph7_value *)pCmpData; /* initialize the result value */ PH7_MemObjInit(pA->pMap->pVm,&sResult); PH7_MemObjInit(pA->pMap->pVm,&sK1); PH7_MemObjInit(pA->pMap->pVm,&sK2); /* Extract nodes keys */ PH7_HashmapExtractNodeKey(pA,&sK1); PH7_HashmapExtractNodeKey(pB,&sK2); apArg[0] = &sK1; apArg[1] = &sK2; /* Mark keys as constants */ sK1.nIdx = SXU32_HIGH; sK2.nIdx = SXU32_HIGH; /* Invoke the callback */ rc = PH7_VmCallUserFunction(pA->pMap->pVm,pCallback,2,apArg,&sResult); if( rc != SXRET_OK ){ /* An error occured while calling user defined function [i.e: not defined] */ rc = -1; /* Set a dummy result */ }else{ /* Extract callback result */ if((sResult.iFlags & MEMOBJ_INT) == 0 ){ /* Perform an int cast */ PH7_MemObjToInteger(&sResult); } rc = (sxi32)sResult.x.iVal; } PH7_MemObjRelease(&sResult); PH7_MemObjRelease(&sK1); PH7_MemObjRelease(&sK2); /* Callback result */ return rc; } /* * Node comparison callback: Random node comparison. * used-by: [shuffle()] */ static sxi32 HashmapCmpCallback7(ph7_hashmap_node *pA,ph7_hashmap_node *pB,void *pCmpData) { sxu32 n; SXUNUSED(pB); /* cc warning */ SXUNUSED(pCmpData); /* Grab a random number */ n = PH7_VmRandomNum(pA->pMap->pVm); /* if the random number is odd then the first node 'pA' is greater then * the second node 'pB'. Otherwise the reverse is assumed. */ return n&1 ? 1 : -1; } /* * Rehash all nodes keys after a merge-sort have been applied. * Used by [sort(),usort() and rsort()]. */ static void HashmapSortRehash(ph7_hashmap *pMap) { ph7_hashmap_node *p,*pLast; sxu32 i; /* Rehash all entries */ pLast = p = pMap->pFirst; pMap->iNextIdx = 0; /* Reset the automatic index */ i = 0; for( ;; ){ if( i >= pMap->nEntry ){ pMap->pLast = pLast; /* Fix the last link broken by the merge-sort */ break; } if( p->iType == HASHMAP_BLOB_NODE ){ /* Do not maintain index association as requested by the PHP specification */ SyBlobRelease(&p->xKey.sKey); /* Change key type */ p->iType = HASHMAP_INT_NODE; } HashmapRehashIntNode(p); /* Point to the next entry */ i++; pLast = p; p = p->pPrev; /* Reverse link */ } } /* * Array functions implementation. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * bool sort(array &$array[,int $sort_flags = SORT_REGULAR ] ) * Sort an array. * Parameters * $array * The input array. * $sort_flags * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: * Sorting type flags: * SORT_REGULAR - compare items normally (don't change types) * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings * Return * TRUE on success or FALSE on failure. * */ static int ph7_hashmap_sort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ sxi32 iCmpFlags = 0; if( nArg > 1 ){ /* Extract comparison flags */ iCmpFlags = ph7_value_to_int(apArg[1]); if( iCmpFlags == 3 /* SORT_REGULAR */ ){ iCmpFlags = 0; /* Standard comparison */ } } /* Do the merge sort */ HashmapMergeSort(pMap,HashmapCmpCallback1,SX_INT_TO_PTR(iCmpFlags)); /* Rehash [Do not maintain index association as requested by the PHP specification] */ HashmapSortRehash(pMap); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool asort(array &$array[,int $sort_flags = SORT_REGULAR ] ) * Sort an array and maintain index association. * Parameters * $array * The input array. * $sort_flags * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: * Sorting type flags: * SORT_REGULAR - compare items normally (don't change types) * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_asort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ sxi32 iCmpFlags = 0; if( nArg > 1 ){ /* Extract comparison flags */ iCmpFlags = ph7_value_to_int(apArg[1]); if( iCmpFlags == 3 /* SORT_REGULAR */ ){ iCmpFlags = 0; /* Standard comparison */ } } /* Do the merge sort */ HashmapMergeSort(pMap,HashmapCmpCallback1,SX_INT_TO_PTR(iCmpFlags)); /* Fix the last link broken by the merge */ while(pMap->pLast->pPrev){ pMap->pLast = pMap->pLast->pPrev; } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool arsort(array &$array[,int $sort_flags = SORT_REGULAR ] ) * Sort an array in reverse order and maintain index association. * Parameters * $array * The input array. * $sort_flags * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: * Sorting type flags: * SORT_REGULAR - compare items normally (don't change types) * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_arsort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ sxi32 iCmpFlags = 0; if( nArg > 1 ){ /* Extract comparison flags */ iCmpFlags = ph7_value_to_int(apArg[1]); if( iCmpFlags == 3 /* SORT_REGULAR */ ){ iCmpFlags = 0; /* Standard comparison */ } } /* Do the merge sort */ HashmapMergeSort(pMap,HashmapCmpCallback3,SX_INT_TO_PTR(iCmpFlags)); /* Fix the last link broken by the merge */ while(pMap->pLast->pPrev){ pMap->pLast = pMap->pLast->pPrev; } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool ksort(array &$array[,int $sort_flags = SORT_REGULAR ] ) * Sort an array by key. * Parameters * $array * The input array. * $sort_flags * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: * Sorting type flags: * SORT_REGULAR - compare items normally (don't change types) * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_ksort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ sxi32 iCmpFlags = 0; if( nArg > 1 ){ /* Extract comparison flags */ iCmpFlags = ph7_value_to_int(apArg[1]); if( iCmpFlags == 3 /* SORT_REGULAR */ ){ iCmpFlags = 0; /* Standard comparison */ } } /* Do the merge sort */ HashmapMergeSort(pMap,HashmapCmpCallback2,SX_INT_TO_PTR(iCmpFlags)); /* Fix the last link broken by the merge */ while(pMap->pLast->pPrev){ pMap->pLast = pMap->pLast->pPrev; } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool krsort(array &$array[,int $sort_flags = SORT_REGULAR ] ) * Sort an array by key in reverse order. * Parameters * $array * The input array. * $sort_flags * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: * Sorting type flags: * SORT_REGULAR - compare items normally (don't change types) * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_krsort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ sxi32 iCmpFlags = 0; if( nArg > 1 ){ /* Extract comparison flags */ iCmpFlags = ph7_value_to_int(apArg[1]); if( iCmpFlags == 3 /* SORT_REGULAR */ ){ iCmpFlags = 0; /* Standard comparison */ } } /* Do the merge sort */ HashmapMergeSort(pMap,HashmapCmpCallback5,SX_INT_TO_PTR(iCmpFlags)); /* Fix the last link broken by the merge */ while(pMap->pLast->pPrev){ pMap->pLast = pMap->pLast->pPrev; } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool rsort(array &$array[,int $sort_flags = SORT_REGULAR ] ) * Sort an array in reverse order. * Parameters * $array * The input array. * $sort_flags * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: * Sorting type flags: * SORT_REGULAR - compare items normally (don't change types) * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_rsort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ sxi32 iCmpFlags = 0; if( nArg > 1 ){ /* Extract comparison flags */ iCmpFlags = ph7_value_to_int(apArg[1]); if( iCmpFlags == 3 /* SORT_REGULAR */ ){ iCmpFlags = 0; /* Standard comparison */ } } /* Do the merge sort */ HashmapMergeSort(pMap,HashmapCmpCallback3,SX_INT_TO_PTR(iCmpFlags)); /* Rehash [Do not maintain index association as requested by the PHP specification] */ HashmapSortRehash(pMap); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool usort(array &$array,callable $cmp_function) * Sort an array by values using a user-defined comparison function. * Parameters * $array * The input array. * $cmp_function * The comparison function must return an integer less than, equal to, or greater * than zero if the first argument is considered to be respectively less than, equal * to, or greater than the second. * int callback ( mixed $a, mixed $b ) * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_usort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ ph7_value *pCallback = 0; ProcNodeCmp xCmp; xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */ if( nArg > 1 && ph7_value_is_callable(apArg[1]) ){ /* Point to the desired callback */ pCallback = apArg[1]; }else{ /* Use the default comparison function */ xCmp = HashmapCmpCallback1; } /* Do the merge sort */ HashmapMergeSort(pMap,xCmp,pCallback); /* Rehash [Do not maintain index association as requested by the PHP specification] */ HashmapSortRehash(pMap); } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool uasort(array &$array,callable $cmp_function) * Sort an array by values using a user-defined comparison function * and maintain index association. * Parameters * $array * The input array. * $cmp_function * The comparison function must return an integer less than, equal to, or greater * than zero if the first argument is considered to be respectively less than, equal * to, or greater than the second. * int callback ( mixed $a, mixed $b ) * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_uasort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ ph7_value *pCallback = 0; ProcNodeCmp xCmp; xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */ if( nArg > 1 && ph7_value_is_callable(apArg[1]) ){ /* Point to the desired callback */ pCallback = apArg[1]; }else{ /* Use the default comparison function */ xCmp = HashmapCmpCallback1; } /* Do the merge sort */ HashmapMergeSort(pMap,xCmp,pCallback); /* Fix the last link broken by the merge */ while(pMap->pLast->pPrev){ pMap->pLast = pMap->pLast->pPrev; } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool uksort(array &$array,callable $cmp_function) * Sort an array by keys using a user-defined comparison * function and maintain index association. * Parameters * $array * The input array. * $cmp_function * The comparison function must return an integer less than, equal to, or greater * than zero if the first argument is considered to be respectively less than, equal * to, or greater than the second. * int callback ( mixed $a, mixed $b ) * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_uksort(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ ph7_value *pCallback = 0; ProcNodeCmp xCmp; xCmp = HashmapCmpCallback6; /* User-defined function as the comparison callback */ if( nArg > 1 && ph7_value_is_callable(apArg[1]) ){ /* Point to the desired callback */ pCallback = apArg[1]; }else{ /* Use the default comparison function */ xCmp = HashmapCmpCallback2; } /* Do the merge sort */ HashmapMergeSort(pMap,xCmp,pCallback); /* Fix the last link broken by the merge */ while(pMap->pLast->pPrev){ pMap->pLast = pMap->pLast->pPrev; } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * bool shuffle(array &$array) * shuffles (randomizes the order of the elements in) an array. * Parameters * $array * The input array. * Return * TRUE on success or FALSE on failure. * */ static int ph7_hashmap_shuffle(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry > 1 ){ /* Do the merge sort */ HashmapMergeSort(pMap,HashmapCmpCallback7,0); /* Fix the last link broken by the merge */ while(pMap->pLast->pPrev){ pMap->pLast = pMap->pLast->pPrev; } } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * int count(array $var [, int $mode = COUNT_NORMAL ]) * Count all elements in an array, or something in an object. * Parameters * $var * The array or the object. * $mode * If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count() * will recursively count the array. This is particularly useful for counting * all the elements of a multidimensional array. count() does not detect infinite * recursion. * Return * Returns the number of elements in the array. */ static int ph7_hashmap_count(ph7_context *pCtx,int nArg,ph7_value **apArg) { int bRecursive = FALSE; sxi64 iCount; if( nArg < 1 ){ /* Missing arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } if( !ph7_value_is_array(apArg[0]) ){ /* TICKET 1433-19: Handle objects */ int res = !ph7_value_is_null(apArg[0]); ph7_result_int(pCtx,res); return PH7_OK; } if( nArg > 1 ){ /* Recursive count? */ bRecursive = ph7_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */; } /* Count */ iCount = HashmapCount((ph7_hashmap *)apArg[0]->x.pOther,bRecursive,0); ph7_result_int64(pCtx,iCount); return PH7_OK; } /* * bool array_key_exists(value $key,array $search) * Checks if the given key or index exists in the array. * Parameters * $key * Value to check. * $search * An array with keys to check. * Return * TRUE on success or FALSE on failure. */ static int ph7_hashmap_key_exists(ph7_context *pCtx,int nArg,ph7_value **apArg) { sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[1]) ){ /* Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the lookup */ rc = PH7_HashmapLookup((ph7_hashmap *)apArg[1]->x.pOther,apArg[0],0); /* lookup result */ ph7_result_bool(pCtx,rc == SXRET_OK ? 1 : 0); return PH7_OK; } /* * value array_pop(array $array) * POP the last inserted element from the array. * Parameter * The array to get the value from. * Return * Poped value or NULL on failure. */ static int ph7_hashmap_pop(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return null */ ph7_result_null(pCtx); return PH7_OK; } pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry < 1 ){ /* Noting to pop,return NULL */ ph7_result_null(pCtx); }else{ ph7_hashmap_node *pLast = pMap->pLast; ph7_value *pObj; pObj = HashmapExtractNodeValue(pLast); if( pObj ){ /* Node value */ ph7_result_value(pCtx,pObj); /* Unlink the node */ PH7_HashmapUnlinkNode(pLast,TRUE); }else{ ph7_result_null(pCtx); } /* Reset the cursor */ pMap->pCur = pMap->pFirst; } return PH7_OK; } /* * int array_push($array,$var,...) * Push one or more elements onto the end of array. (Stack insertion) * Parameters * array * The input array. * var * On or more value to push. * Return * New array count (including old items). */ static int ph7_hashmap_push(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; sxi32 rc; int i; if( nArg < 1 ){ /* Missing arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Start pushing given values */ for( i = 1 ; i < nArg ; ++i ){ rc = PH7_HashmapInsert(pMap,0,apArg[i]); if( rc != SXRET_OK ){ break; } } /* Return the new count */ ph7_result_int64(pCtx,(sxi64)pMap->nEntry); return PH7_OK; } /* * value array_shift(array $array) * Shift an element off the beginning of array. * Parameter * The array to get the value from. * Return * Shifted value or NULL on failure. */ static int ph7_hashmap_shift(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry < 1 ){ /* Empty hashmap,return NULL */ ph7_result_null(pCtx); }else{ ph7_hashmap_node *pEntry = pMap->pFirst; ph7_value *pObj; sxu32 n; pObj = HashmapExtractNodeValue(pEntry); if( pObj ){ /* Node value */ ph7_result_value(pCtx,pObj); /* Unlink the first node */ PH7_HashmapUnlinkNode(pEntry,TRUE); }else{ ph7_result_null(pCtx); } /* Rehash all int keys */ n = pMap->nEntry; pEntry = pMap->pFirst; pMap->iNextIdx = 0; /* Reset the automatic index */ for(;;){ if( n < 1 ){ break; } if( pEntry->iType == HASHMAP_INT_NODE ){ HashmapRehashIntNode(pEntry); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Reset the cursor */ pMap->pCur = pMap->pFirst; } return PH7_OK; } /* * Extract the node cursor value. */ static sxi32 HashmapCurrentValue(ph7_context *pCtx,ph7_hashmap *pMap,int iDirection) { ph7_hashmap_node *pCur = pMap->pCur; ph7_value *pVal; if( pCur == 0 ){ /* Cursor does not point to anything,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( iDirection != 0 ){ if( iDirection > 0 ){ /* Point to the next entry */ pMap->pCur = pCur->pPrev; /* Reverse link */ pCur = pMap->pCur; }else{ /* Point to the previous entry */ pMap->pCur = pCur->pNext; /* Reverse link */ pCur = pMap->pCur; } if( pCur == 0 ){ /* End of input reached,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } } /* Point to the desired element */ pVal = HashmapExtractNodeValue(pCur); if( pVal ){ ph7_result_value(pCtx,pVal); }else{ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * value current(array $array) * Return the current element in an array. * Parameter * $input: The input array. * Return * The current() function simply returns the value of the array element that's currently * being pointed to by the internal pointer. It does not move the pointer in any way. * If the internal pointer points beyond the end of the elements list or the array * is empty, current() returns FALSE. */ static int ph7_hashmap_current(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } HashmapCurrentValue(&(*pCtx),(ph7_hashmap *)apArg[0]->x.pOther,0); return PH7_OK; } /* * value next(array $input) * Advance the internal array pointer of an array. * Parameter * $input: The input array. * Return * next() behaves like current(), with one difference. It advances the internal array * pointer one place forward before returning the element value. That means it returns * the next array value and advances the internal array pointer by one. */ static int ph7_hashmap_next(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } HashmapCurrentValue(&(*pCtx),(ph7_hashmap *)apArg[0]->x.pOther,1); return PH7_OK; } /* * value prev(array $input) * Rewind the internal array pointer. * Parameter * $input: The input array. * Return * Returns the array value in the previous place that's pointed * to by the internal array pointer, or FALSE if there are no more * elements. */ static int ph7_hashmap_prev(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } HashmapCurrentValue(&(*pCtx),(ph7_hashmap *)apArg[0]->x.pOther,-1); return PH7_OK; } /* * value end(array $input) * Set the internal pointer of an array to its last element. * Parameter * $input: The input array. * Return * Returns the value of the last element or FALSE for empty array. */ static int ph7_hashmap_end(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Point to the last node */ pMap->pCur = pMap->pLast; /* Return the last node value */ HashmapCurrentValue(&(*pCtx),pMap,0); return PH7_OK; } /* * value reset(array $array ) * Set the internal pointer of an array to its first element. * Parameter * $input: The input array. * Return * Returns the value of the first array element,or FALSE if the array is empty. */ static int ph7_hashmap_reset(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Point to the first node */ pMap->pCur = pMap->pFirst; /* Return the last node value if available */ HashmapCurrentValue(&(*pCtx),pMap,0); return PH7_OK; } /* * value key(array $array) * Fetch a key from an array * Parameter * $input * The input array. * Return * The key() function simply returns the key of the array element that's currently * being pointed to by the internal pointer. It does not move the pointer in any way. * If the internal pointer points beyond the end of the elements list or the array * is empty, key() returns NULL. */ static int ph7_hashmap_simple_key(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pCur; ph7_hashmap *pMap; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } pMap = (ph7_hashmap *)apArg[0]->x.pOther; pCur = pMap->pCur; if( pCur == 0 ){ /* Cursor does not point to anything,return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( pCur->iType == HASHMAP_INT_NODE){ /* Key is integer */ ph7_result_int64(pCtx,pCur->xKey.iKey); }else{ /* Key is blob */ ph7_result_string(pCtx, (const char *)SyBlobData(&pCur->xKey.sKey),(int)SyBlobLength(&pCur->xKey.sKey)); } return PH7_OK; } /* * array each(array $input) * Return the current key and value pair from an array and advance the array cursor. * Parameter * $input * The input array. * Return * Returns the current key and value pair from the array array. This pair is returned * in a four-element array, with the keys 0, 1, key, and value. Elements 0 and key * contain the key name of the array element, and 1 and value contain the data. * If the internal pointer for the array points past the end of the array contents * each() returns FALSE. */ static int ph7_hashmap_each(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pCur; ph7_hashmap *pMap; ph7_value *pArray; ph7_value *pVal; ph7_value sKey; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation that describe the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->pCur == 0 ){ /* Cursor does not point to anything,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } pCur = pMap->pCur; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_bool(pCtx,0); return PH7_OK; } pVal = HashmapExtractNodeValue(pCur); /* Insert the current value */ ph7_array_add_intkey_elem(pArray,1,pVal); ph7_array_add_strkey_elem(pArray,"value",pVal); /* Make the key */ if( pCur->iType == HASHMAP_INT_NODE ){ PH7_MemObjInitFromInt(pMap->pVm,&sKey,pCur->xKey.iKey); }else{ PH7_MemObjInitFromString(pMap->pVm,&sKey,0); PH7_MemObjStringAppend(&sKey,(const char *)SyBlobData(&pCur->xKey.sKey),SyBlobLength(&pCur->xKey.sKey)); } /* Insert the current key */ ph7_array_add_intkey_elem(pArray,0,&sKey); ph7_array_add_strkey_elem(pArray,"key",&sKey); PH7_MemObjRelease(&sKey); /* Advance the cursor */ pMap->pCur = pCur->pPrev; /* Reverse link */ /* Return the current entry */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array range(int $start,int $limit,int $step) * Create an array containing a range of elements * Parameter * start * First value of the sequence. * limit * The sequence is ended upon reaching the limit value. * step * If a step value is given, it will be used as the increment between elements in the sequence. * step should be given as a positive number. If not specified, step will default to 1. * Return * An array of elements from start to limit, inclusive. * NOTE: * Only 32/64 bit integer key is supported. */ static int ph7_hashmap_range(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pValue,*pArray; sxi64 iOfft,iLimit; int iStep = 1; iOfft = iLimit = 0; /* cc -O6 */ if( nArg > 0 ){ /* Extract the offset */ iOfft = ph7_value_to_int64(apArg[0]); if( nArg > 1 ){ /* Extract the limit */ iLimit = ph7_value_to_int64(apArg[1]); if( nArg > 2 ){ /* Extract the increment */ iStep = ph7_value_to_int(apArg[2]); if( iStep < 1 ){ /* Only positive number are allowed */ iStep = 1; } } } } /* Element container */ pValue = ph7_context_new_scalar(pCtx); /* Create the new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Start filling */ while( iOfft <= iLimit ){ ph7_value_int64(pValue,iOfft); /* Perform the insertion */ ph7_array_add_elem(pArray,0/* Automatic index assign*/,pValue); /* Increment */ iOfft += iStep; } /* Return the new array */ ph7_result_value(pCtx,pArray); /* Dont'worry about freeing 'pValue',it will be released automatically * by the virtual machine as soon we return from this foreign function. */ return PH7_OK; } /* * array array_values(array $input) * Returns all the values from the input array and indexes numerically the array. * Parameters * input: The input array. * Return * An indexed array of values or NULL on failure. */ static int ph7_hashmap_values(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pNode; ph7_hashmap *pMap; ph7_value *pArray; ph7_value *pObj; sxu32 n; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation that describe the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ pNode = pMap->pFirst; for( n = 0 ; n < pMap->nEntry ; ++n ){ pObj = HashmapExtractNodeValue(pNode); if( pObj ){ /* perform the insertion */ ph7_array_add_elem(pArray,0/* Automatic index assign */,pObj); } /* Point to the next entry */ pNode = pNode->pPrev; /* Reverse link */ } /* return the new array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_keys(array $input [, val $search_value [, bool $strict = false ]] ) * Return all the keys or a subset of the keys of an array. * Parameters * $input * An array containing keys to return. * $search_value * If specified, then only keys containing these values are returned. * $strict * Determines if strict comparison (===) should be used during the search. * Return * An array of all the keys in input or NULL on failure. */ static int ph7_hashmap_keys(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pNode; ph7_hashmap *pMap; ph7_value *pArray; ph7_value sObj; ph7_value sVal; SyString sKey; int bStrict; sxi32 rc; sxu32 n; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } bStrict = FALSE; if( nArg > 2 && ph7_value_is_bool(apArg[2]) ){ bStrict = ph7_value_to_bool(apArg[2]); } /* Perform the requested operation */ pNode = pMap->pFirst; PH7_MemObjInit(pMap->pVm,&sVal); for( n = 0 ; n < pMap->nEntry ; ++n ){ if( pNode->iType == HASHMAP_INT_NODE ){ PH7_MemObjInitFromInt(pMap->pVm,&sObj,pNode->xKey.iKey); }else{ SyStringInitFromBuf(&sKey,SyBlobData(&pNode->xKey.sKey),SyBlobLength(&pNode->xKey.sKey)); PH7_MemObjInitFromString(pMap->pVm,&sObj,&sKey); } rc = 0; if( nArg > 1 ){ ph7_value *pValue = HashmapExtractNodeValue(pNode); if( pValue ){ PH7_MemObjLoad(pValue,&sVal); /* Filter key */ rc = ph7_value_compare(&sVal,apArg[1],bStrict); PH7_MemObjRelease(pValue); } } if( rc == 0 ){ /* Perform the insertion */ ph7_array_add_elem(pArray,0,&sObj); } PH7_MemObjRelease(&sObj); /* Point to the next entry */ pNode = pNode->pPrev; /* Reverse link */ } /* return the new array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * bool array_same(array $arr1,array $arr2) * Return TRUE if the given arrays are the same instance. * This function is useful under PH7 since arrays are passed * by reference unlike the zend engine which use pass by values. * Parameters * $arr1 * First array * $arr2 * Second array * Return * TRUE if the arrays are the same instance.FALSE otherwise. * Note * This function is a symisc eXtension. */ static int ph7_hashmap_same(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *p1,*p2; int rc; if( nArg < 2 || !ph7_value_is_array(apArg[0]) || !ph7_value_is_array(apArg[1]) ){ /* Missing or invalid arguments,return FALSE*/ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the hashmaps */ p1 = (ph7_hashmap *)apArg[0]->x.pOther; p2 = (ph7_hashmap *)apArg[1]->x.pOther; rc = (p1 == p2); /* Same instance? */ ph7_result_bool(pCtx,rc); return PH7_OK; } /* * array array_merge(array $array1,...) * Merge one or more arrays. * Parameters * $array1 * Initial array to merge. * ... * More array to merge. * Return * The resulting array. */ static int ph7_hashmap_merge(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap,*pSrc; ph7_value *pArray; int i; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)pArray->x.pOther; /* Start merging */ for( i = 0 ; i < nArg ; i++ ){ /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[i]) ){ /* Insert scalar value */ ph7_array_add_elem(pArray,0,apArg[i]); }else{ pSrc = (ph7_hashmap *)apArg[i]->x.pOther; /* Merge the two hashmaps */ HashmapMerge(pSrc,pMap); } } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_copy(array $source) * Make a blind copy of the target array. * Parameters * $source * Target array * Return * Copy of the target array on success.NULL otherwise. * Note * This function is a symisc eXtension. */ static int ph7_hashmap_copy(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; ph7_value *pArray; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)pArray->x.pOther; if( ph7_value_is_array(apArg[0])){ /* Point to the internal representation of the source */ ph7_hashmap *pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the copy */ PH7_HashmapDup(pSrc,pMap); }else{ /* Simple insertion */ PH7_HashmapInsert(pMap,0/* Automatic index assign*/,apArg[0]); } /* Return the duplicated array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * bool array_erase(array $source) * Remove all elements from a given array. * Parameters * $source * Target array * Return * TRUE on success.FALSE otherwise. * Note * This function is a symisc eXtension. */ static int ph7_hashmap_erase(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; if( nArg < 1 ){ /* Missing arguments */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Erase */ PH7_HashmapRelease(pMap,FALSE); return PH7_OK; } /* * array array_slice(array $array,int $offset [,int $length [, bool $preserve_keys = false ]]) * Extract a slice of the array. * Parameters * $array * The input array. * $offset * If offset is non-negative, the sequence will start at that offset in the array. * If offset is negative, the sequence will start that far from the end of the array. * $length (optional) * If length is given and is positive, then the sequence will have that many elements * in it. If length is given and is negative then the sequence will stop that many * elements from the end of the array. If it is omitted, then the sequence will have * everything from offset up until the end of the array. * $preserve_keys (optional) * Note that array_slice() will reorder and reset the array indices by default. * You can change this behaviour by setting preserve_keys to TRUE. * Return * The new slice. */ static int ph7_hashmap_slice(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap,*pSrc; ph7_hashmap_node *pCur; ph7_value *pArray; int iLength,iOfft; int bPreserve; sxi32 rc; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point the internal representation of the target array */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; bPreserve = FALSE; /* Get the offset */ iOfft = ph7_value_to_int(apArg[1]); if( iOfft < 0 ){ iOfft = (int)pSrc->nEntry + iOfft; } if( iOfft < 0 || iOfft > (int)pSrc->nEntry ){ /* Invalid offset,return the last entry */ iOfft = (int)pSrc->nEntry - 1; } /* Get the length */ iLength = (int)pSrc->nEntry - iOfft; if( nArg > 2 ){ iLength = ph7_value_to_int(apArg[2]); if( iLength < 0 ){ iLength = ((int)pSrc->nEntry + iLength) - iOfft; } if( iLength < 0 || iOfft + iLength >= (int)pSrc->nEntry ){ iLength = (int)pSrc->nEntry - iOfft; } if( nArg > 3 && ph7_value_is_bool(apArg[3]) ){ bPreserve = ph7_value_to_bool(apArg[3]); } } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } if( iLength < 1 ){ /* Don't bother processing,return the empty array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* Point to the desired entry */ pCur = pSrc->pFirst; for(;;){ if( iOfft < 1 ){ break; } /* Point to the next entry */ pCur = pCur->pPrev; /* Reverse link */ iOfft--; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)pArray->x.pOther; for(;;){ if( iLength < 1 ){ break; } rc = HashmapInsertNode(pMap,pCur,bPreserve); if( rc != SXRET_OK ){ break; } /* Point to the next entry */ pCur = pCur->pPrev; /* Reverse link */ iLength--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_splice(array $array,int $offset [,int $length [,value $replacement ]]) * Remove a portion of the array and replace it with something else. * Parameters * $array * The input array. * $offset * If offset is positive then the start of removed portion is at that offset from * the beginning of the input array. If offset is negative then it starts that far * from the end of the input array. * $length (optional) * If length is omitted, removes everything from offset to the end of the array. * If length is specified and is positive, then that many elements will be removed. * If length is specified and is negative then the end of the removed portion will * be that many elements from the end of the array. * $replacement (optional) * If replacement array is specified, then the removed elements are replaced * with elements from this array. * If offset and length are such that nothing is removed, then the elements * from the replacement array are inserted in the place specified by the offset. * Note that keys in replacement array are not preserved. * If replacement is just one element it is not necessary to put array() around * it, unless the element is an array itself, an object or NULL. * Return * A new array consisting of the extracted elements. */ static int ph7_hashmap_splice(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pCur,*pPrev,*pRnode; ph7_value *pArray,*pRvalue,*pOld; ph7_hashmap *pMap,*pSrc,*pRep; int iLength,iOfft; sxi32 rc; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point the internal representation of the target array */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Get the offset */ iOfft = ph7_value_to_int(apArg[1]); if( iOfft < 0 ){ iOfft = (int)pSrc->nEntry + iOfft; } if( iOfft < 0 || iOfft > (int)pSrc->nEntry ){ /* Invalid offset,remove the last entry */ iOfft = (int)pSrc->nEntry - 1; } /* Get the length */ iLength = (int)pSrc->nEntry - iOfft; if( nArg > 2 ){ iLength = ph7_value_to_int(apArg[2]); if( iLength < 0 ){ iLength = ((int)pSrc->nEntry + iLength) - iOfft; } if( iLength < 0 || iOfft + iLength >= (int)pSrc->nEntry ){ iLength = (int)pSrc->nEntry - iOfft; } } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } if( iLength < 1 ){ /* Don't bother processing,return the empty array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* Point to the desired entry */ pCur = pSrc->pFirst; for(;;){ if( iOfft < 1 ){ break; } /* Point to the next entry */ pCur = pCur->pPrev; /* Reverse link */ iOfft--; } pRep = 0; if( nArg > 3 ){ if( !ph7_value_is_array(apArg[3]) ){ /* Perform an array cast */ PH7_MemObjToHashmap(apArg[3]); if(ph7_value_is_array(apArg[3])){ pRep = (ph7_hashmap *)apArg[3]->x.pOther; } }else{ pRep = (ph7_hashmap *)apArg[3]->x.pOther; } if( pRep ){ /* Reset the loop cursor */ pRep->pCur = pRep->pFirst; } } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)pArray->x.pOther; for(;;){ if( iLength < 1 ){ break; } pPrev = pCur->pPrev; rc = HashmapInsertNode(pMap,pCur,FALSE); if( pRep && (pRnode = PH7_HashmapGetNextEntry(pRep)) != 0 ){ /* Extract node value */ pRvalue = HashmapExtractNodeValue(pRnode); /* Replace the old node */ pOld = HashmapExtractNodeValue(pCur); if( pRvalue && pOld ){ PH7_MemObjStore(pRvalue,pOld); } }else{ /* Unlink the node from the source hashmap */ PH7_HashmapUnlinkNode(pCur,TRUE); } if( rc != SXRET_OK ){ break; } /* Point to the next entry */ pCur = pPrev; /* Reverse link */ iLength--; } if( pRep ){ while((pRnode = PH7_HashmapGetNextEntry(pRep)) != 0 ){ HashmapInsertNode(pSrc,pRnode,FALSE); } } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * bool in_array(value $needle,array $haystack[,bool $strict = FALSE ]) * Checks if a value exists in an array. * Parameters * $needle * The searched value. * Note: * If needle is a string, the comparison is done in a case-sensitive manner. * $haystack * The target array. * $strict * If the third parameter strict is set to TRUE then the in_array() function * will also check the types of the needle in the haystack. */ static int ph7_hashmap_in_array(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pNeedle; int bStrict; int rc; if( nArg < 2 ){ /* Missing argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } pNeedle = apArg[0]; bStrict = 0; if( nArg > 2 ){ bStrict = ph7_value_to_bool(apArg[2]); } if( !ph7_value_is_array(apArg[1]) ){ /* haystack must be an array,perform a standard comparison */ rc = ph7_value_compare(pNeedle,apArg[1],bStrict); /* Set the comparison result */ ph7_result_bool(pCtx,rc == 0); return PH7_OK; } /* Perform the lookup */ rc = HashmapFindValue((ph7_hashmap *)apArg[1]->x.pOther,pNeedle,0,bStrict); /* Lookup result */ ph7_result_bool(pCtx,rc == SXRET_OK); return PH7_OK; } /* * value array_search(value $needle,array $haystack[,bool $strict = false ]) * Searches the array for a given value and returns the corresponding key if successful. * Parameters * $needle * The searched value. * $haystack * The array. * $strict * If the third parameter strict is set to TRUE then the array_search() function * will search for identical elements in the haystack. This means it will also check * the types of the needle in the haystack, and objects must be the same instance. * Return * Returns the key for needle if it is found in the array, FALSE otherwise. */ static int ph7_hashmap_search(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_value *pVal,sNeedle; ph7_hashmap *pMap; ph7_value sVal; int bStrict; sxu32 n; int rc; if( nArg < 2 ){ /* Missing argument,return FALSE*/ ph7_result_bool(pCtx,0); return PH7_OK; } bStrict = FALSE; if( !ph7_value_is_array(apArg[1]) ){ /* hasystack must be an array,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( nArg > 2 && ph7_value_is_bool(apArg[2]) ){ bStrict = ph7_value_to_bool(apArg[2]); } /* Point to the internal representation of the internal hashmap */ pMap = (ph7_hashmap *)apArg[1]->x.pOther; /* Perform a linear search since we cannot sort the hashmap based on values */ PH7_MemObjInit(pMap->pVm,&sVal); PH7_MemObjInit(pMap->pVm,&sNeedle); pEntry = pMap->pFirst; n = pMap->nEntry; for(;;){ if( !n ){ break; } /* Extract node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ /* Make a copy of the vuurent values since the comparison routine * can change their type. */ PH7_MemObjLoad(pVal,&sVal); PH7_MemObjLoad(apArg[0],&sNeedle); rc = PH7_MemObjCmp(&sNeedle,&sVal,bStrict,0); PH7_MemObjRelease(&sVal); PH7_MemObjRelease(&sNeedle); if( rc == 0 ){ /* Match found,return key */ if( pEntry->iType == HASHMAP_INT_NODE){ /* INT key */ ph7_result_int64(pCtx,pEntry->xKey.iKey); }else{ SyBlob *pKey = &pEntry->xKey.sKey; /* Blob key */ ph7_result_string(pCtx,(const char *)SyBlobData(pKey),(int)SyBlobLength(pKey)); } return PH7_OK; } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* No such value,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * array array_diff(array $array1,array $array2,...) * Computes the difference of arrays. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against * Return * Returns an array containing all the entries from array1 that * are not present in any of the other arrays. */ static int ph7_hashmap_diff(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc,*pMap; ph7_value *pArray; ph7_value *pVal; sxi32 rc; sxu32 n; int i; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( nArg == 1 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the source hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the diff */ pEntry = pSrc->pFirst; n = pSrc->nEntry; for(;;){ if( n < 1 ){ break; } /* Extract the node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ for( i = 1 ; i < nArg ; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Perform the lookup */ rc = HashmapFindValue(pMap,pVal,0,TRUE); if( rc == SXRET_OK ){ /* Value exist */ break; } } if( i >= nArg ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_udiff(array $array1,array $array2,...,$callback) * Computes the difference of arrays by using a callback function for data comparison. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against. * $callback * The callback comparison function. * The comparison function must return an integer less than, equal to, or greater than zero * if the first argument is considered to be respectively less than, equal to, or greater * than the second. * int callback ( mixed $a, mixed $b ) * Return * Returns an array containing all the entries from array1 that * are not present in any of the other arrays. */ static int ph7_hashmap_udiff(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc,*pMap; ph7_value *pCallback; ph7_value *pArray; ph7_value *pVal; sxi32 rc; sxu32 n; int i; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the callback */ pCallback = apArg[nArg - 1]; if( nArg == 2 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the source hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the diff */ pEntry = pSrc->pFirst; n = pSrc->nEntry; for(;;){ if( n < 1 ){ break; } /* Extract the node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ for( i = 1 ; i < nArg - 1; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Perform the lookup */ rc = HashmapFindValueByCallback(pMap,pVal,pCallback,0); if( rc == SXRET_OK ){ /* Value exist */ break; } } if( i >= (nArg - 1)){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_diff_assoc(array $array1,array $array2,...) * Computes the difference of arrays with additional index check. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against * Return * Returns an array containing all the entries from array1 that * are not present in any of the other arrays. */ static int ph7_hashmap_diff_assoc(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pN1,*pN2,*pEntry; ph7_hashmap *pSrc,*pMap; ph7_value *pArray; ph7_value *pVal; sxi32 rc; sxu32 n; int i; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( nArg == 1 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the source hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the diff */ pEntry = pSrc->pFirst; n = pSrc->nEntry; pN1 = pN2 = 0; for(;;){ if( n < 1 ){ break; } for( i = 1 ; i < nArg ; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Perform a key lookup first */ if( pEntry->iType == HASHMAP_INT_NODE ){ rc = HashmapLookupIntKey(pMap,pEntry->xKey.iKey,&pN1); }else{ rc = HashmapLookupBlobKey(pMap,SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey),&pN1); } if( rc != SXRET_OK ){ /* No such key,break immediately */ break; } /* Extract node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ /* Perform the lookup */ rc = HashmapFindValue(pMap,pVal,&pN2,TRUE); if( rc != SXRET_OK || pN1 != pN2 ){ /* Value does not exist */ break; } } } if( i < nArg ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_diff_uassoc(array $array1,array $array2,...,callback $key_compare_func) * Computes the difference of arrays with additional index check which is performed * by a user supplied callback function. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against. * $key_compare_func * Callback function to use. The callback function must return an integer * less than, equal to, or greater than zero if the first argument is considered * to be respectively less than, equal to, or greater than the second. * Return * Returns an array containing all the entries from array1 that * are not present in any of the other arrays. */ static int ph7_hashmap_diff_uassoc(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pN1,*pN2,*pEntry; ph7_hashmap *pSrc,*pMap; ph7_value *pCallback; ph7_value *pArray; ph7_value *pVal; sxi32 rc; sxu32 n; int i; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the callback */ pCallback = apArg[nArg - 1]; if( nArg == 2 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the source hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the diff */ pEntry = pSrc->pFirst; n = pSrc->nEntry; pN1 = pN2 = 0; /* cc warning */ for(;;){ if( n < 1 ){ break; } for( i = 1 ; i < nArg - 1; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Perform a key lookup first */ if( pEntry->iType == HASHMAP_INT_NODE ){ rc = HashmapLookupIntKey(pMap,pEntry->xKey.iKey,&pN1); }else{ rc = HashmapLookupBlobKey(pMap,SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey),&pN1); } if( rc != SXRET_OK ){ /* No such key,break immediately */ break; } /* Extract node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ /* Invoke the user callback */ rc = HashmapFindValueByCallback(pMap,pVal,pCallback,&pN2); if( rc != SXRET_OK || pN1 != pN2 ){ /* Value does not exist */ break; } } } if( i < (nArg-1) ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_diff_key(array $array1 ,array $array2,...) * Computes the difference of arrays using keys for comparison. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against * Return * Returns an array containing all the entries from array1 whose keys are not present * in any of the other arrays. * Note that NULL is returned on failure. */ static int ph7_hashmap_diff_key(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc,*pMap; ph7_value *pArray; sxi32 rc; sxu32 n; int i; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( nArg == 1 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the main hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perfrom the diff */ pEntry = pSrc->pFirst; n = pSrc->nEntry; for(;;){ if( n < 1 ){ break; } for( i = 1 ; i < nArg ; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } pMap = (ph7_hashmap *)apArg[i]->x.pOther; if( pEntry->iType == HASHMAP_BLOB_NODE ){ SyBlob *pKey = &pEntry->xKey.sKey; /* Blob lookup */ rc = HashmapLookupBlobKey(pMap,SyBlobData(pKey),SyBlobLength(pKey),0); }else{ /* Int lookup */ rc = HashmapLookupIntKey(pMap,pEntry->xKey.iKey,0); } if( rc == SXRET_OK ){ /* Key exists,break immediately */ break; } } if( i >= nArg ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_intersect(array $array1 ,array $array2,...) * Computes the intersection of arrays. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against * Return * Returns an array containing all of the values in array1 whose values exist * in all of the parameters. . * Note that NULL is returned on failure. */ static int ph7_hashmap_intersect(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc,*pMap; ph7_value *pArray; ph7_value *pVal; sxi32 rc; sxu32 n; int i; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( nArg == 1 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the source hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the intersection */ pEntry = pSrc->pFirst; n = pSrc->nEntry; for(;;){ if( n < 1 ){ break; } /* Extract the node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ for( i = 1 ; i < nArg ; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Perform the lookup */ rc = HashmapFindValue(pMap,pVal,0,TRUE); if( rc != SXRET_OK ){ /* Value does not exist */ break; } } if( i >= nArg ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_intersect_assoc(array $array1 ,array $array2,...) * Computes the intersection of arrays. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against * Return * Returns an array containing all of the values in array1 whose values exist * in all of the parameters. . * Note that NULL is returned on failure. */ static int ph7_hashmap_intersect_assoc(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry,*pN1,*pN2; ph7_hashmap *pSrc,*pMap; ph7_value *pArray; ph7_value *pVal; sxi32 rc; sxu32 n; int i; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( nArg == 1 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the source hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the intersection */ pEntry = pSrc->pFirst; n = pSrc->nEntry; pN1 = pN2 = 0; /* cc warning */ for(;;){ if( n < 1 ){ break; } /* Extract the node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ for( i = 1 ; i < nArg ; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Perform a key lookup first */ if( pEntry->iType == HASHMAP_INT_NODE ){ rc = HashmapLookupIntKey(pMap,pEntry->xKey.iKey,&pN1); }else{ rc = HashmapLookupBlobKey(pMap,SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey),&pN1); } if( rc != SXRET_OK ){ /* No such key,break immediately */ break; } /* Perform the lookup */ rc = HashmapFindValue(pMap,pVal,&pN2,TRUE); if( rc != SXRET_OK || pN1 != pN2 ){ /* Value does not exist */ break; } } if( i >= nArg ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_intersect_key(array $array1 ,array $array2,...) * Computes the intersection of arrays using keys for comparison. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against * Return * Returns an associative array containing all the entries of array1 which * have keys that are present in all arguments. * Note that NULL is returned on failure. */ static int ph7_hashmap_intersect_key(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc,*pMap; ph7_value *pArray; sxi32 rc; sxu32 n; int i; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( nArg == 1 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the main hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perfrom the intersection */ pEntry = pSrc->pFirst; n = pSrc->nEntry; for(;;){ if( n < 1 ){ break; } for( i = 1 ; i < nArg ; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } pMap = (ph7_hashmap *)apArg[i]->x.pOther; if( pEntry->iType == HASHMAP_BLOB_NODE ){ SyBlob *pKey = &pEntry->xKey.sKey; /* Blob lookup */ rc = HashmapLookupBlobKey(pMap,SyBlobData(pKey),SyBlobLength(pKey),0); }else{ /* Int key */ rc = HashmapLookupIntKey(pMap,pEntry->xKey.iKey,0); } if( rc != SXRET_OK ){ /* Key does not exists,break immediately */ break; } } if( i >= nArg ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_uintersect(array $array1 ,array $array2,...,$callback) * Computes the intersection of arrays. * Parameters * $array1 * The array to compare from * $array2 * An array to compare against * $... * More arrays to compare against * $callback * The callback comparison function. * The comparison function must return an integer less than, equal to, or greater than zero * if the first argument is considered to be respectively less than, equal to, or greater * than the second. * int callback ( mixed $a, mixed $b ) * Return * Returns an array containing all of the values in array1 whose values exist * in all of the parameters. . * Note that NULL is returned on failure. */ static int ph7_hashmap_uintersect(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc,*pMap; ph7_value *pCallback; ph7_value *pArray; ph7_value *pVal; sxi32 rc; sxu32 n; int i; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the callback */ pCallback = apArg[nArg - 1]; if( nArg == 2 ){ /* Return the first array since we cannot perform a diff */ ph7_result_value(pCtx,apArg[0]); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the source hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the intersection */ pEntry = pSrc->pFirst; n = pSrc->nEntry; for(;;){ if( n < 1 ){ break; } /* Extract the node value */ pVal = HashmapExtractNodeValue(pEntry); if( pVal ){ for( i = 1 ; i < nArg - 1; i++ ){ if( !ph7_value_is_array(apArg[i])) { /* ignore */ continue; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)apArg[i]->x.pOther; /* Perform the lookup */ rc = HashmapFindValueByCallback(pMap,pVal,pCallback,0); if( rc != SXRET_OK ){ /* Value does not exist */ break; } } if( i >= (nArg-1) ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_fill(int $start_index,int $num,var $value) * Fill an array with values. * Parameters * $start_index * The first index of the returned array. * $num * Number of elements to insert. * $value * Value to use for filling. * Return * The filled array or null on failure. */ static int ph7_hashmap_fill(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray; int i,nEntry; if( nArg < 3 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Total number of entries to insert */ nEntry = ph7_value_to_int(apArg[1]); /* Insert the first entry alone because it have it's own key */ ph7_array_add_intkey_elem(pArray,ph7_value_to_int(apArg[0]),apArg[2]); /* Repeat insertion of the desired value */ for( i = 1 ; i < nEntry ; i++ ){ ph7_array_add_elem(pArray,0/*Automatic index assign */,apArg[2]); } /* Return the filled array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_fill_keys(array $input,var $value) * Fill an array with values, specifying keys. * Parameters * $input * Array of values that will be used as key. * $value * Value to use for filling. * Return * The filled array or null on failure. */ static int ph7_hashmap_fill_keys(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc; ph7_value *pArray; sxu32 n; if( nArg < 2 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ pEntry = pSrc->pFirst; for( n = 0 ; n < pSrc->nEntry ; n++ ){ ph7_array_add_elem(pArray,HashmapExtractNodeValue(pEntry),apArg[1]); /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Return the filled array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_combine(array $keys,array $values) * Creates an array by using one array for keys and another for its values. * Parameters * $keys * Array of keys to be used. * $values * Array of values to be used. * Return * Returns the combined array. Otherwise FALSE if the number of elements * for each array isn't equal or if one of the given arguments is * not an array. */ static int ph7_hashmap_combine(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pKe,*pVe; ph7_hashmap *pKey,*pValue; ph7_value *pArray; sxu32 n; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) || !ph7_value_is_array(apArg[1]) ){ /* Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmaps */ pKey = (ph7_hashmap *)apArg[0]->x.pOther; pValue = (ph7_hashmap *)apArg[1]->x.pOther; if( pKey->nEntry != pValue->nEntry ){ /* Array length differs,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ pKe = pKey->pFirst; pVe = pValue->pFirst; for( n = 0 ; n < pKey->nEntry ; n++ ){ ph7_array_add_elem(pArray,HashmapExtractNodeValue(pKe),HashmapExtractNodeValue(pVe)); /* Point to the next entry */ pKe = pKe->pPrev; /* Reverse link */ pVe = pVe->pPrev; } /* Return the filled array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_reverse(array $array [,bool $preserve_keys = false ]) * Return an array with elements in reverse order. * Parameters * $array * The input array. * $preserve_keys (optional) * If set to TRUE keys are preserved. * Return * The reversed array. */ static int ph7_hashmap_reverse(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc; ph7_value *pArray; int bPreserve; sxu32 n; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } bPreserve = FALSE; if( nArg > 1 && ph7_value_is_bool(apArg[1]) ){ bPreserve = ph7_value_to_bool(apArg[1]); } /* Point to the internal representation of the input hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ pEntry = pSrc->pLast; for( n = 0 ; n < pSrc->nEntry ; n++ ){ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,bPreserve); /* Point to the previous entry */ pEntry = pEntry->pNext; /* Reverse link */ } ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_unique(array $array[,int $sort_flags = SORT_STRING ]) * Removes duplicate values from an array * Parameter * $array * The input array. * $sort_flags * The optional second parameter sort_flags may be used to modify the sorting behavior using these values: * Sorting type flags: * SORT_REGULAR - compare items normally (don't change types) * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings * SORT_LOCALE_STRING - compare items as * Return * Filtered array or NULL on failure. */ static int ph7_hashmap_unique(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_value *pNeedle; ph7_hashmap *pSrc; ph7_value *pArray; int bStrict; sxi32 rc; sxu32 n; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } bStrict = FALSE; if( nArg > 1 ){ bStrict = ph7_value_to_int(apArg[1]) == 3 /* SORT_REGULAR */ ? 1 : 0; } /* Point to the internal representation of the input hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ pEntry = pSrc->pFirst; for( n = 0 ; n < pSrc->nEntry ; n++ ){ pNeedle = HashmapExtractNodeValue(pEntry); rc = SXERR_NOTFOUND; if( pNeedle ){ rc = HashmapFindValue((ph7_hashmap *)pArray->x.pOther,pNeedle,0,bStrict); } if( rc != SXRET_OK ){ /* Perform the insertion */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_flip(array $input) * Exchanges all keys with their associated values in an array. * Parameter * $input * Input array. * Return * The flipped array on success or NULL on failure. */ static int ph7_hashmap_flip(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pSrc; ph7_value *pArray; ph7_value *pKey; ph7_value sVal; sxu32 n; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pSrc = (ph7_hashmap *)apArg[0]->x.pOther; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Start processing */ pEntry = pSrc->pFirst; for( n = 0 ; n < pSrc->nEntry ; n++ ){ /* Extract the node value */ pKey = HashmapExtractNodeValue(pEntry); if( pKey && (pKey->iFlags & MEMOBJ_NULL) == 0){ /* Prepare the value for insertion */ if( pEntry->iType == HASHMAP_INT_NODE ){ PH7_MemObjInitFromInt(pSrc->pVm,&sVal,pEntry->xKey.iKey); }else{ SyString sStr; SyStringInitFromBuf(&sStr,SyBlobData(&pEntry->xKey.sKey),SyBlobLength(&pEntry->xKey.sKey)); PH7_MemObjInitFromString(pSrc->pVm,&sVal,&sStr); } /* Perform the insertion */ ph7_array_add_elem(pArray,pKey,&sVal); /* Safely release the value because each inserted entry * have it's own private copy of the value. */ PH7_MemObjRelease(&sVal); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * number array_sum(array $array ) * Calculate the sum of values in an array. * Parameters * $array: The input array. * Return * Returns the sum of values as an integer or float. */ static void DoubleSum(ph7_context *pCtx,ph7_hashmap *pMap) { ph7_hashmap_node *pEntry; ph7_value *pObj; double dSum = 0; sxu32 n; pEntry = pMap->pFirst; for( n = 0 ; n < pMap->nEntry ; n++ ){ pObj = HashmapExtractNodeValue(pEntry); if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES)) == 0){ if( pObj->iFlags & MEMOBJ_REAL ){ dSum += pObj->rVal; }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ dSum += (double)pObj->x.iVal; }else if( pObj->iFlags & MEMOBJ_STRING ){ if( SyBlobLength(&pObj->sBlob) > 0 ){ double dv = 0; SyStrToReal((const char *)SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob),(void *)&dv,0); dSum += dv; } } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Return sum */ ph7_result_double(pCtx,dSum); } static void Int64Sum(ph7_context *pCtx,ph7_hashmap *pMap) { ph7_hashmap_node *pEntry; ph7_value *pObj; sxi64 nSum = 0; sxu32 n; pEntry = pMap->pFirst; for( n = 0 ; n < pMap->nEntry ; n++ ){ pObj = HashmapExtractNodeValue(pEntry); if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES)) == 0){ if( pObj->iFlags & MEMOBJ_REAL ){ nSum += (sxi64)pObj->rVal; }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ nSum += pObj->x.iVal; }else if( pObj->iFlags & MEMOBJ_STRING ){ if( SyBlobLength(&pObj->sBlob) > 0 ){ sxi64 nv = 0; SyStrToInt64((const char *)SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob),(void *)&nv,0); nSum += nv; } } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Return sum */ ph7_result_int64(pCtx,nSum); } /* number array_sum(array $array ) * (See block-coment above) */ static int ph7_hashmap_sum(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; ph7_value *pObj; if( nArg < 1 ){ /* Missing arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry < 1 ){ /* Nothing to compute,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* If the first element is of type float,then perform floating * point computaion.Otherwise switch to int64 computaion. */ pObj = HashmapExtractNodeValue(pMap->pFirst); if( pObj == 0 ){ ph7_result_int(pCtx,0); return PH7_OK; } if( pObj->iFlags & MEMOBJ_REAL ){ DoubleSum(pCtx,pMap); }else{ Int64Sum(pCtx,pMap); } return PH7_OK; } /* * number array_product(array $array ) * Calculate the product of values in an array. * Parameters * $array: The input array. * Return * Returns the product of values as an integer or float. */ static void DoubleProd(ph7_context *pCtx,ph7_hashmap *pMap) { ph7_hashmap_node *pEntry; ph7_value *pObj; double dProd; sxu32 n; pEntry = pMap->pFirst; dProd = 1; for( n = 0 ; n < pMap->nEntry ; n++ ){ pObj = HashmapExtractNodeValue(pEntry); if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES)) == 0){ if( pObj->iFlags & MEMOBJ_REAL ){ dProd *= pObj->rVal; }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ dProd *= (double)pObj->x.iVal; }else if( pObj->iFlags & MEMOBJ_STRING ){ if( SyBlobLength(&pObj->sBlob) > 0 ){ double dv = 0; SyStrToReal((const char *)SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob),(void *)&dv,0); dProd *= dv; } } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Return product */ ph7_result_double(pCtx,dProd); } static void Int64Prod(ph7_context *pCtx,ph7_hashmap *pMap) { ph7_hashmap_node *pEntry; ph7_value *pObj; sxi64 nProd; sxu32 n; pEntry = pMap->pFirst; nProd = 1; for( n = 0 ; n < pMap->nEntry ; n++ ){ pObj = HashmapExtractNodeValue(pEntry); if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES)) == 0){ if( pObj->iFlags & MEMOBJ_REAL ){ nProd *= (sxi64)pObj->rVal; }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ nProd *= pObj->x.iVal; }else if( pObj->iFlags & MEMOBJ_STRING ){ if( SyBlobLength(&pObj->sBlob) > 0 ){ sxi64 nv = 0; SyStrToInt64((const char *)SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob),(void *)&nv,0); nProd *= nv; } } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* Return product */ ph7_result_int64(pCtx,nProd); } /* number array_product(array $array ) * (See block-block comment above) */ static int ph7_hashmap_product(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; ph7_value *pObj; if( nArg < 1 ){ /* Missing arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Make sure we are dealing with a valid hashmap */ if( !ph7_value_is_array(apArg[0]) ){ /* Invalid argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } pMap = (ph7_hashmap *)apArg[0]->x.pOther; if( pMap->nEntry < 1 ){ /* Nothing to compute,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* If the first element is of type float,then perform floating * point computaion.Otherwise switch to int64 computaion. */ pObj = HashmapExtractNodeValue(pMap->pFirst); if( pObj == 0 ){ ph7_result_int(pCtx,0); return PH7_OK; } if( pObj->iFlags & MEMOBJ_REAL ){ DoubleProd(pCtx,pMap); }else{ Int64Prod(pCtx,pMap); } return PH7_OK; } /* * value array_rand(array $input[,int $num_req = 1 ]) * Pick one or more random entries out of an array. * Parameters * $input * The input array. * $num_req * Specifies how many entries you want to pick. * Return * If you are picking only one entry, array_rand() returns the key for a random entry. * Otherwise, it returns an array of keys for the random entries. * NULL is returned on failure. */ static int ph7_hashmap_rand(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pNode; ph7_hashmap *pMap; int nItem = 1; if( nArg < 1 ){ /* Missing argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make sure we are dealing with an array */ if( !ph7_value_is_array(apArg[0]) ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; if(pMap->nEntry < 1 ){ /* Empty hashmap,return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( nArg > 1 ){ nItem = ph7_value_to_int(apArg[1]); } if( nItem < 2 ){ sxu32 nEntry; /* Select a random number */ nEntry = PH7_VmRandomNum(pMap->pVm) % pMap->nEntry; /* Extract the desired entry. * Note that we perform a linear lookup here (later version must change this) */ if( nEntry > pMap->nEntry / 2 ){ pNode = pMap->pLast; nEntry = pMap->nEntry - nEntry; if( nEntry > 1 ){ for(;;){ if( nEntry == 0 ){ break; } /* Point to the previous entry */ pNode = pNode->pNext; /* Reverse link */ nEntry--; } } }else{ pNode = pMap->pFirst; for(;;){ if( nEntry == 0 ){ break; } /* Point to the next entry */ pNode = pNode->pPrev; /* Reverse link */ nEntry--; } } if( pNode->iType == HASHMAP_INT_NODE ){ /* Int key */ ph7_result_int64(pCtx,pNode->xKey.iKey); }else{ /* Blob key */ ph7_result_string(pCtx,(const char *)SyBlobData(&pNode->xKey.sKey),(int)SyBlobLength(&pNode->xKey.sKey)); } }else{ ph7_value sKey,*pArray; ph7_hashmap *pDest; /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the hashmap */ pDest = (ph7_hashmap *)pArray->x.pOther; PH7_MemObjInit(pDest->pVm,&sKey); /* Copy the first n items */ pNode = pMap->pFirst; if( nItem > (int)pMap->nEntry ){ nItem = (int)pMap->nEntry; } while( nItem > 0){ PH7_HashmapExtractNodeKey(pNode,&sKey); PH7_HashmapInsert(pDest,0/* Automatic index assign*/,&sKey); PH7_MemObjRelease(&sKey); /* Point to the next entry */ pNode = pNode->pPrev; /* Reverse link */ nItem--; } /* Shuffle the array */ HashmapMergeSort(pDest,HashmapCmpCallback7,0); /* Rehash node */ HashmapSortRehash(pDest); /* Return the random array */ ph7_result_value(pCtx,pArray); } return PH7_OK; } /* * array array_chunk (array $input,int $size [,bool $preserve_keys = false ]) * Split an array into chunks. * Parameters * $input * The array to work on * $size * The size of each chunk * $preserve_keys * When set to TRUE keys will be preserved. Default is FALSE which will reindex * the chunk numerically. * Return * Returns a multidimensional numerically indexed array, starting with * zero, with each dimension containing size elements. */ static int ph7_hashmap_chunk(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pChunk; ph7_hashmap_node *pEntry; ph7_hashmap *pMap; int bPreserve; sxu32 nChunk; sxu32 nSize; sxu32 n; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Extract the chunk size */ nSize = (sxu32)ph7_value_to_int(apArg[1]); if( nSize < 1 ){ ph7_result_null(pCtx); return PH7_OK; } if( nSize >= pMap->nEntry ){ /* Return the whole array */ ph7_array_add_elem(pArray,0,apArg[0]); ph7_result_value(pCtx,pArray); return PH7_OK; } bPreserve = 0; if( nArg > 2 ){ bPreserve = ph7_value_to_bool(apArg[2]); } /* Start processing */ pEntry = pMap->pFirst; nChunk = 0; pChunk = 0; n = pMap->nEntry; for( ;; ){ if( n < 1 ){ if( nChunk > 0 ){ /* Insert the last chunk */ ph7_array_add_elem(pArray,0,pChunk); /* Will have it's own copy */ } break; } if( nChunk < 1 ){ if( pChunk ){ /* Put the first chunk */ ph7_array_add_elem(pArray,0,pChunk); /* Will have it's own copy */ } /* Create a new dimension */ pChunk = ph7_context_new_array(pCtx); /* Don't worry about freeing memory here,everything * will be automatically released as soon we return * from this function */ if( pChunk == 0 ){ break; } nChunk = nSize; } /* Insert the entry */ HashmapInsertNode((ph7_hashmap *)pChunk->x.pOther,pEntry,bPreserve); /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ nChunk--; n--; } /* Return the multidimensional array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_pad(array $input,int $pad_size,value $pad_value) * Pad array to the specified length with a value. * $input * Initial array of values to pad. * $pad_size * New size of the array. * $pad_value * Value to pad if input is less than pad_size. */ static int ph7_hashmap_pad(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; ph7_value *pArray; int nEntry; if( nArg < 3 || !ph7_value_is_array(apArg[0]) ){ /* Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Extract the total number of desired entry to insert */ nEntry = ph7_value_to_int(apArg[1]); if( nEntry < 0 ){ nEntry = -nEntry; if( nEntry > 1048576 ){ nEntry = 1048576; /* Limit imposed by PHP */ } if( nEntry > (int)pMap->nEntry ){ nEntry -= (int)pMap->nEntry; /* Insert given items first */ while( nEntry > 0 ){ ph7_array_add_elem(pArray,0,apArg[2]); nEntry--; } /* Merge the two arrays */ HashmapMerge(pMap,(ph7_hashmap *)pArray->x.pOther); }else{ PH7_HashmapDup(pMap,(ph7_hashmap *)pArray->x.pOther); } }else if( nEntry > 0 ){ if( nEntry > 1048576 ){ nEntry = 1048576; /* Limit imposed by PHP */ } if( nEntry > (int)pMap->nEntry ){ nEntry -= (int)pMap->nEntry; /* Merge the two arrays first */ HashmapMerge(pMap,(ph7_hashmap *)pArray->x.pOther); /* Insert given items */ while( nEntry > 0 ){ ph7_array_add_elem(pArray,0,apArg[2]); nEntry--; } }else{ PH7_HashmapDup(pMap,(ph7_hashmap *)pArray->x.pOther); } } /* Return the new array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_replace(array &$array,array &$array1,...) * Replaces elements from passed arrays into the first array. * Parameters * $array * The array in which elements are replaced. * $array1 * The array from which elements will be extracted. * .... * More arrays from which elements will be extracted. * Values from later arrays overwrite the previous values. * Return * Returns an array, or NULL if an error occurs. */ static int ph7_hashmap_replace(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; ph7_value *pArray; int i; if( nArg < 1 ){ /* Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ for( i = 0 ; i < nArg ; i++ ){ if( !ph7_value_is_array(apArg[i]) ){ continue; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[i]->x.pOther; HashmapOverwrite(pMap,(ph7_hashmap *)pArray->x.pOther); } /* Return the new array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_filter(array $input [,callback $callback ]) * Filters elements of an array using a callback function. * Parameters * $input * The array to iterate over * $callback * The callback function to use * If no callback is supplied, all entries of input equal to FALSE (see converting to boolean) * will be removed. * Return * The filtered array. */ static int ph7_hashmap_filter(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pMap; ph7_value *pArray; ph7_value sResult; /* Callback result */ ph7_value *pValue; sxi32 rc; int keep; sxu32 n; if( nArg < 1 || !ph7_value_is_array(apArg[0]) ){ /* Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; pEntry = pMap->pFirst; PH7_MemObjInit(pMap->pVm,&sResult); sResult.nIdx = SXU32_HIGH; /* Mark as constant */ /* Perform the requested operation */ for( n = 0 ; n < pMap->nEntry ; n++ ){ /* Extract node value */ pValue = HashmapExtractNodeValue(pEntry); if( nArg > 1 && pValue ){ /* Invoke the given callback */ keep = FALSE; rc = PH7_VmCallUserFunction(pMap->pVm,apArg[1],1,&pValue,&sResult); if( rc == SXRET_OK ){ /* Perform a boolean cast */ keep = ph7_value_to_bool(&sResult); } PH7_MemObjRelease(&sResult); }else{ /* No available callback,check for empty item */ keep = !PH7_MemObjIsEmpty(pValue); } if( keep ){ /* Perform the insertion,now the callback returned true */ HashmapInsertNode((ph7_hashmap *)pArray->x.pOther,pEntry,TRUE); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } ph7_result_value(pCtx,pArray); return PH7_OK; } /* * array array_map(callback $callback,array $arr1) * Applies the callback to the elements of the given arrays. * Parameters * $callback * Callback function to run for each element in each array. * $arr1 * An array to run through the callback function. * Return * Returns an array containing all the elements of arr1 after applying * the callback function to each one. * NOTE: * array_map() passes only a single value to the callback. */ static int ph7_hashmap_map(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pValue,sKey,sResult; ph7_hashmap_node *pEntry; ph7_hashmap *pMap; sxu32 n; if( nArg < 2 || !ph7_value_is_array(apArg[1]) ){ /* Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[1]->x.pOther; PH7_MemObjInit(pMap->pVm,&sResult); PH7_MemObjInit(pMap->pVm,&sKey); sResult.nIdx = SXU32_HIGH; /* Mark as constant */ sKey.nIdx = SXU32_HIGH; /* Mark as constant */ /* Perform the requested operation */ pEntry = pMap->pFirst; for( n = 0 ; n < pMap->nEntry ; n++ ){ /* Extrcat the node value */ pValue = HashmapExtractNodeValue(pEntry); if( pValue ){ sxi32 rc; /* Invoke the supplied callback */ rc = PH7_VmCallUserFunction(pMap->pVm,apArg[0],1,&pValue,&sResult); /* Extract the node key */ PH7_HashmapExtractNodeKey(pEntry,&sKey); if( rc != SXRET_OK ){ /* An error occured while invoking the supplied callback [i.e: not defined] */ ph7_array_add_elem(pArray,&sKey,pValue); /* Keep the same value */ }else{ /* Insert the callback return value */ ph7_array_add_elem(pArray,&sKey,&sResult); } PH7_MemObjRelease(&sKey); PH7_MemObjRelease(&sResult); } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } ph7_result_value(pCtx,pArray); return PH7_OK; } /* * value array_reduce(array $input,callback $function[, value $initial = NULL ]) * Iteratively reduce the array to a single value using a callback function. * Parameters * $input * The input array. * $function * The callback function. * $initial * If the optional initial is available, it will be used at the beginning * of the process, or as a final result in case the array is empty. * Return * Returns the resulting value. * If the array is empty and initial is not passed, array_reduce() returns NULL. */ static int ph7_hashmap_reduce(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap_node *pEntry; ph7_hashmap *pMap; ph7_value *pValue; ph7_value sResult; sxu32 n; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Invalid/Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Assume a NULL initial value */ PH7_MemObjInit(pMap->pVm,&sResult); sResult.nIdx = SXU32_HIGH; /* Mark as constant */ if( nArg > 2 ){ /* Set the initial value */ PH7_MemObjLoad(apArg[2],&sResult); } /* Perform the requested operation */ pEntry = pMap->pFirst; for( n = 0 ; n < pMap->nEntry ; n++ ){ /* Extract the node value */ pValue = HashmapExtractNodeValue(pEntry); /* Invoke the supplied callback */ PH7_VmCallUserFunctionAp(pMap->pVm,apArg[1],&sResult,&sResult,pValue,0); /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } ph7_result_value(pCtx,&sResult); /* Will make it's own copy */ PH7_MemObjRelease(&sResult); return PH7_OK; } /* * bool array_walk(array &$array,callback $funcname [, value $userdata ] ) * Apply a user function to every member of an array. * Parameters * $array * The input array. * $funcname * Typically, funcname takes on two parameters.The array parameter's value being * the first, and the key/index second. * Note: * If funcname needs to be working with the actual values of the array,specify the first * parameter of funcname as a reference. Then, any changes made to those elements will * be made in the original array itself. * $userdata * If the optional userdata parameter is supplied, it will be passed as the third parameter * to the callback funcname. * Return * Returns TRUE on success or FALSE on failure. */ static int ph7_hashmap_walk(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pValue,*pUserData,sKey; ph7_hashmap_node *pEntry; ph7_hashmap *pMap; sxi32 rc; sxu32 n; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Invalid/Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } pUserData = nArg > 2 ? apArg[2] : 0; /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; PH7_MemObjInit(pMap->pVm,&sKey); sKey.nIdx = SXU32_HIGH; /* Mark as constant */ /* Perform the desired operation */ pEntry = pMap->pFirst; for( n = 0 ; n < pMap->nEntry ; n++ ){ /* Extract the node value */ pValue = HashmapExtractNodeValue(pEntry); if( pValue ){ /* Extract the entry key */ PH7_HashmapExtractNodeKey(pEntry,&sKey); /* Invoke the supplied callback */ rc = PH7_VmCallUserFunctionAp(pMap->pVm,apArg[1],0,pValue,&sKey,pUserData,0); PH7_MemObjRelease(&sKey); if( rc != SXRET_OK ){ /* An error occured while invoking the supplied callback [i.e: not defined] */ ph7_result_bool(pCtx,0); /* return FALSE */ return PH7_OK; } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } /* All done,return TRUE */ ph7_result_bool(pCtx,1); return PH7_OK; } /* * Apply a user function to every member of an array.(Recurse on array's). * Refer to the [array_walk_recursive()] implementation for more information. */ static int HashmapWalkRecursive( ph7_hashmap *pMap, /* Target hashmap */ ph7_value *pCallback, /* User callback */ ph7_value *pUserData, /* Callback private data */ int iNest /* Nesting level */ ) { ph7_hashmap_node *pEntry; ph7_value *pValue,sKey; sxi32 rc; sxu32 n; /* Iterate throw hashmap entries */ PH7_MemObjInit(pMap->pVm,&sKey); sKey.nIdx = SXU32_HIGH; /* Mark as constant */ pEntry = pMap->pFirst; for( n = 0 ; n < pMap->nEntry ; n++ ){ /* Extract the node value */ pValue = HashmapExtractNodeValue(pEntry); if( pValue ){ if( pValue->iFlags & MEMOBJ_HASHMAP ){ if( iNest < 32 ){ /* Recurse */ iNest++; HashmapWalkRecursive((ph7_hashmap *)pValue->x.pOther,pCallback,pUserData,iNest); iNest--; } }else{ /* Extract the node key */ PH7_HashmapExtractNodeKey(pEntry,&sKey); /* Invoke the supplied callback */ rc = PH7_VmCallUserFunctionAp(pMap->pVm,pCallback,0,pValue,&sKey,pUserData,0); PH7_MemObjRelease(&sKey); if( rc != SXRET_OK ){ return rc; } } } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ } return SXRET_OK; } /* * bool array_walk_recursive(array &$array,callback $funcname [, value $userdata ] ) * Apply a user function recursively to every member of an array. * Parameters * $array * The input array. * $funcname * Typically, funcname takes on two parameters.The array parameter's value being * the first, and the key/index second. * Note: * If funcname needs to be working with the actual values of the array,specify the first * parameter of funcname as a reference. Then, any changes made to those elements will * be made in the original array itself. * $userdata * If the optional userdata parameter is supplied, it will be passed as the third parameter * to the callback funcname. * Return * Returns TRUE on success or FALSE on failure. */ static int ph7_hashmap_walk_recursive(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_hashmap *pMap; sxi32 rc; if( nArg < 2 || !ph7_value_is_array(apArg[0]) ){ /* Invalid/Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the internal representation of the input hashmap */ pMap = (ph7_hashmap *)apArg[0]->x.pOther; /* Perform the desired operation */ rc = HashmapWalkRecursive(pMap,apArg[1],nArg > 2 ? apArg[2] : 0,0); /* All done */ ph7_result_bool(pCtx,rc == SXRET_OK); return PH7_OK; } /* * Table of hashmap functions. */ static const ph7_builtin_func aHashmapFunc[] = { {"count", ph7_hashmap_count }, {"sizeof", ph7_hashmap_count }, {"array_key_exists", ph7_hashmap_key_exists }, {"array_pop", ph7_hashmap_pop }, {"array_push", ph7_hashmap_push }, {"array_shift", ph7_hashmap_shift }, {"array_product", ph7_hashmap_product }, {"array_sum", ph7_hashmap_sum }, {"array_keys", ph7_hashmap_keys }, {"array_values", ph7_hashmap_values }, {"array_same", ph7_hashmap_same }, /* Symisc eXtension */ {"array_merge", ph7_hashmap_merge }, {"array_slice", ph7_hashmap_slice }, {"array_splice", ph7_hashmap_splice }, {"array_search", ph7_hashmap_search }, {"array_diff", ph7_hashmap_diff }, {"array_udiff", ph7_hashmap_udiff }, {"array_diff_assoc", ph7_hashmap_diff_assoc }, {"array_diff_uassoc", ph7_hashmap_diff_uassoc }, {"array_diff_key", ph7_hashmap_diff_key }, {"array_intersect", ph7_hashmap_intersect}, {"array_intersect_assoc", ph7_hashmap_intersect_assoc}, {"array_uintersect", ph7_hashmap_uintersect}, {"array_intersect_key", ph7_hashmap_intersect_key}, {"array_copy", ph7_hashmap_copy }, {"array_erase", ph7_hashmap_erase }, {"array_fill", ph7_hashmap_fill }, {"array_fill_keys", ph7_hashmap_fill_keys}, {"array_combine", ph7_hashmap_combine }, {"array_reverse", ph7_hashmap_reverse }, {"array_unique", ph7_hashmap_unique }, {"array_flip", ph7_hashmap_flip }, {"array_rand", ph7_hashmap_rand }, {"array_chunk", ph7_hashmap_chunk }, {"array_pad", ph7_hashmap_pad }, {"array_replace", ph7_hashmap_replace }, {"array_filter", ph7_hashmap_filter }, {"array_map", ph7_hashmap_map }, {"array_reduce", ph7_hashmap_reduce }, {"array_walk", ph7_hashmap_walk }, {"array_walk_recursive", ph7_hashmap_walk_recursive }, {"in_array", ph7_hashmap_in_array}, {"sort", ph7_hashmap_sort }, {"asort", ph7_hashmap_asort }, {"arsort", ph7_hashmap_arsort }, {"ksort", ph7_hashmap_ksort }, {"krsort", ph7_hashmap_krsort }, {"rsort", ph7_hashmap_rsort }, {"usort", ph7_hashmap_usort }, {"uasort", ph7_hashmap_uasort }, {"uksort", ph7_hashmap_uksort }, {"shuffle", ph7_hashmap_shuffle }, {"range", ph7_hashmap_range }, {"current", ph7_hashmap_current }, {"each", ph7_hashmap_each }, {"pos", ph7_hashmap_current }, {"next", ph7_hashmap_next }, {"prev", ph7_hashmap_prev }, {"end", ph7_hashmap_end }, {"reset", ph7_hashmap_reset }, {"key", ph7_hashmap_simple_key } }; /* * Register the built-in hashmap functions defined above. */ PH7_PRIVATE void PH7_RegisterHashmapFunctions(ph7_vm *pVm) { sxu32 n; for( n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++ ){ ph7_create_function(&(*pVm),aHashmapFunc[n].zName,aHashmapFunc[n].xFunc,0); } } /* * Dump a hashmap instance and it's entries and the store the dump in * the BLOB given as the first argument. * This function is typically invoked when the user issue a call to * [var_dump(),var_export(),print_r(),...] * This function SXRET_OK on success. Any other return value including * SXERR_LIMIT(infinite recursion) indicates failure. */ PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut,ph7_hashmap *pMap,int ShowType,int nTab,int nDepth) { ph7_hashmap_node *pEntry; ph7_value *pObj; sxu32 n = 0; int isRef; sxi32 rc; int i; if( nDepth > 31 ){ static const char zInfinite[] = "Nesting limit reached: Infinite recursion?"; /* Nesting limit reached */ SyBlobAppend(&(*pOut),zInfinite,sizeof(zInfinite)-1); if( ShowType ){ SyBlobAppend(&(*pOut),")",sizeof(char)); } return SXERR_LIMIT; } /* Point to the first inserted entry */ pEntry = pMap->pFirst; rc = SXRET_OK; if( !ShowType ){ SyBlobAppend(&(*pOut),"Array(",sizeof("Array(")-1); } /* Total entries */ SyBlobFormat(&(*pOut),"%u) {",pMap->nEntry); #ifdef __WINNT__ SyBlobAppend(&(*pOut),"\r\n",sizeof("\r\n")-1); #else SyBlobAppend(&(*pOut),"\n",sizeof(char)); #endif for(;;){ if( n >= pMap->nEntry ){ break; } for( i = 0 ; i < nTab ; i++ ){ SyBlobAppend(&(*pOut)," ",sizeof(char)); } /* Dump key */ if( pEntry->iType == HASHMAP_INT_NODE){ SyBlobFormat(&(*pOut),"[%qd] =>",pEntry->xKey.iKey); }else{ SyBlobFormat(&(*pOut),"[%.*s] =>", SyBlobLength(&pEntry->xKey.sKey),SyBlobData(&pEntry->xKey.sKey)); } #ifdef __WINNT__ SyBlobAppend(&(*pOut),"\r\n",sizeof("\r\n")-1); #else SyBlobAppend(&(*pOut),"\n",sizeof(char)); #endif /* Dump node value */ pObj = HashmapExtractNodeValue(pEntry); isRef = 0; if( pObj ){ if( pEntry->iFlags & HASHMAP_NODE_FOREIGN_OBJ ){ /* Referenced object */ isRef = 1; } rc = PH7_MemObjDump(&(*pOut),pObj,ShowType,nTab+1,nDepth,isRef); if( rc == SXERR_LIMIT ){ break; } } /* Point to the next entry */ n++; pEntry = pEntry->pPrev; /* Reverse link */ } for( i = 0 ; i < nTab ; i++ ){ SyBlobAppend(&(*pOut)," ",sizeof(char)); } SyBlobAppend(&(*pOut),"}",sizeof(char)); return rc; } /* * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each * retrieved entry. * Note that argument are passed to the callback by copy. That is,any modification to * the entry value in the callback body will not alter the real value. * If the callback wishes to abort processing [i.e: it's invocation] it must return * a value different from PH7_OK. * Refer to [ph7_array_walk()] for more information. */ PH7_PRIVATE sxi32 PH7_HashmapWalk( ph7_hashmap *pMap, /* Target hashmap */ int (*xWalk)(ph7_value *,ph7_value *,void *), /* Walker callback */ void *pUserData /* Last argument to xWalk() */ ) { ph7_hashmap_node *pEntry; ph7_value sKey,sValue; sxi32 rc; sxu32 n; /* Initialize walker parameter */ rc = SXRET_OK; PH7_MemObjInit(pMap->pVm,&sKey); PH7_MemObjInit(pMap->pVm,&sValue); n = pMap->nEntry; pEntry = pMap->pFirst; /* Start the iteration process */ for(;;){ if( n < 1 ){ break; } /* Extract a copy of the key and a copy the current value */ PH7_HashmapExtractNodeKey(pEntry,&sKey); PH7_HashmapExtractNodeValue(pEntry,&sValue,FALSE); /* Invoke the user callback */ rc = xWalk(&sKey,&sValue,pUserData); /* Release the copy of the key and the value */ PH7_MemObjRelease(&sKey); PH7_MemObjRelease(&sValue); if( rc != PH7_OK ){ /* Callback request an operation abort */ return SXERR_ABORT; } /* Point to the next entry */ pEntry = pEntry->pPrev; /* Reverse link */ n--; } /* All done */ return SXRET_OK; } /* * ---------------------------------------------------------- * File: constant.c * MD5: 9cf62714d3cc5de3825c4eebc8378bb7 * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: constant.c v1.1 Win7 2012-08-07 08:22 devel $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* This file implement built-in constants for the PH7 engine. */ /* * PH7_VERSION * __PH7__ * Expand the current version of the PH7 engine. */ static void PH7_VER_Const(ph7_value *pVal,void *pUnused) { SXUNUSED(pUnused); ph7_value_string(pVal,ph7_lib_signature(),-1/*Compute length automatically*/); } #ifdef __WINNT__ #include #elif defined(__UNIXES__) #include #endif /* * PHP_OS * Expand the name of the host Operating System. */ static void PH7_OS_Const(ph7_value *pVal,void *pUnused) { #if defined(__WINNT__) ph7_value_string(pVal,"WINNT",(int)sizeof("WINNT")-1); #elif defined(__UNIXES__) struct utsname sInfo; if( uname(&sInfo) != 0 ){ ph7_value_string(pVal,"Unix",(int)sizeof("Unix")-1); }else{ ph7_value_string(pVal,sInfo.sysname,-1); } #else ph7_value_string(pVal,"Host OS",(int)sizeof("Host OS")-1); #endif SXUNUSED(pUnused); } /* * PHP_EOL * Expand the correct 'End Of Line' symbol for this platform. */ static void PH7_EOL_Const(ph7_value *pVal,void *pUnused) { SXUNUSED(pUnused); #ifdef __WINNT__ ph7_value_string(pVal,"\r\n",(int)sizeof("\r\n")-1); #else ph7_value_string(pVal,"\n",(int)sizeof(char)); #endif } /* * PHP_INT_MAX * Expand the largest integer supported. * Note that PH7 deals with 64-bit integer for all platforms. */ static void PH7_INTMAX_Const(ph7_value *pVal,void *pUnused) { SXUNUSED(pUnused); ph7_value_int64(pVal,SXI64_HIGH); } /* * PHP_INT_SIZE * Expand the size in bytes of a 64-bit integer. */ static void PH7_INTSIZE_Const(ph7_value *pVal,void *pUnused) { SXUNUSED(pUnused); ph7_value_int64(pVal,sizeof(sxi64)); } /* * DIRECTORY_SEPARATOR. * Expand the directory separator character. */ static void PH7_DIRSEP_Const(ph7_value *pVal,void *pUnused) { SXUNUSED(pUnused); #ifdef __WINNT__ ph7_value_string(pVal,"\\",(int)sizeof(char)); #else ph7_value_string(pVal,"/",(int)sizeof(char)); #endif } /* * PATH_SEPARATOR. * Expand the path separator character. */ static void PH7_PATHSEP_Const(ph7_value *pVal,void *pUnused) { SXUNUSED(pUnused); #ifdef __WINNT__ ph7_value_string(pVal,";",(int)sizeof(char)); #else ph7_value_string(pVal,":",(int)sizeof(char)); #endif } #ifndef __WINNT__ #include #endif /* * __TIME__ * Expand the current time (GMT). */ static void PH7_TIME_Const(ph7_value *pVal,void *pUnused) { Sytm sTm; #ifdef __WINNT__ SYSTEMTIME sOS; GetSystemTime(&sOS); SYSTEMTIME_TO_SYTM(&sOS,&sTm); #else struct tm *pTm; time_t t; time(&t); pTm = gmtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); #endif SXUNUSED(pUnused); /* cc warning */ /* Expand */ ph7_value_string_format(pVal,"%02d:%02d:%02d",sTm.tm_hour,sTm.tm_min,sTm.tm_sec); } /* * __DATE__ * Expand the current date in the ISO-8601 format. */ static void PH7_DATE_Const(ph7_value *pVal,void *pUnused) { Sytm sTm; #ifdef __WINNT__ SYSTEMTIME sOS; GetSystemTime(&sOS); SYSTEMTIME_TO_SYTM(&sOS,&sTm); #else struct tm *pTm; time_t t; time(&t); pTm = gmtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); #endif SXUNUSED(pUnused); /* cc warning */ /* Expand */ ph7_value_string_format(pVal,"%04d-%02d-%02d",sTm.tm_year,sTm.tm_mon+1,sTm.tm_mday); } /* * __FILE__ * Path of the processed script. */ static void PH7_FILE_Const(ph7_value *pVal,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; SyString *pFile; /* Peek the top entry */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile == 0 ){ /* Expand the magic word: ":MEMORY:" */ ph7_value_string(pVal,":MEMORY:",(int)sizeof(":MEMORY:")-1); }else{ ph7_value_string(pVal,pFile->zString,pFile->nByte); } } /* * __DIR__ * Directory holding the processed script. */ static void PH7_DIR_Const(ph7_value *pVal,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; SyString *pFile; /* Peek the top entry */ pFile = (SyString *)SySetPeek(&pVm->aFiles); if( pFile == 0 ){ /* Expand the magic word: ":MEMORY:" */ ph7_value_string(pVal,":MEMORY:",(int)sizeof(":MEMORY:")-1); }else{ if( pFile->nByte > 0 ){ const char *zDir; int nLen; zDir = PH7_ExtractDirName(pFile->zString,(int)pFile->nByte,&nLen); ph7_value_string(pVal,zDir,nLen); }else{ /* Expand '.' as the current directory*/ ph7_value_string(pVal,".",(int)sizeof(char)); } } } /* * PHP_SHLIB_SUFFIX * Expand shared library suffix. */ static void PH7_PHP_SHLIB_SUFFIX_Const(ph7_value *pVal,void *pUserData) { #ifdef __WINNT__ ph7_value_string(pVal,"dll",(int)sizeof("dll")-1); #else ph7_value_string(pVal,"so",(int)sizeof("so")-1); #endif SXUNUSED(pUserData); /* cc warning */ } /* * E_ERROR * Expands 1 */ static void PH7_E_ERROR_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,1); SXUNUSED(pUserData); } /* * E_WARNING * Expands 2 */ static void PH7_E_WARNING_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,2); SXUNUSED(pUserData); } /* * E_PARSE * Expands 4 */ static void PH7_E_PARSE_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,4); SXUNUSED(pUserData); } /* * E_NOTICE * Expands 8 */ static void PH7_E_NOTICE_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,8); SXUNUSED(pUserData); } /* * E_CORE_ERROR * Expands 16 */ static void PH7_E_CORE_ERROR_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,16); SXUNUSED(pUserData); } /* * E_CORE_WARNING * Expands 32 */ static void PH7_E_CORE_WARNING_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,32); SXUNUSED(pUserData); } /* * E_COMPILE_ERROR * Expands 64 */ static void PH7_E_COMPILE_ERROR_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,64); SXUNUSED(pUserData); } /* * E_COMPILE_WARNING * Expands 128 */ static void PH7_E_COMPILE_WARNING_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,128); SXUNUSED(pUserData); } /* * E_USER_ERROR * Expands 256 */ static void PH7_E_USER_ERROR_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,256); SXUNUSED(pUserData); } /* * E_USER_WARNING * Expands 512 */ static void PH7_E_USER_WARNING_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,512); SXUNUSED(pUserData); } /* * E_USER_NOTICE * Expands 1024 */ static void PH7_E_USER_NOTICE_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,1024); SXUNUSED(pUserData); } /* * E_STRICT * Expands 2048 */ static void PH7_E_STRICT_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,2048); SXUNUSED(pUserData); } /* * E_RECOVERABLE_ERROR * Expands 4096 */ static void PH7_E_RECOVERABLE_ERROR_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,4096); SXUNUSED(pUserData); } /* * E_DEPRECATED * Expands 8192 */ static void PH7_E_DEPRECATED_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,8192); SXUNUSED(pUserData); } /* * E_USER_DEPRECATED * Expands 16384. */ static void PH7_E_USER_DEPRECATED_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,16384); SXUNUSED(pUserData); } /* * E_ALL * Expands 32767 */ static void PH7_E_ALL_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,32767); SXUNUSED(pUserData); } /* * CASE_LOWER * Expands 0. */ static void PH7_CASE_LOWER_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,0); SXUNUSED(pUserData); } /* * CASE_UPPER * Expands 1. */ static void PH7_CASE_UPPER_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,1); SXUNUSED(pUserData); } /* * STR_PAD_LEFT * Expands 0. */ static void PH7_STR_PAD_LEFT_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,0); SXUNUSED(pUserData); } /* * STR_PAD_RIGHT * Expands 1. */ static void PH7_STR_PAD_RIGHT_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,1); SXUNUSED(pUserData); } /* * STR_PAD_BOTH * Expands 2. */ static void PH7_STR_PAD_BOTH_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,2); SXUNUSED(pUserData); } /* * COUNT_NORMAL * Expands 0 */ static void PH7_COUNT_NORMAL_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,0); SXUNUSED(pUserData); } /* * COUNT_RECURSIVE * Expands 1. */ static void PH7_COUNT_RECURSIVE_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,1); SXUNUSED(pUserData); } /* * SORT_ASC * Expands 1. */ static void PH7_SORT_ASC_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,1); SXUNUSED(pUserData); } /* * SORT_DESC * Expands 2. */ static void PH7_SORT_DESC_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,2); SXUNUSED(pUserData); } /* * SORT_REGULAR * Expands 3. */ static void PH7_SORT_REG_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,3); SXUNUSED(pUserData); } /* * SORT_NUMERIC * Expands 4. */ static void PH7_SORT_NUMERIC_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,4); SXUNUSED(pUserData); } /* * SORT_STRING * Expands 5. */ static void PH7_SORT_STRING_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,5); SXUNUSED(pUserData); } /* * PHP_ROUND_HALF_UP * Expands 1. */ static void PH7_PHP_ROUND_HALF_UP_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,1); SXUNUSED(pUserData); } /* * SPHP_ROUND_HALF_DOWN * Expands 2. */ static void PH7_PHP_ROUND_HALF_DOWN_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,2); SXUNUSED(pUserData); } /* * PHP_ROUND_HALF_EVEN * Expands 3. */ static void PH7_PHP_ROUND_HALF_EVEN_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,3); SXUNUSED(pUserData); } /* * PHP_ROUND_HALF_ODD * Expands 4. */ static void PH7_PHP_ROUND_HALF_ODD_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,4); SXUNUSED(pUserData); } /* * DEBUG_BACKTRACE_PROVIDE_OBJECT * Expand 0x01 * NOTE: * The expanded value must be a power of two. */ static void PH7_DBPO_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,0x01); /* MUST BE A POWER OF TWO */ SXUNUSED(pUserData); } /* * DEBUG_BACKTRACE_IGNORE_ARGS * Expand 0x02 * NOTE: * The expanded value must be a power of two. */ static void PH7_DBIA_Const(ph7_value *pVal,void *pUserData) { ph7_value_int(pVal,0x02); /* MUST BE A POWER OF TWO */ SXUNUSED(pUserData); } #ifdef PH7_ENABLE_MATH_FUNC /* * M_PI * Expand the value of pi. */ static void PH7_M_PI_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,PH7_PI); } /* * M_E * Expand 2.7182818284590452354 */ static void PH7_M_E_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,2.7182818284590452354); } /* * M_LOG2E * Expand 2.7182818284590452354 */ static void PH7_M_LOG2E_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,1.4426950408889634074); } /* * M_LOG10E * Expand 0.4342944819032518276 */ static void PH7_M_LOG10E_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,0.4342944819032518276); } /* * M_LN2 * Expand 0.69314718055994530942 */ static void PH7_M_LN2_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,0.69314718055994530942); } /* * M_LN10 * Expand 2.30258509299404568402 */ static void PH7_M_LN10_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,2.30258509299404568402); } /* * M_PI_2 * Expand 1.57079632679489661923 */ static void PH7_M_PI_2_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,1.57079632679489661923); } /* * M_PI_4 * Expand 0.78539816339744830962 */ static void PH7_M_PI_4_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,0.78539816339744830962); } /* * M_1_PI * Expand 0.31830988618379067154 */ static void PH7_M_1_PI_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,0.31830988618379067154); } /* * M_2_PI * Expand 0.63661977236758134308 */ static void PH7_M_2_PI_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,0.63661977236758134308); } /* * M_SQRTPI * Expand 1.77245385090551602729 */ static void PH7_M_SQRTPI_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,1.77245385090551602729); } /* * M_2_SQRTPI * Expand 1.12837916709551257390 */ static void PH7_M_2_SQRTPI_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,1.12837916709551257390); } /* * M_SQRT2 * Expand 1.41421356237309504880 */ static void PH7_M_SQRT2_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,1.41421356237309504880); } /* * M_SQRT3 * Expand 1.73205080756887729352 */ static void PH7_M_SQRT3_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,1.73205080756887729352); } /* * M_SQRT1_2 * Expand 0.70710678118654752440 */ static void PH7_M_SQRT1_2_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,0.70710678118654752440); } /* * M_LNPI * Expand 1.14472988584940017414 */ static void PH7_M_LNPI_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,1.14472988584940017414); } /* * M_EULER * Expand 0.57721566490153286061 */ static void PH7_M_EULER_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_double(pVal,0.57721566490153286061); } #endif /* PH7_DISABLE_BUILTIN_MATH */ /* * DATE_ATOM * Expand Atom (example: 2005-08-15T15:52:01+00:00) */ static void PH7_DATE_ATOM_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"Y-m-d\\TH:i:sP",-1/*Compute length automatically*/); } /* * DATE_COOKIE * HTTP Cookies (example: Monday, 15-Aug-05 15:52:01 UTC) */ static void PH7_DATE_COOKIE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"l, d-M-y H:i:s T",-1/*Compute length automatically*/); } /* * DATE_ISO8601 * ISO-8601 (example: 2005-08-15T15:52:01+0000) */ static void PH7_DATE_ISO8601_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"Y-m-d\\TH:i:sO",-1/*Compute length automatically*/); } /* * DATE_RFC822 * RFC 822 (example: Mon, 15 Aug 05 15:52:01 +0000) */ static void PH7_DATE_RFC822_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"D, d M y H:i:s O",-1/*Compute length automatically*/); } /* * DATE_RFC850 * RFC 850 (example: Monday, 15-Aug-05 15:52:01 UTC) */ static void PH7_DATE_RFC850_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"l, d-M-y H:i:s T",-1/*Compute length automatically*/); } /* * DATE_RFC1036 * RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000) */ static void PH7_DATE_RFC1036_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"D, d M y H:i:s O",-1/*Compute length automatically*/); } /* * DATE_RFC1123 * RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000) */ static void PH7_DATE_RFC1123_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"D, d M Y H:i:s O",-1/*Compute length automatically*/); } /* * DATE_RFC2822 * RFC 2822 (Mon, 15 Aug 2005 15:52:01 +0000) */ static void PH7_DATE_RFC2822_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"D, d M Y H:i:s O",-1/*Compute length automatically*/); } /* * DATE_RSS * RSS (Mon, 15 Aug 2005 15:52:01 +0000) */ static void PH7_DATE_RSS_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"D, d M Y H:i:s O",-1/*Compute length automatically*/); } /* * DATE_W3C * World Wide Web Consortium (example: 2005-08-15T15:52:01+00:00) */ static void PH7_DATE_W3C_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"Y-m-d\\TH:i:sP",-1/*Compute length automatically*/); } /* * ENT_COMPAT * Expand 0x01 (Must be a power of two) */ static void PH7_ENT_COMPAT_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x01); } /* * ENT_QUOTES * Expand 0x02 (Must be a power of two) */ static void PH7_ENT_QUOTES_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x02); } /* * ENT_NOQUOTES * Expand 0x04 (Must be a power of two) */ static void PH7_ENT_NOQUOTES_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x04); } /* * ENT_IGNORE * Expand 0x08 (Must be a power of two) */ static void PH7_ENT_IGNORE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x08); } /* * ENT_SUBSTITUTE * Expand 0x10 (Must be a power of two) */ static void PH7_ENT_SUBSTITUTE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x10); } /* * ENT_DISALLOWED * Expand 0x20 (Must be a power of two) */ static void PH7_ENT_DISALLOWED_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x20); } /* * ENT_HTML401 * Expand 0x40 (Must be a power of two) */ static void PH7_ENT_HTML401_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x40); } /* * ENT_XML1 * Expand 0x80 (Must be a power of two) */ static void PH7_ENT_XML1_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x80); } /* * ENT_XHTML * Expand 0x100 (Must be a power of two) */ static void PH7_ENT_XHTML_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x100); } /* * ENT_HTML5 * Expand 0x200 (Must be a power of two) */ static void PH7_ENT_HTML5_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x200); } /* * ISO-8859-1 * ISO_8859_1 * Expand 1 */ static void PH7_ISO88591_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * UTF-8 * UTF8 * Expand 2 */ static void PH7_UTF8_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * HTML_ENTITIES * Expand 1 */ static void PH7_HTML_ENTITIES_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * HTML_SPECIALCHARS * Expand 2 */ static void PH7_HTML_SPECIALCHARS_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,2); } /* * PHP_URL_SCHEME. * Expand 1 */ static void PH7_PHP_URL_SCHEME_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * PHP_URL_HOST. * Expand 2 */ static void PH7_PHP_URL_HOST_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,2); } /* * PHP_URL_PORT. * Expand 3 */ static void PH7_PHP_URL_PORT_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,3); } /* * PHP_URL_USER. * Expand 4 */ static void PH7_PHP_URL_USER_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,4); } /* * PHP_URL_PASS. * Expand 5 */ static void PH7_PHP_URL_PASS_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,5); } /* * PHP_URL_PATH. * Expand 6 */ static void PH7_PHP_URL_PATH_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,6); } /* * PHP_URL_QUERY. * Expand 7 */ static void PH7_PHP_URL_QUERY_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,7); } /* * PHP_URL_FRAGMENT. * Expand 8 */ static void PH7_PHP_URL_FRAGMENT_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,8); } /* * PHP_QUERY_RFC1738 * Expand 1 */ static void PH7_PHP_QUERY_RFC1738_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * PHP_QUERY_RFC3986 * Expand 1 */ static void PH7_PHP_QUERY_RFC3986_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,2); } /* * FNM_NOESCAPE * Expand 0x01 (Must be a power of two) */ static void PH7_FNM_NOESCAPE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x01); } /* * FNM_PATHNAME * Expand 0x02 (Must be a power of two) */ static void PH7_FNM_PATHNAME_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x02); } /* * FNM_PERIOD * Expand 0x04 (Must be a power of two) */ static void PH7_FNM_PERIOD_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x04); } /* * FNM_CASEFOLD * Expand 0x08 (Must be a power of two) */ static void PH7_FNM_CASEFOLD_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x08); } /* * PATHINFO_DIRNAME * Expand 1. */ static void PH7_PATHINFO_DIRNAME_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * PATHINFO_BASENAME * Expand 2. */ static void PH7_PATHINFO_BASENAME_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,2); } /* * PATHINFO_EXTENSION * Expand 3. */ static void PH7_PATHINFO_EXTENSION_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,3); } /* * PATHINFO_FILENAME * Expand 4. */ static void PH7_PATHINFO_FILENAME_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,4); } /* * ASSERT_ACTIVE. * Expand the value of PH7_ASSERT_ACTIVE defined in ph7Int.h */ static void PH7_ASSERT_ACTIVE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,PH7_ASSERT_DISABLE); } /* * ASSERT_WARNING. * Expand the value of PH7_ASSERT_WARNING defined in ph7Int.h */ static void PH7_ASSERT_WARNING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,PH7_ASSERT_WARNING); } /* * ASSERT_BAIL. * Expand the value of PH7_ASSERT_BAIL defined in ph7Int.h */ static void PH7_ASSERT_BAIL_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,PH7_ASSERT_BAIL); } /* * ASSERT_QUIET_EVAL. * Expand the value of PH7_ASSERT_QUIET_EVAL defined in ph7Int.h */ static void PH7_ASSERT_QUIET_EVAL_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,PH7_ASSERT_QUIET_EVAL); } /* * ASSERT_CALLBACK. * Expand the value of PH7_ASSERT_CALLBACK defined in ph7Int.h */ static void PH7_ASSERT_CALLBACK_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,PH7_ASSERT_CALLBACK); } /* * SEEK_SET. * Expand 0 */ static void PH7_SEEK_SET_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0); } /* * SEEK_CUR. * Expand 1 */ static void PH7_SEEK_CUR_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * SEEK_END. * Expand 2 */ static void PH7_SEEK_END_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,2); } /* * LOCK_SH. * Expand 2 */ static void PH7_LOCK_SH_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * LOCK_NB. * Expand 5 */ static void PH7_LOCK_NB_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,5); } /* * LOCK_EX. * Expand 0x01 (MUST BE A POWER OF TWO) */ static void PH7_LOCK_EX_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x01); } /* * LOCK_UN. * Expand 0 */ static void PH7_LOCK_UN_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0); } /* * FILE_USE_INCLUDE_PATH * Expand 0x01 (Must be a power of two) */ static void PH7_FILE_USE_INCLUDE_PATH_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x1); } /* * FILE_IGNORE_NEW_LINES * Expand 0x02 (Must be a power of two) */ static void PH7_FILE_IGNORE_NEW_LINES_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x2); } /* * FILE_SKIP_EMPTY_LINES * Expand 0x04 (Must be a power of two) */ static void PH7_FILE_SKIP_EMPTY_LINES_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x4); } /* * FILE_APPEND * Expand 0x08 (Must be a power of two) */ static void PH7_FILE_APPEND_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x08); } /* * SCANDIR_SORT_ASCENDING * Expand 0 */ static void PH7_SCANDIR_SORT_ASCENDING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0); } /* * SCANDIR_SORT_DESCENDING * Expand 1 */ static void PH7_SCANDIR_SORT_DESCENDING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * SCANDIR_SORT_NONE * Expand 2 */ static void PH7_SCANDIR_SORT_NONE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,2); } /* * GLOB_MARK * Expand 0x01 (must be a power of two) */ static void PH7_GLOB_MARK_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x01); } /* * GLOB_NOSORT * Expand 0x02 (must be a power of two) */ static void PH7_GLOB_NOSORT_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x02); } /* * GLOB_NOCHECK * Expand 0x04 (must be a power of two) */ static void PH7_GLOB_NOCHECK_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x04); } /* * GLOB_NOESCAPE * Expand 0x08 (must be a power of two) */ static void PH7_GLOB_NOESCAPE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x08); } /* * GLOB_BRACE * Expand 0x10 (must be a power of two) */ static void PH7_GLOB_BRACE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x10); } /* * GLOB_ONLYDIR * Expand 0x20 (must be a power of two) */ static void PH7_GLOB_ONLYDIR_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x20); } /* * GLOB_ERR * Expand 0x40 (must be a power of two) */ static void PH7_GLOB_ERR_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x40); } /* * STDIN * Expand the STDIN handle as a resource. */ static void PH7_STDIN_Const(ph7_value *pVal,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; void *pResource; pResource = PH7_ExportStdin(pVm); ph7_value_resource(pVal,pResource); } /* * STDOUT * Expand the STDOUT handle as a resource. */ static void PH7_STDOUT_Const(ph7_value *pVal,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; void *pResource; pResource = PH7_ExportStdout(pVm); ph7_value_resource(pVal,pResource); } /* * STDERR * Expand the STDERR handle as a resource. */ static void PH7_STDERR_Const(ph7_value *pVal,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; void *pResource; pResource = PH7_ExportStderr(pVm); ph7_value_resource(pVal,pResource); } /* * INI_SCANNER_NORMAL * Expand 1 */ static void PH7_INI_SCANNER_NORMAL_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,1); } /* * INI_SCANNER_RAW * Expand 2 */ static void PH7_INI_SCANNER_RAW_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,2); } /* * EXTR_OVERWRITE * Expand 0x01 (Must be a power of two) */ static void PH7_EXTR_OVERWRITE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x1); } /* * EXTR_SKIP * Expand 0x02 (Must be a power of two) */ static void PH7_EXTR_SKIP_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x2); } /* * EXTR_PREFIX_SAME * Expand 0x04 (Must be a power of two) */ static void PH7_EXTR_PREFIX_SAME_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x4); } /* * EXTR_PREFIX_ALL * Expand 0x08 (Must be a power of two) */ static void PH7_EXTR_PREFIX_ALL_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x8); } /* * EXTR_PREFIX_INVALID * Expand 0x10 (Must be a power of two) */ static void PH7_EXTR_PREFIX_INVALID_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x10); } /* * EXTR_IF_EXISTS * Expand 0x20 (Must be a power of two) */ static void PH7_EXTR_IF_EXISTS_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x20); } /* * EXTR_PREFIX_IF_EXISTS * Expand 0x40 (Must be a power of two) */ static void PH7_EXTR_PREFIX_IF_EXISTS_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,0x40); } #ifndef PH7_DISABLE_BUILTIN_FUNC /* * XML_ERROR_NONE * Expand the value of SXML_ERROR_NO_MEMORY defined in ph7Int.h */ static void PH7_XML_ERROR_NONE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_NO_MEMORY); } /* * XML_ERROR_NO_MEMORY * Expand the value of SXML_ERROR_NONE defined in ph7Int.h */ static void PH7_XML_ERROR_NO_MEMORY_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_NO_MEMORY); } /* * XML_ERROR_SYNTAX * Expand the value of SXML_ERROR_SYNTAX defined in ph7Int.h */ static void PH7_XML_ERROR_SYNTAX_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_SYNTAX); } /* * XML_ERROR_NO_ELEMENTS * Expand the value of SXML_ERROR_NO_ELEMENTS defined in ph7Int.h */ static void PH7_XML_ERROR_NO_ELEMENTS_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_NO_ELEMENTS); } /* * XML_ERROR_INVALID_TOKEN * Expand the value of SXML_ERROR_INVALID_TOKEN defined in ph7Int.h */ static void PH7_XML_ERROR_INVALID_TOKEN_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_INVALID_TOKEN); } /* * XML_ERROR_UNCLOSED_TOKEN * Expand the value of SXML_ERROR_UNCLOSED_TOKEN defined in ph7Int.h */ static void PH7_XML_ERROR_UNCLOSED_TOKEN_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_UNCLOSED_TOKEN); } /* * XML_ERROR_PARTIAL_CHAR * Expand the value of SXML_ERROR_PARTIAL_CHAR defined in ph7Int.h */ static void PH7_XML_ERROR_PARTIAL_CHAR_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_PARTIAL_CHAR); } /* * XML_ERROR_TAG_MISMATCH * Expand the value of SXML_ERROR_TAG_MISMATCH defined in ph7Int.h */ static void PH7_XML_ERROR_TAG_MISMATCH_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_TAG_MISMATCH); } /* * XML_ERROR_DUPLICATE_ATTRIBUTE * Expand the value of SXML_ERROR_DUPLICATE_ATTRIBUTE defined in ph7Int.h */ static void PH7_XML_ERROR_DUPLICATE_ATTRIBUTE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_DUPLICATE_ATTRIBUTE); } /* * XML_ERROR_JUNK_AFTER_DOC_ELEMENT * Expand the value of SXML_ERROR_JUNK_AFTER_DOC_ELEMENT defined in ph7Int.h */ static void PH7_XML_ERROR_JUNK_AFTER_DOC_ELEMENT_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_JUNK_AFTER_DOC_ELEMENT); } /* * XML_ERROR_PARAM_ENTITY_REF * Expand the value of SXML_ERROR_PARAM_ENTITY_REF defined in ph7Int.h */ static void PH7_XML_ERROR_PARAM_ENTITY_REF_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_PARAM_ENTITY_REF); } /* * XML_ERROR_UNDEFINED_ENTITY * Expand the value of SXML_ERROR_UNDEFINED_ENTITY defined in ph7Int.h */ static void PH7_XML_ERROR_UNDEFINED_ENTITY_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_UNDEFINED_ENTITY); } /* * XML_ERROR_RECURSIVE_ENTITY_REF * Expand the value of SXML_ERROR_RECURSIVE_ENTITY_REF defined in ph7Int.h */ static void PH7_XML_ERROR_RECURSIVE_ENTITY_REF_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_RECURSIVE_ENTITY_REF); } /* * XML_ERROR_ASYNC_ENTITY * Expand the value of SXML_ERROR_ASYNC_ENTITY defined in ph7Int.h */ static void PH7_XML_ERROR_ASYNC_ENTITY_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_ASYNC_ENTITY); } /* * XML_ERROR_BAD_CHAR_REF * Expand the value of SXML_ERROR_BAD_CHAR_REF defined in ph7Int.h */ static void PH7_XML_ERROR_BAD_CHAR_REF_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_BAD_CHAR_REF); } /* * XML_ERROR_BINARY_ENTITY_REF * Expand the value of SXML_ERROR_BINARY_ENTITY_REF defined in ph7Int.h */ static void PH7_XML_ERROR_BINARY_ENTITY_REF_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_BINARY_ENTITY_REF); } /* * XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF * Expand the value of SXML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF defined in ph7Int.h */ static void PH7_XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF); } /* * XML_ERROR_MISPLACED_XML_PI * Expand the value of SXML_ERROR_MISPLACED_XML_PI defined in ph7Int.h */ static void PH7_XML_ERROR_MISPLACED_XML_PI_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_MISPLACED_XML_PI); } /* * XML_ERROR_UNKNOWN_ENCODING * Expand the value of SXML_ERROR_UNKNOWN_ENCODING defined in ph7Int.h */ static void PH7_XML_ERROR_UNKNOWN_ENCODING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_UNKNOWN_ENCODING); } /* * XML_ERROR_INCORRECT_ENCODING * Expand the value of SXML_ERROR_INCORRECT_ENCODING defined in ph7Int.h */ static void PH7_XML_ERROR_INCORRECT_ENCODING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_INCORRECT_ENCODING); } /* * XML_ERROR_UNCLOSED_CDATA_SECTION * Expand the value of SXML_ERROR_UNCLOSED_CDATA_SECTION defined in ph7Int.h */ static void PH7_XML_ERROR_UNCLOSED_CDATA_SECTION_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_UNCLOSED_CDATA_SECTION); } /* * XML_ERROR_EXTERNAL_ENTITY_HANDLING * Expand the value of SXML_ERROR_EXTERNAL_ENTITY_HANDLING defined in ph7Int.h */ static void PH7_XML_ERROR_EXTERNAL_ENTITY_HANDLING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_ERROR_EXTERNAL_ENTITY_HANDLING); } /* * XML_OPTION_CASE_FOLDING * Expand the value of SXML_OPTION_CASE_FOLDING defined in ph7Int.h. */ static void PH7_XML_OPTION_CASE_FOLDING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_OPTION_CASE_FOLDING); } /* * XML_OPTION_TARGET_ENCODING * Expand the value of SXML_OPTION_TARGET_ENCODING defined in ph7Int.h. */ static void PH7_XML_OPTION_TARGET_ENCODING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_OPTION_TARGET_ENCODING); } /* * XML_OPTION_SKIP_TAGSTART * Expand the value of SXML_OPTION_SKIP_TAGSTART defined in ph7Int.h. */ static void PH7_XML_OPTION_SKIP_TAGSTART_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_OPTION_SKIP_TAGSTART); } /* * XML_OPTION_SKIP_WHITE * Expand the value of SXML_OPTION_SKIP_TAGSTART defined in ph7Int.h. */ static void PH7_XML_OPTION_SKIP_WHITE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,SXML_OPTION_SKIP_WHITE); } /* * XML_SAX_IMPL. * Expand the name of the underlying XML engine. */ static void PH7_XML_SAX_IMP_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_string(pVal,"Symisc XML engine",(int)sizeof("Symisc XML engine")-1); } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* * JSON_HEX_TAG. * Expand the value of JSON_HEX_TAG defined in ph7Int.h. */ static void PH7_JSON_HEX_TAG_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_HEX_TAG); } /* * JSON_HEX_AMP. * Expand the value of JSON_HEX_AMP defined in ph7Int.h. */ static void PH7_JSON_HEX_AMP_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_HEX_AMP); } /* * JSON_HEX_APOS. * Expand the value of JSON_HEX_APOS defined in ph7Int.h. */ static void PH7_JSON_HEX_APOS_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_HEX_APOS); } /* * JSON_HEX_QUOT. * Expand the value of JSON_HEX_QUOT defined in ph7Int.h. */ static void PH7_JSON_HEX_QUOT_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_HEX_QUOT); } /* * JSON_FORCE_OBJECT. * Expand the value of JSON_FORCE_OBJECT defined in ph7Int.h. */ static void PH7_JSON_FORCE_OBJECT_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_FORCE_OBJECT); } /* * JSON_NUMERIC_CHECK. * Expand the value of JSON_NUMERIC_CHECK defined in ph7Int.h. */ static void PH7_JSON_NUMERIC_CHECK_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_NUMERIC_CHECK); } /* * JSON_BIGINT_AS_STRING. * Expand the value of JSON_BIGINT_AS_STRING defined in ph7Int.h. */ static void PH7_JSON_BIGINT_AS_STRING_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_BIGINT_AS_STRING); } /* * JSON_PRETTY_PRINT. * Expand the value of JSON_PRETTY_PRINT defined in ph7Int.h. */ static void PH7_JSON_PRETTY_PRINT_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_PRETTY_PRINT); } /* * JSON_UNESCAPED_SLASHES. * Expand the value of JSON_UNESCAPED_SLASHES defined in ph7Int.h. */ static void PH7_JSON_UNESCAPED_SLASHES_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_UNESCAPED_SLASHES); } /* * JSON_UNESCAPED_UNICODE. * Expand the value of JSON_UNESCAPED_UNICODE defined in ph7Int.h. */ static void PH7_JSON_UNESCAPED_UNICODE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_UNESCAPED_UNICODE); } /* * JSON_ERROR_NONE. * Expand the value of JSON_ERROR_NONE defined in ph7Int.h. */ static void PH7_JSON_ERROR_NONE_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_ERROR_NONE); } /* * JSON_ERROR_DEPTH. * Expand the value of JSON_ERROR_DEPTH defined in ph7Int.h. */ static void PH7_JSON_ERROR_DEPTH_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_ERROR_DEPTH); } /* * JSON_ERROR_STATE_MISMATCH. * Expand the value of JSON_ERROR_STATE_MISMATCH defined in ph7Int.h. */ static void PH7_JSON_ERROR_STATE_MISMATCH_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_ERROR_STATE_MISMATCH); } /* * JSON_ERROR_CTRL_CHAR. * Expand the value of JSON_ERROR_CTRL_CHAR defined in ph7Int.h. */ static void PH7_JSON_ERROR_CTRL_CHAR_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_ERROR_CTRL_CHAR); } /* * JSON_ERROR_SYNTAX. * Expand the value of JSON_ERROR_SYNTAX defined in ph7Int.h. */ static void PH7_JSON_ERROR_SYNTAX_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_ERROR_SYNTAX); } /* * JSON_ERROR_UTF8. * Expand the value of JSON_ERROR_UTF8 defined in ph7Int.h. */ static void PH7_JSON_ERROR_UTF8_Const(ph7_value *pVal,void *pUserData) { SXUNUSED(pUserData); /* cc warning */ ph7_value_int(pVal,JSON_ERROR_UTF8); } /* * static * Expand the name of the current class. 'static' otherwise. */ static void PH7_static_Const(ph7_value *pVal,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; ph7_class *pClass; /* Extract the target class if available */ pClass = PH7_VmPeekTopClass(pVm); if( pClass ){ SyString *pName = &pClass->sName; /* Expand class name */ ph7_value_string(pVal,pName->zString,(int)pName->nByte); }else{ /* Expand 'static' */ ph7_value_string(pVal,"static",sizeof("static")-1); } } /* * self * __CLASS__ * Expand the name of the current class. NULL otherwise. */ static void PH7_self_Const(ph7_value *pVal,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; ph7_class *pClass; /* Extract the target class if available */ pClass = PH7_VmPeekTopClass(pVm); if( pClass ){ SyString *pName = &pClass->sName; /* Expand class name */ ph7_value_string(pVal,pName->zString,(int)pName->nByte); }else{ /* Expand null */ ph7_value_null(pVal); } } /* parent * Expand the name of the parent class. NULL otherwise. */ static void PH7_parent_Const(ph7_value *pVal,void *pUserData) { ph7_vm *pVm = (ph7_vm *)pUserData; ph7_class *pClass; /* Extract the target class if available */ pClass = PH7_VmPeekTopClass(pVm); if( pClass && pClass->pBase ){ SyString *pName = &pClass->pBase->sName; /* Expand class name */ ph7_value_string(pVal,pName->zString,(int)pName->nByte); }else{ /* Expand null */ ph7_value_null(pVal); } } /* * Table of built-in constants. */ static const ph7_builtin_constant aBuiltIn[] = { {"PH7_VERSION", PH7_VER_Const }, {"PH7_ENGINE", PH7_VER_Const }, {"__PH7__", PH7_VER_Const }, {"PHP_OS", PH7_OS_Const }, {"PHP_EOL", PH7_EOL_Const }, {"PHP_INT_MAX", PH7_INTMAX_Const }, {"MAXINT", PH7_INTMAX_Const }, {"PHP_INT_SIZE", PH7_INTSIZE_Const }, {"PATH_SEPARATOR", PH7_PATHSEP_Const }, {"DIRECTORY_SEPARATOR", PH7_DIRSEP_Const }, {"DIR_SEP", PH7_DIRSEP_Const }, {"__TIME__", PH7_TIME_Const }, {"__DATE__", PH7_DATE_Const }, {"__FILE__", PH7_FILE_Const }, {"__DIR__", PH7_DIR_Const }, {"PHP_SHLIB_SUFFIX", PH7_PHP_SHLIB_SUFFIX_Const }, {"E_ERROR", PH7_E_ERROR_Const }, {"E_WARNING", PH7_E_WARNING_Const}, {"E_PARSE", PH7_E_PARSE_Const }, {"E_NOTICE", PH7_E_NOTICE_Const }, {"E_CORE_ERROR", PH7_E_CORE_ERROR_Const }, {"E_CORE_WARNING", PH7_E_CORE_WARNING_Const }, {"E_COMPILE_ERROR", PH7_E_COMPILE_ERROR_Const }, {"E_COMPILE_WARNING", PH7_E_COMPILE_WARNING_Const }, {"E_USER_ERROR", PH7_E_USER_ERROR_Const }, {"E_USER_WARNING", PH7_E_USER_WARNING_Const }, {"E_USER_NOTICE ", PH7_E_USER_NOTICE_Const }, {"E_STRICT", PH7_E_STRICT_Const }, {"E_RECOVERABLE_ERROR", PH7_E_RECOVERABLE_ERROR_Const }, {"E_DEPRECATED", PH7_E_DEPRECATED_Const }, {"E_USER_DEPRECATED", PH7_E_USER_DEPRECATED_Const }, {"E_ALL", PH7_E_ALL_Const }, {"CASE_LOWER", PH7_CASE_LOWER_Const }, {"CASE_UPPER", PH7_CASE_UPPER_Const }, {"STR_PAD_LEFT", PH7_STR_PAD_LEFT_Const }, {"STR_PAD_RIGHT", PH7_STR_PAD_RIGHT_Const}, {"STR_PAD_BOTH", PH7_STR_PAD_BOTH_Const }, {"COUNT_NORMAL", PH7_COUNT_NORMAL_Const }, {"COUNT_RECURSIVE", PH7_COUNT_RECURSIVE_Const }, {"SORT_ASC", PH7_SORT_ASC_Const }, {"SORT_DESC", PH7_SORT_DESC_Const }, {"SORT_REGULAR", PH7_SORT_REG_Const }, {"SORT_NUMERIC", PH7_SORT_NUMERIC_Const }, {"SORT_STRING", PH7_SORT_STRING_Const }, {"PHP_ROUND_HALF_DOWN", PH7_PHP_ROUND_HALF_DOWN_Const }, {"PHP_ROUND_HALF_EVEN", PH7_PHP_ROUND_HALF_EVEN_Const }, {"PHP_ROUND_HALF_UP", PH7_PHP_ROUND_HALF_UP_Const }, {"PHP_ROUND_HALF_ODD", PH7_PHP_ROUND_HALF_ODD_Const }, {"DEBUG_BACKTRACE_IGNORE_ARGS", PH7_DBIA_Const }, {"DEBUG_BACKTRACE_PROVIDE_OBJECT",PH7_DBPO_Const}, #ifdef PH7_ENABLE_MATH_FUNC {"M_PI", PH7_M_PI_Const }, {"M_E", PH7_M_E_Const }, {"M_LOG2E", PH7_M_LOG2E_Const }, {"M_LOG10E", PH7_M_LOG10E_Const }, {"M_LN2", PH7_M_LN2_Const }, {"M_LN10", PH7_M_LN10_Const }, {"M_PI_2", PH7_M_PI_2_Const }, {"M_PI_4", PH7_M_PI_4_Const }, {"M_1_PI", PH7_M_1_PI_Const }, {"M_2_PI", PH7_M_2_PI_Const }, {"M_SQRTPI", PH7_M_SQRTPI_Const }, {"M_2_SQRTPI", PH7_M_2_SQRTPI_Const }, {"M_SQRT2", PH7_M_SQRT2_Const }, {"M_SQRT3", PH7_M_SQRT3_Const }, {"M_SQRT1_2", PH7_M_SQRT1_2_Const }, {"M_LNPI", PH7_M_LNPI_Const }, {"M_EULER", PH7_M_EULER_Const }, #endif /* PH7_ENABLE_MATH_FUNC */ {"DATE_ATOM", PH7_DATE_ATOM_Const }, {"DATE_COOKIE", PH7_DATE_COOKIE_Const }, {"DATE_ISO8601", PH7_DATE_ISO8601_Const }, {"DATE_RFC822", PH7_DATE_RFC822_Const }, {"DATE_RFC850", PH7_DATE_RFC850_Const }, {"DATE_RFC1036", PH7_DATE_RFC1036_Const }, {"DATE_RFC1123", PH7_DATE_RFC1123_Const }, {"DATE_RFC2822", PH7_DATE_RFC2822_Const }, {"DATE_RFC3339", PH7_DATE_ATOM_Const }, {"DATE_RSS", PH7_DATE_RSS_Const }, {"DATE_W3C", PH7_DATE_W3C_Const }, {"ENT_COMPAT", PH7_ENT_COMPAT_Const }, {"ENT_QUOTES", PH7_ENT_QUOTES_Const }, {"ENT_NOQUOTES", PH7_ENT_NOQUOTES_Const }, {"ENT_IGNORE", PH7_ENT_IGNORE_Const }, {"ENT_SUBSTITUTE", PH7_ENT_SUBSTITUTE_Const}, {"ENT_DISALLOWED", PH7_ENT_DISALLOWED_Const}, {"ENT_HTML401", PH7_ENT_HTML401_Const }, {"ENT_XML1", PH7_ENT_XML1_Const }, {"ENT_XHTML", PH7_ENT_XHTML_Const }, {"ENT_HTML5", PH7_ENT_HTML5_Const }, {"ISO-8859-1", PH7_ISO88591_Const }, {"ISO_8859_1", PH7_ISO88591_Const }, {"UTF-8", PH7_UTF8_Const }, {"UTF8", PH7_UTF8_Const }, {"HTML_ENTITIES", PH7_HTML_ENTITIES_Const}, {"HTML_SPECIALCHARS", PH7_HTML_SPECIALCHARS_Const }, {"PHP_URL_SCHEME", PH7_PHP_URL_SCHEME_Const}, {"PHP_URL_HOST", PH7_PHP_URL_HOST_Const}, {"PHP_URL_PORT", PH7_PHP_URL_PORT_Const}, {"PHP_URL_USER", PH7_PHP_URL_USER_Const}, {"PHP_URL_PASS", PH7_PHP_URL_PASS_Const}, {"PHP_URL_PATH", PH7_PHP_URL_PATH_Const}, {"PHP_URL_QUERY", PH7_PHP_URL_QUERY_Const}, {"PHP_URL_FRAGMENT", PH7_PHP_URL_FRAGMENT_Const}, {"PHP_QUERY_RFC1738", PH7_PHP_QUERY_RFC1738_Const}, {"PHP_QUERY_RFC3986", PH7_PHP_QUERY_RFC3986_Const}, {"FNM_NOESCAPE", PH7_FNM_NOESCAPE_Const }, {"FNM_PATHNAME", PH7_FNM_PATHNAME_Const }, {"FNM_PERIOD", PH7_FNM_PERIOD_Const }, {"FNM_CASEFOLD", PH7_FNM_CASEFOLD_Const }, {"PATHINFO_DIRNAME", PH7_PATHINFO_DIRNAME_Const }, {"PATHINFO_BASENAME", PH7_PATHINFO_BASENAME_Const }, {"PATHINFO_EXTENSION", PH7_PATHINFO_EXTENSION_Const}, {"PATHINFO_FILENAME", PH7_PATHINFO_FILENAME_Const }, {"ASSERT_ACTIVE", PH7_ASSERT_ACTIVE_Const }, {"ASSERT_WARNING", PH7_ASSERT_WARNING_Const }, {"ASSERT_BAIL", PH7_ASSERT_BAIL_Const }, {"ASSERT_QUIET_EVAL", PH7_ASSERT_QUIET_EVAL_Const }, {"ASSERT_CALLBACK", PH7_ASSERT_CALLBACK_Const }, {"SEEK_SET", PH7_SEEK_SET_Const }, {"SEEK_CUR", PH7_SEEK_CUR_Const }, {"SEEK_END", PH7_SEEK_END_Const }, {"LOCK_EX", PH7_LOCK_EX_Const }, {"LOCK_SH", PH7_LOCK_SH_Const }, {"LOCK_NB", PH7_LOCK_NB_Const }, {"LOCK_UN", PH7_LOCK_UN_Const }, {"FILE_USE_INCLUDE_PATH", PH7_FILE_USE_INCLUDE_PATH_Const}, {"FILE_IGNORE_NEW_LINES", PH7_FILE_IGNORE_NEW_LINES_Const}, {"FILE_SKIP_EMPTY_LINES", PH7_FILE_SKIP_EMPTY_LINES_Const}, {"FILE_APPEND", PH7_FILE_APPEND_Const }, {"SCANDIR_SORT_ASCENDING", PH7_SCANDIR_SORT_ASCENDING_Const }, {"SCANDIR_SORT_DESCENDING",PH7_SCANDIR_SORT_DESCENDING_Const }, {"SCANDIR_SORT_NONE", PH7_SCANDIR_SORT_NONE_Const }, {"GLOB_MARK", PH7_GLOB_MARK_Const }, {"GLOB_NOSORT", PH7_GLOB_NOSORT_Const }, {"GLOB_NOCHECK", PH7_GLOB_NOCHECK_Const }, {"GLOB_NOESCAPE", PH7_GLOB_NOESCAPE_Const}, {"GLOB_BRACE", PH7_GLOB_BRACE_Const }, {"GLOB_ONLYDIR", PH7_GLOB_ONLYDIR_Const }, {"GLOB_ERR", PH7_GLOB_ERR_Const }, {"STDIN", PH7_STDIN_Const }, {"stdin", PH7_STDIN_Const }, {"STDOUT", PH7_STDOUT_Const }, {"stdout", PH7_STDOUT_Const }, {"STDERR", PH7_STDERR_Const }, {"stderr", PH7_STDERR_Const }, {"INI_SCANNER_NORMAL", PH7_INI_SCANNER_NORMAL_Const }, {"INI_SCANNER_RAW", PH7_INI_SCANNER_RAW_Const }, {"EXTR_OVERWRITE", PH7_EXTR_OVERWRITE_Const }, {"EXTR_SKIP", PH7_EXTR_SKIP_Const }, {"EXTR_PREFIX_SAME", PH7_EXTR_PREFIX_SAME_Const }, {"EXTR_PREFIX_ALL", PH7_EXTR_PREFIX_ALL_Const }, {"EXTR_PREFIX_INVALID", PH7_EXTR_PREFIX_INVALID_Const }, {"EXTR_IF_EXISTS", PH7_EXTR_IF_EXISTS_Const }, {"EXTR_PREFIX_IF_EXISTS",PH7_EXTR_PREFIX_IF_EXISTS_Const}, #ifndef PH7_DISABLE_BUILTIN_FUNC {"XML_ERROR_NONE", PH7_XML_ERROR_NONE_Const}, {"XML_ERROR_NO_MEMORY", PH7_XML_ERROR_NO_MEMORY_Const}, {"XML_ERROR_SYNTAX", PH7_XML_ERROR_SYNTAX_Const}, {"XML_ERROR_NO_ELEMENTS",PH7_XML_ERROR_NO_ELEMENTS_Const}, {"XML_ERROR_INVALID_TOKEN", PH7_XML_ERROR_INVALID_TOKEN_Const}, {"XML_ERROR_UNCLOSED_TOKEN",PH7_XML_ERROR_UNCLOSED_TOKEN_Const}, {"XML_ERROR_PARTIAL_CHAR", PH7_XML_ERROR_PARTIAL_CHAR_Const}, {"XML_ERROR_TAG_MISMATCH", PH7_XML_ERROR_TAG_MISMATCH_Const}, {"XML_ERROR_DUPLICATE_ATTRIBUTE", PH7_XML_ERROR_DUPLICATE_ATTRIBUTE_Const}, {"XML_ERROR_JUNK_AFTER_DOC_ELEMENT",PH7_XML_ERROR_JUNK_AFTER_DOC_ELEMENT_Const}, {"XML_ERROR_PARAM_ENTITY_REF", PH7_XML_ERROR_PARAM_ENTITY_REF_Const}, {"XML_ERROR_UNDEFINED_ENTITY", PH7_XML_ERROR_UNDEFINED_ENTITY_Const}, {"XML_ERROR_RECURSIVE_ENTITY_REF", PH7_XML_ERROR_RECURSIVE_ENTITY_REF_Const}, {"XML_ERROR_ASYNC_ENTITY", PH7_XML_ERROR_ASYNC_ENTITY_Const}, {"XML_ERROR_BAD_CHAR_REF", PH7_XML_ERROR_BAD_CHAR_REF_Const}, {"XML_ERROR_BINARY_ENTITY_REF", PH7_XML_ERROR_BINARY_ENTITY_REF_Const}, {"XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF", PH7_XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF_Const}, {"XML_ERROR_MISPLACED_XML_PI", PH7_XML_ERROR_MISPLACED_XML_PI_Const}, {"XML_ERROR_UNKNOWN_ENCODING", PH7_XML_ERROR_UNKNOWN_ENCODING_Const}, {"XML_ERROR_INCORRECT_ENCODING", PH7_XML_ERROR_INCORRECT_ENCODING_Const}, {"XML_ERROR_UNCLOSED_CDATA_SECTION", PH7_XML_ERROR_UNCLOSED_CDATA_SECTION_Const}, {"XML_ERROR_EXTERNAL_ENTITY_HANDLING",PH7_XML_ERROR_EXTERNAL_ENTITY_HANDLING_Const}, {"XML_OPTION_CASE_FOLDING", PH7_XML_OPTION_CASE_FOLDING_Const}, {"XML_OPTION_TARGET_ENCODING", PH7_XML_OPTION_TARGET_ENCODING_Const}, {"XML_OPTION_SKIP_TAGSTART", PH7_XML_OPTION_SKIP_TAGSTART_Const}, {"XML_OPTION_SKIP_WHITE", PH7_XML_OPTION_SKIP_WHITE_Const}, {"XML_SAX_IMPL", PH7_XML_SAX_IMP_Const}, #endif /* PH7_DISABLE_BUILTIN_FUNC */ {"JSON_HEX_TAG", PH7_JSON_HEX_TAG_Const}, {"JSON_HEX_AMP", PH7_JSON_HEX_AMP_Const}, {"JSON_HEX_APOS", PH7_JSON_HEX_APOS_Const}, {"JSON_HEX_QUOT", PH7_JSON_HEX_QUOT_Const}, {"JSON_FORCE_OBJECT", PH7_JSON_FORCE_OBJECT_Const}, {"JSON_NUMERIC_CHECK", PH7_JSON_NUMERIC_CHECK_Const}, {"JSON_BIGINT_AS_STRING", PH7_JSON_BIGINT_AS_STRING_Const}, {"JSON_PRETTY_PRINT", PH7_JSON_PRETTY_PRINT_Const}, {"JSON_UNESCAPED_SLASHES", PH7_JSON_UNESCAPED_SLASHES_Const}, {"JSON_UNESCAPED_UNICODE", PH7_JSON_UNESCAPED_UNICODE_Const}, {"JSON_ERROR_NONE", PH7_JSON_ERROR_NONE_Const}, {"JSON_ERROR_DEPTH", PH7_JSON_ERROR_DEPTH_Const}, {"JSON_ERROR_STATE_MISMATCH", PH7_JSON_ERROR_STATE_MISMATCH_Const}, {"JSON_ERROR_CTRL_CHAR", PH7_JSON_ERROR_CTRL_CHAR_Const}, {"JSON_ERROR_SYNTAX", PH7_JSON_ERROR_SYNTAX_Const}, {"JSON_ERROR_UTF8", PH7_JSON_ERROR_UTF8_Const}, {"static", PH7_static_Const }, {"self", PH7_self_Const }, {"__CLASS__", PH7_self_Const }, {"parent", PH7_parent_Const } }; /* * Register the built-in constants defined above. */ PH7_PRIVATE void PH7_RegisterBuiltInConstant(ph7_vm *pVm) { sxu32 n; /* * Note that all built-in constants have access to the ph7 virtual machine * that trigger the constant invocation as their private data. */ for( n = 0 ; n < SX_ARRAYSIZE(aBuiltIn) ; ++n ){ ph7_create_constant(&(*pVm),aBuiltIn[n].zName,aBuiltIn[n].xExpand,&(*pVm)); } } /* * ---------------------------------------------------------- * File: compile.c * MD5: 85c9bc2bcbb35e9f704442f7b8f3b993 * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: compile.c v6.0 Win7 2012-08-18 05:11 stable $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* * This file implement a thread-safe and full-reentrant compiler for the PH7 engine. * That is, routines defined in this file takes a stream of tokens and output * PH7 bytecode instructions. */ /* Forward declaration */ typedef struct LangConstruct LangConstruct; typedef struct JumpFixup JumpFixup; typedef struct Label Label; /* Block [i.e: set of statements] control flags */ #define GEN_BLOCK_LOOP 0x001 /* Loop block [i.e: for,while,...] */ #define GEN_BLOCK_PROTECTED 0x002 /* Protected block */ #define GEN_BLOCK_COND 0x004 /* Conditional block [i.e: if(condition){} ]*/ #define GEN_BLOCK_FUNC 0x008 /* Function body */ #define GEN_BLOCK_GLOBAL 0x010 /* Global block (always set)*/ #define GEN_BLOC_NESTED_FUNC 0x020 /* Nested function body */ #define GEN_BLOCK_EXPR 0x040 /* Expression */ #define GEN_BLOCK_STD 0x080 /* Standard block */ #define GEN_BLOCK_EXCEPTION 0x100 /* Exception block [i.e: try{ } }*/ #define GEN_BLOCK_SWITCH 0x200 /* Switch statement */ /* * Each label seen in the input is recorded in an instance * of the following structure. * A label is a target point [i.e: a jump destination] that is specified * by an identifier followed by a colon. * Example * LABEL: * echo "hello\n"; */ struct Label { ph7_vm_func *pFunc; /* Compiled function where the label was declared.NULL otherwise */ sxu32 nJumpDest; /* Jump destination */ SyString sName; /* Label name */ sxu32 nLine; /* Line number this label occurs */ sxu8 bRef; /* True if the label was referenced */ }; /* * Compilation of some PHP constructs such as if, for, while, the logical or * (||) and logical and (&&) operators in expressions requires the * generation of forward jumps. * Since the destination PC target of these jumps isn't known when the jumps * are emitted, we record each forward jump in an instance of the following * structure. Those jumps are fixed later when the jump destination is resolved. */ struct JumpFixup { sxi32 nJumpType; /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */ sxu32 nInstrIdx; /* Instruction index to fix later when the jump destination is resolved. */ /* The following fields are only used by the goto statement */ SyString sLabel; /* Label name */ ph7_vm_func *pFunc; /* Compiled function inside which the goto was emitted. NULL otherwise */ sxu32 nLine; /* Track line number */ }; /* * Each language construct is represented by an instance * of the following structure. */ struct LangConstruct { sxu32 nID; /* Language construct ID [i.e: PH7_TKWRD_WHILE,PH7_TKWRD_FOR,PH7_TKWRD_IF...] */ ProcLangConstruct xConstruct; /* C function implementing the language construct */ }; /* Compilation flags */ #define PH7_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */ /* Token stream synchronization macros */ #define SWAP_TOKEN_STREAM(GEN,START,END)\ pTmp = GEN->pEnd;\ pGen->pIn = START;\ pGen->pEnd = END #define UPDATE_TOKEN_STREAM(GEN)\ if( GEN->pIn < pTmp ){\ GEN->pIn++;\ }\ GEN->pEnd = pTmp #define SWAP_DELIMITER(GEN,START,END)\ pTmpIn = GEN->pIn;\ pTmpEnd = GEN->pEnd;\ GEN->pIn = START;\ GEN->pEnd = END #define RE_SWAP_DELIMITER(GEN)\ GEN->pIn = pTmpIn;\ GEN->pEnd = pTmpEnd /* Flags related to expression compilation */ #define EXPR_FLAG_LOAD_IDX_STORE 0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */ #define EXPR_FLAG_RDONLY_LOAD 0x002 /* Read-only load, refer to the 'PH7_OP_LOAD' VM instruction for more information */ #define EXPR_FLAG_COMMA_STATEMENT 0x004 /* Treat comma expression as a single statement (used by class attributes) */ /* Forward declaration */ static sxi32 PH7_CompileExpr(ph7_gen_state *pGen,sxi32 iFlags,sxi32 (*xTreeValidator)(ph7_gen_state *,ph7_expr_node *)); /* * Local utility routines used in the code generation phase. */ /* * Check if the given name refer to a valid label. * Return SXRET_OK and write a pointer to that label on success. * Any other return value indicates no such label. */ static sxi32 GenStateGetLabel(ph7_gen_state *pGen,SyString *pName,Label **ppOut) { Label *aLabel; sxu32 n; /* Perform a linear scan on the label table */ aLabel = (Label *)SySetBasePtr(&pGen->aLabel); for( n = 0 ; n < SySetUsed(&pGen->aLabel) ; ++n ){ if( SyStringCmp(&aLabel[n].sName,pName,SyMemcmp) == 0 ){ /* Jump destination found */ aLabel[n].bRef = TRUE; if( ppOut ){ *ppOut = &aLabel[n]; } return SXRET_OK; } } /* No such destination */ return SXERR_NOTFOUND; } /* * Fetch a block that correspond to the given criteria from the stack of * compiled blocks. * Return a pointer to that block on success. NULL otherwise. */ static GenBlock * GenStateFetchBlock(GenBlock *pCurrent,sxi32 iBlockType,sxi32 iCount) { GenBlock *pBlock = pCurrent; for(;;){ if( pBlock->iFlags & iBlockType ){ iCount--; /* Decrement nesting level */ if( iCount < 1 ){ /* Block meet with the desired criteria */ return pBlock; } } /* Point to the upper block */ pBlock = pBlock->pParent; if( pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC)) ){ /* Forbidden */ break; } } /* No such block */ return 0; } /* * Initialize a freshly allocated block instance. */ static void GenStateInitBlock( ph7_gen_state *pGen, /* Code generator state */ GenBlock *pBlock, /* Target block */ sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/ sxu32 nFirstInstr, /* First instruction to compile */ void *pUserData /* Upper layer private data */ ) { /* Initialize block fields */ pBlock->nFirstInstr = nFirstInstr; pBlock->pUserData = pUserData; pBlock->pGen = pGen; pBlock->iFlags = iType; pBlock->pParent = 0; SySetInit(&pBlock->aJumpFix,&pGen->pVm->sAllocator,sizeof(JumpFixup)); SySetInit(&pBlock->aPostContFix,&pGen->pVm->sAllocator,sizeof(JumpFixup)); } /* * Allocate a new block instance. * Return SXRET_OK and write a pointer to the new instantiated block * on success.Otherwise generate a compile-time error and abort * processing on failure. */ static sxi32 GenStateEnterBlock( ph7_gen_state *pGen, /* Code generator state */ sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/ sxu32 nFirstInstr, /* First instruction to compile */ void *pUserData, /* Upper layer private data */ GenBlock **ppBlock /* OUT: instantiated block */ ) { GenBlock *pBlock; /* Allocate a new block instance */ pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator,sizeof(GenBlock)); if( pBlock == 0 ){ /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ PH7_GenCompileError(&(*pGen),E_ERROR,1,"Fatal, PH7 engine is running out-of-memory"); /* Abort processing immediately */ return SXERR_ABORT; } /* Zero the structure */ SyZero(pBlock,sizeof(GenBlock)); GenStateInitBlock(&(*pGen),pBlock,iType,nFirstInstr,pUserData); /* Link to the parent block */ pBlock->pParent = pGen->pCurrent; /* Mark as the current block */ pGen->pCurrent = pBlock; if( ppBlock ){ /* Write a pointer to the new instance */ *ppBlock = pBlock; } return SXRET_OK; } /* * Release block fields without freeing the whole instance. */ static void GenStateReleaseBlock(GenBlock *pBlock) { SySetRelease(&pBlock->aPostContFix); SySetRelease(&pBlock->aJumpFix); } /* * Release a block. */ static void GenStateFreeBlock(GenBlock *pBlock) { ph7_gen_state *pGen = pBlock->pGen; GenStateReleaseBlock(&(*pBlock)); /* Free the instance */ SyMemBackendPoolFree(&pGen->pVm->sAllocator,pBlock); } /* * POP and release a block from the stack of compiled blocks. */ static sxi32 GenStateLeaveBlock(ph7_gen_state *pGen,GenBlock **ppBlock) { GenBlock *pBlock = pGen->pCurrent; if( pBlock == 0 ){ /* No more block to pop */ return SXERR_EMPTY; } /* Point to the upper block */ pGen->pCurrent = pBlock->pParent; if( ppBlock ){ /* Write a pointer to the popped block */ *ppBlock = pBlock; }else{ /* Safely release the block */ GenStateFreeBlock(&(*pBlock)); } return SXRET_OK; } /* * Emit a forward jump. * Notes on forward jumps * Compilation of some PHP constructs such as if,for,while and the logical or * (||) and logical and (&&) operators in expressions requires the * generation of forward jumps. * Since the destination PC target of these jumps isn't known when the jumps * are emitted, we record each forward jump in an instance of the following * structure. Those jumps are fixed later when the jump destination is resolved. */ static sxi32 GenStateNewJumpFixup(GenBlock *pBlock,sxi32 nJumpType,sxu32 nInstrIdx) { JumpFixup sJumpFix; sxi32 rc; /* Init the JumpFixup structure */ sJumpFix.nJumpType = nJumpType; sJumpFix.nInstrIdx = nInstrIdx; /* Insert in the jump fixup table */ rc = SySetPut(&pBlock->aJumpFix,(const void *)&sJumpFix); return rc; } /* * Fix a forward jump now the jump destination is resolved. * Return the total number of fixed jumps. * Notes on forward jumps: * Compilation of some PHP constructs such as if,for,while and the logical or * (||) and logical and (&&) operators in expressions requires the * generation of forward jumps. * Since the destination PC target of these jumps isn't known when the jumps * are emitted, we record each forward jump in an instance of the following * structure.Those jumps are fixed later when the jump destination is resolved. */ static sxu32 GenStateFixJumps(GenBlock *pBlock,sxi32 nJumpType,sxu32 nJumpDest) { JumpFixup *aFix; VmInstr *pInstr; sxu32 nFixed; sxu32 n; /* Point to the jump fixup table */ aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix); /* Fix the desired jumps */ for( nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n ){ if( aFix[n].nJumpType < 0 ){ /* Already fixed */ continue; } if( nJumpType > 0 && aFix[n].nJumpType != nJumpType ){ /* Not of our interest */ continue; } /* Point to the instruction to fix */ pInstr = PH7_VmGetInstr(pBlock->pGen->pVm,aFix[n].nInstrIdx); if( pInstr ){ pInstr->iP2 = nJumpDest; nFixed++; /* Mark as fixed */ aFix[n].nJumpType = -1; } } /* Total number of fixed jumps */ return nFixed; } /* * Fix a 'goto' now the jump destination is resolved. * The goto statement can be used to jump to another section * in the program. * Refer to the routine responsible of compiling the goto * statement for more information. */ static sxi32 GenStateFixGoto(ph7_gen_state *pGen,sxu32 nOfft) { JumpFixup *pJump,*aJumps; Label *pLabel,*aLabel; VmInstr *pInstr; sxi32 rc; sxu32 n; /* Point to the goto table */ aJumps = (JumpFixup *)SySetBasePtr(&pGen->aGoto); /* Fix */ for( n = nOfft ; n < SySetUsed(&pGen->aGoto) ; ++n ){ pJump = &aJumps[n]; /* Extract the target label */ rc = GenStateGetLabel(&(*pGen),&pJump->sLabel,&pLabel); if( rc != SXRET_OK ){ /* No such label */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pJump->nLine,"Label '%z' was referenced but not defined",&pJump->sLabel); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } continue; } /* Make sure the target label is reachable */ if( pLabel->pFunc != pJump->pFunc ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pJump->nLine,"Label '%z' is unreachable",&pJump->sLabel); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Fix the jump now the destination is resolved */ pInstr = PH7_VmGetInstr(pGen->pVm,pJump->nInstrIdx); if( pInstr ){ pInstr->iP2 = pLabel->nJumpDest; } } aLabel = (Label *)SySetBasePtr(&pGen->aLabel); for( n = 0 ; n < SySetUsed(&pGen->aLabel) ; ++n ){ if( aLabel[n].bRef == FALSE ){ /* Emit a warning */ PH7_GenCompileError(&(*pGen),E_WARNING,aLabel[n].nLine, "Label '%z' is defined but not referenced",&aLabel[n].sName); } } return SXRET_OK; } /* * Check if a given token value is installed in the literal table. */ static sxi32 GenStateFindLiteral(ph7_gen_state *pGen,const SyString *pValue,sxu32 *pIdx) { SyHashEntry *pEntry; pEntry = SyHashGet(&pGen->hLiteral,(const void *)pValue->zString,pValue->nByte); if( pEntry == 0 ){ return SXERR_NOTFOUND; } *pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); return SXRET_OK; } /* * Install a given constant index in the literal table. * In order to be installed, the ph7_value must be of type string. */ static sxi32 GenStateInstallLiteral(ph7_gen_state *pGen,ph7_value *pObj,sxu32 nIdx) { if( SyBlobLength(&pObj->sBlob) > 0 ){ SyHashInsert(&pGen->hLiteral,SyBlobData(&pObj->sBlob),SyBlobLength(&pObj->sBlob),SX_INT_TO_PTR(nIdx)); } return SXRET_OK; } /* * Reserve a room for a numeric constant [i.e: 64-bit integer or real number] * in the constant table. */ static ph7_value * GenStateInstallNumLiteral(ph7_gen_state *pGen,sxu32 *pIdx) { ph7_value *pObj; sxu32 nIdx = 0; /* cc warning */ /* Reserve a new constant */ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,1,"PH7 engine is running out of memory"); return 0; } *pIdx = nIdx; /* TODO(chems): Create a numeric table (64bit int keys) same as * the constant string iterals table [optimization purposes]. */ return pObj; } /* * Implementation of the PHP language constructs. */ /* Forward declaration */ static sxi32 GenStateCompileChunk(ph7_gen_state *pGen,sxi32 iFlags); /* * Compile a numeric [i.e: integer or real] literal. * Notes on the integer type. * According to the PHP language reference manual * Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8) * or binary (base 2) notation, optionally preceded by a sign (- or +). * To use octal notation, precede the number with a 0 (zero). To use hexadecimal * notation precede the number with 0x. To use binary notation precede the number with 0b. * Symisc eXtension to the integer type. * PH7 introduced platform-independant 64-bit integer unlike the standard PHP engine * where the size of an integer is platform-dependent.That is,the size of an integer * is 8 bytes and the maximum integer size is 0x7FFFFFFFFFFFFFFF for all platforms * [i.e: either 32bit or 64bit]. * For more information on this powerfull extension please refer to the official * documentation. */ static sxi32 PH7_CompileNumLiteral(ph7_gen_state *pGen,sxi32 iCompileFlag) { SyToken *pToken = pGen->pIn; /* Raw token */ sxu32 nIdx = 0; if( pToken->nType & PH7_TK_INTEGER ){ ph7_value *pObj; sxi64 iValue; iValue = PH7_TokenValueToInt64(&pToken->sData); pObj = GenStateInstallNumLiteral(&(*pGen),&nIdx); if( pObj == 0 ){ SXUNUSED(iCompileFlag); /* cc warning */ return SXERR_ABORT; } PH7_MemObjInitFromInt(pGen->pVm,pObj,iValue); }else{ /* Real number */ ph7_value *pObj; /* Reserve a new constant */ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,1,"PH7 engine is running out of memory"); return SXERR_ABORT; } PH7_MemObjInitFromString(pGen->pVm,pObj,&pToken->sData); PH7_MemObjToReal(pObj); } /* Emit the load constant instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); /* Node successfully compiled */ return SXRET_OK; } /* * Compile a single quoted string. * According to the PHP language reference manual: * * The simplest way to specify a string is to enclose it in single quotes (the character ' ). * To specify a literal single quote, escape it with a backslash (\). To specify a literal * backslash, double it (\\). All other instances of backslash will be treated as a literal * backslash: this means that the other escape sequences you might be used to, such as \r * or \n, will be output literally as specified rather than having any special meaning. * */ PH7_PRIVATE sxi32 PH7_CompileSimpleString(ph7_gen_state *pGen,sxi32 iCompileFlag) { SyString *pStr = &pGen->pIn->sData; /* Constant string literal */ const char *zIn,*zCur,*zEnd; ph7_value *pObj; sxu32 nIdx; nIdx = 0; /* Prevent compiler warning */ /* Delimit the string */ zIn = pStr->zString; zEnd = &zIn[pStr->nByte]; if( zIn >= zEnd ){ /* Empty string,load NULL */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0,0,0); return SXRET_OK; } if( SXRET_OK == GenStateFindLiteral(&(*pGen),pStr,&nIdx) ){ /* Already processed,emit the load constant instruction * and return. */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); return SXRET_OK; } /* Reserve a new constant */ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,1,"PH7 engine is running out of memory"); SXUNUSED(iCompileFlag); /* cc warning */ return SXERR_ABORT; } PH7_MemObjInitFromString(pGen->pVm,pObj,0); /* Compile the node */ for(;;){ if( zIn >= zEnd ){ /* End of input */ break; } zCur = zIn; while( zIn < zEnd && zIn[0] != '\\' ){ zIn++; } if( zIn > zCur ){ /* Append raw contents*/ PH7_MemObjStringAppend(pObj,zCur,(sxu32)(zIn-zCur)); } zIn++; if( zIn < zEnd ){ if( zIn[0] == '\\' ){ /* A literal backslash */ PH7_MemObjStringAppend(pObj,"\\",sizeof(char)); }else if( zIn[0] == '\'' ){ /* A single quote */ PH7_MemObjStringAppend(pObj,"'",sizeof(char)); }else{ /* verbatim copy */ zIn--; PH7_MemObjStringAppend(pObj,zIn,sizeof(char)*2); zIn++; } } /* Advance the stream cursor */ zIn++; } /* Emit the load constant instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); if( pStr->nByte < 1024 ){ /* Install in the literal table */ GenStateInstallLiteral(pGen,pObj,nIdx); } /* Node successfully compiled */ return SXRET_OK; } /* * Compile a nowdoc string. * According to the PHP language reference manual: * * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings. * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc. * The construct is ideal for embedding PHP code or other large blocks of text without the * need for escaping. It shares some features in common with the SGML * construct, in that it declares a block of text which is not for parsing. * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier * which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc * identifiers also apply to nowdoc identifiers, especially those regarding the appearance * of the closing identifier. */ static sxi32 PH7_CompileNowDoc(ph7_gen_state *pGen,sxi32 iCompileFlag) { SyString *pStr = &pGen->pIn->sData; /* Constant string literal */ ph7_value *pObj; sxu32 nIdx; nIdx = 0; /* Prevent compiler warning */ if( pStr->nByte <= 0 ){ /* Empty string,load NULL */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0,0,0); return SXRET_OK; } /* Reserve a new constant */ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"PH7 engine is running out of memory"); SXUNUSED(iCompileFlag); /* cc warning */ return SXERR_ABORT; } /* No processing is done here, simply a memcpy() operation */ PH7_MemObjInitFromString(pGen->pVm,pObj,pStr); /* Emit the load constant instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); /* Node successfully compiled */ return SXRET_OK; } /* * Process variable expression [i.e: "$var","${var}"] embedded in a double quoted/heredoc string. * According to the PHP language reference manual * When a string is specified in double quotes or with heredoc,variables are parsed within it. * There are two types of syntax: a simple one and a complex one. The simple syntax is the most * common and convenient. It provides a way to embed a variable, an array value, or an object * property in a string with a minimum of effort. * Simple syntax * If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible * to form a valid variable name. Enclose the variable name in curly braces to explicitly specify * the end of the name. * Similarly, an array index or an object property can be parsed. With array indices, the closing * square bracket (]) marks the end of the index. The same rules apply to object properties * as to simple variables. * Complex (curly) syntax * This isn't called complex because the syntax is complex, but because it allows for the use * of complex expressions. * Any scalar variable, array element or object property with a string representation can be * included via this syntax. Simply write the expression the same way as it would appear outside * the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only * be recognised when the $ immediately follows the {. Use {\$ to get a literal {$ */ static sxi32 GenStateProcessStringExpression( ph7_gen_state *pGen, /* Code generator state */ sxu32 nLine, /* Line number */ const char *zIn, /* Raw expression */ const char *zEnd /* End of the expression */ ) { SyToken *pTmpIn,*pTmpEnd; SySet sToken; sxi32 rc; /* Initialize the token set */ SySetInit(&sToken,&pGen->pVm->sAllocator,sizeof(SyToken)); /* Preallocate some slots */ SySetAlloc(&sToken,0x08); /* Tokenize the text */ PH7_TokenizePHP(zIn,(sxu32)(zEnd-zIn),nLine,&sToken); /* Swap delimiter */ pTmpIn = pGen->pIn; pTmpEnd = pGen->pEnd; pGen->pIn = (SyToken *)SySetBasePtr(&sToken); pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)]; /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,0); /* Restore token stream */ pGen->pIn = pTmpIn; pGen->pEnd = pTmpEnd; /* Release the token set */ SySetRelease(&sToken); /* Compilation result */ return rc; } /* * Reserve a new constant for a double quoted/heredoc string. */ static ph7_value * GenStateNewStrObj(ph7_gen_state *pGen,sxi32 *pCount) { ph7_value *pConstObj; sxu32 nIdx = 0; /* Reserve a new constant */ pConstObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pConstObj == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"PH7 engine is running out of memory"); return 0; } (*pCount)++; PH7_MemObjInitFromString(pGen->pVm,pConstObj,0); /* Emit the load constant instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); return pConstObj; } /* * Compile a double quoted/heredoc string. * According to the PHP language reference manual * Heredoc * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier * is provided, then a newline. The string itself follows, and then the same identifier again * to close the quotation. * The closing identifier must begin in the first column of the line. Also, the identifier must * follow the same naming rules as any other label in PHP: it must contain only alphanumeric * characters and underscores, and must start with a non-digit character or underscore. * Warning * It is very important to note that the line with the closing identifier must contain * no other characters, except possibly a semicolon (;). That means especially that the identifier * may not be indented, and there may not be any spaces or tabs before or after the semicolon. * It's also important to realize that the first character before the closing identifier must * be a newline as defined by the local operating system. This is \n on UNIX systems, including Mac OS X. * The closing delimiter (possibly followed by a semicolon) must also be followed by a newline. * If this rule is broken and the closing identifier is not "clean", it will not be considered a closing * identifier, and PHP will continue looking for one. If a proper closing identifier is not found before * the end of the current file, a parse error will result at the last line. * Heredocs can not be used for initializing class properties. * Double quoted * If the string is enclosed in double-quotes ("), PHP will interpret more escape sequences for special characters: * Escaped characters Sequence Meaning * \n linefeed (LF or 0x0A (10) in ASCII) * \r carriage return (CR or 0x0D (13) in ASCII) * \t horizontal tab (HT or 0x09 (9) in ASCII) * \v vertical tab (VT or 0x0B (11) in ASCII) * \f form feed (FF or 0x0C (12) in ASCII) * \\ backslash * \$ dollar sign * \" double-quote * \[0-7]{1,3} the sequence of characters matching the regular expression is a character in octal notation * \x[0-9A-Fa-f]{1,2} the sequence of characters matching the regular expression is a character in hexadecimal notation * As in single quoted strings, escaping any other character will result in the backslash being printed too. * The most important feature of double-quoted strings is the fact that variable names will be expanded. * See string parsing for details. */ static sxi32 GenStateCompileString(ph7_gen_state *pGen) { SyString *pStr = &pGen->pIn->sData; /* Raw token value */ const char *zIn,*zCur,*zEnd; ph7_value *pObj = 0; sxi32 iCons; sxi32 rc; /* Delimit the string */ zIn = pStr->zString; zEnd = &zIn[pStr->nByte]; if( zIn >= zEnd ){ /* Empty string,load NULL */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0,0,0); return SXRET_OK; } zCur = 0; /* Compile the node */ iCons = 0; for(;;){ zCur = zIn; while( zIn < zEnd && zIn[0] != '\\' ){ if( zIn[0] == '{' && &zIn[1] < zEnd && zIn[1] == '$' ){ break; }else if(zIn[0] == '$' && &zIn[1] < zEnd && (((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '{' || zIn[1] == '_')) ){ break; } zIn++; } if( zIn > zCur ){ if( pObj == 0 ){ pObj = GenStateNewStrObj(&(*pGen),&iCons); if( pObj == 0 ){ return SXERR_ABORT; } } PH7_MemObjStringAppend(pObj,zCur,(sxu32)(zIn-zCur)); } if( zIn >= zEnd ){ break; } if( zIn[0] == '\\' ){ const char *zPtr = 0; sxu32 n; zIn++; if( zIn >= zEnd ){ break; } if( pObj == 0 ){ pObj = GenStateNewStrObj(&(*pGen),&iCons); if( pObj == 0 ){ return SXERR_ABORT; } } n = sizeof(char); /* size of conversion */ switch( zIn[0] ){ case '$': /* Dollar sign */ PH7_MemObjStringAppend(pObj,"$",sizeof(char)); break; case '\\': /* A literal backslash */ PH7_MemObjStringAppend(pObj,"\\",sizeof(char)); break; case 'a': /* The "alert" character (BEL)[ctrl+g] ASCII code 7 */ PH7_MemObjStringAppend(pObj,"\a",sizeof(char)); break; case 'b': /* Backspace (BS)[ctrl+h] ASCII code 8 */ PH7_MemObjStringAppend(pObj,"\b",sizeof(char)); break; case 'f': /* Form-feed (FF)[ctrl+l] ASCII code 12 */ PH7_MemObjStringAppend(pObj,"\f",sizeof(char)); break; case 'n': /* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */ PH7_MemObjStringAppend(pObj,"\n",sizeof(char)); break; case 'r': /* Carriage return (CR)[ctrl+m] ASCII code 13 */ PH7_MemObjStringAppend(pObj,"\r",sizeof(char)); break; case 't': /* Horizontal tab (HT)[ctrl+i] ASCII code 9 */ PH7_MemObjStringAppend(pObj,"\t",sizeof(char)); break; case 'v': /* Vertical tab(VT)[ctrl+k] ASCII code 11 */ PH7_MemObjStringAppend(pObj,"\v",sizeof(char)); break; case '\'': /* Single quote */ PH7_MemObjStringAppend(pObj,"'",sizeof(char)); break; case '"': /* Double quote */ PH7_MemObjStringAppend(pObj,"\"",sizeof(char)); break; case '0': /* NUL byte */ PH7_MemObjStringAppend(pObj,"\0",sizeof(char)); break; case 'x': if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1]) ){ int c; /* Hex digit */ c = SyHexToint(zIn[1]) << 4; if( &zIn[2] < zEnd ){ c += SyHexToint(zIn[2]); } /* Output char */ PH7_MemObjStringAppend(pObj,(const char *)&c,sizeof(char)); n += sizeof(char) * 2; }else{ /* Output literal character */ PH7_MemObjStringAppend(pObj,"x",sizeof(char)); } break; case 'o': if( &zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8 ){ /* Octal digit stream */ int c; c = 0; zIn++; for( zPtr = zIn ; zPtr < &zIn[3*sizeof(char)] ; zPtr++ ){ if( zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7 ){ break; } c = c * 8 + (zPtr[0] - '0'); } if ( c > 0 ){ PH7_MemObjStringAppend(pObj,(const char *)&c,sizeof(char)); } n = (sxu32)(zPtr-zIn); }else{ /* Output literal character */ PH7_MemObjStringAppend(pObj,"o",sizeof(char)); } break; default: /* Output without a slash */ PH7_MemObjStringAppend(pObj,zIn,sizeof(char)); break; } /* Advance the stream cursor */ zIn += n; continue; } if( zIn[0] == '{' ){ /* Curly syntax */ const char *zExpr; sxi32 iNest = 1; zIn++; zExpr = zIn; /* Synchronize with the next closing curly braces */ while( zIn < zEnd ){ if( zIn[0] == '{' ){ /* Increment nesting level */ iNest++; }else if(zIn[0] == '}' ){ /* Decrement nesting level */ iNest--; if( iNest <= 0 ){ break; } } zIn++; } /* Process the expression */ rc = GenStateProcessStringExpression(&(*pGen),pGen->pIn->nLine,zExpr,zIn); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( rc != SXERR_EMPTY ){ ++iCons; } if( zIn < zEnd ){ /* Jump the trailing curly */ zIn++; } }else{ /* Simple syntax */ const char *zExpr = zIn; /* Assemble variable name */ for(;;){ /* Jump leading dollars */ while( zIn < zEnd && zIn[0] == '$' ){ zIn++; } for(;;){ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_' ) ){ zIn++; } if((unsigned char)zIn[0] >= 0xc0 ){ /* UTF-8 stream */ zIn++; while( zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){ zIn++; } continue; } break; } if( zIn >= zEnd ){ break; } if( zIn[0] == '[' ){ sxi32 iSquare = 1; zIn++; while( zIn < zEnd ){ if( zIn[0] == '[' ){ iSquare++; }else if (zIn[0] == ']' ){ iSquare--; if( iSquare <= 0 ){ break; } } zIn++; } if( zIn < zEnd ){ zIn++; } break; }else if(zIn[0] == '{' ){ sxi32 iCurly = 1; zIn++; while( zIn < zEnd ){ if( zIn[0] == '{' ){ iCurly++; }else if (zIn[0] == '}' ){ iCurly--; if( iCurly <= 0 ){ break; } } zIn++; } if( zIn < zEnd ){ zIn++; } break; }else if( zIn[0] == '-' && &zIn[1] < zEnd && zIn[1] == '>' ){ /* Member access operator '->' */ zIn += 2; }else if(zIn[0] == ':' && &zIn[1] < zEnd && zIn[1] == ':'){ /* Static member access operator '::' */ zIn += 2; }else{ break; } } /* Process the expression */ rc = GenStateProcessStringExpression(&(*pGen),pGen->pIn->nLine,zExpr,zIn); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( rc != SXERR_EMPTY ){ ++iCons; } } /* Invalidate the previously used constant */ pObj = 0; }/*for(;;)*/ if( iCons > 1 ){ /* Concatenate all compiled constants */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_CAT,iCons,0,0,0); } /* Node successfully compiled */ return SXRET_OK; } /* * Compile a double quoted string. * See the block-comment above for more information. */ PH7_PRIVATE sxi32 PH7_CompileString(ph7_gen_state *pGen,sxi32 iCompileFlag) { sxi32 rc; rc = GenStateCompileString(&(*pGen)); SXUNUSED(iCompileFlag); /* cc warning */ /* Compilation result */ return rc; } /* * Compile a Heredoc string. * See the block-comment above for more information. */ static sxi32 PH7_CompileHereDoc(ph7_gen_state *pGen,sxi32 iCompileFlag) { sxi32 rc; rc = GenStateCompileString(&(*pGen)); SXUNUSED(iCompileFlag); /* cc warning */ /* Compilation result */ return SXRET_OK; } /* * Compile an array entry whether it is a key or a value. * Notes on array entries. * According to the PHP language reference manual * An array can be created by the array() language construct. * It takes as parameters any number of comma-separated key => value pairs. * array( key => value * , ... * ) * A key may be either an integer or a string. If a key is the standard representation * of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while * "08" will be interpreted as "08"). Floats in key are truncated to integer. * The indexed and associative array types are the same type in PHP, which can both * contain integer and string indices. * A value can be any PHP type. * If a key is not specified for a value, the maximum of the integer indices is taken * and the new key will be that value plus 1. If a key that already has an assigned value * is specified, that value will be overwritten. */ static sxi32 GenStateCompileArrayEntry( ph7_gen_state *pGen, /* Code generator state */ SyToken *pIn, /* Token stream */ SyToken *pEnd, /* End of the token stream */ sxi32 iFlags, /* Compilation flags */ sxi32 (*xValidator)(ph7_gen_state *,ph7_expr_node *) /* Expression tree validator callback */ ) { SyToken *pTmpIn,*pTmpEnd; sxi32 rc; /* Swap token stream */ SWAP_DELIMITER(pGen,pIn,pEnd); /* Compile the expression*/ rc = PH7_CompileExpr(&(*pGen),iFlags,xValidator); /* Restore token stream */ RE_SWAP_DELIMITER(pGen); return rc; } /* * Expression tree validator callback for the 'array' language construct. * Return SXRET_OK if the tree is valid. Any other return value indicates * an invalid expression tree and this function will generate the appropriate * error message. * See the routine responible of compiling the array language construct * for more inforation. */ static sxi32 GenStateArrayNodeValidator(ph7_gen_state *pGen,ph7_expr_node *pRoot) { sxi32 rc = SXRET_OK; if( pRoot->pOp ){ if( pRoot->pOp->iOp != EXPR_OP_SUBSCRIPT /* $a[] */ && pRoot->pOp->iOp != EXPR_OP_FUNC_CALL /* function() [Symisc extension: i.e: array(&foo())] */ && pRoot->pOp->iOp != EXPR_OP_ARROW /* -> */ && pRoot->pOp->iOp != EXPR_OP_DC /* :: */){ /* Unexpected expression */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pRoot->pStart? pRoot->pStart->nLine : 0, "array(): Expecting a variable/array member/function call after reference operator '&'"); if( rc != SXERR_ABORT ){ rc = SXERR_INVALID; } } }else if( pRoot->xCode != PH7_CompileVariable ){ /* Unexpected expression */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pRoot->pStart? pRoot->pStart->nLine : 0, "array(): Expecting a variable after reference operator '&'"); if( rc != SXERR_ABORT ){ rc = SXERR_INVALID; } } return rc; } /* * Compile the 'array' language construct. * According to the PHP language reference manual * An array in PHP is actually an ordered map. A map is a type that associates * values to keys. This type is optimized for several different uses; it can * be treated as an array, list (vector), hash table (an implementation of a map) * dictionary, collection, stack, queue, and probably more. As array values can be * other arrays, trees and multidimensional arrays are also possible. */ PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen,sxi32 iCompileFlag) { sxi32 (*xValidator)(ph7_gen_state *,ph7_expr_node *); /* Expression tree validator callback */ SyToken *pKey,*pCur; sxi32 iEmitRef = 0; sxi32 nPair = 0; sxi32 iNest; sxi32 rc; /* Jump the 'array' keyword,the leading left parenthesis and the trailing parenthesis. */ pGen->pIn += 2; pGen->pEnd--; xValidator = 0; SXUNUSED(iCompileFlag); /* cc warning */ for(;;){ /* Jump leading commas */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA) ){ pGen->pIn++; } pCur = pGen->pIn; if( SXRET_OK != PH7_GetNextExpr(pGen->pIn,pGen->pEnd,&pGen->pIn) ){ /* No more entry to process */ break; } if( pCur >= pGen->pIn ){ continue; } /* Compile the key if available */ pKey = pCur; iNest = 0; while( pCur < pGen->pIn ){ if( (pCur->nType & PH7_TK_ARRAY_OP) && iNest <= 0 ){ break; } if( pCur->nType & PH7_TK_LPAREN /*'('*/ ){ iNest++; }else if( pCur->nType & PH7_TK_RPAREN /*')'*/ ){ /* Don't worry about mismatched parenthesis here,the expression * parser will shortly detect any syntax error. */ iNest--; } pCur++; } rc = SXERR_EMPTY; if( pCur < pGen->pIn ){ if( &pCur[1] >= pGen->pIn ){ /* Missing value */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pCur->nLine,"array(): Missing entry value"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXRET_OK; } /* Compile the expression holding the key */ rc = GenStateCompileArrayEntry(&(*pGen),pKey,pCur, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } pCur++; /* Jump the '=>' operator */ }else if( pKey == pCur ){ /* Key is omitted,emit a warning */ PH7_GenCompileError(&(*pGen),E_WARNING,pCur->nLine,"array(): Missing entry key"); pCur++; /* Jump the '=>' operator */ }else{ /* Reset back the cursor and point to the entry value */ pCur = pKey; } if( rc == SXERR_EMPTY ){ /* No available key,load NULL */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0 /* nil index */,0,0); } if( pCur->nType & PH7_TK_AMPER /*'&'*/){ /* Insertion by reference, [i.e: $a = array(&$x);] */ xValidator = GenStateArrayNodeValidator; /* Only variable are allowed */ iEmitRef = 1; pCur++; /* Jump the '&' token */ if( pCur >= pGen->pIn ){ /* Missing value */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pCur->nLine,"array(): Missing referenced variable"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXRET_OK; } } /* Compile indice value */ rc = GenStateCompileArrayEntry(&(*pGen),pCur,pGen->pIn,EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,xValidator); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( iEmitRef ){ /* Emit the load reference instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOAD_REF,0,0,0,0); } xValidator = 0; iEmitRef = 0; nPair++; } /* Emit the load map instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOAD_MAP,nPair * 2,0,0,0); /* Node successfully compiled */ return SXRET_OK; } /* * Expression tree validator callback for the 'list' language construct. * Return SXRET_OK if the tree is valid. Any other return value indicates * an invalid expression tree and this function will generate the appropriate * error message. * See the routine responible of compiling the list language construct * for more inforation. */ static sxi32 GenStateListNodeValidator(ph7_gen_state *pGen,ph7_expr_node *pRoot) { sxi32 rc = SXRET_OK; if( pRoot->pOp ){ if( pRoot->pOp->iOp != EXPR_OP_SUBSCRIPT /* $a[] */ && pRoot->pOp->iOp != EXPR_OP_ARROW /* -> */ && pRoot->pOp->iOp != EXPR_OP_DC /* :: */ ){ /* Unexpected expression */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pRoot->pStart? pRoot->pStart->nLine : 0, "list(): Expecting a variable not an expression"); if( rc != SXERR_ABORT ){ rc = SXERR_INVALID; } } }else if( pRoot->xCode != PH7_CompileVariable ){ /* Unexpected expression */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pRoot->pStart? pRoot->pStart->nLine : 0, "list(): Expecting a variable not an expression"); if( rc != SXERR_ABORT ){ rc = SXERR_INVALID; } } return rc; } /* * Compile the 'list' language construct. * According to the PHP language reference * list(): Assign variables as if they were an array. * list() is used to assign a list of variables in one operation. * Description * array list (mixed $varname [, mixed $... ] ) * Like array(), this is not really a function, but a language construct. * list() is used to assign a list of variables in one operation. * Parameters * $varname: A variable. * Return Values * The assigned array. */ PH7_PRIVATE sxi32 PH7_CompileList(ph7_gen_state *pGen,sxi32 iCompileFlag) { SyToken *pNext; sxi32 nExpr; sxi32 rc; nExpr = 0; /* Jump the 'list' keyword,the leading left parenthesis and the trailing parenthesis */ pGen->pIn += 2; pGen->pEnd--; SXUNUSED(iCompileFlag); /* cc warning */ while( SXRET_OK == PH7_GetNextExpr(pGen->pIn,pGen->pEnd,&pNext) ){ if( pGen->pIn < pNext ){ /* Compile the expression holding the variable */ rc = GenStateCompileArrayEntry(&(*pGen),pGen->pIn,pNext,EXPR_FLAG_LOAD_IDX_STORE,GenStateListNodeValidator); if( rc != SXRET_OK ){ /* Do not bother compiling this expression, it's broken anyway */ return SXRET_OK; } }else{ /* Empty entry,load NULL */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0/* NULL index */,0,0); } nExpr++; /* Advance the stream cursor */ pGen->pIn = &pNext[1]; } /* Emit the LOAD_LIST instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOAD_LIST,nExpr,0,0,0); /* Node successfully compiled */ return SXRET_OK; } /* Forward declaration */ static sxi32 GenStateCompileFunc(ph7_gen_state *pGen,SyString *pName,sxi32 iFlags,int bHandleClosure,ph7_vm_func **ppFunc); /* * Compile an annoynmous function or a closure. * According to the PHP language reference * Anonymous functions, also known as closures, allow the creation of functions * which have no specified name. They are most useful as the value of callback * parameters, but they have many other uses. Closures can also be used as * the values of variables; Assigning a closure to a variable uses the same * syntax as any other assignment, including the trailing semicolon: * Example Anonymous function variable assignment example * * Note that the implementation of annoynmous function and closure under * PH7 is completely different from the one used by the zend engine. */ PH7_PRIVATE sxi32 PH7_CompileAnnonFunc(ph7_gen_state *pGen,sxi32 iCompileFlag) { ph7_vm_func *pAnnonFunc; /* Annonymous function body */ char zName[512]; /* Unique lambda name */ static int iCnt = 1; /* There is no worry about thread-safety here,because only * one thread is allowed to compile the script. */ ph7_value *pObj; SyString sName; sxu32 nIdx; sxu32 nLen; sxi32 rc; pGen->pIn++; /* Jump the 'function' keyword */ if( pGen->pIn->nType & (PH7_TK_ID|PH7_TK_KEYWORD) ){ pGen->pIn++; } /* Reserve a constant for the lambda */ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,1,"Fatal, PH7 engine is running out of memory"); SXUNUSED(iCompileFlag); /* cc warning */ return SXERR_ABORT; } /* Generate a unique name */ nLen = SyBufferFormat(zName,sizeof(zName),"[lambda_%d]",iCnt++); /* Make sure the generated name is unique */ while( SyHashGet(&pGen->pVm->hFunction,zName,nLen) != 0 && nLen < sizeof(zName) - 2 ){ nLen = SyBufferFormat(zName,sizeof(zName),"[lambda_%d]",iCnt++); } SyStringInitFromBuf(&sName,zName,nLen); PH7_MemObjInitFromString(pGen->pVm,pObj,&sName); /* Compile the lambda body */ rc = GenStateCompileFunc(&(*pGen),&sName,0,TRUE,&pAnnonFunc); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( pAnnonFunc->iFlags & VM_FUNC_CLOSURE ){ /* Emit the load closure instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOAD_CLOSURE,0,0,pAnnonFunc,0); }else{ /* Emit the load constant instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); } /* Node successfully compiled */ return SXRET_OK; } /* * Compile a backtick quoted string. */ static sxi32 PH7_CompileBacktic(ph7_gen_state *pGen,sxi32 iCompileFlag) { /* TICKET 1433-40: This construct is disabled in the current release of the PH7 engine. * If you want this feature,please contact symisc systems via contact@symisc.net */ PH7_GenCompileError(&(*pGen),E_NOTICE,pGen->pIn->nLine, "Command line invocation is disabled in the current release of the PH7(%s) engine", ph7_lib_version() ); /* Load NULL */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0,0,0); SXUNUSED(iCompileFlag); /* cc warning */ /* Node successfully compiled */ return SXRET_OK; } /* * Compile a function [i.e: die(),exit(),include(),...] which is a langauge * construct. */ PH7_PRIVATE sxi32 PH7_CompileLangConstruct(ph7_gen_state *pGen,sxi32 iCompileFlag) { SyString *pName; sxu32 nKeyID; sxi32 rc; /* Name of the language construct [i.e: echo,die...]*/ pName = &pGen->pIn->sData; nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData); pGen->pIn++; /* Jump the language construct keyword */ if( nKeyID == PH7_TKWRD_ECHO ){ SyToken *pTmp,*pNext = 0; /* Compile arguments one after one */ pTmp = pGen->pEnd; /* Symisc eXtension to the PHP programming language: * 'echo' can be used in the context of a function which * mean that the following expression is valid: * fopen('file.txt','r') or echo "IO error"; */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,1 /* Boolean true index */,0,0); while( SXRET_OK == PH7_GetNextExpr(pGen->pIn,pTmp,&pNext) ){ if( pGen->pIn < pNext ){ pGen->pEnd = pNext; rc = PH7_CompileExpr(&(*pGen),EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( rc != SXERR_EMPTY ){ /* Ticket 1433-008: Optimization #1: Consume input directly * without the overhead of a function call. * This is a very powerful optimization that improve * performance greatly. */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_CONSUME,1,0,0,0); } } /* Jump trailing commas */ while( pNext < pTmp && (pNext->nType & PH7_TK_COMMA) ){ pNext++; } pGen->pIn = pNext; } /* Restore token stream */ pGen->pEnd = pTmp; }else{ sxi32 nArg = 0; sxu32 nIdx = 0; rc = PH7_CompileExpr(&(*pGen),EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if(rc != SXERR_EMPTY ){ nArg = 1; } if( SXRET_OK != GenStateFindLiteral(&(*pGen),pName,&nIdx) ){ ph7_value *pObj; /* Emit the call instruction */ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,1,"Fatal, PH7 engine is running out of memory"); SXUNUSED(iCompileFlag); /* cc warning */ return SXERR_ABORT; } PH7_MemObjInitFromString(pGen->pVm,pObj,pName); /* Install in the literal table */ GenStateInstallLiteral(&(*pGen),pObj,nIdx); } /* Emit the call instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); PH7_VmEmitInstr(pGen->pVm,PH7_OP_CALL,nArg,0,0,0); } /* Node successfully compiled */ return SXRET_OK; } /* * Compile a node holding a variable declaration. * According to the PHP language reference * Variables in PHP are represented by a dollar sign followed by the name of the variable. * The variable name is case-sensitive. * Variable names follow the same rules as other labels in PHP. A valid variable name starts * with a letter or underscore, followed by any number of letters, numbers, or underscores. * As a regular expression, it would be expressed thus: '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' * Note: For our purposes here, a letter is a-z, A-Z, and the bytes from 127 through 255 (0x7f-0xff). * Note: $this is a special variable that can't be assigned. * By default, variables are always assigned by value. That is to say, when you assign an expression * to a variable, the entire value of the original expression is copied into the destination variable. * This means, for instance, that after assigning one variable's value to another, changing one of those * variables will have no effect on the other. For more information on this kind of assignment, see * the chapter on Expressions. * PHP also offers another way to assign values to variables: assign by reference. This means that * the new variable simply references (in other words, "becomes an alias for" or "points to") the original * variable. Changes to the new variable affect the original, and vice versa. * To assign by reference, simply prepend an ampersand (&) to the beginning of the variable which * is being assigned (the source variable). */ PH7_PRIVATE sxi32 PH7_CompileVariable(ph7_gen_state *pGen,sxi32 iCompileFlag) { sxu32 nLine = pGen->pIn->nLine; sxi32 iVv; sxi32 iP1; void *p3; sxi32 rc; iVv = -1; /* Variable variable counter */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_DOLLAR) ){ pGen->pIn++; iVv++; } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_ID|PH7_TK_KEYWORD|PH7_TK_OCB/*'{'*/)) == 0 ){ /* Invalid variable name */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Invalid variable name"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } p3 = 0; if( pGen->pIn->nType & PH7_TK_OCB/*'{'*/ ){ /* Dynamic variable creation */ pGen->pIn++; /* Jump the open curly */ pGen->pEnd--; /* Ignore the trailing curly */ if( pGen->pIn >= pGen->pEnd ){ /* Empty expression */ PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Invalid variable name"); return SXRET_OK; } /* Compile the expression holding the variable name */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if( rc == SXERR_EMPTY ){ PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Missing variable name"); return SXRET_OK; } }else{ SyHashEntry *pEntry; SyString *pName; char *zName = 0; /* Extract variable name */ pName = &pGen->pIn->sData; /* Advance the stream cursor */ pGen->pIn++; pEntry = SyHashGet(&pGen->hVar,(const void *)pName->zString,pName->nByte); if( pEntry == 0 ){ /* Duplicate name */ zName = SyMemBackendStrDup(&pGen->pVm->sAllocator,pName->zString,pName->nByte); if( zName == 0 ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } /* Install in the hashtable */ SyHashInsert(&pGen->hVar,zName,pName->nByte,zName); }else{ /* Name already available */ zName = (char *)pEntry->pUserData; } p3 = (void *)zName; } iP1 = 0; if( iCompileFlag & EXPR_FLAG_RDONLY_LOAD ){ if( (iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0 ){ /* Read-only load.In other words do not create the variable if inexistant */ iP1 = 1; } } /* Emit the load instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOAD,iP1,0,p3,0); while( iVv > 0 ){ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOAD,iP1,0,0,0); iVv--; } /* Node successfully compiled */ return SXRET_OK; } /* * Load a literal. */ static sxi32 GenStateLoadLiteral(ph7_gen_state *pGen) { SyToken *pToken = pGen->pIn; ph7_value *pObj; SyString *pStr; sxu32 nIdx; /* Extract token value */ pStr = &pToken->sData; /* Deal with the reserved literals [i.e: null,false,true,...] first */ if( pStr->nByte == sizeof("NULL") - 1 ){ if( SyStrnicmp(pStr->zString,"null",sizeof("NULL")-1) == 0 ){ /* NULL constant are always indexed at 0 */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0,0,0); return SXRET_OK; }else if( SyStrnicmp(pStr->zString,"true",sizeof("TRUE")-1) == 0 ){ /* TRUE constant are always indexed at 1 */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,1,0,0); return SXRET_OK; } }else if (pStr->nByte == sizeof("FALSE") - 1 && SyStrnicmp(pStr->zString,"false",sizeof("FALSE")-1) == 0 ){ /* FALSE constant are always indexed at 2 */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,2,0,0); return SXRET_OK; }else if(pStr->nByte == sizeof("__LINE__") - 1 && SyMemcmp(pStr->zString,"__LINE__",sizeof("__LINE__")-1) == 0 ){ /* TICKET 1433-004: __LINE__ constant must be resolved at compile time,not run time */ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(pGen,E_ERROR,pToken->nLine,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } PH7_MemObjInitFromInt(pGen->pVm,pObj,pToken->nLine); /* Emit the load constant instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); return SXRET_OK; }else if( (pStr->nByte == sizeof("__FUNCTION__") - 1 && SyMemcmp(pStr->zString,"__FUNCTION__",sizeof("__FUNCTION__")-1) == 0) || (pStr->nByte == sizeof("__METHOD__") - 1 && SyMemcmp(pStr->zString,"__METHOD__",sizeof("__METHOD__")-1) == 0) ){ GenBlock *pBlock = pGen->pCurrent; /* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time,not run time */ while( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0 ){ /* Point to the upper block */ pBlock = pBlock->pParent; } if( pBlock == 0 ){ /* Called in the global scope,load NULL */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0,0,0); }else{ /* Extract the target function/method */ ph7_vm_func *pFunc = (ph7_vm_func *)pBlock->pUserData; if( pStr->zString[2] == 'M' /* METHOD */ && (pFunc->iFlags & VM_FUNC_CLASS_METHOD) == 0 ){ /* Not a class method,Load null */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,0,0,0); }else{ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(pGen,E_ERROR,pToken->nLine,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } PH7_MemObjInitFromString(pGen->pVm,pObj,&pFunc->sName); /* Emit the load constant instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nIdx,0,0); } } return SXRET_OK; } /* Query literal table */ if( SXRET_OK != GenStateFindLiteral(&(*pGen),&pToken->sData,&nIdx) ){ ph7_value *pObj; /* Unknown literal,install it in the literal table */ pObj = PH7_ReserveConstObj(pGen->pVm,&nIdx); if( pObj == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,1,"PH7 engine is running out of memory"); return SXERR_ABORT; } PH7_MemObjInitFromString(pGen->pVm,pObj,&pToken->sData); GenStateInstallLiteral(&(*pGen),pObj,nIdx); } /* Emit the load constant instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,1,nIdx,0,0); return SXRET_OK; } /* * Resolve a namespace path or simply load a literal: * As of this version namespace support is disabled. If you need * a working version that implement namespace,please contact * symisc systems via contact@symisc.net */ static sxi32 GenStateResolveNamespaceLiteral(ph7_gen_state *pGen) { int emit = 0; sxi32 rc; while( pGen->pIn < &pGen->pEnd[-1] ){ /* Emit a warning */ if( !emit ){ PH7_GenCompileError(&(*pGen),E_WARNING,pGen->pIn->nLine, "Namespace support is disabled in the current release of the PH7(%s) engine", ph7_lib_version() ); emit = 1; } pGen->pIn++; /* Ignore the token */ } /* Load literal */ rc = GenStateLoadLiteral(&(*pGen)); return rc; } /* * Compile a literal which is an identifier(name) for a simple value. */ PH7_PRIVATE sxi32 PH7_CompileLiteral(ph7_gen_state *pGen,sxi32 iCompileFlag) { sxi32 rc; rc = GenStateResolveNamespaceLiteral(&(*pGen)); if( rc != SXRET_OK ){ SXUNUSED(iCompileFlag); /* cc warning */ return rc; } /* Node successfully compiled */ return SXRET_OK; } /* * Recover from a compile-time error. In other words synchronize * the token stream cursor with the first semi-colon seen. */ static sxi32 PH7_ErrorRecover(ph7_gen_state *pGen) { /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI /*';'*/) == 0){ pGen->pIn++; } return SXRET_OK; } /* * Check if the given identifier name is reserved or not. * Return TRUE if reserved.FALSE otherwise. */ static int GenStateIsReservedConstant(SyString *pName) { if( pName->nByte == sizeof("null") - 1 ){ if( SyStrnicmp(pName->zString,"null",sizeof("null")-1) == 0 ){ return TRUE; }else if( SyStrnicmp(pName->zString,"true",sizeof("true")-1) == 0 ){ return TRUE; } }else if( pName->nByte == sizeof("false") - 1 ){ if( SyStrnicmp(pName->zString,"false",sizeof("false")-1) == 0 ){ return TRUE; } } /* Not a reserved constant */ return FALSE; } /* * Compile the 'const' statement. * According to the PHP language reference * A constant is an identifier (name) for a simple value. As the name suggests, that value * cannot change during the execution of the script (except for magic constants, which aren't actually constants). * A constant is case-sensitive by default. By convention, constant identifiers are always uppercase. * The name of a constant follows the same rules as any label in PHP. A valid constant name starts * with a letter or underscore, followed by any number of letters, numbers, or underscores. * As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]* * Syntax * You can define a constant by using the define()-function or by using the const keyword outside * a class definition. Once a constant is defined, it can never be changed or undefined. * You can get the value of a constant by simply specifying its name. Unlike with variables * you should not prepend a constant with a $. You can also use the function constant() to read * a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants() * to get a list of all defined constants. * * Symisc eXtension. * PH7 allow any complex expression to be associated with the constant while the zend engine * would allow only simple scalar value. * Example * const HELLO = "Welcome "." guest ".rand_str(3); //Valid under PH7/Generate error using the zend engine * Refer to the official documentation for more information on this feature. */ static sxi32 PH7_CompileConstant(ph7_gen_state *pGen) { SySet *pConsCode,*pInstrContainer; sxu32 nLine = pGen->pIn->nLine; SyString *pName; sxi32 rc; pGen->pIn++; /* Jump the 'const' keyword */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SSTR|PH7_TK_DSTR|PH7_TK_ID|PH7_TK_KEYWORD)) == 0 ){ /* Invalid constant name */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"const: Invalid constant name"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Peek constant name */ pName = &pGen->pIn->sData; /* Make sure the constant name isn't reserved */ if( GenStateIsReservedConstant(pName) ){ /* Reserved constant */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"const: Cannot redeclare a reserved constant '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } pGen->pIn++; if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_EQUAL /* '=' */) == 0 ){ /* Invalid statement*/ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"const: Expected '=' after constant name"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } pGen->pIn++; /*Jump the equal sign */ /* Allocate a new constant value container */ pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator,sizeof(SySet)); if( pConsCode == 0 ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } SySetInit(pConsCode,&pGen->pVm->sAllocator,sizeof(VmInstr)); /* Swap bytecode container */ pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); PH7_VmSetByteCodeContainer(pGen->pVm,pConsCode); /* Compile constant value */ rc = PH7_CompileExpr(&(*pGen),0,0); /* Emit the done instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,(rc != SXERR_EMPTY ? 1 : 0),0,0,0); PH7_VmSetByteCodeContainer(pGen->pVm,pInstrContainer); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } SySetSetUserData(pConsCode,pGen->pVm); /* Register the constant */ rc = PH7_VmRegisterConstant(pGen->pVm,pName,PH7_VmExpandConstantValue,pConsCode); if( rc != SXRET_OK ){ SySetRelease(pConsCode); SyMemBackendPoolFree(&pGen->pVm->sAllocator,pConsCode); } return SXRET_OK; Synchronize: /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */ while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* * Compile the 'continue' statement. * According to the PHP language reference * continue is used within looping structures to skip the rest of the current loop iteration * and continue execution at the condition evaluation and then the beginning of the next * iteration. * Note: Note that in PHP the switch statement is considered a looping structure for * the purposes of continue. * continue accepts an optional numeric argument which tells it how many levels * of enclosing loops it should skip to the end of. * Note: * continue 0; and continue 1; is the same as running continue;. */ static sxi32 PH7_CompileContinue(ph7_gen_state *pGen) { GenBlock *pLoop; /* Target loop */ sxi32 iLevel; /* How many nesting loop to skip */ sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; iLevel = 0; /* Jump the 'continue' keyword */ pGen->pIn++; if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_NUM) ){ /* optional numeric argument which tells us how many levels * of enclosing loops we should skip to the end of. */ iLevel = (sxi32)PH7_TokenValueToInt64(&pGen->pIn->sData); if( iLevel < 2 ){ iLevel = 0; } pGen->pIn++; /* Jump the optional numeric argument */ } /* Point to the target loop */ pLoop = GenStateFetchBlock(pGen->pCurrent,GEN_BLOCK_LOOP,iLevel); if( pLoop == 0 ){ /* Illegal continue */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"A 'continue' statement may only be used within a loop or switch"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } }else{ sxu32 nInstrIdx = 0; if( pLoop->iFlags & GEN_BLOCK_SWITCH ){ /* According to the PHP language reference manual * Note that unlike some other languages, the continue statement applies to switch * and acts similar to break. If you have a switch inside a loop and wish to continue * to the next iteration of the outer loop, use continue 2. */ rc = PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,0,0,&nInstrIdx); if( rc == SXRET_OK ){ GenStateNewJumpFixup(pLoop,PH7_OP_JMP,nInstrIdx); } }else{ /* Emit the unconditional jump to the beginning of the target loop */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,pLoop->nFirstInstr,0,&nInstrIdx); if( pLoop->bPostContinue == TRUE ){ JumpFixup sJumpFix; /* Post-continue */ sJumpFix.nJumpType = PH7_OP_JMP; sJumpFix.nInstrIdx = nInstrIdx; SySetPut(&pLoop->aPostContFix,(const void *)&sJumpFix); } } } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ /* Not so fatal,emit a warning only */ PH7_GenCompileError(&(*pGen),E_WARNING,pGen->pIn->nLine,"Expected semi-colon ';' after 'continue' statement"); } /* Statement successfully compiled */ return SXRET_OK; } /* * Compile the 'break' statement. * According to the PHP language reference * break ends execution of the current for, foreach, while, do-while or switch * structure. * break accepts an optional numeric argument which tells it how many nested * enclosing structures are to be broken out of. */ static sxi32 PH7_CompileBreak(ph7_gen_state *pGen) { GenBlock *pLoop; /* Target loop */ sxi32 iLevel; /* How many nesting loop to skip */ sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; iLevel = 0; /* Jump the 'break' keyword */ pGen->pIn++; if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_NUM) ){ /* optional numeric argument which tells us how many levels * of enclosing loops we should skip to the end of. */ iLevel = (sxi32)PH7_TokenValueToInt64(&pGen->pIn->sData); if( iLevel < 2 ){ iLevel = 0; } pGen->pIn++; /* Jump the optional numeric argument */ } /* Extract the target loop */ pLoop = GenStateFetchBlock(pGen->pCurrent,GEN_BLOCK_LOOP,iLevel); if( pLoop == 0 ){ /* Illegal break */ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"A 'break' statement may only be used within a loop or switch"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } }else{ sxu32 nInstrIdx; rc = PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,0,0,&nInstrIdx); if( rc == SXRET_OK ){ /* Fix the jump later when the jump destination is resolved */ GenStateNewJumpFixup(pLoop,PH7_OP_JMP,nInstrIdx); } } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ /* Not so fatal,emit a warning only */ PH7_GenCompileError(&(*pGen),E_WARNING,pGen->pIn->nLine,"Expected semi-colon ';' after 'break' statement"); } /* Statement successfully compiled */ return SXRET_OK; } /* * Compile or record a label. * A label is a target point that is specified by an identifier followed by a colon. * Example * goto LABEL; * echo 'Foo'; * LABEL: * echo 'Bar'; */ static sxi32 PH7_CompileLabel(ph7_gen_state *pGen) { GenBlock *pBlock; Label sLabel; /* Make sure the label does not occur inside a loop or a try{}catch(); block */ pBlock = GenStateFetchBlock(pGen->pCurrent,GEN_BLOCK_LOOP|GEN_BLOCK_EXCEPTION,0); if( pBlock ){ sxi32 rc; rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine, "Label '%z' inside loop or try/catch block is disallowed",&pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } }else{ SyString *pTarget = &pGen->pIn->sData; char *zDup; /* Initialize label fields */ sLabel.nJumpDest = PH7_VmInstrLength(pGen->pVm); /* Duplicate label name */ zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,pTarget->zString,pTarget->nByte); if( zDup == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } SyStringInitFromBuf(&sLabel.sName,zDup,pTarget->nByte); sLabel.bRef = FALSE; sLabel.nLine = pGen->pIn->nLine; pBlock = pGen->pCurrent; while( pBlock ){ if( pBlock->iFlags & (GEN_BLOCK_FUNC|GEN_BLOCK_EXCEPTION) ){ break; } /* Point to the upper block */ pBlock = pBlock->pParent; } if( pBlock ){ sLabel.pFunc = (ph7_vm_func *)pBlock->pUserData; }else{ sLabel.pFunc = 0; } /* Insert in label set */ SySetPut(&pGen->aLabel,(const void *)&sLabel); } pGen->pIn += 2; /* Jump the label name and the semi-colon*/ return SXRET_OK; } /* * Compile the so hated 'goto' statement. * You've probably been taught that gotos are bad, but this sort * of rewriting happens all the time, in fact every time you run * a compiler it has to do this. * According to the PHP language reference manual * The goto operator can be used to jump to another section in the program. * The target point is specified by a label followed by a colon, and the instruction * is given as goto followed by the desired target label. This is not a full unrestricted goto. * The target label must be within the same file and context, meaning that you cannot jump out * of a function or method, nor can you jump into one. You also cannot jump into any sort of loop * or switch structure. You may jump out of these, and a common use is to use a goto in place * of a multi-level break */ static sxi32 PH7_CompileGoto(ph7_gen_state *pGen) { JumpFixup sJump; sxi32 rc; pGen->pIn++; /* Jump the 'goto' keyword */ if( pGen->pIn >= pGen->pEnd ){ /* Missing label */ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"goto: expecting a 'label_name'"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } if( (pGen->pIn->nType & (PH7_TK_KEYWORD|PH7_TK_ID)) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"goto: Invalid label name: '%z'",&pGen->pIn->sData); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } }else{ SyString *pTarget = &pGen->pIn->sData; GenBlock *pBlock; char *zDup; /* Prepare the jump destination */ sJump.nJumpType = PH7_OP_JMP; sJump.nLine = pGen->pIn->nLine; /* Duplicate label name */ zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,pTarget->zString,pTarget->nByte); if( zDup == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } SyStringInitFromBuf(&sJump.sLabel,zDup,pTarget->nByte); pBlock = pGen->pCurrent; while( pBlock ){ if( pBlock->iFlags & (GEN_BLOCK_FUNC|GEN_BLOCK_EXCEPTION) ){ break; } /* Point to the upper block */ pBlock = pBlock->pParent; } if( pBlock && pBlock->iFlags & GEN_BLOCK_EXCEPTION ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"goto inside try/catch block is disallowed"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } } if( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC)){ sJump.pFunc = (ph7_vm_func *)pBlock->pUserData; }else{ sJump.pFunc = 0; } /* Emit the unconditional jump */ if( SXRET_OK == PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,0,0,&sJump.nInstrIdx) ){ SySetPut(&pGen->aGoto,(const void *)&sJump); } } pGen->pIn++; /* Jump the label name */ if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Expected semi-colon ';' after 'goto' statement"); } /* Statement successfully compiled */ return SXRET_OK; } /* * Point to the next PHP chunk that will be processed shortly. * Return SXRET_OK on success. Any other return value indicates * failure. */ static sxi32 GenStateNextChunk(ph7_gen_state *pGen) { ph7_value *pRawObj; /* Raw chunk [i.e: HTML,XML...] */ sxu32 nRawObj; sxu32 nObjIdx; /* Consume raw chunks verbatim without any processing until we get * a PHP block. */ Consume: nRawObj = nObjIdx = 0; while( pGen->pRawIn < pGen->pRawEnd && pGen->pRawIn->nType != PH7_TOKEN_PHP ){ pRawObj = PH7_ReserveConstObj(pGen->pVm,&nObjIdx); if( pRawObj == 0 ){ PH7_GenCompileError(pGen,E_ERROR,1,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } /* Mark as constant and emit the load constant instruction */ PH7_MemObjInitFromString(pGen->pVm,pRawObj,&pGen->pRawIn->sData); PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOADC,0,nObjIdx,0,0); ++nRawObj; pGen->pRawIn++; /* Next chunk */ } if( nRawObj > 0 ){ /* Emit the consume instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_CONSUME,nRawObj,0,0,0); } if( pGen->pRawIn < pGen->pRawEnd ){ SySet *pTokenSet = pGen->pTokenSet; /* Reset the token set */ SySetReset(pTokenSet); /* Tokenize input */ PH7_TokenizePHP(SyStringData(&pGen->pRawIn->sData),SyStringLength(&pGen->pRawIn->sData), pGen->pRawIn->nLine,pTokenSet); /* Point to the fresh token stream */ pGen->pIn = (SyToken *)SySetBasePtr(pTokenSet); pGen->pEnd = &pGen->pIn[SySetUsed(pTokenSet)]; /* Advance the stream cursor */ pGen->pRawIn++; /* TICKET 1433-011 */ if( pGen->pIn < pGen->pEnd && ( pGen->pIn->nType & PH7_TK_EQUAL ) ){ static const sxu32 nKeyID = PH7_TKWRD_ECHO; sxi32 rc; /* Refer to TICKET 1433-009 */ pGen->pIn->nType = PH7_TK_KEYWORD; pGen->pIn->pUserData = SX_INT_TO_PTR(nKeyID); SyStringInitFromBuf(&pGen->pIn->sData,"echo",sizeof("echo")-1); rc = PH7_CompileExpr(pGen,0,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if( rc != SXERR_EMPTY ){ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP,1,0,0,0); } goto Consume; } }else{ /* No more chunks to process */ pGen->pIn = pGen->pEnd; return SXERR_EOF; } return SXRET_OK; } /* * Compile a PHP block. * A block is simply one or more PHP statements and expressions to compile * optionally delimited by braces {}. * Return SXRET_OK on success. Any other return value indicates failure * and this function takes care of generating the appropriate error * message. */ static sxi32 PH7_CompileBlock( ph7_gen_state *pGen, /* Code generator state */ sxi32 nKeywordEnd /* EOF-keyword [i.e: endif;endfor;...]. 0 (zero) otherwise */ ) { sxi32 rc; if( pGen->pIn->nType & PH7_TK_OCB /* '{' */ ){ sxu32 nLine = pGen->pIn->nLine; rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_STD,PH7_VmInstrLength(pGen->pVm),0,0); if( rc != SXRET_OK ){ return SXERR_ABORT; } pGen->pIn++; /* Compile until we hit the closing braces '}' */ for(;;){ if( pGen->pIn >= pGen->pEnd ){ rc = GenStateNextChunk(&(*pGen)); if (rc == SXERR_ABORT ){ return SXERR_ABORT; } if( rc == SXERR_EOF ){ /* No more token to process. Missing closing braces */ PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Missing closing braces '}'"); break; } } if( pGen->pIn->nType & PH7_TK_CCB/*'}'*/ ){ /* Closing braces found,break immediately*/ pGen->pIn++; break; } /* Compile a single statement */ rc = GenStateCompileChunk(&(*pGen),PH7_COMPILE_SINGLE_STMT); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } GenStateLeaveBlock(&(*pGen),0); }else if( (pGen->pIn->nType & PH7_TK_COLON /* ':' */) && nKeywordEnd > 0 ){ pGen->pIn++; rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_STD,PH7_VmInstrLength(pGen->pVm),0,0); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Compile until we hit the EOF-keyword [i.e: endif;endfor;...] */ for(;;){ if( pGen->pIn >= pGen->pEnd ){ rc = GenStateNextChunk(&(*pGen)); if (rc == SXERR_ABORT ){ return SXERR_ABORT; } if( rc == SXERR_EOF || pGen->pIn >= pGen->pEnd ){ /* No more token to process */ if( rc == SXERR_EOF ){ PH7_GenCompileError(&(*pGen),E_WARNING,pGen->pEnd[-1].nLine, "Missing 'endfor;','endwhile;','endswitch;' or 'endforeach;' keyword"); } break; } } if( pGen->pIn->nType & PH7_TK_KEYWORD ){ sxi32 nKwrd; /* Keyword found */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == nKeywordEnd || (nKeywordEnd == PH7_TKWRD_ENDIF && (nKwrd == PH7_TKWRD_ELSE || nKwrd == PH7_TKWRD_ELIF)) ){ /* Delimiter keyword found,break */ if( nKwrd != PH7_TKWRD_ELSE && nKwrd != PH7_TKWRD_ELIF ){ pGen->pIn++; /* endif;endswitch... */ } break; } } /* Compile a single statement */ rc = GenStateCompileChunk(&(*pGen),PH7_COMPILE_SINGLE_STMT); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } GenStateLeaveBlock(&(*pGen),0); }else{ /* Compile a single statement */ rc = GenStateCompileChunk(&(*pGen),PH7_COMPILE_SINGLE_STMT); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Jump trailing semi-colons ';' */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) ){ pGen->pIn++; } return SXRET_OK; } /* * Compile the gentle 'while' statement. * According to the PHP language reference * while loops are the simplest type of loop in PHP.They behave just like their C counterparts. * The basic form of a while statement is: * while (expr) * statement * The meaning of a while statement is simple. It tells PHP to execute the nested statement(s) * repeatedly, as long as the while expression evaluates to TRUE. The value of the expression * is checked each time at the beginning of the loop, so even if this value changes during * the execution of the nested statement(s), execution will not stop until the end of the iteration * (each time PHP runs the statements in the loop is one iteration). Sometimes, if the while * expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once. * Like with the if statement, you can group multiple statements within the same while loop by surrounding * a group of statements with curly braces, or by using the alternate syntax: * while (expr): * statement * endwhile; */ static sxi32 PH7_CompileWhile(ph7_gen_state *pGen) { GenBlock *pWhileBlock = 0; SyToken *pTmp,*pEnd = 0; sxu32 nFalseJump; sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; /* Jump the 'while' keyword */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '(' after 'while' keyword"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Jump the left parenthesis '(' */ pGen->pIn++; /* Create the loop block */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_LOOP,PH7_VmInstrLength(pGen->pVm),0,&pWhileBlock); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Delimit the condition */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_LPAREN /* '(' */,PH7_TK_RPAREN /* ')' */,&pEnd); if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ /* Empty expression */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected expression after 'while' keyword"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } } /* Swap token streams */ pTmp = pGen->pEnd; pGen->pEnd = pEnd; /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ /* Expression handler request an operation abort [i.e: Out-of-memory] */ return SXERR_ABORT; } /* Update token stream */ while(pGen->pIn < pEnd ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Unexpected token '%z'",&pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } pGen->pIn++; } /* Synchronize pointers */ pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; /* Emit the false jump */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JZ,0,0,0,&nFalseJump); /* Save the instruction index so we can fix it later when the jump destination is resolved */ GenStateNewJumpFixup(pWhileBlock,PH7_OP_JZ,nFalseJump); /* Compile the loop body */ rc = PH7_CompileBlock(&(*pGen),PH7_TKWRD_ENDWHILE); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* Emit the unconditional jump to the start of the loop */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,pWhileBlock->nFirstInstr,0,0); /* Fix all jumps now the destination is resolved */ GenStateFixJumps(pWhileBlock,-1,PH7_VmInstrLength(pGen->pVm)); /* Release the loop block */ GenStateLeaveBlock(pGen,0); /* Statement successfully compiled */ return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon ';' so we can avoid * compiling this erroneous block. */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI|PH7_TK_OCB)) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* * Compile the ugly do..while() statement. * According to the PHP language reference * do-while loops are very similar to while loops, except the truth expression is checked * at the end of each iteration instead of in the beginning. The main difference from regular * while loops is that the first iteration of a do-while loop is guaranteed to run * (the truth expression is only checked at the end of the iteration), whereas it may not * necessarily run with a regular while loop (the truth expression is checked at the beginning * of each iteration, if it evaluates to FALSE right from the beginning, the loop execution * would end immediately). * There is just one syntax for do-while loops: * 0); * ?> */ static sxi32 PH7_CompileDoWhile(ph7_gen_state *pGen) { SyToken *pTmp,*pEnd = 0; GenBlock *pDoBlock = 0; sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; /* Jump the 'do' keyword */ pGen->pIn++; /* Create the loop block */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_LOOP,PH7_VmInstrLength(pGen->pVm),0,&pDoBlock); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Deffer 'continue;' jumps until we compile the block */ pDoBlock->bPostContinue = TRUE; rc = PH7_CompileBlock(&(*pGen),0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( pGen->pIn < pGen->pEnd ){ nLine = pGen->pIn->nLine; } if( pGen->pIn >= pGen->pEnd || pGen->pIn->nType != PH7_TK_KEYWORD || SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_WHILE ){ /* Missing 'while' statement */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Missing 'while' statement after 'do' block"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Jump the 'while' keyword */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '(' after 'while' keyword"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Jump the left parenthesis '(' */ pGen->pIn++; /* Delimit the condition */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_LPAREN /* '(' */,PH7_TK_RPAREN /* ')' */,&pEnd); if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ /* Empty expression */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected expression after 'while' keyword"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Fix post-continue jumps now the jump destination is resolved */ if( SySetUsed(&pDoBlock->aPostContFix) > 0 ){ JumpFixup *aPost; VmInstr *pInstr; sxu32 nJumpDest; sxu32 n; aPost = (JumpFixup *)SySetBasePtr(&pDoBlock->aPostContFix); nJumpDest = PH7_VmInstrLength(pGen->pVm); for( n = 0 ; n < SySetUsed(&pDoBlock->aPostContFix) ; ++n ){ pInstr = PH7_VmGetInstr(pGen->pVm,aPost[n].nInstrIdx); if( pInstr ){ /* Fix */ pInstr->iP2 = nJumpDest; } } } /* Swap token streams */ pTmp = pGen->pEnd; pGen->pEnd = pEnd; /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ /* Expression handler request an operation abort [i.e: Out-of-memory] */ return SXERR_ABORT; } /* Update token stream */ while(pGen->pIn < pEnd ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Unexpected token '%z'",&pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } pGen->pIn++; } pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; /* Emit the true jump to the beginning of the loop */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JNZ,0,pDoBlock->nFirstInstr,0,0); /* Fix all jumps now the destination is resolved */ GenStateFixJumps(pDoBlock,-1,PH7_VmInstrLength(pGen->pVm)); /* Release the loop block */ GenStateLeaveBlock(pGen,0); /* Statement successfully compiled */ return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon ';' so we can avoid * compiling this erroneous block. */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI|PH7_TK_OCB)) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* * Compile the complex and powerful 'for' statement. * According to the PHP language reference * for loops are the most complex loops in PHP. They behave like their C counterparts. * The syntax of a for loop is: * for (expr1; expr2; expr3) * statement * The first expression (expr1) is evaluated (executed) once unconditionally at * the beginning of the loop. * In the beginning of each iteration, expr2 is evaluated. If it evaluates to * TRUE, the loop continues and the nested statement(s) are executed. If it evaluates * to FALSE, the execution of the loop ends. * At the end of each iteration, expr3 is evaluated (executed). * Each of the expressions can be empty or contain multiple expressions separated by commas. * In expr2, all expressions separated by a comma are evaluated but the result is taken * from the last part. expr2 being empty means the loop should be run indefinitely * (PHP implicitly considers it as TRUE, like C). This may not be as useless as you might * think, since often you'd want to end the loop using a conditional break statement instead * of using the for truth expression. */ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) { SyToken *pTmp,*pPostStart,*pEnd = 0; GenBlock *pForBlock = 0; sxu32 nFalseJump; sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; /* Jump the 'for' keyword */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '(' after 'for' keyword"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } /* Jump the left parenthesis '(' */ pGen->pIn++; /* Delimit the init-expr;condition;post-expr */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_LPAREN /* '(' */,PH7_TK_RPAREN /* ')' */,&pEnd); if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ /* Empty expression */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"for: Invalid expression"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } /* Synchronize */ pGen->pIn = pEnd; if( pGen->pIn < pGen->pEnd ){ pGen->pIn++; } return SXRET_OK; } /* Swap token streams */ pTmp = pGen->pEnd; pGen->pEnd = pEnd; /* Compile initialization expressions if available */ rc = PH7_CompileExpr(&(*pGen),0,0); /* Pop operand lvalues */ if( rc == SXERR_ABORT ){ /* Expression handler request an operation abort [i.e: Out-of-memory] */ return SXERR_ABORT; }else if( rc != SXERR_EMPTY ){ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP,1,0,0,0); } if( (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "for: Expected ';' after initialization expressions"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } /* Jump the trailing ';' */ pGen->pIn++; /* Create the loop block */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_LOOP,PH7_VmInstrLength(pGen->pVm),0,&pForBlock); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Deffer continue jumps */ pForBlock->bPostContinue = TRUE; /* Compile the condition */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ /* Expression handler request an operation abort [i.e: Out-of-memory] */ return SXERR_ABORT; }else if( rc != SXERR_EMPTY ){ /* Emit the false jump */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JZ,0,0,0,&nFalseJump); /* Save the instruction index so we can fix it later when the jump destination is resolved */ GenStateNewJumpFixup(pForBlock,PH7_OP_JZ,nFalseJump); } if( (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "for: Expected ';' after conditionals expressions"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } /* Jump the trailing ';' */ pGen->pIn++; /* Save the post condition stream */ pPostStart = pGen->pIn; /* Compile the loop body */ pGen->pIn = &pEnd[1]; /* Jump the trailing parenthesis ')' */ pGen->pEnd = pTmp; rc = PH7_CompileBlock(&(*pGen),PH7_TKWRD_ENDFOR); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* Fix post-continue jumps */ if( SySetUsed(&pForBlock->aPostContFix) > 0 ){ JumpFixup *aPost; VmInstr *pInstr; sxu32 nJumpDest; sxu32 n; aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix); nJumpDest = PH7_VmInstrLength(pGen->pVm); for( n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n ){ pInstr = PH7_VmGetInstr(pGen->pVm,aPost[n].nInstrIdx); if( pInstr ){ /* Fix jump */ pInstr->iP2 = nJumpDest; } } } /* compile the post-expressions if available */ while( pPostStart < pEnd && (pPostStart->nType & PH7_TK_SEMI) ){ pPostStart++; } if( pPostStart < pEnd ){ SyToken *pTmpIn,*pTmpEnd; SWAP_DELIMITER(pGen,pPostStart,pEnd); rc = PH7_CompileExpr(&(*pGen),0,0); if( pGen->pIn < pGen->pEnd ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"for: Expected ')' after post-expressions"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } RE_SWAP_DELIMITER(pGen); if( rc == SXERR_ABORT ){ /* Expression handler request an operation abort [i.e: Out-of-memory] */ return SXERR_ABORT; }else if( rc != SXERR_EMPTY){ /* Pop operand lvalue */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP,1,0,0,0); } } /* Emit the unconditional jump to the start of the loop */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,pForBlock->nFirstInstr,0,0); /* Fix all jumps now the destination is resolved */ GenStateFixJumps(pForBlock,-1,PH7_VmInstrLength(pGen->pVm)); /* Release the loop block */ GenStateLeaveBlock(pGen,0); /* Statement successfully compiled */ return SXRET_OK; } /* Expression tree validator callback used by the 'foreach' statement. * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...] * are allowed. */ static sxi32 GenStateForEachNodeValidator(ph7_gen_state *pGen,ph7_expr_node *pRoot) { sxi32 rc = SXRET_OK; /* Assume a valid expression tree */ if( pRoot->xCode != PH7_CompileVariable ){ /* Unexpected expression */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pRoot->pStart? pRoot->pStart->nLine : 0, "foreach: Expecting a variable name"); if( rc != SXERR_ABORT ){ rc = SXERR_INVALID; } } return rc; } /* * Compile the 'foreach' statement. * According to the PHP language reference * The foreach construct simply gives an easy way to iterate over arrays. foreach works * only on arrays (and objects), and will issue an error when you try to use it on a variable * with a different data type or an uninitialized variable. There are two syntaxes; the second * is a minor but useful extension of the first: * foreach (array_expression as $value) * statement * foreach (array_expression as $key => $value) * statement * The first form loops over the array given by array_expression. On each loop, the value * of the current element is assigned to $value and the internal array pointer is advanced * by one (so on the next loop, you'll be looking at the next element). * The second form does the same thing, except that the current element's key will be assigned * to the variable $key on each loop. * Note: * When foreach first starts executing, the internal array pointer is automatically reset to the * first element of the array. This means that you do not need to call reset() before a foreach loop. * Note: * Unless the array is referenced, foreach operates on a copy of the specified array and not the array * itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during * or after the foreach without resetting it. * You can easily modify array's elements by preceding $value with &. This will assign reference instead * of copying the value. */ static sxi32 PH7_CompileForeach(ph7_gen_state *pGen) { SyToken *pCur,*pTmp,*pEnd = 0; GenBlock *pForeachBlock = 0; ph7_foreach_info *pInfo; sxu32 nFalseJump; VmInstr *pInstr; sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; /* Jump the 'foreach' keyword */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"foreach: Expected '('"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Jump the left parenthesis '(' */ pGen->pIn++; /* Create the loop block */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_LOOP,PH7_VmInstrLength(pGen->pVm),0,&pForeachBlock); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Delimit the expression */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_LPAREN /* '(' */,PH7_TK_RPAREN /* ')' */,&pEnd); if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ /* Empty expression */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"foreach: Missing expression"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } /* Synchronize */ pGen->pIn = pEnd; if( pGen->pIn < pGen->pEnd ){ pGen->pIn++; } return SXRET_OK; } /* Compile the array expression */ pCur = pGen->pIn; while( pCur < pEnd ){ if( pCur->nType & PH7_TK_KEYWORD ){ sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData); if( nKeywrd == PH7_TKWRD_AS ){ /* Break with the first 'as' found */ break; } } /* Advance the stream cursor */ pCur++; } if( pCur <= pGen->pIn ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine, "foreach: Missing array/object expression"); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } goto Synchronize; } /* Swap token streams */ pTmp = pGen->pEnd; pGen->pEnd = pCur; rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ /* Expression handler request an operation abort [i.e: Out-of-memory] */ return SXERR_ABORT; } /* Update token stream */ while(pGen->pIn < pCur ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"foreach: Unexpected token '%z'",&pGen->pIn->sData); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } pGen->pIn++; } pCur++; /* Jump the 'as' keyword */ pGen->pIn = pCur; if( pGen->pIn >= pEnd ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"foreach: Missing $key => $value pair"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Create the foreach context */ pInfo = (ph7_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator,sizeof(ph7_foreach_info)); if( pInfo == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Fatal, PH7 engine is running out-of-memory"); return SXERR_ABORT; } /* Zero the structure */ SyZero(pInfo,sizeof(ph7_foreach_info)); /* Initialize structure fields */ SySetInit(&pInfo->aStep,&pGen->pVm->sAllocator,sizeof(ph7_foreach_step *)); /* Check if we have a key field */ while( pCur < pEnd && (pCur->nType & PH7_TK_ARRAY_OP) == 0 ){ pCur++; } if( pCur < pEnd ){ /* Compile the expression holding the key name */ if( pGen->pIn >= pCur ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"foreach: Missing $key"); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } }else{ pGen->pEnd = pCur; rc = PH7_CompileExpr(&(*pGen),0,GenStateForEachNodeValidator); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } pInstr = PH7_VmPopInstr(pGen->pVm); if( pInstr->p3 ){ /* Record key name */ SyStringInitFromBuf(&pInfo->sKey,pInstr->p3,SyStrlen((const char *)pInstr->p3)); } pInfo->iFlags |= PH7_4EACH_STEP_KEY; } pGen->pIn = &pCur[1]; /* Jump the arrow */ } pGen->pEnd = pEnd; if( pGen->pIn >= pEnd ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"foreach: Missing $value"); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } goto Synchronize; } if( pGen->pIn->nType & PH7_TK_AMPER /*'&'*/){ pGen->pIn++; /* Pass by reference */ pInfo->iFlags |= PH7_4EACH_STEP_REF; } /* Compile the expression holding the value name */ rc = PH7_CompileExpr(&(*pGen),0,GenStateForEachNodeValidator); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } pInstr = PH7_VmPopInstr(pGen->pVm); if( pInstr->p3 ){ /* Record value name */ SyStringInitFromBuf(&pInfo->sValue,pInstr->p3,SyStrlen((const char *)pInstr->p3)); } /* Emit the 'FOREACH_INIT' instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_FOREACH_INIT,0,0,pInfo,&nFalseJump); /* Save the instruction index so we can fix it later when the jump destination is resolved */ GenStateNewJumpFixup(pForeachBlock,PH7_OP_FOREACH_INIT,nFalseJump); /* Record the first instruction to execute */ pForeachBlock->nFirstInstr = PH7_VmInstrLength(pGen->pVm); /* Emit the FOREACH_STEP instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_FOREACH_STEP,0,0,pInfo,&nFalseJump); /* Save the instruction index so we can fix it later when the jump destination is resolved */ GenStateNewJumpFixup(pForeachBlock,PH7_OP_FOREACH_STEP,nFalseJump); /* Compile the loop body */ pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; rc = PH7_CompileBlock(&(*pGen),PH7_TKWRD_END4EACH); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } /* Emit the unconditional jump to the start of the loop */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,pForeachBlock->nFirstInstr,0,0); /* Fix all jumps now the destination is resolved */ GenStateFixJumps(pForeachBlock,-1,PH7_VmInstrLength(pGen->pVm)); /* Release the loop block */ GenStateLeaveBlock(pGen,0); /* Statement successfully compiled */ return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon ';' so we can avoid * compiling this erroneous block. */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI|PH7_TK_OCB)) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* * Compile the infamous if/elseif/else if/else statements. * According to the PHP language reference * The if construct is one of the most important features of many languages PHP included. * It allows for conditional execution of code fragments. PHP features an if structure * that is similar to that of C: * if (expr) * statement * else construct: * Often you'd want to execute a statement if a certain condition is met, and a different * statement if the condition is not met. This is what else is for. else extends an if statement * to execute a statement in case the expression in the if statement evaluates to FALSE. * For example, the following code would display a is greater than b if $a is greater than * $b, and a is NOT greater than b otherwise. * The else statement is only executed if the if expression evaluated to FALSE, and if there * were any elseif expressions - only if they evaluated to FALSE as well * elseif * elseif, as its name suggests, is a combination of if and else. Like else, it extends * an if statement to execute a different statement in case the original if expression evaluates * to FALSE. However, unlike else, it will execute that alternative expression only if the elseif * conditional expression evaluates to TRUE. For example, the following code would display a is bigger * than b, a equal to b or a is smaller than b: * $b) { * echo "a is bigger than b"; * } elseif ($a == $b) { * echo "a is equal to b"; * } else { * echo "a is smaller than b"; * } * ?> */ static sxi32 PH7_CompileIf(ph7_gen_state *pGen) { SyToken *pToken,*pTmp,*pEnd = 0; GenBlock *pCondBlock = 0; sxu32 nJumpIdx; sxu32 nKeyID; sxi32 rc; /* Jump the 'if' keyword */ pGen->pIn++; pToken = pGen->pIn; /* Create the conditional block */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_COND,PH7_VmInstrLength(pGen->pVm),0,&pCondBlock); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Process as many [if/else if/elseif/else] blocks as we can */ for(;;){ if( pToken >= pGen->pEnd || (pToken->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ if( pToken >= pGen->pEnd ){ pToken--; } rc = PH7_GenCompileError(pGen,E_ERROR,pToken->nLine,"if/else/elseif: Missing '('"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Jump the left parenthesis '(' */ pToken++; /* Delimit the condition */ PH7_DelimitNestedTokens(pToken,pGen->pEnd,PH7_TK_LPAREN /* '(' */,PH7_TK_RPAREN /* ')' */,&pEnd); if( pToken >= pEnd || (pEnd->nType & PH7_TK_RPAREN) == 0 ){ /* Syntax error */ if( pToken >= pGen->pEnd ){ pToken--; } rc = PH7_GenCompileError(pGen,E_ERROR,pToken->nLine,"if/else/elseif: Missing ')'"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Swap token streams */ SWAP_TOKEN_STREAM(pGen,pToken,pEnd); /* Compile the condition */ rc = PH7_CompileExpr(&(*pGen),0,0); /* Update token stream */ while(pGen->pIn < pEnd ){ PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Unexpected token '%z'",&pGen->pIn->sData); pGen->pIn++; } pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; if( rc == SXERR_ABORT ){ /* Expression handler request an operation abort [i.e: Out-of-memory] */ return SXERR_ABORT; } /* Emit the false jump */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JZ,0,0,0,&nJumpIdx); /* Save the instruction index so we can fix it later when the jump destination is resolved */ GenStateNewJumpFixup(pCondBlock,PH7_OP_JZ,nJumpIdx); /* Compile the body */ rc = PH7_CompileBlock(&(*pGen),PH7_TKWRD_ENDIF); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ){ break; } /* Ensure that the keyword ID is 'else if' or 'else' */ nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData); if( (nKeyID & (PH7_TKWRD_ELSE|PH7_TKWRD_ELIF)) == 0 ){ break; } /* Emit the unconditional jump */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,0,0,&nJumpIdx); /* Save the instruction index so we can fix it later when the jump destination is resolved */ GenStateNewJumpFixup(pCondBlock,PH7_OP_JMP,nJumpIdx); if( nKeyID & PH7_TKWRD_ELSE ){ pToken = &pGen->pIn[1]; if( pToken >= pGen->pEnd || (pToken->nType & PH7_TK_KEYWORD) == 0 || SX_PTR_TO_INT(pToken->pUserData) != PH7_TKWRD_IF ){ break; } pGen->pIn++; /* Jump the 'else' keyword */ } pGen->pIn++; /* Jump the 'elseif/if' keyword */ /* Synchronize cursors */ pToken = pGen->pIn; /* Fix the false jump */ GenStateFixJumps(pCondBlock,PH7_OP_JZ,PH7_VmInstrLength(pGen->pVm)); } /* For(;;) */ /* Fix the false jump */ GenStateFixJumps(pCondBlock,PH7_OP_JZ,PH7_VmInstrLength(pGen->pVm)); if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && (SX_PTR_TO_INT(pGen->pIn->pUserData) & PH7_TKWRD_ELSE) ){ /* Compile the else block */ pGen->pIn++; rc = PH7_CompileBlock(&(*pGen),PH7_TKWRD_ENDIF); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } nJumpIdx = PH7_VmInstrLength(pGen->pVm); /* Fix all unconditional jumps now the destination is resolved */ GenStateFixJumps(pCondBlock,PH7_OP_JMP,nJumpIdx); /* Release the conditional block */ GenStateLeaveBlock(pGen,0); /* Statement successfully compiled */ return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block. */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI|PH7_TK_OCB)) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* * Compile the global construct. * According to the PHP language reference * In PHP global variables must be declared global inside a function if they are going * to be used in that function. * Example #1 Using global * * The above script will output 3. By declaring $a and $b global within the function * all references to either variable will refer to the global version. There is no limit * to the number of global variables that can be manipulated by a function. */ static sxi32 PH7_CompileGlobal(ph7_gen_state *pGen) { SyToken *pTmp,*pNext = 0; sxi32 nExpr; sxi32 rc; /* Jump the 'global' keyword */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_SEMI) ){ /* Nothing to process */ return SXRET_OK; } pTmp = pGen->pEnd; nExpr = 0; while( SXRET_OK == PH7_GetNextExpr(pGen->pIn,pTmp,&pNext) ){ if( pGen->pIn < pNext ){ pGen->pEnd = pNext; if( (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"global: Expected variable name"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } }else{ pGen->pIn++; if( pGen->pIn >= pGen->pEnd ){ /* Emit a warning */ PH7_GenCompileError(&(*pGen),E_WARNING,pGen->pIn[-1].nLine,"global: Empty variable name"); }else{ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if(rc != SXERR_EMPTY ){ nExpr++; } } } } /* Next expression in the stream */ pGen->pIn = pNext; /* Jump trailing commas */ while( pGen->pIn < pTmp && (pGen->pIn->nType & PH7_TK_COMMA) ){ pGen->pIn++; } } /* Restore token stream */ pGen->pEnd = pTmp; if( nExpr > 0 ){ /* Emit the uplink instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_UPLINK,nExpr,0,0,0); } return SXRET_OK; } /* * Compile the return statement. * According to the PHP language reference * If called from within a function, the return() statement immediately ends execution * of the current function, and returns its argument as the value of the function call. * return() will also end the execution of an eval() statement or script file. * If called from the global scope, then execution of the current script file is ended. * If the current script file was include()ed or require()ed, then control is passed back * to the calling file. Furthermore, if the current script file was include()ed, then the value * given to return() will be returned as the value of the include() call. If return() is called * from within the main script file, then script execution end. * Note that since return() is a language construct and not a function, the parentheses * surrounding its arguments are not required. It is common to leave them out, and you actually * should do so as PHP has less work to do in this case. * Note: If no parameter is supplied, then the parentheses must be omitted and NULL will be returned. */ static sxi32 PH7_CompileReturn(ph7_gen_state *pGen) { sxi32 nRet = 0; /* TRUE if there is a return value */ sxi32 rc; /* Jump the 'return' keyword */ pGen->pIn++; if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if(rc != SXERR_EMPTY ){ nRet = 1; } } /* Emit the done instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,nRet,0,0,0); return SXRET_OK; } /* * Compile the die/exit language construct. * The role of these constructs is to terminate execution of the script. * Shutdown functions will always be executed even if exit() is called. */ static sxi32 PH7_CompileHalt(ph7_gen_state *pGen) { sxi32 nExpr = 0; sxi32 rc; /* Jump the die/exit keyword */ pGen->pIn++; if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if(rc != SXERR_EMPTY ){ nExpr = 1; } } /* Emit the HALT instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_HALT,nExpr,0,0,0); return SXRET_OK; } /* * Compile the 'echo' language construct. */ static sxi32 PH7_CompileEcho(ph7_gen_state *pGen) { SyToken *pTmp,*pNext = 0; sxi32 rc; /* Jump the 'echo' keyword */ pGen->pIn++; /* Compile arguments one after one */ pTmp = pGen->pEnd; while( SXRET_OK == PH7_GetNextExpr(pGen->pIn,pTmp,&pNext) ){ if( pGen->pIn < pNext ){ pGen->pEnd = pNext; rc = PH7_CompileExpr(&(*pGen),EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if( rc != SXERR_EMPTY ){ /* Emit the consume instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_CONSUME,1,0,0,0); } } /* Jump trailing commas */ while( pNext < pTmp && (pNext->nType & PH7_TK_COMMA) ){ pNext++; } pGen->pIn = pNext; } /* Restore token stream */ pGen->pEnd = pTmp; return SXRET_OK; } /* * Compile the static statement. * According to the PHP language reference * Another important feature of variable scoping is the static variable. * A static variable exists only in a local function scope, but it does not lose its value * when program execution leaves this scope. * Static variables also provide one way to deal with recursive functions. * Symisc eXtension. * PH7 allow any complex expression to be associated with the static variable while * the zend engine would allow only simple scalar value. * Example * static $myVar = "Welcome "." guest ".rand_str(3); //Valid under PH7/Generate error using the zend engine * Refer to the official documentation for more information on this feature. */ static sxi32 PH7_CompileStatic(ph7_gen_state *pGen) { ph7_vm_func_static_var sStatic; /* Structure describing the static variable */ ph7_vm_func *pFunc; /* Enclosing function */ GenBlock *pBlock; SyString *pName; char *zDup; sxu32 nLine; sxi32 rc; /* Jump the static keyword */ nLine = pGen->pIn->nLine; pGen->pIn++; /* Extract the enclosing function if any */ pBlock = pGen->pCurrent; while( pBlock ){ if( pBlock->iFlags & GEN_BLOCK_FUNC){ break; } /* Point to the upper block */ pBlock = pBlock->pParent; } if( pBlock == 0 ){ /* Static statement,called outside of a function body,treat it as a simple variable. */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Expected variable after 'static' keyword"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto Synchronize; } /* Compile the expression holding the variable */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if( rc != SXERR_EMPTY ){ /* Emit the POP instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP,1,0,0,0); } return SXRET_OK; } pFunc = (ph7_vm_func *)pBlock->pUserData; /* Make sure we are dealing with a valid statement */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd || (pGen->pIn[1].nType & (PH7_TK_ID|PH7_TK_KEYWORD)) == 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Expected variable after 'static' keyword"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto Synchronize; } pGen->pIn++; /* Extract variable name */ pName = &pGen->pIn->sData; pGen->pIn++; /* Jump the var name */ if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/|PH7_TK_EQUAL/*'='*/)) == 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"static: Unexpected token '%z'",&pGen->pIn->sData); goto Synchronize; } /* Initialize the structure describing the static variable */ SySetInit(&sStatic.aByteCode,&pGen->pVm->sAllocator,sizeof(VmInstr)); sStatic.nIdx = SXU32_HIGH; /* Not yet created */ /* Duplicate variable name */ zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,pName->zString,pName->nByte); if( zDup == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } SyStringInitFromBuf(&sStatic.sName,zDup,pName->nByte); /* Check if we have an expression to compile */ if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_EQUAL) ){ SySet *pInstrContainer; /* TICKET 1433-014: Symisc extension to the PHP programming language * Static variable can take any complex expression including function * call as their initialization value. * Example: * static $var = foo(1,4+5,bar()); */ pGen->pIn++; /* Jump the equal '=' sign */ /* Swap bytecode container */ pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); PH7_VmSetByteCodeContainer(pGen->pVm,&sStatic.aByteCode); /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,0); /* Emit the done instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,(rc != SXERR_EMPTY ? 1 : 0),0,0,0); /* Restore default bytecode container */ PH7_VmSetByteCodeContainer(pGen->pVm,pInstrContainer); } /* Finally save the compiled static variable in the appropriate container */ SySetPut(&pFunc->aStatic,(const void *)&sStatic); return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon ';',so we can avoid compiling this erroneous * statement. */ while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* * Compile the var statement. * Symisc Extension: * var statement can be used outside of a class definition. */ static sxi32 PH7_CompileVar(ph7_gen_state *pGen) { sxu32 nLine = pGen->pIn->nLine; sxi32 rc; /* Jump the 'var' keyword */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"var: Expecting variable name"); /* Synchronize with the first semi-colon */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0 ){ pGen->pIn++; } if( rc == SXERR_ABORT ){ return SXERR_ABORT; } }else{ /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; }else if( rc != SXERR_EMPTY ){ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP,1,0,0,0); } } return SXRET_OK; } /* * Compile a namespace statement * According to the PHP language reference manual * What are namespaces? In the broadest definition namespaces are a way of encapsulating items. * This can be seen as an abstract concept in many places. For example, in any operating system * directories serve to group related files, and act as a namespace for the files within them. * As a concrete example, the file foo.txt can exist in both directory /home/greg and in /home/other * but two copies of foo.txt cannot co-exist in the same directory. In addition, to access the foo.txt * file outside of the /home/greg directory, we must prepend the directory name to the file name using * the directory separator to get /home/greg/foo.txt. This same principle extends to namespaces in the * programming world. * In the PHP world, namespaces are designed to solve two problems that authors of libraries and applications * encounter when creating re-usable code elements such as classes or functions: * Name collisions between code you create, and internal PHP classes/functions/constants or third-party * classes/functions/constants. * Ability to alias (or shorten) Extra_Long_Names designed to alleviate the first problem, improving * readability of source code. * PHP Namespaces provide a way in which to group related classes, interfaces, functions and constants. * Here is an example of namespace syntax in PHP: * namespace my\name; // see "Defining Namespaces" section * class MyClass {} * function myfunction() {} * const MYCONST = 1; * $a = new MyClass; * $c = new \my\name\MyClass; * $a = strlen('hi'); * $d = namespace\MYCONST; * $d = __NAMESPACE__ . '\MYCONST'; * echo constant($d); * NOTE * AS OF THIS VERSION NAMESPACE SUPPORT IS DISABLED. IF YOU NEED A WORKING VERSION THAT IMPLEMENT * NAMESPACE,PLEASE CONTACT SYMISC SYSTEMS VIA contact@symisc.net. */ static sxi32 PH7_CompileNamespace(ph7_gen_state *pGen) { sxu32 nLine = pGen->pIn->nLine; sxi32 rc; pGen->pIn++; /* Jump the 'namespace' keyword */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_NSSEP|PH7_TK_ID|PH7_TK_KEYWORD|PH7_TK_SEMI/*';'*/|PH7_TK_OCB/*'{'*/)) == 0 ){ SyToken *pTok = pGen->pIn; if( pTok >= pGen->pEnd ){ pTok--; } /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Namespace: Unexpected token '%z'",&pTok->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Ignore the path */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_NSSEP/*'\'*/|PH7_TK_ID|PH7_TK_KEYWORD)) ){ pGen->pIn++; } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/|PH7_TK_OCB/*'{'*/)) == 0 ){ /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine, "Namespace: Unexpected token '%z',expecting ';' or '{'",&pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Emit a warning */ PH7_GenCompileError(&(*pGen),E_WARNING,nLine, "Namespace support is disabled in the current release of the PH7(%s) engine",ph7_lib_version()); return SXRET_OK; } /* * Compile the 'use' statement * According to the PHP language reference manual * The ability to refer to an external fully qualified name with an alias or importing * is an important feature of namespaces. This is similar to the ability of unix-based * filesystems to create symbolic links to a file or to a directory. * PHP namespaces support three kinds of aliasing or importing: aliasing a class name * aliasing an interface name, and aliasing a namespace name. Note that importing * a function or constant is not supported. * In PHP, aliasing is accomplished with the 'use' operator. * NOTE * AS OF THIS VERSION NAMESPACE SUPPORT IS DISABLED. IF YOU NEED A WORKING VERSION THAT IMPLEMENT * NAMESPACE,PLEASE CONTACT SYMISC SYSTEMS VIA contact@symisc.net. */ static sxi32 PH7_CompileUse(ph7_gen_state *pGen) { sxu32 nLine = pGen->pIn->nLine; sxi32 rc; pGen->pIn++; /* Jump the 'use' keyword */ /* Assemeble one or more real namespace path */ for(;;){ if( pGen->pIn >= pGen->pEnd ){ break; } /* Ignore the path */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_NSSEP|PH7_TK_ID)) ){ pGen->pIn++; } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA/*','*/) ){ pGen->pIn++; /* Jump the comma and process the next path */ }else{ break; } } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && PH7_TKWRD_AS == SX_PTR_TO_INT(pGen->pIn->pUserData) ){ pGen->pIn++; /* Jump the 'as' keyword */ /* Compile one or more aliasses */ for(;;){ if( pGen->pIn >= pGen->pEnd ){ break; } while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_NSSEP|PH7_TK_ID)) ){ pGen->pIn++; } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA/*','*/) ){ pGen->pIn++; /* Jump the comma and process the next alias */ }else{ break; } } } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0 ){ /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"use statement: Unexpected token '%z',expecting ';'", &pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Emit a notice */ PH7_GenCompileError(&(*pGen),E_NOTICE,nLine, "Namespace support is disabled in the current release of the PH7(%s) engine", ph7_lib_version() ); return SXRET_OK; } /* * Compile the stupid 'declare' language construct. * * According to the PHP language reference manual. * The declare construct is used to set execution directives for a block of code. * The syntax of declare is similar to the syntax of other flow control constructs: * declare (directive) * statement * The directive section allows the behavior of the declare block to be set. * Currently only two directives are recognized: the ticks directive and the encoding directive. * The statement part of the declare block will be executed - how it is executed and what side * effects occur during execution may depend on the directive set in the directive block. * The declare construct can also be used in the global scope, affecting all code following * it (however if the file with declare was included then it does not affect the parent file). * * * Well,actually this language construct is a NO-OP in the current release of the PH7 engine. */ static sxi32 PH7_CompileDeclare(ph7_gen_state *pGen) { sxu32 nLine = pGen->pIn->nLine; SyToken *pEnd = 0; /* cc warning */ sxi32 rc; pGen->pIn++; /* Jump the 'declare' keyword */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 /*'('*/ ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"declare: Expecting opening parenthesis '('"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto Synchro; } pGen->pIn++; /* Jump the left parenthesis */ /* Delimit the directive */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_LPAREN/*'('*/,PH7_TK_RPAREN/*')'*/,&pEnd); if( pEnd >= pGen->pEnd ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"declare: Missing closing parenthesis ')'"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXRET_OK; } /* Update the cursor */ pGen->pIn = &pEnd[1]; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/|PH7_TK_OCB/*'{'*/)) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"declare: Expecting ';' or '{' after directive"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* TICKET 1433-81: This construct is disabled in the current release of the PH7 engine. */ PH7_GenCompileError(&(*pGen),E_NOTICE,nLine, /* Emit a notice */ "the declare construct is a no-op in the current release of the PH7(%s) engine", ph7_lib_version() ); /*All done */ return SXRET_OK; Synchro: /* Sycnhronize with the first semi-colon ';' or curly braces '{' */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/|PH7_TK_OCB/*'{'*/)) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* * Process default argument values. That is,a function may define C++-style default value * as follows: * function makecoffee($type = "cappuccino") * { * return "Making a cup of $type.\n"; * } * Symisc eXtension. * 1 -) Default arguments value can be any complex expression [i.e: function call,annynoymous * functions,array member,..] unlike the zend which would allow only single scalar value. * Example: Work only with PH7,generate error under zend * function test($a = 'Hello'.'World: '.rand_str(3)) * { * var_dump($a); * } * //call test without args * test(); * 2 -) Full type hinting: (Arguments are automatically casted to the desired type) * Example: * function a(string $a){} function b(int $a,string $c,float $d){} * 3 -) Function overloading!! * Example: * function foo($a) { * return $a.PHP_EOL; * } * function foo($a, $b) { * return $a + $b; * } * echo foo(5); // Prints "5" * echo foo(5, 2); // Prints "7" * // Same arg * function foo(string $a) * { * echo "a is a string\n"; * var_dump($a); * } * function foo(int $a) * { * echo "a is integer\n"; * var_dump($a); * } * function foo(array $a) * { * echo "a is an array\n"; * var_dump($a); * } * foo('This is a great feature'); // a is a string [first foo] * foo(52); // a is integer [second foo] * foo(array(14,__TIME__,__DATE__)); // a is an array [third foo] * Please refer to the official documentation for more information on the powerful extension * introduced by the PH7 engine. */ static sxi32 GenStateProcessArgValue(ph7_gen_state *pGen,ph7_vm_func_arg *pArg,SyToken *pIn,SyToken *pEnd) { SyToken *pTmpIn,*pTmpEnd; SySet *pInstrContainer; sxi32 rc; /* Swap token stream */ SWAP_DELIMITER(pGen,pIn,pEnd); pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); PH7_VmSetByteCodeContainer(pGen->pVm,&pArg->aByteCode); /* Compile the expression holding the argument value */ rc = PH7_CompileExpr(&(*pGen),0,0); /* Emit the done instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,(rc != SXERR_EMPTY ? 1 : 0),0,0,0); PH7_VmSetByteCodeContainer(pGen->pVm,pInstrContainer); RE_SWAP_DELIMITER(pGen); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXRET_OK; } /* * Collect function arguments one after one. * According to the PHP language reference manual. * Information may be passed to functions via the argument list, which is a comma-delimited * list of expressions. * PHP supports passing arguments by value (the default), passing by reference * and default argument values. Variable-length argument lists are also supported, * see also the function references for func_num_args(), func_get_arg(), and func_get_args() * for more information. * Example #1 Passing arrays to functions * * Making arguments be passed by reference * By default, function arguments are passed by value (so that if the value of the argument * within the function is changed, it does not get changed outside of the function). * To allow a function to modify its arguments, they must be passed by reference. * To have an argument to a function always passed by reference, prepend an ampersand (&) * to the argument name in the function definition: * Example #2 Passing function parameters by reference * * * PH7 have introduced powerful extension including full type hinting,function overloading * complex agrument values.Please refer to the official documentation for more information * on these extension. */ static sxi32 GenStateCollectFuncArgs(ph7_vm_func *pFunc,ph7_gen_state *pGen,SyToken *pEnd) { ph7_vm_func_arg sArg; /* Current processed argument */ SyToken *pCur,*pIn; /* Token stream */ SyBlob sSig; /* Function signature */ char *zDup; /* Copy of argument name */ sxi32 rc; pIn = pGen->pIn; pCur = 0; SyBlobInit(&sSig,&pGen->pVm->sAllocator); /* Process arguments one after one */ for(;;){ if( pIn >= pEnd ){ /* No more arguments to process */ break; } SyZero(&sArg,sizeof(ph7_vm_func_arg)); SySetInit(&sArg.aByteCode,&pGen->pVm->sAllocator,sizeof(VmInstr)); if( pIn->nType & (PH7_TK_ID|PH7_TK_KEYWORD) ){ if( pIn->nType & PH7_TK_KEYWORD ){ sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData)); if( nKey & PH7_TKWRD_ARRAY ){ sArg.nType = MEMOBJ_HASHMAP; }else if( nKey & PH7_TKWRD_BOOL ){ sArg.nType = MEMOBJ_BOOL; }else if( nKey & PH7_TKWRD_INT ){ sArg.nType = MEMOBJ_INT; }else if( nKey & PH7_TKWRD_STRING ){ sArg.nType = MEMOBJ_STRING; }else if( nKey & PH7_TKWRD_FLOAT ){ sArg.nType = MEMOBJ_REAL; }else{ PH7_GenCompileError(&(*pGen),E_WARNING,pGen->pIn->nLine, "Invalid argument type '%z',Automatic cast will not be performed", &pIn->sData); } }else{ SyString *pName = &pIn->sData; /* Class name */ char *zDup; /* Argument must be a class instance,record that*/ zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,pName->zString,pName->nByte); if( zDup ){ sArg.nType = SXU32_HIGH; /* 0xFFFFFFFF as sentinel */ SyStringInitFromBuf(&sArg.sClass,zDup,pName->nByte); } } pIn++; } if( pIn >= pEnd ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Missing argument name"); return rc; } if( pIn->nType & PH7_TK_AMPER ){ /* Pass by reference,record that */ sArg.iFlags = VM_FUNC_ARG_BY_REF; pIn++; } if( pIn >= pEnd || (pIn->nType & PH7_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (PH7_TK_ID|PH7_TK_KEYWORD)) == 0 ){ /* Invalid argument */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Invalid argument name"); return rc; } pIn++; /* Jump the dollar sign */ /* Copy argument name */ zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,SyStringData(&pIn->sData),SyStringLength(&pIn->sData)); if( zDup == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,pIn->nLine,"PH7 engine is running out of memory"); return SXERR_ABORT; } SyStringInitFromBuf(&sArg.sName,zDup,SyStringLength(&pIn->sData)); pIn++; if( pIn < pEnd ){ if( pIn->nType & PH7_TK_EQUAL ){ SyToken *pDefend; sxi32 iNest = 0; pIn++; /* Jump the equal sign */ pDefend = pIn; /* Process the default value associated with this argument */ while( pDefend < pEnd ){ if( (pDefend->nType & PH7_TK_COMMA) && iNest <= 0 ){ break; } if( pDefend->nType & (PH7_TK_LPAREN/*'('*/|PH7_TK_OCB/*'{'*/|PH7_TK_OSB/*[*/) ){ /* Increment nesting level */ iNest++; }else if( pDefend->nType & (PH7_TK_RPAREN/*')'*/|PH7_TK_CCB/*'}'*/|PH7_TK_CSB/*]*/) ){ /* Decrement nesting level */ iNest--; } pDefend++; } if( pIn >= pDefend ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pIn->nLine,"Missing argument default value"); return rc; } /* Process default value */ rc = GenStateProcessArgValue(&(*pGen),&sArg,pIn,pDefend); if( rc != SXRET_OK ){ return rc; } /* Point beyond the default value */ pIn = pDefend; } if( pIn < pEnd && (pIn->nType & PH7_TK_COMMA) == 0 ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pIn->nLine,"Unexpected token '%z'",&pIn->sData); return rc; } pIn++; /* Jump the trailing comma */ } /* Append argument signature */ if( sArg.nType > 0 ){ if( SyStringLength(&sArg.sClass) > 0 ){ /* Class name */ SyBlobAppend(&sSig,SyStringData(&sArg.sClass),SyStringLength(&sArg.sClass)); }else{ int c; c = 'n'; /* cc warning */ /* Type leading character */ switch(sArg.nType){ case MEMOBJ_HASHMAP: /* Hashmap aka 'array' */ c = 'h'; break; case MEMOBJ_INT: /* Integer */ c = 'i'; break; case MEMOBJ_BOOL: /* Bool */ c = 'b'; break; case MEMOBJ_REAL: /* Float */ c = 'f'; break; case MEMOBJ_STRING: /* String */ c = 's'; break; default: break; } SyBlobAppend(&sSig,(const void *)&c,sizeof(char)); } }else{ /* No type is associated with this parameter which mean * that this function is not condidate for overloading. */ SyBlobRelease(&sSig); } /* Save in the argument set */ SySetPut(&pFunc->aArgs,(const void *)&sArg); } if( SyBlobLength(&sSig) > 0 ){ /* Save function signature */ SyStringInitFromBuf(&pFunc->sSignature,SyBlobData(&sSig),SyBlobLength(&sSig)); } return SXRET_OK; } /* * Compile function [i.e: standard function, annonymous function or closure ] body. * Return SXRET_OK on success. Any other return value indicates failure * and this routine takes care of generating the appropriate error message. */ static sxi32 GenStateCompileFuncBody( ph7_gen_state *pGen, /* Code generator state */ ph7_vm_func *pFunc /* Function state */ ) { SySet *pInstrContainer; /* Instruction container */ GenBlock *pBlock; sxu32 nGotoOfft; sxi32 rc; /* Attach the new function */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC,PH7_VmInstrLength(pGen->pVm),pFunc,&pBlock); if( rc != SXRET_OK ){ PH7_GenCompileError(&(*pGen),E_ERROR,1,"PH7 engine is running out-of-memory"); /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } nGotoOfft = SySetUsed(&pGen->aGoto); /* Swap bytecode containers */ pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); PH7_VmSetByteCodeContainer(pGen->pVm,&pFunc->aByteCode); /* Compile the body */ PH7_CompileBlock(&(*pGen),0); /* Fix exception jumps now the destination is resolved */ GenStateFixJumps(pGen->pCurrent,PH7_OP_THROW,PH7_VmInstrLength(pGen->pVm)); /* Emit the final return if not yet done */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,0,0,0,0); /* Fix gotos jumps now the destination is resolved */ if( SXERR_ABORT == GenStateFixGoto(&(*pGen),nGotoOfft) ){ rc = SXERR_ABORT; } SySetTruncate(&pGen->aGoto,nGotoOfft); /* Restore the default container */ PH7_VmSetByteCodeContainer(pGen->pVm,pInstrContainer); /* Leave function block */ GenStateLeaveBlock(&(*pGen),0); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } /* All done, function body compiled */ return SXRET_OK; } /* * Compile a PHP function whether is a Standard or Annonymous function. * According to the PHP language reference manual. * Function names follow the same rules as other labels in PHP. A valid function name * starts with a letter or underscore, followed by any number of letters, numbers, or * underscores. As a regular expression, it would be expressed thus: * [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*. * Functions need not be defined before they are referenced. * All functions and classes in PHP have the global scope - they can be called outside * a function even if they were defined inside and vice versa. * It is possible to call recursive functions in PHP. However avoid recursive function/method * calls with over 32-64 recursion levels. * * PH7 have introduced powerful extension including full type hinting, function overloading, * complex agrument values and more. Please refer to the official documentation for more information * on these extension. */ static sxi32 GenStateCompileFunc( ph7_gen_state *pGen, /* Code generator state */ SyString *pName, /* Function name. NULL otherwise */ sxi32 iFlags, /* Control flags */ int bHandleClosure, /* TRUE if we are dealing with a closure */ ph7_vm_func **ppFunc /* OUT: function state */ ) { ph7_vm_func *pFunc; SyToken *pEnd; sxu32 nLine; char *zName; sxi32 rc; /* Extract line number */ nLine = pGen->pIn->nLine; /* Jump the left parenthesis '(' */ pGen->pIn++; /* Delimit the function signature */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_LPAREN /* '(' */,PH7_TK_RPAREN /* ')' */,&pEnd); if( pEnd >= pGen->pEnd ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Missing ')' after function '%z' signature",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } pGen->pIn = pGen->pEnd; return SXRET_OK; } /* Create the function state */ pFunc = (ph7_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator,sizeof(ph7_vm_func)); if( pFunc == 0 ){ goto OutOfMem; } /* function ID */ zName = SyMemBackendStrDup(&pGen->pVm->sAllocator,pName->zString,pName->nByte); if( zName == 0 ){ /* Don't worry about freeing memory, everything will be released shortly */ goto OutOfMem; } /* Initialize the function state */ PH7_VmInitFuncState(pGen->pVm,pFunc,zName,pName->nByte,iFlags,0); if( pGen->pIn < pEnd ){ /* Collect function arguments */ rc = GenStateCollectFuncArgs(pFunc,&(*pGen),pEnd); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } } /* Compile function body */ pGen->pIn = &pEnd[1]; if( bHandleClosure ){ ph7_vm_func_closure_env sEnv; int got_this = 0; /* TRUE if $this have been seen */ if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_USE ){ sxu32 nLine = pGen->pIn->nLine; /* Closure,record environment variable */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Closure: Unexpected token. Expecting a left parenthesis '('"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } pGen->pIn++; /* Jump the left parenthesis or any other unexpected token */ /* Compile until we hit the first closing parenthesis */ while( pGen->pIn < pGen->pEnd ){ int iFlags = 0; if( pGen->pIn->nType & PH7_TK_RPAREN ){ pGen->pIn++; /* Jump the closing parenthesis */ break; } nLine = pGen->pIn->nLine; if( pGen->pIn->nType & PH7_TK_AMPER ){ /* Pass by reference,record that */ PH7_GenCompileError(pGen,E_WARNING,nLine, "Closure: Pass by reference is disabled in the current release of the PH7 engine,PH7 is switching to pass by value" ); iFlags = VM_FUNC_ARG_BY_REF; pGen->pIn++; } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd || (pGen->pIn[1].nType & (PH7_TK_ID|PH7_TK_KEYWORD)) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine, "Closure: Unexpected token. Expecting a variable name"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* Find the closing parenthesis */ while( (pGen->pIn < pGen->pEnd) && (pGen->pIn->nType & PH7_TK_RPAREN) == 0 ){ pGen->pIn++; } if(pGen->pIn < pGen->pEnd){ pGen->pIn++; } break; /* TICKET 1433-95: No need for the else block below.*/ }else{ SyString *pName; char *zDup; /* Duplicate variable name */ pName = &pGen->pIn[1].sData; zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,pName->zString,pName->nByte); if( zDup ){ /* Zero the structure */ SyZero(&sEnv,sizeof(ph7_vm_func_closure_env)); sEnv.iFlags = iFlags; PH7_MemObjInit(pGen->pVm,&sEnv.sValue); SyStringInitFromBuf(&sEnv.sName,zDup,pName->nByte); if( !got_this && pName->nByte == sizeof("this")-1 && SyMemcmp((const void *)zDup,(const void *)"this",sizeof("this")-1) == 0 ){ got_this = 1; } /* Save imported variable */ SySetPut(&pFunc->aClosureEnv,(const void *)&sEnv); }else{ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } } pGen->pIn += 2; /* $ + variable name or any other unexpected token */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA /*','*/) ){ /* Ignore trailing commas */ pGen->pIn++; } } if( !got_this ){ /* Make the $this variable [Current processed Object (class instance)] * available to the closure environment. */ SyZero(&sEnv,sizeof(ph7_vm_func_closure_env)); sEnv.iFlags = VM_FUNC_ARG_IGNORE; /* Do not install if NULL */ PH7_MemObjInit(pGen->pVm,&sEnv.sValue); SyStringInitFromBuf(&sEnv.sName,"this",sizeof("this")-1); SySetPut(&pFunc->aClosureEnv,(const void *)&sEnv); } if( SySetUsed(&pFunc->aClosureEnv) > 0 ){ /* Mark as closure */ pFunc->iFlags |= VM_FUNC_CLOSURE; } } } /* Compile the body */ rc = GenStateCompileFuncBody(&(*pGen),pFunc); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } if( ppFunc ){ *ppFunc = pFunc; } rc = SXRET_OK; if( (pFunc->iFlags & VM_FUNC_CLOSURE) == 0 ){ /* Finally register the function */ rc = PH7_VmInstallUserFunction(pGen->pVm,pFunc,0); } if( rc == SXRET_OK ){ return SXRET_OK; } /* Fall through if something goes wrong */ OutOfMem: /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ PH7_GenCompileError(&(*pGen),E_ERROR,1,"Fatal, PH7 engine is running out-of-memory"); return SXERR_ABORT; } /* * Compile a standard PHP function. * Refer to the block-comment above for more information. */ static sxi32 PH7_CompileFunction(ph7_gen_state *pGen) { SyString *pName; sxi32 iFlags; sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; pGen->pIn++; /* Jump the 'function' keyword */ iFlags = 0; if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_AMPER) ){ /* Return by reference,remember that */ iFlags |= VM_FUNC_REF_RETURN; /* Jump the '&' token */ pGen->pIn++; } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_ID|PH7_TK_KEYWORD)) == 0 ){ /* Invalid function name */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Invalid function name"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* Sychronize with the next semi-colon or braces*/ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI|PH7_TK_OCB)) == 0 ){ pGen->pIn++; } return SXRET_OK; } pName = &pGen->pIn->sData; nLine = pGen->pIn->nLine; /* Jump the function name */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '(' after function name '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } /* Sychronize with the next semi-colon or '{' */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI|PH7_TK_OCB)) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* Compile function body */ rc = GenStateCompileFunc(&(*pGen),pName,iFlags,FALSE,0); return rc; } /* * Extract the visibility level associated with a given keyword. * According to the PHP language reference manual * Visibility: * The visibility of a property or method can be defined by prefixing * the declaration with the keywords public, protected or private. * Class members declared public can be accessed everywhere. * Members declared protected can be accessed only within the class * itself and by inherited and parent classes. Members declared as private * may only be accessed by the class that defines the member. */ static sxi32 GetProtectionLevel(sxi32 nKeyword) { if( nKeyword == PH7_TKWRD_PRIVATE ){ return PH7_CLASS_PROT_PRIVATE; }else if( nKeyword == PH7_TKWRD_PROTECTED ){ return PH7_CLASS_PROT_PROTECTED; } /* Assume public by default */ return PH7_CLASS_PROT_PUBLIC; } /* * Compile a class constant. * According to the PHP language reference manual * Class Constants * It is possible to define constant values on a per-class basis remaining * the same and unchangeable. Constants differ from normal variables in that * you don't use the $ symbol to declare or use them. * The value must be a constant expression, not (for example) a variable, * a property, a result of a mathematical operation, or a function call. * It's also possible for interfaces to have constants. * Symisc eXtension. * PH7 allow any complex expression to be associated with the constant while * the zend engine would allow only simple scalar value. * Example: * class Test{ * const MyConst = "Hello"."world: ".rand_str(3); //concatenation operation + Function call * }; * var_dump(TEST::MyConst); * Refer to the official documentation for more information on the powerful extension * introduced by the PH7 engine to the OO subsystem. */ static sxi32 GenStateCompileClassConstant(ph7_gen_state *pGen,sxi32 iProtection,sxi32 iFlags,ph7_class *pClass) { sxu32 nLine = pGen->pIn->nLine; SySet *pInstrContainer; ph7_class_attr *pCons; SyString *pName; sxi32 rc; /* Extract visibility level */ iProtection = GetProtectionLevel(iProtection); pGen->pIn++; /* Jump the 'const' keyword */ loop: /* Mark as constant */ iFlags |= PH7_CLASS_ATTR_CONSTANT; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0 ){ /* Invalid constant name */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Invalid constant name"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Peek constant name */ pName = &pGen->pIn->sData; /* Make sure the constant name isn't reserved */ if( GenStateIsReservedConstant(pName) ){ /* Reserved constant name */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Cannot redeclare a reserved constant '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Advance the stream cursor */ pGen->pIn++; if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_EQUAL /* '=' */) == 0 ){ /* Invalid declaration */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '=' after class constant %z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } pGen->pIn++; /* Jump the equal sign */ /* Allocate a new class attribute */ pCons = PH7_NewClassAttr(pGen->pVm,pName,nLine,iProtection,iFlags); if( pCons == 0 ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } /* Swap bytecode container */ pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); PH7_VmSetByteCodeContainer(pGen->pVm,&pCons->aByteCode); /* Compile constant value. */ rc = PH7_CompileExpr(&(*pGen),EXPR_FLAG_COMMA_STATEMENT,0); if( rc == SXERR_EMPTY ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Empty constant '%z' value",pName); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Emit the done instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,1,0,0,0); PH7_VmSetByteCodeContainer(pGen->pVm,pInstrContainer); if( rc == SXERR_ABORT ){ /* Don't worry about freeing memory, everything will be released shortly */ return SXERR_ABORT; } /* All done,install the constant */ rc = PH7_ClassInstallAttr(pClass,pCons); if( rc != SXRET_OK ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA /*','*/) ){ /* Multiple constants declarations [i.e: const min=-1,max = 10] */ pGen->pIn++; /* Jump the comma */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0 ){ SyToken *pTok = pGen->pIn; if( pTok >= pGen->pEnd ){ pTok--; } rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z',expecting constant declaration inside class '%z'", &pTok->sData,&pClass->sName); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } }else{ if( pGen->pIn->nType & PH7_TK_ID ){ goto loop; } } } return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon */ while(pGen->pIn < pGen->pEnd && ((pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) ){ pGen->pIn++; } return SXERR_CORRUPT; } /* * complie a class attribute or Properties in the PHP jargon. * According to the PHP language reference manual * Properties * Class member variables are called "properties". You may also see them referred * to using other terms such as "attributes" or "fields", but for the purposes * of this reference we will use "properties". They are defined by using one * of the keywords public, protected, or private, followed by a normal variable * declaration. This declaration may include an initialization, but this initialization * must be a constant value--that is, it must be able to be evaluated at compile time * and must not depend on run-time information in order to be evaluated. * Symisc eXtension. * PH7 allow any complex expression to be associated with the attribute while * the zend engine would allow only simple scalar value. * Example: * class Test{ * public static $myVar = "Hello"."world: ".rand_str(3); //concatenation operation + Function call * }; * var_dump(TEST::myVar); * Refer to the official documentation for more information on the powerful extension * introduced by the PH7 engine to the OO subsystem. */ static sxi32 GenStateCompileClassAttr(ph7_gen_state *pGen,sxi32 iProtection,sxi32 iFlags,ph7_class *pClass) { sxu32 nLine = pGen->pIn->nLine; ph7_class_attr *pAttr; SyString *pName; sxi32 rc; /* Extract visibility level */ iProtection = GetProtectionLevel(iProtection); loop: pGen->pIn++; /* Jump the dollar sign */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD|PH7_TK_ID)) == 0 ){ /* Invalid attribute name */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Invalid attribute name"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Peek attribute name */ pName = &pGen->pIn->sData; /* Advance the stream cursor */ pGen->pIn++; if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_EQUAL/*'='*/|PH7_TK_SEMI/*';'*/|PH7_TK_COMMA/*','*/)) == 0 ){ /* Invalid declaration */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '=' or ';' after attribute name '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Allocate a new class attribute */ pAttr = PH7_NewClassAttr(pGen->pVm,pName,nLine,iProtection,iFlags); if( pAttr == 0 ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } if( pGen->pIn->nType & PH7_TK_EQUAL /*'='*/ ){ SySet *pInstrContainer; pGen->pIn++; /*Jump the equal sign */ /* Swap bytecode container */ pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); PH7_VmSetByteCodeContainer(pGen->pVm,&pAttr->aByteCode); /* Compile attribute value. */ rc = PH7_CompileExpr(&(*pGen),EXPR_FLAG_COMMA_STATEMENT,0); if( rc == SXERR_EMPTY ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Attribute '%z': Missing default value",pName); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Emit the done instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,1,0,0,0); PH7_VmSetByteCodeContainer(pGen->pVm,pInstrContainer); } /* All done,install the attribute */ rc = PH7_ClassInstallAttr(pClass,pAttr); if( rc != SXRET_OK ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA /*','*/) ){ /* Multiple attribute declarations [i.e: public $var1,$var2=5<<1,$var3] */ pGen->pIn++; /* Jump the comma */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0 ){ SyToken *pTok = pGen->pIn; if( pTok >= pGen->pEnd ){ pTok--; } rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z',expecting attribute declaration inside class '%z'", &pTok->sData,&pClass->sName); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } }else{ if( pGen->pIn->nType & PH7_TK_DOLLAR ){ goto loop; } } } return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon */ while(pGen->pIn < pGen->pEnd && ((pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) ){ pGen->pIn++; } return SXERR_CORRUPT; } /* * Compile a class method. * * Refer to the official documentation for more information * on the powerful extension introduced by the PH7 engine * to the OO subsystem such as full type hinting,method * overloading and many more. */ static sxi32 GenStateCompileClassMethod( ph7_gen_state *pGen, /* Code generator state */ sxi32 iProtection, /* Visibility level */ sxi32 iFlags, /* Configuration flags */ int doBody, /* TRUE to process method body */ ph7_class *pClass /* Class this method belongs */ ) { sxu32 nLine = pGen->pIn->nLine; ph7_class_method *pMeth; sxi32 iFuncFlags; SyString *pName; SyToken *pEnd; sxi32 rc; /* Extract visibility level */ iProtection = GetProtectionLevel(iProtection); pGen->pIn++; /* Jump the 'function' keyword */ iFuncFlags = 0; if( pGen->pIn >= pGen->pEnd ){ /* Invalid method name */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Invalid method name"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_AMPER) ){ /* Return by reference,remember that */ iFuncFlags |= VM_FUNC_REF_RETURN; /* Jump the '&' token */ pGen->pIn++; } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_ID)) == 0 ){ /* Invalid method name */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Invalid method name"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto Synchronize; } /* Peek method name */ pName = &pGen->pIn->sData; nLine = pGen->pIn->nLine; /* Jump the method name */ pGen->pIn++; if( iFlags & PH7_CLASS_ATTR_ABSTRACT ){ /* Abstract method */ if( iProtection == PH7_CLASS_PROT_PRIVATE ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine, "Access type for abstract method '%z::%z' cannot be 'private'", &pClass->sName,pName); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Assemble method signature only */ doBody = FALSE; } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '(' after method name '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Allocate a new class_method instance */ pMeth = PH7_NewClassMethod(pGen->pVm,pClass,pName,nLine,iProtection,iFlags,iFuncFlags); if( pMeth == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } /* Jump the left parenthesis '(' */ pGen->pIn++; pEnd = 0; /* cc warning */ /* Delimit the method signature */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_LPAREN /* '(' */,PH7_TK_RPAREN /* ')' */,&pEnd); if( pEnd >= pGen->pEnd ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Missing ')' after method '%z' declaration",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } if( pGen->pIn < pEnd ){ /* Collect method arguments */ rc = GenStateCollectFuncArgs(&pMeth->sFunc,&(*pGen),pEnd); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } /* Point beyond method signature */ pGen->pIn = &pEnd[1]; if( doBody ){ /* Compile method body */ rc = GenStateCompileFuncBody(&(*pGen),&pMeth->sFunc); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } }else{ /* Only method signature is allowed */ if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI /* ';'*/) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Expected ';' after method signature '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXERR_CORRUPT; } } /* All done,install the method */ rc = PH7_ClassInstallMethod(pClass,pMeth); if( rc != SXRET_OK ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon */ while(pGen->pIn < pGen->pEnd && ((pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) ){ pGen->pIn++; } return SXERR_CORRUPT; } /* * Compile an object interface. * According to the PHP language reference manual * Object Interfaces: * Object interfaces allow you to create code which specifies which methods * a class must implement, without having to define how these methods are handled. * Interfaces are defined using the interface keyword, in the same way as a standard * class, but without any of the methods having their contents defined. * All methods declared in an interface must be public, this is the nature of an interface. */ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { sxu32 nLine = pGen->pIn->nLine; ph7_class *pClass,*pBase; SyToken *pEnd,*pTmp; SyString *pName; sxi32 nKwrd; sxi32 rc; /* Jump the 'interface' keyword */ pGen->pIn++; /* Extract interface name */ pName = &pGen->pIn->sData; /* Advance the stream cursor */ pGen->pIn++; /* Obtain a raw class */ pClass = PH7_NewRawClass(pGen->pVm,pName,nLine); if( pClass == 0 ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } /* Mark as an interface */ pClass->iFlags = PH7_CLASS_INTERFACE; /* Assume no base class is given */ pBase = 0; if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) ){ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_EXTENDS /* interface b extends a */ ){ SyString *pBaseName; /* Extract base interface */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine, "Expected 'interface_name' after 'extends' keyword inside interface '%z'", pName); SyMemBackendPoolFree(&pGen->pVm->sAllocator,pClass); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } pBaseName = &pGen->pIn->sData; pBase = PH7_VmExtractClass(pGen->pVm,pBaseName->zString,pBaseName->nByte,FALSE,0); /* Only interfaces is allowed */ while( pBase && (pBase->iFlags & PH7_CLASS_INTERFACE) == 0 ){ pBase = pBase->pNextName; } if( pBase == 0 ){ /* Inexistant interface */ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"Inexistant base interface '%z'",pBaseName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } } /* Advance the stream cursor */ pGen->pIn++; } } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_OCB /*'{'*/) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '{' after interface '%z' definition",pName); SyMemBackendPoolFree(&pGen->pVm->sAllocator,pClass); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } pGen->pIn++; /* Jump the leading curly brace */ pEnd = 0; /* cc warning */ /* Delimit the interface body */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_OCB/*'{'*/,PH7_TK_CCB/*'}'*/,&pEnd); if( pEnd >= pGen->pEnd ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Missing '}' after interface '%z' definition",pName); SyMemBackendPoolFree(&pGen->pVm->sAllocator,pClass); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } /* Swap token stream */ pTmp = pGen->pEnd; pGen->pEnd = pEnd; /* Start the parse process * Note (According to the PHP reference manual): * Only constants and function signatures(without body) are allowed. * Only 'public' visibility is allowed. */ for(;;){ /* Jump leading/trailing semi-colons */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) ){ pGen->pIn++; } if( pGen->pIn >= pGen->pEnd ){ /* End of interface body */ break; } if( (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z'.Expecting method signature or constant declaration inside interface '%z'", &pGen->pIn->sData,pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } /* Extract the current keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED ){ /* Emit a warning and switch to public visibility */ PH7_GenCompileError(&(*pGen),E_WARNING,pGen->pIn->nLine,"interface: Access type must be public"); nKwrd = PH7_TKWRD_PUBLIC; } if( nKwrd != PH7_TKWRD_PUBLIC && nKwrd != PH7_TKWRD_FUNCTION && nKwrd != PH7_TKWRD_CONST && nKwrd != PH7_TKWRD_STATIC ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Expecting method signature or constant declaration inside interface '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } if( nKwrd == PH7_TKWRD_PUBLIC ){ /* Advance the stream cursor */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Expecting method signature inside interface '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd != PH7_TKWRD_FUNCTION && nKwrd != PH7_TKWRD_CONST && nKwrd != PH7_TKWRD_STATIC ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Expecting method signature or constant declaration inside interface '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } } if( nKwrd == PH7_TKWRD_CONST ){ /* Parse constant */ rc = GenStateCompileClassConstant(&(*pGen),0,0,pClass); if( rc != SXRET_OK ){ if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto done; } }else{ sxi32 iFlags = 0; if( nKwrd == PH7_TKWRD_STATIC ){ /* Static method,record that */ iFlags |= PH7_CLASS_ATTR_STATIC; /* Advance the stream cursor */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_FUNCTION ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Expecting method signature inside interface '%z'",pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } } /* Process method signature */ rc = GenStateCompileClassMethod(&(*pGen),0,FALSE/* Only method signature*/,iFlags,pClass); if( rc != SXRET_OK ){ if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto done; } } } /* Install the interface */ rc = PH7_VmInstallClass(pGen->pVm,pClass); if( rc == SXRET_OK && pBase ){ /* Inherit from the base interface */ rc = PH7_ClassInterfaceInherit(pClass,pBase); } if( rc != SXRET_OK ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } done: /* Point beyond the interface body */ pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; return PH7_OK; } /* * Compile a user-defined class. * According to the PHP language reference manual * class * Basic class definitions begin with the keyword class, followed by a class * name, followed by a pair of curly braces which enclose the definitions * of the properties and methods belonging to the class. * The class name can be any valid label which is a not a PHP reserved word. * A valid class name starts with a letter or underscore, followed by any number * of letters, numbers, or underscores. As a regular expression, it would be expressed * thus: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*. * A class may contain its own constants, variables (called "properties"), and functions * (called "methods"). */ static sxi32 GenStateCompileClass(ph7_gen_state *pGen,sxi32 iFlags) { sxu32 nLine = pGen->pIn->nLine; ph7_class *pClass,*pBase; SyToken *pEnd,*pTmp; sxi32 iProtection; SySet aInterfaces; sxi32 iAttrflags; SyString *pName; sxi32 nKwrd; sxi32 rc; /* Jump the 'class' keyword */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Invalid class name"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } /* Synchronize with the first semi-colon or curly braces */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_OCB/*'{'*/|PH7_TK_SEMI/*';'*/)) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* Extract class name */ pName = &pGen->pIn->sData; /* Advance the stream cursor */ pGen->pIn++; /* Obtain a raw class */ pClass = PH7_NewRawClass(pGen->pVm,pName,nLine); if( pClass == 0 ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } /* implemented interfaces container */ SySetInit(&aInterfaces,&pGen->pVm->sAllocator,sizeof(ph7_class *)); /* Assume a standalone class */ pBase = 0; if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) ){ SyString *pBaseName; nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_EXTENDS /* class b extends a */ ){ pGen->pIn++; /* Advance the stream cursor */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine, "Expected 'class_name' after 'extends' keyword inside class '%z'", pName); SyMemBackendPoolFree(&pGen->pVm->sAllocator,pClass); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } /* Extract base class name */ pBaseName = &pGen->pIn->sData; /* Perform the query */ pBase = PH7_VmExtractClass(pGen->pVm,pBaseName->zString,pBaseName->nByte,FALSE,0); /* Interfaces are not allowed */ while( pBase && (pBase->iFlags & PH7_CLASS_INTERFACE) ){ pBase = pBase->pNextName; } if( pBase == 0 ){ /* Inexistant base class */ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"Inexistant base class '%z'",pBaseName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } }else{ if( pBase->iFlags & PH7_CLASS_FINAL ){ rc = PH7_GenCompileError(pGen,E_ERROR,nLine, "Class '%z' may not inherit from final class '%z'",pName,&pBase->sName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } } } /* Advance the stream cursor */ pGen->pIn++; } if (pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_IMPLEMENTS ){ ph7_class *pInterface; SyString *pIntName; /* Interface implementation */ pGen->pIn++; /* Advance the stream cursor */ for(;;){ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine, "Expected 'interface_name' after 'implements' keyword inside class '%z' declaration", pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } break; } /* Extract interface name */ pIntName = &pGen->pIn->sData; /* Make sure the interface is already defined */ pInterface = PH7_VmExtractClass(pGen->pVm,pIntName->zString,pIntName->nByte,FALSE,0); /* Only interfaces are allowed */ while( pInterface && (pInterface->iFlags & PH7_CLASS_INTERFACE) == 0 ){ pInterface = pInterface->pNextName; } if( pInterface == 0 ){ /* Inexistant interface */ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"Inexistant base interface '%z'",pIntName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } }else{ /* Register interface */ SySetPut(&aInterfaces,(const void *)&pInterface); } /* Advance the stream cursor */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_COMMA) == 0 ){ break; } pGen->pIn++;/* Jump the comma */ } } } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_OCB /*'{'*/) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '{' after class '%z' declaration",pName); SyMemBackendPoolFree(&pGen->pVm->sAllocator,pClass); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } pGen->pIn++; /* Jump the leading curly brace */ pEnd = 0; /* cc warning */ /* Delimit the class body */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_OCB/*'{'*/,PH7_TK_CCB/*'}'*/,&pEnd); if( pEnd >= pGen->pEnd ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Missing closing braces'}' after class '%z' definition",pName); SyMemBackendPoolFree(&pGen->pVm->sAllocator,pClass); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } return SXRET_OK; } /* Swap token stream */ pTmp = pGen->pEnd; pGen->pEnd = pEnd; /* Set the inherited flags */ pClass->iFlags = iFlags; /* Start the parse process */ for(;;){ /* Jump leading/trailing semi-colons */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) ){ pGen->pIn++; } if( pGen->pIn >= pGen->pEnd ){ /* End of class body */ break; } if( (pGen->pIn->nType & (PH7_TK_KEYWORD|PH7_TK_DOLLAR)) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z'. Expecting attribute declaration inside class '%z'", &pGen->pIn->sData,pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } /* Assume public visibility */ iProtection = PH7_TKWRD_PUBLIC; iAttrflags = 0; if( pGen->pIn->nType & PH7_TK_KEYWORD ){ /* Extract the current keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_PUBLIC || nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED ){ iProtection = nKwrd; pGen->pIn++; /* Jump the visibility token */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD|PH7_TK_DOLLAR)) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z'. Expecting attribute declaration inside class '%z'", &pGen->pIn->sData,pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } if( pGen->pIn->nType & PH7_TK_DOLLAR ){ /* Attribute declaration */ rc = GenStateCompileClassAttr(&(*pGen),iProtection,iAttrflags,pClass); if( rc != SXRET_OK ){ if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto done; } continue; } /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); } if( nKwrd == PH7_TKWRD_CONST ){ /* Process constant declaration */ rc = GenStateCompileClassConstant(&(*pGen),iProtection,iAttrflags,pClass); if( rc != SXRET_OK ){ if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto done; } }else{ if( nKwrd == PH7_TKWRD_STATIC ){ /* Static method or attribute,record that */ iAttrflags |= PH7_CLASS_ATTR_STATIC; pGen->pIn++; /* Jump the static keyword */ if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) ){ /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_PUBLIC || nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED ){ iProtection = nKwrd; pGen->pIn++; /* Jump the visibility token */ } } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD|PH7_TK_DOLLAR)) == 0 ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z',Expecting method,attribute or constant declaration inside class '%z'", &pGen->pIn->sData,pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } if( pGen->pIn->nType & PH7_TK_DOLLAR ){ /* Attribute declaration */ rc = GenStateCompileClassAttr(&(*pGen),iProtection,iAttrflags,pClass); if( rc != SXRET_OK ){ if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto done; } continue; } /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); }else if( nKwrd == PH7_TKWRD_ABSTRACT ){ /* Abstract method,record that */ iAttrflags |= PH7_CLASS_ATTR_ABSTRACT; /* Mark the whole class as abstract */ pClass->iFlags |= PH7_CLASS_ABSTRACT; /* Advance the stream cursor */ pGen->pIn++; if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) ){ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_PUBLIC || nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED ){ iProtection = nKwrd; pGen->pIn++; /* Jump the visibility token */ } } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_STATIC ){ /* Static method */ iAttrflags |= PH7_CLASS_ATTR_STATIC; pGen->pIn++; /* Jump the static keyword */ } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_FUNCTION ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z',Expecting method declaration after 'abstract' keyword inside class '%z'", &pGen->pIn->sData,pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } nKwrd = PH7_TKWRD_FUNCTION; }else if( nKwrd == PH7_TKWRD_FINAL ){ /* final method ,record that */ iAttrflags |= PH7_CLASS_ATTR_FINAL; pGen->pIn++; /* Jump the final keyword */ if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) ){ /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_PUBLIC || nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED ){ iProtection = nKwrd; pGen->pIn++; /* Jump the visibility token */ } } if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_STATIC ){ /* Static method */ iAttrflags |= PH7_CLASS_ATTR_STATIC; pGen->pIn++; /* Jump the static keyword */ } if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_FUNCTION ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z',Expecting method declaration after 'final' keyword inside class '%z'", &pGen->pIn->sData,pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } nKwrd = PH7_TKWRD_FUNCTION; } if( nKwrd != PH7_TKWRD_FUNCTION && nKwrd != PH7_TKWRD_VAR ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Unexpected token '%z',Expecting method declaration inside class '%z'", &pGen->pIn->sData,pName); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } if( nKwrd == PH7_TKWRD_VAR ){ pGen->pIn++; /* Jump the 'var' keyword */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Expecting attribute declaration after 'var' keyword"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } /* Attribute declaration */ rc = GenStateCompileClassAttr(&(*pGen),iProtection,iAttrflags,pClass); }else{ /* Process method declaration */ rc = GenStateCompileClassMethod(&(*pGen),iProtection,iAttrflags,TRUE,pClass); } if( rc != SXRET_OK ){ if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto done; } } }else{ /* Attribute declaration */ rc = GenStateCompileClassAttr(&(*pGen),iProtection,iAttrflags,pClass); if( rc != SXRET_OK ){ if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto done; } } } /* Install the class */ rc = PH7_VmInstallClass(pGen->pVm,pClass); if( rc == SXRET_OK ){ ph7_class **apInterface; sxu32 n; if( pBase ){ /* Inherit from base class and mark as a subclass */ rc = PH7_ClassInherit(&(*pGen),pClass,pBase); } apInterface = (ph7_class **)SySetBasePtr(&aInterfaces); for( n = 0 ; n < SySetUsed(&aInterfaces) ; n++ ){ /* Implements one or more interface */ rc = PH7_ClassImplement(pClass,apInterface[n]); if( rc != SXRET_OK ){ break; } } } SySetRelease(&aInterfaces); if( rc != SXRET_OK ){ PH7_GenCompileError(pGen,E_ERROR,nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } done: /* Point beyond the class body */ pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; return PH7_OK; } /* * Compile a user-defined abstract class. * According to the PHP language reference manual * PHP 5 introduces abstract classes and methods. Classes defined as abstract * may not be instantiated, and any class that contains at least one abstract * method must also be abstract. Methods defined as abstract simply declare * the method's signature - they cannot define the implementation. * When inheriting from an abstract class, all methods marked abstract in the parent's * class declaration must be defined by the child; additionally, these methods must be * defined with the same (or a less restricted) visibility. For example, if the abstract * method is defined as protected, the function implementation must be defined as either * protected or public, but not private. Furthermore the signatures of the methods must * match, i.e. the type hints and the number of required arguments must be the same. * This also applies to constructors as of PHP 5.4. Before 5.4 constructor signatures * could differ. */ static sxi32 PH7_CompileAbstractClass(ph7_gen_state *pGen) { sxi32 rc; pGen->pIn++; /* Jump the 'abstract' keyword */ rc = GenStateCompileClass(&(*pGen),PH7_CLASS_ABSTRACT); return rc; } /* * Compile a user-defined final class. * According to the PHP language reference manual * PHP 5 introduces the final keyword, which prevents child classes from overriding * a method by prefixing the definition with final. If the class itself is being defined * final then it cannot be extended. */ static sxi32 PH7_CompileFinalClass(ph7_gen_state *pGen) { sxi32 rc; pGen->pIn++; /* Jump the 'final' keyword */ rc = GenStateCompileClass(&(*pGen),PH7_CLASS_FINAL); return rc; } /* * Compile a user-defined class. * According to the PHP language reference manual * Basic class definitions begin with the keyword class, followed * by a class name, followed by a pair of curly braces which enclose * the definitions of the properties and methods belonging to the class. * A class may contain its own constants, variables (called "properties") * and functions (called "methods"). */ static sxi32 PH7_CompileClass(ph7_gen_state *pGen) { sxi32 rc; rc = GenStateCompileClass(&(*pGen),0); return rc; } /* * Exception handling. * According to the PHP language reference manual * An exception can be thrown, and caught ("catched") within PHP. Code may be surrounded * in a try block, to facilitate the catching of potential exceptions. Each try must have * at least one corresponding catch block. Multiple catch blocks can be used to catch * different classes of exceptions. Normal execution (when no exception is thrown within * the try block, or when a catch matching the thrown exception's class is not present) * will continue after that last catch block defined in sequence. Exceptions can be thrown * (or re-thrown) within a catch block. * When an exception is thrown, code following the statement will not be executed, and PHP * will attempt to find the first matching catch block. If an exception is not caught, a PHP * Fatal Error will be issued with an "Uncaught Exception ..." message, unless a handler has * been defined with set_exception_handler(). * The thrown object must be an instance of the Exception class or a subclass of Exception. * Trying to throw an object that is not will result in a PHP Fatal Error. */ /* * Expression tree validator callback associated with the 'throw' statement. * Return SXRET_OK if the tree form a valid expression.Any other error * indicates failure. */ static sxi32 GenStateThrowNodeValidator(ph7_gen_state *pGen,ph7_expr_node *pRoot) { sxi32 rc = SXRET_OK; if( pRoot->pOp ){ if( pRoot->pOp->iOp != EXPR_OP_SUBSCRIPT /* $a[] */ && pRoot->pOp->iOp != EXPR_OP_NEW /* new Exception() */ && pRoot->pOp->iOp != EXPR_OP_ARROW /* -> */ && pRoot->pOp->iOp != EXPR_OP_DC /* :: */){ /* Unexpected expression */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pRoot->pStart? pRoot->pStart->nLine : 0, "throw: Expecting an exception class instance"); if( rc != SXERR_ABORT ){ rc = SXERR_INVALID; } } }else if( pRoot->xCode != PH7_CompileVariable ){ /* Unexpected expression */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pRoot->pStart? pRoot->pStart->nLine : 0, "throw: Expecting an exception class instance"); if( rc != SXERR_ABORT ){ rc = SXERR_INVALID; } } return rc; } /* * Compile a 'throw' statement. * throw: This is how you trigger an exception. * Each "throw" block must have at least one "catch" block associated with it. */ static sxi32 PH7_CompileThrow(ph7_gen_state *pGen) { sxu32 nLine = pGen->pIn->nLine; GenBlock *pBlock; sxu32 nIdx; sxi32 rc; pGen->pIn++; /* Jump the 'throw' keyword */ /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,GenStateThrowNodeValidator); if( rc == SXERR_EMPTY ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"throw: Expecting an exception class instance"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXRET_OK; } pBlock = pGen->pCurrent; /* Point to the top most function or try block and emit the forward jump */ while(pBlock->pParent){ if( pBlock->iFlags & (GEN_BLOCK_EXCEPTION|GEN_BLOCK_FUNC) ){ break; } /* Point to the parent block */ pBlock = pBlock->pParent; } /* Emit the throw instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_THROW,0,0,0,&nIdx); /* Emit the jump */ GenStateNewJumpFixup(pBlock,PH7_OP_THROW,nIdx); return SXRET_OK; } /* * Compile a 'catch' block. * Catch: A "catch" block retrieves an exception and creates * an object containing the exception information. */ static sxi32 PH7_CompileCatch(ph7_gen_state *pGen,ph7_exception *pException) { sxu32 nLine = pGen->pIn->nLine; ph7_exception_block sCatch; SySet *pInstrContainer; GenBlock *pCatch; SyToken *pToken; SyString *pName; char *zDup; sxi32 rc; pGen->pIn++; /* Jump the 'catch' keyword */ /* Zero the structure */ SyZero(&sCatch,sizeof(ph7_exception_block)); /* Initialize fields */ SySetInit(&sCatch.sByteCode,&pException->pVm->sAllocator,sizeof(VmInstr)); if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 /*(*/ || &pGen->pIn[1] >= pGen->pEnd || (pGen->pIn[1].nType & (PH7_TK_ID|PH7_TK_KEYWORD)) == 0 ){ /* Unexpected token,break immediately */ pToken = pGen->pIn; if( pToken >= pGen->pEnd ){ pToken--; } rc = PH7_GenCompileError(pGen,E_ERROR,pToken->nLine, "Catch: Unexpected token '%z',excpecting class name",&pToken->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXERR_INVALID; } /* Extract the exception class */ pGen->pIn++; /* Jump the left parenthesis '(' */ /* Duplicate class name */ pName = &pGen->pIn->sData; zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,pName->zString,pName->nByte); if( zDup == 0 ){ goto Mem; } SyStringInitFromBuf(&sCatch.sClass,zDup,pName->nByte); pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 /*$*/ || &pGen->pIn[1] >= pGen->pEnd || (pGen->pIn[1].nType & (PH7_TK_ID|PH7_TK_KEYWORD)) == 0 ){ /* Unexpected token,break immediately */ pToken = pGen->pIn; if( pToken >= pGen->pEnd ){ pToken--; } rc = PH7_GenCompileError(pGen,E_ERROR,pToken->nLine, "Catch: Unexpected token '%z',expecting variable name",&pToken->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXERR_INVALID; } pGen->pIn++; /* Jump the dollar sign */ /* Duplicate instance name */ pName = &pGen->pIn->sData; zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,pName->zString,pName->nByte); if( zDup == 0 ){ goto Mem; } SyStringInitFromBuf(&sCatch.sThis,zDup,pName->nByte); pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_RPAREN) == 0 /*)*/ ){ /* Unexpected token,break immediately */ pToken = pGen->pIn; if( pToken >= pGen->pEnd ){ pToken--; } rc = PH7_GenCompileError(pGen,E_ERROR,pToken->nLine, "Catch: Unexpected token '%z',expecting right parenthesis ')'",&pToken->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXERR_INVALID; } /* Compile the block */ pGen->pIn++; /* Jump the right parenthesis */ /* Create the catch block */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_EXCEPTION,PH7_VmInstrLength(pGen->pVm),0,&pCatch); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Swap bytecode container */ pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); PH7_VmSetByteCodeContainer(pGen->pVm,&sCatch.sByteCode); /* Compile the block */ PH7_CompileBlock(&(*pGen),0); /* Fix forward jumps now the destination is resolved */ GenStateFixJumps(pCatch,-1,PH7_VmInstrLength(pGen->pVm)); /* Emit the DONE instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,0,0,0,0); /* Leave the block */ GenStateLeaveBlock(&(*pGen),0); /* Restore the default container */ PH7_VmSetByteCodeContainer(pGen->pVm,pInstrContainer); /* Install the catch block */ rc = SySetPut(&pException->sEntry,(const void *)&sCatch); if( rc != SXRET_OK ){ goto Mem; } return SXRET_OK; Mem: PH7_GenCompileError(&(*pGen),E_ERROR,nLine,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } /* * Compile a 'try' block. * A function using an exception should be in a "try" block. * If the exception does not trigger, the code will continue * as normal. However if the exception triggers, an exception * is "thrown". */ static sxi32 PH7_CompileTry(ph7_gen_state *pGen) { ph7_exception *pException; GenBlock *pTry; sxu32 nJmpIdx; sxi32 rc; /* Create the exception container */ pException = (ph7_exception *)SyMemBackendAlloc(&pGen->pVm->sAllocator,sizeof(ph7_exception)); if( pException == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR, pGen->pIn->nLine,"Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; } /* Zero the structure */ SyZero(pException,sizeof(ph7_exception)); /* Initialize fields */ SySetInit(&pException->sEntry,&pGen->pVm->sAllocator,sizeof(ph7_exception_block)); pException->pVm = pGen->pVm; /* Create the try block */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_EXCEPTION,PH7_VmInstrLength(pGen->pVm),0,&pTry); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Emit the 'LOAD_EXCEPTION' instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_LOAD_EXCEPTION,0,0,pException,&nJmpIdx); /* Fix the jump later when the destination is resolved */ GenStateNewJumpFixup(pTry,PH7_OP_LOAD_EXCEPTION,nJmpIdx); pGen->pIn++; /* Jump the 'try' keyword */ /* Compile the block */ rc = PH7_CompileBlock(&(*pGen),0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* Fix forward jumps now the destination is resolved */ GenStateFixJumps(pTry,-1,PH7_VmInstrLength(pGen->pVm)); /* Emit the 'POP_EXCEPTION' instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP_EXCEPTION,0,0,pException,0); /* Leave the block */ GenStateLeaveBlock(&(*pGen),0); /* Compile the catch block */ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_CATCH ){ SyToken *pTok = pGen->pIn; if( pTok >= pGen->pEnd ){ pTok--; /* Point back */ } /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pTok->nLine, "Try: Unexpected token '%z',expecting 'catch' block",&pTok->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXRET_OK; } /* Compile one or more catch blocks */ for(;;){ if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_CATCH ){ /* No more blocks */ break; } /* Compile the catch block */ rc = PH7_CompileCatch(&(*pGen),pException); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return SXRET_OK; } /* * Compile a switch block. * (See block-comment below for more information) */ static sxi32 GenStateCompileSwitchBlock(ph7_gen_state *pGen,sxu32 iTokenDelim,sxu32 *pBlockStart) { sxi32 rc = SXRET_OK; while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/|PH7_TK_COLON/*':'*/)) == 0 ){ /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Unexpected token '%z'",&pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } pGen->pIn++; } pGen->pIn++; /* First instruction to execute in this block. */ *pBlockStart = PH7_VmInstrLength(pGen->pVm); /* Compile the block until we hit a case/default/endswitch keyword * or the '}' token */ for(;;){ if( pGen->pIn >= pGen->pEnd ){ /* No more input to process */ break; } rc = SXRET_OK; if( (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ){ if( pGen->pIn->nType & PH7_TK_CCB /*'}' */ ){ if( iTokenDelim != PH7_TK_CCB ){ /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Unexpected token '%z'", &pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* FALL THROUGH */ } rc = SXERR_EOF; break; } }else{ sxi32 nKwrd; /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_CASE || nKwrd == PH7_TKWRD_DEFAULT ){ break; } if( nKwrd == PH7_TKWRD_ENDSWITCH /* endswitch; */){ if( iTokenDelim != PH7_TK_KEYWORD ){ /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Unexpected token '%z'", &pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* FALL THROUGH */ } /* Block compiled */ break; } } /* Compile block */ rc = PH7_CompileBlock(&(*pGen),0); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } return rc; } /* * Compile a case eXpression. * (See block-comment below for more information) */ static sxi32 GenStateCompileCaseExpr(ph7_gen_state *pGen,ph7_case_expr *pExpr) { SySet *pInstrContainer; SyToken *pEnd,*pTmp; sxi32 iNest = 0; sxi32 rc; /* Delimit the expression */ pEnd = pGen->pIn; while( pEnd < pGen->pEnd ){ if( pEnd->nType & PH7_TK_LPAREN /*(*/ ){ /* Increment nesting level */ iNest++; }else if( pEnd->nType & PH7_TK_RPAREN /*)*/ ){ /* Decrement nesting level */ iNest--; }else if( pEnd->nType & (PH7_TK_SEMI/*';'*/|PH7_TK_COLON/*;'*/) && iNest < 1 ){ break; } pEnd++; } if( pGen->pIn >= pEnd ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine,"Empty case expression"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } } /* Swap token stream */ pTmp = pGen->pEnd; pGen->pEnd = pEnd; pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); PH7_VmSetByteCodeContainer(pGen->pVm,&pExpr->aByteCode); rc = PH7_CompileExpr(&(*pGen),0,0); /* Emit the done instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,(rc != SXERR_EMPTY ? 1 : 0),0,0,0); PH7_VmSetByteCodeContainer(pGen->pVm,pInstrContainer); /* Update token stream */ pGen->pIn = pEnd; pGen->pEnd = pTmp; if( rc == SXERR_ABORT ){ return SXERR_ABORT; } return SXRET_OK; } /* * Compile the smart switch statement. * According to the PHP language reference manual * The switch statement is similar to a series of IF statements on the same expression. * In many occasions, you may want to compare the same variable (or expression) with many * different values, and execute a different piece of code depending on which value it equals to. * This is exactly what the switch statement is for. * Note: Note that unlike some other languages, the continue statement applies to switch and acts * similar to break. If you have a switch inside a loop and wish to continue to the next iteration * of the outer loop, use continue 2. * Note that switch/case does loose comparision. * It is important to understand how the switch statement is executed in order to avoid mistakes. * The switch statement executes line by line (actually, statement by statement). * In the beginning, no code is executed. Only when a case statement is found with a value that * matches the value of the switch expression does PHP begin to execute the statements. * PHP continues to execute the statements until the end of the switch block, or the first time * it sees a break statement. If you don't write a break statement at the end of a case's statement list. * In a switch statement, the condition is evaluated only once and the result is compared to each * case statement. In an elseif statement, the condition is evaluated again. If your condition * is more complicated than a simple compare and/or is in a tight loop, a switch may be faster. * The statement list for a case can also be empty, which simply passes control into the statement * list for the next case. * The case expression may be any expression that evaluates to a simple type, that is, integer * or floating-point numbers and strings. */ static sxi32 PH7_CompileSwitch(ph7_gen_state *pGen) { GenBlock *pSwitchBlock; SyToken *pTmp,*pEnd; ph7_switch *pSwitch; sxu32 nToken; sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; /* Jump the 'switch' keyword */ pGen->pIn++; if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 ){ /* Syntax error */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected '(' after 'switch' keyword"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto Synchronize; } /* Jump the left parenthesis '(' */ pGen->pIn++; pEnd = 0; /* cc warning */ /* Create the loop block */ rc = GenStateEnterBlock(&(*pGen),GEN_BLOCK_LOOP|GEN_BLOCK_SWITCH, PH7_VmInstrLength(pGen->pVm),0,&pSwitchBlock); if( rc != SXRET_OK ){ return SXERR_ABORT; } /* Delimit the condition */ PH7_DelimitNestedTokens(pGen->pIn,pGen->pEnd,PH7_TK_LPAREN /* '(' */,PH7_TK_RPAREN /* ')' */,&pEnd); if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){ /* Empty expression */ rc = PH7_GenCompileError(pGen,E_ERROR,nLine,"Expected expression after 'switch' keyword"); if( rc == SXERR_ABORT ){ /* Error count limit reached,abort immediately */ return SXERR_ABORT; } } /* Swap token streams */ pTmp = pGen->pEnd; pGen->pEnd = pEnd; /* Compile the expression */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc == SXERR_ABORT ){ /* Expression handler request an operation abort [i.e: Out-of-memory] */ return SXERR_ABORT; } /* Update token stream */ while(pGen->pIn < pEnd ){ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine, "Switch: Unexpected token '%z'",&pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } pGen->pIn++; } pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; if( pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_OCB/*'{'*/|PH7_TK_COLON/*:*/)) == 0 ){ pTmp = pGen->pIn; if( pTmp >= pGen->pEnd ){ pTmp--; } /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pTmp->nLine,"Switch: Unexpected token '%z'",&pTmp->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } goto Synchronize; } /* Set the delimiter token */ if( pGen->pIn->nType & PH7_TK_COLON ){ nToken = PH7_TK_KEYWORD; /* Stop compilation when the 'endswitch;' keyword is seen */ }else{ nToken = PH7_TK_CCB; /* '}' */ } pGen->pIn++; /* Jump the leading curly braces/colons */ /* Create the switch blocks container */ pSwitch = (ph7_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator,sizeof(ph7_switch)); if( pSwitch == 0 ){ /* Abort compilation */ PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Fatal, PH7 is running out of memory"); return SXERR_ABORT; } /* Zero the structure */ SyZero(pSwitch,sizeof(ph7_switch)); /* Initialize fields */ SySetInit(&pSwitch->aCaseExpr,&pGen->pVm->sAllocator,sizeof(ph7_case_expr)); /* Emit the switch instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_SWITCH,0,0,pSwitch,0); /* Compile case blocks */ for(;;){ sxu32 nKwrd; if( pGen->pIn >= pGen->pEnd ){ /* No more input to process */ break; } if( (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ){ if( nToken != PH7_TK_CCB || (pGen->pIn->nType & PH7_TK_CCB /*}*/) == 0 ){ /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Switch: Unexpected token '%z'", &pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* FALL THROUGH */ } /* Block compiled */ break; } /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); if( nKwrd == PH7_TKWRD_ENDSWITCH /* endswitch; */){ if( nToken != PH7_TK_KEYWORD ){ /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Switch: Unexpected token '%z'", &pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* FALL THROUGH */ } /* Block compiled */ break; } if( nKwrd == PH7_TKWRD_DEFAULT ){ /* * Accroding to the PHP language reference manual * A special case is the default case. This case matches anything * that wasn't matched by the other cases. */ if( pSwitch->nDefault > 0 ){ /* Default case already compiled */ rc = PH7_GenCompileError(&(*pGen),E_WARNING,pGen->pIn->nLine,"Switch: 'default' case already compiled"); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } } pGen->pIn++; /* Jump the 'default' keyword */ /* Compile the default block */ rc = GenStateCompileSwitchBlock(pGen,nToken,&pSwitch->nDefault); if( rc == SXERR_ABORT){ return SXERR_ABORT; }else if( rc == SXERR_EOF ){ break; } }else if( nKwrd == PH7_TKWRD_CASE ){ ph7_case_expr sCase; /* Standard case block */ pGen->pIn++; /* Jump the 'case' keyword */ /* initialize the structure */ SySetInit(&sCase.aByteCode,&pGen->pVm->sAllocator,sizeof(VmInstr)); /* Compile the case expression */ rc = GenStateCompileCaseExpr(pGen,&sCase); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } /* Compile the case block */ rc = GenStateCompileSwitchBlock(pGen,nToken,&sCase.nStart); /* Insert in the switch container */ SySetPut(&pSwitch->aCaseExpr,(const void *)&sCase); if( rc == SXERR_ABORT){ return SXERR_ABORT; }else if( rc == SXERR_EOF ){ break; } }else{ /* Unexpected token */ rc = PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"Switch: Unexpected token '%z'", &pGen->pIn->sData); if( rc == SXERR_ABORT ){ return SXERR_ABORT; } break; } } /* Fix all jumps now the destination is resolved */ pSwitch->nOut = PH7_VmInstrLength(pGen->pVm); GenStateFixJumps(pSwitchBlock,-1,PH7_VmInstrLength(pGen->pVm)); /* Release the loop block */ GenStateLeaveBlock(pGen,0); if( pGen->pIn < pGen->pEnd ){ /* Jump the trailing curly braces or the endswitch keyword*/ pGen->pIn++; } /* Statement successfully compiled */ return SXRET_OK; Synchronize: /* Synchronize with the first semi-colon */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0 ){ pGen->pIn++; } return SXRET_OK; } /* * Generate bytecode for a given expression tree. * If something goes wrong while generating bytecode * for the expression tree (A very unlikely scenario) * this function takes care of generating the appropriate * error message. */ static sxi32 GenStateEmitExprCode( ph7_gen_state *pGen, /* Code generator state */ ph7_expr_node *pNode, /* Root of the expression tree */ sxi32 iFlags /* Control flags */ ) { VmInstr *pInstr; sxu32 nJmpIdx; sxi32 iP1 = 0; sxu32 iP2 = 0; void *p3 = 0; sxi32 iVmOp; sxi32 rc; if( pNode->xCode ){ SyToken *pTmpIn,*pTmpEnd; /* Compile node */ SWAP_DELIMITER(pGen,pNode->pStart,pNode->pEnd); rc = pNode->xCode(&(*pGen),iFlags); RE_SWAP_DELIMITER(pGen); return rc; } if( pNode->pOp == 0 ){ PH7_GenCompileError(&(*pGen),E_ERROR,pNode->pStart->nLine, "Invalid expression node,PH7 is aborting compilation"); return SXERR_ABORT; } iVmOp = pNode->pOp->iVmOp; if( pNode->pOp->iOp == EXPR_OP_QUESTY ){ sxu32 nJz,nJmp; /* Ternary operator require special handling */ /* Phase#1: Compile the condition */ rc = GenStateEmitExprCode(&(*pGen),pNode->pCond,iFlags); if( rc != SXRET_OK ){ return rc; } nJz = nJmp = 0; /* cc -O6 warning */ /* Phase#2: Emit the false jump */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JZ,0,0,0,&nJz); if( pNode->pLeft ){ /* Phase#3: Compile the 'then' expression */ rc = GenStateEmitExprCode(&(*pGen),pNode->pLeft,iFlags); if( rc != SXRET_OK ){ return rc; } } /* Phase#4: Emit the unconditional jump */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JMP,0,0,0,&nJmp); /* Phase#5: Fix the false jump now the jump destination is resolved. */ pInstr = PH7_VmGetInstr(pGen->pVm,nJz); if( pInstr ){ pInstr->iP2 = PH7_VmInstrLength(pGen->pVm); } /* Phase#6: Compile the 'else' expression */ if( pNode->pRight ){ rc = GenStateEmitExprCode(&(*pGen),pNode->pRight,iFlags); if( rc != SXRET_OK ){ return rc; } } if( nJmp > 0 ){ /* Phase#7: Fix the unconditional jump */ pInstr = PH7_VmGetInstr(pGen->pVm,nJmp); if( pInstr ){ pInstr->iP2 = PH7_VmInstrLength(pGen->pVm); } } /* All done */ return SXRET_OK; } /* Generate code for the left tree */ if( pNode->pLeft ){ if( iVmOp == PH7_OP_CALL ){ ph7_expr_node **apNode; sxi32 n; /* Recurse and generate bytecodes for function arguments */ apNode = (ph7_expr_node **)SySetBasePtr(&pNode->aNodeArgs); /* Read-only load */ iFlags |= EXPR_FLAG_RDONLY_LOAD; for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){ rc = GenStateEmitExprCode(&(*pGen),apNode[n],iFlags&~EXPR_FLAG_LOAD_IDX_STORE); if( rc != SXRET_OK ){ return rc; } } /* Total number of given arguments */ iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs); /* Remove stale flags now */ iFlags &= ~EXPR_FLAG_RDONLY_LOAD; } rc = GenStateEmitExprCode(&(*pGen),pNode->pLeft,iFlags); if( rc != SXRET_OK ){ return rc; } if( iVmOp == PH7_OP_CALL ){ pInstr = PH7_VmPeekInstr(pGen->pVm); if( pInstr ){ if ( pInstr->iOp == PH7_OP_LOADC ){ /* Prevent constant expansion */ pInstr->iP1 = 0; }else if( pInstr->iOp == PH7_OP_MEMBER /* $a->b(1,2,3) */ || pInstr->iOp == PH7_OP_NEW ){ /* Method call,flag that */ pInstr->iP2 = 1; } } }else if( iVmOp == PH7_OP_LOAD_IDX ){ ph7_expr_node **apNode; sxi32 n; /* Recurse and generate bytecodes for array index */ apNode = (ph7_expr_node **)SySetBasePtr(&pNode->aNodeArgs); for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){ rc = GenStateEmitExprCode(&(*pGen),apNode[n],iFlags&~EXPR_FLAG_LOAD_IDX_STORE); if( rc != SXRET_OK ){ return rc; } } if( SySetUsed(&pNode->aNodeArgs) > 0 ){ iP1 = 1; /* Node have an index associated with it */ } if( iFlags & EXPR_FLAG_LOAD_IDX_STORE ){ /* Create an empty entry when the desired index is not found */ iP2 = 1; } }else if( pNode->pOp->iOp == EXPR_OP_COMMA ){ /* POP the left node */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP,1,0,0,0); } } rc = SXRET_OK; nJmpIdx = 0; /* Generate code for the right tree */ if( pNode->pRight ){ if( iVmOp == PH7_OP_LAND ){ /* Emit the false jump so we can short-circuit the logical and */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JZ,1/* Keep the value on the stack */,0,0,&nJmpIdx); }else if (iVmOp == PH7_OP_LOR ){ /* Emit the true jump so we can short-circuit the logical or*/ PH7_VmEmitInstr(pGen->pVm,PH7_OP_JNZ,1/* Keep the value on the stack */,0,0,&nJmpIdx); }else if( pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =,'.=','+=',*=' ...] precedence */ ){ iFlags |= EXPR_FLAG_LOAD_IDX_STORE; } rc = GenStateEmitExprCode(&(*pGen),pNode->pRight,iFlags); if( iVmOp == PH7_OP_STORE ){ pInstr = PH7_VmPeekInstr(pGen->pVm); if( pInstr ){ if( pInstr->iOp == PH7_OP_LOAD_LIST ){ /* Hide the STORE instruction */ iVmOp = 0; }else if(pInstr->iOp == PH7_OP_MEMBER ){ /* Perform a member store operation [i.e: $this->x = 50] */ iP2 = 1; }else{ if( pInstr->iOp == PH7_OP_LOAD_IDX ){ /* Transform the STORE instruction to STORE_IDX instruction */ iVmOp = PH7_OP_STORE_IDX; iP1 = pInstr->iP1; }else{ p3 = pInstr->p3; } /* POP the last dynamic load instruction */ (void)PH7_VmPopInstr(pGen->pVm); } } }else if( iVmOp == PH7_OP_STORE_REF ){ pInstr = PH7_VmPopInstr(pGen->pVm); if( pInstr ){ if( pInstr->iOp == PH7_OP_LOAD_IDX ){ /* Array insertion by reference [i.e: $pArray[] =& $some_var; ] * We have to convert the STORE_REF instruction into STORE_IDX_REF */ iVmOp = PH7_OP_STORE_IDX_REF; iP1 = pInstr->iP1; iP2 = pInstr->iP2; p3 = pInstr->p3; }else{ p3 = pInstr->p3; } } } } if( iVmOp > 0 ){ if( iVmOp == PH7_OP_INCR || iVmOp == PH7_OP_DECR ){ if( pNode->iFlags & EXPR_NODE_PRE_INCR ){ /* Pre-increment/decrement operator [i.e: ++$i,--$j ] */ iP1 = 1; } }else if( iVmOp == PH7_OP_NEW ){ pInstr = PH7_VmPeekInstr(pGen->pVm); if( pInstr && pInstr->iOp == PH7_OP_CALL ){ VmInstr *pPrev; pPrev = PH7_VmPeekNextInstr(pGen->pVm); if( pPrev == 0 || pPrev->iOp != PH7_OP_MEMBER ){ /* Pop the call instruction */ iP1 = pInstr->iP1; (void)PH7_VmPopInstr(pGen->pVm); } } }else if( iVmOp == PH7_OP_MEMBER){ if( pNode->pOp->iOp == EXPR_OP_DC /* '::' */){ /* Static member access,remember that */ iP1 = 1; pInstr = PH7_VmPeekInstr(pGen->pVm); if( pInstr && pInstr->iOp == PH7_OP_LOAD ){ p3 = pInstr->p3; (void)PH7_VmPopInstr(pGen->pVm); } } } /* Finally,emit the VM instruction associated with this operator */ PH7_VmEmitInstr(pGen->pVm,iVmOp,iP1,iP2,p3,0); if( nJmpIdx > 0 ){ /* Fix short-circuited jumps now the destination is resolved */ pInstr = PH7_VmGetInstr(pGen->pVm,nJmpIdx); if( pInstr ){ pInstr->iP2 = PH7_VmInstrLength(pGen->pVm); } } } return rc; } /* * Compile a PHP expression. * According to the PHP language reference manual: * Expressions are the most important building stones of PHP. * In PHP, almost anything you write is an expression. * The simplest yet most accurate way to define an expression * is "anything that has a value". * If something goes wrong while compiling the expression,this * function takes care of generating the appropriate error * message. */ static sxi32 PH7_CompileExpr( ph7_gen_state *pGen, /* Code generator state */ sxi32 iFlags, /* Control flags */ sxi32 (*xTreeValidator)(ph7_gen_state *,ph7_expr_node *) /* Node validator callback.NULL otherwise */ ) { ph7_expr_node *pRoot; SySet sExprNode; SyToken *pEnd; sxi32 nExpr; sxi32 iNest; sxi32 rc; /* Initialize worker variables */ nExpr = 0; pRoot = 0; SySetInit(&sExprNode,&pGen->pVm->sAllocator,sizeof(ph7_expr_node *)); SySetAlloc(&sExprNode,0x10); rc = SXRET_OK; /* Delimit the expression */ pEnd = pGen->pIn; iNest = 0; while( pEnd < pGen->pEnd ){ if( pEnd->nType & PH7_TK_OCB /* '{' */ ){ /* Ticket 1433-30: Annonymous/Closure functions body */ iNest++; }else if(pEnd->nType & PH7_TK_CCB /* '}' */ ){ iNest--; }else if( pEnd->nType & PH7_TK_SEMI /* ';' */ ){ if( iNest <= 0 ){ break; } } pEnd++; } if( iFlags & EXPR_FLAG_COMMA_STATEMENT ){ SyToken *pEnd2 = pGen->pIn; iNest = 0; /* Stop at the first comma */ while( pEnd2 < pEnd ){ if( pEnd2->nType & (PH7_TK_OCB/*'{'*/|PH7_TK_OSB/*'['*/|PH7_TK_LPAREN/*'('*/) ){ iNest++; }else if(pEnd2->nType & (PH7_TK_CCB/*'}'*/|PH7_TK_CSB/*']'*/|PH7_TK_RPAREN/*')'*/)){ iNest--; }else if( pEnd2->nType & PH7_TK_COMMA /*','*/ ){ if( iNest <= 0 ){ break; } } pEnd2++; } if( pEnd2 pGen->pIn ){ SyToken *pTmp = pGen->pEnd; /* Swap delimiter */ pGen->pEnd = pEnd; /* Try to get an expression tree */ rc = PH7_ExprMakeTree(&(*pGen),&sExprNode,&pRoot); if( rc == SXRET_OK && pRoot ){ rc = SXRET_OK; if( xTreeValidator ){ /* Call the upper layer validator callback */ rc = xTreeValidator(&(*pGen),pRoot); } if( rc != SXERR_ABORT ){ /* Generate code for the given tree */ rc = GenStateEmitExprCode(&(*pGen),pRoot,iFlags); } nExpr = 1; } /* Release the whole tree */ PH7_ExprFreeTree(&(*pGen),&sExprNode); /* Synchronize token stream */ pGen->pEnd = pTmp; pGen->pIn = pEnd; if( rc == SXERR_ABORT ){ SySetRelease(&sExprNode); return SXERR_ABORT; } } SySetRelease(&sExprNode); return nExpr > 0 ? SXRET_OK : SXERR_EMPTY; } /* * Return a pointer to the node construct handler associated * with a given node type [i.e: string,integer,float,...]. */ PH7_PRIVATE ProcNodeConstruct PH7_GetNodeHandler(sxu32 nNodeType) { if( nNodeType & PH7_TK_NUM ){ /* Numeric literal: Either real or integer */ return PH7_CompileNumLiteral; }else if( nNodeType & PH7_TK_DSTR ){ /* Double quoted string */ return PH7_CompileString; }else if( nNodeType & PH7_TK_SSTR ){ /* Single quoted string */ return PH7_CompileSimpleString; }else if( nNodeType & PH7_TK_HEREDOC ){ /* Heredoc */ return PH7_CompileHereDoc; }else if( nNodeType & PH7_TK_NOWDOC ){ /* Nowdoc */ return PH7_CompileNowDoc; }else if( nNodeType & PH7_TK_BSTR ){ /* Backtick quoted string */ return PH7_CompileBacktic; } return 0; } /* * PHP Language construct table. */ static const LangConstruct aLangConstruct[] = { { PH7_TKWRD_ECHO, PH7_CompileEcho }, /* echo language construct */ { PH7_TKWRD_IF, PH7_CompileIf }, /* if statement */ { PH7_TKWRD_FOR, PH7_CompileFor }, /* for statement */ { PH7_TKWRD_WHILE, PH7_CompileWhile }, /* while statement */ { PH7_TKWRD_FOREACH, PH7_CompileForeach }, /* foreach statement */ { PH7_TKWRD_FUNCTION, PH7_CompileFunction }, /* function statement */ { PH7_TKWRD_CONTINUE, PH7_CompileContinue }, /* continue statement */ { PH7_TKWRD_BREAK, PH7_CompileBreak }, /* break statement */ { PH7_TKWRD_RETURN, PH7_CompileReturn }, /* return statement */ { PH7_TKWRD_SWITCH, PH7_CompileSwitch }, /* Switch statement */ { PH7_TKWRD_DO, PH7_CompileDoWhile }, /* do{ }while(); statement */ { PH7_TKWRD_GLOBAL, PH7_CompileGlobal }, /* global statement */ { PH7_TKWRD_STATIC, PH7_CompileStatic }, /* static statement */ { PH7_TKWRD_DIE, PH7_CompileHalt }, /* die language construct */ { PH7_TKWRD_EXIT, PH7_CompileHalt }, /* exit language construct */ { PH7_TKWRD_TRY, PH7_CompileTry }, /* try statement */ { PH7_TKWRD_THROW, PH7_CompileThrow }, /* throw statement */ { PH7_TKWRD_GOTO, PH7_CompileGoto }, /* goto statement */ { PH7_TKWRD_CONST, PH7_CompileConstant }, /* const statement */ { PH7_TKWRD_VAR, PH7_CompileVar }, /* var statement */ { PH7_TKWRD_NAMESPACE, PH7_CompileNamespace }, /* namespace statement */ { PH7_TKWRD_USE, PH7_CompileUse }, /* use statement */ { PH7_TKWRD_DECLARE, PH7_CompileDeclare } /* declare statement */ }; /* * Return a pointer to the statement handler routine associated * with a given PHP keyword [i.e: if,for,while,...]. */ static ProcLangConstruct GenStateGetStatementHandler( sxu32 nKeywordID, /* Keyword ID*/ SyToken *pLookahed /* Look-ahead token */ ) { sxu32 n = 0; for(;;){ if( n >= SX_ARRAYSIZE(aLangConstruct) ){ break; } if( aLangConstruct[n].nID == nKeywordID ){ if( nKeywordID == PH7_TKWRD_STATIC && pLookahed && (pLookahed->nType & PH7_TK_OP)){ const ph7_expr_op *pOp = (const ph7_expr_op *)pLookahed->pUserData; if( pOp && pOp->iOp == EXPR_OP_DC /*::*/){ /* 'static' (class context),return null */ return 0; } } /* Return a pointer to the handler. */ return aLangConstruct[n].xConstruct; } n++; } if( pLookahed ){ if(nKeywordID == PH7_TKWRD_INTERFACE && (pLookahed->nType & PH7_TK_ID) ){ return PH7_CompileClassInterface; }else if(nKeywordID == PH7_TKWRD_CLASS && (pLookahed->nType & PH7_TK_ID) ){ return PH7_CompileClass; }else if( nKeywordID == PH7_TKWRD_ABSTRACT && (pLookahed->nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pLookahed->pUserData) == PH7_TKWRD_CLASS ){ return PH7_CompileAbstractClass; }else if( nKeywordID == PH7_TKWRD_FINAL && (pLookahed->nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pLookahed->pUserData) == PH7_TKWRD_CLASS ){ return PH7_CompileFinalClass; } } /* Not a language construct */ return 0; } /* * Check if the given keyword is in fact a PHP language construct. * Return TRUE on success. FALSE otheriwse. */ static int GenStateisLangConstruct(sxu32 nKeyword) { int rc; rc = PH7_IsLangConstruct(nKeyword,TRUE); if( rc == FALSE ){ if( nKeyword == PH7_TKWRD_SELF || nKeyword == PH7_TKWRD_PARENT || nKeyword == PH7_TKWRD_STATIC /*|| nKeyword == PH7_TKWRD_CLASS || nKeyword == PH7_TKWRD_FINAL || nKeyword == PH7_TKWRD_EXTENDS || nKeyword == PH7_TKWRD_ABSTRACT || nKeyword == PH7_TKWRD_INTERFACE || nKeyword == PH7_TKWRD_PUBLIC || nKeyword == PH7_TKWRD_PROTECTED || nKeyword == PH7_TKWRD_PRIVATE || nKeyword == PH7_TKWRD_IMPLEMENTS */ ){ rc = TRUE; } } return rc; } /* * Compile a PHP chunk. * If something goes wrong while compiling the PHP chunk,this function * takes care of generating the appropriate error message. */ static sxi32 GenStateCompileChunk( ph7_gen_state *pGen, /* Code generator state */ sxi32 iFlags /* Compile flags */ ) { ProcLangConstruct xCons; sxi32 rc; rc = SXRET_OK; /* Prevent compiler warning */ for(;;){ if( pGen->pIn >= pGen->pEnd ){ /* No more input to process */ break; } if( pGen->pIn->nType & PH7_TK_OCB /* '{' */ ){ /* Compile block */ rc = PH7_CompileBlock(&(*pGen),0); if( rc == SXERR_ABORT ){ break; } }else{ xCons = 0; if( pGen->pIn->nType & PH7_TK_KEYWORD ){ sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData); /* Try to extract a language construct handler */ xCons = GenStateGetStatementHandler(nKeyword,(&pGen->pIn[1] < pGen->pEnd) ? &pGen->pIn[1] : 0); if( xCons == 0 && GenStateisLangConstruct(nKeyword) == FALSE ){ rc = PH7_GenCompileError(pGen,E_ERROR,pGen->pIn->nLine, "Syntax error: Unexpected keyword '%z'", &pGen->pIn->sData); if( rc == SXERR_ABORT ){ break; } /* Synchronize with the first semi-colon and avoid compiling * this erroneous statement. */ xCons = PH7_ErrorRecover; } }else if( (pGen->pIn->nType & PH7_TK_ID) && (&pGen->pIn[1] < pGen->pEnd) && (pGen->pIn[1].nType & PH7_TK_COLON /*':'*/) ){ /* Label found [i.e: Out: ],point to the routine responsible of compiling it */ xCons = PH7_CompileLabel; } if( xCons == 0 ){ /* Assume an expression an try to compile it */ rc = PH7_CompileExpr(&(*pGen),0,0); if( rc != SXERR_EMPTY ){ /* Pop l-value */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP,1,0,0,0); } }else{ /* Go compile the sucker */ rc = xCons(&(*pGen)); } if( rc == SXERR_ABORT ){ /* Request to abort compilation */ break; } } /* Ignore trailing semi-colons ';' */ while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) ){ pGen->pIn++; } if( iFlags & PH7_COMPILE_SINGLE_STMT ){ /* Compile a single statement and return */ break; } /* LOOP ONE */ /* LOOP TWO */ /* LOOP THREE */ /* LOOP FOUR */ } /* Return compilation status */ return rc; } /* * Compile a Raw PHP chunk. * If something goes wrong while compiling the PHP chunk,this function * takes care of generating the appropriate error message. */ static sxi32 PH7_CompilePHP( ph7_gen_state *pGen, /* Code generator state */ SySet *pTokenSet, /* Token set */ int is_expr /* TRUE if we are dealing with a simple expression */ ) { SyToken *pScript = pGen->pRawIn; /* Script to compile */ sxi32 rc; /* Reset the token set */ SySetReset(&(*pTokenSet)); /* Mark as the default token set */ pGen->pTokenSet = &(*pTokenSet); /* Advance the stream cursor */ pGen->pRawIn++; /* Tokenize the PHP chunk first */ PH7_TokenizePHP(SyStringData(&pScript->sData),SyStringLength(&pScript->sData),pScript->nLine,&(*pTokenSet)); /* Point to the head and tail of the token stream. */ pGen->pIn = (SyToken *)SySetBasePtr(pTokenSet); pGen->pEnd = &pGen->pIn[SySetUsed(pTokenSet)]; if( is_expr ){ rc = SXERR_EMPTY; if( pGen->pIn < pGen->pEnd ){ /* A simple expression,compile it */ rc = PH7_CompileExpr(pGen,0,0); } /* Emit the DONE instruction */ PH7_VmEmitInstr(pGen->pVm,PH7_OP_DONE,(rc != SXERR_EMPTY ? 1 : 0),0,0,0); return SXRET_OK; } if( pGen->pIn < pGen->pEnd && ( pGen->pIn->nType & PH7_TK_EQUAL ) ){ static const sxu32 nKeyID = PH7_TKWRD_ECHO; /* * Shortcut syntax for the 'echo' language construct. * According to the PHP reference manual: * echo() also has a shortcut syntax, where you can * immediately follow * the opening tag with an equals sign as follows: * is the same as * Symisc extension: * This short syntax works with all PHP opening * tags unlike the default PHP engine that handle * only short tag. */ /* Ticket 1433-009: Emulate the 'echo' call */ pGen->pIn->nType = PH7_TK_KEYWORD; pGen->pIn->pUserData = SX_INT_TO_PTR(nKeyID); SyStringInitFromBuf(&pGen->pIn->sData,"echo",sizeof("echo")-1); rc = PH7_CompileExpr(pGen,0,0); if( rc != SXERR_EMPTY ){ PH7_VmEmitInstr(pGen->pVm,PH7_OP_POP,1,0,0,0); } return SXRET_OK; } /* Compile the PHP chunk */ rc = GenStateCompileChunk(pGen,0); /* Fix exceptions jumps */ GenStateFixJumps(pGen->pCurrent,PH7_OP_THROW,PH7_VmInstrLength(pGen->pVm)); /* Fix gotos now, the jump destination is resolved */ if( SXERR_ABORT == GenStateFixGoto(&(*pGen),0) ){ rc = SXERR_ABORT; } /* Reset container */ SySetReset(&pGen->aGoto); SySetReset(&pGen->aLabel); /* Compilation result */ return rc; } /* * Compile a raw chunk. The raw chunk can contain PHP code embedded * in HTML, XML and so on. This function handle all the stuff. * This is the only compile interface exported from this file. */ PH7_PRIVATE sxi32 PH7_CompileScript( ph7_vm *pVm, /* Generate PH7 byte-codes for this Virtual Machine */ SyString *pScript, /* Script to compile */ sxi32 iFlags /* Compile flags */ ) { SySet aPhpToken,aRawToken; ph7_gen_state *pCodeGen; ph7_value *pRawObj; sxu32 nObjIdx; sxi32 nRawObj; int is_expr; sxi32 rc; if( pScript->nByte < 1 ){ /* Nothing to compile */ return PH7_OK; } /* Initialize the tokens containers */ SySetInit(&aRawToken,&pVm->sAllocator,sizeof(SyToken)); SySetInit(&aPhpToken,&pVm->sAllocator,sizeof(SyToken)); SySetAlloc(&aPhpToken,0xc0); is_expr = 0; if( iFlags & PH7_PHP_ONLY ){ SyToken sTmp; /* PHP only: -*/ sTmp.nLine = 1; sTmp.nType = PH7_TOKEN_PHP; sTmp.pUserData = 0; SyStringDupPtr(&sTmp.sData,pScript); SySetPut(&aRawToken,(const void *)&sTmp); if( iFlags & PH7_PHP_EXPR ){ /* A simple PHP expression */ is_expr = 1; } }else{ /* Tokenize raw text */ SySetAlloc(&aRawToken,32); PH7_TokenizeRawText(pScript->zString,pScript->nByte,&aRawToken); } pCodeGen = &pVm->sCodeGen; /* Process high-level tokens */ pCodeGen->pRawIn = (SyToken *)SySetBasePtr(&aRawToken); pCodeGen->pRawEnd = &pCodeGen->pRawIn[SySetUsed(&aRawToken)]; rc = PH7_OK; if( is_expr ){ /* Compile the expression */ rc = PH7_CompilePHP(pCodeGen,&aPhpToken,TRUE); goto cleanup; } nObjIdx = 0; /* Start the compilation process */ for(;;){ if( pCodeGen->pRawIn >= pCodeGen->pRawEnd ){ break; /* No more tokens to process */ } if( pCodeGen->pRawIn->nType & PH7_TOKEN_PHP ){ /* Compile the PHP chunk */ rc = PH7_CompilePHP(pCodeGen,&aPhpToken,FALSE); if( rc == SXERR_ABORT ){ break; } continue; } /* Raw chunk: [i.e: HTML, XML, etc.] */ nRawObj = 0; while( (pCodeGen->pRawIn < pCodeGen->pRawEnd) && (pCodeGen->pRawIn->nType != PH7_TOKEN_PHP) ){ /* Consume the raw chunk without any processing */ pRawObj = PH7_ReserveConstObj(&(*pVm),&nObjIdx); if( pRawObj == 0 ){ rc = SXERR_MEM; break; } /* Mark as constant and emit the load constant instruction */ PH7_MemObjInitFromString(pVm,pRawObj,&pCodeGen->pRawIn->sData); PH7_VmEmitInstr(&(*pVm),PH7_OP_LOADC,0,nObjIdx,0,0); ++nRawObj; pCodeGen->pRawIn++; /* Next chunk */ } if( nRawObj > 0 ){ /* Emit the consume instruction */ PH7_VmEmitInstr(&(*pVm),PH7_OP_CONSUME,nRawObj,0,0,0); } } cleanup: SySetRelease(&aRawToken); SySetRelease(&aPhpToken); return rc; } /* * Utility routines.Initialize the code generator. */ PH7_PRIVATE sxi32 PH7_InitCodeGenerator( ph7_vm *pVm, /* Target VM */ ProcConsumer xErr, /* Error log consumer callabck */ void *pErrData /* Last argument to xErr() */ ) { ph7_gen_state *pGen = &pVm->sCodeGen; /* Zero the structure */ SyZero(pGen,sizeof(ph7_gen_state)); /* Initial state */ pGen->pVm = &(*pVm); pGen->xErr = xErr; pGen->pErrData = pErrData; SySetInit(&pGen->aLabel,&pVm->sAllocator,sizeof(Label)); SySetInit(&pGen->aGoto,&pVm->sAllocator,sizeof(JumpFixup)); SyHashInit(&pGen->hLiteral,&pVm->sAllocator,0,0); SyHashInit(&pGen->hVar,&pVm->sAllocator,0,0); /* Error log buffer */ SyBlobInit(&pGen->sErrBuf,&pVm->sAllocator); /* General purpose working buffer */ SyBlobInit(&pGen->sWorker,&pVm->sAllocator); /* Create the global scope */ GenStateInitBlock(pGen,&pGen->sGlobal,GEN_BLOCK_GLOBAL,PH7_VmInstrLength(&(*pVm)),0); /* Point to the global scope */ pGen->pCurrent = &pGen->sGlobal; return SXRET_OK; } /* * Utility routines. Reset the code generator to it's initial state. */ PH7_PRIVATE sxi32 PH7_ResetCodeGenerator( ph7_vm *pVm, /* Target VM */ ProcConsumer xErr, /* Error log consumer callabck */ void *pErrData /* Last argument to xErr() */ ) { ph7_gen_state *pGen = &pVm->sCodeGen; GenBlock *pBlock,*pParent; /* Reset state */ SySetReset(&pGen->aLabel); SySetReset(&pGen->aGoto); SyBlobRelease(&pGen->sErrBuf); SyBlobRelease(&pGen->sWorker); /* Point to the global scope */ pBlock = pGen->pCurrent; while( pBlock->pParent != 0 ){ pParent = pBlock->pParent; GenStateFreeBlock(pBlock); pBlock = pParent; } pGen->xErr = xErr; pGen->pErrData = pErrData; pGen->pCurrent = &pGen->sGlobal; pGen->pRawIn = pGen->pRawEnd = 0; pGen->pIn = pGen->pEnd = 0; pGen->nErr = 0; return SXRET_OK; } /* * Generate a compile-time error message. * If the error count limit is reached (usually 15 error message) * this function return SXERR_ABORT.In that case upper-layers must * abort compilation immediately. */ PH7_PRIVATE sxi32 PH7_GenCompileError(ph7_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...) { SyBlob *pWorker = &pGen->sErrBuf; const char *zErr = "Error"; SyString *pFile; va_list ap; sxi32 rc; /* Reset the working buffer */ SyBlobReset(pWorker); /* Peek the processed file path if available */ pFile = (SyString *)SySetPeek(&pGen->pVm->aFiles); if( pFile && pGen->xErr ){ /* Append file name */ SyBlobAppend(pWorker,pFile->zString,pFile->nByte); SyBlobAppend(pWorker,(const void *)": ",sizeof(": ")-1); } if( nErrType == E_ERROR ){ /* Increment the error counter */ pGen->nErr++; if( pGen->nErr > 15 ){ /* Error count limit reached */ if( pGen->xErr ){ SyBlobFormat(pWorker,"%u Error count limit reached,PH7 is aborting compilation\n",nLine); if( SyBlobLength(pWorker) > 0 ){ /* Consume the generated error message */ pGen->xErr(SyBlobData(pWorker),SyBlobLength(pWorker),pGen->pErrData); } } /* Abort immediately */ return SXERR_ABORT; } } if( pGen->xErr == 0 ){ /* No available error consumer,return immediately */ return SXRET_OK; } switch(nErrType){ case E_WARNING: zErr = "Warning"; break; case E_PARSE: zErr = "Parse error"; break; case E_NOTICE: zErr = "Notice"; break; case E_USER_ERROR: zErr = "User error"; break; case E_USER_WARNING: zErr = "User warning"; break; case E_USER_NOTICE: zErr = "User notice"; break; default: break; } rc = SXRET_OK; /* Format the error message */ SyBlobFormat(pWorker,"%u %s: ",nLine,zErr); va_start(ap,zFormat); SyBlobFormatAp(pWorker,zFormat,ap); va_end(ap); /* Append a new line */ SyBlobAppend(pWorker,(const void *)"\n",sizeof(char)); if( SyBlobLength(pWorker) > 0 ){ /* Consume the generated error message */ pGen->xErr(SyBlobData(pWorker),SyBlobLength(pWorker),pGen->pErrData); } return rc; } /* * ---------------------------------------------------------- * File: builtin.c * MD5: 243e3ae4de6382dfa13bd461b136240b * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: builtin.c v1.0 FreeBSD 2012-08-06 08:39 devel $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* This file implement built-in 'foreign' functions for the PH7 engine */ /* * Section: * Variable handling Functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * bool is_bool($var) * Finds out whether a variable is a boolean. * Parameters * $var: The variable being evaluated. * Return * TRUE if var is a boolean. False otherwise. */ static int PH7_builtin_is_bool(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_bool(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_float($var) * bool is_real($var) * bool is_double($var) * Finds out whether a variable is a float. * Parameters * $var: The variable being evaluated. * Return * TRUE if var is a float. False otherwise. */ static int PH7_builtin_is_float(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_float(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_int($var) * bool is_integer($var) * bool is_long($var) * Finds out whether a variable is an integer. * Parameters * $var: The variable being evaluated. * Return * TRUE if var is an integer. False otherwise. */ static int PH7_builtin_is_int(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_int(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_string($var) * Finds out whether a variable is a string. * Parameters * $var: The variable being evaluated. * Return * TRUE if var is string. False otherwise. */ static int PH7_builtin_is_string(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_string(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_null($var) * Finds out whether a variable is NULL. * Parameters * $var: The variable being evaluated. * Return * TRUE if var is NULL. False otherwise. */ static int PH7_builtin_is_null(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_null(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_numeric($var) * Find out whether a variable is NULL. * Parameters * $var: The variable being evaluated. * Return * True if var is numeric. False otherwise. */ static int PH7_builtin_is_numeric(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_numeric(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_scalar($var) * Find out whether a variable is a scalar. * Parameters * $var: The variable being evaluated. * Return * True if var is scalar. False otherwise. */ static int PH7_builtin_is_scalar(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_scalar(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_array($var) * Find out whether a variable is an array. * Parameters * $var: The variable being evaluated. * Return * True if var is an array. False otherwise. */ static int PH7_builtin_is_array(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_array(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_object($var) * Find out whether a variable is an object. * Parameters * $var: The variable being evaluated. * Return * True if var is an object. False otherwise. */ static int PH7_builtin_is_object(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_object(apArg[0]); } /* Query result */ ph7_result_bool(pCtx,res); return PH7_OK; } /* * bool is_resource($var) * Find out whether a variable is a resource. * Parameters * $var: The variable being evaluated. * Return * True if a resource. False otherwise. */ static int PH7_builtin_is_resource(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 0; /* Assume false by default */ if( nArg > 0 ){ res = ph7_value_is_resource(apArg[0]); } ph7_result_bool(pCtx,res); return PH7_OK; } /* * float floatval($var) * Get float value of a variable. * Parameter * $var: The variable being processed. * Return * the float value of a variable. */ static int PH7_builtin_floatval(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg < 1 ){ /* return 0.0 */ ph7_result_double(pCtx,0); }else{ double dval; /* Perform the cast */ dval = ph7_value_to_double(apArg[0]); ph7_result_double(pCtx,dval); } return PH7_OK; } /* * int intval($var) * Get integer value of a variable. * Parameter * $var: The variable being processed. * Return * the int value of a variable. */ static int PH7_builtin_intval(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg < 1 ){ /* return 0 */ ph7_result_int(pCtx,0); }else{ sxi64 iVal; /* Perform the cast */ iVal = ph7_value_to_int64(apArg[0]); ph7_result_int64(pCtx,iVal); } return PH7_OK; } /* * string strval($var) * Get the string representation of a variable. * Parameter * $var: The variable being processed. * Return * the string value of a variable. */ static int PH7_builtin_strval(ph7_context *pCtx,int nArg,ph7_value **apArg) { if( nArg < 1 ){ /* return NULL */ ph7_result_null(pCtx); }else{ const char *zVal; int iLen = 0; /* cc -O6 warning */ /* Perform the cast */ zVal = ph7_value_to_string(apArg[0],&iLen); ph7_result_string(pCtx,zVal,iLen); } return PH7_OK; } /* * bool empty($var) * Determine whether a variable is empty. * Parameters * $var: The variable being checked. * Return * 0 if var has a non-empty and non-zero value.1 otherwise. */ static int PH7_builtin_empty(ph7_context *pCtx,int nArg,ph7_value **apArg) { int res = 1; /* Assume empty by default */ if( nArg > 0 ){ res = ph7_value_is_empty(apArg[0]); } ph7_result_bool(pCtx,res); return PH7_OK; } #ifndef PH7_DISABLE_BUILTIN_FUNC #ifdef PH7_ENABLE_MATH_FUNC /* * Section: * Math Functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ #include /* abs */ #include /* * float sqrt(float $arg ) * Square root of the given number. * Parameter * The number to process. * Return * The square root of arg or the special value Nan of failure. */ static int PH7_builtin_sqrt(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = sqrt(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float exp(float $arg ) * Calculates the exponent of e. * Parameter * The number to process. * Return * 'e' raised to the power of arg. */ static int PH7_builtin_exp(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = exp(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float floor(float $arg ) * Round fractions down. * Parameter * The number to process. * Return * Returns the next lowest integer value by rounding down value if necessary. */ static int PH7_builtin_floor(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = floor(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float cos(float $arg ) * Cosine. * Parameter * The number to process. * Return * The cosine of arg. */ static int PH7_builtin_cos(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = cos(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float acos(float $arg ) * Arc cosine. * Parameter * The number to process. * Return * The arc cosine of arg. */ static int PH7_builtin_acos(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = acos(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float cosh(float $arg ) * Hyperbolic cosine. * Parameter * The number to process. * Return * The hyperbolic cosine of arg. */ static int PH7_builtin_cosh(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = cosh(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float sin(float $arg ) * Sine. * Parameter * The number to process. * Return * The sine of arg. */ static int PH7_builtin_sin(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = sin(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float asin(float $arg ) * Arc sine. * Parameter * The number to process. * Return * The arc sine of arg. */ static int PH7_builtin_asin(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = asin(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float sinh(float $arg ) * Hyperbolic sine. * Parameter * The number to process. * Return * The hyperbolic sine of arg. */ static int PH7_builtin_sinh(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = sinh(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float ceil(float $arg ) * Round fractions up. * Parameter * The number to process. * Return * The next highest integer value by rounding up value if necessary. */ static int PH7_builtin_ceil(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = ceil(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float tan(float $arg ) * Tangent. * Parameter * The number to process. * Return * The tangent of arg. */ static int PH7_builtin_tan(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = tan(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float atan(float $arg ) * Arc tangent. * Parameter * The number to process. * Return * The arc tangent of arg. */ static int PH7_builtin_atan(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = atan(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float tanh(float $arg ) * Hyperbolic tangent. * Parameter * The number to process. * Return * The Hyperbolic tangent of arg. */ static int PH7_builtin_tanh(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = tanh(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float atan2(float $y,float $x) * Arc tangent of two variable. * Parameter * $y = Dividend parameter. * $x = Divisor parameter. * Return * The arc tangent of y/x in radian. */ static int PH7_builtin_atan2(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x,y; if( nArg < 2 ){ /* Missing arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } y = ph7_value_to_double(apArg[0]); x = ph7_value_to_double(apArg[1]); /* Perform the requested operation */ r = atan2(y,x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float/int64 abs(float/int64 $arg ) * Absolute value. * Parameter * The number to process. * Return * The absolute value of number. */ static int PH7_builtin_abs(ph7_context *pCtx,int nArg,ph7_value **apArg) { int is_float; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } is_float = ph7_value_is_float(apArg[0]); if( is_float ){ double r,x; x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = fabs(x); ph7_result_double(pCtx,r); }else{ int r,x; x = ph7_value_to_int(apArg[0]); /* Perform the requested operation */ r = abs(x); ph7_result_int(pCtx,r); } return PH7_OK; } /* * float log(float $arg,[int/float $base]) * Natural logarithm. * Parameter * $arg: The number to process. * $base: The optional logarithmic base to use. (only base-10 is supported) * Return * The logarithm of arg to base, if given, or the natural logarithm. * Note: * only Natural log and base-10 log are supported. */ static int PH7_builtin_log(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ if( nArg == 2 && ph7_value_is_numeric(apArg[1]) && ph7_value_to_int(apArg[1]) == 10 ){ /* Base-10 log */ r = log10(x); }else{ r = log(x); } /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float log10(float $arg ) * Base-10 logarithm. * Parameter * The number to process. * Return * The Base-10 logarithm of the given number. */ static int PH7_builtin_log10(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); /* Perform the requested operation */ r = log10(x); /* store the result back */ ph7_result_double(pCtx,r); return PH7_OK; } /* * number pow(number $base,number $exp) * Exponential expression. * Parameter * base * The base to use. * exp * The exponent. * Return * base raised to the power of exp. * If the result can be represented as integer it will be returned * as type integer, else it will be returned as type float. */ static int PH7_builtin_pow(ph7_context *pCtx,int nArg,ph7_value **apArg) { double r,x,y; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } x = ph7_value_to_double(apArg[0]); y = ph7_value_to_double(apArg[1]); /* Perform the requested operation */ r = pow(x,y); ph7_result_double(pCtx,r); return PH7_OK; } /* * float pi(void) * Returns an approximation of pi. * Note * you can use the M_PI constant which yields identical results to pi(). * Return * The value of pi as float. */ static int PH7_builtin_pi(ph7_context *pCtx,int nArg,ph7_value **apArg) { SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); ph7_result_double(pCtx,PH7_PI); return PH7_OK; } /* * float fmod(float $x,float $y) * Returns the floating point remainder (modulo) of the division of the arguments. * Parameters * $x * The dividend * $y * The divisor * Return * The floating point remainder of x/y. */ static int PH7_builtin_fmod(ph7_context *pCtx,int nArg,ph7_value **apArg) { double x,y,r; if( nArg < 2 ){ /* Missing arguments */ ph7_result_double(pCtx,0); return PH7_OK; } /* Extract given arguments */ x = ph7_value_to_double(apArg[0]); y = ph7_value_to_double(apArg[1]); /* Perform the requested operation */ r = fmod(x,y); /* Processing result */ ph7_result_double(pCtx,r); return PH7_OK; } /* * float hypot(float $x,float $y) * Calculate the length of the hypotenuse of a right-angle triangle . * Parameters * $x * Length of first side * $y * Length of first side * Return * Calculated length of the hypotenuse. */ static int PH7_builtin_hypot(ph7_context *pCtx,int nArg,ph7_value **apArg) { double x,y,r; if( nArg < 2 ){ /* Missing arguments */ ph7_result_double(pCtx,0); return PH7_OK; } /* Extract given arguments */ x = ph7_value_to_double(apArg[0]); y = ph7_value_to_double(apArg[1]); /* Perform the requested operation */ r = hypot(x,y); /* Processing result */ ph7_result_double(pCtx,r); return PH7_OK; } #endif /* PH7_ENABLE_MATH_FUNC */ /* * float round ( float $val [, int $precision = 0 [, int $mode = PHP_ROUND_HALF_UP ]] ) * Exponential expression. * Parameter * $val * The value to round. * $precision * The optional number of decimal digits to round to. * $mode * One of PHP_ROUND_HALF_UP, PHP_ROUND_HALF_DOWN, PHP_ROUND_HALF_EVEN, or PHP_ROUND_HALF_ODD. * (not supported). * Return * The rounded value. */ static int PH7_builtin_round(ph7_context *pCtx,int nArg,ph7_value **apArg) { int n = 0; double r; if( nArg < 1 ){ /* Missing argument,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Extract the precision if available */ if( nArg > 1 ){ n = ph7_value_to_int(apArg[1]); if( n>30 ){ n = 30; } if( n<0 ){ n = 0; } } r = ph7_value_to_double(apArg[0]); /* If Y==0 and X will fit in a 64-bit int, * handle the rounding directly.Otherwise * use our own cutsom printf [i.e:SyBufferFormat()]. */ if( n==0 && r>=0 && r= 0xc0 ){ /* UTF-8 stream */ zString++; while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){ zString++; } }else{ if( SyisHex(zString[0]) ){ break; } /* Ignore */ zString++; } } if( zString < zEnd ){ /* Cast */ SyHexStrToInt64(zString,(sxu32)(zEnd-zString),(void *)&iVal,0); } }else{ /* Extract as a 64-bit integer */ iVal = ph7_value_to_int64(apArg[0]); } /* Return the number */ ph7_result_int64(pCtx,iVal); return PH7_OK; } /* * int64 bindec(string $bin_string) * Binary to decimal. * Parameters * $bin_string * The binary string to convert * Return * Returns the decimal equivalent of the binary number represented by the binary_string argument. */ static int PH7_builtin_bindec(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString; ph7_int64 iVal; int nLen; if( nArg < 1 ){ /* Missing arguments,return -1 */ ph7_result_int(pCtx,-1); return PH7_OK; } iVal = 0; if( ph7_value_is_string(apArg[0]) ){ /* Extract the given string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen > 0 ){ /* Perform a binary cast */ SyBinaryStrToInt64(zString,(sxu32)nLen,(void *)&iVal,0); } }else{ /* Extract as a 64-bit integer */ iVal = ph7_value_to_int64(apArg[0]); } /* Return the number */ ph7_result_int64(pCtx,iVal); return PH7_OK; } /* * int64 octdec(string $oct_string) * Octal to decimal. * Parameters * $oct_string * The octal string to convert * Return * Returns the decimal equivalent of the octal number represented by the octal_string argument. */ static int PH7_builtin_octdec(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString; ph7_int64 iVal; int nLen; if( nArg < 1 ){ /* Missing arguments,return -1 */ ph7_result_int(pCtx,-1); return PH7_OK; } iVal = 0; if( ph7_value_is_string(apArg[0]) ){ /* Extract the given string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen > 0 ){ /* Perform the cast */ SyOctalStrToInt64(zString,(sxu32)nLen,(void *)&iVal,0); } }else{ /* Extract as a 64-bit integer */ iVal = ph7_value_to_int64(apArg[0]); } /* Return the number */ ph7_result_int64(pCtx,iVal); return PH7_OK; } /* * srand([int $seed]) * mt_srand([int $seed]) * Seed the random number generator. * Parameters * $seed * Optional seed value * Return * null. * Note: * THIS FUNCTION IS A NO-OP. * THE PH7 PRNG IS AUTOMATICALLY SEEDED WHEN THE VM IS CREATED. */ static int PH7_builtin_srand(ph7_context *pCtx,int nArg,ph7_value **apArg) { SXUNUSED(nArg); SXUNUSED(apArg); ph7_result_null(pCtx); return PH7_OK; } /* * string base_convert(string $number,int $frombase,int $tobase) * Convert a number between arbitrary bases. * Parameters * $number * The number to convert * $frombase * The base number is in * $tobase * The base to convert number to * Return * Number converted to base tobase */ static int PH7_builtin_base_convert(ph7_context *pCtx,int nArg,ph7_value **apArg) { int nLen,iFbase,iTobase; const char *zNum; ph7_int64 iNum; if( nArg < 3 ){ /* Return the empty string*/ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Base numbers */ iFbase = ph7_value_to_int(apArg[1]); iTobase = ph7_value_to_int(apArg[2]); if( ph7_value_is_string(apArg[0]) ){ /* Extract the target number */ zNum = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Return the empty string*/ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Base conversion */ switch(iFbase){ case 16: /* Hex */ SyHexStrToInt64(zNum,(sxu32)nLen,(void *)&iNum,0); break; case 8: /* Octal */ SyOctalStrToInt64(zNum,(sxu32)nLen,(void *)&iNum,0); break; case 2: /* Binary */ SyBinaryStrToInt64(zNum,(sxu32)nLen,(void *)&iNum,0); break; default: /* Decimal */ SyStrToInt64(zNum,(sxu32)nLen,(void *)&iNum,0); break; } }else{ iNum = ph7_value_to_int64(apArg[0]); } switch(iTobase){ case 16: /* Hex */ ph7_result_string_format(pCtx,"%qx",iNum); /* Quad hex */ break; case 8: /* Octal */ ph7_result_string_format(pCtx,"%qo",iNum); /* Quad octal */ break; case 2: /* Binary */ ph7_result_string_format(pCtx,"%qB",iNum); /* Quad binary */ break; default: /* Decimal */ ph7_result_string_format(pCtx,"%qd",iNum); /* Quad decimal */ break; } return PH7_OK; } /* * Section: * String handling Functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * string substr(string $string,int $start[, int $length ]) * Return part of a string. * Parameters * $string * The input string. Must be one character or longer. * $start * If start is non-negative, the returned string will start at the start'th position * in string, counting from zero. For instance, in the string 'abcdef', the character * at position 0 is 'a', the character at position 2 is 'c', and so forth. * If start is negative, the returned string will start at the start'th character * from the end of string. * If string is less than or equal to start characters long, FALSE will be returned. * $length * If length is given and is positive, the string returned will contain at most length * characters beginning from start (depending on the length of string). * If length is given and is negative, then that many characters will be omitted from * the end of string (after the start position has been calculated when a start is negative). * If start denotes the position of this truncation or beyond, false will be returned. * If length is given and is 0, FALSE or NULL an empty string will be returned. * If length is omitted, the substring starting from start until the end of the string * will be returned. * Return * Returns the extracted part of string, or FALSE on failure or an empty string. */ static int PH7_builtin_substr(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zSource,*zOfft; int nOfft,nLen,nSrcLen; if( nArg < 2 ){ /* return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zSource = ph7_value_to_string(apArg[0],&nSrcLen); if( nSrcLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } nLen = nSrcLen; /* cc warning */ /* Extract the offset */ nOfft = ph7_value_to_int(apArg[1]); if( nOfft < 0 ){ zOfft = &zSource[nSrcLen+nOfft]; if( zOfft < zSource ){ /* Invalid offset */ ph7_result_bool(pCtx,0); return PH7_OK; } nLen = (int)(&zSource[nSrcLen]-zOfft); nOfft = (int)(zOfft-zSource); }else if( nOfft >= nSrcLen ){ /* Invalid offset */ ph7_result_bool(pCtx,0); return PH7_OK; }else{ zOfft = &zSource[nOfft]; nLen = nSrcLen - nOfft; } if( nArg > 2 ){ /* Extract the length */ nLen = ph7_value_to_int(apArg[2]); if( nLen == 0 ){ /* Invalid length,return an empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; }else if( nLen < 0 ){ nLen = nSrcLen + nLen - nOfft; if( nLen < 1 ){ /* Invalid length */ nLen = nSrcLen - nOfft; } } if( nLen + nOfft > nSrcLen ){ /* Invalid length */ nLen = nSrcLen - nOfft; } } /* Return the substring */ ph7_result_string(pCtx,zOfft,nLen); return PH7_OK; } /* * int substr_compare(string $main_str,string $str ,int $offset[,int $length[,bool $case_insensitivity = false ]]) * Binary safe comparison of two strings from an offset, up to length characters. * Parameters * $main_str * The main string being compared. * $str * The secondary string being compared. * $offset * The start position for the comparison. If negative, it starts counting from * the end of the string. * $length * The length of the comparison. The default value is the largest of the length * of the str compared to the length of main_str less the offset. * $case_insensitivity * If case_insensitivity is TRUE, comparison is case insensitive. * Return * Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than * str, and 0 if they are equal. If offset is equal to or greater than the length of main_str * or length is set and is less than 1, substr_compare() prints a warning and returns FALSE. */ static int PH7_builtin_substr_compare(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zSource,*zOfft,*zSub; int nOfft,nLen,nSrcLen,nSublen; int iCase = 0; int rc; if( nArg < 3 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zSource = ph7_value_to_string(apArg[0],&nSrcLen); if( nSrcLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } nLen = nSrcLen; /* cc warning */ /* Extract the substring */ zSub = ph7_value_to_string(apArg[1],&nSublen); if( nSublen < 1 || nSublen > nSrcLen){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the offset */ nOfft = ph7_value_to_int(apArg[2]); if( nOfft < 0 ){ zOfft = &zSource[nSrcLen+nOfft]; if( zOfft < zSource ){ /* Invalid offset */ ph7_result_bool(pCtx,0); return PH7_OK; } nLen = (int)(&zSource[nSrcLen]-zOfft); nOfft = (int)(zOfft-zSource); }else if( nOfft >= nSrcLen ){ /* Invalid offset */ ph7_result_bool(pCtx,0); return PH7_OK; }else{ zOfft = &zSource[nOfft]; nLen = nSrcLen - nOfft; } if( nArg > 3 ){ /* Extract the length */ nLen = ph7_value_to_int(apArg[3]); if( nLen < 1 ){ /* Invalid length */ ph7_result_int(pCtx,1); return PH7_OK; }else if( nLen + nOfft > nSrcLen ){ /* Invalid length */ nLen = nSrcLen - nOfft; } if( nArg > 4 ){ /* Case-sensitive or not */ iCase = ph7_value_to_bool(apArg[4]); } } /* Perform the comparison */ if( iCase ){ rc = SyStrnicmp(zOfft,zSub,(sxu32)nLen); }else{ rc = SyStrncmp(zOfft,zSub,(sxu32)nLen); } /* Comparison result */ ph7_result_int(pCtx,rc); return PH7_OK; } /* * int substr_count(string $haystack,string $needle[,int $offset = 0 [,int $length ]]) * Count the number of substring occurrences. * Parameters * $haystack * The string to search in * $needle * The substring to search for * $offset * The offset where to start counting * $length (NOT USED) * The maximum length after the specified offset to search for the substring. * It outputs a warning if the offset plus the length is greater than the haystack length. * Return * Toral number of substring occurrences. */ static int PH7_builtin_substr_count(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zText,*zPattern,*zEnd; int nTextlen,nPatlen; int iCount = 0; sxu32 nOfft; sxi32 rc; if( nArg < 2 ){ /* Missing arguments */ ph7_result_int(pCtx,0); return PH7_OK; } /* Point to the haystack */ zText = ph7_value_to_string(apArg[0],&nTextlen); /* Point to the neddle */ zPattern = ph7_value_to_string(apArg[1],&nPatlen); if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){ /* NOOP,return zero */ ph7_result_int(pCtx,0); return PH7_OK; } if( nArg > 2 ){ int nOfft; /* Extract the offset */ nOfft = ph7_value_to_int(apArg[2]); if( nOfft < 0 || nOfft > nTextlen ){ /* Invalid offset,return zero */ ph7_result_int(pCtx,0); return PH7_OK; } /* Point to the desired offset */ zText = &zText[nOfft]; /* Adjust length */ nTextlen -= nOfft; } /* Point to the end of the string */ zEnd = &zText[nTextlen]; if( nArg > 3 ){ int nLen; /* Extract the length */ nLen = ph7_value_to_int(apArg[3]); if( nLen < 0 || nLen > nTextlen ){ /* Invalid length,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Adjust pointer */ nTextlen = nLen; zEnd = &zText[nTextlen]; } /* Perform the search */ for(;;){ rc = SyBlobSearch((const void *)zText,(sxu32)(zEnd-zText),(const void *)zPattern,nPatlen,&nOfft); if( rc != SXRET_OK ){ /* Pattern not found,break immediately */ break; } /* Increment counter and update the offset */ iCount++; zText += nOfft + nPatlen; if( zText >= zEnd ){ break; } } /* Pattern count */ ph7_result_int(pCtx,iCount); return PH7_OK; } /* * string chunk_split(string $body[,int $chunklen = 76 [, string $end = "\r\n" ]]) * Split a string into smaller chunks. * Parameters * $body * The string to be chunked. * $chunklen * The chunk length. * $end * The line ending sequence. * Return * The chunked string or NULL on failure. */ static int PH7_builtin_chunk_split(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn,*zEnd,*zSep = "\r\n"; int nSepLen,nChunkLen,nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Nothing to split,return null */ ph7_result_null(pCtx); return PH7_OK; } /* initialize/Extract arguments */ nSepLen = (int)sizeof("\r\n") - 1; nChunkLen = 76; zIn = ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nArg > 1 ){ /* Chunk length */ nChunkLen = ph7_value_to_int(apArg[1]); if( nChunkLen < 1 ){ /* Switch back to the default length */ nChunkLen = 76; } if( nArg > 2 ){ /* Separator */ zSep = ph7_value_to_string(apArg[2],&nSepLen); if( nSepLen < 1 ){ /* Switch back to the default separator */ zSep = "\r\n"; nSepLen = (int)sizeof("\r\n") - 1; } } } /* Perform the requested operation */ if( nChunkLen > nLen ){ /* Nothing to split,return the string and the separator */ ph7_result_string_format(pCtx,"%.*s%.*s",nLen,zIn,nSepLen,zSep); return PH7_OK; } while( zIn < zEnd ){ if( nChunkLen > (int)(zEnd-zIn) ){ nChunkLen = (int)(zEnd - zIn); } /* Append the chunk and the separator */ ph7_result_string_format(pCtx,"%.*s%.*s",nChunkLen,zIn,nSepLen,zSep); /* Point beyond the chunk */ zIn += nChunkLen; } return PH7_OK; } /* * string addslashes(string $str) * Quote string with slashes. * Returns a string with backslashes before characters that need * to be quoted in database queries etc. These characters are single * quote ('), double quote ("), backslash (\) and NUL (the NULL byte). * Parameter * str: The string to be escaped. * Return * Returns the escaped string */ static int PH7_builtin_addslashes(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zCur,*zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Nothing to process,retun NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the string to process */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } zEnd = &zIn[nLen]; zCur = 0; /* cc warning */ for(;;){ if( zIn >= zEnd ){ /* No more input */ break; } zCur = zIn; while( zIn < zEnd && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '\\' ){ zIn++; } if( zIn > zCur ){ /* Append raw contents */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } if( zIn < zEnd ){ int c = zIn[0]; ph7_result_string_format(pCtx,"\\%c",c); } zIn++; } return PH7_OK; } /* * Check if the given character is present in the given mask. * Return TRUE if present. FALSE otherwise. */ static int cSlashCheckMask(int c,const char *zMask,int nLen) { const char *zEnd = &zMask[nLen]; while( zMask < zEnd ){ if( zMask[0] == c ){ /* Character present,return TRUE */ return 1; } /* Advance the pointer */ zMask++; } /* Not present */ return 0; } /* * string addcslashes(string $str,string $charlist) * Quote string with slashes in a C style. * Parameter * $str: * The string to be escaped. * $charlist: * A list of characters to be escaped. If charlist contains characters \n, \r etc. * they are converted in C-like style, while other non-alphanumeric characters * with ASCII codes lower than 32 and higher than 126 converted to octal representation. * Return * Returns the escaped string. * Note: * Range characters [i.e: 'A..Z'] is not implemented in the current release. */ static int PH7_builtin_addcslashes(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zCur,*zIn,*zEnd,*zMask; int nLen,nMask; if( nArg < 1 ){ /* Nothing to process,retun NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the string to process */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 || nArg < 2 ){ /* Return the string untouched */ ph7_result_string(pCtx,zIn,nLen); return PH7_OK; } /* Extract the desired mask */ zMask = ph7_value_to_string(apArg[1],&nMask); zEnd = &zIn[nLen]; zCur = 0; /* cc warning */ for(;;){ if( zIn >= zEnd ){ /* No more input */ break; } zCur = zIn; while( zIn < zEnd && !cSlashCheckMask(zIn[0],zMask,nMask) ){ zIn++; } if( zIn > zCur ){ /* Append raw contents */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } if( zIn < zEnd ){ int c = zIn[0]; if( c > 126 || (c < 32 && (!SyisAlphaNum(c)/*EBCDIC*/ && !SyisSpace(c))) ){ /* Convert to octal */ ph7_result_string_format(pCtx,"\\%o",c); }else{ ph7_result_string_format(pCtx,"\\%c",c); } } zIn++; } return PH7_OK; } /* * string quotemeta(string $str) * Quote meta characters. * Parameter * $str: * The string to be escaped. * Return * Returns the escaped string. */ static int PH7_builtin_quotemeta(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zCur,*zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Nothing to process,retun NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the string to process */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } zEnd = &zIn[nLen]; zCur = 0; /* cc warning */ for(;;){ if( zIn >= zEnd ){ /* No more input */ break; } zCur = zIn; while( zIn < zEnd && !cSlashCheckMask(zIn[0],".\\+*?[^]($)",(int)sizeof(".\\+*?[^]($)")-1) ){ zIn++; } if( zIn > zCur ){ /* Append raw contents */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } if( zIn < zEnd ){ int c = zIn[0]; ph7_result_string_format(pCtx,"\\%c",c); } zIn++; } return PH7_OK; } /* * string stripslashes(string $str) * Un-quotes a quoted string. * Returns a string with backslashes before characters that need * to be quoted in database queries etc. These characters are single * quote ('), double quote ("), backslash (\) and NUL (the NULL byte). * Parameter * $str * The input string. * Return * Returns a string with backslashes stripped off. */ static int PH7_builtin_stripslashes(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zCur,*zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Nothing to process,retun NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the string to process */ zIn = ph7_value_to_string(apArg[0],&nLen); if( zIn == 0 ){ ph7_result_null(pCtx); return PH7_OK; } zEnd = &zIn[nLen]; zCur = 0; /* cc warning */ /* Encode the string */ for(;;){ if( zIn >= zEnd ){ /* No more input */ break; } zCur = zIn; while( zIn < zEnd && zIn[0] != '\\' ){ zIn++; } if( zIn > zCur ){ /* Append raw contents */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } if( &zIn[1] < zEnd ){ int c = zIn[1]; if( c == '\'' || c == '"' || c == '\\' ){ /* Ignore the backslash */ zIn++; } }else{ break; } } return PH7_OK; } /* * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]]) * HTML escaping of special characters. * The translations performed are: * '&' (ampersand) ==> '&' * '"' (double quote) ==> '"' when ENT_NOQUOTES is not set. * "'" (single quote) ==> ''' only when ENT_QUOTES is set. * '<' (less than) ==> '<' * '>' (greater than) ==> '>' * Parameters * $string * The string being converted. * $flags * A bitmask of one or more of the following flags, which specify how to handle quotes. * The default is ENT_COMPAT | ENT_HTML401. * ENT_COMPAT Will convert double-quotes and leave single-quotes alone. * ENT_QUOTES Will convert both double and single quotes. * ENT_NOQUOTES Will leave both double and single quotes unconverted. * ENT_IGNORE Silently discard invalid code unit sequences instead of returning an empty string. * $charset * Defines character set used in conversion. The default character set is ISO-8859-1. (Not used) * Return * The escaped string or NULL on failure. */ static int PH7_builtin_htmlspecialchars(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zCur,*zIn,*zEnd; int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */ int nLen,c; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; /* Extract the flags if available */ if( nArg > 1 ){ iFlags = ph7_value_to_int(apArg[1]); if( iFlags < 0 ){ iFlags = 0x01|0x40; } } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ break; } zCur = zIn; while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){ zIn++; } if( zCur < zIn ){ /* Append the raw string verbatim */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } if( zIn >= zEnd ){ break; } c = zIn[0]; if( c == '&' ){ /* Expand '&' */ ph7_result_string(pCtx,"&",(int)sizeof("&")-1); }else if( c == '<' ){ /* Expand '<' */ ph7_result_string(pCtx,"<",(int)sizeof("<")-1); }else if( c == '>' ){ /* Expand '>' */ ph7_result_string(pCtx,">",(int)sizeof(">")-1); }else if( c == '\'' ){ if( iFlags & 0x02 /*ENT_QUOTES*/ ){ /* Expand ''' */ ph7_result_string(pCtx,"'",(int)sizeof("'")-1); }else{ /* Leave the single quote untouched */ ph7_result_string(pCtx,"'",(int)sizeof(char)); } }else if( c == '"' ){ if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){ /* Expand '"' */ ph7_result_string(pCtx,""",(int)sizeof(""")-1); }else{ /* Leave the double quote untouched */ ph7_result_string(pCtx,"\"",(int)sizeof(char)); } } /* Ignore the unsafe HTML character */ zIn++; } return PH7_OK; } /* * string htmlspecialchars_decode(string $string[,int $quote_style = ENT_COMPAT ]) * Unescape HTML entities. * Parameters * $string * The string to decode * $quote_style * The quote style. One of the following constants: * ENT_COMPAT Will convert double-quotes and leave single-quotes alone (default) * ENT_QUOTES Will convert both double and single quotes * ENT_NOQUOTES Will leave both double and single quotes unconverted * Return * The unescaped string or NULL on failure. */ static int PH7_builtin_htmlspecialchars_decode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zCur,*zIn,*zEnd; int iFlags = 0x01; /* ENT_COMPAT */ int nLen,nJump; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; /* Extract the flags if available */ if( nArg > 1 ){ iFlags = ph7_value_to_int(apArg[1]); if( iFlags < 0 ){ iFlags = 0x01; } } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ break; } zCur = zIn; while( zIn < zEnd && zIn[0] != '&' ){ zIn++; } if( zCur < zIn ){ /* Append the raw string verbatim */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } nLen = (int)(zEnd-zIn); nJump = (int)sizeof(char); if( nLen >= (int)sizeof("&")-1 && SyStrnicmp(zIn,"&",sizeof("&")-1) == 0 ){ /* & ==> '&' */ ph7_result_string(pCtx,"&",(int)sizeof(char)); nJump = (int)sizeof("&")-1; }else if( nLen >= (int)sizeof("<")-1 && SyStrnicmp(zIn,"<",sizeof("<")-1) == 0 ){ /* < ==> < */ ph7_result_string(pCtx,"<",(int)sizeof(char)); nJump = (int)sizeof("<")-1; }else if( nLen >= (int)sizeof(">")-1 && SyStrnicmp(zIn,">",sizeof(">")-1) == 0 ){ /* > ==> '>' */ ph7_result_string(pCtx,">",(int)sizeof(char)); nJump = (int)sizeof(">")-1; }else if( nLen >= (int)sizeof(""")-1 && SyStrnicmp(zIn,""",sizeof(""")-1) == 0 ){ /* " ==> '"' */ if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){ ph7_result_string(pCtx,"\"",(int)sizeof(char)); }else{ /* Leave untouched */ ph7_result_string(pCtx,""",(int)sizeof(""")-1); } nJump = (int)sizeof(""")-1; }else if( nLen >= (int)sizeof("'")-1 && SyStrnicmp(zIn,"'",sizeof("'")-1) == 0 ){ /* ' ==> ''' */ if( iFlags & 0x02 /*ENT_QUOTES*/ ){ /* Expand ''' */ ph7_result_string(pCtx,"'",(int)sizeof(char)); }else{ /* Leave untouched */ ph7_result_string(pCtx,"'",(int)sizeof("'")-1); } nJump = (int)sizeof("'")-1; }else if( nLen >= (int)sizeof(char) ){ /* expand '&' */ ph7_result_string(pCtx,"&",(int)sizeof(char)); }else{ /* No more input to process */ break; } zIn += nJump; } return PH7_OK; } /* HTML encoding/Decoding table * Source: Symisc RunTime API.[chm@symisc.net] */ static const char *azHtmlEscape[] = { "<","<",">",">","&","&",""","\"","'","'", "!","!","$","$","#","#","%","%","(","(", ")",")","{","{","}","}","=","=","+","+", "?","?","[","[","]","]","@","@",",","," }; /* * array get_html_translation_table(void) * Returns the translation table used by htmlspecialchars() and htmlentities(). * Parameters * None * Return * The translation table as an array or NULL on failure. */ static int PH7_builtin_get_html_translation_table(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pArray,*pValue; sxu32 n; /* Element value */ pValue = ph7_context_new_scalar(pCtx); if( pValue == 0 ){ SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Make the table */ for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ /* Prepare the value */ ph7_value_string(pValue,azHtmlEscape[n],-1 /* Compute length automatically */); /* Insert the value */ ph7_array_add_strkey_elem(pArray,azHtmlEscape[n+1],pValue); /* Reset the string cursor */ ph7_value_reset_string_cursor(pValue); } /* * Return the array. * Don't worry about freeing memory, everything will be automatically * released upon we return from this function. */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]); * Convert all applicable characters to HTML entities * Parameters * $string * The input string. * $flags * A bitmask of one or more of the flags (see block-comment on PH7_builtin_htmlspecialchars()) * Return * The encoded string. */ static int PH7_builtin_htmlentities(ph7_context *pCtx,int nArg,ph7_value **apArg) { int iFlags = 0x01; /* ENT_COMPAT */ const char *zIn,*zEnd; int nLen,c; sxu32 n; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; /* Extract the flags if available */ if( nArg > 1 ){ iFlags = ph7_value_to_int(apArg[1]); if( iFlags < 0 ){ iFlags = 0x01; } } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* No more input to process */ break; } c = zIn[0]; /* Perform a linear lookup on the decoding table */ for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ if( azHtmlEscape[n+1][0] == c ){ /* Got one */ break; } } if( n < SX_ARRAYSIZE(azHtmlEscape) ){ /* Output the safe sequence [i.e: '<' ==> '<"] */ if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){ /* Expand the double quote verbatim */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); }else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){ /* expand single quote verbatim */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); }else{ ph7_result_string(pCtx,azHtmlEscape[n],-1/*Compute length automatically */); } }else{ /* Output character verbatim */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); } zIn++; } return PH7_OK; } /* * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]]) * Perform the reverse operation of html_entity_decode(). * Parameters * $string * The input string. * $flags * A bitmask of one or more of the flags (see comment on PH7_builtin_htmlspecialchars()) * Return * The decoded string. */ static int PH7_builtin_html_entity_decode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zCur,*zIn,*zEnd; int iFlags = 0x01; /* ENT_COMPAT */ int nLen; sxu32 n; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; /* Extract the flags if available */ if( nArg > 1 ){ iFlags = ph7_value_to_int(apArg[1]); if( iFlags < 0 ){ iFlags = 0x01; } } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* No more input to process */ break; } zCur = zIn; while( zIn < zEnd && zIn[0] != '&' ){ zIn++; } if( zCur < zIn ){ /* Append raw string verbatim */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } if( zIn >= zEnd ){ break; } nLen = (int)(zEnd-zIn); /* Find an encoded sequence */ for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ int iLen = (int)SyStrlen(azHtmlEscape[n]); if( nLen >= iLen && SyStrnicmp(zIn,azHtmlEscape[n],(sxu32)iLen) == 0 ){ /* Got one */ zIn += iLen; break; } } if( n < SX_ARRAYSIZE(azHtmlEscape) ){ int c = azHtmlEscape[n+1][0]; /* Output the decoded character */ if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/) ){ /* Do not process single quotes */ ph7_result_string(pCtx,azHtmlEscape[n],-1); }else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){ /* Do not process double quotes */ ph7_result_string(pCtx,azHtmlEscape[n],-1); }else{ ph7_result_string(pCtx,azHtmlEscape[n+1],-1); /* Compute length automatically */ } }else{ /* Append '&' */ ph7_result_string(pCtx,"&",(int)sizeof(char)); zIn++; } } return PH7_OK; } /* * int strlen($string) * return the length of the given string. * Parameter * string: The string being measured for length. * Return * length of the given string. */ static int PH7_builtin_strlen(ph7_context *pCtx,int nArg,ph7_value **apArg) { int iLen = 0; if( nArg > 0 ){ ph7_value_to_string(apArg[0],&iLen); } /* String length */ ph7_result_int(pCtx,iLen); return PH7_OK; } /* * int strcmp(string $str1,string $str2) * Perform a binary safe string comparison. * Parameter * str1: The first string * str2: The second string * Return * Returns < 0 if str1 is less than str2; > 0 if str1 is greater * than str2, and 0 if they are equal. */ static int PH7_builtin_strcmp(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *z1,*z2; int n1,n2; int res; if( nArg < 2 ){ res = nArg == 0 ? 0 : 1; ph7_result_int(pCtx,res); return PH7_OK; } /* Perform the comparison */ z1 = ph7_value_to_string(apArg[0],&n1); z2 = ph7_value_to_string(apArg[1],&n2); res = SyStrncmp(z1,z2,(sxu32)(SXMAX(n1,n2))); /* Comparison result */ ph7_result_int(pCtx,res); return PH7_OK; } /* * int strncmp(string $str1,string $str2,int n) * Perform a binary safe string comparison of the first n characters. * Parameter * str1: The first string * str2: The second string * Return * Returns < 0 if str1 is less than str2; > 0 if str1 is greater * than str2, and 0 if they are equal. */ static int PH7_builtin_strncmp(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *z1,*z2; int res; int n; if( nArg < 3 ){ /* Perform a standard comparison */ return PH7_builtin_strcmp(pCtx,nArg,apArg); } /* Desired comparison length */ n = ph7_value_to_int(apArg[2]); if( n < 0 ){ /* Invalid length */ ph7_result_int(pCtx,-1); return PH7_OK; } /* Perform the comparison */ z1 = ph7_value_to_string(apArg[0],0); z2 = ph7_value_to_string(apArg[1],0); res = SyStrncmp(z1,z2,(sxu32)n); /* Comparison result */ ph7_result_int(pCtx,res); return PH7_OK; } /* * int strcasecmp(string $str1,string $str2,int n) * Perform a binary safe case-insensitive string comparison. * Parameter * str1: The first string * str2: The second string * Return * Returns < 0 if str1 is less than str2; > 0 if str1 is greater * than str2, and 0 if they are equal. */ static int PH7_builtin_strcasecmp(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *z1,*z2; int n1,n2; int res; if( nArg < 2 ){ res = nArg == 0 ? 0 : 1; ph7_result_int(pCtx,res); return PH7_OK; } /* Perform the comparison */ z1 = ph7_value_to_string(apArg[0],&n1); z2 = ph7_value_to_string(apArg[1],&n2); res = SyStrnicmp(z1,z2,(sxu32)(SXMAX(n1,n2))); /* Comparison result */ ph7_result_int(pCtx,res); return PH7_OK; } /* * int strncasecmp(string $str1,string $str2,int n) * Perform a binary safe case-insensitive string comparison of the first n characters. * Parameter * $str1: The first string * $str2: The second string * $len: The length of strings to be used in the comparison. * Return * Returns < 0 if str1 is less than str2; > 0 if str1 is greater * than str2, and 0 if they are equal. */ static int PH7_builtin_strncasecmp(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *z1,*z2; int res; int n; if( nArg < 3 ){ /* Perform a standard comparison */ return PH7_builtin_strcasecmp(pCtx,nArg,apArg); } /* Desired comparison length */ n = ph7_value_to_int(apArg[2]); if( n < 0 ){ /* Invalid length */ ph7_result_int(pCtx,-1); return PH7_OK; } /* Perform the comparison */ z1 = ph7_value_to_string(apArg[0],0); z2 = ph7_value_to_string(apArg[1],0); res = SyStrnicmp(z1,z2,(sxu32)n); /* Comparison result */ ph7_result_int(pCtx,res); return PH7_OK; } /* * Implode context [i.e: it's private data]. * A pointer to the following structure is forwarded * verbatim to the array walker callback defined below. */ struct implode_data { ph7_context *pCtx; /* Call context */ int bRecursive; /* TRUE if recursive implode [this is a symisc eXtension] */ const char *zSep; /* Arguments separator if any */ int nSeplen; /* Separator length */ int bFirst; /* TRUE if first call */ int nRecCount; /* Recursion count to avoid infinite loop */ }; /* * Implode walker callback for the [ph7_array_walk()] interface. * The following routine is invoked for each array entry passed * to the implode() function. */ static int implode_callback(ph7_value *pKey,ph7_value *pValue,void *pUserData) { struct implode_data *pData = (struct implode_data *)pUserData; const char *zData; int nLen; if( pData->bRecursive && ph7_value_is_array(pValue) && pData->nRecCount < 32 ){ if( pData->nSeplen > 0 ){ if( !pData->bFirst ){ /* append the separator first */ ph7_result_string(pData->pCtx,pData->zSep,pData->nSeplen); }else{ pData->bFirst = 0; } } /* Recurse */ pData->bFirst = 1; pData->nRecCount++; PH7_HashmapWalk((ph7_hashmap *)pValue->x.pOther,implode_callback,pData); pData->nRecCount--; return PH7_OK; } /* Extract the string representation of the entry value */ zData = ph7_value_to_string(pValue,&nLen); if( nLen > 0 ){ if( pData->nSeplen > 0 ){ if( !pData->bFirst ){ /* append the separator first */ ph7_result_string(pData->pCtx,pData->zSep,pData->nSeplen); }else{ pData->bFirst = 0; } } ph7_result_string(pData->pCtx,zData,nLen); }else{ SXUNUSED(pKey); /* cc warning */ } return PH7_OK; } /* * string implode(string $glue,array $pieces,...) * string implode(array $pieces,...) * Join array elements with a string. * $glue * Defaults to an empty string. This is not the preferred usage of implode() as glue * would be the second parameter and thus, the bad prototype would be used. * $pieces * The array of strings to implode. * Return * Returns a string containing a string representation of all the array elements in the same * order, with the glue string between each element. */ static int PH7_builtin_implode(ph7_context *pCtx,int nArg,ph7_value **apArg) { struct implode_data imp_data; int i = 1; if( nArg < 1 ){ /* Missing argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Prepare the implode context */ imp_data.pCtx = pCtx; imp_data.bRecursive = 0; imp_data.bFirst = 1; imp_data.nRecCount = 0; if( !ph7_value_is_array(apArg[0]) ){ imp_data.zSep = ph7_value_to_string(apArg[0],&imp_data.nSeplen); }else{ imp_data.zSep = 0; imp_data.nSeplen = 0; i = 0; } ph7_result_string(pCtx,"",0); /* Set an empty stirng */ /* Start the 'join' process */ while( i < nArg ){ if( ph7_value_is_array(apArg[i]) ){ /* Iterate throw array entries */ ph7_array_walk(apArg[i],implode_callback,&imp_data); }else{ const char *zData; int nLen; /* Extract the string representation of the ph7 value */ zData = ph7_value_to_string(apArg[i],&nLen); if( nLen > 0 ){ if( imp_data.nSeplen > 0 ){ if( !imp_data.bFirst ){ /* append the separator first */ ph7_result_string(pCtx,imp_data.zSep,imp_data.nSeplen); }else{ imp_data.bFirst = 0; } } ph7_result_string(pCtx,zData,nLen); } } i++; } return PH7_OK; } /* * Symisc eXtension: * string implode_recursive(string $glue,array $pieces,...) * Purpose * Same as implode() but recurse on arrays. * Example: * $a = array('usr',array('home','dean')); * echo implode_recursive("/",$a); * Will output * usr/home/dean. * While the standard implode would produce. * usr/Array. * Parameter * Refer to implode(). * Return * Refer to implode(). */ static int PH7_builtin_implode_recursive(ph7_context *pCtx,int nArg,ph7_value **apArg) { struct implode_data imp_data; int i = 1; if( nArg < 1 ){ /* Missing argument,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Prepare the implode context */ imp_data.pCtx = pCtx; imp_data.bRecursive = 1; imp_data.bFirst = 1; imp_data.nRecCount = 0; if( !ph7_value_is_array(apArg[0]) ){ imp_data.zSep = ph7_value_to_string(apArg[0],&imp_data.nSeplen); }else{ imp_data.zSep = 0; imp_data.nSeplen = 0; i = 0; } ph7_result_string(pCtx,"",0); /* Set an empty stirng */ /* Start the 'join' process */ while( i < nArg ){ if( ph7_value_is_array(apArg[i]) ){ /* Iterate throw array entries */ ph7_array_walk(apArg[i],implode_callback,&imp_data); }else{ const char *zData; int nLen; /* Extract the string representation of the ph7 value */ zData = ph7_value_to_string(apArg[i],&nLen); if( nLen > 0 ){ if( imp_data.nSeplen > 0 ){ if( !imp_data.bFirst ){ /* append the separator first */ ph7_result_string(pCtx,imp_data.zSep,imp_data.nSeplen); }else{ imp_data.bFirst = 0; } } ph7_result_string(pCtx,zData,nLen); } } i++; } return PH7_OK; } /* * array explode(string $delimiter,string $string[,int $limit ]) * Returns an array of strings, each of which is a substring of string * formed by splitting it on boundaries formed by the string delimiter. * Parameters * $delimiter * The boundary string. * $string * The input string. * $limit * If limit is set and positive, the returned array will contain a maximum * of limit elements with the last element containing the rest of string. * If the limit parameter is negative, all fields except the last -limit are returned. * If the limit parameter is zero, then this is treated as 1. * Returns * Returns an array of strings created by splitting the string parameter * on boundaries formed by the delimiter. * If delimiter is an empty string (""), explode() will return FALSE. * If delimiter contains a value that is not contained in string and a negative * limit is used, then an empty array will be returned, otherwise an array containing string * will be returned. * NOTE: * Negative limit is not supported. */ static int PH7_builtin_explode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zDelim,*zString,*zCur,*zEnd; int nDelim,nStrlen,iLimit; ph7_value *pArray; ph7_value *pValue; sxu32 nOfft; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the delimiter */ zDelim = ph7_value_to_string(apArg[0],&nDelim); if( nDelim < 1 ){ /* Empty delimiter,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the string */ zString = ph7_value_to_string(apArg[1],&nStrlen); if( nStrlen < 1 ){ /* Empty delimiter,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the end of the string */ zEnd = &zString[nStrlen]; /* Create the array */ pArray = ph7_context_new_array(pCtx); pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pValue == 0 ){ /* Out of memory,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Set a defualt limit */ iLimit = SXI32_HIGH; if( nArg > 2 ){ iLimit = ph7_value_to_int(apArg[2]); if( iLimit < 0 ){ iLimit = -iLimit; } if( iLimit == 0 ){ iLimit = 1; } iLimit--; } /* Start exploding */ for(;;){ if( zString >= zEnd ){ /* No more entry to process */ break; } rc = SyBlobSearch(zString,(sxu32)(zEnd-zString),zDelim,nDelim,&nOfft); if( rc != SXRET_OK || iLimit <= (int)ph7_array_count(pArray) ){ /* Limit reached,insert the rest of the string and break */ if( zEnd > zString ){ ph7_value_string(pValue,zString,(int)(zEnd-zString)); ph7_array_add_elem(pArray,0/* Automatic index assign*/,pValue); } break; } /* Point to the desired offset */ zCur = &zString[nOfft]; if( zCur > zString ){ /* Perform the store operation */ ph7_value_string(pValue,zString,(int)(zCur-zString)); ph7_array_add_elem(pArray,0/* Automatic index assign*/,pValue); } /* Point beyond the delimiter */ zString = &zCur[nDelim]; /* Reset the cursor */ ph7_value_reset_string_cursor(pValue); } /* Return the freshly created array */ ph7_result_value(pCtx,pArray); /* NOTE that every allocated ph7_value will be automatically * released as soon we return from this foregin function. */ return PH7_OK; } /* * string trim(string $str[,string $charlist ]) * Strip whitespace (or other characters) from the beginning and end of a string. * Parameters * $str * The string that will be trimmed. * $charlist * Optionally, the stripped characters can also be specified using the charlist parameter. * Simply list all characters that you want to be stripped. * With .. you can specify a range of characters. * Returns. * Thr processed string. * NOTE: * RANGE CHARACTERS [I.E: 'a'..'z'] are not supported. */ static int PH7_builtin_trim(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString; int nLen; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Start the trim process */ if( nArg < 2 ){ SyString sStr; /* Remove white spaces and NUL bytes */ SyStringInitFromBuf(&sStr,zString,nLen); SyStringFullTrimSafe(&sStr); ph7_result_string(pCtx,sStr.zString,(int)sStr.nByte); }else{ /* Char list */ const char *zList; int nListlen; zList = ph7_value_to_string(apArg[1],&nListlen); if( nListlen < 1 ){ /* Return the string unchanged */ ph7_result_string(pCtx,zString,nLen); }else{ const char *zEnd = &zString[nLen]; const char *zCur = zString; const char *zPtr; int i; /* Left trim */ for(;;){ if( zCur >= zEnd ){ break; } zPtr = zCur; for( i = 0 ; i < nListlen ; i++ ){ if( zCur < zEnd && zCur[0] == zList[i] ){ zCur++; } } if( zCur == zPtr ){ /* No match,break immediately */ break; } } /* Right trim */ zEnd--; for(;;){ if( zEnd <= zCur ){ break; } zPtr = zEnd; for( i = 0 ; i < nListlen ; i++ ){ if( zEnd > zCur && zEnd[0] == zList[i] ){ zEnd--; } } if( zEnd == zPtr ){ break; } } if( zCur >= zEnd ){ /* Return the empty string */ ph7_result_string(pCtx,"",0); }else{ zEnd++; ph7_result_string(pCtx,zCur,(int)(zEnd-zCur)); } } } return PH7_OK; } /* * string rtrim(string $str[,string $charlist ]) * Strip whitespace (or other characters) from the end of a string. * Parameters * $str * The string that will be trimmed. * $charlist * Optionally, the stripped characters can also be specified using the charlist parameter. * Simply list all characters that you want to be stripped. * With .. you can specify a range of characters. * Returns. * Thr processed string. * NOTE: * RANGE CHARACTERS [I.E: 'a'..'z'] are not supported. */ static int PH7_builtin_rtrim(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString; int nLen; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Start the trim process */ if( nArg < 2 ){ SyString sStr; /* Remove white spaces and NUL bytes*/ SyStringInitFromBuf(&sStr,zString,nLen); SyStringRightTrimSafe(&sStr); ph7_result_string(pCtx,sStr.zString,(int)sStr.nByte); }else{ /* Char list */ const char *zList; int nListlen; zList = ph7_value_to_string(apArg[1],&nListlen); if( nListlen < 1 ){ /* Return the string unchanged */ ph7_result_string(pCtx,zString,nLen); }else{ const char *zEnd = &zString[nLen - 1]; const char *zCur = zString; const char *zPtr; int i; /* Right trim */ for(;;){ if( zEnd <= zCur ){ break; } zPtr = zEnd; for( i = 0 ; i < nListlen ; i++ ){ if( zEnd > zCur && zEnd[0] == zList[i] ){ zEnd--; } } if( zEnd == zPtr ){ break; } } if( zEnd <= zCur ){ /* Return the empty string */ ph7_result_string(pCtx,"",0); }else{ zEnd++; ph7_result_string(pCtx,zCur,(int)(zEnd-zCur)); } } } return PH7_OK; } /* * string ltrim(string $str[,string $charlist ]) * Strip whitespace (or other characters) from the beginning and end of a string. * Parameters * $str * The string that will be trimmed. * $charlist * Optionally, the stripped characters can also be specified using the charlist parameter. * Simply list all characters that you want to be stripped. * With .. you can specify a range of characters. * Returns. * Thr processed string. * NOTE: * RANGE CHARACTERS [I.E: 'a'..'z'] are not supported. */ static int PH7_builtin_ltrim(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString; int nLen; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Start the trim process */ if( nArg < 2 ){ SyString sStr; /* Remove white spaces and NUL byte */ SyStringInitFromBuf(&sStr,zString,nLen); SyStringLeftTrimSafe(&sStr); ph7_result_string(pCtx,sStr.zString,(int)sStr.nByte); }else{ /* Char list */ const char *zList; int nListlen; zList = ph7_value_to_string(apArg[1],&nListlen); if( nListlen < 1 ){ /* Return the string unchanged */ ph7_result_string(pCtx,zString,nLen); }else{ const char *zEnd = &zString[nLen]; const char *zCur = zString; const char *zPtr; int i; /* Left trim */ for(;;){ if( zCur >= zEnd ){ break; } zPtr = zCur; for( i = 0 ; i < nListlen ; i++ ){ if( zCur < zEnd && zCur[0] == zList[i] ){ zCur++; } } if( zCur == zPtr ){ /* No match,break immediately */ break; } } if( zCur >= zEnd ){ /* Return the empty string */ ph7_result_string(pCtx,"",0); }else{ ph7_result_string(pCtx,zCur,(int)(zEnd-zCur)); } } } return PH7_OK; } /* * string strtolower(string $str) * Make a string lowercase. * Parameters * $str * The input string. * Returns. * The lowercased string. */ static int PH7_builtin_strtolower(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zCur,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Perform the requested operation */ zEnd = &zString[nLen]; for(;;){ if( zString >= zEnd ){ /* No more input,break immediately */ break; } if( (unsigned char)zString[0] >= 0xc0 ){ /* UTF-8 stream,output verbatim */ zCur = zString; zString++; while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){ zString++; } /* Append UTF-8 stream */ ph7_result_string(pCtx,zCur,(int)(zString-zCur)); }else{ int c = zString[0]; if( SyisUpper(c) ){ c = SyToLower(zString[0]); } /* Append character */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); /* Advance the cursor */ zString++; } } return PH7_OK; } /* * string strtolower(string $str) * Make a string uppercase. * Parameters * $str * The input string. * Returns. * The uppercased string. */ static int PH7_builtin_strtoupper(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zCur,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Perform the requested operation */ zEnd = &zString[nLen]; for(;;){ if( zString >= zEnd ){ /* No more input,break immediately */ break; } if( (unsigned char)zString[0] >= 0xc0 ){ /* UTF-8 stream,output verbatim */ zCur = zString; zString++; while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){ zString++; } /* Append UTF-8 stream */ ph7_result_string(pCtx,zCur,(int)(zString-zCur)); }else{ int c = zString[0]; if( SyisLower(c) ){ c = SyToUpper(zString[0]); } /* Append character */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); /* Advance the cursor */ zString++; } } return PH7_OK; } /* * string ucfirst(string $str) * Returns a string with the first character of str capitalized, if that * character is alphabetic. * Parameters * $str * The input string. * Returns. * The processed string. */ static int PH7_builtin_ucfirst(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zEnd; int nLen,c; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Perform the requested operation */ zEnd = &zString[nLen]; c = zString[0]; if( SyisLower(c) ){ c = SyToUpper(c); } /* Append the first character */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); zString++; if( zString < zEnd ){ /* Append the rest of the input verbatim */ ph7_result_string(pCtx,zString,(int)(zEnd-zString)); } return PH7_OK; } /* * string lcfirst(string $str) * Make a string's first character lowercase. * Parameters * $str * The input string. * Returns. * The processed string. */ static int PH7_builtin_lcfirst(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zEnd; int nLen,c; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Perform the requested operation */ zEnd = &zString[nLen]; c = zString[0]; if( SyisUpper(c) ){ c = SyToLower(c); } /* Append the first character */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); zString++; if( zString < zEnd ){ /* Append the rest of the input verbatim */ ph7_result_string(pCtx,zString,(int)(zEnd-zString)); } return PH7_OK; } /* * int ord(string $string) * Returns the ASCII value of the first character of string. * Parameters * $str * The input string. * Returns. * The ASCII value as an integer. */ static int PH7_builtin_ord(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString; int nLen,c; if( nArg < 1 ){ /* Missing arguments,return -1 */ ph7_result_int(pCtx,-1); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return -1 */ ph7_result_int(pCtx,-1); return PH7_OK; } /* Extract the ASCII value of the first character */ c = zString[0]; /* Return that value */ ph7_result_int(pCtx,c); return PH7_OK; } /* * string chr(int $ascii) * Returns a one-character string containing the character specified by ascii. * Parameters * $ascii * The ascii code. * Returns. * The specified character. */ static int PH7_builtin_chr(ph7_context *pCtx,int nArg,ph7_value **apArg) { int c; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the ASCII value */ c = ph7_value_to_int(apArg[0]); /* Return the specified character */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); return PH7_OK; } /* * Binary to hex consumer callback. * This callback is the default consumer used by the hash functions * [i.e: bin2hex(),md5(),sha1(),md5_file() ... ] defined below. */ static int HashConsumer(const void *pData,unsigned int nLen,void *pUserData) { /* Append hex chunk verbatim */ ph7_result_string((ph7_context *)pUserData,(const char *)pData,(int)nLen); return SXRET_OK; } /* * string bin2hex(string $str) * Convert binary data into hexadecimal representation. * Parameters * $str * The input string. * Returns. * Returns the hexadecimal representation of the given string. */ static int PH7_builtin_bin2hex(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString; int nLen; if( nArg < 1 ){ /* Missing arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Perform the requested operation */ SyBinToHexConsumer((const void *)zString,(sxu32)nLen,HashConsumer,pCtx); return PH7_OK; } /* Search callback signature */ typedef sxi32 (*ProcStringMatch)(const void *,sxu32,const void *,sxu32,sxu32 *); /* * Case-insensitive pattern match. * Brute force is the default search method used here. * This is due to the fact that brute-forcing works quite * well for short/medium texts on modern hardware. */ static sxi32 iPatternMatch(const void *pText,sxu32 nLen,const void *pPattern,sxu32 iPatLen,sxu32 *pOfft) { const char *zpIn = (const char *)pPattern; const char *zIn = (const char *)pText; const char *zpEnd = &zpIn[iPatLen]; const char *zEnd = &zIn[nLen]; const char *zPtr,*zPtr2; int c,d; if( iPatLen > nLen ){ /* Don't bother processing */ return SXERR_NOTFOUND; } for(;;){ if( zIn >= zEnd ){ break; } c = SyToLower(zIn[0]); d = SyToLower(zpIn[0]); if( c == d ){ zPtr = &zIn[1]; zPtr2 = &zpIn[1]; for(;;){ if( zPtr2 >= zpEnd ){ /* Pattern found */ if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); } return SXRET_OK; } if( zPtr >= zEnd ){ break; } c = SyToLower(zPtr[0]); d = SyToLower(zPtr2[0]); if( c != d ){ break; } zPtr++; zPtr2++; } } zIn++; } /* Pattern not found */ return SXERR_NOTFOUND; } /* * string strstr(string $haystack,string $needle[,bool $before_needle = false ]) * Find the first occurrence of a string. * Parameters * $haystack * The input string. * $needle * Search pattern (must be a string). * $before_needle * If TRUE, strstr() returns the part of the haystack before the first occurrence * of the needle (excluding the needle). * Return * Returns the portion of string, or FALSE if needle is not found. */ static int PH7_builtin_strstr(ph7_context *pCtx,int nArg,ph7_value **apArg) { ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ const char *zBlob,*zPattern; int nLen,nPatLen; sxu32 nOfft; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the needle and the haystack */ zBlob = ph7_value_to_string(apArg[0],&nLen); zPattern = ph7_value_to_string(apArg[1],&nPatLen); nOfft = 0; /* cc warning */ if( nLen > 0 && nPatLen > 0 ){ int before = 0; /* Perform the lookup */ rc = xPatternMatch(zBlob,(sxu32)nLen,zPattern,(sxu32)nPatLen,&nOfft); if( rc != SXRET_OK ){ /* Pattern not found,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the portion of the string */ if( nArg > 2 ){ before = ph7_value_to_int(apArg[2]); } if( before ){ ph7_result_string(pCtx,zBlob,(int)(&zBlob[nOfft]-zBlob)); }else{ ph7_result_string(pCtx,&zBlob[nOfft],(int)(&zBlob[nLen]-&zBlob[nOfft])); } }else{ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * string stristr(string $haystack,string $needle[,bool $before_needle = false ]) * Case-insensitive strstr(). * Parameters * $haystack * The input string. * $needle * Search pattern (must be a string). * $before_needle * If TRUE, strstr() returns the part of the haystack before the first occurrence * of the needle (excluding the needle). * Return * Returns the portion of string, or FALSE if needle is not found. */ static int PH7_builtin_stristr(ph7_context *pCtx,int nArg,ph7_value **apArg) { ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ const char *zBlob,*zPattern; int nLen,nPatLen; sxu32 nOfft; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the needle and the haystack */ zBlob = ph7_value_to_string(apArg[0],&nLen); zPattern = ph7_value_to_string(apArg[1],&nPatLen); nOfft = 0; /* cc warning */ if( nLen > 0 && nPatLen > 0 ){ int before = 0; /* Perform the lookup */ rc = xPatternMatch(zBlob,(sxu32)nLen,zPattern,(sxu32)nPatLen,&nOfft); if( rc != SXRET_OK ){ /* Pattern not found,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the portion of the string */ if( nArg > 2 ){ before = ph7_value_to_int(apArg[2]); } if( before ){ ph7_result_string(pCtx,zBlob,(int)(&zBlob[nOfft]-zBlob)); }else{ ph7_result_string(pCtx,&zBlob[nOfft],(int)(&zBlob[nLen]-&zBlob[nOfft])); } }else{ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * int strpos(string $haystack,string $needle [,int $offset = 0 ] ) * Returns the numeric position of the first occurrence of needle in the haystack string. * Parameters * $haystack * The input string. * $needle * Search pattern (must be a string). * $offset * This optional offset parameter allows you to specify which character in haystack * to start searching. The position returned is still relative to the beginning * of haystack. * Return * Returns the position as an integer.If needle is not found, strpos() will return FALSE. */ static int PH7_builtin_strpos(ph7_context *pCtx,int nArg,ph7_value **apArg) { ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ const char *zBlob,*zPattern; int nLen,nPatLen,nStart; sxu32 nOfft; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the needle and the haystack */ zBlob = ph7_value_to_string(apArg[0],&nLen); zPattern = ph7_value_to_string(apArg[1],&nPatLen); nOfft = 0; /* cc warning */ nStart = 0; /* Peek the starting offset if available */ if( nArg > 2 ){ nStart = ph7_value_to_int(apArg[2]); if( nStart < 0 ){ nStart = -nStart; } if( nStart >= nLen ){ /* Invalid offset */ nStart = 0; }else{ zBlob += nStart; nLen -= nStart; } } if( nLen > 0 && nPatLen > 0 ){ /* Perform the lookup */ rc = xPatternMatch(zBlob,(sxu32)nLen,zPattern,(sxu32)nPatLen,&nOfft); if( rc != SXRET_OK ){ /* Pattern not found,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the pattern position */ ph7_result_int64(pCtx,(ph7_int64)(nOfft+nStart)); }else{ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * int stripos(string $haystack,string $needle [,int $offset = 0 ] ) * Case-insensitive strpos. * Parameters * $haystack * The input string. * $needle * Search pattern (must be a string). * $offset * This optional offset parameter allows you to specify which character in haystack * to start searching. The position returned is still relative to the beginning * of haystack. * Return * Returns the position as an integer.If needle is not found, strpos() will return FALSE. */ static int PH7_builtin_stripos(ph7_context *pCtx,int nArg,ph7_value **apArg) { ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ const char *zBlob,*zPattern; int nLen,nPatLen,nStart; sxu32 nOfft; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the needle and the haystack */ zBlob = ph7_value_to_string(apArg[0],&nLen); zPattern = ph7_value_to_string(apArg[1],&nPatLen); nOfft = 0; /* cc warning */ nStart = 0; /* Peek the starting offset if available */ if( nArg > 2 ){ nStart = ph7_value_to_int(apArg[2]); if( nStart < 0 ){ nStart = -nStart; } if( nStart >= nLen ){ /* Invalid offset */ nStart = 0; }else{ zBlob += nStart; nLen -= nStart; } } if( nLen > 0 && nPatLen > 0 ){ /* Perform the lookup */ rc = xPatternMatch(zBlob,(sxu32)nLen,zPattern,(sxu32)nPatLen,&nOfft); if( rc != SXRET_OK ){ /* Pattern not found,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the pattern position */ ph7_result_int64(pCtx,(ph7_int64)(nOfft+nStart)); }else{ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * int strrpos(string $haystack,string $needle [,int $offset = 0 ] ) * Find the numeric position of the last occurrence of needle in the haystack string. * Parameters * $haystack * The input string. * $needle * Search pattern (must be a string). * $offset * If specified, search will start this number of characters counted from the beginning * of the string. If the value is negative, search will instead start from that many * characters from the end of the string, searching backwards. * Return * Returns the position as an integer.If needle is not found, strrpos() will return FALSE. */ static int PH7_builtin_strrpos(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zStart,*zBlob,*zPattern,*zPtr,*zEnd; ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ int nLen,nPatLen; sxu32 nOfft; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the needle and the haystack */ zBlob = ph7_value_to_string(apArg[0],&nLen); zPattern = ph7_value_to_string(apArg[1],&nPatLen); /* Point to the end of the pattern */ zPtr = &zBlob[nLen - 1]; zEnd = &zBlob[nLen]; /* Save the starting posistion */ zStart = zBlob; nOfft = 0; /* cc warning */ /* Peek the starting offset if available */ if( nArg > 2 ){ int nStart; nStart = ph7_value_to_int(apArg[2]); if( nStart < 0 ){ nStart = -nStart; if( nStart >= nLen ){ /* Invalid offset */ ph7_result_bool(pCtx,0); return PH7_OK; }else{ nLen -= nStart; zPtr = &zBlob[nLen - 1]; zEnd = &zBlob[nLen]; } }else{ if( nStart >= nLen ){ /* Invalid offset */ ph7_result_bool(pCtx,0); return PH7_OK; }else{ zBlob += nStart; nLen -= nStart; } } } if( nLen > 0 && nPatLen > 0 ){ /* Perform the lookup */ for(;;){ if( zBlob >= zPtr ){ break; } rc = xPatternMatch((const void *)zPtr,(sxu32)(zEnd-zPtr),(const void *)zPattern,(sxu32)nPatLen,&nOfft); if( rc == SXRET_OK ){ /* Pattern found,return it's position */ ph7_result_int64(pCtx,(ph7_int64)(&zPtr[nOfft] - zStart)); return PH7_OK; } zPtr--; } /* Pattern not found,return FALSE */ ph7_result_bool(pCtx,0); }else{ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * int strripos(string $haystack,string $needle [,int $offset = 0 ] ) * Case-insensitive strrpos. * Parameters * $haystack * The input string. * $needle * Search pattern (must be a string). * $offset * If specified, search will start this number of characters counted from the beginning * of the string. If the value is negative, search will instead start from that many * characters from the end of the string, searching backwards. * Return * Returns the position as an integer.If needle is not found, strrpos() will return FALSE. */ static int PH7_builtin_strripos(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zStart,*zBlob,*zPattern,*zPtr,*zEnd; ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ int nLen,nPatLen; sxu32 nOfft; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the needle and the haystack */ zBlob = ph7_value_to_string(apArg[0],&nLen); zPattern = ph7_value_to_string(apArg[1],&nPatLen); /* Point to the end of the pattern */ zPtr = &zBlob[nLen - 1]; zEnd = &zBlob[nLen]; /* Save the starting posistion */ zStart = zBlob; nOfft = 0; /* cc warning */ /* Peek the starting offset if available */ if( nArg > 2 ){ int nStart; nStart = ph7_value_to_int(apArg[2]); if( nStart < 0 ){ nStart = -nStart; if( nStart >= nLen ){ /* Invalid offset */ ph7_result_bool(pCtx,0); return PH7_OK; }else{ nLen -= nStart; zPtr = &zBlob[nLen - 1]; zEnd = &zBlob[nLen]; } }else{ if( nStart >= nLen ){ /* Invalid offset */ ph7_result_bool(pCtx,0); return PH7_OK; }else{ zBlob += nStart; nLen -= nStart; } } } if( nLen > 0 && nPatLen > 0 ){ /* Perform the lookup */ for(;;){ if( zBlob >= zPtr ){ break; } rc = xPatternMatch((const void *)zPtr,(sxu32)(zEnd-zPtr),(const void *)zPattern,(sxu32)nPatLen,&nOfft); if( rc == SXRET_OK ){ /* Pattern found,return it's position */ ph7_result_int64(pCtx,(ph7_int64)(&zPtr[nOfft] - zStart)); return PH7_OK; } zPtr--; } /* Pattern not found,return FALSE */ ph7_result_bool(pCtx,0); }else{ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * int strrchr(string $haystack,mixed $needle) * Find the last occurrence of a character in a string. * Parameters * $haystack * The input string. * $needle * If needle contains more than one character, only the first is used. * This behavior is different from that of strstr(). * If needle is not a string, it is converted to an integer and applied * as the ordinal value of a character. * Return * This function returns the portion of string, or FALSE if needle is not found. */ static int PH7_builtin_strrchr(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zBlob; int nLen,c; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the haystack */ zBlob = ph7_value_to_string(apArg[0],&nLen); c = 0; /* cc warning */ if( nLen > 0 ){ sxu32 nOfft; sxi32 rc; if( ph7_value_is_string(apArg[1]) ){ const char *zPattern; zPattern = ph7_value_to_string(apArg[1],0); /* Never fail,so there is no need to check * for NULL pointer. */ c = zPattern[0]; }else{ /* Int cast */ c = ph7_value_to_int(apArg[1]); } /* Perform the lookup */ rc = SyByteFind2(zBlob,(sxu32)nLen,c,&nOfft); if( rc != SXRET_OK ){ /* No such entry,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Return the string portion */ ph7_result_string(pCtx,&zBlob[nOfft],(int)(&zBlob[nLen]-&zBlob[nOfft])); }else{ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * string strrev(string $string) * Reverse a string. * Parameters * $string * String to be reversed. * Return * The reversed string. */ static int PH7_builtin_strrev(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn,*zEnd; int nLen,c; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string Return null */ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ zEnd = &zIn[nLen - 1]; for(;;){ if( zEnd < zIn ){ /* No more input to process */ break; } /* Append current character */ c = zEnd[0]; ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); zEnd--; } return PH7_OK; } /* * string ucwords(string $string) * Uppercase the first character of each word in a string. * The definition of a word is any string of characters that is immediately after * a whitespace (These are: space, form-feed, newline, carriage return, horizontal tab, and vertical tab). * Parameters * $string * The input string. * Return * The modified string.. */ static int PH7_builtin_ucwords(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn,*zCur,*zEnd; int nLen,c; if( nArg < 1 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string Return null */ ph7_result_null(pCtx); return PH7_OK; } /* Perform the requested operation */ zEnd = &zIn[nLen]; for(;;){ /* Jump leading white spaces */ zCur = zIn; while( zIn < zEnd && (unsigned char)zIn[0] < 0x80 && SyisSpace(zIn[0]) ){ zIn++; } if( zCur < zIn ){ /* Append white space stream */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } if( zIn >= zEnd ){ /* No more input to process */ break; } c = zIn[0]; if( c < 0x80 && SyisLower(c) ){ c = SyToUpper(c); } /* Append the upper-cased character */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); zIn++; zCur = zIn; /* Append the word varbatim */ while( zIn < zEnd ){ if( (unsigned char)zIn[0] >= 0xc0 ){ /* UTF-8 stream */ zIn++; SX_JMP_UTF8(zIn,zEnd); }else if( !SyisSpace(zIn[0]) ){ zIn++; }else{ break; } } if( zCur < zIn ){ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } } return PH7_OK; } /* * string str_repeat(string $input,int $multiplier) * Returns input repeated multiplier times. * Parameters * $string * String to be repeated. * $multiplier * Number of time the input string should be repeated. * multiplier has to be greater than or equal to 0. If the multiplier is set * to 0, the function will return an empty string. * Return * The repeated string. */ static int PH7_builtin_str_repeat(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn; int nLen,nMul; int rc; if( nArg < 2 ){ /* Missing arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string.Return null */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the multiplier */ nMul = ph7_value_to_int(apArg[1]); if( nMul < 1 ){ /* Return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( !nMul ){ break; } /* Append the copy */ rc = ph7_result_string(pCtx,zIn,nLen); if( rc != PH7_OK ){ /* Out of memory,break immediately */ break; } nMul--; } return PH7_OK; } /* * string nl2br(string $string[,bool $is_xhtml = true ]) * Inserts HTML line breaks before all newlines in a string. * Parameters * $string * The input string. * $is_xhtml * Whenever to use XHTML compatible line breaks or not. * Return * The processed string. */ static int PH7_builtin_nl2br(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn,*zCur,*zEnd; int is_xhtml = 0; int nLen; if( nArg < 1 ){ /* Missing arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string,return null */ ph7_result_null(pCtx); return PH7_OK; } if( nArg > 1 ){ is_xhtml = ph7_value_to_bool(apArg[1]); } zEnd = &zIn[nLen]; /* Perform the requested operation */ for(;;){ zCur = zIn; /* Delimit the string */ while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){ zIn++; } if( zCur < zIn ){ /* Output chunk verbatim */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } if( zIn >= zEnd ){ /* No more input to process */ break; } /* Output the HTML line break */ if( is_xhtml ){ ph7_result_string(pCtx,"
",(int)sizeof("
")-1); }else{ ph7_result_string(pCtx,"
",(int)sizeof("
")-1); } zCur = zIn; /* Append trailing line */ while( zIn < zEnd && (zIn[0] == '\n' || zIn[0] == '\r') ){ zIn++; } if( zCur < zIn ){ /* Output chunk verbatim */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } } return PH7_OK; } /* * Format a given string and invoke the given callback on each processed chunk. * According to the PHP reference manual. * The format string is composed of zero or more directives: ordinary characters * (excluding %) that are copied directly to the result, and conversion * specifications, each of which results in fetching its own parameter. * This applies to both sprintf() and printf(). * Each conversion specification consists of a percent sign (%), followed by one * or more of these elements, in order: * An optional sign specifier that forces a sign (- or +) to be used on a number. * By default, only the - sign is used on a number if it's negative. This specifier forces * positive numbers to have the + sign attached as well. * An optional padding specifier that says what character will be used for padding * the results to the right string size. This may be a space character or a 0 (zero character). * The default is to pad with spaces. An alternate padding character can be specified by prefixing * it with a single quote ('). See the examples below. * An optional alignment specifier that says if the result should be left-justified or right-justified. * The default is right-justified; a - character here will make it left-justified. * An optional number, a width specifier that says how many characters (minimum) this conversion * should result in. * An optional precision specifier in the form of a period (`.') followed by an optional decimal * digit string that says how many decimal digits should be displayed for floating-point numbers. * When using this specifier on a string, it acts as a cutoff point, setting a maximum character * limit to the string. * A type specifier that says what type the argument data should be treated as. Possible types: * % - a literal percent character. No argument is required. * b - the argument is treated as an integer, and presented as a binary number. * c - the argument is treated as an integer, and presented as the character with that ASCII value. * d - the argument is treated as an integer, and presented as a (signed) decimal number. * e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands * for the number of digits after the decimal point. * E - like %e but uses uppercase letter (e.g. 1.2E+2). * u - the argument is treated as an integer, and presented as an unsigned decimal number. * f - the argument is treated as a float, and presented as a floating-point number (locale aware). * F - the argument is treated as a float, and presented as a floating-point number (non-locale aware). * g - shorter of %e and %f. * G - shorter of %E and %f. * o - the argument is treated as an integer, and presented as an octal number. * s - the argument is treated as and presented as a string. * x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters). * X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters). */ /* * This implementation is based on the one found in the SQLite3 source tree. */ #define PH7_FMT_BUFSIZ 1024 /* Conversion buffer size */ /* ** Conversion types fall into various categories as defined by the ** following enumeration. */ #define PH7_FMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */ #define PH7_FMT_FLOAT 2 /* Floating point.%f */ #define PH7_FMT_EXP 3 /* Exponentional notation.%e and %E */ #define PH7_FMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */ #define PH7_FMT_SIZE 5 /* Total number of characters processed so far.%n */ #define PH7_FMT_STRING 6 /* Strings.%s */ #define PH7_FMT_PERCENT 7 /* Percent symbol.%% */ #define PH7_FMT_CHARX 8 /* Characters.%c */ #define PH7_FMT_ERROR 9 /* Used to indicate no such conversion type */ /* ** Allowed values for ph7_fmt_info.flags */ #define PH7_FMT_FLAG_SIGNED 0x01 #define PH7_FMT_FLAG_UNSIGNED 0x02 /* ** Each builtin conversion character (ex: the 'd' in "%d") is described ** by an instance of the following structure */ typedef struct ph7_fmt_info ph7_fmt_info; struct ph7_fmt_info { char fmttype; /* The format field code letter [i.e: 'd','s','x'] */ sxu8 base; /* The base for radix conversion */ int flags; /* One or more of PH7_FMT_FLAG_ constants below */ sxu8 type; /* Conversion paradigm */ char *charset; /* The character set for conversion */ char *prefix; /* Prefix on non-zero values in alt format */ }; #ifndef PH7_OMIT_FLOATING_POINT /* ** "*val" is a double such that 0.1 <= *val < 10.0 ** Return the ascii code for the leading digit of *val, then ** multiply "*val" by 10.0 to renormalize. ** ** Example: ** input: *val = 3.14159 ** output: *val = 1.4159 function return = '3' ** ** The counter *cnt is incremented each time. After counter exceeds ** 16 (the number of significant digits in a 64-bit float) '0' is ** always returned. */ static int vxGetdigit(sxlongreal *val,int *cnt) { sxlongreal d; int digit; if( (*cnt)++ >= 16 ){ return '0'; } digit = (int)*val; d = digit; *val = (*val - d)*10.0; return digit + '0' ; } #endif /* PH7_OMIT_FLOATING_POINT */ /* * The following table is searched linearly, so it is good to put the most frequently * used conversion types first. */ static const ph7_fmt_info aFmt[] = { { 'd', 10, PH7_FMT_FLAG_SIGNED, PH7_FMT_RADIX, "0123456789",0 }, { 's', 0, 0, PH7_FMT_STRING, 0, 0 }, { 'c', 0, 0, PH7_FMT_CHARX, 0, 0 }, { 'x', 16, 0, PH7_FMT_RADIX, "0123456789abcdef", "x0" }, { 'X', 16, 0, PH7_FMT_RADIX, "0123456789ABCDEF", "X0" }, { 'b', 2, 0, PH7_FMT_RADIX, "01", "b0"}, { 'o', 8, 0, PH7_FMT_RADIX, "01234567", "0" }, { 'u', 10, 0, PH7_FMT_RADIX, "0123456789", 0 }, { 'f', 0, PH7_FMT_FLAG_SIGNED, PH7_FMT_FLOAT, 0, 0 }, { 'F', 0, PH7_FMT_FLAG_SIGNED, PH7_FMT_FLOAT, 0, 0 }, { 'e', 0, PH7_FMT_FLAG_SIGNED, PH7_FMT_EXP, "e", 0 }, { 'E', 0, PH7_FMT_FLAG_SIGNED, PH7_FMT_EXP, "E", 0 }, { 'g', 0, PH7_FMT_FLAG_SIGNED, PH7_FMT_GENERIC, "e", 0 }, { 'G', 0, PH7_FMT_FLAG_SIGNED, PH7_FMT_GENERIC, "E", 0 }, { '%', 0, 0, PH7_FMT_PERCENT, 0, 0 } }; /* * Format a given string. * The root program. All variations call this core. * INPUTS: * xConsumer This is a pointer to a function taking four arguments * 1. A pointer to the call context. * 2. A pointer to the list of characters to be output * (Note, this list is NOT null terminated.) * 3. An integer number of characters to be output. * (Note: This number might be zero.) * 4. Upper layer private data. * zIn This is the format string, as in the usual print. * apArg This is a pointer to a list of arguments. */ PH7_PRIVATE sxi32 PH7_InputFormat( int (*xConsumer)(ph7_context *,const char *,int,void *), /* Format consumer */ ph7_context *pCtx, /* call context */ const char *zIn, /* Format string */ int nByte, /* Format string length */ int nArg, /* Total argument of the given arguments */ ph7_value **apArg, /* User arguments */ void *pUserData, /* Last argument to xConsumer() */ int vf /* TRUE if called from vfprintf,vsprintf context */ ) { char spaces[] = " "; #define etSPACESIZE ((int)sizeof(spaces)-1) const char *zCur,*zEnd = &zIn[nByte]; char *zBuf,zWorker[PH7_FMT_BUFSIZ]; /* Working buffer */ const ph7_fmt_info *pInfo; /* Pointer to the appropriate info structure */ int flag_alternateform; /* True if "#" flag is present */ int flag_leftjustify; /* True if "-" flag is present */ int flag_blanksign; /* True if " " flag is present */ int flag_plussign; /* True if "+" flag is present */ int flag_zeropad; /* True if field width constant starts with zero */ ph7_value *pArg; /* Current processed argument */ ph7_int64 iVal; int precision; /* Precision of the current field */ char *zExtra; int c,rc,n; int length; /* Length of the field */ int prefix; sxu8 xtype; /* Conversion paradigm */ int width; /* Width of the current field */ int idx; n = (vf == TRUE) ? 0 : 1; #define NEXT_ARG ( n < nArg ? apArg[n++] : 0 ) /* Start the format process */ for(;;){ zCur = zIn; while( zIn < zEnd && zIn[0] != '%' ){ zIn++; } if( zCur < zIn ){ /* Consume chunk verbatim */ rc = xConsumer(pCtx,zCur,(int)(zIn-zCur),pUserData); if( rc == SXERR_ABORT ){ /* Callback request an operation abort */ break; } } if( zIn >= zEnd ){ /* No more input to process,break immediately */ break; } /* Find out what flags are present */ flag_leftjustify = flag_plussign = flag_blanksign = flag_alternateform = flag_zeropad = 0; zIn++; /* Jump the precent sign */ do{ c = zIn[0]; switch( c ){ case '-': flag_leftjustify = 1; c = 0; break; case '+': flag_plussign = 1; c = 0; break; case ' ': flag_blanksign = 1; c = 0; break; case '#': flag_alternateform = 1; c = 0; break; case '0': flag_zeropad = 1; c = 0; break; case '\'': zIn++; if( zIn < zEnd ){ /* An alternate padding character can be specified by prefixing it with a single quote (') */ c = zIn[0]; for(idx = 0 ; idx < etSPACESIZE ; ++idx ){ spaces[idx] = (char)c; } c = 0; } break; default: break; } }while( c==0 && (zIn++ < zEnd) ); /* Get the field width */ width = 0; while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ width = width*10 + (zIn[0] - '0'); zIn++; } if( zIn < zEnd && zIn[0] == '$' ){ /* Position specifer */ if( width > 0 ){ n = width; if( vf && n > 0 ){ n--; } } zIn++; width = 0; if( zIn < zEnd && zIn[0] == '0' ){ flag_zeropad = 1; zIn++; } while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ width = width*10 + (zIn[0] - '0'); zIn++; } } if( width > PH7_FMT_BUFSIZ-10 ){ width = PH7_FMT_BUFSIZ-10; } /* Get the precision */ precision = -1; if( zIn < zEnd && zIn[0] == '.' ){ precision = 0; zIn++; while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ precision = precision*10 + (zIn[0] - '0'); zIn++; } } if( zIn >= zEnd ){ /* No more input */ break; } /* Fetch the info entry for the field */ pInfo = 0; xtype = PH7_FMT_ERROR; c = zIn[0]; zIn++; /* Jump the format specifer */ for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){ if( c==aFmt[idx].fmttype ){ pInfo = &aFmt[idx]; xtype = pInfo->type; break; } } zBuf = zWorker; /* Point to the working buffer */ length = 0; zExtra = 0; /* ** At this point, variables are initialized as follows: ** ** flag_alternateform TRUE if a '#' is present. ** flag_plussign TRUE if a '+' is present. ** flag_leftjustify TRUE if a '-' is present or if the ** field width was negative. ** flag_zeropad TRUE if the width began with 0. ** the conversion character. ** flag_blanksign TRUE if a ' ' is present. ** width The specified field width. This is ** always non-negative. Zero is the default. ** precision The specified precision. The default ** is -1. */ switch(xtype){ case PH7_FMT_PERCENT: /* A literal percent character */ zWorker[0] = '%'; length = (int)sizeof(char); break; case PH7_FMT_CHARX: /* The argument is treated as an integer, and presented as the character * with that ASCII value */ pArg = NEXT_ARG; if( pArg == 0 ){ c = 0; }else{ c = ph7_value_to_int(pArg); } /* NUL byte is an acceptable value */ zWorker[0] = (char)c; length = (int)sizeof(char); break; case PH7_FMT_STRING: /* the argument is treated as and presented as a string */ pArg = NEXT_ARG; if( pArg == 0 ){ length = 0; }else{ zBuf = (char *)ph7_value_to_string(pArg,&length); } if( length < 1 ){ zBuf = " "; length = (int)sizeof(char); } if( precision>=0 && precisionPH7_FMT_BUFSIZ-40 ){ precision = PH7_FMT_BUFSIZ-40; } #if 1 /* For the format %#x, the value zero is printed "0" not "0x0". ** I think this is stupid.*/ if( iVal==0 ) flag_alternateform = 0; #else /* More sensible: turn off the prefix for octal (to prevent "00"), ** but leave the prefix for hex.*/ if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0; #endif if( pInfo->flags & PH7_FMT_FLAG_SIGNED ){ if( iVal<0 ){ iVal = -iVal; /* Ticket 1433-003 */ if( iVal < 0 ){ /* Overflow */ iVal= 0x7FFFFFFFFFFFFFFF; } prefix = '-'; }else if( flag_plussign ) prefix = '+'; else if( flag_blanksign ) prefix = ' '; else prefix = 0; }else{ if( iVal<0 ){ iVal = -iVal; /* Ticket 1433-003 */ if( iVal < 0 ){ /* Overflow */ iVal= 0x7FFFFFFFFFFFFFFF; } } prefix = 0; } if( flag_zeropad && precisioncharset; base = pInfo->base; do{ /* Convert to ascii */ *(--zBuf) = cset[iVal%base]; iVal = iVal/base; }while( iVal>0 ); } length = &zWorker[PH7_FMT_BUFSIZ-1]-zBuf; for(idx=precision-length; idx>0; idx--){ *(--zBuf) = '0'; /* Zero pad */ } if( prefix ) *(--zBuf) = (char)prefix; /* Add sign */ if( flag_alternateform && pInfo->prefix ){ /* Add "0" or "0x" */ char *pre, x; pre = pInfo->prefix; if( *zBuf!=pre[0] ){ for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x; } } length = &zWorker[PH7_FMT_BUFSIZ-1]-zBuf; break; case PH7_FMT_FLOAT: case PH7_FMT_EXP: case PH7_FMT_GENERIC:{ #ifndef PH7_OMIT_FLOATING_POINT long double realvalue; int exp; /* exponent of real numbers */ double rounder; /* Used for rounding floating point values */ int flag_dp; /* True if decimal point should be shown */ int flag_rtz; /* True if trailing zeros should be removed */ int flag_exp; /* True to force display of the exponent */ int nsd; /* Number of significant digits returned */ pArg = NEXT_ARG; if( pArg == 0 ){ realvalue = 0; }else{ realvalue = ph7_value_to_double(pArg); } if( precision<0 ) precision = 6; /* Set default precision */ if( precision>PH7_FMT_BUFSIZ-40) precision = PH7_FMT_BUFSIZ-40; if( realvalue<0.0 ){ realvalue = -realvalue; prefix = '-'; }else{ if( flag_plussign ) prefix = '+'; else if( flag_blanksign ) prefix = ' '; else prefix = 0; } if( pInfo->type==PH7_FMT_GENERIC && precision>0 ) precision--; rounder = 0.0; #if 0 /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */ for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); #else /* It makes more sense to use 0.5 */ for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1); #endif if( pInfo->type==PH7_FMT_FLOAT ) realvalue += rounder; /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ exp = 0; if( realvalue>0.0 ){ while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } if( exp>350 || exp<-350 ){ zBuf = "NaN"; length = 3; break; } } zBuf = zWorker; /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ flag_exp = xtype==PH7_FMT_EXP; if( xtype!=PH7_FMT_FLOAT ){ realvalue += rounder; if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } } if( xtype==PH7_FMT_GENERIC ){ flag_rtz = !flag_alternateform; if( exp<-4 || exp>precision ){ xtype = PH7_FMT_EXP; }else{ precision = precision - exp; xtype = PH7_FMT_FLOAT; } }else{ flag_rtz = 0; } /* ** The "exp+precision" test causes output to be of type etEXP if ** the precision is too large to fit in buf[]. */ nsd = 0; if( xtype==PH7_FMT_FLOAT && exp+precision0 || flag_alternateform); if( prefix ) *(zBuf++) = (char)prefix; /* Sign */ if( exp<0 ) *(zBuf++) = '0'; /* Digits before "." */ else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue,&nsd); if( flag_dp ) *(zBuf++) = '.'; /* The decimal point */ for(exp++; exp<0 && precision>0; precision--, exp++){ *(zBuf++) = '0'; } while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue,&nsd); *(zBuf--) = 0; /* Null terminate */ if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */ while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0; if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0; } zBuf++; /* point to next free slot */ }else{ /* etEXP or etGENERIC */ flag_dp = (precision>0 || flag_alternateform); if( prefix ) *(zBuf++) = (char)prefix; /* Sign */ *(zBuf++) = (char)vxGetdigit(&realvalue,&nsd); /* First digit */ if( flag_dp ) *(zBuf++) = '.'; /* Decimal point */ while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue,&nsd); zBuf--; /* point to last digit */ if( flag_rtz && flag_dp ){ /* Remove tail zeros */ while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0; if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0; } zBuf++; /* point to next free slot */ if( exp || flag_exp ){ *(zBuf++) = pInfo->charset[0]; if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */ else { *(zBuf++) = '+'; } if( exp>=100 ){ *(zBuf++) = (char)((exp/100)+'0'); /* 100's digit */ exp %= 100; } *(zBuf++) = (char)(exp/10+'0'); /* 10's digit */ *(zBuf++) = (char)(exp%10+'0'); /* 1's digit */ } } /* The converted number is in buf[] and zero terminated.Output it. ** Note that the number is in the usual order, not reversed as with ** integer conversions.*/ length = (int)(zBuf-zWorker); zBuf = zWorker; /* Special case: Add leading zeros if the flag_zeropad flag is ** set and we are not left justified */ if( flag_zeropad && !flag_leftjustify && length < width){ int i; int nPad = width - length; for(i=width; i>=nPad; i--){ zBuf[i] = zBuf[i-nPad]; } i = prefix!=0; while( nPad-- ) zBuf[i++] = '0'; length = width; } #else zBuf = " "; length = (int)sizeof(char); #endif /* PH7_OMIT_FLOATING_POINT */ break; } default: /* Invalid format specifer */ zWorker[0] = '?'; length = (int)sizeof(char); break; } /* ** The text of the conversion is pointed to by "zBuf" and is ** "length" characters long.The field width is "width".Do ** the output. */ if( !flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ while( nspace>=etSPACESIZE ){ rc = xConsumer(pCtx,spaces,etSPACESIZE,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } nspace -= etSPACESIZE; } if( nspace>0 ){ rc = xConsumer(pCtx,spaces,(unsigned int)nspace,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } } } } if( length>0 ){ rc = xConsumer(pCtx,zBuf,(unsigned int)length,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } } if( flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ while( nspace>=etSPACESIZE ){ rc = xConsumer(pCtx,spaces,etSPACESIZE,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } nspace -= etSPACESIZE; } if( nspace>0 ){ rc = xConsumer(pCtx,spaces,(unsigned int)nspace,pUserData); if( rc != SXRET_OK ){ return SXERR_ABORT; /* Consumer routine request an operation abort */ } } } } }/* for(;;) */ return SXRET_OK; } /* * Callback [i.e: Formatted input consumer] of the sprintf function. */ static int sprintfConsumer(ph7_context *pCtx,const char *zInput,int nLen,void *pUserData) { /* Consume directly */ ph7_result_string(pCtx,zInput,nLen); SXUNUSED(pUserData); /* cc warning */ return PH7_OK; } /* * string sprintf(string $format[,mixed $args [, mixed $... ]]) * Return a formatted string. * Parameters * $format * The format string (see block comment above) * Return * A string produced according to the formatting string format. */ static int PH7_builtin_sprintf(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zFormat; int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the string format */ zFormat = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Format the string */ PH7_InputFormat(sprintfConsumer,pCtx,zFormat,nLen,nArg,apArg,0,FALSE); return PH7_OK; } /* * Callback [i.e: Formatted input consumer] of the printf function. */ static int printfConsumer(ph7_context *pCtx,const char *zInput,int nLen,void *pUserData) { ph7_int64 *pCounter = (ph7_int64 *)pUserData; /* Call the VM output consumer directly */ ph7_context_output(pCtx,zInput,nLen); /* Increment counter */ *pCounter += nLen; return PH7_OK; } /* * int64 printf(string $format[,mixed $args[,mixed $... ]]) * Output a formatted string. * Parameters * $format * See sprintf() for a description of format. * Return * The length of the outputted string. */ static int PH7_builtin_printf(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_int64 nCounter = 0; const char *zFormat; int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Extract the string format */ zFormat = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string */ ph7_result_int(pCtx,0); return PH7_OK; } /* Format the string */ PH7_InputFormat(printfConsumer,pCtx,zFormat,nLen,nArg,apArg,(void *)&nCounter,FALSE); /* Return the length of the outputted string */ ph7_result_int64(pCtx,nCounter); return PH7_OK; } /* * int vprintf(string $format,array $args) * Output a formatted string. * Parameters * $format * See sprintf() for a description of format. * Return * The length of the outputted string. */ static int PH7_builtin_vprintf(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_int64 nCounter = 0; const char *zFormat; ph7_hashmap *pMap; SySet sArg; int nLen,n; if( nArg < 2 || !ph7_value_is_string(apArg[0]) || !ph7_value_is_array(apArg[1]) ){ /* Missing/Invalid arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Extract the string format */ zFormat = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string */ ph7_result_int(pCtx,0); return PH7_OK; } /* Point to the hashmap */ pMap = (ph7_hashmap *)apArg[1]->x.pOther; /* Extract arguments from the hashmap */ n = PH7_HashmapValuesToSet(pMap,&sArg); /* Format the string */ PH7_InputFormat(printfConsumer,pCtx,zFormat,nLen,n,(ph7_value **)SySetBasePtr(&sArg),(void *)&nCounter,TRUE); /* Return the length of the outputted string */ ph7_result_int64(pCtx,nCounter); /* Release the container */ SySetRelease(&sArg); return PH7_OK; } /* * int vsprintf(string $format,array $args) * Output a formatted string. * Parameters * $format * See sprintf() for a description of format. * Return * A string produced according to the formatting string format. */ static int PH7_builtin_vsprintf(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zFormat; ph7_hashmap *pMap; SySet sArg; int nLen,n; if( nArg < 2 || !ph7_value_is_string(apArg[0]) || !ph7_value_is_array(apArg[1]) ){ /* Missing/Invalid arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the string format */ zFormat = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Point to hashmap */ pMap = (ph7_hashmap *)apArg[1]->x.pOther; /* Extract arguments from the hashmap */ n = PH7_HashmapValuesToSet(pMap,&sArg); /* Format the string */ PH7_InputFormat(sprintfConsumer,pCtx,zFormat,nLen,n,(ph7_value **)SySetBasePtr(&sArg),0,TRUE); /* Release the container */ SySetRelease(&sArg); return PH7_OK; } /* * Symisc eXtension. * string size_format(int64 $size) * Return a smart string represenation of the given size [i.e: 64-bit integer] * Example: * echo size_format(1*1024*1024*1024);// 1GB * echo size_format(512*1024*1024); // 512 MB * echo size_format(file_size(/path/to/my/file_8192)); //8KB * Parameter * $size * Entity size in bytes. * Return * Formatted string representation of the given size. */ static int PH7_builtin_size_format(ph7_context *pCtx,int nArg,ph7_value **apArg) { /*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/ static const char zUnit[] = {"KMGTPEZ"}; sxi32 nRest,i_32; ph7_int64 iSize; int c = -1; /* index in zUnit[] */ if( nArg < 1 ){ /* Missing argument,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the given size */ iSize = ph7_value_to_int64(apArg[0]); if( iSize < 100 /* Bytes */ ){ /* Don't bother formatting,return immediately */ ph7_result_string(pCtx,"0.1 KB",(int)sizeof("0.1 KB")-1); return PH7_OK; } for(;;){ nRest = (sxi32)(iSize & 0x3FF); iSize >>= 10; c++; if( (iSize & (~0 ^ 1023)) == 0 ){ break; } } nRest /= 100; if( nRest > 9 ){ nRest = 9; } if( iSize > 999 ){ c++; nRest = 9; iSize = 0; } i_32 = (sxi32)iSize; /* Format */ ph7_result_string_format(pCtx,"%d.%d %cB",i_32,nRest,zUnit[c]); return PH7_OK; } #if !defined(PH7_DISABLE_HASH_FUNC) /* * string md5(string $str[,bool $raw_output = false]) * Calculate the md5 hash of a string. * Parameter * $str * Input string * $raw_output * If the optional raw_output is set to TRUE, then the md5 digest * is instead returned in raw binary format with a length of 16. * Return * MD5 Hash as a 32-character hexadecimal string. */ static int PH7_builtin_md5(ph7_context *pCtx,int nArg,ph7_value **apArg) { unsigned char zDigest[16]; int raw_output = FALSE; const void *pIn; int nLen; if( nArg < 1 ){ /* Missing arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the input string */ pIn = (const void *)ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } if( nArg > 1 && ph7_value_is_bool(apArg[1])){ raw_output = ph7_value_to_bool(apArg[1]); } /* Compute the MD5 digest */ SyMD5Compute(pIn,(sxu32)nLen,zDigest); if( raw_output ){ /* Output raw digest */ ph7_result_string(pCtx,(const char *)zDigest,(int)sizeof(zDigest)); }else{ /* Perform a binary to hex conversion */ SyBinToHexConsumer((const void *)zDigest,sizeof(zDigest),HashConsumer,pCtx); } return PH7_OK; } /* * string sha1(string $str[,bool $raw_output = false]) * Calculate the sha1 hash of a string. * Parameter * $str * Input string * $raw_output * If the optional raw_output is set to TRUE, then the md5 digest * is instead returned in raw binary format with a length of 16. * Return * SHA1 Hash as a 40-character hexadecimal string. */ static int PH7_builtin_sha1(ph7_context *pCtx,int nArg,ph7_value **apArg) { unsigned char zDigest[20]; int raw_output = FALSE; const void *pIn; int nLen; if( nArg < 1 ){ /* Missing arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the input string */ pIn = (const void *)ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } if( nArg > 1 && ph7_value_is_bool(apArg[1])){ raw_output = ph7_value_to_bool(apArg[1]); } /* Compute the SHA1 digest */ SySha1Compute(pIn,(sxu32)nLen,zDigest); if( raw_output ){ /* Output raw digest */ ph7_result_string(pCtx,(const char *)zDigest,(int)sizeof(zDigest)); }else{ /* Perform a binary to hex conversion */ SyBinToHexConsumer((const void *)zDigest,sizeof(zDigest),HashConsumer,pCtx); } return PH7_OK; } /* * int64 crc32(string $str) * Calculates the crc32 polynomial of a strin. * Parameter * $str * Input string * Return * CRC32 checksum of the given input (64-bit integer). */ static int PH7_builtin_crc32(ph7_context *pCtx,int nArg,ph7_value **apArg) { const void *pIn; sxu32 nCRC; int nLen; if( nArg < 1 ){ /* Missing arguments,return 0 */ ph7_result_int(pCtx,0); return PH7_OK; } /* Extract the input string */ pIn = (const void *)ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty string */ ph7_result_int(pCtx,0); return PH7_OK; } /* Calculate the sum */ nCRC = SyCrc32(pIn,(sxu32)nLen); /* Return the CRC32 as 64-bit integer */ ph7_result_int64(pCtx,(ph7_int64)nCRC^ 0xFFFFFFFF); return PH7_OK; } #endif /* PH7_DISABLE_HASH_FUNC */ /* * Parse a CSV string and invoke the supplied callback for each processed xhunk. */ PH7_PRIVATE sxi32 PH7_ProcessCsv( const char *zInput, /* Raw input */ int nByte, /* Input length */ int delim, /* Delimiter */ int encl, /* Enclosure */ int escape, /* Escape character */ sxi32 (*xConsumer)(const char *,int,void *), /* User callback */ void *pUserData /* Last argument to xConsumer() */ ) { const char *zEnd = &zInput[nByte]; const char *zIn = zInput; const char *zPtr; int isEnc; /* Start processing */ for(;;){ if( zIn >= zEnd ){ /* No more input to process */ break; } isEnc = 0; zPtr = zIn; /* Find the first delimiter */ while( zIn < zEnd ){ if( zIn[0] == delim && !isEnc){ /* Delimiter found,break imediately */ break; }else if( zIn[0] == encl ){ /* Inside enclosure? */ isEnc = !isEnc; }else if( zIn[0] == escape ){ /* Escape sequence */ zIn++; } /* Advance the cursor */ zIn++; } if( zIn > zPtr ){ int nByte = (int)(zIn-zPtr); sxi32 rc; /* Invoke the supllied callback */ if( zPtr[0] == encl ){ zPtr++; nByte-=2; } if( nByte > 0 ){ rc = xConsumer(zPtr,nByte,pUserData); if( rc == SXERR_ABORT ){ /* User callback request an operation abort */ break; } } } /* Ignore trailing delimiter */ while( zIn < zEnd && zIn[0] == delim ){ zIn++; } } return SXRET_OK; } /* * Default consumer callback for the CSV parsing routine defined above. * All the processed input is insereted into an array passed as the last * argument to this callback. */ PH7_PRIVATE sxi32 PH7_CsvConsumer(const char *zToken,int nTokenLen,void *pUserData) { ph7_value *pArray = (ph7_value *)pUserData; ph7_value sEntry; SyString sToken; /* Insert the token in the given array */ SyStringInitFromBuf(&sToken,zToken,nTokenLen); /* Remove trailing and leading white spcaces and null bytes */ SyStringFullTrimSafe(&sToken); if( sToken.nByte < 1){ return SXRET_OK; } PH7_MemObjInitFromString(pArray->pVm,&sEntry,&sToken); ph7_array_add_elem(pArray,0,&sEntry); PH7_MemObjRelease(&sEntry); return SXRET_OK; } /* * array str_getcsv(string $input[,string $delimiter = ','[,string $enclosure = '"' [,string $escape='\\']]]) * Parse a CSV string into an array. * Parameters * $input * The string to parse. * $delimiter * Set the field delimiter (one character only). * $enclosure * Set the field enclosure character (one character only). * $escape * Set the escape character (one character only). Defaults as a backslash (\) * Return * An indexed array containing the CSV fields or NULL on failure. */ static int PH7_builtin_str_getcsv(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zInput,*zPtr; ph7_value *pArray; int delim = ','; /* Delimiter */ int encl = '"' ; /* Enclosure */ int escape = '\\'; /* Escape character */ int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Extract the raw input */ zInput = ph7_value_to_string(apArg[0],&nLen); if( nArg > 1 ){ int i; if( ph7_value_is_string(apArg[1]) ){ /* Extract the delimiter */ zPtr = ph7_value_to_string(apArg[1],&i); if( i > 0 ){ delim = zPtr[0]; } } if( nArg > 2 ){ if( ph7_value_is_string(apArg[2]) ){ /* Extract the enclosure */ zPtr = ph7_value_to_string(apArg[2],&i); if( i > 0 ){ encl = zPtr[0]; } } if( nArg > 3 ){ if( ph7_value_is_string(apArg[3]) ){ /* Extract the escape character */ zPtr = ph7_value_to_string(apArg[3],&i); if( i > 0 ){ escape = zPtr[0]; } } } } } /* Create our array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); ph7_result_null(pCtx); return PH7_OK; } /* Parse the raw input */ PH7_ProcessCsv(zInput,nLen,delim,encl,escape,PH7_CsvConsumer,pArray); /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * Extract a tag name from a raw HTML input and insert it in the given * container. * Refer to [strip_tags()]. */ static sxi32 AddTag(SySet *pSet,const char *zTag,int nByte) { const char *zEnd = &zTag[nByte]; const char *zPtr; SyString sEntry; /* Strip tags */ for(;;){ while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' || zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){ zTag++; } if( zTag >= zEnd ){ break; } zPtr = zTag; /* Delimit the tag */ while(zTag < zEnd ){ if( (unsigned char)zTag[0] >= 0xc0 ){ /* UTF-8 stream */ zTag++; SX_JMP_UTF8(zTag,zEnd); }else if( !SyisAlphaNum(zTag[0]) ){ break; }else{ zTag++; } } if( zTag > zPtr ){ /* Perform the insertion */ SyStringInitFromBuf(&sEntry,zPtr,(int)(zTag-zPtr)); SyStringFullTrim(&sEntry); SySetPut(pSet,(const void *)&sEntry); } /* Jump the trailing '>' */ zTag++; } return SXRET_OK; } /* * Check if the given HTML tag name is present in the given container. * Return SXRET_OK if present.SXERR_NOTFOUND otherwise. * Refer to [strip_tags()]. */ static sxi32 FindTag(SySet *pSet,const char *zTag,int nByte) { if( SySetUsed(pSet) > 0 ){ const char *zCur,*zEnd = &zTag[nByte]; SyString sTag; while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){ zTag++; } /* Delimit the tag */ zCur = zTag; while(zTag < zEnd ){ if( (unsigned char)zTag[0] >= 0xc0 ){ /* UTF-8 stream */ zTag++; SX_JMP_UTF8(zTag,zEnd); }else if( !SyisAlphaNum(zTag[0]) ){ break; }else{ zTag++; } } SyStringInitFromBuf(&sTag,zCur,zTag-zCur); /* Trim leading white spaces and null bytes */ SyStringLeftTrimSafe(&sTag); if( sTag.nByte > 0 ){ SyString *aEntry,*pEntry; sxi32 rc; sxu32 n; /* Perform the lookup */ aEntry = (SyString *)SySetBasePtr(pSet); for( n = 0 ; n < SySetUsed(pSet) ; ++n ){ pEntry = &aEntry[n]; /* Do the comparison */ rc = SyStringCmp(pEntry,&sTag,SyStrnicmp); if( !rc ){ return SXRET_OK; } } } } /* No such tag */ return SXERR_NOTFOUND; } /* * This function tries to return a string [i.e: in the call context result buffer] * with all NUL bytes,HTML and PHP tags stripped from a given string. * Refer to [strip_tags()]. */ PH7_PRIVATE sxi32 PH7_StripTagsFromString(ph7_context *pCtx,const char *zIn,int nByte,const char *zTaglist,int nTaglen) { const char *zEnd = &zIn[nByte]; const char *zPtr,*zTag; SySet sSet; /* initialize the set of allowed tags */ SySetInit(&sSet,&pCtx->pVm->sAllocator,sizeof(SyString)); if( nTaglen > 0 ){ /* Set of allowed tags */ AddTag(&sSet,zTaglist,nTaglen); } /* Set the empty string */ ph7_result_string(pCtx,"",0); /* Start processing */ for(;;){ if(zIn >= zEnd){ /* No more input to process */ break; } zPtr = zIn; /* Find a tag */ while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){ zIn++; } if( zIn > zPtr ){ /* Consume raw input */ ph7_result_string(pCtx,zPtr,(int)(zIn-zPtr)); } /* Ignore trailing null bytes */ while( zIn < zEnd && zIn[0] == 0 ){ zIn++; } if(zIn >= zEnd){ /* No more input to process */ break; } if( zIn[0] == '<' ){ sxi32 rc; zTag = zIn++; /* Delimit the tag */ while( zIn < zEnd && zIn[0] != '>' ){ zIn++; } if( zIn < zEnd ){ zIn++; /* Ignore the trailing closing tag */ } /* Query the set */ rc = FindTag(&sSet,zTag,(int)(zIn-zTag)); if( rc == SXRET_OK ){ /* Keep the tag */ ph7_result_string(pCtx,zTag,(int)(zIn-zTag)); } } } /* Cleanup */ SySetRelease(&sSet); return SXRET_OK; } /* * string strip_tags(string $str[,string $allowable_tags]) * Strip HTML and PHP tags from a string. * Parameters * $str * The input string. * $allowable_tags * You can use the optional second parameter to specify tags which should not be stripped. * Return * Returns the stripped string. */ static int PH7_builtin_strip_tags(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zTaglist = 0; const char *zString; int nTaglen = 0; int nLen; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Point to the raw string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nArg > 1 && ph7_value_is_string(apArg[1]) ){ /* Allowed tag */ zTaglist = ph7_value_to_string(apArg[1],&nTaglen); } /* Process input */ PH7_StripTagsFromString(pCtx,zString,nLen,zTaglist,nTaglen); return PH7_OK; } /* * string str_shuffle(string $str) * Randomly shuffles a string. * Parameters * $str * The input string. * Return * Returns the shuffled string. */ static int PH7_builtin_str_shuffle(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString; int nLen,i,c; sxu32 iR; if( nArg < 1 ){ /* Missing arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Nothing to shuffle */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Shuffle the string */ for( i = 0 ; i < nLen ; ++i ){ /* Generate a random number first */ iR = ph7_context_random_num(pCtx); /* Extract a random offset */ c = zString[iR % nLen]; /* Append it */ ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); } return PH7_OK; } /* * array str_split(string $string[,int $split_length = 1 ]) * Convert a string to an array. * Parameters * $str * The input string. * $split_length * Maximum length of the chunk. * Return * If the optional split_length parameter is specified, the returned array * will be broken down into chunks with each being split_length in length, otherwise * each chunk will be one character in length. FALSE is returned if split_length is less than 1. * If the split_length length exceeds the length of string, the entire string is returned * as the first (and only) array element. */ static int PH7_builtin_str_split(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zEnd; ph7_value *pArray,*pValue; int split_len; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the target string */ zString = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Nothing to process,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } split_len = (int)sizeof(char); if( nArg > 1 ){ /* Split length */ split_len = ph7_value_to_int(apArg[1]); if( split_len < 1 ){ /* Invalid length,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } if( split_len > nLen ){ split_len = nLen; } } /* Create the array and the scalar value */ pArray = ph7_context_new_array(pCtx); /*Chunk value */ pValue = ph7_context_new_scalar(pCtx); if( pValue == 0 || pArray == 0 ){ /* Return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the end of the string */ zEnd = &zString[nLen]; /* Perform the requested operation */ for(;;){ int nMax; if( zString >= zEnd ){ /* No more input to process */ break; } nMax = (int)(zEnd-zString); if( nMax < split_len ){ split_len = nMax; } /* Copy the current chunk */ ph7_value_string(pValue,zString,split_len); /* Insert it */ ph7_array_add_elem(pArray,0,pValue); /* Will make it's own copy */ /* reset the string cursor */ ph7_value_reset_string_cursor(pValue); /* Update position */ zString += split_len; } /* * Return the array. * Don't worry about freeing memory, everything will be automatically released * upon we return from this function. */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * Tokenize a raw string and extract the first non-space token. * Refer to [strspn()]. */ static sxi32 ExtractNonSpaceToken(const char **pzIn,const char *zEnd,SyString *pOut) { const char *zIn = *pzIn; const char *zPtr; /* Ignore leading white spaces */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } if( zIn >= zEnd ){ /* End of input */ return SXERR_EOF; } zPtr = zIn; /* Extract the token */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){ zIn++; } SyStringInitFromBuf(pOut,zPtr,zIn-zPtr); /* Synchronize pointers */ *pzIn = zIn; /* Return to the caller */ return SXRET_OK; } /* * Check if the given string contains only characters from the given mask. * return the longest match. * Refer to [strspn()]. */ static int LongestStringMask(const char *zString,int nLen,const char *zMask,int nMaskLen) { const char *zEnd = &zString[nLen]; const char *zIn = zString; int i,c; for(;;){ if( zString >= zEnd ){ break; } /* Extract current character */ c = zString[0]; /* Perform the lookup */ for( i = 0 ; i < nMaskLen ; i++ ){ if( c == zMask[i] ){ /* Character found */ break; } } if( i >= nMaskLen ){ /* Character not in the current mask,break immediately */ break; } /* Advance cursor */ zString++; } /* Longest match */ return (int)(zString-zIn); } /* * Do the reverse operation of the previous function [i.e: LongestStringMask()]. * Refer to [strcspn()]. */ static int LongestStringMask2(const char *zString,int nLen,const char *zMask,int nMaskLen) { const char *zEnd = &zString[nLen]; const char *zIn = zString; int i,c; for(;;){ if( zString >= zEnd ){ break; } /* Extract current character */ c = zString[0]; /* Perform the lookup */ for( i = 0 ; i < nMaskLen ; i++ ){ if( c == zMask[i] ){ break; } } if( i < nMaskLen ){ /* Character in the current mask,break immediately */ break; } /* Advance cursor */ zString++; } /* Longest match */ return (int)(zString-zIn); } /* * int strspn(string $str,string $mask[,int $start[,int $length]]) * Finds the length of the initial segment of a string consisting entirely * of characters contained within a given mask. * Parameters * $str * The input string. * $mask * The list of allowable characters. * $start * The position in subject to start searching. * If start is given and is non-negative, then strspn() will begin examining * subject at the start'th position. For instance, in the string 'abcdef', the character * at position 0 is 'a', the character at position 2 is 'c', and so forth. * If start is given and is negative, then strspn() will begin examining subject at the * start'th position from the end of subject. * $length * The length of the segment from subject to examine. * If length is given and is non-negative, then subject will be examined for length * characters after the starting position. * If lengthis given and is negative, then subject will be examined from the starting * position up to length characters from the end of subject. * Return * Returns the length of the initial segment of subject which consists entirely of characters * in mask. */ static int PH7_builtin_strspn(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zMask,*zEnd; int iMasklen,iLen; SyString sToken; int iCount = 0; int rc; if( nArg < 2 ){ /* Missing agruments,return zero */ ph7_result_int(pCtx,0); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&iLen); /* Extract the mask */ zMask = ph7_value_to_string(apArg[1],&iMasklen); if( iLen < 1 || iMasklen < 1 ){ /* Nothing to process,return zero */ ph7_result_int(pCtx,0); return PH7_OK; } if( nArg > 2 ){ int nOfft; /* Extract the offset */ nOfft = ph7_value_to_int(apArg[2]); if( nOfft < 0 ){ const char *zBase = &zString[iLen + nOfft]; if( zBase > zString ){ iLen = (int)(&zString[iLen]-zBase); zString = zBase; }else{ /* Invalid offset */ ph7_result_int(pCtx,0); return PH7_OK; } }else{ if( nOfft >= iLen ){ /* Invalid offset */ ph7_result_int(pCtx,0); return PH7_OK; }else{ /* Update offset */ zString += nOfft; iLen -= nOfft; } } if( nArg > 3 ){ int iUserlen; /* Extract the desired length */ iUserlen = ph7_value_to_int(apArg[3]); if( iUserlen > 0 && iUserlen < iLen ){ iLen = iUserlen; } } } /* Point to the end of the string */ zEnd = &zString[iLen]; /* Extract the first non-space token */ rc = ExtractNonSpaceToken(&zString,zEnd,&sToken); if( rc == SXRET_OK && sToken.nByte > 0 ){ /* Compare against the current mask */ iCount = LongestStringMask(sToken.zString,(int)sToken.nByte,zMask,iMasklen); } /* Longest match */ ph7_result_int(pCtx,iCount); return PH7_OK; } /* * int strcspn(string $str,string $mask[,int $start[,int $length]]) * Find length of initial segment not matching mask. * Parameters * $str * The input string. * $mask * The list of not allowed characters. * $start * The position in subject to start searching. * If start is given and is non-negative, then strspn() will begin examining * subject at the start'th position. For instance, in the string 'abcdef', the character * at position 0 is 'a', the character at position 2 is 'c', and so forth. * If start is given and is negative, then strspn() will begin examining subject at the * start'th position from the end of subject. * $length * The length of the segment from subject to examine. * If length is given and is non-negative, then subject will be examined for length * characters after the starting position. * If lengthis given and is negative, then subject will be examined from the starting * position up to length characters from the end of subject. * Return * Returns the length of the segment as an integer. */ static int PH7_builtin_strcspn(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zMask,*zEnd; int iMasklen,iLen; SyString sToken; int iCount = 0; int rc; if( nArg < 2 ){ /* Missing agruments,return zero */ ph7_result_int(pCtx,0); return PH7_OK; } /* Extract the target string */ zString = ph7_value_to_string(apArg[0],&iLen); /* Extract the mask */ zMask = ph7_value_to_string(apArg[1],&iMasklen); if( iLen < 1 ){ /* Nothing to process,return zero */ ph7_result_int(pCtx,0); return PH7_OK; } if( iMasklen < 1 ){ /* No given mask,return the string length */ ph7_result_int(pCtx,iLen); return PH7_OK; } if( nArg > 2 ){ int nOfft; /* Extract the offset */ nOfft = ph7_value_to_int(apArg[2]); if( nOfft < 0 ){ const char *zBase = &zString[iLen + nOfft]; if( zBase > zString ){ iLen = (int)(&zString[iLen]-zBase); zString = zBase; }else{ /* Invalid offset */ ph7_result_int(pCtx,0); return PH7_OK; } }else{ if( nOfft >= iLen ){ /* Invalid offset */ ph7_result_int(pCtx,0); return PH7_OK; }else{ /* Update offset */ zString += nOfft; iLen -= nOfft; } } if( nArg > 3 ){ int iUserlen; /* Extract the desired length */ iUserlen = ph7_value_to_int(apArg[3]); if( iUserlen > 0 && iUserlen < iLen ){ iLen = iUserlen; } } } /* Point to the end of the string */ zEnd = &zString[iLen]; /* Extract the first non-space token */ rc = ExtractNonSpaceToken(&zString,zEnd,&sToken); if( rc == SXRET_OK && sToken.nByte > 0 ){ /* Compare against the current mask */ iCount = LongestStringMask2(sToken.zString,(int)sToken.nByte,zMask,iMasklen); } /* Longest match */ ph7_result_int(pCtx,iCount); return PH7_OK; } /* * string strpbrk(string $haystack,string $char_list) * Search a string for any of a set of characters. * Parameters * $haystack * The string where char_list is looked for. * $char_list * This parameter is case sensitive. * Return * Returns a string starting from the character found, or FALSE if it is not found. */ static int PH7_builtin_strpbrk(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zString,*zList,*zEnd; int iLen,iListLen,i,c; sxu32 nOfft,nMax; sxi32 rc; if( nArg < 2 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the haystack and the char list */ zString = ph7_value_to_string(apArg[0],&iLen); zList = ph7_value_to_string(apArg[1],&iListLen); if( iLen < 1 ){ /* Nothing to process,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Point to the end of the string */ zEnd = &zString[iLen]; nOfft = nMax = SXU32_HIGH; /* perform the requested operation */ for( i = 0 ; i < iListLen ; i++ ){ c = zList[i]; rc = SyByteFind(zString,(sxu32)iLen,c,&nMax); if( rc == SXRET_OK ){ if( nMax < nOfft ){ nOfft = nMax; } } } if( nOfft == SXU32_HIGH ){ /* No such substring,return FALSE */ ph7_result_bool(pCtx,0); }else{ /* Return the substring */ ph7_result_string(pCtx,&zString[nOfft],(int)(zEnd-&zString[nOfft])); } return PH7_OK; } /* * string soundex(string $str) * Calculate the soundex key of a string. * Parameters * $str * The input string. * Return * Returns the soundex key as a string. * Note: * This implementation is based on the one found in the SQLite3 * source tree. */ static int PH7_builtin_soundex(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn; char zResult[8]; int i, j; static const unsigned char iCode[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, }; if( nArg < 1 ){ /* Missing arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } zIn = (unsigned char *)ph7_value_to_string(apArg[0],0); for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){} if( zIn[i] ){ unsigned char prevcode = iCode[zIn[i]&0x7f]; zResult[0] = (char)SyToUpper(zIn[i]); for(j=1; j<4 && zIn[i]; i++){ int code = iCode[zIn[i]&0x7f]; if( code>0 ){ if( code!=prevcode ){ prevcode = (unsigned char)code; zResult[j++] = (char)code + '0'; } }else{ prevcode = 0; } } while( j<4 ){ zResult[j++] = '0'; } ph7_result_string(pCtx,zResult,4); }else{ ph7_result_string(pCtx,"?000",4); } return PH7_OK; } /* * string wordwrap(string $str[,int $width = 75[,string $break = "\n"]]) * Wraps a string to a given number of characters. * Parameters * $str * The input string. * $width * The column width. * $break * The line is broken using the optional break parameter. * Return * Returns the given string wrapped at the specified column. */ static int PH7_builtin_wordwrap(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn,*zEnd,*zBreak; int iLen,iBreaklen,iChunk; if( nArg < 1 ){ /* Missing arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the input string */ zIn = ph7_value_to_string(apArg[0],&iLen); if( iLen < 1 ){ /* Nothing to process,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Chunk length */ iChunk = 75; iBreaklen = 0; zBreak = ""; /* cc warning */ if( nArg > 1 ){ iChunk = ph7_value_to_int(apArg[1]); if( iChunk < 1 ){ iChunk = 75; } if( nArg > 2 ){ zBreak = ph7_value_to_string(apArg[2],&iBreaklen); } } if( iBreaklen < 1 ){ /* Set a default column break */ #ifdef __WINNT__ zBreak = "\r\n"; iBreaklen = (int)sizeof("\r\n")-1; #else zBreak = "\n"; iBreaklen = (int)sizeof(char); #endif } /* Perform the requested operation */ zEnd = &zIn[iLen]; for(;;){ int nMax; if( zIn >= zEnd ){ /* No more input to process */ break; } nMax = (int)(zEnd-zIn); if( iChunk > nMax ){ iChunk = nMax; } /* Append the column first */ ph7_result_string(pCtx,zIn,iChunk); /* Will make it's own copy */ /* Advance the cursor */ zIn += iChunk; if( zIn < zEnd ){ /* Append the line break */ ph7_result_string(pCtx,zBreak,iBreaklen); } } return PH7_OK; } /* * Check if the given character is a member of the given mask. * Return TRUE on success. FALSE otherwise. * Refer to [strtok()]. */ static int CheckMask(int c,const char *zMask,int nMasklen,int *pOfft) { int i; for( i = 0 ; i < nMasklen ; ++i ){ if( c == zMask[i] ){ if( pOfft ){ *pOfft = i; } return TRUE; } } return FALSE; } /* * Extract a single token from the input stream. * Refer to [strtok()]. */ static sxi32 ExtractToken(const char **pzIn,const char *zEnd,const char *zMask,int nMasklen,SyString *pOut) { const char *zIn = *pzIn; const char *zPtr; /* Ignore leading delimiter */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0],zMask,nMasklen,0) ){ zIn++; } if( zIn >= zEnd ){ /* End of input */ return SXERR_EOF; } zPtr = zIn; /* Extract the token */ while( zIn < zEnd ){ if( (unsigned char)zIn[0] >= 0xc0 ){ /* UTF-8 stream */ zIn++; SX_JMP_UTF8(zIn,zEnd); }else{ if( CheckMask(zIn[0],zMask,nMasklen,0) ){ break; } zIn++; } } SyStringInitFromBuf(pOut,zPtr,zIn-zPtr); /* Update the cursor */ *pzIn = zIn; /* Return to the caller */ return SXRET_OK; } /* strtok auxiliary private data */ typedef struct strtok_aux_data strtok_aux_data; struct strtok_aux_data { const char *zDup; /* Complete duplicate of the input */ const char *zIn; /* Current input stream */ const char *zEnd; /* End of input */ }; /* * string strtok(string $str,string $token) * string strtok(string $token) * strtok() splits a string (str) into smaller strings (tokens), with each token * being delimited by any character from token. That is, if you have a string like * "This is an example string" you could tokenize this string into its individual * words by using the space character as the token. * Note that only the first call to strtok uses the string argument. Every subsequent * call to strtok only needs the token to use, as it keeps track of where it is in * the current string. To start over, or to tokenize a new string you simply call strtok * with the string argument again to initialize it. Note that you may put multiple tokens * in the token parameter. The string will be tokenized when any one of the characters in * the argument are found. * Parameters * $str * The string being split up into smaller strings (tokens). * $token * The delimiter used when splitting up str. * Return * Current token or FALSE on EOF. */ static int PH7_builtin_strtok(ph7_context *pCtx,int nArg,ph7_value **apArg) { strtok_aux_data *pAux; const char *zMask; SyString sToken; int nMasklen; sxi32 rc; if( nArg < 2 ){ /* Extract top aux data */ pAux = (strtok_aux_data *)ph7_context_peek_aux_data(pCtx); if( pAux == 0 ){ /* No aux data,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } nMasklen = 0; zMask = ""; /* cc warning */ if( nArg > 0 ){ /* Extract the mask */ zMask = ph7_value_to_string(apArg[0],&nMasklen); } if( nMasklen < 1 ){ /* Invalid mask,return FALSE */ ph7_context_free_chunk(pCtx,(void *)pAux->zDup); ph7_context_free_chunk(pCtx,pAux); (void)ph7_context_pop_aux_data(pCtx); ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the token */ rc = ExtractToken(&pAux->zIn,pAux->zEnd,zMask,nMasklen,&sToken); if( rc != SXRET_OK ){ /* EOF ,discard the aux data */ ph7_context_free_chunk(pCtx,(void *)pAux->zDup); ph7_context_free_chunk(pCtx,pAux); (void)ph7_context_pop_aux_data(pCtx); ph7_result_bool(pCtx,0); }else{ /* Return the extracted token */ ph7_result_string(pCtx,sToken.zString,(int)sToken.nByte); } }else{ const char *zInput,*zCur; char *zDup; int nLen; /* Extract the raw input */ zCur = zInput = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Empty input,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the mask */ zMask = ph7_value_to_string(apArg[1],&nMasklen); if( nMasklen < 1 ){ /* Set a default mask */ #define TOK_MASK " \n\t\r\f" zMask = TOK_MASK; nMasklen = (int)sizeof(TOK_MASK) - 1; #undef TOK_MASK } /* Extract a single token */ rc = ExtractToken(&zInput,&zInput[nLen],zMask,nMasklen,&sToken); if( rc != SXRET_OK ){ /* Empty input */ ph7_result_bool(pCtx,0); return PH7_OK; }else{ /* Return the extracted token */ ph7_result_string(pCtx,sToken.zString,(int)sToken.nByte); } /* Create our auxilliary data and copy the input */ pAux = (strtok_aux_data *)ph7_context_alloc_chunk(pCtx,sizeof(strtok_aux_data),TRUE,FALSE); if( pAux ){ nLen -= (int)(zInput-zCur); if( nLen < 1 ){ ph7_context_free_chunk(pCtx,pAux); return PH7_OK; } /* Duplicate input */ zDup = (char *)ph7_context_alloc_chunk(pCtx,(unsigned int)(nLen+1),TRUE,FALSE); if( zDup ){ SyMemcpy(zInput,zDup,(sxu32)nLen); /* Register the aux data */ pAux->zDup = pAux->zIn = zDup; pAux->zEnd = &zDup[nLen]; ph7_context_push_aux_data(pCtx,pAux); } } } return PH7_OK; } /* * string str_pad(string $input,int $pad_length[,string $pad_string = " " [,int $pad_type = STR_PAD_RIGHT]]) * Pad a string to a certain length with another string * Parameters * $input * The input string. * $pad_length * If the value of pad_length is negative, less than, or equal to the length of the input * string, no padding takes place. * $pad_string * Note: * The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly * divided by the pad_string's length. * $pad_type * Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type * is not specified it is assumed to be STR_PAD_RIGHT. * Return * The padded string. */ static int PH7_builtin_str_pad(ph7_context *pCtx,int nArg,ph7_value **apArg) { int iLen,iPadlen,iType,i,iDiv,iStrpad,iRealPad,jPad; const char *zIn,*zPad; if( nArg < 2 ){ /* Missing arguments,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Extract the target string */ zIn = ph7_value_to_string(apArg[0],&iLen); /* Padding length */ iRealPad = iPadlen = ph7_value_to_int(apArg[1]); if( iPadlen > 0 ){ iPadlen -= iLen; } if( iPadlen < 1 ){ /* Return the string verbatim */ ph7_result_string(pCtx,zIn,iLen); return PH7_OK; } zPad = " "; /* Whitespace padding */ iStrpad = (int)sizeof(char); iType = 1 ; /* STR_PAD_RIGHT */ if( nArg > 2 ){ /* Padding string */ zPad = ph7_value_to_string(apArg[2],&iStrpad); if( iStrpad < 1 ){ /* Empty string */ zPad = " "; /* Whitespace padding */ iStrpad = (int)sizeof(char); } if( nArg > 3 ){ /* Padd type */ iType = ph7_value_to_int(apArg[3]); if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){ iType = 1 ; /* STR_PAD_RIGHT */ } } } iDiv = 1; if( iType == 2 ){ iDiv = 2; /* STR_PAD_BOTH */ } /* Perform the requested operation */ if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){ jPad = iStrpad; for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){ /* Padding */ if( (int)ph7_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){ break; } ph7_result_string(pCtx,zPad,jPad); } if( iType == 0 /* STR_PAD_LEFT */ ){ while( (int)ph7_context_result_buf_length(pCtx) + iLen < iRealPad ){ jPad = iRealPad - (iLen + (int)ph7_context_result_buf_length(pCtx) ); if( jPad > iStrpad ){ jPad = iStrpad; } if( jPad < 1){ break; } ph7_result_string(pCtx,zPad,jPad); } } } if( iLen > 0 ){ /* Append the input string */ ph7_result_string(pCtx,zIn,iLen); } if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){ for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){ /* Padding */ if( (int)ph7_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){ break; } ph7_result_string(pCtx,zPad,iStrpad); } while( (int)ph7_context_result_buf_length(pCtx) < iRealPad ){ jPad = iRealPad - (int)ph7_context_result_buf_length(pCtx); if( jPad > iStrpad ){ jPad = iStrpad; } if( jPad < 1){ break; } ph7_result_string(pCtx,zPad,jPad); } } return PH7_OK; } /* * String replacement private data. */ typedef struct str_replace_data str_replace_data; struct str_replace_data { /* The following two fields are only used by the strtr function */ SyBlob *pWorker; /* Working buffer */ ProcStringMatch xMatch; /* Pattern match routine */ /* The following two fields are only used by the str_replace function */ SySet *pCollector; /* Argument collector*/ ph7_context *pCtx; /* Call context */ }; /* * Remove a substring. */ #define STRDEL(SRC,SLEN,OFFT,ILEN){\ for(;;){\ if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\ }\ } /* * Shift right and insert algorithm. */ #define SHIFTRANDINSERT(SRC,LEN,OFFT,ENTRY,ELEN){\ sxu32 INLEN = LEN - OFFT;\ for(;;){\ if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \ }\ for(;;){\ if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\ }\ } /* * Replace all occurrences of the search string at offset (nOfft) with the given * replacement string [i.e: zReplace]. */ static int StringReplace(SyBlob *pWorker,sxu32 nOfft,int nLen,const char *zReplace,int nReplen) { char *zInput = (char *)SyBlobData(pWorker); sxu32 n,m; n = SyBlobLength(pWorker); m = nOfft; /* Delete the old entry */ STRDEL(zInput,n,m,nLen); SyBlobLength(pWorker) -= nLen; if( nReplen > 0 ){ sxi32 iRep = nReplen; sxi32 rc; /* * Make sure the working buffer is big enough to hold the replacement * string. */ rc = SyBlobAppend(pWorker,0/* Grow without an append operation*/,(sxu32)nReplen); if( rc != SXRET_OK ){ /* Simply ignore any memory failure problem */ return SXRET_OK; } /* Perform the insertion now */ zInput = (char *)SyBlobData(pWorker); n = SyBlobLength(pWorker); SHIFTRANDINSERT(zInput,n,nOfft,zReplace,iRep); SyBlobLength(pWorker) += nReplen; } return SXRET_OK; } /* * String replacement walker callback. * The following callback is invoked for each array entry that hold * the replace string. * Refer to the strtr() implementation for more information. */ static int StringReplaceWalker(ph7_value *pKey,ph7_value *pData,void *pUserData) { str_replace_data *pRepData = (str_replace_data *)pUserData; const char *zTarget,*zReplace; SyBlob *pWorker; int tLen,nLen; sxu32 nOfft; sxi32 rc; /* Point to the working buffer */ pWorker = pRepData->pWorker; if( !ph7_value_is_string(pKey) ){ /* Target and replace must be a string */ return PH7_OK; } /* Extract the target and the replace */ zTarget = ph7_value_to_string(pKey,&tLen); if( tLen < 1 ){ /* Empty target,return immediately */ return PH7_OK; } /* Perform a pattern search */ rc = pRepData->xMatch(SyBlobData(pWorker),SyBlobLength(pWorker),(const void *)zTarget,(sxu32)tLen,&nOfft); if( rc != SXRET_OK ){ /* Pattern not found */ return PH7_OK; } /* Extract the replace string */ zReplace = ph7_value_to_string(pData,&nLen); /* Perform the replace process */ StringReplace(pWorker,nOfft,tLen,zReplace,nLen); /* All done */ return PH7_OK; } /* * The following walker callback is invoked by the str_rplace() function inorder * to collect search/replace string. * This callback is invoked only if the given argument is of type array. */ static int StrReplaceWalker(ph7_value *pKey,ph7_value *pData,void *pUserData) { str_replace_data *pRep = (str_replace_data *)pUserData; SyString sWorker; const char *zIn; int nByte; /* Extract a string representation of the given argument */ zIn = ph7_value_to_string(pData,&nByte); SyStringInitFromBuf(&sWorker,0,0); if( nByte > 0 ){ char *zDup; /* Duplicate the chunk */ zDup = (char *)ph7_context_alloc_chunk(pRep->pCtx,(unsigned int)nByte,FALSE, TRUE /* Release the chunk automatically,upon this context is destroyd */ ); if( zDup == 0 ){ /* Ignore any memory failure problem */ ph7_context_throw_error(pRep->pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); return PH7_OK; } SyMemcpy(zIn,zDup,(sxu32)nByte); /* Save the chunk */ SyStringInitFromBuf(&sWorker,zDup,nByte); } /* Save for later processing */ SySetPut(pRep->pCollector,(const void *)&sWorker); /* All done */ SXUNUSED(pKey); /* cc warning */ return PH7_OK; } /* * mixed str_replace(mixed $search,mixed $replace,mixed $subject[,int &$count ]) * mixed str_ireplace(mixed $search,mixed $replace,mixed $subject[,int &$count ]) * Replace all occurrences of the search string with the replacement string. * Parameters * If search and replace are arrays, then str_replace() takes a value from each * array and uses them to search and replace on subject. If replace has fewer values * than search, then an empty string is used for the rest of replacement values. * If search is an array and replace is a string, then this replacement string is used * for every value of search. The converse would not make sense, though. * If search or replace are arrays, their elements are processed first to last. * $search * The value being searched for, otherwise known as the needle. An array may be used * to designate multiple needles. * $replace * The replacement value that replaces found search values. An array may be used * to designate multiple replacements. * $subject * The string or array being searched and replaced on, otherwise known as the haystack. * If subject is an array, then the search and replace is performed with every entry * of subject, and the return value is an array as well. * $count (Not used) * If passed, this will be set to the number of replacements performed. * Return * This function returns a string or an array with the replaced values. */ static int PH7_builtin_str_replace(ph7_context *pCtx,int nArg,ph7_value **apArg) { SyString sTemp,*pSearch,*pReplace; ProcStringMatch xMatch; const char *zIn,*zFunc; str_replace_data sRep; SyBlob sWorker; SySet sReplace; SySet sSearch; int rep_str; int nByte; sxi32 rc; if( nArg < 3 ){ /* Missing/Invalid arguments,return null */ ph7_result_null(pCtx); return PH7_OK; } /* Initialize fields */ SySetInit(&sSearch,&pCtx->pVm->sAllocator,sizeof(SyString)); SySetInit(&sReplace,&pCtx->pVm->sAllocator,sizeof(SyString)); SyBlobInit(&sWorker,&pCtx->pVm->sAllocator); SyZero(&sRep,sizeof(str_replace_data)); sRep.pCtx = pCtx; sRep.pCollector = &sSearch; rep_str = 0; /* Extract the subject */ zIn = ph7_value_to_string(apArg[2],&nByte); if( nByte < 1 ){ /* Nothing to replace,return the empty string */ ph7_result_string(pCtx,"",0); return PH7_OK; } /* Copy the subject */ SyBlobAppend(&sWorker,(const void *)zIn,(sxu32)nByte); /* Search string */ if( ph7_value_is_array(apArg[0]) ){ /* Collect search string */ ph7_array_walk(apArg[0],StrReplaceWalker,&sRep); }else{ /* Single pattern */ zIn = ph7_value_to_string(apArg[0],&nByte); if( nByte < 1 ){ /* Return the subject untouched since no search string is available */ ph7_result_value(pCtx,apArg[2]/* Subject as thrird argument*/); return PH7_OK; } SyStringInitFromBuf(&sTemp,zIn,nByte); /* Save for later processing */ SySetPut(&sSearch,(const void *)&sTemp); } /* Replace string */ if( ph7_value_is_array(apArg[1]) ){ /* Collect replace string */ sRep.pCollector = &sReplace; ph7_array_walk(apArg[1],StrReplaceWalker,&sRep); }else{ /* Single needle */ zIn = ph7_value_to_string(apArg[1],&nByte); rep_str = 1; SyStringInitFromBuf(&sTemp,zIn,nByte); /* Save for later processing */ SySetPut(&sReplace,(const void *)&sTemp); } /* Reset loop cursors */ SySetResetCursor(&sSearch); SySetResetCursor(&sReplace); pReplace = pSearch = 0; /* cc warning */ SyStringInitFromBuf(&sTemp,"",0); /* Extract function name */ zFunc = ph7_function_name(pCtx); /* Set the default pattern match routine */ xMatch = SyBlobSearch; if( SyStrncmp(zFunc,"str_ireplace",sizeof("str_ireplace") - 1) == 0 ){ /* Case insensitive pattern match */ xMatch = iPatternMatch; } /* Start the replace process */ while( SXRET_OK == SySetGetNextEntry(&sSearch,(void **)&pSearch) ){ sxu32 nCount,nOfft; if( pSearch->nByte < 1 ){ /* Empty string,ignore */ continue; } /* Extract the replace string */ if( rep_str ){ pReplace = (SyString *)SySetPeek(&sReplace); }else{ if( SXRET_OK != SySetGetNextEntry(&sReplace,(void **)&pReplace) ){ /* Sepecial case when 'replace set' has fewer values than the search set. * An empty string is used for the rest of replacement values */ pReplace = 0; } } if( pReplace == 0 ){ /* Use an empty string instead */ pReplace = &sTemp; } nOfft = nCount = 0; for(;;){ if( nCount >= SyBlobLength(&sWorker) ){ break; } /* Perform a pattern lookup */ rc = xMatch(SyBlobDataAt(&sWorker,nCount),SyBlobLength(&sWorker) - nCount,(const void *)pSearch->zString, pSearch->nByte,&nOfft); if( rc != SXRET_OK ){ /* Pattern not found */ break; } /* Perform the replace operation */ StringReplace(&sWorker,nCount+nOfft,(int)pSearch->nByte,pReplace->zString,(int)pReplace->nByte); /* Increment offset counter */ nCount += nOfft + pReplace->nByte; } } /* All done,clean-up the mess left behind */ ph7_result_string(pCtx,(const char *)SyBlobData(&sWorker),(int)SyBlobLength(&sWorker)); SySetRelease(&sSearch); SySetRelease(&sReplace); SyBlobRelease(&sWorker); return PH7_OK; } /* * string strtr(string $str,string $from,string $to) * string strtr(string $str,array $replace_pairs) * Translate characters or replace substrings. * Parameters * $str * The string being translated. * $from * The string being translated to to. * $to * The string replacing from. * $replace_pairs * The replace_pairs parameter may be used instead of to and * from, in which case it's an array in the form array('from' => 'to', ...). * Return * The translated string. * If replace_pairs contains a key which is an empty string (""), FALSE will be returned. */ static int PH7_builtin_strtr(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn; int nLen; if( nArg < 1 ){ /* Nothing to replace,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 || nArg < 2 ){ /* Invalid arguments */ ph7_result_string(pCtx,zIn,nLen); return PH7_OK; } if( nArg == 2 && ph7_value_is_array(apArg[1]) ){ str_replace_data sRepData; SyBlob sWorker; /* Initilaize the working buffer */ SyBlobInit(&sWorker,&pCtx->pVm->sAllocator); /* Copy raw string */ SyBlobAppend(&sWorker,(const void *)zIn,(sxu32)nLen); /* Init our replace data instance */ sRepData.pWorker = &sWorker; sRepData.xMatch = SyBlobSearch; /* Iterate throw array entries and perform the replace operation.*/ ph7_array_walk(apArg[1],StringReplaceWalker,&sRepData); /* All done, return the result string */ ph7_result_string(pCtx,(const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker)); /* Will make it's own copy */ /* Clean-up */ SyBlobRelease(&sWorker); }else{ int i,flen,tlen,c,iOfft; const char *zFrom,*zTo; if( nArg < 3 ){ /* Nothing to replace */ ph7_result_string(pCtx,zIn,nLen); return PH7_OK; } /* Extract given arguments */ zFrom = ph7_value_to_string(apArg[1],&flen); zTo = ph7_value_to_string(apArg[2],&tlen); if( flen < 1 || tlen < 1 ){ /* Nothing to replace */ ph7_result_string(pCtx,zIn,nLen); return PH7_OK; } /* Start the replace process */ for( i = 0 ; i < nLen ; ++i ){ c = zIn[i]; if( CheckMask(c,zFrom,flen,&iOfft) ){ if ( iOfft < tlen ){ c = zTo[iOfft]; } } ph7_result_string(pCtx,(const char *)&c,(int)sizeof(char)); } } return PH7_OK; } /* * Parse an INI string. * According to wikipedia * The INI file format is an informal standard for configuration files for some platforms or software. * INI files are simple text files with a basic structure composed of "sections" and "properties". * Format * Properties * The basic element contained in an INI file is the property. Every property has a name and a value * delimited by an equals sign (=). The name appears to the left of the equals sign. * Example: * name=value * Sections * Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself * in square brackets ([ and ]). All properties after the section declaration are associated with that section. * There is no explicit "end of section" delimiter; sections end at the next section declaration * or the end of the file. Sections may not be nested. * Example: * [section] * Comments * Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored. * This function return an array holding parsed values on success.FALSE otherwise. */ PH7_PRIVATE sxi32 PH7_ParseIniString(ph7_context *pCtx,const char *zIn,sxu32 nByte,int bProcessSection) { ph7_value *pCur,*pArray,*pSection,*pWorker,*pValue; const char *zCur,*zEnd = &zIn[nByte]; SyHashEntry *pEntry; SyString sEntry; SyHash sHash; int c; /* Create an empty array and worker variables */ pArray = ph7_context_new_array(pCtx); pWorker = ph7_context_new_scalar(pCtx); pValue = ph7_context_new_scalar(pCtx); if( pArray == 0 || pWorker == 0 || pValue == 0){ /* Out of memory */ ph7_context_throw_error(pCtx,PH7_CTX_ERR,"PH7 is running out of memory"); /* Return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } SyHashInit(&sHash,&pCtx->pVm->sAllocator,0,0); pCur = pArray; /* Start the parse process */ for(;;){ /* Ignore leading white spaces */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){ zIn++; } if( zIn >= zEnd ){ /* No more input to process */ break; } if( zIn[0] == ';' || zIn[0] == '#' ){ /* Comment til the end of line */ zIn++; while(zIn < zEnd && zIn[0] != '\n' ){ zIn++; } continue; } /* Reset the string cursor of the working variable */ ph7_value_reset_string_cursor(pWorker); if( zIn[0] == '[' ){ /* Section: Extract the section name */ zIn++; zCur = zIn; while( zIn < zEnd && zIn[0] != ']' ){ zIn++; } if( zIn > zCur && bProcessSection ){ /* Save the section name */ SyStringInitFromBuf(&sEntry,zCur,(int)(zIn-zCur)); SyStringFullTrim(&sEntry); ph7_value_string(pWorker,sEntry.zString,(int)sEntry.nByte); if( sEntry.nByte > 0 ){ /* Associate an array with the section */ pSection = ph7_context_new_array(pCtx); if( pSection ){ ph7_array_add_elem(pArray,pWorker/*Section name*/,pSection); pCur = pSection; } } } zIn++; /* Trailing square brackets ']' */ }else{ ph7_value *pOldCur; int is_array; int iLen; /* Properties */ is_array = 0; zCur = zIn; iLen = 0; /* cc warning */ pOldCur = pCur; while( zIn < zEnd && zIn[0] != '=' ){ if( zIn[0] == '[' && !is_array ){ /* Array */ iLen = (int)(zIn-zCur); is_array = 1; if( iLen > 0 ){ ph7_value *pvArr = 0; /* cc warning */ /* Query the hashtable */ SyStringInitFromBuf(&sEntry,zCur,iLen); SyStringFullTrim(&sEntry); pEntry = SyHashGet(&sHash,(const void *)sEntry.zString,sEntry.nByte); if( pEntry ){ pvArr = (ph7_value *)SyHashEntryGetUserData(pEntry); }else{ /* Create an empty array */ pvArr = ph7_context_new_array(pCtx); if( pvArr ){ /* Save the entry */ SyHashInsert(&sHash,(const void *)sEntry.zString,sEntry.nByte,pvArr); /* Insert the entry */ ph7_value_reset_string_cursor(pWorker); ph7_value_string(pWorker,sEntry.zString,(int)sEntry.nByte); ph7_array_add_elem(pCur,pWorker,pvArr); ph7_value_reset_string_cursor(pWorker); } } if( pvArr ){ pCur = pvArr; } } while ( zIn < zEnd && zIn[0] != ']' ){ zIn++; } } zIn++; } if( !is_array ){ iLen = (int)(zIn-zCur); } /* Trim the key */ SyStringInitFromBuf(&sEntry,zCur,iLen); SyStringFullTrim(&sEntry); if( sEntry.nByte > 0 ){ if( !is_array ){ /* Save the key name */ ph7_value_string(pWorker,sEntry.zString,(int)sEntry.nByte); } /* extract key value */ ph7_value_reset_string_cursor(pValue); zIn++; /* '=' */ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ zIn++; } if( zIn < zEnd ){ zCur = zIn; c = zIn[0]; if( c == '"' || c == '\'' ){ zIn++; /* Delimit the value */ while( zIn < zEnd ){ if ( zIn[0] == c && zIn[-1] != '\\' ){ break; } zIn++; } if( zIn < zEnd ){ zIn++; } }else{ while( zIn < zEnd ){ if( zIn[0] == '\n' ){ if( zIn[-1] != '\\' ){ break; } }else if( zIn[0] == ';' || zIn[0] == '#' ){ /* Inline comments */ break; } zIn++; } } /* Trim the value */ SyStringInitFromBuf(&sEntry,zCur,(int)(zIn-zCur)); SyStringFullTrim(&sEntry); if( c == '"' || c == '\'' ){ SyStringTrimLeadingChar(&sEntry,c); SyStringTrimTrailingChar(&sEntry,c); } if( sEntry.nByte > 0 ){ ph7_value_string(pValue,sEntry.zString,(int)sEntry.nByte); } /* Insert the key and it's value */ ph7_array_add_elem(pCur,is_array ? 0 /*Automatic index assign */: pWorker,pValue); } }else{ while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){ zIn++; } } pCur = pOldCur; } } SyHashRelease(&sHash); /* Return the parse of the INI string */ ph7_result_value(pCtx,pArray); return SXRET_OK; } /* * array parse_ini_string(string $ini[,bool $process_sections = false[,int $scanner_mode = INI_SCANNER_NORMAL ]]) * Parse a configuration string. * Parameters * $ini * The contents of the ini file being parsed. * $process_sections * By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names * and settings included. The default for process_sections is FALSE. * $scanner_mode (Not used) * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied * then option values will not be parsed. * Return * The settings are returned as an associative array on success, and FALSE on failure. */ static int PH7_builtin_parse_ini_string(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIni; int nByte; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid arguments,return FALSE*/ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the raw INI buffer */ zIni = ph7_value_to_string(apArg[0],&nByte); /* Process the INI buffer*/ PH7_ParseIniString(pCtx,zIni,(sxu32)nByte,(nArg > 1) ? ph7_value_to_bool(apArg[1]) : 0); return PH7_OK; } /* * Ctype Functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * bool ctype_alnum(string $text) * Checks if all of the characters in the provided string, text, are alphanumeric. * Parameters * $text * The tested string. * Return * TRUE if every character in text is either a letter or a digit, FALSE otherwise. */ static int PH7_builtin_ctype_alnum(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( !SyisAlphaNum(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_alpha(string $text) * Checks if all of the characters in the provided string, text, are alphabetic. * Parameters * $text * The tested string. * Return * TRUE if every character in text is a letter from the current locale, FALSE otherwise. */ static int PH7_builtin_ctype_alpha(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( !SyisAlpha(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_cntrl(string $text) * Checks if all of the characters in the provided string, text, are control characters. * Parameters * $text * The tested string. * Return * TRUE if every character in text is a control characters,FALSE otherwise. */ static int PH7_builtin_ctype_cntrl(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( zIn[0] >= 0xc0 ){ /* UTF-8 stream */ break; } if( !SyisCtrl(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_digit(string $text) * Checks if all of the characters in the provided string, text, are numerical. * Parameters * $text * The tested string. * Return * TRUE if every character in the string text is a decimal digit, FALSE otherwise. */ static int PH7_builtin_ctype_digit(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( zIn[0] >= 0xc0 ){ /* UTF-8 stream */ break; } if( !SyisDigit(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_xdigit(string $text) * Check for character(s) representing a hexadecimal digit. * Parameters * $text * The tested string. * Return * Returns TRUE if every character in text is a hexadecimal 'digit', that is * a decimal digit or a character from [A-Fa-f] , FALSE otherwise. */ static int PH7_builtin_ctype_xdigit(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( zIn[0] >= 0xc0 ){ /* UTF-8 stream */ break; } if( !SyisHex(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_graph(string $text) * Checks if all of the characters in the provided string, text, creates visible output. * Parameters * $text * The tested string. * Return * Returns TRUE if every character in text is printable and actually creates visible output * (no white space), FALSE otherwise. */ static int PH7_builtin_ctype_graph(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( zIn[0] >= 0xc0 ){ /* UTF-8 stream */ break; } if( !SyisGraph(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_print(string $text) * Checks if all of the characters in the provided string, text, are printable. * Parameters * $text * The tested string. * Return * Returns TRUE if every character in text will actually create output (including blanks). * Returns FALSE if text contains control characters or characters that do not have any output * or control function at all. */ static int PH7_builtin_ctype_print(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( zIn[0] >= 0xc0 ){ /* UTF-8 stream */ break; } if( !SyisPrint(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_punct(string $text) * Checks if all of the characters in the provided string, text, are punctuation character. * Parameters * $text * The tested string. * Return * Returns TRUE if every character in text is printable, but neither letter * digit or blank, FALSE otherwise. */ static int PH7_builtin_ctype_punct(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( zIn[0] >= 0xc0 ){ /* UTF-8 stream */ break; } if( !SyisPunct(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_space(string $text) * Checks if all of the characters in the provided string, text, creates whitespace. * Parameters * $text * The tested string. * Return * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. * Besides the blank character this also includes tab, vertical tab, line feed, carriage return * and form feed characters. */ static int PH7_builtin_ctype_space(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( zIn[0] >= 0xc0 ){ /* UTF-8 stream */ break; } if( !SyisSpace(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_lower(string $text) * Checks if all of the characters in the provided string, text, are lowercase letters. * Parameters * $text * The tested string. * Return * Returns TRUE if every character in text is a lowercase letter in the current locale. */ static int PH7_builtin_ctype_lower(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( !SyisLower(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * bool ctype_upper(string $text) * Checks if all of the characters in the provided string, text, are uppercase letters. * Parameters * $text * The tested string. * Return * Returns TRUE if every character in text is a uppercase letter in the current locale. */ static int PH7_builtin_ctype_upper(ph7_context *pCtx,int nArg,ph7_value **apArg) { const unsigned char *zIn,*zEnd; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the target string */ zIn = (const unsigned char *)ph7_value_to_string(apArg[0],&nLen); zEnd = &zIn[nLen]; if( nLen < 1 ){ /* Empty string,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the requested operation */ for(;;){ if( zIn >= zEnd ){ /* If we reach the end of the string,then the test succeeded. */ ph7_result_bool(pCtx,1); return PH7_OK; } if( !SyisUpper(zIn[0]) ){ break; } /* Point to the next character */ zIn++; } /* The test failed,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* * Date/Time functions * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Devel. */ #include #ifdef __WINNT__ /* GetSystemTime() */ #include #ifdef _WIN32_WCE /* ** WindowsCE does not have a localtime() function. So create a ** substitute. ** Taken from the SQLite3 source tree. ** Status: Public domain */ struct tm *__cdecl localtime(const time_t *t) { static struct tm y; FILETIME uTm, lTm; SYSTEMTIME pTm; ph7_int64 t64; t64 = *t; t64 = (t64 + 11644473600)*10000000; uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); uTm.dwHighDateTime= (DWORD)(t64 >> 32); FileTimeToLocalFileTime(&uTm,&lTm); FileTimeToSystemTime(&lTm,&pTm); y.tm_year = pTm.wYear - 1900; y.tm_mon = pTm.wMonth - 1; y.tm_wday = pTm.wDayOfWeek; y.tm_mday = pTm.wDay; y.tm_hour = pTm.wHour; y.tm_min = pTm.wMinute; y.tm_sec = pTm.wSecond; return &y; } #endif /*_WIN32_WCE */ #elif defined(__UNIXES__) #include #endif /* __WINNT__*/ /* * int64 time(void) * Current Unix timestamp * Parameters * None. * Return * Returns the current time measured in the number of seconds * since the Unix Epoch (January 1 1970 00:00:00 GMT). */ static int PH7_builtin_time(ph7_context *pCtx,int nArg,ph7_value **apArg) { time_t tt; SXUNUSED(nArg); /* cc warning */ SXUNUSED(apArg); /* Extract the current time */ time(&tt); /* Return as 64-bit integer */ ph7_result_int64(pCtx,(ph7_int64)tt); return PH7_OK; } /* * string/float microtime([ bool $get_as_float = false ]) * microtime() returns the current Unix timestamp with microseconds. * Parameters * $get_as_float * If used and set to TRUE, microtime() will return a float instead of a string * as described in the return values section below. * Return * By default, microtime() returns a string in the form "msec sec", where sec * is the current time measured in the number of seconds since the Unix * epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds * that have elapsed since sec expressed in seconds. * If get_as_float is set to TRUE, then microtime() returns a float, which represents * the current time in seconds since the Unix epoch accurate to the nearest microsecond. */ static int PH7_builtin_microtime(ph7_context *pCtx,int nArg,ph7_value **apArg) { int bFloat = 0; sytime sTime; #if defined(__UNIXES__) struct timeval tv; gettimeofday(&tv,0); sTime.tm_sec = (long)tv.tv_sec; sTime.tm_usec = (long)tv.tv_usec; #else time_t tt; time(&tt); sTime.tm_sec = (long)tt; sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC); #endif /* __UNIXES__ */ if( nArg > 0 ){ bFloat = ph7_value_to_bool(apArg[0]); } if( bFloat ){ /* Return as float */ ph7_result_double(pCtx,(double)sTime.tm_sec); }else{ /* Return as string */ ph7_result_string_format(pCtx,"%ld %ld",sTime.tm_usec,sTime.tm_sec); } return PH7_OK; } /* * array getdate ([ int $timestamp = time() ]) * Get date/time information. * Parameter * $timestamp: The optional timestamp parameter is an integer Unix timestamp * that defaults to the current local time if a timestamp is not given. * In other words, it defaults to the value of time(). * Returns * Returns an associative array of information related to the timestamp. * Elements from the returned associative array are as follows: * KEY VALUE * --------- ------- * "seconds" Numeric representation of seconds 0 to 59 * "minutes" Numeric representation of minutes 0 to 59 * "hours" Numeric representation of hours 0 to 23 * "mday" Numeric representation of the day of the month 1 to 31 * "wday" Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday) * "mon" Numeric representation of a month 1 through 12 * "year" A full numeric representation of a year, 4 digits Examples: 1999 or 2003 * "yday" Numeric representation of the day of the year 0 through 365 * "weekday" A full textual representation of the day of the week Sunday through Saturday * "month" A full textual representation of a month, such as January or March January through December * 0 Seconds since the Unix Epoch, similar to the values returned by time() and used by date(). * NOTE: * NULL is returned on failure. */ static int PH7_builtin_getdate(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pValue,*pArray; Sytm sTm; if( nArg < 1 ){ #ifdef __WINNT__ SYSTEMTIME sOS; GetSystemTime(&sOS); SYSTEMTIME_TO_SYTM(&sOS,&sTm); #else struct tm *pTm; time_t t; time(&t); pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); #endif }else{ /* Use the given timestamp */ time_t t; struct tm *pTm; #ifdef __WINNT__ #ifdef _MSC_VER #if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ #pragma warning(disable:4996) /* _CRT_SECURE...*/ #endif #endif #endif if( ph7_value_is_int(apArg[0]) ){ t = (time_t)ph7_value_to_int64(apArg[0]); pTm = localtime(&t); if( pTm == 0 ){ time(&t); } }else{ time(&t); } pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); } /* Element value */ pValue = ph7_context_new_scalar(pCtx); if( pValue == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array */ /* Seconds */ ph7_value_int(pValue,sTm.tm_sec); ph7_array_add_strkey_elem(pArray,"seconds",pValue); /* Minutes */ ph7_value_int(pValue,sTm.tm_min); ph7_array_add_strkey_elem(pArray,"minutes",pValue); /* Hours */ ph7_value_int(pValue,sTm.tm_hour); ph7_array_add_strkey_elem(pArray,"hours",pValue); /* mday */ ph7_value_int(pValue,sTm.tm_mday); ph7_array_add_strkey_elem(pArray,"mday",pValue); /* wday */ ph7_value_int(pValue,sTm.tm_wday); ph7_array_add_strkey_elem(pArray,"wday",pValue); /* mon */ ph7_value_int(pValue,sTm.tm_mon+1); ph7_array_add_strkey_elem(pArray,"mon",pValue); /* year */ ph7_value_int(pValue,sTm.tm_year); ph7_array_add_strkey_elem(pArray,"year",pValue); /* yday */ ph7_value_int(pValue,sTm.tm_yday); ph7_array_add_strkey_elem(pArray,"yday",pValue); /* Weekday */ ph7_value_string(pValue,SyTimeGetDay(sTm.tm_wday),-1); ph7_array_add_strkey_elem(pArray,"weekday",pValue); /* Month */ ph7_value_reset_string_cursor(pValue); ph7_value_string(pValue,SyTimeGetMonth(sTm.tm_mon),-1); ph7_array_add_strkey_elem(pArray,"month",pValue); /* Seconds since the epoch */ ph7_value_int64(pValue,(ph7_int64)time(0)); ph7_array_add_intkey_elem(pArray,0 /* Index zero */,pValue); /* Return the freshly created array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * mixed gettimeofday([ bool $return_float = false ] ) * Returns an associative array containing the data returned from the system call. * Parameters * $return_float * When set to TRUE, a float instead of an array is returned. * Return * By default an array is returned. If return_float is set, then * a float is returned. */ static int PH7_builtin_gettimeofday(ph7_context *pCtx,int nArg,ph7_value **apArg) { int bFloat = 0; sytime sTime; #if defined(__UNIXES__) struct timeval tv; gettimeofday(&tv,0); sTime.tm_sec = (long)tv.tv_sec; sTime.tm_usec = (long)tv.tv_usec; #else time_t tt; time(&tt); sTime.tm_sec = (long)tt; sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC); #endif /* __UNIXES__ */ if( nArg > 0 ){ bFloat = ph7_value_to_bool(apArg[0]); } if( bFloat ){ /* Return as float */ ph7_result_double(pCtx,(double)sTime.tm_sec); }else{ /* Return an associative array */ ph7_value *pValue,*pArray; /* Create a new array */ pArray = ph7_context_new_array(pCtx); /* Element value */ pValue = ph7_context_new_scalar(pCtx); if( pValue == 0 || pArray == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Fill the array */ /* sec */ ph7_value_int64(pValue,sTime.tm_sec); ph7_array_add_strkey_elem(pArray,"sec",pValue); /* usec */ ph7_value_int64(pValue,sTime.tm_usec); ph7_array_add_strkey_elem(pArray,"usec",pValue); /* Return the array */ ph7_result_value(pCtx,pArray); } return PH7_OK; } /* Check if the given year is leap or not */ #define IS_LEAP_YEAR(YEAR) (YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1) /* ISO-8601 numeric representation of the day of the week */ static const int aISO8601[] = { 7 /* Sunday */,1 /* Monday */,2,3,4,5,6 }; /* * Format a given date string. * Supported format: (Taken from PHP online docs) * character Description * d Day of the month * D A textual representation of a days * j Day of the month without leading zeros * l A full textual representation of the day of the week * N ISO-8601 numeric representation of the day of the week * w Numeric representation of the day of the week * z The day of the year (starting from 0) * F A full textual representation of a month, such as January or March * m Numeric representation of a month, with leading zeros 01 through 12 * M A short textual representation of a month, three letters Jan through Dec * n Numeric representation of a month, without leading zeros 1 through 12 * t Number of days in the given month 28 through 31 * L Whether it's a leap year 1 if it is a leap year, 0 otherwise. * o ISO-8601 year number. This has the same value as Y, except that if the ISO week number * (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0) Examples: 1999 or 2003 * Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003 * y A two digit representation of a year Examples: 99 or 03 * a Lowercase Ante meridiem and Post meridiem am or pm * A Uppercase Ante meridiem and Post meridiem AM or PM * g 12-hour format of an hour without leading zeros 1 through 12 * G 24-hour format of an hour without leading zeros 0 through 23 * h 12-hour format of an hour with leading zeros 01 through 12 * H 24-hour format of an hour with leading zeros 00 through 23 * i Minutes with leading zeros 00 to 59 * s Seconds, with leading zeros 00 through 59 * u Microseconds Example: 654321 * e Timezone identifier Examples: UTC, GMT, Atlantic/Azores * I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise. * r RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200 * U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) * S English ordinal suffix for the day of the month, 2 characters * O Difference to Greenwich time (GMT) in hours * Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those * east of UTC is always positive. * c ISO 8601 date */ static sxi32 DateFormat(ph7_context *pCtx,const char *zIn,int nLen,Sytm *pTm) { const char *zEnd = &zIn[nLen]; const char *zCur; /* Start the format process */ for(;;){ if( zIn >= zEnd ){ /* No more input to process */ break; } switch(zIn[0]){ case 'd': /* Day of the month, 2 digits with leading zeros */ ph7_result_string_format(pCtx,"%02d",pTm->tm_mday); break; case 'D': /*A textual representation of a day, three letters*/ zCur = SyTimeGetDay(pTm->tm_wday); ph7_result_string(pCtx,zCur,3); break; case 'j': /* Day of the month without leading zeros */ ph7_result_string_format(pCtx,"%d",pTm->tm_mday); break; case 'l': /* A full textual representation of the day of the week */ zCur = SyTimeGetDay(pTm->tm_wday); ph7_result_string(pCtx,zCur,-1/*Compute length automatically*/); break; case 'N':{ /* ISO-8601 numeric representation of the day of the week */ ph7_result_string_format(pCtx,"%d",aISO8601[pTm->tm_wday % 7 ]); break; } case 'w': /*Numeric representation of the day of the week*/ ph7_result_string_format(pCtx,"%d",pTm->tm_wday); break; case 'z': /*The day of the year*/ ph7_result_string_format(pCtx,"%d",pTm->tm_yday); break; case 'F': /*A full textual representation of a month, such as January or March*/ zCur = SyTimeGetMonth(pTm->tm_mon); ph7_result_string(pCtx,zCur,-1/*Compute length automatically*/); break; case 'm': /*Numeric representation of a month, with leading zeros*/ ph7_result_string_format(pCtx,"%02d",pTm->tm_mon + 1); break; case 'M': /*A short textual representation of a month, three letters*/ zCur = SyTimeGetMonth(pTm->tm_mon); ph7_result_string(pCtx,zCur,3); break; case 'n': /*Numeric representation of a month, without leading zeros*/ ph7_result_string_format(pCtx,"%d",pTm->tm_mon + 1); break; case 't':{ static const int aMonDays[] = {31,29,31,30,31,30,31,31,30,31,30,31 }; int nDays = aMonDays[pTm->tm_mon % 12 ]; if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){ nDays = 28; } /*Number of days in the given month*/ ph7_result_string_format(pCtx,"%d",nDays); break; } case 'L':{ int isLeap = IS_LEAP_YEAR(pTm->tm_year); /* Whether it's a leap year */ ph7_result_string_format(pCtx,"%d",isLeap); break; } case 'o': /* ISO-8601 year number.*/ ph7_result_string_format(pCtx,"%4d",pTm->tm_year); break; case 'Y': /* A full numeric representation of a year, 4 digits */ ph7_result_string_format(pCtx,"%4d",pTm->tm_year); break; case 'y': /*A two digit representation of a year*/ ph7_result_string_format(pCtx,"%02d",pTm->tm_year%100); break; case 'a': /* Lowercase Ante meridiem and Post meridiem */ ph7_result_string(pCtx,pTm->tm_hour > 12 ? "pm" : "am",2); break; case 'A': /* Uppercase Ante meridiem and Post meridiem */ ph7_result_string(pCtx,pTm->tm_hour > 12 ? "PM" : "AM",2); break; case 'g': /* 12-hour format of an hour without leading zeros*/ ph7_result_string_format(pCtx,"%d",1+(pTm->tm_hour%12)); break; case 'G': /* 24-hour format of an hour without leading zeros */ ph7_result_string_format(pCtx,"%d",pTm->tm_hour); break; case 'h': /* 12-hour format of an hour with leading zeros */ ph7_result_string_format(pCtx,"%02d",1+(pTm->tm_hour%12)); break; case 'H': /* 24-hour format of an hour with leading zeros */ ph7_result_string_format(pCtx,"%02d",pTm->tm_hour); break; case 'i': /* Minutes with leading zeros */ ph7_result_string_format(pCtx,"%02d",pTm->tm_min); break; case 's': /* second with leading zeros */ ph7_result_string_format(pCtx,"%02d",pTm->tm_sec); break; case 'u': /* Microseconds */ ph7_result_string_format(pCtx,"%u",pTm->tm_sec * SX_USEC_PER_SEC); break; case 'S':{ /* English ordinal suffix for the day of the month, 2 characters */ static const char zSuffix[] = "thstndrdthththththth"; int v = pTm->tm_mday; ph7_result_string(pCtx,&zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)],(int)sizeof(char) * 2); break; } case 'e': /* Timezone identifier */ zCur = pTm->tm_zone; if( zCur == 0 ){ /* Assume GMT */ zCur = "GMT"; } ph7_result_string(pCtx,zCur,-1); break; case 'I': /* Whether or not the date is in daylight saving time */ #ifdef __WINNT__ #ifdef _MSC_VER #ifndef _WIN32_WCE _get_daylight(&pTm->tm_isdst); #endif #endif #endif ph7_result_string_format(pCtx,"%d",pTm->tm_isdst == 1); break; case 'r': /* RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 */ ph7_result_string_format(pCtx,"%.3s, %02d %.3s %4d %02d:%02d:%02d", SyTimeGetDay(pTm->tm_wday), pTm->tm_mday, SyTimeGetMonth(pTm->tm_mon), pTm->tm_year, pTm->tm_hour, pTm->tm_min, pTm->tm_sec ); break; case 'U':{ time_t tt; /* Seconds since the Unix Epoch */ time(&tt); ph7_result_string_format(pCtx,"%u",(unsigned int)tt); break; } case 'O': case 'P': /* Difference to Greenwich time (GMT) in hours */ ph7_result_string_format(pCtx,"%+05d",pTm->tm_gmtoff); break; case 'Z': /* Timezone offset in seconds. The offset for timezones west of UTC * is always negative, and for those east of UTC is always positive. */ ph7_result_string_format(pCtx,"%+05d",pTm->tm_gmtoff); break; case 'c': /* ISO 8601 date */ ph7_result_string_format(pCtx,"%4d-%02d-%02dT%02d:%02d:%02d%+05d", pTm->tm_year, pTm->tm_mon+1, pTm->tm_mday, pTm->tm_hour, pTm->tm_min, pTm->tm_sec, pTm->tm_gmtoff ); break; case '\\': zIn++; /* Expand verbatim */ if( zIn < zEnd ){ ph7_result_string(pCtx,zIn,(int)sizeof(char)); } break; default: /* Unknown format specifer,expand verbatim */ ph7_result_string(pCtx,zIn,(int)sizeof(char)); break; } /* Point to the next character */ zIn++; } return SXRET_OK; } /* * PH7 implementation of the strftime() function. * The following formats are supported: * %a An abbreviated textual representation of the day * %A A full textual representation of the day * %d Two-digit day of the month (with leading zeros) * %e Day of the month, with a space preceding single digits. * %j Day of the year, 3 digits with leading zeros * %u ISO-8601 numeric representation of the day of the week 1 (for Monday) though 7 (for Sunday) * %w Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday) * %U Week number of the given year, starting with the first Sunday as the first week * %V ISO-8601:1988 week number of the given year, starting with the first week of the year with at least * 4 weekdays, with Monday being the start of the week. * %W A numeric representation of the week of the year * %b Abbreviated month name, based on the locale * %B Full month name, based on the locale * %h Abbreviated month name, based on the locale (an alias of %b) * %m Two digit representation of the month * %C Two digit representation of the century (year divided by 100, truncated to an integer) * %g Two digit representation of the year going by ISO-8601:1988 standards (see %V) * %G The full four-digit version of %g * %y Two digit representation of the year * %Y Four digit representation for the year * %H Two digit representation of the hour in 24-hour format * %I Two digit representation of the hour in 12-hour format * %l (lower-case 'L') Hour in 12-hour format, with a space preceeding single digits * %M Two digit representation of the minute * %p UPPER-CASE 'AM' or 'PM' based on the given time * %P lower-case 'am' or 'pm' based on the given time * %r Same as "%I:%M:%S %p" * %R Same as "%H:%M" * %S Two digit representation of the second * %T Same as "%H:%M:%S" * %X Preferred time representation based on locale, without the date * %z Either the time zone offset from UTC or the abbreviation * %Z The time zone offset/abbreviation option NOT given by %z * %c Preferred date and time stamp based on local * %D Same as "%m/%d/%y" * %F Same as "%Y-%m-%d" * %s Unix Epoch Time timestamp (same as the time() function) * %x Preferred date representation based on locale, without the time * %n A newline character ("\n") * %t A Tab character ("\t") * %% A literal percentage character ("%") */ static int PH7_Strftime( ph7_context *pCtx, /* Call context */ const char *zIn, /* Input string */ int nLen, /* Input length */ Sytm *pTm /* Parse of the given time */ ) { const char *zCur,*zEnd = &zIn[nLen]; int c; /* Start the format process */ for(;;){ zCur = zIn; while(zIn < zEnd && zIn[0] != '%' ){ zIn++; } if( zIn > zCur ){ /* Consume input verbatim */ ph7_result_string(pCtx,zCur,(int)(zIn-zCur)); } zIn++; /* Jump the percent sign */ if( zIn >= zEnd ){ /* No more input to process */ break; } c = zIn[0]; /* Act according to the current specifer */ switch(c){ case '%': /* A literal percentage character ("%") */ ph7_result_string(pCtx,"%",(int)sizeof(char)); break; case 't': /* A Tab character */ ph7_result_string(pCtx,"\t",(int)sizeof(char)); break; case 'n': /* A newline character */ ph7_result_string(pCtx,"\n",(int)sizeof(char)); break; case 'a': /* An abbreviated textual representation of the day */ ph7_result_string(pCtx,SyTimeGetDay(pTm->tm_wday),(int)sizeof(char)*3); break; case 'A': /* A full textual representation of the day */ ph7_result_string(pCtx,SyTimeGetDay(pTm->tm_wday),-1/*Compute length automatically*/); break; case 'e': /* Day of the month, 2 digits with leading space for single digit*/ ph7_result_string_format(pCtx,"%2d",pTm->tm_mday); break; case 'd': /* Two-digit day of the month (with leading zeros) */ ph7_result_string_format(pCtx,"%02d",pTm->tm_mon+1); break; case 'j': /*The day of the year,3 digits with leading zeros*/ ph7_result_string_format(pCtx,"%03d",pTm->tm_yday); break; case 'u': /* ISO-8601 numeric representation of the day of the week */ ph7_result_string_format(pCtx,"%d",aISO8601[pTm->tm_wday % 7 ]); break; case 'w': /* Numeric representation of the day of the week */ ph7_result_string_format(pCtx,"%d",pTm->tm_wday); break; case 'b': case 'h': /*A short textual representation of a month, three letters (Not based on locale)*/ ph7_result_string(pCtx,SyTimeGetMonth(pTm->tm_mon),(int)sizeof(char)*3); break; case 'B': /* Full month name (Not based on locale) */ ph7_result_string(pCtx,SyTimeGetMonth(pTm->tm_mon),-1/*Compute length automatically*/); break; case 'm': /*Numeric representation of a month, with leading zeros*/ ph7_result_string_format(pCtx,"%02d",pTm->tm_mon + 1); break; case 'C': /* Two digit representation of the century */ ph7_result_string_format(pCtx,"%2d",pTm->tm_year/100); break; case 'y': case 'g': /* Two digit representation of the year */ ph7_result_string_format(pCtx,"%2d",pTm->tm_year%100); break; case 'Y': case 'G': /* Four digit representation of the year */ ph7_result_string_format(pCtx,"%4d",pTm->tm_year); break; case 'I': /* 12-hour format of an hour with leading zeros */ ph7_result_string_format(pCtx,"%02d",1+(pTm->tm_hour%12)); break; case 'l': /* 12-hour format of an hour with leading space */ ph7_result_string_format(pCtx,"%2d",1+(pTm->tm_hour%12)); break; case 'H': /* 24-hour format of an hour with leading zeros */ ph7_result_string_format(pCtx,"%02d",pTm->tm_hour); break; case 'M': /* Minutes with leading zeros */ ph7_result_string_format(pCtx,"%02d",pTm->tm_min); break; case 'S': /* Seconds with leading zeros */ ph7_result_string_format(pCtx,"%02d",pTm->tm_sec); break; case 'z': case 'Z': /* Timezone identifier */ zCur = pTm->tm_zone; if( zCur == 0 ){ /* Assume GMT */ zCur = "GMT"; } ph7_result_string(pCtx,zCur,-1); break; case 'T': case 'X': /* Same as "%H:%M:%S" */ ph7_result_string_format(pCtx,"%02d:%02d:%02d",pTm->tm_hour,pTm->tm_min,pTm->tm_sec); break; case 'R': /* Same as "%H:%M" */ ph7_result_string_format(pCtx,"%02d:%02d",pTm->tm_hour,pTm->tm_min); break; case 'P': /* Lowercase Ante meridiem and Post meridiem */ ph7_result_string(pCtx,pTm->tm_hour > 12 ? "pm" : "am",(int)sizeof(char)*2); break; case 'p': /* Uppercase Ante meridiem and Post meridiem */ ph7_result_string(pCtx,pTm->tm_hour > 12 ? "PM" : "AM",(int)sizeof(char)*2); break; case 'r': /* Same as "%I:%M:%S %p" */ ph7_result_string_format(pCtx,"%02d:%02d:%02d %s", 1+(pTm->tm_hour%12), pTm->tm_min, pTm->tm_sec, pTm->tm_hour > 12 ? "PM" : "AM" ); break; case 'D': case 'x': /* Same as "%m/%d/%y" */ ph7_result_string_format(pCtx,"%02d/%02d/%02d", pTm->tm_mon+1, pTm->tm_mday, pTm->tm_year%100 ); break; case 'F': /* Same as "%Y-%m-%d" */ ph7_result_string_format(pCtx,"%d-%02d-%02d", pTm->tm_year, pTm->tm_mon+1, pTm->tm_mday ); break; case 'c': ph7_result_string_format(pCtx,"%d-%02d-%02d %02d:%02d:%02d", pTm->tm_year, pTm->tm_mon+1, pTm->tm_mday, pTm->tm_hour, pTm->tm_min, pTm->tm_sec ); break; case 's':{ time_t tt; /* Seconds since the Unix Epoch */ time(&tt); ph7_result_string_format(pCtx,"%u",(unsigned int)tt); break; } default: /* unknown specifer,simply ignore*/ break; } /* Advance the cursor */ zIn++; } return SXRET_OK; } /* * string date(string $format [, int $timestamp = time() ] ) * Returns a string formatted according to the given format string using * the given integer timestamp or the current time if no timestamp is given. * In other words, timestamp is optional and defaults to the value of time(). * Parameters * $format * The format of the outputted date string (See code above) * $timestamp * The optional timestamp parameter is an integer Unix timestamp * that defaults to the current local time if a timestamp is not given. * In other words, it defaults to the value of time(). * Return * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned. */ static int PH7_builtin_date(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zFormat; int nLen; Sytm sTm; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } zFormat = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Don't bother processing return the empty string */ ph7_result_string(pCtx,"",0); } if( nArg < 2 ){ #ifdef __WINNT__ SYSTEMTIME sOS; GetSystemTime(&sOS); SYSTEMTIME_TO_SYTM(&sOS,&sTm); #else struct tm *pTm; time_t t; time(&t); pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); #endif }else{ /* Use the given timestamp */ time_t t; struct tm *pTm; if( ph7_value_is_int(apArg[1]) ){ t = (time_t)ph7_value_to_int64(apArg[1]); pTm = localtime(&t); if( pTm == 0 ){ time(&t); } }else{ time(&t); } pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); } /* Format the given string */ DateFormat(pCtx,zFormat,nLen,&sTm); return PH7_OK; } /* * string strftime(string $format [, int $timestamp = time() ] ) * Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE) * Parameters * $format * The format of the outputted date string (See code above) * $timestamp * The optional timestamp parameter is an integer Unix timestamp * that defaults to the current local time if a timestamp is not given. * In other words, it defaults to the value of time(). * Return * Returns a string formatted according format using the given timestamp * or the current local time if no timestamp is given. */ static int PH7_builtin_strftime(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zFormat; int nLen; Sytm sTm; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } zFormat = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Don't bother processing return FALSE */ ph7_result_bool(pCtx,0); } if( nArg < 2 ){ #ifdef __WINNT__ SYSTEMTIME sOS; GetSystemTime(&sOS); SYSTEMTIME_TO_SYTM(&sOS,&sTm); #else struct tm *pTm; time_t t; time(&t); pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); #endif }else{ /* Use the given timestamp */ time_t t; struct tm *pTm; if( ph7_value_is_int(apArg[1]) ){ t = (time_t)ph7_value_to_int64(apArg[1]); pTm = localtime(&t); if( pTm == 0 ){ time(&t); } }else{ time(&t); } pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); } /* Format the given string */ PH7_Strftime(pCtx,zFormat,nLen,&sTm); if( ph7_context_result_buf_length(pCtx) < 1 ){ /* Nothing was formatted,return FALSE */ ph7_result_bool(pCtx,0); } return PH7_OK; } /* * string gmdate(string $format [, int $timestamp = time() ] ) * Identical to the date() function except that the time returned * is Greenwich Mean Time (GMT). * Parameters * $format * The format of the outputted date string (See code above) * $timestamp * The optional timestamp parameter is an integer Unix timestamp * that defaults to the current local time if a timestamp is not given. * In other words, it defaults to the value of time(). * Return * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned. */ static int PH7_builtin_gmdate(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zFormat; int nLen; Sytm sTm; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } zFormat = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Don't bother processing return the empty string */ ph7_result_string(pCtx,"",0); } if( nArg < 2 ){ #ifdef __WINNT__ SYSTEMTIME sOS; GetSystemTime(&sOS); SYSTEMTIME_TO_SYTM(&sOS,&sTm); #else struct tm *pTm; time_t t; time(&t); pTm = gmtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); #endif }else{ /* Use the given timestamp */ time_t t; struct tm *pTm; if( ph7_value_is_int(apArg[1]) ){ t = (time_t)ph7_value_to_int64(apArg[1]); pTm = gmtime(&t); if( pTm == 0 ){ time(&t); } }else{ time(&t); } pTm = gmtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); } /* Format the given string */ DateFormat(pCtx,zFormat,nLen,&sTm); return PH7_OK; } /* * array localtime([ int $timestamp = time() [, bool $is_associative = false ]]) * Return the local time. * Parameter * $timestamp: The optional timestamp parameter is an integer Unix timestamp * that defaults to the current local time if a timestamp is not given. * In other words, it defaults to the value of time(). * $is_associative * If set to FALSE or not supplied then the array is returned as a regular, numerically * indexed array. If the argument is set to TRUE then localtime() returns an associative * array containing all the different elements of the structure returned by the C function * call to localtime. The names of the different keys of the associative array are as follows: * "tm_sec" - seconds, 0 to 59 * "tm_min" - minutes, 0 to 59 * "tm_hour" - hours, 0 to 23 * "tm_mday" - day of the month, 1 to 31 * "tm_mon" - month of the year, 0 (Jan) to 11 (Dec) * "tm_year" - years since 1900 * "tm_wday" - day of the week, 0 (Sun) to 6 (Sat) * "tm_yday" - day of the year, 0 to 365 * "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown. * Returns * An associative array of information related to the timestamp. */ static int PH7_builtin_localtime(ph7_context *pCtx,int nArg,ph7_value **apArg) { ph7_value *pValue,*pArray; int isAssoc = 0; Sytm sTm; if( nArg < 1 ){ #ifdef __WINNT__ SYSTEMTIME sOS; GetSystemTime(&sOS); /* TODO(chems): GMT not local */ SYSTEMTIME_TO_SYTM(&sOS,&sTm); #else struct tm *pTm; time_t t; time(&t); pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); #endif }else{ /* Use the given timestamp */ time_t t; struct tm *pTm; if( ph7_value_is_int(apArg[0]) ){ t = (time_t)ph7_value_to_int64(apArg[0]); pTm = localtime(&t); if( pTm == 0 ){ time(&t); } }else{ time(&t); } pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); } /* Element value */ pValue = ph7_context_new_scalar(pCtx); if( pValue == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } /* Create a new array */ pArray = ph7_context_new_array(pCtx); if( pArray == 0 ){ /* Return NULL */ ph7_result_null(pCtx); return PH7_OK; } if( nArg > 1 ){ isAssoc = ph7_value_to_bool(apArg[1]); } /* Fill the array */ /* Seconds */ ph7_value_int(pValue,sTm.tm_sec); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_sec",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* Minutes */ ph7_value_int(pValue,sTm.tm_min); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_min",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* Hours */ ph7_value_int(pValue,sTm.tm_hour); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_hour",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* mday */ ph7_value_int(pValue,sTm.tm_mday); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_mday",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* mon */ ph7_value_int(pValue,sTm.tm_mon); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_mon",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* year since 1900 */ ph7_value_int(pValue,sTm.tm_year-1900); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_year",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* wday */ ph7_value_int(pValue,sTm.tm_wday); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_wday",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* yday */ ph7_value_int(pValue,sTm.tm_yday); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_yday",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* isdst */ #ifdef __WINNT__ #ifdef _MSC_VER #ifndef _WIN32_WCE _get_daylight(&sTm.tm_isdst); #endif #endif #endif ph7_value_int(pValue,sTm.tm_isdst); if( isAssoc ){ ph7_array_add_strkey_elem(pArray,"tm_isdst",pValue); }else{ ph7_array_add_elem(pArray,0/* Automatic index */,pValue); } /* Return the array */ ph7_result_value(pCtx,pArray); return PH7_OK; } /* * int idate(string $format [, int $timestamp = time() ]) * Returns a number formatted according to the given format string * using the given integer timestamp or the current local time if * no timestamp is given. In other words, timestamp is optional and defaults * to the value of time(). * Unlike the function date(), idate() accepts just one char in the format * parameter. * $Parameters * Supported format * d Day of the month * h Hour (12 hour format) * H Hour (24 hour format) * i Minutes * I (uppercase i)1 if DST is activated, 0 otherwise * L (uppercase l) returns 1 for leap year, 0 otherwise * m Month number * s Seconds * t Days in current month * U Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time() * w Day of the week (0 on Sunday) * W ISO-8601 week number of year, weeks starting on Monday * y Year (1 or 2 digits - check note below) * Y Year (4 digits) * z Day of the year * Z Timezone offset in seconds * $timestamp * The optional timestamp parameter is an integer Unix timestamp that defaults * to the current local time if a timestamp is not given. In other words, it defaults * to the value of time(). * Return * An integer. */ static int PH7_builtin_idate(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zFormat; ph7_int64 iVal = 0; int nLen; Sytm sTm; if( nArg < 1 || !ph7_value_is_string(apArg[0]) ){ /* Missing/Invalid argument,return -1 */ ph7_result_int(pCtx,-1); return PH7_OK; } zFormat = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Don't bother processing return -1*/ ph7_result_int(pCtx,-1); } if( nArg < 2 ){ #ifdef __WINNT__ SYSTEMTIME sOS; GetSystemTime(&sOS); SYSTEMTIME_TO_SYTM(&sOS,&sTm); #else struct tm *pTm; time_t t; time(&t); pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); #endif }else{ /* Use the given timestamp */ time_t t; struct tm *pTm; if( ph7_value_is_int(apArg[1]) ){ t = (time_t)ph7_value_to_int64(apArg[1]); pTm = localtime(&t); if( pTm == 0 ){ time(&t); } }else{ time(&t); } pTm = localtime(&t); STRUCT_TM_TO_SYTM(pTm,&sTm); } /* Perform the requested operation */ switch(zFormat[0]){ case 'd': /* Day of the month */ iVal = sTm.tm_mday; break; case 'h': /* Hour (12 hour format)*/ iVal = 1 + (sTm.tm_hour % 12); break; case 'H': /* Hour (24 hour format)*/ iVal = sTm.tm_hour; break; case 'i': /*Minutes*/ iVal = sTm.tm_min; break; case 'I': /* returns 1 if DST is activated, 0 otherwise */ #ifdef __WINNT__ #ifdef _MSC_VER #ifndef _WIN32_WCE _get_daylight(&sTm.tm_isdst); #endif #endif #endif iVal = sTm.tm_isdst; break; case 'L': /* returns 1 for leap year, 0 otherwise */ iVal = IS_LEAP_YEAR(sTm.tm_year); break; case 'm': /* Month number*/ iVal = sTm.tm_mon; break; case 's': /*Seconds*/ iVal = sTm.tm_sec; break; case 't':{ /*Days in current month*/ static const int aMonDays[] = {31,29,31,30,31,30,31,31,30,31,30,31 }; int nDays = aMonDays[sTm.tm_mon % 12 ]; if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){ nDays = 28; } iVal = nDays; break; } case 'U': /*Seconds since the Unix Epoch*/ iVal = (ph7_int64)time(0); break; case 'w': /* Day of the week (0 on Sunday) */ iVal = sTm.tm_wday; break; case 'W': { /* ISO-8601 week number of year, weeks starting on Monday */ static const int aISO8601[] = { 7 /* Sunday */,1 /* Monday */,2,3,4,5,6 }; iVal = aISO8601[sTm.tm_wday % 7 ]; break; } case 'y': /* Year (2 digits) */ iVal = sTm.tm_year % 100; break; case 'Y': /* Year (4 digits) */ iVal = sTm.tm_year; break; case 'z': /* Day of the year */ iVal = sTm.tm_yday; break; case 'Z': /*Timezone offset in seconds*/ iVal = sTm.tm_gmtoff; break; default: /* unknown format,throw a warning */ ph7_context_throw_error(pCtx,PH7_CTX_WARNING,"Unknown date format token"); break; } /* Return the time value */ ph7_result_int64(pCtx,iVal); return PH7_OK; } /* * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") * [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] ) * Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer * containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time * specified. * Arguments may be left out in order from right to left; any arguments thus omitted will be set to * the current value according to the local date and time. * Parameters * $hour * The number of the hour relevant to the start of the day determined by month, day and year. * Negative values reference the hour before midnight of the day in question. Values greater * than 23 reference the appropriate hour in the following day(s). * $minute * The number of the minute relevant to the start of the hour. Negative values reference * the minute in the previous hour. Values greater than 59 reference the appropriate minute * in the following hour(s). * $second * The number of seconds relevant to the start of the minute. Negative values reference * the second in the previous minute. Values greater than 59 reference the appropriate * second in the following minute(s). * $month * The number of the month relevant to the end of the previous year. Values 1 to 12 reference * the normal calendar months of the year in question. Values less than 1 (including negative values) * reference the months in the previous year in reverse order, so 0 is December, -1 is November)... * $day * The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31 * (depending upon the month) reference the normal days in the relevant month. Values less than 1 * (including negative values) reference the days in the previous month, so 0 is the last day * of the previous month, -1 is the day before that, etc. Values greater than the number of days * in the relevant month reference the appropriate day in the following month(s). * $year * The number of the year, may be a two or four digit value, with values between 0-69 mapping * to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as * most common today, the valid range for year is somewhere between 1901 and 2038. * $is_dst * This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not, * or -1 (the default) if it is unknown whether the time is within daylight savings time or not. * Return * mktime() returns the Unix timestamp of the arguments given. * If the arguments are invalid, the function returns FALSE */ static int PH7_builtin_mktime(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zFunction; ph7_int64 iVal = 0; struct tm *pTm; time_t t; /* Extract function name */ zFunction = ph7_function_name(pCtx); /* Get the current time */ time(&t); if( zFunction[0] == 'g' /* gmmktime */ ){ pTm = gmtime(&t); }else{ /* localtime */ pTm = localtime(&t); } if( nArg > 0 ){ int iVal; /* Hour */ iVal = ph7_value_to_int(apArg[0]); pTm->tm_hour = iVal; if( nArg > 1 ){ /* Minutes */ iVal = ph7_value_to_int(apArg[1]); pTm->tm_min = iVal; if( nArg > 2 ){ /* Seconds */ iVal = ph7_value_to_int(apArg[2]); pTm->tm_sec = iVal; if( nArg > 3 ){ /* Month */ iVal = ph7_value_to_int(apArg[3]); pTm->tm_mon = iVal - 1; if( nArg > 4 ){ /* mday */ iVal = ph7_value_to_int(apArg[4]); pTm->tm_mday = iVal; if( nArg > 5 ){ /* Year */ iVal = ph7_value_to_int(apArg[5]); if( iVal > 1900 ){ iVal -= 1900; } pTm->tm_year = iVal; if( nArg > 6 ){ /* is_dst */ iVal = ph7_value_to_bool(apArg[6]); pTm->tm_isdst = iVal; } } } } } } } /* Make the time */ iVal = (ph7_int64)mktime(pTm); /* Return the timesatmp as a 64bit integer */ ph7_result_int64(pCtx,iVal); return PH7_OK; } /* * Section: * URL handling Functions. * Authors: * Symisc Systems,devel@symisc.net. * Copyright (C) Symisc Systems,http://ph7.symisc.net * Status: * Stable. */ /* * Output consumer callback for the standard Symisc routines. * [i.e: SyBase64Encode(),SyBase64Decode(),SyUriEncode(),...]. */ static int Consumer(const void *pData,unsigned int nLen,void *pUserData) { /* Store in the call context result buffer */ ph7_result_string((ph7_context *)pUserData,(const char *)pData,(int)nLen); return SXRET_OK; } /* * string base64_encode(string $data) * string convert_uuencode(string $data) * Encodes data with MIME base64 * Parameter * $data * Data to encode * Return * Encoded data or FALSE on failure. */ static int PH7_builtin_base64_encode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the input string */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Nothing to process,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the BASE64 encoding */ SyBase64Encode(zIn,(sxu32)nLen,Consumer,pCtx); return PH7_OK; } /* * string base64_decode(string $data) * string convert_uudecode(string $data) * Decodes data encoded with MIME base64 * Parameter * $data * Encoded data. * Return * Returns the original data or FALSE on failure. */ static int PH7_builtin_base64_decode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the input string */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Nothing to process,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the BASE64 decoding */ SyBase64Decode(zIn,(sxu32)nLen,Consumer,pCtx); return PH7_OK; } /* * string urlencode(string $str) * URL encoding * Parameter * $data * Input string. * Return * Returns a string in which all non-alphanumeric characters except -_. have * been replaced with a percent (%) sign followed by two hex digits and spaces * encoded as plus (+) signs. */ static int PH7_builtin_urlencode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the input string */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Nothing to process,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the URL encoding */ SyUriEncode(zIn,(sxu32)nLen,Consumer,pCtx); return PH7_OK; } /* * string urldecode(string $str) * Decodes any %## encoding in the given string. * Plus symbols ('+') are decoded to a space character. * Parameter * $data * Input string. * Return * Decoded URL or FALSE on failure. */ static int PH7_builtin_urldecode(ph7_context *pCtx,int nArg,ph7_value **apArg) { const char *zIn; int nLen; if( nArg < 1 ){ /* Missing arguments,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Extract the input string */ zIn = ph7_value_to_string(apArg[0],&nLen); if( nLen < 1 ){ /* Nothing to process,return FALSE */ ph7_result_bool(pCtx,0); return PH7_OK; } /* Perform the URL decoding */ SyUriDecode(zIn,(sxu32)nLen,Consumer,pCtx,TRUE); return PH7_OK; } #endif /* PH7_DISABLE_BUILTIN_FUNC */ /* Table of the built-in functions */ static const ph7_builtin_func aBuiltInFunc[] = { /* Variable handling functions */ { "is_bool" , PH7_builtin_is_bool }, { "is_float" , PH7_builtin_is_float }, { "is_real" , PH7_builtin_is_float }, { "is_double" , PH7_builtin_is_float }, { "is_int" , PH7_builtin_is_int }, { "is_integer" , PH7_builtin_is_int }, { "is_long" , PH7_builtin_is_int }, { "is_string" , PH7_builtin_is_string }, { "is_null" , PH7_builtin_is_null }, { "is_numeric" , PH7_builtin_is_numeric }, { "is_scalar" , PH7_builtin_is_scalar }, { "is_array" , PH7_builtin_is_array }, { "is_object" , PH7_builtin_is_object }, { "is_resource", PH7_builtin_is_resource }, { "douleval" , PH7_builtin_floatval }, { "floatval" , PH7_builtin_floatval }, { "intval" , PH7_builtin_intval }, { "strval" , PH7_builtin_strval }, { "empty" , PH7_builtin_empty }, #ifndef PH7_DISABLE_BUILTIN_FUNC #ifdef PH7_ENABLE_MATH_FUNC /* Math functions */ { "abs" , PH7_builtin_abs }, { "sqrt" , PH7_builtin_sqrt }, { "exp" , PH7_builtin_exp }, { "floor", PH7_builtin_floor }, { "cos" , PH7_builtin_cos }, { "sin" , PH7_builtin_sin }, { "acos" , PH7_builtin_acos }, { "asin" , PH7_builtin_asin }, { "cosh" , PH7_builtin_cosh }, { "sinh" , PH7_builtin_sinh }, { "ceil" , PH7_builtin_ceil }, { "tan" , PH7_builtin_tan }, { "tanh" , PH7_builtin_tanh }, { "atan" , PH7_builtin_atan }, { "atan2", PH7_builtin_atan2 }, { "log" , PH7_builtin_log }, { "log10" , PH7_builtin_log10 }, { "pow" , PH7_builtin_pow }, { "pi", PH7_builtin_pi }, { "fmod", PH7_builtin_fmod }, { "hypot", PH7_builtin_hypot }, #endif /* PH7_ENABLE_MATH_FUNC */ { "round", PH7_builtin_round }, { "dechex", PH7_builtin_dechex }, { "decoct", PH7_builtin_decoct }, { "decbin", PH7_builtin_decbin }, { "hexdec", PH7_builtin_hexdec }, { "bindec", PH7_builtin_bindec }, { "octdec", PH7_builtin_octdec }, { "srand", PH7_builtin_srand }, { "mt_srand",PH7_builtin_srand }, { "base_convert", PH7_builtin_base_convert }, /* String handling functions */ { "substr", PH7_builtin_substr }, { "substr_compare", PH7_builtin_substr_compare }, { "substr_count", PH7_builtin_substr_count }, { "chunk_split", PH7_builtin_chunk_split}, { "addslashes" , PH7_builtin_addslashes }, { "addcslashes", PH7_builtin_addcslashes}, { "quotemeta", PH7_builtin_quotemeta }, { "stripslashes", PH7_builtin_stripslashes }, { "htmlspecialchars",PH7_builtin_htmlspecialchars }, { "htmlspecialchars_decode", PH7_builtin_htmlspecialchars_decode }, { "get_html_translation_table",PH7_builtin_get_html_translation_table }, { "htmlentities",PH7_builtin_htmlentities}, { "html_entity_decode", PH7_builtin_html_entity_decode}, { "strlen" , PH7_builtin_strlen }, { "strcmp" , PH7_builtin_strcmp }, { "strcoll" , PH7_builtin_strcmp }, { "strncmp" , PH7_builtin_strncmp }, { "strcasecmp" , PH7_builtin_strcasecmp }, { "strncasecmp", PH7_builtin_strncasecmp}, { "implode" , PH7_builtin_implode }, { "join" , PH7_builtin_implode }, { "implode_recursive" , PH7_builtin_implode_recursive }, { "join_recursive" , PH7_builtin_implode_recursive }, { "explode" , PH7_builtin_explode }, { "trim" , PH7_builtin_trim }, { "rtrim" , PH7_builtin_rtrim }, { "chop" , PH7_builtin_rtrim }, { "ltrim" , PH7_builtin_ltrim }, { "strtolower", PH7_builtin_strtolower }, { "mb_strtolower",PH7_builtin_strtolower }, /* Only UTF-8 encoding is supported */ { "strtoupper", PH7_builtin_strtoupper }, { "mb_strtoupper",PH7_builtin_strtoupper }, /* Only UTF-8 encoding is supported */ { "ucfirst", PH7_builtin_ucfirst }, { "lcfirst", PH7_builtin_lcfirst }, { "ord", PH7_builtin_ord }, { "chr", PH7_builtin_chr }, { "bin2hex", PH7_builtin_bin2hex }, { "strstr", PH7_builtin_strstr }, { "stristr", PH7_builtin_stristr }, { "strchr", PH7_builtin_strstr }, { "strpos", PH7_builtin_strpos }, { "stripos", PH7_builtin_stripos }, { "strrpos", PH7_builtin_strrpos }, { "strripos", PH7_builtin_strripos }, { "strrchr", PH7_builtin_strrchr }, { "strrev", PH7_builtin_strrev }, { "ucwords", PH7_builtin_ucwords }, { "str_repeat", PH7_builtin_str_repeat }, { "nl2br", PH7_builtin_nl2br }, { "sprintf", PH7_builtin_sprintf }, { "printf", PH7_builtin_printf }, { "vprintf", PH7_builtin_vprintf }, { "vsprintf", PH7_builtin_vsprintf }, { "size_format", PH7_builtin_size_format}, #if !defined(PH7_DISABLE_HASH_FUNC) { "md5", PH7_builtin_md5 }, { "sha1", PH7_builtin_sha1 }, { "crc32", PH7_builtin_crc32 }, #endif /* PH7_DISABLE_HASH_FUNC */ { "str_getcsv", PH7_builtin_str_getcsv }, { "strip_tags", PH7_builtin_strip_tags }, { "str_shuffle", PH7_builtin_str_shuffle}, { "str_split", PH7_builtin_str_split }, { "strspn", PH7_builtin_strspn }, { "strcspn", PH7_builtin_strcspn }, { "strpbrk", PH7_builtin_strpbrk }, { "soundex", PH7_builtin_soundex }, { "wordwrap", PH7_builtin_wordwrap }, { "strtok", PH7_builtin_strtok }, { "str_pad", PH7_builtin_str_pad }, { "str_replace", PH7_builtin_str_replace}, { "str_ireplace", PH7_builtin_str_replace}, { "strtr", PH7_builtin_strtr }, { "parse_ini_string", PH7_builtin_parse_ini_string}, /* Ctype functions */ { "ctype_alnum", PH7_builtin_ctype_alnum }, { "ctype_alpha", PH7_builtin_ctype_alpha }, { "ctype_cntrl", PH7_builtin_ctype_cntrl }, { "ctype_digit", PH7_builtin_ctype_digit }, { "ctype_xdigit",PH7_builtin_ctype_xdigit}, { "ctype_graph", PH7_builtin_ctype_graph }, { "ctype_print", PH7_builtin_ctype_print }, { "ctype_punct", PH7_builtin_ctype_punct }, { "ctype_space", PH7_builtin_ctype_space }, { "ctype_lower", PH7_builtin_ctype_lower }, { "ctype_upper", PH7_builtin_ctype_upper }, /* Time functions */ { "time" , PH7_builtin_time }, { "microtime", PH7_builtin_microtime }, { "getdate" , PH7_builtin_getdate }, { "gettimeofday",PH7_builtin_gettimeofday }, { "date", PH7_builtin_date }, { "strftime", PH7_builtin_strftime }, { "idate", PH7_builtin_idate }, { "gmdate", PH7_builtin_gmdate }, { "localtime", PH7_builtin_localtime }, { "mktime", PH7_builtin_mktime }, { "gmmktime", PH7_builtin_mktime }, /* URL functions */ { "base64_encode",PH7_builtin_base64_encode }, { "base64_decode",PH7_builtin_base64_decode }, { "convert_uuencode",PH7_builtin_base64_encode }, { "convert_uudecode",PH7_builtin_base64_decode }, { "urlencode", PH7_builtin_urlencode }, { "urldecode", PH7_builtin_urldecode }, { "rawurlencode", PH7_builtin_urlencode }, { "rawurldecode", PH7_builtin_urldecode }, #endif /* PH7_DISABLE_BUILTIN_FUNC */ }; /* * Register the built-in functions defined above,the array functions * defined in hashmap.c and the IO functions defined in vfs.c. */ PH7_PRIVATE void PH7_RegisterBuiltInFunction(ph7_vm *pVm) { sxu32 n; for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){ ph7_create_function(&(*pVm),aBuiltInFunc[n].zName,aBuiltInFunc[n].xFunc,0); } /* Register hashmap functions [i.e: array_merge(),sort(),count(),array_diff(),...] */ PH7_RegisterHashmapFunctions(&(*pVm)); /* Register IO functions [i.e: fread(),fwrite(),chdir(),mkdir(),file(),...] */ PH7_RegisterIORoutine(&(*pVm)); } /* * ---------------------------------------------------------- * File: api.c * MD5: ec37aefad456de49a24c8f73f45f8c84 * ---------------------------------------------------------- */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* $SymiscID: api.c v2.0 FreeBSD 2012-08-18 06:54 stable $ */ #ifndef PH7_AMALGAMATION #include "ph7int.h" #endif /* This file implement the public interfaces presented to host-applications. * Routines in other files are for internal use by PH7 and should not be * accessed by users of the library. */ #define PH7_ENGINE_MAGIC 0xF874BCD7 #define PH7_ENGINE_MISUSE(ENGINE) (ENGINE == 0 || ENGINE->nMagic != PH7_ENGINE_MAGIC) #define PH7_VM_MISUSE(VM) (VM == 0 || VM->nMagic == PH7_VM_STALE) /* If another thread have released a working instance,the following macros * evaluates to true. These macros are only used when the library * is built with threading support enabled which is not the case in * the default built. */ #define PH7_THRD_ENGINE_RELEASE(ENGINE) (ENGINE->nMagic != PH7_ENGINE_MAGIC) #define PH7_THRD_VM_RELEASE(VM) (VM->nMagic == PH7_VM_STALE) /* IMPLEMENTATION: ph7@embedded@symisc 311-12-32 */ /* * All global variables are collected in the structure named "sMPGlobal". * That way it is clear in the code when we are using static variable because * its name start with sMPGlobal. */ static struct Global_Data { SyMemBackend sAllocator; /* Global low level memory allocator */ #if defined(PH7_ENABLE_THREADS) const SyMutexMethods *pMutexMethods; /* Mutex methods */ SyMutex *pMutex; /* Global mutex */ sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded * The threading level can be set using the [ph7_lib_config()] * interface with a configuration verb set to * PH7_LIB_CONFIG_THREAD_LEVEL_SINGLE or * PH7_LIB_CONFIG_THREAD_LEVEL_MULTI */ #endif const ph7_vfs *pVfs; /* Underlying virtual file system */ sxi32 nEngine; /* Total number of active engines */ ph7 *pEngines; /* List of active engine */ sxu32 nMagic; /* Sanity check against library misuse */ }sMPGlobal = { {0,0,0,0,0,0,0,0,{0}}, #if defined(PH7_ENABLE_THREADS) 0, 0, 0, #endif 0, 0, 0, 0 }; #define PH7_LIB_MAGIC 0xEA1495BA #define PH7_LIB_MISUSE (sMPGlobal.nMagic != PH7_LIB_MAGIC) /* * Supported threading level. * These options have meaning only when the library is compiled with multi-threading * support.That is,the PH7_ENABLE_THREADS compile time directive must be defined * when PH7 is built. * PH7_THREAD_LEVEL_SINGLE: * In this mode,mutexing is disabled and the library can only be used by a single thread. * PH7_THREAD_LEVEL_MULTI * In this mode, all mutexes including the recursive mutexes on [ph7] objects * are enabled so that the application is free to share the same engine * between different threads at the same time. */ #define PH7_THREAD_LEVEL_SINGLE 1 #define PH7_THREAD_LEVEL_MULTI 2 /* * Configure a running PH7 engine instance. * return PH7_OK on success.Any other return * value indicates failure. * Refer to [ph7_config()]. */ static sxi32 EngineConfig(ph7 *pEngine,sxi32 nOp,va_list ap) { ph7_conf *pConf = &pEngine->xConf; int rc = PH7_OK; /* Perform the requested operation */ switch(nOp){ case PH7_CONFIG_ERR_OUTPUT: { ProcConsumer xConsumer = va_arg(ap,ProcConsumer); void *pUserData = va_arg(ap,void *); /* Compile time error consumer routine */ if( xConsumer == 0 ){ rc = PH7_CORRUPT; break; } /* Install the error consumer */ pConf->xErr = xConsumer; pConf->pErrData = pUserData; break; } case PH7_CONFIG_ERR_LOG:{ /* Extract compile-time error log if any */ const char **pzPtr = va_arg(ap,const char **); int *pLen = va_arg(ap,int *); if( pzPtr == 0 ){ rc = PH7_CORRUPT; break; } /* NULL terminate the error-log buffer */ SyBlobNullAppend(&pConf->sErrConsumer); /* Point to the error-log buffer */ *pzPtr = (const char *)SyBlobData(&pConf->sErrConsumer); if( pLen ){ if( SyBlobLength(&pConf->sErrConsumer) > 1 /* NULL '\0' terminator */ ){ *pLen = (int)SyBlobLength(&pConf->sErrConsumer); }else{ *pLen = 0; } } break; } case PH7_CONFIG_ERR_ABORT: /* Reserved for future use */ break; default: /* Unknown configuration verb */ rc = PH7_CORRUPT; break; } /* Switch() */ return rc; } /* * Configure the PH7 library. * return PH7_OK on success.Any other return value * indicates failure. * Refer to [ph7_lib_config()]. */ static sxi32 PH7CoreConfigure(sxi32 nOp,va_list ap) { int rc = PH7_OK; switch(nOp){ case PH7_LIB_CONFIG_VFS:{ /* Install a virtual file system */ const ph7_vfs *pVfs = va_arg(ap,const ph7_vfs *); sMPGlobal.pVfs = pVfs; break; } case PH7_LIB_CONFIG_USER_MALLOC: { /* Use an alternative low-level memory allocation routines */ const SyMemMethods *pMethods = va_arg(ap,const SyMemMethods *); /* Save the memory failure callback (if available) */ ProcMemError xMemErr = sMPGlobal.sAllocator.xMemError; void *pMemErr = sMPGlobal.sAllocator.pUserData; if( pMethods == 0 ){ /* Use the built-in memory allocation subsystem */ rc = SyMemBackendInit(&sMPGlobal.sAllocator,xMemErr,pMemErr); }else{ rc = SyMemBackendInitFromOthers(&sMPGlobal.sAllocator,pMethods,xMemErr,pMemErr); } break; } case PH7_LIB_CONFIG_MEM_ERR_CALLBACK: { /* Memory failure callback */ ProcMemError xMemErr = va_arg(ap,ProcMemError); void *pUserData = va_arg(ap,void *); sMPGlobal.sAllocator.xMemError = xMemErr; sMPGlobal.sAllocator.pUserData = pUserData; break; } case PH7_LIB_CONFIG_USER_MUTEX: { #if defined(PH7_ENABLE_THREADS) /* Use an alternative low-level mutex subsystem */ const SyMutexMethods *pMethods = va_arg(ap,const SyMutexMethods *); #if defined (UNTRUST) if( pMethods == 0 ){ rc = PH7_CORRUPT; } #endif /* Sanity check */ if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){ /* At least three criticial callbacks xEnter(),xLeave() and xNew() must be supplied */ rc = PH7_CORRUPT; break; } if( sMPGlobal.pMutexMethods ){ /* Overwrite the previous mutex subsystem */ SyMutexRelease(sMPGlobal.pMutexMethods,sMPGlobal.pMutex); if( sMPGlobal.pMutexMethods->xGlobalRelease ){ sMPGlobal.pMutexMethods->xGlobalRelease(); } sMPGlobal.pMutex = 0; } /* Initialize and install the new mutex subsystem */ if( pMethods->xGlobalInit ){ rc = pMethods->xGlobalInit(); if ( rc != PH7_OK ){ break; } } /* Create the global mutex */ sMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST); if( sMPGlobal.pMutex == 0 ){ /* * If the supplied mutex subsystem is so sick that we are unable to * create a single mutex,there is no much we can do here. */ if( pMethods->xGlobalRelease ){ pMethods->xGlobalRelease(); } rc = PH7_CORRUPT; break; } sMPGlobal.pMutexMethods = pMethods; if( sMPGlobal.nThreadingLevel == 0 ){ /* Set a default threading level */ sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_MULTI; } #endif break; } case PH7_LIB_CONFIG_THREAD_LEVEL_SINGLE: #if defined(PH7_ENABLE_THREADS) /* Single thread mode(Only one thread is allowed to play with the library) */ sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_SINGLE; #endif break; case PH7_LIB_CONFIG_THREAD_LEVEL_MULTI: #if defined(PH7_ENABLE_THREADS) /* Multi-threading mode (library is thread safe and PH7 engines and virtual machines * may be shared between multiple threads). */ sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_MULTI; #endif break; default: /* Unknown configuration option */ rc = PH7_CORRUPT; break; } return rc; } /* * [CAPIREF: ph7_lib_config()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_lib_config(int nConfigOp,...) { va_list ap; int rc; if( sMPGlobal.nMagic == PH7_LIB_MAGIC ){ /* Library is already initialized,this operation is forbidden */ return PH7_LOOKED; } va_start(ap,nConfigOp); rc = PH7CoreConfigure(nConfigOp,ap); va_end(ap); return rc; } /* * Global library initialization * Refer to [ph7_lib_init()] * This routine must be called to initialize the memory allocation subsystem,the mutex * subsystem prior to doing any serious work with the library.The first thread to call * this routine does the initialization process and set the magic number so no body later * can re-initialize the library.If subsequent threads call this routine before the first * thread have finished the initialization process, then the subsequent threads must block * until the initialization process is done. */ static sxi32 PH7CoreInitialize(void) { const ph7_vfs *pVfs; /* Built-in vfs */ #if defined(PH7_ENABLE_THREADS) const SyMutexMethods *pMutexMethods = 0; SyMutex *pMaster = 0; #endif int rc; /* * If the library is already initialized,then a call to this routine * is a no-op. */ if( sMPGlobal.nMagic == PH7_LIB_MAGIC ){ return PH7_OK; /* Already initialized */ } /* Point to the built-in vfs */ pVfs = PH7_ExportBuiltinVfs(); /* Install it */ ph7_lib_config(PH7_LIB_CONFIG_VFS,pVfs); #if defined(PH7_ENABLE_THREADS) if( sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_SINGLE ){ pMutexMethods = sMPGlobal.pMutexMethods; if( pMutexMethods == 0 ){ /* Use the built-in mutex subsystem */ pMutexMethods = SyMutexExportMethods(); if( pMutexMethods == 0 ){ return PH7_CORRUPT; /* Can't happen */ } /* Install the mutex subsystem */ rc = ph7_lib_config(PH7_LIB_CONFIG_USER_MUTEX,pMutexMethods); if( rc != PH7_OK ){ return rc; } } /* Obtain a static mutex so we can initialize the library without calling malloc() */ pMaster = SyMutexNew(pMutexMethods,SXMUTEX_TYPE_STATIC_1); if( pMaster == 0 ){ return PH7_CORRUPT; /* Can't happen */ } } /* Lock the master mutex */ rc = PH7_OK; SyMutexEnter(pMutexMethods,pMaster); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ if( sMPGlobal.nMagic != PH7_LIB_MAGIC ){ #endif if( sMPGlobal.sAllocator.pMethods == 0 ){ /* Install a memory subsystem */ rc = ph7_lib_config(PH7_LIB_CONFIG_USER_MALLOC,0); /* zero mean use the built-in memory backend */ if( rc != PH7_OK ){ /* If we are unable to initialize the memory backend,there is no much we can do here.*/ goto End; } } #if defined(PH7_ENABLE_THREADS) if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE ){ /* Protect the memory allocation subsystem */ rc = SyMemBackendMakeThreadSafe(&sMPGlobal.sAllocator,sMPGlobal.pMutexMethods); if( rc != PH7_OK ){ goto End; } } #endif /* Our library is initialized,set the magic number */ sMPGlobal.nMagic = PH7_LIB_MAGIC; rc = PH7_OK; #if defined(PH7_ENABLE_THREADS) } /* sMPGlobal.nMagic != PH7_LIB_MAGIC */ #endif End: #if defined(PH7_ENABLE_THREADS) /* Unlock the master mutex */ SyMutexLeave(pMutexMethods,pMaster); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif return rc; } /* * [CAPIREF: ph7_lib_init()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_lib_init(void) { int rc; rc = PH7CoreInitialize(); return rc; } /* * Release an active PH7 engine and it's associated active virtual machines. */ static sxi32 EngineRelease(ph7 *pEngine) { ph7_vm *pVm,*pNext; /* Release all active VM */ pVm = pEngine->pVms; for(;;){ if( pEngine->iVm <= 0 ){ break; } pNext = pVm->pNext; PH7_VmRelease(pVm); pVm = pNext; pEngine->iVm--; } /* Set a dummy magic number */ pEngine->nMagic = 0x7635; /* Release the private memory subsystem */ SyMemBackendRelease(&pEngine->sAllocator); return PH7_OK; } /* * Release all resources consumed by the library. * If PH7 is already shut down when this routine * is invoked then this routine is a harmless no-op. * Note: This call is not thread safe. * Refer to [ph7_lib_shutdown()]. */ static void PH7CoreShutdown(void) { ph7 *pEngine,*pNext; /* Release all active engines first */ pEngine = sMPGlobal.pEngines; for(;;){ if( sMPGlobal.nEngine < 1 ){ break; } pNext = pEngine->pNext; EngineRelease(pEngine); pEngine = pNext; sMPGlobal.nEngine--; } #if defined(PH7_ENABLE_THREADS) /* Release the mutex subsystem */ if( sMPGlobal.pMutexMethods ){ if( sMPGlobal.pMutex ){ SyMutexRelease(sMPGlobal.pMutexMethods,sMPGlobal.pMutex); sMPGlobal.pMutex = 0; } if( sMPGlobal.pMutexMethods->xGlobalRelease ){ sMPGlobal.pMutexMethods->xGlobalRelease(); } sMPGlobal.pMutexMethods = 0; } sMPGlobal.nThreadingLevel = 0; #endif if( sMPGlobal.sAllocator.pMethods ){ /* Release the memory backend */ SyMemBackendRelease(&sMPGlobal.sAllocator); } sMPGlobal.nMagic = 0x1928; } /* * [CAPIREF: ph7_lib_shutdown()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_lib_shutdown(void) { if( sMPGlobal.nMagic != PH7_LIB_MAGIC ){ /* Already shut */ return PH7_OK; } PH7CoreShutdown(); return PH7_OK; } /* * [CAPIREF: ph7_lib_is_threadsafe()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_lib_is_threadsafe(void) { if( sMPGlobal.nMagic != PH7_LIB_MAGIC ){ return 0; } #if defined(PH7_ENABLE_THREADS) if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE ){ /* Muli-threading support is enabled */ return 1; }else{ /* Single-threading */ return 0; } #else return 0; #endif } /* * [CAPIREF: ph7_lib_version()] * Please refer to the official documentation for function purpose and expected parameters. */ const char * ph7_lib_version(void) { return PH7_VERSION; } /* * [CAPIREF: ph7_lib_signature()] * Please refer to the official documentation for function purpose and expected parameters. */ const char * ph7_lib_signature(void) { return PH7_SIG; } /* * [CAPIREF: ph7_lib_ident()] * Please refer to the official documentation for function purpose and expected parameters. */ const char * ph7_lib_ident(void) { return PH7_IDENT; } /* * [CAPIREF: ph7_lib_copyright()] * Please refer to the official documentation for function purpose and expected parameters. */ const char * ph7_lib_copyright(void) { return PH7_COPYRIGHT; } /* * [CAPIREF: ph7_config()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_config(ph7 *pEngine,int nConfigOp,...) { va_list ap; int rc; if( PH7_ENGINE_MISUSE(pEngine) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif va_start(ap,nConfigOp); rc = EngineConfig(&(*pEngine),nConfigOp,ap); va_end(ap); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_init()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_init(ph7 **ppEngine) { ph7 *pEngine; int rc; #if defined(UNTRUST) if( ppEngine == 0 ){ return PH7_CORRUPT; } #endif *ppEngine = 0; /* One-time automatic library initialization */ rc = PH7CoreInitialize(); if( rc != PH7_OK ){ return rc; } /* Allocate a new engine */ pEngine = (ph7 *)SyMemBackendPoolAlloc(&sMPGlobal.sAllocator,sizeof(ph7)); if( pEngine == 0 ){ return PH7_NOMEM; } /* Zero the structure */ SyZero(pEngine,sizeof(ph7)); /* Initialize engine fields */ pEngine->nMagic = PH7_ENGINE_MAGIC; rc = SyMemBackendInitFromParent(&pEngine->sAllocator,&sMPGlobal.sAllocator); if( rc != PH7_OK ){ goto Release; } #if defined(PH7_ENABLE_THREADS) SyMemBackendDisbaleMutexing(&pEngine->sAllocator); #endif /* Default configuration */ SyBlobInit(&pEngine->xConf.sErrConsumer,&pEngine->sAllocator); /* Install a default compile-time error consumer routine */ ph7_config(pEngine,PH7_CONFIG_ERR_OUTPUT,PH7_VmBlobConsumer,&pEngine->xConf.sErrConsumer); /* Built-in vfs */ pEngine->pVfs = sMPGlobal.pVfs; #if defined(PH7_ENABLE_THREADS) if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE ){ /* Associate a recursive mutex with this instance */ pEngine->pMutex = SyMutexNew(sMPGlobal.pMutexMethods,SXMUTEX_TYPE_RECURSIVE); if( pEngine->pMutex == 0 ){ rc = PH7_NOMEM; goto Release; } } #endif /* Link to the list of active engines */ #if defined(PH7_ENABLE_THREADS) /* Enter the global mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif MACRO_LD_PUSH(sMPGlobal.pEngines,pEngine); sMPGlobal.nEngine++; #if defined(PH7_ENABLE_THREADS) /* Leave the global mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif /* Write a pointer to the new instance */ *ppEngine = pEngine; return PH7_OK; Release: SyMemBackendRelease(&pEngine->sAllocator); SyMemBackendPoolFree(&sMPGlobal.sAllocator,pEngine); return rc; } /* * [CAPIREF: ph7_release()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_release(ph7 *pEngine) { int rc; if( PH7_ENGINE_MISUSE(pEngine) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Release the engine */ rc = EngineRelease(&(*pEngine)); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ /* Release engine mutex */ SyMutexRelease(sMPGlobal.pMutexMethods,pEngine->pMutex) /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif #if defined(PH7_ENABLE_THREADS) /* Enter the global mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif /* Unlink from the list of active engines */ MACRO_LD_REMOVE(sMPGlobal.pEngines,pEngine); sMPGlobal.nEngine--; #if defined(PH7_ENABLE_THREADS) /* Leave the global mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */ #endif /* Release the memory chunk allocated to this engine */ SyMemBackendPoolFree(&sMPGlobal.sAllocator,pEngine); return rc; } /* * Compile a raw PHP script. * To execute a PHP code, it must first be compiled into a byte-code program using this routine. * If something goes wrong [i.e: compile-time error], your error log [i.e: error consumer callback] * should display the appropriate error message and this function set ppVm to null and return * an error code that is different from PH7_OK. Otherwise when the script is successfully compiled * ppVm should hold the PH7 byte-code and it's safe to call [ph7_vm_exec(), ph7_vm_reset(), etc.]. * This API does not actually evaluate the PHP code. It merely compile and prepares the PHP script * for evaluation. */ static sxi32 ProcessScript( ph7 *pEngine, /* Running PH7 engine */ ph7_vm **ppVm, /* OUT: A pointer to the virtual machine */ SyString *pScript, /* Raw PHP script to compile */ sxi32 iFlags, /* Compile-time flags */ const char *zFilePath /* File path if script come from a file. NULL otherwise */ ) { ph7_vm *pVm; int rc; /* Allocate a new virtual machine */ pVm = (ph7_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(ph7_vm)); if( pVm == 0 ){ /* If the supplied memory subsystem is so sick that we are unable to allocate * a tiny chunk of memory, there is no much we can do here. */ if( ppVm ){ *ppVm = 0; } return PH7_NOMEM; } if( iFlags < 0 ){ /* Default compile-time flags */ iFlags = 0; } /* Initialize the Virtual Machine */ rc = PH7_VmInit(pVm,&(*pEngine)); if( rc != PH7_OK ){ SyMemBackendPoolFree(&pEngine->sAllocator,pVm); if( ppVm ){ *ppVm = 0; } return PH7_VM_ERR; } if( zFilePath ){ /* Push processed file path */ PH7_VmPushFilePath(pVm,zFilePath,-1,TRUE,0); } /* Reset the error message consumer */ SyBlobReset(&pEngine->xConf.sErrConsumer); /* Compile the script */ PH7_CompileScript(pVm,&(*pScript),iFlags); if( pVm->sCodeGen.nErr > 0 || pVm == 0){ sxu32 nErr = pVm->sCodeGen.nErr; /* Compilation error or null ppVm pointer,release this VM */ SyMemBackendRelease(&pVm->sAllocator); SyMemBackendPoolFree(&pEngine->sAllocator,pVm); if( ppVm ){ *ppVm = 0; } return nErr > 0 ? PH7_COMPILE_ERR : PH7_OK; } /* Prepare the virtual machine for bytecode execution */ rc = PH7_VmMakeReady(pVm); if( rc != PH7_OK ){ goto Release; } /* Install local import path which is the current directory */ ph7_vm_config(pVm,PH7_VM_CONFIG_IMPORT_PATH,"./"); #if defined(PH7_ENABLE_THREADS) if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE ){ /* Associate a recursive mutex with this instance */ pVm->pMutex = SyMutexNew(sMPGlobal.pMutexMethods,SXMUTEX_TYPE_RECURSIVE); if( pVm->pMutex == 0 ){ goto Release; } } #endif /* Script successfully compiled,link to the list of active virtual machines */ MACRO_LD_PUSH(pEngine->pVms,pVm); pEngine->iVm++; /* Point to the freshly created VM */ *ppVm = pVm; /* Ready to execute PH7 bytecode */ return PH7_OK; Release: SyMemBackendRelease(&pVm->sAllocator); SyMemBackendPoolFree(&pEngine->sAllocator,pVm); *ppVm = 0; return PH7_VM_ERR; } /* * [CAPIREF: ph7_compile()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_compile(ph7 *pEngine,const char *zSource,int nLen,ph7_vm **ppOutVm) { SyString sScript; int rc; if( PH7_ENGINE_MISUSE(pEngine) || zSource == 0){ return PH7_CORRUPT; } if( nLen < 0 ){ /* Compute input length automatically */ nLen = (int)SyStrlen(zSource); } SyStringInitFromBuf(&sScript,zSource,nLen); #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Compile the script */ rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,0,0); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif /* Compilation result */ return rc; } /* * [CAPIREF: ph7_compile_v2()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_compile_v2(ph7 *pEngine,const char *zSource,int nLen,ph7_vm **ppOutVm,int iFlags) { SyString sScript; int rc; if( PH7_ENGINE_MISUSE(pEngine) || zSource == 0){ return PH7_CORRUPT; } if( nLen < 0 ){ /* Compute input length automatically */ nLen = (int)SyStrlen(zSource); } SyStringInitFromBuf(&sScript,zSource,nLen); #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Compile the script */ rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,iFlags,0); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif /* Compilation result */ return rc; } /* * [CAPIREF: ph7_compile_file()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_compile_file(ph7 *pEngine,const char *zFilePath,ph7_vm **ppOutVm,int iFlags) { const ph7_vfs *pVfs; int rc; if( ppOutVm ){ *ppOutVm = 0; } rc = PH7_OK; /* cc warning */ if( PH7_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* * Check if the underlying vfs implement the memory map * [i.e: mmap() under UNIX/MapViewOfFile() under windows] function. */ pVfs = pEngine->pVfs; if( pVfs == 0 || pVfs->xMmap == 0 ){ /* Memory map routine not implemented */ rc = PH7_IO_ERR; }else{ void *pMapView = 0; /* cc warning */ ph7_int64 nSize = 0; /* cc warning */ SyString sScript; /* Try to get a memory view of the whole file */ rc = pVfs->xMmap(zFilePath,&pMapView,&nSize); if( rc != PH7_OK ){ /* Assume an IO error */ rc = PH7_IO_ERR; }else{ /* Compile the file */ SyStringInitFromBuf(&sScript,pMapView,nSize); rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,iFlags,zFilePath); /* Release the memory view of the whole file */ if( pVfs->xUnmap ){ pVfs->xUnmap(pMapView,nSize); } } } #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif /* Compilation result */ return rc; } /* * [CAPIREF: ph7_vm_dump_v2()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_dump_v2(ph7_vm *pVm,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData) { int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } #ifdef UNTRUST if( xConsumer == 0 ){ return PH7_CORRUPT; } #endif /* Dump VM instructions */ rc = PH7_VmDump(&(*pVm),xConsumer,pUserData); return rc; } /* * [CAPIREF: ph7_vm_config()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_config(ph7_vm *pVm,int iConfigOp,...) { va_list ap; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Confiugure the virtual machine */ va_start(ap,iConfigOp); rc = PH7_VmConfigure(&(*pVm),iConfigOp,ap); va_end(ap); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_vm_exec()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_exec(ph7_vm *pVm,int *pExitStatus) { int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Execute PH7 byte-code */ rc = PH7_VmByteCodeExec(&(*pVm)); if( pExitStatus ){ /* Exit status */ *pExitStatus = pVm->iExitStatus; } #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif /* Execution result */ return rc; } /* * [CAPIREF: ph7_vm_reset()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_reset(ph7_vm *pVm) { int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif rc = PH7_VmReset(&(*pVm)); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_vm_release()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_vm_release(ph7_vm *pVm) { ph7 *pEngine; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif pEngine = pVm->pEngine; rc = PH7_VmRelease(&(*pVm)); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif if( rc == PH7_OK ){ /* Unlink from the list of active VM */ #if defined(PH7_ENABLE_THREADS) /* Acquire engine mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_ENGINE_RELEASE(pEngine) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif MACRO_LD_REMOVE(pEngine->pVms,pVm); pEngine->iVm--; /* Release the memory chunk allocated to this VM */ SyMemBackendPoolFree(&pEngine->sAllocator,pVm); #if defined(PH7_ENABLE_THREADS) /* Leave engine mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif } return rc; } /* * [CAPIREF: ph7_create_function()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_create_function(ph7_vm *pVm,const char *zName,int (*xFunc)(ph7_context *,int,ph7_value **),void *pUserData) { SyString sName; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } SyStringInitFromBuf(&sName,zName,SyStrlen(zName)); /* Remove leading and trailing white spaces */ SyStringFullTrim(&sName); /* Ticket 1433-003: NULL values are not allowed */ if( sName.nByte < 1 || xFunc == 0 ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Install the foreign function */ rc = PH7_VmInstallForeignFunction(&(*pVm),&sName,xFunc,pUserData); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_delete_function()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_delete_function(ph7_vm *pVm,const char *zName) { ph7_user_func *pFunc = 0; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Perform the deletion */ rc = SyHashDeleteEntry(&pVm->hHostFunction,(const void *)zName,SyStrlen(zName),(void **)&pFunc); if( rc == PH7_OK ){ /* Release internal fields */ SySetRelease(&pFunc->aAux); SyMemBackendFree(&pVm->sAllocator,(void *)SyStringData(&pFunc->sName)); SyMemBackendPoolFree(&pVm->sAllocator,pFunc); } #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_create_constant()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_create_constant(ph7_vm *pVm,const char *zName,void (*xExpand)(ph7_value *,void *),void *pUserData) { SyString sName; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } SyStringInitFromBuf(&sName,zName,SyStrlen(zName)); /* Remove leading and trailing white spaces */ SyStringFullTrim(&sName); if( sName.nByte < 1 ){ /* Empty constant name */ return PH7_CORRUPT; } /* TICKET 1433-003: NULL pointer harmless operation */ if( xExpand == 0 ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Perform the registration */ rc = PH7_VmRegisterConstant(&(*pVm),&sName,xExpand,pUserData); #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_delete_constant()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_delete_constant(ph7_vm *pVm,const char *zName) { ph7_constant *pCons; int rc; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } #if defined(PH7_ENABLE_THREADS) /* Acquire VM mutex */ SyMutexEnter(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ if( sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE && PH7_THRD_VM_RELEASE(pVm) ){ return PH7_ABORT; /* Another thread have released this instance */ } #endif /* Query the constant hashtable */ rc = SyHashDeleteEntry(&pVm->hConstant,(const void *)zName,SyStrlen(zName),(void **)&pCons); if( rc == PH7_OK ){ /* Perform the deletion */ SyMemBackendFree(&pVm->sAllocator,(void *)SyStringData(&pCons->sName)); SyMemBackendPoolFree(&pVm->sAllocator,pCons); } #if defined(PH7_ENABLE_THREADS) /* Leave VM mutex */ SyMutexLeave(sMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */ #endif return rc; } /* * [CAPIREF: ph7_new_scalar()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value * ph7_new_scalar(ph7_vm *pVm) { ph7_value *pObj; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return 0; } /* Allocate a new scalar variable */ pObj = (ph7_value *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_value)); if( pObj == 0 ){ return 0; } /* Nullify the new scalar */ PH7_MemObjInit(pVm,pObj); return pObj; } /* * [CAPIREF: ph7_new_array()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value * ph7_new_array(ph7_vm *pVm) { ph7_hashmap *pMap; ph7_value *pObj; /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return 0; } /* Create a new hashmap first */ pMap = PH7_NewHashmap(&(*pVm),0,0); if( pMap == 0 ){ return 0; } /* Associate a new ph7_value with this hashmap */ pObj = (ph7_value *)SyMemBackendPoolAlloc(&pVm->sAllocator,sizeof(ph7_value)); if( pObj == 0 ){ PH7_HashmapRelease(pMap,TRUE); return 0; } PH7_MemObjInitFromArray(pVm,pObj,pMap); return pObj; } /* * [CAPIREF: ph7_release_value()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_release_value(ph7_vm *pVm,ph7_value *pValue) { /* Ticket 1433-002: NULL VM is harmless operation */ if ( PH7_VM_MISUSE(pVm) ){ return PH7_CORRUPT; } if( pValue ){ /* Release the value */ PH7_MemObjRelease(pValue); SyMemBackendPoolFree(&pVm->sAllocator,pValue); } return PH7_OK; } /* * [CAPIREF: ph7_value_to_int()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_to_int(ph7_value *pValue) { int rc; rc = PH7_MemObjToInteger(pValue); if( rc != PH7_OK ){ return 0; } return (int)pValue->x.iVal; } /* * [CAPIREF: ph7_value_to_bool()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_to_bool(ph7_value *pValue) { int rc; rc = PH7_MemObjToBool(pValue); if( rc != PH7_OK ){ return 0; } return (int)pValue->x.iVal; } /* * [CAPIREF: ph7_value_to_int64()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_int64 ph7_value_to_int64(ph7_value *pValue) { int rc; rc = PH7_MemObjToInteger(pValue); if( rc != PH7_OK ){ return 0; } return pValue->x.iVal; } /* * [CAPIREF: ph7_value_to_double()] * Please refer to the official documentation for function purpose and expected parameters. */ double ph7_value_to_double(ph7_value *pValue) { int rc; rc = PH7_MemObjToReal(pValue); if( rc != PH7_OK ){ return (double)0; } return (double)pValue->rVal; } /* * [CAPIREF: ph7_value_to_string()] * Please refer to the official documentation for function purpose and expected parameters. */ const char * ph7_value_to_string(ph7_value *pValue,int *pLen) { PH7_MemObjToString(pValue); if( SyBlobLength(&pValue->sBlob) > 0 ){ SyBlobNullAppend(&pValue->sBlob); if( pLen ){ *pLen = (int)SyBlobLength(&pValue->sBlob); } return (const char *)SyBlobData(&pValue->sBlob); }else{ /* Return the empty string */ if( pLen ){ *pLen = 0; } return ""; } } /* * [CAPIREF: ph7_value_to_resource()] * Please refer to the official documentation for function purpose and expected parameters. */ void * ph7_value_to_resource(ph7_value *pValue) { if( (pValue->iFlags & MEMOBJ_RES) == 0 ){ /* Not a resource,return NULL */ return 0; } return pValue->x.pOther; } /* * [CAPIREF: ph7_value_compare()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_compare(ph7_value *pLeft,ph7_value *pRight,int bStrict) { int rc; if( pLeft == 0 || pRight == 0 ){ /* TICKET 1433-24: NULL values is harmless operation */ return 1; } /* Perform the comparison */ rc = PH7_MemObjCmp(&(*pLeft),&(*pRight),bStrict,0); /* Comparison result */ return rc; } /* * [CAPIREF: ph7_result_int()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_int(ph7_context *pCtx,int iValue) { return ph7_value_int(pCtx->pRet,iValue); } /* * [CAPIREF: ph7_result_int64()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_int64(ph7_context *pCtx,ph7_int64 iValue) { return ph7_value_int64(pCtx->pRet,iValue); } /* * [CAPIREF: ph7_result_bool()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_bool(ph7_context *pCtx,int iBool) { return ph7_value_bool(pCtx->pRet,iBool); } /* * [CAPIREF: ph7_result_double()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_double(ph7_context *pCtx,double Value) { return ph7_value_double(pCtx->pRet,Value); } /* * [CAPIREF: ph7_result_null()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_null(ph7_context *pCtx) { /* Invalidate any prior representation and set the NULL flag */ PH7_MemObjRelease(pCtx->pRet); return PH7_OK; } /* * [CAPIREF: ph7_result_string()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_string(ph7_context *pCtx,const char *zString,int nLen) { return ph7_value_string(pCtx->pRet,zString,nLen); } /* * [CAPIREF: ph7_result_string_format()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_string_format(ph7_context *pCtx,const char *zFormat,...) { ph7_value *p; va_list ap; int rc; p = pCtx->pRet; if( (p->iFlags & MEMOBJ_STRING) == 0 ){ /* Invalidate any prior representation */ PH7_MemObjRelease(p); MemObjSetType(p,MEMOBJ_STRING); } /* Format the given string */ va_start(ap,zFormat); rc = SyBlobFormatAp(&p->sBlob,zFormat,ap); va_end(ap); return rc; } /* * [CAPIREF: ph7_result_value()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_value(ph7_context *pCtx,ph7_value *pValue) { int rc = PH7_OK; if( pValue == 0 ){ PH7_MemObjRelease(pCtx->pRet); }else{ rc = PH7_MemObjStore(pValue,pCtx->pRet); } return rc; } /* * [CAPIREF: ph7_result_resource()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_result_resource(ph7_context *pCtx,void *pUserData) { return ph7_value_resource(pCtx->pRet,pUserData); } /* * [CAPIREF: ph7_context_new_scalar()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value * ph7_context_new_scalar(ph7_context *pCtx) { ph7_value *pVal; pVal = ph7_new_scalar(pCtx->pVm); if( pVal ){ /* Record value address so it can be freed automatically * when the calling function returns. */ SySetPut(&pCtx->sVar,(const void *)&pVal); } return pVal; } /* * [CAPIREF: ph7_context_new_array()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value * ph7_context_new_array(ph7_context *pCtx) { ph7_value *pVal; pVal = ph7_new_array(pCtx->pVm); if( pVal ){ /* Record value address so it can be freed automatically * when the calling function returns. */ SySetPut(&pCtx->sVar,(const void *)&pVal); } return pVal; } /* * [CAPIREF: ph7_context_release_value()] * Please refer to the official documentation for function purpose and expected parameters. */ void ph7_context_release_value(ph7_context *pCtx,ph7_value *pValue) { PH7_VmReleaseContextValue(&(*pCtx),pValue); } /* * [CAPIREF: ph7_context_alloc_chunk()] * Please refer to the official documentation for function purpose and expected parameters. */ void * ph7_context_alloc_chunk(ph7_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease) { void *pChunk; pChunk = SyMemBackendAlloc(&pCtx->pVm->sAllocator,nByte); if( pChunk ){ if( ZeroChunk ){ /* Zero the memory chunk */ SyZero(pChunk,nByte); } if( AutoRelease ){ ph7_aux_data sAux; /* Track the chunk so that it can be released automatically * upon this context is destroyed. */ sAux.pAuxData = pChunk; SySetPut(&pCtx->sChunk,(const void *)&sAux); } } return pChunk; } /* * Check if the given chunk address is registered in the call context * chunk container. * Return TRUE if registered.FALSE otherwise. * Refer to [ph7_context_realloc_chunk(),ph7_context_free_chunk()]. */ static ph7_aux_data * ContextFindChunk(ph7_context *pCtx,void *pChunk) { ph7_aux_data *aAux,*pAux; sxu32 n; if( SySetUsed(&pCtx->sChunk) < 1 ){ /* Don't bother processing,the container is empty */ return 0; } /* Perform the lookup */ aAux = (ph7_aux_data *)SySetBasePtr(&pCtx->sChunk); for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){ pAux = &aAux[n]; if( pAux->pAuxData == pChunk ){ /* Chunk found */ return pAux; } } /* No such allocated chunk */ return 0; } /* * [CAPIREF: ph7_context_realloc_chunk()] * Please refer to the official documentation for function purpose and expected parameters. */ void * ph7_context_realloc_chunk(ph7_context *pCtx,void *pChunk,unsigned int nByte) { ph7_aux_data *pAux; void *pNew; pNew = SyMemBackendRealloc(&pCtx->pVm->sAllocator,pChunk,nByte); if( pNew ){ pAux = ContextFindChunk(pCtx,pChunk); if( pAux ){ pAux->pAuxData = pNew; } } return pNew; } /* * [CAPIREF: ph7_context_free_chunk()] * Please refer to the official documentation for function purpose and expected parameters. */ void ph7_context_free_chunk(ph7_context *pCtx,void *pChunk) { ph7_aux_data *pAux; if( pChunk == 0 ){ /* TICKET-1433-93: NULL chunk is a harmless operation */ return; } pAux = ContextFindChunk(pCtx,pChunk); if( pAux ){ /* Mark as destroyed */ pAux->pAuxData = 0; } SyMemBackendFree(&pCtx->pVm->sAllocator,pChunk); } /* * [CAPIREF: ph7_array_fetch()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value * ph7_array_fetch(ph7_value *pArray,const char *zKey,int nByte) { ph7_hashmap_node *pNode; ph7_value *pValue; ph7_value skey; int rc; /* Make sure we are dealing with a valid hashmap */ if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ return 0; } if( nByte < 0 ){ nByte = (int)SyStrlen(zKey); } /* Convert the key to a ph7_value */ PH7_MemObjInit(pArray->pVm,&skey); PH7_MemObjStringAppend(&skey,zKey,(sxu32)nByte); /* Perform the lookup */ rc = PH7_HashmapLookup((ph7_hashmap *)pArray->x.pOther,&skey,&pNode); PH7_MemObjRelease(&skey); if( rc != PH7_OK ){ /* No such entry */ return 0; } /* Extract the target value */ pValue = (ph7_value *)SySetAt(&pArray->pVm->aMemObj,pNode->nValIdx); return pValue; } /* * [CAPIREF: ph7_array_walk()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_array_walk(ph7_value *pArray,int (*xWalk)(ph7_value *pValue,ph7_value *,void *),void *pUserData) { int rc; if( xWalk == 0 ){ return PH7_CORRUPT; } /* Make sure we are dealing with a valid hashmap */ if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ return PH7_CORRUPT; } /* Start the walk process */ rc = PH7_HashmapWalk((ph7_hashmap *)pArray->x.pOther,xWalk,pUserData); return rc != PH7_OK ? PH7_ABORT /* User callback request an operation abort*/ : PH7_OK; } /* * [CAPIREF: ph7_array_add_elem()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_array_add_elem(ph7_value *pArray,ph7_value *pKey,ph7_value *pValue) { int rc; /* Make sure we are dealing with a valid hashmap */ if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ return PH7_CORRUPT; } /* Perform the insertion */ rc = PH7_HashmapInsert((ph7_hashmap *)pArray->x.pOther,&(*pKey),&(*pValue)); return rc; } /* * [CAPIREF: ph7_array_add_strkey_elem()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_array_add_strkey_elem(ph7_value *pArray,const char *zKey,ph7_value *pValue) { int rc; /* Make sure we are dealing with a valid hashmap */ if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ return PH7_CORRUPT; } /* Perform the insertion */ if( SX_EMPTY_STR(zKey) ){ /* Empty key,assign an automatic index */ rc = PH7_HashmapInsert((ph7_hashmap *)pArray->x.pOther,0,&(*pValue)); }else{ ph7_value sKey; PH7_MemObjInitFromString(pArray->pVm,&sKey,0); PH7_MemObjStringAppend(&sKey,zKey,(sxu32)SyStrlen(zKey)); rc = PH7_HashmapInsert((ph7_hashmap *)pArray->x.pOther,&sKey,&(*pValue)); PH7_MemObjRelease(&sKey); } return rc; } /* * [CAPIREF: ph7_array_add_intkey_elem()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_array_add_intkey_elem(ph7_value *pArray,int iKey,ph7_value *pValue) { ph7_value sKey; int rc; /* Make sure we are dealing with a valid hashmap */ if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ return PH7_CORRUPT; } PH7_MemObjInitFromInt(pArray->pVm,&sKey,iKey); /* Perform the insertion */ rc = PH7_HashmapInsert((ph7_hashmap *)pArray->x.pOther,&sKey,&(*pValue)); PH7_MemObjRelease(&sKey); return rc; } /* * [CAPIREF: ph7_array_count()] * Please refer to the official documentation for function purpose and expected parameters. */ unsigned int ph7_array_count(ph7_value *pArray) { ph7_hashmap *pMap; /* Make sure we are dealing with a valid hashmap */ if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){ return 0; } /* Point to the internal representation of the hashmap */ pMap = (ph7_hashmap *)pArray->x.pOther; return pMap->nEntry; } /* * [CAPIREF: ph7_object_walk()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_object_walk(ph7_value *pObject,int (*xWalk)(const char *,ph7_value *,void *),void *pUserData) { int rc; if( xWalk == 0 ){ return PH7_CORRUPT; } /* Make sure we are dealing with a valid class instance */ if( (pObject->iFlags & MEMOBJ_OBJ) == 0 ){ return PH7_CORRUPT; } /* Start the walk process */ rc = PH7_ClassInstanceWalk((ph7_class_instance *)pObject->x.pOther,xWalk,pUserData); return rc != PH7_OK ? PH7_ABORT /* User callback request an operation abort*/ : PH7_OK; } /* * [CAPIREF: ph7_object_fetch_attr()] * Please refer to the official documentation for function purpose and expected parameters. */ ph7_value * ph7_object_fetch_attr(ph7_value *pObject,const char *zAttr) { ph7_value *pValue; SyString sAttr; /* Make sure we are dealing with a valid class instance */ if( (pObject->iFlags & MEMOBJ_OBJ) == 0 || zAttr == 0 ){ return 0; } SyStringInitFromBuf(&sAttr,zAttr,SyStrlen(zAttr)); /* Extract the attribute value if available. */ pValue = PH7_ClassInstanceFetchAttr((ph7_class_instance *)pObject->x.pOther,&sAttr); return pValue; } /* * [CAPIREF: ph7_object_get_class_name()] * Please refer to the official documentation for function purpose and expected parameters. */ const char * ph7_object_get_class_name(ph7_value *pObject,int *pLength) { ph7_class *pClass; if( pLength ){ *pLength = 0; } /* Make sure we are dealing with a valid class instance */ if( (pObject->iFlags & MEMOBJ_OBJ) == 0 ){ return 0; } /* Point to the class */ pClass = ((ph7_class_instance *)pObject->x.pOther)->pClass; /* Return the class name */ if( pLength ){ *pLength = (int)SyStringLength(&pClass->sName); } return SyStringData(&pClass->sName); } /* * [CAPIREF: ph7_context_output()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_output(ph7_context *pCtx,const char *zString,int nLen) { SyString sData; int rc; if( nLen < 0 ){ nLen = (int)SyStrlen(zString); } SyStringInitFromBuf(&sData,zString,nLen); rc = PH7_VmOutputConsume(pCtx->pVm,&sData); return rc; } /* * [CAPIREF: ph7_context_output_format()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_output_format(ph7_context *pCtx,const char *zFormat,...) { va_list ap; int rc; va_start(ap,zFormat); rc = PH7_VmOutputConsumeAp(pCtx->pVm,zFormat,ap); va_end(ap); return rc; } /* * [CAPIREF: ph7_context_throw_error()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_throw_error(ph7_context *pCtx,int iErr,const char *zErr) { int rc = PH7_OK; if( zErr ){ rc = PH7_VmThrowError(pCtx->pVm,&pCtx->pFunc->sName,iErr,zErr); } return rc; } /* * [CAPIREF: ph7_context_throw_error_format()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_throw_error_format(ph7_context *pCtx,int iErr,const char *zFormat,...) { va_list ap; int rc; if( zFormat == 0){ return PH7_OK; } va_start(ap,zFormat); rc = PH7_VmThrowErrorAp(pCtx->pVm,&pCtx->pFunc->sName,iErr,zFormat,ap); va_end(ap); return rc; } /* * [CAPIREF: ph7_context_random_num()] * Please refer to the official documentation for function purpose and expected parameters. */ unsigned int ph7_context_random_num(ph7_context *pCtx) { sxu32 n; n = PH7_VmRandomNum(pCtx->pVm); return n; } /* * [CAPIREF: ph7_context_random_string()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_random_string(ph7_context *pCtx,char *zBuf,int nBuflen) { if( nBuflen < 3 ){ return PH7_CORRUPT; } PH7_VmRandomString(pCtx->pVm,zBuf,nBuflen); return PH7_OK; } /* * IMP-12-07-2012 02:10 Experimantal public API. * * ph7_vm * ph7_context_get_vm(ph7_context *pCtx) * { * return pCtx->pVm; * } */ /* * [CAPIREF: ph7_context_user_data()] * Please refer to the official documentation for function purpose and expected parameters. */ void * ph7_context_user_data(ph7_context *pCtx) { return pCtx->pFunc->pUserData; } /* * [CAPIREF: ph7_context_push_aux_data()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_context_push_aux_data(ph7_context *pCtx,void *pUserData) { ph7_aux_data sAux; int rc; sAux.pAuxData = pUserData; rc = SySetPut(&pCtx->pFunc->aAux,(const void *)&sAux); return rc; } /* * [CAPIREF: ph7_context_peek_aux_data()] * Please refer to the official documentation for function purpose and expected parameters. */ void * ph7_context_peek_aux_data(ph7_context *pCtx) { ph7_aux_data *pAux; pAux = (ph7_aux_data *)SySetPeek(&pCtx->pFunc->aAux); return pAux ? pAux->pAuxData : 0; } /* * [CAPIREF: ph7_context_pop_aux_data()] * Please refer to the official documentation for function purpose and expected parameters. */ void * ph7_context_pop_aux_data(ph7_context *pCtx) { ph7_aux_data *pAux; pAux = (ph7_aux_data *)SySetPop(&pCtx->pFunc->aAux); return pAux ? pAux->pAuxData : 0; } /* * [CAPIREF: ph7_context_result_buf_length()] * Please refer to the official documentation for function purpose and expected parameters. */ unsigned int ph7_context_result_buf_length(ph7_context *pCtx) { return SyBlobLength(&pCtx->pRet->sBlob); } /* * [CAPIREF: ph7_function_name()] * Please refer to the official documentation for function purpose and expected parameters. */ const char * ph7_function_name(ph7_context *pCtx) { SyString *pName; pName = &pCtx->pFunc->sName; return pName->zString; } /* * [CAPIREF: ph7_value_int()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_int(ph7_value *pVal,int iValue) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); pVal->x.iVal = (ph7_int64)iValue; MemObjSetType(pVal,MEMOBJ_INT); return PH7_OK; } /* * [CAPIREF: ph7_value_int64()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_int64(ph7_value *pVal,ph7_int64 iValue) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); pVal->x.iVal = iValue; MemObjSetType(pVal,MEMOBJ_INT); return PH7_OK; } /* * [CAPIREF: ph7_value_bool()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_bool(ph7_value *pVal,int iBool) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); pVal->x.iVal = iBool ? 1 : 0; MemObjSetType(pVal,MEMOBJ_BOOL); return PH7_OK; } /* * [CAPIREF: ph7_value_null()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_null(ph7_value *pVal) { /* Invalidate any prior representation and set the NULL flag */ PH7_MemObjRelease(pVal); return PH7_OK; } /* * [CAPIREF: ph7_value_double()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_double(ph7_value *pVal,double Value) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); pVal->rVal = (ph7_real)Value; MemObjSetType(pVal,MEMOBJ_REAL); /* Try to get an integer representation also */ PH7_MemObjTryInteger(pVal); return PH7_OK; } /* * [CAPIREF: ph7_value_string()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_string(ph7_value *pVal,const char *zString,int nLen) { if((pVal->iFlags & MEMOBJ_STRING) == 0 ){ /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); MemObjSetType(pVal,MEMOBJ_STRING); } if( zString ){ if( nLen < 0 ){ /* Compute length automatically */ nLen = (int)SyStrlen(zString); } SyBlobAppend(&pVal->sBlob,(const void *)zString,(sxu32)nLen); } return PH7_OK; } /* * [CAPIREF: ph7_value_string_format()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_string_format(ph7_value *pVal,const char *zFormat,...) { va_list ap; int rc; if((pVal->iFlags & MEMOBJ_STRING) == 0 ){ /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); MemObjSetType(pVal,MEMOBJ_STRING); } va_start(ap,zFormat); rc = SyBlobFormatAp(&pVal->sBlob,zFormat,ap); va_end(ap); return PH7_OK; } /* * [CAPIREF: ph7_value_reset_string_cursor()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_reset_string_cursor(ph7_value *pVal) { /* Reset the string cursor */ SyBlobReset(&pVal->sBlob); return PH7_OK; } /* * [CAPIREF: ph7_value_resource()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_resource(ph7_value *pVal,void *pUserData) { /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); /* Reflect the new type */ pVal->x.pOther = pUserData; MemObjSetType(pVal,MEMOBJ_RES); return PH7_OK; } /* * [CAPIREF: ph7_value_release()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_release(ph7_value *pVal) { PH7_MemObjRelease(pVal); return PH7_OK; } /* * [CAPIREF: ph7_value_is_int()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_int(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_INT) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_float()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_float(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_REAL) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_bool()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_bool(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_string()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_string(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_null()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_null(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_numeric()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_numeric(ph7_value *pVal) { int rc; rc = PH7_MemObjIsNumeric(pVal); return rc; } /* * [CAPIREF: ph7_value_is_callable()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_callable(ph7_value *pVal) { int rc; rc = PH7_VmIsCallable(pVal->pVm,pVal,FALSE); return rc; } /* * [CAPIREF: ph7_value_is_scalar()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_scalar(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_SCALAR) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_array()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_array(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_HASHMAP) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_object()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_object(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_OBJ) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_resource()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_resource(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE; } /* * [CAPIREF: ph7_value_is_empty()] * Please refer to the official documentation for function purpose and expected parameters. */ int ph7_value_is_empty(ph7_value *pVal) { int rc; rc = PH7_MemObjIsEmpty(pVal); return rc; } /* END-OF-IMPLEMENTATION: ph7@embedded@symisc 345-09-46 */ /* * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language. * Copyright (C) 2011-2012,Symisc Systems http://ph7.symisc.net/ * Version 2.1.4 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES * please contact Symisc Systems via: * legal@symisc.net * licensing@symisc.net * contact@symisc.net * or visit: * http://ph7.symisc.net/ */ /* * Copyright (C) 2011, 2012 Symisc Systems. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Redistributions in any form must be accompanied by information on * how to obtain complete source code for the PH7 engine and any * accompanying software that uses the PH7 engine software. * The source code must either be included in the distribution * or be available for no more than the cost of distribution plus * a nominal fee, and must be freely redistributable under reasonable * conditions. For an executable file, complete source code means * the source code for all modules it contains.It does not include * source code for modules or files that typically accompany the major * components of the operating system on which the executable file runs. * * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */