From 5b10ea3b41f2e61a300edab7fec0d6c77051f7bf Mon Sep 17 00:00:00 2001 From: Rafal Kupiec Date: Thu, 12 Jul 2018 13:26:32 +0200 Subject: [PATCH] de-amalgamation of code for easier maintenance --- api.c | 2075 ++ builtin.c | 8754 ++++++++ compile.c | 6538 ++++++ constant.c | 2074 ++ hashmap.c | 5788 +++++ lex.c | 1162 + lib.c | 5500 +++++ memobj.c | 1301 ++ oo.c | 1147 + parse.c | 1620 ++ ph7.c | 62027 --------------------------------------------------- ph7int.h | 2030 ++ vfs.c | 8255 +++++++ vm.c | 14861 ++++++++++++ 14 files changed, 61105 insertions(+), 62027 deletions(-) create mode 100644 api.c create mode 100644 builtin.c create mode 100644 compile.c create mode 100644 constant.c create mode 100644 hashmap.c create mode 100644 lex.c create mode 100644 lib.c create mode 100644 memobj.c create mode 100644 oo.c create mode 100644 parse.c delete mode 100644 ph7.c create mode 100644 ph7int.h create mode 100644 vfs.c create mode 100644 vm.c diff --git a/api.c b/api.c new file mode 100644 index 0000000..4a87255 --- /dev/null +++ b/api.c @@ -0,0 +1,2075 @@ +/* + * 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; +} diff --git a/builtin.c b/builtin.c new file mode 100644 index 0000000..d3ce17f --- /dev/null +++ b/builtin.c @@ -0,0 +1,8754 @@ +/* + * 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)); +} diff --git a/compile.c b/compile.c new file mode 100644 index 0000000..15bd376 --- /dev/null +++ b/compile.c @@ -0,0 +1,6538 @@ +/* + * 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; +} diff --git a/constant.c b/constant.c new file mode 100644 index 0000000..27308b7 --- /dev/null +++ b/constant.c @@ -0,0 +1,2074 @@ +/* + * 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)); + } +} diff --git a/hashmap.c b/hashmap.c new file mode 100644 index 0000000..071e625 --- /dev/null +++ b/hashmap.c @@ -0,0 +1,5788 @@ +/* + * 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; +} diff --git a/lex.c b/lex.c new file mode 100644 index 0000000..8fe6b27 --- /dev/null +++ b/lex.c @@ -0,0 +1,1162 @@ +/* + * 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; +} diff --git a/lib.c b/lib.c new file mode 100644 index 0000000..8a17457 --- /dev/null +++ b/lib.c @@ -0,0 +1,5500 @@ +/* + * 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 */ diff --git a/memobj.c b/memobj.c new file mode 100644 index 0000000..c8170ab --- /dev/null +++ b/memobj.c @@ -0,0 +1,1301 @@ +/* + * 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; +} diff --git a/oo.c b/oo.c new file mode 100644 index 0000000..04d08ce --- /dev/null +++ b/oo.c @@ -0,0 +1,1147 @@ +/* + * 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); +} diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..87e3274 --- /dev/null +++ b/parse.c @@ -0,0 +1,1620 @@ +/* + * 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; +} diff --git a/ph7.c b/ph7.c deleted file mode 100644 index 6460b28..0000000 --- a/ph7.c +++ /dev/null @@ -1,62027 +0,0 @@ -/* - * 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. - */ diff --git a/ph7int.h b/ph7int.h new file mode 100644 index 0000000..6aa0319 --- /dev/null +++ b/ph7int.h @@ -0,0 +1,2030 @@ +/* + * 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__ */ diff --git a/vfs.c b/vfs.c new file mode 100644 index 0000000..f452051 --- /dev/null +++ b/vfs.c @@ -0,0 +1,8255 @@ +/* + * 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 +} diff --git a/vm.c b/vm.c new file mode 100644 index 0000000..21a83ee --- /dev/null +++ b/vm.c @@ -0,0 +1,14861 @@ +/* + * 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; + }