62028 lines
1.9 MiB
62028 lines
1.9 MiB
/*
|
||
* 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 <chm@symisc.net> $
|
||
*/
|
||
/* 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: <ph7.h>" 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: <ph7.h>
|
||
*/
|
||
/*
|
||
* ----------------------------------------------------------
|
||
* 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 <chm@symisc.net> $ */
|
||
#include <stdarg.h> /* 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 <time.h> 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 <?php ?> 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 <chm@symisc.net> $ */
|
||
#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 <ctype.h>
|
||
#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 <chm@symisc.net> $ */
|
||
#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 pNos<pTos.
|
||
* 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_LE P1 P2 P3
|
||
*
|
||
* Pop the top two elements from the stack. If the second element (the top of stack)
|
||
* is less than or equal to the first (next on stack),then jump to instruction P2.
|
||
* Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
|
||
* 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_LT:
|
||
case PH7_OP_LE: {
|
||
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_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 pNos<pTos.
|
||
* 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_GE P1 P2 P3
|
||
*
|
||
* Pop the top two elements from the stack. If the second element (the top of stack)
|
||
* is greater than or equal to the first (next on stack),then jump to instruction P2.
|
||
* Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
|
||
* 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_GT:
|
||
case PH7_OP_GE: {
|
||
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_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 <20> 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 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"\
|
||
"<html><head>"\
|
||
"<!-- Copyright (C) 2011-2012 Symisc Systems,http://www.symisc.net contact@symisc.net -->"\
|
||
"<meta content=\"text/html; charset=UTF-8\" http-equiv=\"content-type\"><title>PH7 engine credits</title>"\
|
||
"<style type=\"text/css\">"\
|
||
"div {"\
|
||
"border: 1px solid #cccccc;"\
|
||
"-moz-border-radius-topleft: 10px;"\
|
||
"-moz-border-radius-bottomright: 10px;"\
|
||
"-moz-border-radius-bottomleft: 10px;"\
|
||
"-moz-border-radius-topright: 10px;"\
|
||
"-webkit-border-radius: 10px;"\
|
||
"-o-border-radius: 10px;"\
|
||
"border-radius: 10px;"\
|
||
"padding-left: 2em;"\
|
||
"background-color: white;"\
|
||
"margin-left: auto;"\
|
||
"font-family: verdana;"\
|
||
"padding-right: 2em;"\
|
||
"margin-right: auto;"\
|
||
"}"\
|
||
"body {"\
|
||
"padding: 0.2em;"\
|
||
"font-style: normal;"\
|
||
"font-size: medium;"\
|
||
"background-color: #f2f2f2;"\
|
||
"}"\
|
||
"hr {"\
|
||
"border-style: solid none none;"\
|
||
"border-width: 1px medium medium;"\
|
||
"border-top: 1px solid #cccccc;"\
|
||
"height: 1px;"\
|
||
"}"\
|
||
"a {"\
|
||
"color: #3366cc;"\
|
||
"text-decoration: none;"\
|
||
"}"\
|
||
"a:hover {"\
|
||
"color: #999999;"\
|
||
"}"\
|
||
"a:active {"\
|
||
"color: #663399;"\
|
||
"}"\
|
||
"h1 {"\
|
||
"margin: 0;"\
|
||
"padding: 0;"\
|
||
"font-family: Verdana;"\
|
||
"font-weight: bold;"\
|
||
"font-style: normal;"\
|
||
"font-size: medium;"\
|
||
"text-transform: capitalize;"\
|
||
"color: #0a328c;"\
|
||
"}"\
|
||
"p {"\
|
||
"margin: 0 auto;"\
|
||
"font-size: medium;"\
|
||
"font-style: normal;"\
|
||
"font-family: verdana;"\
|
||
"}"\
|
||
"</style></head><body>"\
|
||
"<div style=\"background-color: white; width: 699px;\">"\
|
||
"<h1 style=\"font-family: Verdana; text-align: right;\"><small><small>PH7 Engine Credits</small></small></h1>"\
|
||
"<hr style=\"margin-left: auto; margin-right: auto;\">"\
|
||
"<p><small><a href=\"http://ph7.symisc.net/\"><small><span style=\"font-weight: bold;\">"\
|
||
"Symisc PH7</span></small></a><small> </small></small></p>"\
|
||
"<p style=\"text-align: left;\"><small><small>"\
|
||
"A highly efficient embeddable bytecode compiler and a Virtual Machine for the PHP(5) Programming Language.</small></small></p>"\
|
||
"<p style=\"text-align: left;\"><small><small>Copyright (C) Symisc Systems.<br></small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold;\"><small><small>Engine Version:</small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold; margin-left: 40px;\">"
|
||
|
||
#define PH7_HTML_PAGE_FORMAT "<small><small><span style=\"font-weight: normal;\">%s</span></small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold;\"><small><small>Engine ID:</small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold; margin-left: 40px;\"><small><small><span style=\"font-weight: normal;\">%s %s</span></small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold;\"><small><small>Underlying VFS:</small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold; margin-left: 40px;\"><small><small><span style=\"font-weight: normal;\">%s</span></small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold;\"><small><small>Total Built-in Functions:</small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold; margin-left: 40px;\"><small><small><span style=\"font-weight: normal;\">%d</span></small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold;\"><small><small>Total Built-in Classes:</small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold; margin-left: 40px;\"><small><small><span style=\"font-weight: normal;\">%d</span></small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold;\"><small><small>Host Operating System:</small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold; margin-left: 40px;\"><small><small><span style=\"font-weight: normal;\">%s</span></small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold;\"><small style=\"font-weight: bold;\"><small><small></small></small></small></p>"\
|
||
"<p style=\"text-align: left; font-weight: bold;\"><small><small>Licensed To: <Public Release Under The <a href=\"http://www.symisc.net/spl.txt\">"\
|
||
"Symisc Public License (SPL)</a>></small></small></p>"
|
||
|
||
#define PH7_HTML_PAGE_FOOTER "<p style=\"text-align: left; font-weight: bold; margin-left: 40px;\"><small><small><span style=\"font-weight: normal;\">/*<br>"\
|
||
" * Copyright (C) 2011, 2012 Symisc Systems. All rights reserved.<br>"\
|
||
" *<br>"\
|
||
" * Redistribution and use in source and binary forms, with or without<br>"\
|
||
" * modification, are permitted provided that the following conditions<br>"\
|
||
" * are met:<br>"\
|
||
" * 1. Redistributions of source code must retain the above copyright<br>"\
|
||
" * notice, this list of conditions and the following disclaimer.<br>"\
|
||
" * 2. Redistributions in binary form must reproduce the above copyright<br>"\
|
||
" * notice, this list of conditions and the following disclaimer in the<br>"\
|
||
" * documentation and/or other materials provided with the distribution.<br>"\
|
||
" * 3. Redistributions in any form must be accompanied by information on<br>"\
|
||
" * how to obtain complete source code for the PH7 engine and any <br>"\
|
||
" * accompanying software that uses the PH7 engine software.<br>"\
|
||
" * The source code must either be included in the distribution<br>"\
|
||
" * or be available for no more than the cost of distribution plus<br>"\
|
||
" * a nominal fee, and must be freely redistributable under reasonable<br>"\
|
||
" * conditions. For an executable file, complete source code means<br>"\
|
||
" * the source code for all modules it contains.It does not include<br>"\
|
||
" * source code for modules or files that typically accompany the major<br>"\
|
||
" * components of the operating system on which the executable file runs.<br>"\
|
||
" *<br>"\
|
||
" * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS<br>"\
|
||
" * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED<br>"\
|
||
" * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR<br>"\
|
||
" * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS<br>"\
|
||
" * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br>"\
|
||
" * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<br>"\
|
||
" * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR<br>"\
|
||
" * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,<br>"\
|
||
" * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE<br>"\
|
||
" * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN<br>"\
|
||
" * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br>"\
|
||
" */<br>"\
|
||
"</span></small></small></p>"\
|
||
"<p style=\"text-align: right;\"><small><small>Copyright (C) <a href=\"http://www.symisc.net/\">Symisc Systems</a></small></small><big>"\
|
||
"</big></p></div></body></html>"
|
||
/*
|
||
* 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 <20> whether absolute (starting with a drive letter
|
||
* or \ on Windows, or / on Unix/Linux systems) or relative to the current
|
||
* directory (starting with . or ..) <20> 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 <chm@symisc.net>
|
||
* 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 <chm@symisc.net> $ */
|
||
#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 <Windows.h>
|
||
#elif defined(__UNIXES__)
|
||
#include <sys/utsname.h>
|
||
#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 <Windows.h>
|
||
/*
|
||
** 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 <sys/types.h>
|
||
#include <limits.h>
|
||
#include <fcntl.h>
|
||
#include <unistd.h>
|
||
#include <sys/uio.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/mman.h>
|
||
#include <sys/file.h>
|
||
#include <pwd.h>
|
||
#include <grp.h>
|
||
#include <dirent.h>
|
||
#include <utime.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
/* 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; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
|
||
zDir=azDirs[i];
|
||
if( zDir==0 ) continue;
|
||
if( stat(zDir, &buf) ) continue;
|
||
if( !S_ISDIR(buf.st_mode) ) continue;
|
||
if( access(zDir, 07) ) continue;
|
||
/* Got one */
|
||
ph7_result_string(pCtx,zDir,-1);
|
||
return;
|
||
}
|
||
/* Default temp dir */
|
||
ph7_result_string(pCtx,"/tmp",(int)sizeof("/tmp")-1);
|
||
}
|
||
/* unsigned int (*xProcessId)(void) */
|
||
static unsigned int UnixVfs_ProcessId(void)
|
||
{
|
||
return (unsigned int)getpid();
|
||
}
|
||
/* int (*xUid)(void) */
|
||
static int UnixVfs_uid(void)
|
||
{
|
||
return (int)getuid();
|
||
}
|
||
/* int (*xGid)(void) */
|
||
static int UnixVfs_gid(void)
|
||
{
|
||
return (int)getgid();
|
||
}
|
||
/* int (*xUmask)(int) */
|
||
static int UnixVfs_Umask(int new_mask)
|
||
{
|
||
int old_mask;
|
||
old_mask = umask(new_mask);
|
||
return old_mask;
|
||
}
|
||
/* void (*xUsername)(ph7_context *) */
|
||
static void UnixVfs_Username(ph7_context *pCtx)
|
||
{
|
||
#ifndef PH7_UNIX_STATIC_BUILD
|
||
struct passwd *pwd;
|
||
uid_t uid;
|
||
uid = getuid();
|
||
pwd = getpwuid(uid); /* Try getting UID for username */
|
||
if (pwd == 0) {
|
||
return;
|
||
}
|
||
/* Return the username */
|
||
ph7_result_string(pCtx,pwd->pw_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 <chm@symisc.net> $ */
|
||
#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:
|
||
* <?php
|
||
* function foo ()
|
||
* {
|
||
* return 5;
|
||
* }
|
||
* ?>
|
||
* 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:
|
||
* <?php
|
||
* $first ? $second : $third
|
||
* ?>
|
||
* 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 <chm@symisc.net> $ */
|
||
#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
|
||
* <?php
|
||
* class foo
|
||
* {
|
||
* public function printItem($string)
|
||
* {
|
||
* echo 'Foo: ' . $string . PHP_EOL;
|
||
* }
|
||
*
|
||
* public function printPHP()
|
||
* {
|
||
* echo 'PHP is great.' . PHP_EOL;
|
||
* }
|
||
* }
|
||
* class bar extends foo
|
||
* {
|
||
* public function printItem($string)
|
||
* {
|
||
* echo 'Bar: ' . $string . PHP_EOL;
|
||
* }
|
||
* }
|
||
* $foo = new foo();
|
||
* $bar = new bar();
|
||
* $foo->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
|
||
* <?php
|
||
* $instance = new SimpleClass();
|
||
* // This can also be done with a variable:
|
||
* $className = 'Foo';
|
||
* $instance = new $className(); // Foo()
|
||
* ?>
|
||
* 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
|
||
* <?php
|
||
* class SimpleClass(){
|
||
* public $var;
|
||
* };
|
||
* $instance = new SimpleClass();
|
||
* $assigned = $instance;
|
||
* $reference =& $instance;
|
||
* $instance->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
|
||
* <?php
|
||
* class Test
|
||
* {
|
||
* static public function getNew()
|
||
* {
|
||
* return new static;
|
||
* }
|
||
* }
|
||
* class Child extends Test
|
||
* {}
|
||
* $obj1 = new Test();
|
||
* $obj2 = new $obj1;
|
||
* var_dump($obj1 !== $obj2);
|
||
* $obj3 = Test::getNew();
|
||
* var_dump($obj3 instanceof Test);
|
||
* $obj4 = Child::getNew();
|
||
* var_dump($obj4 instanceof Child);
|
||
* ?>
|
||
* 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
|
||
* <?php
|
||
* class SubObject
|
||
* {
|
||
* static $instances = 0;
|
||
* public $instance;
|
||
*
|
||
* public function __construct() {
|
||
* $this->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
|
||
* <?php
|
||
* function bool2str($bool)
|
||
* {
|
||
* if ($bool === false) {
|
||
* return 'FALSE';
|
||
* } else {
|
||
* return 'TRUE';
|
||
* }
|
||
* }
|
||
* function compareObjects(&$o1, &$o2)
|
||
* {
|
||
* echo 'o1 == o2 : ' . bool2str($o1 == $o2) . "\n";
|
||
* echo 'o1 != o2 : ' . bool2str($o1 != $o2) . "\n";
|
||
* echo 'o1 === o2 : ' . bool2str($o1 === $o2) . "\n";
|
||
* echo 'o1 !== o2 : ' . bool2str($o1 !== $o2) . "\n";
|
||
* }
|
||
* class Flag
|
||
* {
|
||
* public $flag;
|
||
*
|
||
* function Flag($flag = true) {
|
||
* $this->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
|
||
* <?php
|
||
* // Declare a simple class
|
||
* class TestClass
|
||
* {
|
||
* public $foo;
|
||
*
|
||
* public function __construct($foo)
|
||
* {
|
||
* $this->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 <chm@symisc.net> $ */
|
||
#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.iVal<LARGEST_INT64 ){
|
||
pObj->iFlags |= 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 <chm@symisc.net> $ */
|
||
/*
|
||
* 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 <Windows.h>
|
||
#else
|
||
#include <stdlib.h>
|
||
#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 <pthread.h>
|
||
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<nLen ; n += 4){
|
||
w = aBase64Trans[zB64[n] & 0x7F];
|
||
x = aBase64Trans[zB64[n+1] & 0x7F];
|
||
y = aBase64Trans[zB64[n+2] & 0x7F];
|
||
z = aBase64Trans[zB64[n+3] & 0x7F];
|
||
zOut[0] = ((w<<2) & 0xFC) | ((x>>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 && precision<width-(prefix!=0) ){
|
||
precision = width-(prefix!=0);
|
||
}
|
||
bufpt = &buf[SXFMT_BUFSIZ-1];
|
||
{
|
||
register char *cset; /* Use registers for speed */
|
||
register int base;
|
||
cset = infop->charset;
|
||
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+precision<SXFMT_BUFSIZ-30 ){
|
||
flag_dp = (precision>0 || 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<precision; idx++) buf[idx] = (char)c;
|
||
length = precision;
|
||
}else{
|
||
length =1;
|
||
}
|
||
bufpt = buf;
|
||
break;
|
||
case SXFMT_STRING:
|
||
bufpt = va_arg(ap,char*);
|
||
if( bufpt==0 ){
|
||
bufpt = " ";
|
||
length = (int)sizeof(" ")-1;
|
||
break;
|
||
}
|
||
length = precision;
|
||
if( precision < 0 ){
|
||
/* Symisc extension */
|
||
length = (int)SyStrlen(bufpt);
|
||
}
|
||
if( precision>=0 && precision<length ) length = precision;
|
||
break;
|
||
case SXFMT_RAWSTR:{
|
||
/* Symisc extension */
|
||
SyString *pStr = va_arg(ap,SyString *);
|
||
if( pStr == 0 || pStr->zString == 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 <chm@symisc.net>
|
||
* @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 <http://www.sqlite.org/>
|
||
* @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 <sys/types.h>
|
||
#include <sys/stat.h>
|
||
#include <fcntl.h>
|
||
#include <unistd.h>
|
||
#include <errno.h>
|
||
#include <time.h>
|
||
#include <sys/time.h>
|
||
#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<<s | 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 <steve@edmweb.com>
|
||
* 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 <chm@symisc.net> $ */
|
||
#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 <chm@symisc.net> 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 <![CDATA[ ]]> 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.
|
||
* <?php echo 'While this is going to be parsed.'; ?>
|
||
* <p>This will also be ignored.</p>
|
||
* You can also use more advanced structures:
|
||
* Example #1 Advanced escaping
|
||
* <?php
|
||
* if ($expression) {
|
||
* ?>
|
||
* <strong>This is true.</strong>
|
||
* <?php
|
||
* } else {
|
||
* ?>
|
||
* <strong>This is false.</strong>
|
||
* <?php
|
||
* }
|
||
* ?>
|
||
* 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, <?php ?>
|
||
* <script language="php"> </script> 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 <?php ?> tags to remain
|
||
* compliant with standards.
|
||
* Example #2 PHP Opening and Closing Tags
|
||
* 1. <?php echo 'if you want to serve XHTML or XML documents, do it like this'; ?>
|
||
* 2. <script language="php">
|
||
* echo 'some editors (like FrontPage) don\'t
|
||
* like processing instructions';
|
||
* </script>
|
||
*
|
||
* 3. <? echo 'this is the simplest, an SGML processing instruction'; ?>
|
||
* <?= expression ?> This is a shortcut for "<? echo expression ?>"
|
||
*/
|
||
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: <?php */
|
||
zIn += sizeof("php")-1;
|
||
}
|
||
/* Look for the closing 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 <chm@symisc.net> $ */
|
||
#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.
|
||
* <?php
|
||
* $a = array("a" => "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:
|
||
* <?php
|
||
* $a = array("a" => "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; i<N_SORT_BUCKET-1; i++){
|
||
if( a[i]==0 ){
|
||
a[i] = p;
|
||
break;
|
||
}else{
|
||
p = HashmapNodeMerge(a[i],p,xCmp,pCmpData);
|
||
a[i] = 0;
|
||
}
|
||
}
|
||
if( i==N_SORT_BUCKET-1 ){
|
||
/* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
|
||
* But that is impossible.
|
||
*/
|
||
a[i] = HashmapNodeMerge(a[i], p,xCmp,pCmpData);
|
||
}
|
||
}
|
||
p = a[0];
|
||
for(i=1; i<N_SORT_BUCKET; i++){
|
||
p = HashmapNodeMerge(p,a[i],xCmp,pCmpData);
|
||
}
|
||
p->pNext = 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 <chm@symisc.net> $ */
|
||
#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 <Windows.h>
|
||
#elif defined(__UNIXES__)
|
||
#include <sys/utsname.h>
|
||
#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 <time.h>
|
||
#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 <chm@symisc.net> $ */
|
||
#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 <![CDATA[ ]]>
|
||
* 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
|
||
* <?php
|
||
* $greet = function($name)
|
||
* {
|
||
* printf("Hello %s\r\n", $name);
|
||
* };
|
||
* $greet('World');
|
||
* $greet('PHP');
|
||
* ?>
|
||
* 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:
|
||
* <?php
|
||
* $i = 0;
|
||
* do {
|
||
* echo $i;
|
||
* } while ($i > 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:
|
||
* <?php
|
||
* if ($a > $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
|
||
* <?php
|
||
* $a = 1;
|
||
* $b = 2;
|
||
* function Sum()
|
||
* {
|
||
* global $a, $b;
|
||
* $b = $a + $b;
|
||
* }
|
||
* Sum();
|
||
* echo $b;
|
||
* ?>
|
||
* 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).
|
||
* <?php
|
||
* // these are the same:
|
||
* // you can use this:
|
||
* declare(ticks=1) {
|
||
* // entire script here
|
||
* }
|
||
* // or you can use this:
|
||
* declare(ticks=1);
|
||
* // entire script here
|
||
* ?>
|
||
*
|
||
* 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
|
||
* <?php
|
||
* function takes_array($input)
|
||
* {
|
||
* echo "$input[0] + $input[1] = ", $input[0]+$input[1];
|
||
* }
|
||
* ?>
|
||
* 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
|
||
* <?php
|
||
* function add_some_extra(&$string)
|
||
* {
|
||
* $string .= 'and something extra.';
|
||
* }
|
||
* $str = 'This is a string, ';
|
||
* add_some_extra($str);
|
||
* echo $str; // outputs 'This is a string, and something extra.'
|
||
* ?>
|
||
*
|
||
* 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 <pEnd ){
|
||
pEnd = pEnd2;
|
||
}
|
||
}
|
||
if( pEnd > 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:
|
||
* <?= 4+5?> is the same as <?echo 4+5?>
|
||
* 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 <chm@symisc.net> $ */
|
||
#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 <stdlib.h> /* abs */
|
||
#include <math.h>
|
||
/*
|
||
* 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<LARGEST_INT64-1 ){
|
||
r = (double)((ph7_int64)(r+0.5));
|
||
}else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
|
||
r = -(double)((ph7_int64)((-r)+0.5));
|
||
}else{
|
||
char zBuf[256];
|
||
sxu32 nLen;
|
||
nLen = SyBufferFormat(zBuf,sizeof(zBuf),"%.*f",n,r);
|
||
/* Convert the string to real number */
|
||
SyStrToReal(zBuf,nLen,(void *)&r,0);
|
||
}
|
||
/* Return thr rounded value */
|
||
ph7_result_double(pCtx,r);
|
||
return PH7_OK;
|
||
}
|
||
/*
|
||
* string dechex(int $number)
|
||
* Decimal to hexadecimal.
|
||
* Parameters
|
||
* $number
|
||
* Decimal value to convert
|
||
* Return
|
||
* Hexadecimal string representation of number
|
||
*/
|
||
static int PH7_builtin_dechex(ph7_context *pCtx,int nArg,ph7_value **apArg)
|
||
{
|
||
int iVal;
|
||
if( nArg < 1 ){
|
||
/* Missing arguments,return null */
|
||
ph7_result_null(pCtx);
|
||
return PH7_OK;
|
||
}
|
||
/* Extract the given number */
|
||
iVal = ph7_value_to_int(apArg[0]);
|
||
/* Format */
|
||
ph7_result_string_format(pCtx,"%x",iVal);
|
||
return PH7_OK;
|
||
}
|
||
/*
|
||
* string decoct(int $number)
|
||
* Decimal to Octal.
|
||
* Parameters
|
||
* $number
|
||
* Decimal value to convert
|
||
* Return
|
||
* Octal string representation of number
|
||
*/
|
||
static int PH7_builtin_decoct(ph7_context *pCtx,int nArg,ph7_value **apArg)
|
||
{
|
||
int iVal;
|
||
if( nArg < 1 ){
|
||
/* Missing arguments,return null */
|
||
ph7_result_null(pCtx);
|
||
return PH7_OK;
|
||
}
|
||
/* Extract the given number */
|
||
iVal = ph7_value_to_int(apArg[0]);
|
||
/* Format */
|
||
ph7_result_string_format(pCtx,"%o",iVal);
|
||
return PH7_OK;
|
||
}
|
||
/*
|
||
* string decbin(int $number)
|
||
* Decimal to binary.
|
||
* Parameters
|
||
* $number
|
||
* Decimal value to convert
|
||
* Return
|
||
* Binary string representation of number
|
||
*/
|
||
static int PH7_builtin_decbin(ph7_context *pCtx,int nArg,ph7_value **apArg)
|
||
{
|
||
int iVal;
|
||
if( nArg < 1 ){
|
||
/* Missing arguments,return null */
|
||
ph7_result_null(pCtx);
|
||
return PH7_OK;
|
||
}
|
||
/* Extract the given number */
|
||
iVal = ph7_value_to_int(apArg[0]);
|
||
/* Format */
|
||
ph7_result_string_format(pCtx,"%B",iVal);
|
||
return PH7_OK;
|
||
}
|
||
/*
|
||
* int64 hexdec(string $hex_string)
|
||
* Hexadecimal to decimal.
|
||
* Parameters
|
||
* $hex_string
|
||
* The hexadecimal string to convert
|
||
* Return
|
||
* The decimal representation of hex_string
|
||
*/
|
||
static int PH7_builtin_hexdec(ph7_context *pCtx,int nArg,ph7_value **apArg)
|
||
{
|
||
const char *zString,*zEnd;
|
||
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);
|
||
/* Delimit the string */
|
||
zEnd = &zString[nLen];
|
||
/* Ignore non hex-stream */
|
||
while( zString < zEnd ){
|
||
if( (unsigned char)zString[0] >= 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,"<br>",(int)sizeof("<br>")-1);
|
||
}else{
|
||
ph7_result_string(pCtx,"<br/>",(int)sizeof("<br/>")-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 && precision<length ){
|
||
length = precision;
|
||
}
|
||
if( flag_zeropad ){
|
||
/* zero-padding works on strings too */
|
||
for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
|
||
spaces[idx] = '0';
|
||
}
|
||
}
|
||
break;
|
||
case PH7_FMT_RADIX:
|
||
pArg = NEXT_ARG;
|
||
if( pArg == 0 ){
|
||
iVal = 0;
|
||
}else{
|
||
iVal = ph7_value_to_int64(pArg);
|
||
}
|
||
/* Limit the precision to prevent overflowing buf[] during conversion */
|
||
if( precision>PH7_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 && precision<width-(prefix!=0) ){
|
||
precision = width-(prefix!=0);
|
||
}
|
||
zBuf = &zWorker[PH7_FMT_BUFSIZ-1];
|
||
{
|
||
register char *cset; /* Use registers for speed */
|
||
register int base;
|
||
cset = pInfo->charset;
|
||
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+precision<PH7_FMT_BUFSIZ-30 ){
|
||
flag_dp = (precision>0 || 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 <time.h>
|
||
#ifdef __WINNT__
|
||
/* GetSystemTime() */
|
||
#include <Windows.h>
|
||
#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 <sys/time.h>
|
||
#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 <chm@symisc.net> $ */
|
||
#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.
|
||
*/
|