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; + }