/ethica130820_maquette/src/jx9.c
C | 14971 lines | 10653 code | 27 blank | 4291 comment | 1726 complexity | dfa6379633ff9bb33331493c0d91a612 MD5 | raw file
- /*
- * Symisc Jx9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
- * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
- * Version 1.7.2
- * 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://jx9.symisc.net/
- */
- /*
- * Copyright (C) 2012, 2013 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 JX9 engine and any
- * accompanying software that uses the JX9 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: jx9.c v1.7 UNIX|Win32/64 2013-01-04 09:00 stable <chm@symisc.net> $
- */
- /* This file is an amalgamation of many separate C source files from JX9 version 1.7.2
- * 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 JX9 is compiled as a single translation unit.
- *
- * This file is all you need to compile JX9. To use JX9 in other programs, you need
- * this file and the "jx9.h" header file that defines the programming interface to the
- * JX9 engine.(If you do not have the "jx9.h" header file at hand, you will find
- * a copy embedded within the text of this file.Search for "Header file: <jx9.h>" to find
- * the start of the embedded jx9.h header file.) Additional code files may be needed if
- * you want a wrapper to interface JX9 with your choice of programming language.
- * To get the official documentation, please visit http://jx9.symisc.net/
- */
- /*
- * Make the sure the following directive is defined in the amalgamation build.
- */
- #ifndef JX9_AMALGAMATION
- #define JX9_AMALGAMATION
- #endif /* JX9_AMALGAMATION */
- /*
- * Embedded header file for Jx9: <jx9.h>
- */
- /*
- * ----------------------------------------------------------
- * File: jx9.h
- * MD5: 2b1283746396e4a0a504caa586f398f5
- * ----------------------------------------------------------
- */
- /* This file was automatically generated. Do not edit (except for compile time directive)! */
- #ifndef _JX9H_
- #define _JX9H_
- /*
- * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
- * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
- * Version 1.7.2
- * 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://jx9.symisc.net/
- */
- /*
- * Copyright (C) 2012, 2013 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 JX9 engine and any
- * accompanying software that uses the JX9 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: jx9.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 [jx9_lib_version()], [jx9_lib_signature()], [jx9_lib_ident()] and
- * [jx9_lib_copyright()] for more information.
- */
- /*
- * The JX9_VERSION C preprocessor macroevaluates to a string literal
- * that is the jx9 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 JX9_VERSION "1.7.2"
- /*
- * The JX9_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 [JX9_VERSION].
- */
- #define JX9_VERSION_NUMBER 1007002
- /*
- * The JX9_SIG C preprocessor macro evaluates to a string
- * literal which is the public signature of the jx9 engine.
- * This signature could be included for example in a host-application
- * generated Server MIME header as follows:
- * Server: YourWebServer/x.x Jx9/x.x.x \r\n
- */
- #define JX9_SIG "Jx9/1.7.2"
- /*
- * JX9 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 jx9.
- */
- #define JX9_IDENT "jx9:d217a6e8c7f10fb35a8becb2793101fd2036aeb7"
- /*
- * Copyright notice.
- * If you have any questions about the licensing situation, please
- * visit http://jx9.symisc.net/licensing.html
- * or contact Symisc Systems via:
- * legal@symisc.net
- * licensing@symisc.net
- * contact@symisc.net
- */
- #define JX9_COPYRIGHT "Copyright (C) Symisc Systems 2012-2013, http://jx9.symisc.net/"
- /* Make sure we can call this stuff from C++ */
- #ifdef __cplusplus
- extern "C" {
- #endif
- /* Forward declaration to public objects */
- typedef struct jx9_io_stream jx9_io_stream;
- typedef struct jx9_context jx9_context;
- typedef struct jx9_value jx9_value;
- typedef struct jx9_vfs jx9_vfs;
- typedef struct jx9_vm jx9_vm;
- typedef struct jx9 jx9;
- /*
- * ------------------------------
- * Compile time directives
- * ------------------------------
- * For most purposes, JX9 can be built just fine using the default compilation options.
- * However, if required, the compile-time options documented below can be used to omit
- * JX9 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.
- *
- * JX9_ENABLE_THREADS
- * This option controls whether or not code is included in JX9 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 JX9 in a multithreaded program. When compiled
- * with the JX9_ENABLE_THREADS directive enabled, JX9 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 JX9_ENABLE_THREADS can be determined at run-time using the
- * jx9_lib_is_threadsafe() interface.When JX9 has been compiled with JX9_ENABLE_THREAD
- * then the threading mode can be altered at run-time using the jx9_lib_config()
- * interface together with one of these verbs:
- * JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE
- * JX9_LIB_CONFIG_THREAD_LEVEL_MULTI
- * Also note, platforms others than Windows and UNIX systems must install their own
- * mutex subsystem via jx9_lib_config() with a configuration verb set to
- * JX9_LIB_CONFIG_USER_MUTEX. Otherwise the library is not threadsafe.
- * Note that you must link JX9 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 JX9 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.
- * JX9_DISABLE_BUILTIN_FUNC
- * JX9 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.
- * JX9_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
- * JX9 with the math library in same linux/BSD flavor (i.e: -lm).Otherwise you
- * will get a link time error.
- * JX9_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.
- * JX9_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.
- * JX9_OMIT_FLOATING_POINT
- * This option is used to omit floating-point number support from the JX9 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 JX9 return values */
- #define JX9_OK SXRET_OK /* Successful result */
- /* beginning-of-error-codes */
- #define JX9_NOMEM SXERR_MEM /* Out of memory */
- #define JX9_ABORT SXERR_ABORT /* Foreign Function request operation abort/Another thread have released this instance */
- #define JX9_IO_ERR SXERR_IO /* IO error */
- #define JX9_CORRUPT SXERR_CORRUPT /* Corrupt pointer/Unknown configuration option */
- #define JX9_LOOKED SXERR_LOCKED /* Forbidden Operation */
- #define JX9_COMPILE_ERR (-70) /* Compilation error */
- #define JX9_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 JX9_OMIT_FLOATING_POINT
- typedef sxi64 jx9_real;
- #else
- typedef double jx9_real;
- #endif
- typedef sxi64 jx9_int64;
- #define JX9_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 JX9 engine.
- * These constants must be passed as the second argument to the [jx9_config()]
- * interface.
- * Each options require a variable number of arguments.
- * The [jx9_config()] interface will return JX9_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://jx9.symisc.net/c_api_func.html#jx9_config
- */
- #define JX9_CONFIG_ERR_ABORT 1 /* RESERVED FOR FUTURE USE */
- #define JX9_CONFIG_ERR_LOG 2 /* 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 JX9 Virtual machine.
- * These constants must be passed as the second argument to the [jx9_vm_config()]
- * interface.
- * Each options require a variable number of arguments.
- * The [jx9_vm_config()] interface will return JX9_OK on success, any other return
- * value indicates failure.
- * There are many options but the most importants are: JX9_VM_CONFIG_OUTPUT which install
- * a VM output consumer callback, JX9_VM_CONFIG_HTTP_REQUEST which parse and register
- * a HTTP request and JX9_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://jx9.symisc.net/c_api_func.html#jx9_vm_config
- */
- #define JX9_VM_CONFIG_OUTPUT 1 /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
- #define JX9_VM_CONFIG_IMPORT_PATH 2 /* ONE ARGUMENT: const char *zIncludePath */
- #define JX9_VM_CONFIG_ERR_REPORT 3 /* NO ARGUMENTS: Report all run-time errors in the VM output */
- #define JX9_VM_CONFIG_RECURSION_DEPTH 4 /* ONE ARGUMENT: int nMaxDepth */
- #define JX9_VM_OUTPUT_LENGTH 5 /* ONE ARGUMENT: unsigned int *pLength */
- #define JX9_VM_CONFIG_CREATE_VAR 6 /* TWO ARGUMENTS: const char *zName, jx9_value *pValue */
- #define JX9_VM_CONFIG_HTTP_REQUEST 7 /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
- #define JX9_VM_CONFIG_SERVER_ATTR 8 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
- #define JX9_VM_CONFIG_ENV_ATTR 9 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
- #define JX9_VM_CONFIG_EXEC_VALUE 10 /* ONE ARGUMENT: jx9_value **ppValue */
- #define JX9_VM_CONFIG_IO_STREAM 11 /* ONE ARGUMENT: const jx9_io_stream *pStream */
- #define JX9_VM_CONFIG_ARGV_ENTRY 12 /* ONE ARGUMENT: const char *zValue */
- #define JX9_VM_CONFIG_EXTRACT_OUTPUT 13 /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
- /*
- * 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 [jx9_lib_config()]
- * interface.
- * Each options require a variable number of arguments.
- * The [jx9_lib_config()] interface will return JX9_OK on success, any other return
- * value indicates failure.
- * Notes:
- * The default configuration is recommended for most applications and so the call to
- * [jx9_lib_config()] is usually not necessary. It is provided to support rare
- * applications with unusual needs.
- * The [jx9_lib_config()] interface is not threadsafe. The application must insure that
- * no other [jx9_*()] interfaces are invoked by other threads while [jx9_lib_config()]
- * is running. Furthermore, [jx9_lib_config()] may only be invoked prior to library
- * initialization using [jx9_lib_init()] or [jx9_init()] or after shutdown
- * by [jx9_lib_shutdown()]. If [jx9_lib_config()] is called after [jx9_lib_init()]
- * or [jx9_init()] and before [jx9_lib_shutdown()] then it will return jx9LOCKED.
- * For a full discussion on the configuration verbs and their expected parameters, please
- * refer to this page:
- * http://jx9.symisc.net/c_api_func.html#Global_Library_Management_Interfaces
- */
- #define JX9_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */
- #define JX9_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
- #define JX9_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */
- #define JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */
- #define JX9_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */
- #define JX9_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const jx9_vfs *pVfs */
- /*
- * Call Context Error Message Serverity Level.
- *
- * The following constans are the allowed severity level that can
- * passed as the second argument to the [jx9_context_throw_error()] or
- * [jx9_context_throw_error_format()] interfaces.
- * Refer to the official documentation for additional information.
- */
- #define JX9_CTX_ERR 1 /* Call context error such as unexpected number of arguments, invalid types and so on. */
- #define JX9_CTX_WARNING 2 /* Call context Warning */
- #define JX9_CTX_NOTICE 3 /* Call context Notice */
- /* Current VFS structure version*/
- #define JX9_VFS_VERSION 2
- /*
- * JX9 Virtual File System (VFS).
- *
- * An instance of the jx9_vfs object defines the interface between the JX9 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 JX9 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 JX9.
- * Additional fields may be appended to this object when the iVersion value is increased.
- * Only a single vfs can be registered within the JX9 core. Vfs registration is done
- * using the jx9_lib_config() interface with a configuration verb set to JX9_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 JX9 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 JX9 system functions.
- * Also note that the jx9_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 their own vfs an contact symisc systems to obtain
- * the JX9 VFS C/C++ Specification manual.
- */
- struct jx9_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)(jx9_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 *, jx9_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 */
- jx9_int64 (*xFreeSpace)(const char *); /* Available space on filesystem or disk partition */
- jx9_int64 (*xTotalSpace)(const char *); /* Total space on filesystem or disk partition */
- jx9_int64 (*xFileSize)(const char *); /* Gets file size */
- jx9_int64 (*xFileAtime)(const char *); /* Gets last access time of file */
- jx9_int64 (*xFileMtime)(const char *); /* Gets file modification time */
- jx9_int64 (*xFileCtime)(const char *); /* Gets inode change time of file */
- int (*xStat)(const char *, jx9_value *, jx9_value *); /* Gives information about a file */
- int (*xlStat)(const char *, jx9_value *, jx9_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 *, jx9_context *); /* Gets file type [i.e: fifo, dir, file..] */
- int (*xGetenv)(const char *, jx9_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 *, jx9_int64, jx9_int64); /* Sets access and modification time of file */
- int (*xMmap)(const char *, void **, jx9_int64 *); /* Read-only memory map of the whole file */
- void (*xUnmap)(void *, jx9_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)(jx9_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)(jx9_context *); /* Running username */
- int (*xExec)(const char *, jx9_context *); /* Execute an external program */
- };
- /* Current JX9 IO stream structure version. */
- #define JX9_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 JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html)
- * for additional information.
- */
- #define JX9_IO_OPEN_RDONLY 0x001 /* Read-only open */
- #define JX9_IO_OPEN_WRONLY 0x002 /* Write-only open */
- #define JX9_IO_OPEN_RDWR 0x004 /* Read-write open. */
- #define JX9_IO_OPEN_CREATE 0x008 /* If the file does not exist it will be created */
- #define JX9_IO_OPEN_TRUNC 0x010 /* Truncate the file to zero length */
- #define JX9_IO_OPEN_APPEND 0x020 /* Append mode.The file offset is positioned at the end of the file */
- #define JX9_IO_OPEN_EXCL 0x040 /* Ensure that this call creates the file, the file must not exist before */
- #define JX9_IO_OPEN_BINARY 0x080 /* Simple hint: Data is binary */
- #define JX9_IO_OPEN_TEMP 0x100 /* Simple hint: Temporary file */
- #define JX9_IO_OPEN_TEXT 0x200 /* Simple hint: Data is textual */
- /*
- * JX9 IO Stream Device.
- *
- * An instance of the jx9_io_stream object defines the interface between the JX9 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.
- * JX9 come with two built-in IO streams device:
- * The file:// stream which perform very efficient disk IO and the jx9:// stream
- * which is a special stream that allow access various I/O streams (See the JX9 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 jx9. 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 jx9_vm_config() with a configuration verb
- * set to JX9_VM_CONFIG_IO_STREAM.
- * Currently the JX9 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 JX9 engine.
- * Developers wishing to implement their own IO stream devices must understand and follow
- * The JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html).
- */
- struct jx9_io_stream
- {
- const char *zName; /* Underlying stream name [i.e: file/http/zip/jx9, ..] */
- int iVersion; /* IO stream structure version [default 1]*/
- int (*xOpen)(const char *, int, jx9_value *, void **); /* Open handle*/
- int (*xOpenDir)(const char *, jx9_value *, void **); /* Open directory handle */
- void (*xClose)(void *); /* Close file handle */
- void (*xCloseDir)(void *); /* Close directory handle */
- jx9_int64 (*xRead)(void *, void *, jx9_int64); /* Read from the open stream */
- int (*xReadDir)(void *, jx9_context *); /* Read entry from directory handle */
- jx9_int64 (*xWrite)(void *, const void *, jx9_int64); /* Write to the open stream */
- int (*xSeek)(void *, jx9_int64, int); /* Seek on the open stream */
- int (*xLock)(void *, int); /* Lock/Unlock the open stream */
- void (*xRewindDir)(void *); /* Rewind directory handle */
- jx9_int64 (*xTell)(void *); /* Current position of the stream read/write pointer */
- int (*xTrunc)(void *, jx9_int64); /* Truncates the open stream to a given length */
- int (*xSync)(void *); /* Flush open stream data */
- int (*xStat)(void *, jx9_value *, jx9_value *); /* Stat an open stream handle */
- };
- /*
- * C-API-REF: Please refer to the official documentation for interfaces
- * purpose and expected parameters.
- */
- /* Engine Handling Interfaces */
- JX9_APIEXPORT int jx9_init(jx9 **ppEngine);
- JX9_APIEXPORT int jx9_config(jx9 *pEngine, int nConfigOp, ...);
- JX9_APIEXPORT int jx9_release(jx9 *pEngine);
- /* Compile Interfaces */
- JX9_APIEXPORT int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm);
- JX9_APIEXPORT int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm);
- /* Virtual Machine Handling Interfaces */
- JX9_APIEXPORT int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...);
- JX9_APIEXPORT int jx9_vm_exec(jx9_vm *pVm, int *pExitStatus);
- JX9_APIEXPORT jx9_value * jx9_vm_extract_variable(jx9_vm *pVm,const char *zVarname);
- JX9_APIEXPORT int jx9_vm_reset(jx9_vm *pVm);
- JX9_APIEXPORT int jx9_vm_release(jx9_vm *pVm);
- JX9_APIEXPORT int jx9_vm_dump_v2(jx9_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);
- /* In-process Extending Interfaces */
- JX9_APIEXPORT int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData);
- JX9_APIEXPORT int jx9_delete_function(jx9_vm *pVm, const char *zName);
- JX9_APIEXPORT int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData);
- JX9_APIEXPORT int jx9_delete_constant(jx9_vm *pVm, const char *zName);
- /* Foreign Function Parameter Values */
- JX9_APIEXPORT int jx9_value_to_int(jx9_value *pValue);
- JX9_APIEXPORT int jx9_value_to_bool(jx9_value *pValue);
- JX9_APIEXPORT jx9_int64 jx9_value_to_int64(jx9_value *pValue);
- JX9_APIEXPORT double jx9_value_to_double(jx9_value *pValue);
- JX9_APIEXPORT const char * jx9_value_to_string(jx9_value *pValue, int *pLen);
- JX9_APIEXPORT void * jx9_value_to_resource(jx9_value *pValue);
- JX9_APIEXPORT int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict);
- /* Setting The Result Of A Foreign Function */
- JX9_APIEXPORT int jx9_result_int(jx9_context *pCtx, int iValue);
- JX9_APIEXPORT int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue);
- JX9_APIEXPORT int jx9_result_bool(jx9_context *pCtx, int iBool);
- JX9_APIEXPORT int jx9_result_double(jx9_context *pCtx, double Value);
- JX9_APIEXPORT int jx9_result_null(jx9_context *pCtx);
- JX9_APIEXPORT int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen);
- JX9_APIEXPORT int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...);
- JX9_APIEXPORT int jx9_result_value(jx9_context *pCtx, jx9_value *pValue);
- JX9_APIEXPORT int jx9_result_resource(jx9_context *pCtx, void *pUserData);
- /* Call Context Handling Interfaces */
- JX9_APIEXPORT int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen);
- JX9_APIEXPORT int jx9_context_output_format(jx9_context *pCtx, const char *zFormat, ...);
- JX9_APIEXPORT int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr);
- JX9_APIEXPORT int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...);
- JX9_APIEXPORT unsigned int jx9_context_random_num(jx9_context *pCtx);
- JX9_APIEXPORT int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen);
- JX9_APIEXPORT void * jx9_context_user_data(jx9_context *pCtx);
- JX9_APIEXPORT int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData);
- JX9_APIEXPORT void * jx9_context_peek_aux_data(jx9_context *pCtx);
- JX9_APIEXPORT void * jx9_context_pop_aux_data(jx9_context *pCtx);
- JX9_APIEXPORT unsigned int jx9_context_result_buf_length(jx9_context *pCtx);
- JX9_APIEXPORT const char * jx9_function_name(jx9_context *pCtx);
- /* Call Context Memory Management Interfaces */
- JX9_APIEXPORT void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease);
- JX9_APIEXPORT void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte);
- JX9_APIEXPORT void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk);
- /* On Demand Dynamically Typed Value Object allocation interfaces */
- JX9_APIEXPORT jx9_value * jx9_new_scalar(jx9_vm *pVm);
- JX9_APIEXPORT jx9_value * jx9_new_array(jx9_vm *pVm);
- JX9_APIEXPORT int jx9_release_value(jx9_vm *pVm, jx9_value *pValue);
- JX9_APIEXPORT jx9_value * jx9_context_new_scalar(jx9_context *pCtx);
- JX9_APIEXPORT jx9_value * jx9_context_new_array(jx9_context *pCtx);
- JX9_APIEXPORT void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue);
- /* Dynamically Typed Value Object Management Interfaces */
- JX9_APIEXPORT int jx9_value_int(jx9_value *pVal, int iValue);
- JX9_APIEXPORT int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue);
- JX9_APIEXPORT int jx9_value_bool(jx9_value *pVal, int iBool);
- JX9_APIEXPORT int jx9_value_null(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_double(jx9_value *pVal, double Value);
- JX9_APIEXPORT int jx9_value_string(jx9_value *pVal, const char *zString, int nLen);
- JX9_APIEXPORT int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...);
- JX9_APIEXPORT int jx9_value_reset_string_cursor(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_resource(jx9_value *pVal, void *pUserData);
- JX9_APIEXPORT int jx9_value_release(jx9_value *pVal);
- /* JSON Array/Object Management Interfaces */
- JX9_APIEXPORT jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte);
- JX9_APIEXPORT int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
- JX9_APIEXPORT int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue);
- JX9_APIEXPORT int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue);
- JX9_APIEXPORT unsigned int jx9_array_count(jx9_value *pArray);
- /* Dynamically Typed Value Object Query Interfaces */
- JX9_APIEXPORT int jx9_value_is_int(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_float(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_bool(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_string(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_null(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_numeric(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_callable(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_scalar(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_json_array(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_json_object(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_resource(jx9_value *pVal);
- JX9_APIEXPORT int jx9_value_is_empty(jx9_value *pVal);
- /* Global Library Management Interfaces */
- JX9_APIEXPORT int jx9_lib_init(void);
- JX9_APIEXPORT int jx9_lib_config(int nConfigOp, ...);
- JX9_APIEXPORT int jx9_lib_shutdown(void);
- JX9_APIEXPORT int jx9_lib_is_threadsafe(void);
- JX9_APIEXPORT const char * jx9_lib_version(void);
- JX9_APIEXPORT const char * jx9_lib_signature(void);
- JX9_APIEXPORT const char * jx9_lib_ident(void);
- JX9_APIEXPORT const char * jx9_lib_copyright(void);
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
- #endif /* _JX9H_ */
- /*
- * ----------------------------------------------------------
- * File: jx9Int.h
- * MD5: e8105144ba748be0e69264041eb7def2
- * ----------------------------------------------------------
- */
- /*
- * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
- * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
- * Version 1.7.2
- * 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://jx9.symisc.net/
- */
- /* $SymiscID: jx9Int.h v1.9 FreeBSD 2012-08-13 23:25 devel <chm@symisc.net> $ */
- #ifndef __JX9INT_H__
- #define __JX9INT_H__
- /* Internal interface definitions for JX9. */
- #ifdef JX9_AMALGAMATION
- /* Marker for routines not intended for external use */
- #define JX9_PRIVATE static
- #else
- #define JX9_PRIVATE
- #include "jx9.h"
- #endif
- #ifndef JX9_PI
- /* Value of PI */
- #define JX9_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 jx9_foreach_info jx9_foreach_info;
- typedef struct jx9_foreach_step jx9_foreach_step;
- typedef struct jx9_hashmap_node jx9_hashmap_node;
- typedef struct jx9_hashmap jx9_hashmap;
- /* 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 JX9_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 /* JX9_DISABLE_BUILTIN_FUNC */
- #ifndef JX9_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 /* JX9_DISABLE_HASH_FUNC */
- /* JX9 private declaration */
- /*
- * Memory Objects.
- * Internally, the JX9 virtual machine manipulates nearly all JX9 values
- * [i.e: string, int, float, resource, object, bool, null] as jx9_values structures.
- * Each jx9_values struct may cache multiple representations (string, integer etc.)
- * of the same value.
- */
- struct jx9_value
- {
- union{
- jx9_real rVal; /* Real value */
- sxi64 iVal; /* Integer value */
- void *pOther; /* Other values (Object, Array, Resource, Namespace, etc.) */
- }x;
- sxi32 iFlags; /* Control flags (see below) */
- jx9_vm *pVm; /* VM this instance belong */
- SyBlob sBlob; /* String values */
- sxu32 nIdx; /* Object index in the global pool */
- };
- /* 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 (JSON representation of Array and Objects) */
- #define MEMOBJ_RES 0x100 /* Memory value is a resource [User private data] */
- /* Mask of all known types */
- #define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)
- /* Scalar variables
- * According to the JX9 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)
- /*
- * The following macro clear the current jx9_value type and replace
- * it with the given one.
- */
- #define MemObjSetType(OBJ, TYPE) ((OBJ)->iFlags = ((OBJ)->iFlags&~MEMOBJ_ALL)|TYPE)
- /* jx9_value cast method signature */
- typedef sxi32 (*ProcMemObjCast)(jx9_value *);
- /* Forward reference */
- typedef struct jx9_output_consumer jx9_output_consumer;
- typedef struct jx9_user_func jx9_user_func;
- typedef struct jx9_conf jx9_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 [JX9_VM_CONFIG_OUTPUT] configuration directive.
- * Please refer to the official documentation for more information
- * on how to register an output consumer callback.
- */
- struct jx9_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() */
- };
- /*
- * JX9 engine [i.e: jx9 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 jx9 engine instance.
- */
- struct jx9_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)(jx9_value *, void *);
- /*
- * Each registered constant [i.e: __TIME__, __DATE__, JX9_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 jx9_constant jx9_constant;
- struct jx9_constant
- {
- SyString sName; /* Constant name */
- ProcConstant xExpand; /* Function responsible of expanding constant value */
- void *pUserData; /* Last argument to xExpand() */
- };
- typedef struct jx9_aux_data jx9_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 jx9_aux_data
- {
- void *pAuxData; /* Aux data */
- };
- /* Foreign functions signature */
- typedef int (*ProcHostFunction)(jx9_context *, int, jx9_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 jx9_user_func
- {
- jx9_vm *pVm; /* VM that own this instance */
- SyString sName; /* Foreign function name */
- ProcHostFunction 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 jx9_context
- {
- jx9_user_func *pFunc; /* Function information. */
- jx9_value *pRet; /* Return value is stored here. */
- SySet sVar; /* Container of dynamically allocated jx9_values
- * [i.e: Garbage collection purposes.]
- */
- SySet sChunk; /* Track dynamically allocated chunks [jx9_aux_data instance].
- * [i.e: Garbage collection purposes.]
- */
- jx9_vm *pVm; /* Virtual machine that own this context */
- sxi32 iFlags; /* Call flags */
- };
- /* Hashmap control flags */
- #define HASHMAP_JSON_OBJECT 0x001 /* Hashmap represent JSON Object*/
- /*
- * Each hashmap entry [i.e: array(4, 5, 6)] is recorded in an instance
- * of the following structure.
- */
- struct jx9_hashmap_node
- {
- jx9_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 */
- jx9_hashmap_node *pNext, *pPrev; /* Link to other entries [i.e: linear traversal] */
- jx9_hashmap_node *pNextCollide, *pPrevCollide; /* Collision chain */
- };
- /*
- * Each active hashmap aka array in the JX9 jargon is represented
- * by an instance of the following structure.
- */
- struct jx9_hashmap
- {
- jx9_vm *pVm; /* VM that own this instance */
- jx9_hashmap_node **apBucket; /* Hash bucket */
- jx9_hashmap_node *pFirst; /* First inserted entry */
- jx9_hashmap_node *pLast; /* Last inserted entry */
- jx9_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 */
- sxi32 iFlags; /* Hashmap control flags */
- 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 jx9_foreach_info
- {
- SyString sKey; /* Key name. Empty otherwise*/
- SyString sValue; /* Value name */
- sxi32 iFlags; /* Control flags */
- SySet aStep; /* Stack of steps [i.e: jx9_foreach_step instance] */
- };
- struct jx9_foreach_step
- {
- sxi32 iFlags; /* Control flags (see below) */
- /* Iterate on this map*/
- jx9_hashmap *pMap; /* Hashmap [i.e: array in the JX9 jargon] iteration
- * Ex: foreach(array(1, 2, 3) as $key=>$value){}
- */
-
- };
- /* Foreach step control flags */
- #define JX9_4EACH_STEP_KEY 0x001 /* Make Key available */
- /*
- * Each JX9 engine is identified by an instance of the following structure.
- * Please refer to the official documentation for more information
- * on how to configure your JX9 engine instance.
- */
- struct jx9
- {
- SyMemBackend sAllocator; /* Low level memory allocation subsystem */
- const jx9_vfs *pVfs; /* Underlying Virtual File System */
- jx9_conf xConf; /* Configuration */
- #if defined(JX9_ENABLE_THREADS)
- SyMutex *pMutex; /* Per-engine mutex */
- #endif
- jx9_vm *pVms; /* List of active VM */
- sxi32 iVm; /* Total number of active VM */
- jx9 *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 jx9_expr_node jx9_expr_node;
- typedef struct jx9_expr_op jx9_expr_op;
- typedef struct jx9_gen_state jx9_gen_state;
- typedef struct GenBlock GenBlock;
- typedef sxi32 (*ProcLangConstruct)(jx9_gen_state *);
- typedef sxi32 (*ProcNodeConstruct)(jx9_gen_state *, sxi32);
- /*
- * Each supported operator [i.e: +, -, ==, *, %, >>, >=, new, etc.] is represented
- * by an instance of the following structure.
- * The JX9 parser does not use any external tools and is 100% handcoded.
- * That is, the JX9 parser is thread-safe , full reentrant, produce consistant
- * compile-time errrors and at least 7 times faster than the standard JX9 parser.
- */
- struct jx9_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: JX9_OP_EQ, JX9_OP_LT, JX9_OP_MUL...]*/
- };
- /*
- * Each expression node is parsed out and recorded
- * in an instance of the following structure.
- */
- struct jx9_expr_node
- {
- const jx9_expr_op *pOp; /* Operator ID or NULL if literal, constant, variable, function or object method call */
- jx9_expr_node *pLeft; /* Left expression tree */
- jx9_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]*/
- jx9_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
- {
- jx9_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 jx9_gen_state
- {
- jx9_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() */
- SyToken *pIn; /* Current processed token */
- SyToken *pEnd; /* Last token in the stream */
- sxu32 nErr; /* Total number of compilation error */
- };
- /* Forward references */
- typedef struct jx9_vm_func_static_var jx9_vm_func_static_var;
- typedef struct jx9_vm_func_arg jx9_vm_func_arg;
- typedef struct jx9_vm_func jx9_vm_func;
- typedef struct VmFrame VmFrame;
- /*
- * Each collected function argument is recorded in an instance
- * of the following structure.
- * Note that as an extension, JX9 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, JX9 allow function arguments to have
- * any complex default value associated with them unlike the standard
- * JX9 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 JX9 engine.
- */
- struct jx9_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.] */
- sxi32 iFlags; /* Configuration flags */
- };
- /*
- * Each static variable is parsed out and remembered in an instance
- * of the following structure.
- * Note that as an extension, JX9 allow static variable have
- * any complex default value associated with them unlike the standard
- * JX9 engine.
- * Example:
- * static $rand_str = 'JX9'.rand_str(3); // Concatenate 'JX9' with
- * // a random three characters(English alphabet)
- * dump($rand_str);
- * //You should see something like this
- * string(6 'JX9awt');
- */
- struct jx9_vm_func_static_var
- {
- SyString sName; /* Static variable name */
- SySet aByteCode; /* Compiled initialization expression */
- sxu32 nIdx; /* Object index in the global memory object container */
- };
- /* Function configuration flags */
- #define VM_FUNC_ARG_HAS_DEF 0x001 /* Argument has default value associated with it */
- #define VM_FUNC_ARG_IGNORE 0x002 /* Do not install argument in the current frame */
- /*
- * Each user defined function is parsed out and stored in an instance
- * of the following structure.
- * JX9 introduced some powerfull extensions to the JX9 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 jx9_vm_func
- {
- SySet aArgs; /* Expected arguments (jx9_vm_func_arg instance) */
- SySet aStatic; /* Static variable (jx9_vm_func_static_var instance) */
- SyString sName; /* Function name */
- SySet aByteCode; /* Compiled function body */
- 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 */
- jx9_vm_func *pNextName; /* Next VM function with the same name as this one */
- };
- /* Forward reference */
- typedef struct jx9_builtin_constant jx9_builtin_constant;
- typedef struct jx9_builtin_func jx9_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 jx9_builtin_func
- {
- const char *zName; /* Function name [i.e: strlen(), rand(), array_merge(), etc.]*/
- ProcHostFunction 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 jx9_builtin_constant
- {
- const char *zName; /* Constant name */
- ProcConstant xExpand; /* C routine responsible of expanding constant value*/
- };
- /*
- * A single instruction of the virtual machine has an opcode
- * and as many as three operands.
- * Each VM instruction resulting from compiling a JX9 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) */
- };
- /* Forward reference */
- typedef struct jx9_case_expr jx9_case_expr;
- typedef struct jx9_switch jx9_switch;
- /*
- * Each compiled case block in a swicth statement is compiled
- * and stored in an instance of the following structure.
- */
- struct jx9_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 jx9_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 JX9_ASSERT_DISABLE 0x01 /* Disable assertion */
- #define JX9_ASSERT_WARNING 0x02 /* Issue a warning for each failed assertion */
- #define JX9_ASSERT_BAIL 0x04 /* Terminate execution on failed assertions */
- #define JX9_ASSERT_QUIET_EVAL 0x08 /* Not used */
- #define JX9_ASSERT_CALLBACK 0x10 /* Callback to call on failed assertions */
- /*
- * An instance of the following structure hold the bytecode instructions
- * resulting from compiling a JX9 script.
- * This structure contains the complete state of the virtual machine.
- */
- struct jx9_vm
- {
- SyMemBackend sAllocator; /* Memory backend */
- #if defined(JX9_ENABLE_THREADS)
- SyMutex *pMutex; /* Recursive mutex associated with VM. */
- #endif
- jx9 *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 */
- jx9_value *aOps; /* Operand stack */
- SySet aFreeObj; /* Stack of free memory objects */
- SyHash hConstant; /* Host-application and user defined constants container */
- SyHash hHostFunction; /* Host-application installable functions */
- SyHash hFunction; /* Compiled functions */
- SyHash hSuper; /* Global variable */
- 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 aIOstream; /* Installed IO stream container */
- const jx9_io_stream *pDefStream; /* Default IO stream [i.e: typically this is the 'file://' stream] */
- jx9_value sExec; /* Compiled script return value [Can be extracted via the JX9_VM_CONFIG_EXEC_VALUE directive]*/
- 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 */
- sxu32 nOutputLen; /* Total number of generated output */
- jx9_output_consumer sVmConsumer; /* Registered output consumer callback */
- int iAssertFlags; /* Assertion flags */
- jx9_value sAssertCallback; /* Callback to call on failed assertions */
- sxi32 iExitStatus; /* Script exit status */
- jx9_gen_state sCodeGen; /* Code generator module */
- jx9_vm *pNext, *pPrev; /* List of active VM's */
- sxu32 nMagic; /* Sanity check against misuse */
- };
- /*
- * Allowed value for jx9_vm.nMagic
- */
- #define JX9_VM_INIT 0xEA12CD72 /* VM correctly initialized */
- #define JX9_VM_RUN 0xBA851227 /* VM ready to execute JX9 bytecode */
- #define JX9_VM_EXEC 0xCDFE1DAD /* VM executing JX9 bytecode */
- #define JX9_VM_STALE 0xDEAD2BAD /* Stale VM */
- /*
- * Error codes according to the JX9 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 JX9 is an out-of-memory. All others erros
- * even a call to undefined function will not halt script execution.
- */
- E_WARNING , /* Run-time warnings (non-fatal errors). Execution of the script is not halted. */
- E_PARSE , /* Compile-time parse errors. Parse errors should only be generated by the parser.*/
- E_NOTICE , /* 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.
- */
- };
- /*
- * Each VM instruction resulting from compiling a JX9 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 jx9_vm_op {
- JX9_OP_DONE = 1, /* Done */
- JX9_OP_HALT, /* Halt */
- JX9_OP_LOAD, /* Load memory object */
- JX9_OP_LOADC, /* Load constant */
- JX9_OP_LOAD_IDX, /* Load array entry */
- JX9_OP_LOAD_MAP, /* Load hashmap('array') */
- JX9_OP_NOOP, /* NOOP */
- JX9_OP_JMP, /* Unconditional jump */
- JX9_OP_JZ, /* Jump on zero (FALSE jump) */
- JX9_OP_JNZ, /* Jump on non-zero (TRUE jump) */
- JX9_OP_POP, /* Stack POP */
- JX9_OP_CAT, /* Concatenation */
- JX9_OP_CVT_INT, /* Integer cast */
- JX9_OP_CVT_STR, /* String cast */
- JX9_OP_CVT_REAL, /* Float cast */
- JX9_OP_CALL, /* Function call */
- JX9_OP_UMINUS, /* Unary minus '-'*/
- JX9_OP_UPLUS, /* Unary plus '+'*/
- JX9_OP_BITNOT, /* Bitwise not '~' */
- JX9_OP_LNOT, /* Logical not '!' */
- JX9_OP_MUL, /* Multiplication '*' */
- JX9_OP_DIV, /* Division '/' */
- JX9_OP_MOD, /* Modulus '%' */
- JX9_OP_ADD, /* Add '+' */
- JX9_OP_SUB, /* Sub '-' */
- JX9_OP_SHL, /* Left shift '<<' */
- JX9_OP_SHR, /* Right shift '>>' */
- JX9_OP_LT, /* Less than '<' */
- JX9_OP_LE, /* Less or equal '<=' */
- JX9_OP_GT, /* Greater than '>' */
- JX9_OP_GE, /* Greater or equal '>=' */
- JX9_OP_EQ, /* Equal '==' */
- JX9_OP_NEQ, /* Not equal '!=' */
- JX9_OP_TEQ, /* Type equal '===' */
- JX9_OP_TNE, /* Type not equal '!==' */
- JX9_OP_BAND, /* Bitwise and '&' */
- JX9_OP_BXOR, /* Bitwise xor '^' */
- JX9_OP_BOR, /* Bitwise or '|' */
- JX9_OP_LAND, /* Logical and '&&','and' */
- JX9_OP_LOR, /* Logical or '||','or' */
- JX9_OP_LXOR, /* Logical xor 'xor' */
- JX9_OP_STORE, /* Store Object */
- JX9_OP_STORE_IDX, /* Store indexed object */
- JX9_OP_PULL, /* Stack pull */
- JX9_OP_SWAP, /* Stack swap */
- JX9_OP_YIELD, /* Stack yield */
- JX9_OP_CVT_BOOL, /* Boolean cast */
- JX9_OP_CVT_NUMC, /* Numeric (integer, real or both) type cast */
- JX9_OP_INCR, /* Increment ++ */
- JX9_OP_DECR, /* Decrement -- */
- JX9_OP_ADD_STORE, /* Add and store '+=' */
- JX9_OP_SUB_STORE, /* Sub and store '-=' */
- JX9_OP_MUL_STORE, /* Mul and store '*=' */
- JX9_OP_DIV_STORE, /* Div and store '/=' */
- JX9_OP_MOD_STORE, /* Mod and store '%=' */
- JX9_OP_CAT_STORE, /* Cat and store '.=' */
- JX9_OP_SHL_STORE, /* Shift left and store '>>=' */
- JX9_OP_SHR_STORE, /* Shift right and store '<<=' */
- JX9_OP_BAND_STORE, /* Bitand and store '&=' */
- JX9_OP_BOR_STORE, /* Bitor and store '|=' */
- JX9_OP_BXOR_STORE, /* Bitxor and store '^=' */
- JX9_OP_CONSUME, /* Consume VM output */
- JX9_OP_MEMBER, /* Object member run-time access */
- JX9_OP_UPLINK, /* Run-Time frame link */
- JX9_OP_CVT_NULL, /* NULL cast */
- JX9_OP_CVT_ARRAY, /* Array cast */
- JX9_OP_FOREACH_INIT, /* For each init */
- JX9_OP_FOREACH_STEP, /* For each step */
- JX9_OP_SWITCH /* Switch operation */
- };
- /* -- END-OF INSTRUCTIONS -- */
- /*
- * Expression Operators ID.
- */
- enum jx9_expr_id {
- EXPR_OP_DOT, /* Member access */
- 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_DDOT, /* 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 */
- };
- /*
- * Lexer token codes
- * The following set of constants are the tokens recognized
- * by the lexer when processing JX9 input.
- * Important: Token values MUST BE A POWER OF TWO.
- */
- #define JX9_TK_INTEGER 0x0000001 /* Integer */
- #define JX9_TK_REAL 0x0000002 /* Real number */
- #define JX9_TK_NUM (JX9_TK_INTEGER|JX9_TK_REAL) /* Numeric token, either integer or real */
- #define JX9_TK_KEYWORD 0x0000004 /* Keyword [i.e: while, for, if, foreach...] */
- #define JX9_TK_ID 0x0000008 /* Alphanumeric or UTF-8 stream */
- #define JX9_TK_DOLLAR 0x0000010 /* '$' Dollar sign */
- #define JX9_TK_OP 0x0000020 /* Operator [i.e: +, *, /...] */
- #define JX9_TK_OCB 0x0000040 /* Open curly brace'{' */
- #define JX9_TK_CCB 0x0000080 /* Closing curly brace'}' */
- #define JX9_TK_DOT 0x0000100 /* Dot . */
- #define JX9_TK_LPAREN 0x0000200 /* Left parenthesis '(' */
- #define JX9_TK_RPAREN 0x0000400 /* Right parenthesis ')' */
- #define JX9_TK_OSB 0x0000800 /* Open square bracket '[' */
- #define JX9_TK_CSB 0x0001000 /* Closing square bracket ']' */
- #define JX9_TK_DSTR 0x0002000 /* Double quoted string "$str" */
- #define JX9_TK_SSTR 0x0004000 /* Single quoted string 'str' */
- #define JX9_TK_NOWDOC 0x0010000 /* Nowdoc <<< */
- #define JX9_TK_COMMA 0x0020000 /* Comma ',' */
- #define JX9_TK_SEMI 0x0040000 /* Semi-colon ";" */
- #define JX9_TK_BSTR 0x0080000 /* Backtick quoted string [i.e: Shell command `date`] */
- #define JX9_TK_COLON 0x0100000 /* single Colon ':' */
- #define JX9_TK_AMPER 0x0200000 /* Ampersand '&' */
- #define JX9_TK_EQUAL 0x0400000 /* Equal '=' */
- #define JX9_TK_OTHER 0x1000000 /* Other symbols */
- /*
- * JX9 keyword.
- * These words have special meaning in JX9. 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, object names, function or method names.
- * Using them as variable names is generally OK, but could lead to confusion.
- */
- #define JX9_TKWRD_SWITCH 1 /* switch */
- #define JX9_TKWRD_PRINT 2 /* print */
- #define JX9_TKWRD_ELIF 0x4000000 /* elseif: MUST BE A POWER OF TWO */
- #define JX9_TKWRD_ELSE 0x8000000 /* else: MUST BE A POWER OF TWO */
- #define JX9_TKWRD_IF 3 /* if */
- #define JX9_TKWRD_STATIC 4 /* static */
- #define JX9_TKWRD_CASE 5 /* case */
- #define JX9_TKWRD_FUNCTION 6 /* function */
- #define JX9_TKWRD_CONST 7 /* const */
- /* The number '8' is reserved for JX9_TK_ID */
- #define JX9_TKWRD_WHILE 9 /* while */
- #define JX9_TKWRD_DEFAULT 10 /* default */
- #define JX9_TKWRD_AS 11 /* as */
- #define JX9_TKWRD_CONTINUE 12 /* continue */
- #define JX9_TKWRD_EXIT 13 /* exit */
- #define JX9_TKWRD_DIE 14 /* die */
- #define JX9_TKWRD_IMPORT 15 /* import */
- #define JX9_TKWRD_INCLUDE 16 /* include */
- #define JX9_TKWRD_FOR 17 /* for */
- #define JX9_TKWRD_FOREACH 18 /* foreach */
- #define JX9_TKWRD_RETURN 19 /* return */
- #define JX9_TKWRD_BREAK 20 /* break */
- #define JX9_TKWRD_UPLINK 21 /* uplink */
- #define JX9_TKWRD_BOOL 0x8000 /* bool: MUST BE A POWER OF TWO */
- #define JX9_TKWRD_INT 0x10000 /* int: MUST BE A POWER OF TWO */
- #define JX9_TKWRD_FLOAT 0x20000 /* float: MUST BE A POWER OF TWO */
- #define JX9_TKWRD_STRING 0x40000 /* string: MUST BE A POWER OF TWO */
- /* json.c function prototypes */
- JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut);
- JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte);
- /* memobj.c function prototypes */
- JX9_PRIVATE sxi32 jx9MemObjDump(SyBlob *pOut, jx9_value *pObj);
- JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal);
- JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore);
- JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest);
- JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal);
- JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray);
- #if 0
- /* Not used in the current release of the JX9 engine */
- JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal);
- #endif
- JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal);
- JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal);
- JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen);
- #if 0
- /* Not used in the current release of the JX9 engine */
- JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap);
- #endif
- JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest);
- JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest);
- JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj);
- JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags);
- JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj);
- JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj);
- JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pData);
- /* lex.c function prototypes */
- JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput, sxu32 nLen, SySet *pOut);
- /* vm.c function prototypes */
- JX9_PRIVATE void jx9VmReleaseContextValue(jx9_context *pCtx, jx9_value *pValue);
- JX9_PRIVATE sxi32 jx9VmInitFuncState(jx9_vm *pVm, jx9_vm_func *pFunc, const char *zName, sxu32 nByte,
- sxi32 iFlags, void *pUserData);
- JX9_PRIVATE sxi32 jx9VmInstallUserFunction(jx9_vm *pVm, jx9_vm_func *pFunc, SyString *pName);
- JX9_PRIVATE sxi32 jx9VmRegisterConstant(jx9_vm *pVm, const SyString *pName, ProcConstant xExpand, void *pUserData);
- JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(jx9_vm *pVm, const SyString *pName, ProcHostFunction xFunc, void *pUserData);
- JX9_PRIVATE sxi32 jx9VmBlobConsumer(const void *pSrc, unsigned int nLen, void *pUserData);
- JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIndex);
- JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex);
- JX9_PRIVATE sxi32 jx9VmOutputConsume(jx9_vm *pVm, SyString *pString);
- JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(jx9_vm *pVm, const char *zFormat, va_list ap);
- JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap);
- JX9_PRIVATE sxi32 jx9VmThrowError(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zMessage);
- JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData);
- JX9_PRIVATE sxi32 jx9VmDump(jx9_vm *pVm, ProcConsumer xConsumer, void *pUserData);
- JX9_PRIVATE sxi32 jx9VmInit(jx9_vm *pVm, jx9 *pEngine);
- JX9_PRIVATE sxi32 jx9VmConfigure(jx9_vm *pVm, sxi32 nOp, va_list ap);
- JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm);
- JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar);
- JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm);
- JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm);
- JX9_PRIVATE sxi32 jx9VmMakeReady(jx9_vm *pVm);
- JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm);
- JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm);
- JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm);
- JX9_PRIVATE VmInstr *jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex);
- JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm);
- JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer);
- JX9_PRIVATE sxi32 jx9VmEmitInstr(jx9_vm *pVm, sxi32 iOp, sxi32 iP1, sxu32 iP2, void *p3, sxu32 *pIndex);
- JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm);
- JX9_PRIVATE sxi32 jx9VmCallUserFunction(jx9_vm *pVm, jx9_value *pFunc, int nArg, jx9_value **apArg, jx9_value *pResult);
- JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(jx9_vm *pVm, jx9_value *pFunc, jx9_value *pResult, ...);
- JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm, sxu32 nObjIdx);
- JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen);
- JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue);
- JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew);
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(jx9_vm *pVm, const char **pzDevice, int nByte);
- #endif /* JX9_DISABLE_BUILTIN_FUNC */
- JX9_PRIVATE int jx9Utf8Read(
- 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 */
- JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID);
- JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot);
- JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart, SyToken *pEnd, SyToken **ppNext);
- JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn, SyToken *pEnd, sxu32 nTokStart, sxu32 nTokEnd, SyToken **ppEnd);
- JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast);
- JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet);
- /* compile.c function prototypes */
- JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType);
- JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen, sxi32 iCompileFlag);
- JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag);
- JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag);
- JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen, sxi32 iCompileFlag);
- JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen, sxi32 iCompileFlag);
- JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag);
- JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag);
- JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen, sxi32 iCompileFlag);
- JX9_PRIVATE sxi32 jx9InitCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
- JX9_PRIVATE sxi32 jx9ResetCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
- JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen, sxi32 nErrType, sxu32 nLine, const char *zFormat, ...);
- JX9_PRIVATE sxi32 jx9CompileScript(jx9_vm *pVm, SyString *pScript, sxi32 iFlags);
- /* constant.c function prototypes */
- JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm);
- /* builtin.c function prototypes */
- JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm);
- /* hashmap.c function prototypes */
- JX9_PRIVATE jx9_hashmap * jx9NewHashmap(jx9_vm *pVm, sxu32 (*xIntHash)(sxi64), sxu32 (*xBlobHash)(const void *, sxu32));
- JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm);
- JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS);
- JX9_PRIVATE void jx9HashmapUnref(jx9_hashmap *pMap);
- JX9_PRIVATE sxi32 jx9HashmapLookup(jx9_hashmap *pMap, jx9_value *pKey, jx9_hashmap_node **ppNode);
- JX9_PRIVATE sxi32 jx9HashmapInsert(jx9_hashmap *pMap, jx9_value *pKey, jx9_value *pVal);
- JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight);
- JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest);
- JX9_PRIVATE sxi32 jx9HashmapCmp(jx9_hashmap *pLeft, jx9_hashmap *pRight, int bStrict);
- JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap);
- JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap);
- JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore);
- JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode, jx9_value *pKey);
- JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm);
- JX9_PRIVATE sxi32 jx9HashmapWalk(jx9_hashmap *pMap, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut);
- /* builtin.c function prototypes */
- JX9_PRIVATE sxi32 jx9InputFormat(int (*xConsumer)(jx9_context *, const char *, int, void *),
- jx9_context *pCtx, const char *zIn, int nByte, int nArg, jx9_value **apArg, void *pUserData, int vf);
- JX9_PRIVATE sxi32 jx9ProcessCsv(const char *zInput, int nByte, int delim, int encl,
- int escape, sxi32 (*xConsumer)(const char *, int, void *), void *pUserData);
- JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData);
- JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen);
- JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection);
- #endif
- /* vfs.c */
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
- int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew);
- JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut);
- JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle);
- #endif /* JX9_DISABLE_BUILTIN_FUNC */
- JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen);
- JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm);
- JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void);
- JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm);
- JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm);
- JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm);
- /* lib.c function prototypes */
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp);
- JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch);
- JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch);
- JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry);
- JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen);
- #endif /* JX9_DISABLE_BUILTIN_FUNC */
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData);
- #endif /* JX9_DISABLE_BUILTIN_FUNC */
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- #ifndef JX9_DISABLE_HASH_FUNC
- JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen);
- JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len);
- JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx);
- JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx);
- JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16]);
- JX9_PRIVATE void SHA1Init(SHA1Context *context);
- JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len);
- JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]);
- JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20]);
- #endif
- #endif /* JX9_DISABLE_BUILTIN_FUNC */
- JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen);
- JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void *pUserData);
- JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...);
- JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap);
- JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...);
- JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...);
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE const char *SyTimeGetMonth(sxi32 iMonth);
- JX9_PRIVATE const char *SyTimeGetDay(sxi32 iDay);
- #endif /* JX9_DISABLE_BUILTIN_FUNC */
- JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8);
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
- #endif
- JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex);
- JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp);
- JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData);
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
- JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
- #endif /* JX9_DISABLE_BUILTIN_FUNC */
- JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
- JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
- JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
- JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
- JX9_PRIVATE sxi32 SyHexToint(sxi32 c);
- JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
- JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
- JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail);
- JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData);
- JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32(*xStep)(SyHashEntry *, void *), void *pUserData);
- JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData);
- JX9_PRIVATE SyHashEntry *SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen);
- JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash);
- JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp);
- JX9_PRIVATE void *SySetAt(SySet *pSet, sxu32 nIdx);
- JX9_PRIVATE void *SySetPop(SySet *pSet);
- JX9_PRIVATE void *SySetPeek(SySet *pSet);
- JX9_PRIVATE sxi32 SySetRelease(SySet *pSet);
- JX9_PRIVATE sxi32 SySetReset(SySet *pSet);
- JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet);
- JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry);
- JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem);
- JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem);
- JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize);
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft);
- #endif
- JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob);
- JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob);
- JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest);
- JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob);
- JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize);
- JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte);
- JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator);
- JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize);
- JX9_PRIVATE char *SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize);
- JX9_PRIVATE void *SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize);
- JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend);
- JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void *pUserData);
- JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void *pUserData);
- JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend, SyMemBackend *pParent);
- #if 0
- /* Not used in the current release of the JX9 engine */
- JX9_PRIVATE void *SyMemBackendPoolRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
- #endif
- JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void *pChunk);
- JX9_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte);
- JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void *pChunk);
- JX9_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
- JX9_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte);
- #if defined(JX9_ENABLE_THREADS)
- JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
- JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
- #endif
- JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen);
- JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize);
- JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize);
- JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen);
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen);
- #endif
- JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos);
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
- #endif
- JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
- JX9_PRIVATE sxu32 SyStrlen(const char *zSrc);
- #if defined(JX9_ENABLE_THREADS)
- JX9_PRIVATE const SyMutexMethods *SyMutexExportMethods(void);
- JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
- JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
- #endif
- #endif /* __JX9INT_H__ */
- /*
- * ----------------------------------------------------------
- * File: vm.c
- * MD5: a0a6393b5981e604af8f6c86d0659845
- * ----------------------------------------------------------
- */
- /*
- * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
- * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
- * Version 1.7.2
- * 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://jx9.symisc.net/
- */
- /* $SymiscID: lex.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
- #ifndef JX9_AMALGAMATION
- #include "jx9Int.h"
- #endif
- /*
- * The code in this file implements execution method of the JX9 Virtual Machine.
- * The JX9 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 JX9
- * statements.
- * JX9 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 jx9_value.
- * JX9 uses the jx9_value object to represent all values that can be stored in a JX9 variable.
- * Since JX9 uses dynamic typing for the values it stores. Values stored in jx9_value objects
- * can be integers, floating point values, strings, arrays, object instances (object in the JX9 jargon)
- * and so on.
- * Internally, the JX9 virtual machine manipulates nearly all values as jx9_values structures.
- * Each jx9_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 JX9 bytecode program. But other routines are also provided
- * to help in building up a program instruction by instruction.
- */
- /*
- * 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 */
- SySet sLocal; /* Local variables container (VmSlot instance) */
- jx9_vm *pVm; /* VM that own this frame */
- SyHash hVar; /* Variable hashtable for fast lookup */
- SySet sArg; /* Function arguments container */
- sxi32 iFlags; /* Frame configuration flags (See below)*/
- sxu32 iExceptionJump; /* Exception jump destination */
- };
- /*
- * When a user defined variable is 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 */
- };
- /*
- * 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 JX9 program.
- * The constant expansion mechanism under JX9 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.
- */
- JX9_PRIVATE sxi32 jx9VmRegisterConstant(
- jx9_vm *pVm, /* Target VM */
- const SyString *pName, /* Constant name */
- ProcConstant xExpand, /* Constant expansion callback */
- void *pUserData /* Last argument to xExpand() */
- )
- {
- jx9_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 = (jx9_constant *)pEntry->pUserData;
- pCons->xExpand = xExpand;
- pCons->pUserData = pUserData;
- return SXRET_OK;
- }
- /* Allocate a new constant instance */
- pCons = (jx9_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_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 JX9 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 jx9NewForeignFunction(
- jx9_vm *pVm, /* Target VM */
- const SyString *pName, /* Foreign function name */
- ProcHostFunction xFunc, /* Foreign function implementation */
- void *pUserData, /* Foreign function private data */
- jx9_user_func **ppOut /* OUT: VM image of the foreign function */
- )
- {
- jx9_user_func *pFunc;
- char *zDup;
- /* Allocate a new user function */
- pFunc = (jx9_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_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(jx9_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(jx9_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 JX9 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.
- */
- JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(
- jx9_vm *pVm, /* Target VM */
- const SyString *pName, /* Foreign function name */
- ProcHostFunction xFunc, /* Foreign function implementation */
- void *pUserData /* Foreign function private data */
- )
- {
- jx9_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 = (jx9_user_func *)pEntry->pUserData;
- pFunc->pUserData = pUserData;
- pFunc->xFunc = xFunc;
- SySetReset(&pFunc->aAux);
- return SXRET_OK;
- }
- /* Create a new user function */
- rc = jx9NewForeignFunction(&(*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.
- */
- JX9_PRIVATE sxi32 jx9VmInitFuncState(
- jx9_vm *pVm, /* Target VM */
- jx9_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(jx9_vm_func));
- /* Initialize structure fields */
- /* Arguments container */
- SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(jx9_vm_func_arg));
- /* Static variable container */
- SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(jx9_vm_func_static_var));
- /* Bytecode container */
- SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
- /* Preallocate some instruction slots */
- SySetAlloc(&pFunc->aByteCode, 0x10);
- pFunc->iFlags = iFlags;
- pFunc->pUserData = pUserData;
- SyStringInitFromBuf(&pFunc->sName, zName, nByte);
- return SXRET_OK;
- }
- /*
- * Install a user defined function in the corresponding VM container.
- */
- JX9_PRIVATE sxi32 jx9VmInstallUserFunction(
- jx9_vm *pVm, /* Target VM */
- jx9_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 ){
- jx9_vm_func *pLink = (jx9_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;
- }
- /*
- * Instruction builder interface.
- */
- JX9_PRIVATE sxi32 jx9VmEmitInstr(
- jx9_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 ){
- jx9GenCompileError(&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.
- */
- JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_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.
- */
- JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm)
- {
- return pVm->pByteContainer;
- }
- /*
- * Extract the VM instruction rooted at nIndex.
- */
- JX9_PRIVATE VmInstr * jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex)
- {
- VmInstr *pInstr;
- pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex);
- return pInstr;
- }
- /*
- * Return the total number of VM instructions recorded so far.
- */
- JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm)
- {
- return SySetUsed(pVm->pByteContainer);
- }
- /*
- * Pop the last VM instruction.
- */
- JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm)
- {
- return (VmInstr *)SySetPop(pVm->pByteContainer);
- }
- /*
- * Peek the last VM instruction.
- */
- JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm)
- {
- return (VmInstr *)SySetPeek(pVm->pByteContainer);
- }
- /*
- * Allocate a new virtual machine frame.
- */
- static VmFrame * VmNewFrame(
- jx9_vm *pVm, /* Target VM */
- void *pUserData /* Upper-layer private data */
- )
- {
- 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->pVm = pVm;
- SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0);
- SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot));
- SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot));
- return pFrame;
- }
- /*
- * Enter a VM frame.
- */
- static sxi32 VmEnterFrame(
- jx9_vm *pVm, /* Target VM */
- void *pUserData, /* Upper-layer private data */
- VmFrame **ppFrame /* OUT: Top most active frame */
- )
- {
- VmFrame *pFrame;
- /* Allocate a new frame */
- pFrame = VmNewFrame(&(*pVm), pUserData);
- 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 JX9_OP_UPLINK instruction implementation for more
- * information.
- */
- static sxi32 VmFrameLink(jx9_vm *pVm,SyString *pName)
- {
- VmFrame *pTarget, *pFrame;
- SyHashEntry *pEntry = 0;
- sxi32 rc;
- /* Point to the upper frame */
- pFrame = pVm->pFrame;
- pTarget = pFrame;
- pFrame = pTarget->pParent;
- while( pFrame ){
- /* 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);
- return rc;
- }
- /*
- * Leave the top-most active frame.
- */
- static void VmLeaveFrame(jx9_vm *pVm)
- {
- VmFrame *pFrame = pVm->pFrame;
- if( pFrame ){
- /* Unlink from the list of active VM frame */
- pVm->pFrame = pFrame->pParent;
- if( pFrame->pParent ){
- 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 */
- jx9VmUnsetMemObj(&(*pVm), aSlot[n].nIdx);
- }
- }
- /* Release internal containers */
- SyHashRelease(&pFrame->hVar);
- SySetRelease(&pFrame->sArg);
- SySetRelease(&pFrame->sLocal);
- /* 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 JX9 engine.
- * Refer to the official documentation for more information.
- */
- static jx9_vm_func * VmOverload(
- jx9_vm *pVm, /* Target VM */
- jx9_vm_func *pList, /* Linked list of candidates for overloading */
- jx9_value *aArg, /* Array of passed arguments */
- int nArg /* Total number of passed arguments */
- )
- {
- int iTarget, i, j, iCur, iMax;
- jx9_vm_func *apSet[10]; /* Maximum number of candidates */
- jx9_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';
- }
- 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];
- }
- /*
- * Dummy read-only buffer used for slot reservation.
- */
- static const char zDummy[sizeof(jx9_value)] = { 0 }; /* Must be >= sizeof(jx9_value) */
- /*
- * Reserve a constant memory object.
- * Return a pointer to the raw jx9_value on success. NULL on failure.
- */
- JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex)
- {
- jx9_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 = (jx9_value *)SySetPeek(&pVm->aLitObj);
- return pObj;
- }
- /*
- * Reserve a memory object.
- * Return a pointer to the raw jx9_value on success. NULL on failure.
- */
- static jx9_value * VmReserveMemObj(jx9_vm *pVm, sxu32 *pIndex)
- {
- jx9_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 = (jx9_value *)SySetPeek(&pVm->aMemObj);
- return pObj;
- }
- /* Forward declaration */
- static sxi32 VmEvalChunk(jx9_vm *pVm, jx9_context *pCtx, SyString *pChunk, int iFlags, int bTrueReturn);
- /*
- * Built-in functions that cannot be implemented directly as foreign functions.
- */
- #define JX9_BUILTIN_LIB \
- "function scandir(string $directory, int $sort_order = SCANDIR_SORT_ASCENDING)"\
- "{"\
- " if( func_num_args() < 1 ){ return FALSE; }"\
- " $aDir = [];"\
- " $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 = []; /* 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.'JX9'.rand_str(12), 'w+');"\
- " return $pHandle;"\
- "}"\
- "/* Creates a temporary filename */"\
- "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */, string $zPrefix = 'JX9')"\
- "{"\
- " return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\
- "}"\
- "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;"\
- "}"
- /*
- * Initialize a freshly allocated JX9 Virtual Machine so that we can
- * start compiling the target JX9 program.
- */
- JX9_PRIVATE sxi32 jx9VmInit(
- jx9_vm *pVm, /* Initialize this */
- jx9 *pEngine /* Master engine */
- )
- {
- SyString sBuiltin;
- jx9_value *pObj;
- sxi32 rc;
- /* Zero the structure */
- SyZero(pVm, sizeof(jx9_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(jx9_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(jx9_value));
- SySetAlloc(&pVm->aLitObj, 0xFF);
- SyHashInit(&pVm->hHostFunction, &pVm->sAllocator, 0, 0);
- SyHashInit(&pVm->hFunction, &pVm->sAllocator, 0, 0);
- SyHashInit(&pVm->hConstant, &pVm->sAllocator, 0, 0);
- SyHashInit(&pVm->hSuper, &pVm->sAllocator, 0, 0);
- SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot));
- /* 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->aIOstream, &pVm->sAllocator, sizeof(jx9_io_stream *));
- /* Error callbacks containers */
- jx9MemObjInit(&(*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 = JX9_ASSERT_WARNING; /* Issue a warning for each failed assertion */
- /* PRNG context */
- SyRandomnessInit(&pVm->sPrng, 0, 0);
- /* Install the null constant */
- pObj = jx9VmReserveConstObj(&(*pVm), 0);
- if( pObj == 0 ){
- rc = SXERR_MEM;
- goto Err;
- }
- jx9MemObjInit(pVm, pObj);
- /* Install the boolean TRUE constant */
- pObj = jx9VmReserveConstObj(&(*pVm), 0);
- if( pObj == 0 ){
- rc = SXERR_MEM;
- goto Err;
- }
- jx9MemObjInitFromBool(pVm, pObj, 1);
- /* Install the boolean FALSE constant */
- pObj = jx9VmReserveConstObj(&(*pVm), 0);
- if( pObj == 0 ){
- rc = SXERR_MEM;
- goto Err;
- }
- jx9MemObjInitFromBool(pVm, pObj, 0);
- /* Create the global frame */
- rc = VmEnterFrame(&(*pVm), 0, 0);
- if( rc != SXRET_OK ){
- goto Err;
- }
- /* Initialize the code generator */
- rc = jx9InitCodeGenerator(pVm, pEngine->xConf.xErr, pEngine->xConf.pErrData);
- if( rc != SXRET_OK ){
- goto Err;
- }
- /* VM correctly initialized, set the magic number */
- pVm->nMagic = JX9_VM_INIT;
- SyStringInitFromBuf(&sBuiltin,JX9_BUILTIN_LIB, sizeof(JX9_BUILTIN_LIB)-1);
- /* Compile the built-in library */
- VmEvalChunk(&(*pVm), 0, &sBuiltin, 0, FALSE);
- /* Reset the code generator */
- jx9ResetCodeGenerator(&(*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 [jx9_vm_exec()] via
- * the [jx9_vm_config()] interface with a configuration verb set to
- * jx9VM_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 (jx9VM_CONFIG_OUTPUT) rather than waiting for the VM
- * to finish executing and extracting the output.
- */
- JX9_PRIVATE sxi32 jx9VmBlobConsumer(
- 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 JX9 program.
- * Return a pointer to the operand stack (array of jx9_values)
- * on success. NULL (Fatal error) on failure.
- */
- static jx9_value * VmNewOperandStack(
- jx9_vm *pVm, /* Target VM */
- sxu32 nInstr /* Total numer of generated bytecode instructions */
- )
- {
- jx9_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 = (jx9_value *)SyMemBackendAlloc(&pVm->sAllocator, nInstr * sizeof(jx9_value));
- if( pStack == 0 ){
- return 0;
- }
- /* Initialize the operand stack */
- while( nInstr > 0 ){
- jx9MemObjInit(&(*pVm), &pStack[nInstr - 1]);
- --nInstr;
- }
- /* Ready for bytecode execution */
- return pStack;
- }
- /* Forward declaration */
- static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm);
- /*
- * Prepare the Virtual Machine for bytecode execution.
- * This routine gets called by the JX9 engine after
- * successful compilation of the target JX9 program.
- */
- JX9_PRIVATE sxi32 jx9VmMakeReady(
- jx9_vm *pVm /* Target VM */
- )
- {
- sxi32 rc;
- if( pVm->nMagic != JX9_VM_INIT ){
- /* Initialize your VM first */
- return SXERR_CORRUPT;
- }
- /* Mark the VM ready for bytecode execution */
- pVm->nMagic = JX9_VM_RUN;
- /* Release the code generator now we have compiled our program */
- jx9ResetCodeGenerator(pVm, 0, 0);
- /* Emit the DONE instruction */
- rc = jx9VmEmitInstr(&(*pVm), JX9_OP_DONE, 0, 0, 0, 0);
- if( rc != SXRET_OK ){
- return SXERR_MEM;
- }
- /* Script return value */
- jx9MemObjInit(&(*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 = jx9VmBlobConsumer;
- pVm->sVmConsumer.pUserData = &pVm->sConsumer;
- /* Register special functions first [i.e: print, 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 = jx9HashmapLoadBuiltin(&(*pVm));
- if( rc != SXRET_OK ){
- /* Don't worry about freeing memory, everything will be released shortly */
- return rc;
- }
- /* Register built-in constants [i.e: JX9_EOL, JX9_OS...] */
- jx9RegisterBuiltInConstant(&(*pVm));
- /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */
- jx9RegisterBuiltInFunction(&(*pVm));
- /* VM is ready for bytecode execution */
- return SXRET_OK;
- }
- /*
- * Reset a Virtual Machine to it's initial state.
- */
- JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm)
- {
- if( pVm->nMagic != JX9_VM_RUN && pVm->nMagic != JX9_VM_EXEC ){
- return SXERR_CORRUPT;
- }
- /* TICKET 1433-003: As of this version, the VM is automatically reset */
- SyBlobReset(&pVm->sConsumer);
- jx9MemObjRelease(&pVm->sExec);
- /* Set the ready flag */
- pVm->nMagic = JX9_VM_RUN;
- return SXRET_OK;
- }
- /*
- * Release a Virtual Machine.
- * Every virtual machine must be destroyed in order to avoid memory leaks.
- */
- JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm)
- {
- /* Set the stale magic number */
- pVm->nMagic = JX9_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 jx9_context object.
- * A pointer to a jx9_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 jx9_result_int(), jx9_result_string(), jx9_result_value(),
- * jx9_context_new_scalar(), jx9_context_alloc_chunk(), jx9_context_output(), jx9_context_throw_error()
- * and many more. Refer to the C/C++ Interfaces documentation for additional information.
- */
- static sxi32 VmInitCallContext(
- jx9_context *pOut, /* Call Context */
- jx9_vm *pVm, /* Target VM */
- jx9_user_func *pFunc, /* Foreign function to execute shortly */
- jx9_value *pRet, /* Store return value here*/
- sxi32 iFlags /* Control flags */
- )
- {
- pOut->pFunc = pFunc;
- pOut->pVm = pVm;
- SySetInit(&pOut->sVar, &pVm->sAllocator, sizeof(jx9_value *));
- SySetInit(&pOut->sChunk, &pVm->sAllocator, sizeof(jx9_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(jx9_context *pCtx)
- {
- sxu32 n;
- if( SySetUsed(&pCtx->sVar) > 0 ){
- jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
- for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
- if( apObj[n] == 0 ){
- /* Already released */
- continue;
- }
- jx9MemObjRelease(apObj[n]);
- SyMemBackendPoolFree(&pCtx->pVm->sAllocator, apObj[n]);
- }
- SySetRelease(&pCtx->sVar);
- }
- if( SySetUsed(&pCtx->sChunk) > 0 ){
- jx9_aux_data *aAux;
- void *pChunk;
- /* Automatic release of dynamically allocated chunk
- * using [jx9_context_alloc_chunk()].
- */
- aAux = (jx9_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 jx9_value allocated from the body of a foreign function.
- * Refer to [jx9_context_release_value()] for additional information.
- */
- JX9_PRIVATE void jx9VmReleaseContextValue(
- jx9_context *pCtx, /* Call context */
- jx9_value *pValue /* Release this value */
- )
- {
- if( pValue == 0 ){
- /* NULL value is a harmless operation */
- return;
- }
- if( SySetUsed(&pCtx->sVar) > 0 ){
- jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
- sxu32 n;
- for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
- if( apObj[n] == pValue ){
- jx9MemObjRelease(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(
- jx9_value **ppTos, /* Operand stack */
- sxi32 nPop /* Total number of memory objects to pop */
- )
- {
- jx9_value *pTos = *ppTos;
- while( nPop > 0 ){
- jx9MemObjRelease(pTos);
- pTos--;
- nPop--;
- }
- /* Top of the stack */
- *ppTos = pTos;
- }
- /*
- * Reserve a memory object.
- * Return a pointer to the raw jx9_value on success. NULL on failure.
- */
- JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIdx)
- {
- jx9_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 = (jx9_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 */
- jx9MemObjInit(&(*pVm), pObj);
- if( pIdx ){
- *pIdx = nIdx;
- }
- pObj->nIdx = nIdx;
- return pObj;
- }
- /*
- * 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 jx9_value * VmExtractMemObj(
- jx9_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;
- jx9_value *pObj;
- sxu32 nIdx;
- sxi32 rc;
- /* Point to the top active frame */
- pFrame = pVm->pFrame;
- /* 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 */
- return 0;
- }
- /* No such variable, automatically create a new one and install
- * it in the current frame.
- */
- pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
- if( pObj == 0 ){
- return 0;
- }
- 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{
- /* Extract variable contents */
- nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
- pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
- if( bNullify && pObj ){
- jx9MemObjRelease(pObj);
- }
- }
- }else{
- /* Superglobal */
- nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
- pObj = (jx9_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 jx9_value * VmExtractSuper(
- jx9_vm *pVm, /* Target VM */
- const char *zName, /* Superglobal name: NOT NULL TERMINATED */
- sxu32 nByte /* zName length */
- )
- {
- SyHashEntry *pEntry;
- jx9_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 = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
- return pValue;
- }
- /*
- * Perform a raw hashmap insertion.
- * Refer to the [jx9VmConfigure()] implementation for additional information.
- */
- static sxi32 VmHashmapInsert(
- jx9_hashmap *pMap, /* Target hashmap */
- const char *zKey, /* Entry key */
- int nKeylen, /* zKey length*/
- const char *zData, /* Entry data */
- int nLen /* zData length */
- )
- {
- jx9_value sKey,sValue;
- jx9_value *pKey;
- sxi32 rc;
- pKey = 0;
- jx9MemObjInit(pMap->pVm, &sKey);
- jx9MemObjInitFromString(pMap->pVm, &sValue, 0);
- if( zKey ){
- if( nKeylen < 0 ){
- nKeylen = (int)SyStrlen(zKey);
- }
- jx9MemObjStringAppend(&sKey, zKey, (sxu32)nKeylen);
- pKey = &sKey;
- }
- if( zData ){
- if( nLen < 0 ){
- /* Compute length automatically */
- nLen = (int)SyStrlen(zData);
- }
- jx9MemObjStringAppend(&sValue, zData, (sxu32)nLen);
- }
- /* Perform the insertion */
- rc = jx9HashmapInsert(&(*pMap),pKey,&sValue);
- jx9MemObjRelease(&sKey);
- jx9MemObjRelease(&sValue);
- return rc;
- }
- /* Forward declaration */
- static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte);
- /*
- * Configure a working virtual machine instance.
- *
- * This routine is used to configure a JX9 virtual machine obtained by a prior
- * successful call to one of the compile interface such as jx9_compile()
- * jx9_compile_v2() or jx9_compile_file().
- * The second argument to this function is an integer configuration option
- * that determines what property of the JX9 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 JX9_VM_CONFIG_OUTPUT,
- * JX9_VM_CONFIG_HTTP_REQUEST and JX9_VM_CONFIG_ARGV_ENTRY.
- * Refer to the official documentation for the list of allowed verbs.
- */
- JX9_PRIVATE sxi32 jx9VmConfigure(
- jx9_vm *pVm, /* Target VM */
- sxi32 nOp, /* Configuration verb */
- va_list ap /* Subsequent option arguments */
- )
- {
- sxi32 rc = SXRET_OK;
- switch(nOp){
- case JX9_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 JX9_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 JX9_VM_CONFIG_ERR_REPORT:
- /* Run-Time Error report */
- pVm->bErrReport = 1;
- break;
- case JX9_VM_CONFIG_RECURSION_DEPTH:{
- /* Recursion depth */
- int nDepth = va_arg(ap, int);
- if( nDepth > 2 && nDepth < 1024 ){
- pVm->nMaxDepth = nDepth;
- }
- break;
- }
- case JX9_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 JX9_VM_CONFIG_CREATE_VAR: {
- /* Create a new superglobal/global variable */
- const char *zName = va_arg(ap, const char *);
- jx9_value *pValue = va_arg(ap, jx9_value *);
- SyHashEntry *pEntry;
- jx9_value *pObj;
- sxu32 nByte;
- sxu32 nIdx;
- #ifdef UNTRUST
- if( SX_EMPTY_STR(zName) || pValue == 0 ){
- rc = SXERR_CORRUPT;
- break;
- }
- #endif
- nByte = SyStrlen(zName);
- /* Check if the superglobal is already installed */
- pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
- if( pEntry ){
- /* Variable already installed */
- nIdx = SX_PTR_TO_INT(pEntry->pUserData);
- /* Extract contents */
- pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
- if( pObj ){
- /* Overwrite old contents */
- jx9MemObjStore(pValue, pObj);
- }
- }else{
- /* Install a new variable */
- pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
- if( pObj == 0 ){
- rc = SXERR_MEM;
- break;
- }
- /* Copy value */
- jx9MemObjStore(pValue, pObj);
- /* Install the superglobal */
- rc = SyHashInsert(&pVm->hSuper, (const void *)zName, nByte, SX_INT_TO_PTR(nIdx));
- }
- break;
- }
- case JX9_VM_CONFIG_SERVER_ATTR:
- case JX9_VM_CONFIG_ENV_ATTR: {
- const char *zKey = va_arg(ap, const char *);
- const char *zValue = va_arg(ap, const char *);
- int nLen = va_arg(ap, int);
- jx9_hashmap *pMap;
- jx9_value *pValue;
- if( nOp == JX9_VM_CONFIG_ENV_ATTR ){
- /* Extract the $_ENV superglobal */
- pValue = VmExtractSuper(&(*pVm), "_ENV", sizeof("_ENV")-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 = (jx9_hashmap *)pValue->x.pOther;
- /* Perform the insertion */
- rc = VmHashmapInsert(pMap, zKey, -1, zValue, nLen);
- break;
- }
- case JX9_VM_CONFIG_ARGV_ENTRY:{
- /* Script arguments */
- const char *zValue = va_arg(ap, const char *);
- jx9_hashmap *pMap;
- jx9_value *pValue;
- /* 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 = (jx9_hashmap *)pValue->x.pOther;
- /* Perform the insertion */
- rc = VmHashmapInsert(pMap, 0, 0, zValue,-1);
- if( rc == SXRET_OK && zValue && zValue[0] != 0 ){
- if( pMap->nEntry > 1 ){
- /* Append space separator first */
- SyBlobAppend(&pVm->sArgv, (const void *)" ", sizeof(char));
- }
- SyBlobAppend(&pVm->sArgv, (const void *)zValue,SyStrlen(zValue));
- }
- break;
- }
- case JX9_VM_CONFIG_EXEC_VALUE: {
- /* Script return value */
- jx9_value **ppValue = va_arg(ap, jx9_value **);
- #ifdef UNTRUST
- if( ppValue == 0 ){
- rc = SXERR_CORRUPT;
- break;
- }
- #endif
- *ppValue = &pVm->sExec;
- break;
- }
- case JX9_VM_CONFIG_IO_STREAM: {
- /* Register an IO stream device */
- const jx9_io_stream *pStream = va_arg(ap, const jx9_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 JX9_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 JX9_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 JX9 bytecode 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"
- "JX9 VM Dump Copyright (C) 2012-2013 Symisc Systems\n"
- " http://jx9.symisc.net/\n"
- "====================================================\n"
- };
- VmInstr *pInstr, *pEnd;
- sxi32 rc = SXRET_OK;
- sxu32 n;
- /* Point to the JX9 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;
- }
- /*
- * Consume a generated run-time error message by invoking the VM output
- * consumer callback.
- */
- static sxi32 VmCallErrorHandler(jx9_vm *pVm, SyBlob *pMsg)
- {
- jx9_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);
- /* 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 [jx9_context_throw_error()] for additional
- * information.
- */
- JX9_PRIVATE sxi32 jx9VmThrowError(
- jx9_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 JX9_CTX_WARNING: zErr = "Warning: "; break;
- case JX9_CTX_NOTICE: zErr = "Notice: "; break;
- default:
- iErr = JX9_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 [jx9_context_throw_error_format()] for additional
- * information.
- */
- static sxi32 VmThrowErrorAp(
- jx9_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 JX9_CTX_WARNING: zErr = "Warning: "; break;
- case JX9_CTX_NOTICE: zErr = "Notice: "; break;
- default:
- iErr = JX9_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 [jx9_context_throw_error_format()] for additional
- * information.
- * ------------------------------------
- * Simple boring wrapper function.
- * ------------------------------------
- */
- static sxi32 VmErrorFormat(jx9_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 [jx9_context_throw_error_format()] for additional
- * information.
- * ------------------------------------
- * Simple boring wrapper function.
- * ------------------------------------
- */
- JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap)
- {
- sxi32 rc;
- rc = VmThrowErrorAp(&(*pVm), &(*pFuncName), iErr, zFormat, ap);
- return rc;
- }
- /* Forward declaration */
- static sxi32 VmLocalExec(jx9_vm *pVm,SySet *pByteCode,jx9_value *pResult);
- /*
- * Execute as much of a JX9 bytecode program as we can then return.
- *
- * [jx9VmMakeReady()] 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 [jx9VmMakeReady()] for additional information.
- * If the installed VM output consumer callback ever returns JX9_ABORT
- * then the program execution is halted.
- * After this routine has finished, [jx9VmRelease()] or [jx9VmReset()]
- * 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(
- jx9_vm *pVm, /* Target VM */
- VmInstr *aInstr, /* JX9 bytecode program */
- jx9_value *pStack, /* Operand stack */
- int nTos, /* Top entry in the operand stack (usually -1) */
- jx9_value *pResult /* Store program return value here. NULL otherwise */
- )
- {
- VmInstr *pInstr;
- jx9_value *pTos;
- SySet aArg;
- sxi32 pc;
- sxi32 rc;
- /* Argument container */
- SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_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 JX9_OP_DONE:
- if( pInstr->iP1 ){
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- if( pResult ){
- /* Execution result */
- jx9MemObjStore(pTos, pResult);
- }
- VmPopOperand(&pTos, 1);
- }
- goto Done;
- /*
- * HALT: P1 * *
- *
- * Program execution aborted: Clean up the mess left behind
- * and abort immediately.
- */
- case JX9_OP_HALT:
- if( pInstr->iP1 ){
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- 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);
- /* 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);
- }
- goto Abort;
- /*
- * JMP: * P2 *
- *
- * Unconditional jump: The next instruction executed will be
- * the one at index P2 from the beginning of the program.
- */
- case JX9_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 JX9_OP_JZ:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Get a boolean value */
- if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
- jx9MemObjToBool(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 JX9_OP_JNZ:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Get a boolean value */
- if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
- jx9MemObjToBool(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 JX9_OP_NOOP:
- break;
- /*
- * POP: P1 * *
- *
- * Pop P1 elements from the operand stack.
- */
- case JX9_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 JX9_OP_CVT_INT:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- if((pTos->iFlags & MEMOBJ_INT) == 0 ){
- jx9MemObjToInteger(pTos);
- }
- /* Invalidate any prior representation */
- MemObjSetType(pTos, MEMOBJ_INT);
- break;
- /*
- * CVT_REAL: * * *
- *
- * Force the top of the stack to be a real.
- */
- case JX9_OP_CVT_REAL:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- if((pTos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pTos);
- }
- /* Invalidate any prior representation */
- MemObjSetType(pTos, MEMOBJ_REAL);
- break;
- /*
- * CVT_STR: * * *
- *
- * Force the top of the stack to be a string.
- */
- case JX9_OP_CVT_STR:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
- jx9MemObjToString(pTos);
- }
- break;
- /*
- * CVT_BOOL: * * *
- *
- * Force the top of the stack to be a boolean.
- */
- case JX9_OP_CVT_BOOL:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
- jx9MemObjToBool(pTos);
- }
- break;
- /*
- * CVT_NULL: * * *
- *
- * Nullify the top of the stack.
- */
- case JX9_OP_CVT_NULL:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- jx9MemObjRelease(pTos);
- break;
- /*
- * CVT_NUMC: * * *
- *
- * Force the top of the stack to be a numeric type (integer, real or both).
- */
- case JX9_OP_CVT_NUMC:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Force a numeric cast */
- jx9MemObjToNumeric(pTos);
- break;
- /*
- * CVT_ARRAY: * * *
- *
- * Force the top of the stack to be a hashmap aka 'array'.
- */
- case JX9_OP_CVT_ARRAY:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Force a hashmap cast */
- rc = jx9MemObjToHashmap(pTos);
- if( rc != SXRET_OK ){
- /* Not so fatal, emit a simple warning */
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
- "JX9 engine is running out of memory while performing an array cast");
- }
- break;
- /*
- * LOADC P1 P2 *
- *
- * Load a constant [i.e: JX9_EOL, JX9_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 JX9_OP_LOADC: {
- jx9_value *pObj;
- /* Reserve a room */
- pTos++;
- if( (pObj = (jx9_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 ){
- jx9_constant *pCons = (jx9_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;
- }
- }
- jx9MemObjLoad(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 JX9_OP_LOAD:{
- jx9_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 ){
- jx9MemObjToString(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 ){
- jx9MemObjRelease(pTos);
- }else{
- MemObjSetType(pTos, MEMOBJ_NULL);
- }
- pTos->nIdx = SXU32_HIGH; /* Mark as constant */
- break;
- }else{
- /* Fatal error */
- VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
- goto Abort;
- }
- }
- /* Load variable contents */
- jx9MemObjLoad(pObj, pTos);
- pTos->nIdx = pObj->nIdx;
- break;
- }
- /*
- * LOAD_MAP P1 * *
- *
- * Allocate a new empty hashmap (array in the JX9 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 JX9_OP_LOAD_MAP: {
- jx9_hashmap *pMap;
- int is_json_object; /* TRUE if we are dealing with a JSON object */
- int iIncr = 1;
- /* Allocate a new hashmap instance */
- pMap = jx9NewHashmap(&(*pVm), 0, 0);
- if( pMap == 0 ){
- VmErrorFormat(&(*pVm), JX9_CTX_ERR,
- "Fatal, JX9 engine is running out of memory while loading JSON array/object at instruction #:%d", pc);
- goto Abort;
- }
- is_json_object = 0;
- if( pInstr->iP2 ){
- /* JSON object, record that */
- pMap->iFlags |= HASHMAP_JSON_OBJECT;
- is_json_object = 1;
- iIncr = 2;
- }
- if( pInstr->iP1 > 0 ){
- jx9_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */
- /* Perform the insertion */
- while( pEntry <= pTos ){
- /* Standard insertion */
- jx9HashmapInsert(pMap,
- is_json_object ? pEntry : 0 /* Automatic index assign */,
- is_json_object ? &pEntry[1] : pEntry
- );
- /* Next pair on the stack */
- pEntry += iIncr;
- }
- /* Pop P1 elements */
- VmPopOperand(&pTos, pInstr->iP1);
- }
- /* Push the hashmap */
- pTos++;
- pTos->x.pOther = pMap;
- MemObjSetType(pTos, MEMOBJ_HASHMAP);
- 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 JX9_OP_LOAD_IDX: {
- jx9_hashmap_node *pNode = 0; /* cc warning */
- jx9_hashmap *pMap = 0;
- jx9_value *pIdx;
- pIdx = 0;
- if( pInstr->iP1 == 0 ){
- if( !pInstr->iP2){
- /* No available index, load NULL */
- if( pTos >= pStack ){
- jx9MemObjRelease(pTos);
- }else{
- /* TICKET 1433-020: Empty stack */
- pTos++;
- MemObjSetType(pTos, MEMOBJ_NULL);
- pTos->nIdx = SXU32_HIGH;
- }
- /* Emit a notice */
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_NOTICE,
- "JSON Array/Object: Attempt to access an undefined member, JX9 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 */
- jx9MemObjToInteger(pIdx);
- }
- nOfft = (sxu32)pIdx->x.iVal;
- if( nOfft >= SyBlobLength(&pTos->sBlob) ){
- /* Invalid offset, load null */
- jx9MemObjRelease(pTos);
- }else{
- const char *zData = (const char *)SyBlobData(&pTos->sBlob);
- int c = zData[nOfft];
- jx9MemObjRelease(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 ){
- jx9_value *pObj;
- if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- jx9MemObjToHashmap(pObj);
- jx9MemObjLoad(pObj, pTos);
- }
- }
- }
- rc = SXERR_NOTFOUND; /* Assume the index is invalid */
- if( pTos->iFlags & MEMOBJ_HASHMAP ){
- /* Point to the hashmap */
- pMap = (jx9_hashmap *)pTos->x.pOther;
- if( pIdx ){
- /* Load the desired entry */
- rc = jx9HashmapLookup(pMap, pIdx, &pNode);
- }
- if( rc != SXRET_OK && pInstr->iP2 ){
- /* Create a new empty entry */
- rc = jx9HashmapInsert(pMap, pIdx, 0);
- if( rc == SXRET_OK ){
- /* Point to the last inserted entry */
- pNode = pMap->pLast;
- }
- }
- }
- if( pIdx ){
- jx9MemObjRelease(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;
- jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
- }else{
- pTos->nIdx = pNode->nValIdx;
- jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
- jx9HashmapUnref(pMap);
- }
- }else{
- /* No such entry, load NULL */
- jx9MemObjRelease(pTos);
- pTos->nIdx = SXU32_HIGH;
- }
- break;
- }
- /*
- * STORE * P2 P3
- *
- * Perform a store (Assignment) operation.
- */
- case JX9_OP_STORE: {
- jx9_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 ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
- "Cannot perform assignment on a constant object attribute, JX9 is loading NULL");
- pTos->nIdx = SXU32_HIGH;
- }else{
- /* Point to the desired memory object */
- pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
- if( pObj ){
- /* Perform the store operation */
- jx9MemObjStore(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 */
- jx9MemObjToString(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), JX9_CTX_ERR,
- "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
- goto Abort;
- }
- if( !pInstr->p3 ){
- jx9MemObjRelease(&pTos[1]);
- }
- /* Perform the store operation */
- jx9MemObjStore(pTos, pObj);
- break;
- }
- /*
- * STORE_IDX: P1 * P3
- *
- * Perfrom a store operation an a hashmap entry.
- */
- case JX9_OP_STORE_IDX: {
- jx9_hashmap *pMap = 0; /* cc warning */
- jx9_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 = (jx9_hashmap *)pTos->x.pOther;
- if( pMap->iRef < 2 ){
- /* TICKET 1433-48: Prevent garbage collection */
- pMap->iRef = 2;
- }
- }else{
- jx9_value *pObj;
- pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
- if( pObj == 0 ){
- if( pKey ){
- jx9MemObjRelease(pKey);
- }
- VmPopOperand(&pTos, 1);
- break;
- }
- /* Phase#1: Load the array */
- if( (pObj->iFlags & MEMOBJ_STRING) ){
- VmPopOperand(&pTos, 1);
- if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){
- /* Force a string cast */
- jx9MemObjToString(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 */
- jx9MemObjToInteger(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 ){
- jx9MemObjRelease(pKey);
- }
- break;
- }else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
- /* Force a hashmap cast */
- rc = jx9MemObjToHashmap(pObj);
- if( rc != SXRET_OK ){
- VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while creating a new array");
- goto Abort;
- }
- }
- pMap = (jx9_hashmap *)pObj->x.pOther;
- }
- VmPopOperand(&pTos, 1);
- /* Phase#2: Perform the insertion */
- jx9HashmapInsert(pMap, pKey, pTos);
- if( pKey ){
- jx9MemObjRelease(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 JX9_OP_INCR:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){
- if( pTos->nIdx != SXU32_HIGH ){
- jx9_value *pObj;
- if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- /* Force a numeric cast */
- jx9MemObjToNumeric(pObj);
- if( pObj->iFlags & MEMOBJ_REAL ){
- pObj->x.rVal++;
- /* Try to get an integer representation */
- jx9MemObjTryInteger(pTos);
- }else{
- pObj->x.iVal++;
- MemObjSetType(pTos, MEMOBJ_INT);
- }
- if( pInstr->iP1 ){
- /* Pre-icrement */
- jx9MemObjStore(pObj, pTos);
- }
- }
- }else{
- if( pInstr->iP1 ){
- /* Force a numeric cast */
- jx9MemObjToNumeric(pTos);
- /* Pre-increment */
- if( pTos->iFlags & MEMOBJ_REAL ){
- pTos->x.rVal++;
- /* Try to get an integer representation */
- jx9MemObjTryInteger(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 JX9_OP_DECR:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){
- /* Force a numeric cast */
- jx9MemObjToNumeric(pTos);
- if( pTos->nIdx != SXU32_HIGH ){
- jx9_value *pObj;
- if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- /* Force a numeric cast */
- jx9MemObjToNumeric(pObj);
- if( pObj->iFlags & MEMOBJ_REAL ){
- pObj->x.rVal--;
- /* Try to get an integer representation */
- jx9MemObjTryInteger(pTos);
- }else{
- pObj->x.iVal--;
- MemObjSetType(pTos, MEMOBJ_INT);
- }
- if( pInstr->iP1 ){
- /* Pre-icrement */
- jx9MemObjStore(pObj, pTos);
- }
- }
- }else{
- if( pInstr->iP1 ){
- /* Pre-increment */
- if( pTos->iFlags & MEMOBJ_REAL ){
- pTos->x.rVal--;
- /* Try to get an integer representation */
- jx9MemObjTryInteger(pTos);
- }else{
- pTos->x.iVal--;
- MemObjSetType(pTos, MEMOBJ_INT);
- }
- }
- }
- }
- break;
- /*
- * UMINUS: * * *
- *
- * Perform a unary minus operation.
- */
- case JX9_OP_UMINUS:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Force a numeric (integer, real or both) cast */
- jx9MemObjToNumeric(pTos);
- if( pTos->iFlags & MEMOBJ_REAL ){
- pTos->x.rVal = -pTos->x.rVal;
- }
- if( pTos->iFlags & MEMOBJ_INT ){
- pTos->x.iVal = -pTos->x.iVal;
- }
- break;
- /*
- * UPLUS: * * *
- *
- * Perform a unary plus operation.
- */
- case JX9_OP_UPLUS:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Force a numeric (integer, real or both) cast */
- jx9MemObjToNumeric(pTos);
- if( pTos->iFlags & MEMOBJ_REAL ){
- pTos->x.rVal = +pTos->x.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 JX9_OP_LNOT:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Force a boolean cast */
- if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
- jx9MemObjToBool(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 JX9_OP_BITNOT:
- #ifdef UNTRUST
- if( pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Force an integer cast */
- if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
- jx9MemObjToInteger(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 JX9_OP_MUL:
- case JX9_OP_MUL_STORE: {
- jx9_value *pNos = &pTos[-1];
- /* Force the operand to be numeric */
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- jx9MemObjToNumeric(pTos);
- jx9MemObjToNumeric(pNos);
- /* Perform the requested operation */
- if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
- /* Floating point arithemic */
- jx9_real a, b, r;
- if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pNos);
- }
- a = pNos->x.rVal;
- b = pTos->x.rVal;
- r = a * b;
- /* Push the result */
- pNos->x.rVal = r;
- MemObjSetType(pNos, MEMOBJ_REAL);
- /* Try to get an integer representation */
- jx9MemObjTryInteger(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 == JX9_OP_MUL_STORE ){
- jx9_value *pObj;
- if( pTos->nIdx == SXU32_HIGH ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
- }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- jx9MemObjStore(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 JX9_OP_ADD:{
- jx9_value *pNos = &pTos[-1];
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- /* Perform the addition */
- jx9MemObjAdd(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 JX9_OP_ADD_STORE:{
- jx9_value *pNos = &pTos[-1];
- jx9_value *pObj;
- sxu32 nIdx;
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- /* Perform the addition */
- nIdx = pTos->nIdx;
- jx9MemObjAdd(pTos, pNos, TRUE);
- /* Peform the store operation */
- if( nIdx == SXU32_HIGH ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
- }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx)) != 0 ){
- jx9MemObjStore(pTos, pObj);
- }
- /* Ticket 1433-35: Perform a stack dup */
- jx9MemObjStore(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 JX9_OP_SUB: {
- jx9_value *pNos = &pTos[-1];
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
- /* Floating point arithemic */
- jx9_real a, b, r;
- if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pNos);
- }
- a = pNos->x.rVal;
- b = pTos->x.rVal;
- r = a - b;
- /* Push the result */
- pNos->x.rVal = r;
- MemObjSetType(pNos, MEMOBJ_REAL);
- /* Try to get an integer representation */
- jx9MemObjTryInteger(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 JX9_OP_SUB_STORE: {
- jx9_value *pNos = &pTos[-1];
- jx9_value *pObj;
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
- /* Floating point arithemic */
- jx9_real a, b, r;
- if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pNos);
- }
- a = pTos->x.rVal;
- b = pNos->x.rVal;
- r = a - b;
- /* Push the result */
- pNos->x.rVal = r;
- MemObjSetType(pNos, MEMOBJ_REAL);
- /* Try to get an integer representation */
- jx9MemObjTryInteger(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 ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
- }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- jx9MemObjStore(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 JX9_OP_MOD:{
- jx9_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 ){
- jx9MemObjToInteger(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
- jx9MemObjToInteger(pNos);
- }
- /* Perform the requested operation */
- a = pNos->x.iVal;
- b = pTos->x.iVal;
- if( b == 0 ){
- r = 0;
- VmErrorFormat(&(*pVm), JX9_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 JX9_OP_MOD_STORE: {
- jx9_value *pNos = &pTos[-1];
- jx9_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 ){
- jx9MemObjToInteger(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
- jx9MemObjToInteger(pNos);
- }
- /* Perform the requested operation */
- a = pTos->x.iVal;
- b = pNos->x.iVal;
- if( b == 0 ){
- r = 0;
- VmErrorFormat(&(*pVm), JX9_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 ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
- }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- jx9MemObjStore(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 JX9_OP_DIV:{
- jx9_value *pNos = &pTos[-1];
- jx9_real a, b, r;
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- /* Force the operands to be real */
- if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pNos);
- }
- /* Perform the requested operation */
- a = pNos->x.rVal;
- b = pTos->x.rVal;
- if( b == 0 ){
- /* Division by zero */
- r = 0;
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Division by zero");
- /* goto Abort; */
- }else{
- r = a/b;
- /* Push the result */
- pNos->x.rVal = r;
- MemObjSetType(pNos, MEMOBJ_REAL);
- /* Try to get an integer representation */
- jx9MemObjTryInteger(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 JX9_OP_DIV_STORE:{
- jx9_value *pNos = &pTos[-1];
- jx9_value *pObj;
- jx9_real a, b, r;
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- /* Force the operands to be real */
- if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
- jx9MemObjToReal(pNos);
- }
- /* Perform the requested operation */
- a = pTos->x.rVal;
- b = pNos->x.rVal;
- if( b == 0 ){
- /* Division by zero */
- r = 0;
- VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd/0", a);
- /* goto Abort; */
- }else{
- r = a/b;
- /* Push the result */
- pNos->x.rVal = r;
- MemObjSetType(pNos, MEMOBJ_REAL);
- /* Try to get an integer representation */
- jx9MemObjTryInteger(pNos);
- }
- if( pTos->nIdx == SXU32_HIGH ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
- }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- jx9MemObjStore(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 JX9_OP_BAND:
- case JX9_OP_BOR:
- case JX9_OP_BXOR:{
- jx9_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 ){
- jx9MemObjToInteger(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
- jx9MemObjToInteger(pNos);
- }
- /* Perform the requested operation */
- a = pNos->x.iVal;
- b = pTos->x.iVal;
- switch(pInstr->iOp){
- case JX9_OP_BOR_STORE:
- case JX9_OP_BOR: r = a|b; break;
- case JX9_OP_BXOR_STORE:
- case JX9_OP_BXOR: r = a^b; break;
- case JX9_OP_BAND_STORE:
- case JX9_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 JX9_OP_BAND_STORE:
- case JX9_OP_BOR_STORE:
- case JX9_OP_BXOR_STORE:{
- jx9_value *pNos = &pTos[-1];
- jx9_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 ){
- jx9MemObjToInteger(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
- jx9MemObjToInteger(pNos);
- }
- /* Perform the requested operation */
- a = pTos->x.iVal;
- b = pNos->x.iVal;
- switch(pInstr->iOp){
- case JX9_OP_BOR_STORE:
- case JX9_OP_BOR: r = a|b; break;
- case JX9_OP_BXOR_STORE:
- case JX9_OP_BXOR: r = a^b; break;
- case JX9_OP_BAND_STORE:
- case JX9_OP_BAND:
- default: r = a&b; break;
- }
- /* Push the result */
- pNos->x.iVal = r;
- MemObjSetType(pNos, MEMOBJ_INT);
- if( pTos->nIdx == SXU32_HIGH ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
- }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- jx9MemObjStore(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 JX9_OP_SHL:
- case JX9_OP_SHR: {
- jx9_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 ){
- jx9MemObjToInteger(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
- jx9MemObjToInteger(pNos);
- }
- /* Perform the requested operation */
- a = pNos->x.iVal;
- b = (sxi32)pTos->x.iVal;
- if( pInstr->iOp == JX9_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 JX9_OP_SHL_STORE:
- case JX9_OP_SHR_STORE: {
- jx9_value *pNos = &pTos[-1];
- jx9_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 ){
- jx9MemObjToInteger(pTos);
- }
- if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
- jx9MemObjToInteger(pNos);
- }
- /* Perform the requested operation */
- a = pTos->x.iVal;
- b = (sxi32)pNos->x.iVal;
- if( pInstr->iOp == JX9_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 ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
- }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- jx9MemObjStore(pNos, pObj);
- }
- VmPopOperand(&pTos, 1);
- break;
- }
- /* CAT: P1 * *
- *
- * Pop P1 elements from the stack. Concatenate them togeher and push the result
- * back.
- */
- case JX9_OP_CAT:{
- jx9_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 ){
- jx9MemObjToString(pNos);
- }
- pCur = &pNos[1];
- while( pCur <= pTos ){
- if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){
- jx9MemObjToString(pCur);
- }
- /* Perform the concatenation */
- if( SyBlobLength(&pCur->sBlob) > 0 ){
- jx9MemObjStringAppend(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 JX9_OP_CAT_STORE:{
- jx9_value *pNos = &pTos[-1];
- jx9_value *pObj;
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- if((pTos->iFlags & MEMOBJ_STRING) == 0 ){
- /* Force a string cast */
- jx9MemObjToString(pTos);
- }
- if((pNos->iFlags & MEMOBJ_STRING) == 0 ){
- /* Force a string cast */
- jx9MemObjToString(pNos);
- }
- /* Perform the concatenation (Reverse order) */
- if( SyBlobLength(&pNos->sBlob) > 0 ){
- jx9MemObjStringAppend(pTos, (const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob));
- }
- /* Perform the store operation */
- if( pTos->nIdx == SXU32_HIGH ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
- }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
- jx9MemObjStore(pTos, pObj);
- }
- jx9MemObjStore(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 JX9_OP_LAND:
- case JX9_OP_LOR: {
- jx9_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 ){
- jx9MemObjToBool(pTos);
- }
- if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
- jx9MemObjToBool(pNos);
- }
- v1 = pNos->x.iVal == 0 ? 1 : 0;
- v2 = pTos->x.iVal == 0 ? 1 : 0;
- if( pInstr->iOp == JX9_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 JX9 language reference manual:
- * $a xor $b is evaluated to TRUE if either $a or $b is
- * TRUE, but not both.
- */
- case JX9_OP_LXOR:{
- jx9_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 ){
- jx9MemObjToBool(pTos);
- }
- if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
- jx9MemObjToBool(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 JX9_OP_EQ:
- case JX9_OP_NEQ: {
- jx9_value *pNos = &pTos[-1];
- /* Perform the comparison and act accordingly */
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
- if( pInstr->iOp == JX9_OP_EQ ){
- rc = rc == 0;
- }else{
- rc = rc != 0;
- }
- VmPopOperand(&pTos, 1);
- if( !pInstr->iP2 ){
- /* Push comparison result without taking the jump */
- jx9MemObjRelease(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 JX9_OP_TEQ: {
- jx9_value *pNos = &pTos[-1];
- /* Perform the comparison and act accordingly */
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) == 0;
- VmPopOperand(&pTos, 1);
- if( !pInstr->iP2 ){
- /* Push comparison result without taking the jump */
- jx9MemObjRelease(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 JX9_OP_TNE: {
- jx9_value *pNos = &pTos[-1];
- /* Perform the comparison and act accordingly */
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) != 0;
- VmPopOperand(&pTos, 1);
- if( !pInstr->iP2 ){
- /* Push comparison result without taking the jump */
- jx9MemObjRelease(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 JX9_OP_LT:
- case JX9_OP_LE: {
- jx9_value *pNos = &pTos[-1];
- /* Perform the comparison and act accordingly */
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
- if( pInstr->iOp == JX9_OP_LE ){
- rc = rc < 1;
- }else{
- rc = rc < 0;
- }
- VmPopOperand(&pTos, 1);
- if( !pInstr->iP2 ){
- /* Push comparison result without taking the jump */
- jx9MemObjRelease(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 JX9_OP_GT:
- case JX9_OP_GE: {
- jx9_value *pNos = &pTos[-1];
- /* Perform the comparison and act accordingly */
- #ifdef UNTRUST
- if( pNos < pStack ){
- goto Abort;
- }
- #endif
- rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
- if( pInstr->iOp == JX9_OP_GE ){
- rc = rc >= 0;
- }else{
- rc = rc > 0;
- }
- VmPopOperand(&pTos, 1);
- if( !pInstr->iP2 ){
- /* Push comparison result without taking the jump */
- jx9MemObjRelease(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_FOREACH_INIT * P2 P3
- * Prepare a foreach step.
- */
- case JX9_OP_FOREACH_INIT: {
- jx9_foreach_info *pInfo = (jx9_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 */
- jx9MemObjToString(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 & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){
- if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
- /* Force a string cast */
- jx9MemObjToString(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 [i.e. JSON array or object ]*/
- if( (pTos->iFlags & (MEMOBJ_HASHMAP)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){
- /* Jump out of the loop */
- if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
- "Invalid argument supplied for the foreach statement, expecting JSON array or object instance");
- }
- pc = pInstr->iP2 - 1;
- }else{
- jx9_foreach_step *pStep;
- pStep = (jx9_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_foreach_step));
- if( pStep == 0 ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 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(jx9_foreach_step));
- /* Prepare the step */
- pStep->iFlags = pInfo->iFlags;
- if( pTos->iFlags & MEMOBJ_HASHMAP ){
- jx9_hashmap *pMap = (jx9_hashmap *)pTos->x.pOther;
- /* Reset the internal loop cursor */
- jx9HashmapResetLoopCursor(pMap);
- /* Mark the step */
- pStep->pMap = pMap;
- pMap->iRef++;
- }
- }
- if( SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep) ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 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 JX9_OP_FOREACH_STEP: {
- jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
- jx9_foreach_step **apStep, *pStep;
- jx9_hashmap_node *pNode;
- jx9_hashmap *pMap;
- jx9_value *pValue;
- /* Peek the last step */
- apStep = (jx9_foreach_step **)SySetBasePtr(&pInfo->aStep);
- pStep = apStep[SySetUsed(&pInfo->aStep) - 1];
- pMap = pStep->pMap;
- /* Extract the current node value */
- pNode = jx9HashmapGetNextEntry(pMap);
- if( pNode == 0 ){
- /* No more entry to process */
- pc = pInstr->iP2 - 1; /* Jump to this destination */
- /* Automatically reset the loop cursor */
- jx9HashmapResetLoopCursor(pMap);
- /* Cleanup the mess left behind */
- SyMemBackendPoolFree(&pVm->sAllocator, pStep);
- SySetPop(&pInfo->aStep);
- jx9HashmapUnref(pMap);
- }else{
- if( (pStep->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){
- jx9_value *pKey = VmExtractMemObj(&(*pVm), &pInfo->sKey, FALSE, TRUE);
- if( pKey ){
- jx9HashmapExtractNodeKey(pNode, pKey);
- }
- }
- /* Make a copy of the entry value */
- pValue = VmExtractMemObj(&(*pVm), &pInfo->sValue, FALSE, TRUE);
- if( pValue ){
- jx9HashmapExtractNodeValue(pNode, pValue, TRUE);
- }
- }
- break;
- }
- /*
- * OP_MEMBER P1 P2
- * Load JSON object entry on the stack.
- */
- case JX9_OP_MEMBER: {
- jx9_hashmap_node *pNode = 0; /* cc warning */
- jx9_hashmap *pMap = 0;
- jx9_value *pIdx;
- pIdx = pTos;
- pTos--;
- rc = SXERR_NOTFOUND; /* Assume the index is invalid */
- if( pTos->iFlags & MEMOBJ_HASHMAP ){
- /* Point to the hashmap */
- pMap = (jx9_hashmap *)pTos->x.pOther;
- /* Load the desired entry */
- rc = jx9HashmapLookup(pMap, pIdx, &pNode);
- }
- jx9MemObjRelease(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;
- jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
- }else{
- pTos->nIdx = pNode->nValIdx;
- jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
- jx9HashmapUnref(pMap);
- }
- }else{
- /* No such entry, load NULL */
- jx9MemObjRelease(pTos);
- pTos->nIdx = SXU32_HIGH;
- }
- break;
- }
- /*
- * OP_SWITCH * * P3
- * This is the bytecode implementation of the complex switch() JX9 construct.
- */
- case JX9_OP_SWITCH: {
- jx9_switch *pSwitch = (jx9_switch *)pInstr->p3;
- jx9_case_expr *aCase, *pCase;
- jx9_value sValue, sCaseValue;
- sxu32 n, nEntry;
- #ifdef UNTRUST
- if( pSwitch == 0 || pTos < pStack ){
- goto Abort;
- }
- #endif
- /* Point to the case table */
- aCase = (jx9_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr);
- nEntry = SySetUsed(&pSwitch->aCaseExpr);
- /* Select the appropriate case block to execute */
- jx9MemObjInit(pVm, &sValue);
- jx9MemObjInit(pVm, &sCaseValue);
- for( n = 0 ; n < nEntry ; ++n ){
- pCase = &aCase[n];
- jx9MemObjLoad(pTos, &sValue);
- /* Execute the case expression first */
- VmLocalExec(pVm,&pCase->aByteCode, &sCaseValue);
- /* Compare the two expression */
- rc = jx9MemObjCmp(&sValue, &sCaseValue, FALSE, 0);
- jx9MemObjRelease(&sValue);
- jx9MemObjRelease(&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_UPLINK P1 * *
- * Link a variable to the top active VM frame.
- * This is used to implement the 'uplink' JX9 construct.
- */
- case JX9_OP_UPLINK: {
- if( pVm->pFrame->pParent ){
- jx9_value *pLink = &pTos[-pInstr->iP1+1];
- SyString sName;
- /* Perform the link */
- while( pLink <= pTos ){
- if((pLink->iFlags & MEMOBJ_STRING) == 0 ){
- /* Force a string cast */
- jx9MemObjToString(pLink);
- }
- SyStringInitFromBuf(&sName, SyBlobData(&pLink->sBlob), SyBlobLength(&pLink->sBlob));
- if( sName.nByte > 0 ){
- VmFrameLink(&(*pVm), &sName);
- }
- pLink++;
- }
- }
- VmPopOperand(&pTos, pInstr->iP1);
- break;
- }
- /*
- * OP_CALL P1 * *
- * Call a JX9 or a foreign function and push the return value of the called
- * function on the stack.
- */
- case JX9_OP_CALL: {
- jx9_value *pArg = &pTos[-pInstr->iP1];
- SyHashEntry *pEntry;
- SyString sName;
- /* Extract function name */
- if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
- /* Raise exception: Invalid function name */
- VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Invalid function name, JX9 is returning NULL.");
- /* 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 */
- jx9MemObjRelease(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 ){
- jx9_vm_func_arg *aFormalArg;
- jx9_value *pFrameStack;
- jx9_vm_func *pVmFunc;
- VmFrame *pFrame;
- jx9_value *pObj;
- VmSlot sArg;
- sxu32 n;
- pVmFunc = (jx9_vm_func *)pEntry->pUserData;
- /* Check The recursion limit */
- if( pVm->nRecursionDepth > pVm->nMaxDepth ){
- VmErrorFormat(&(*pVm), JX9_CTX_ERR,
- "Recursion limit reached while invoking user function '%z', JX9 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 */
- jx9MemObjRelease(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 = (jx9_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs);
- /* Create a new VM frame */
- rc = VmEnterFrame(&(*pVm),pVmFunc,&pFrame);
- if( rc != SXRET_OK ){
- /* Raise exception: Out of memory */
- VmErrorFormat(&(*pVm), JX9_CTX_ERR,
- "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
- &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 */
- jx9MemObjRelease(pTos);
- break;
- }
- if( SySetUsed(&pVmFunc->aStatic) > 0 ){
- jx9_vm_func_static_var *pStatic, *aStatic;
- /* Install static variables */
- aStatic = (jx9_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 */
- jx9MemObjInit(&(*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 == JX9_ABORT ){
- goto Abort;
- }
- }
- /* Make sure the given arguments are of the correct type */
- if( aFormalArg[n].nType > 0 ){
- if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){
- ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
- /* Cast to the desired type */
- if( xCast ){
- xCast(pArg);
- }
- }
- }
- /* 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 ){
- jx9MemObjStore(pArg, pObj);
- /* Insert argument index */
- sArg.nIdx = pObj->nIdx;
- sArg.pUserData = 0;
- SySetPut(&pFrame->sArg, (const void *)&sArg);
- }
- jx9MemObjRelease(pArg);
- pArg++;
- ++n;
- }
- /* 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 == JX9_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 = jx9MemObjCastMethod(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.
- */
- jx9MemObjRelease(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), JX9_CTX_ERR, "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
- &pVmFunc->sName);
- if( pInstr->iP1 > 0 ){
- VmPopOperand(&pTos, pInstr->iP1);
- }
- break;
- }
- /* Increment nesting level */
- pVm->nRecursionDepth++;
- /* Execute function body */
- rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(&pVmFunc->aByteCode), pFrameStack, -1, pTos);
- /* Decrement nesting level */
- pVm->nRecursionDepth--;
- /* Free the operand stack */
- SyMemBackendFree(&pVm->sAllocator, pFrameStack);
- /* Leave the frame */
- VmLeaveFrame(&(*pVm));
- if( rc == JX9_ABORT ){
- /* Abort processing immeditaley */
- goto Abort;
- }
- }else{
- jx9_user_func *pFunc;
- jx9_context sCtx;
- jx9_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), JX9_CTX_WARNING, "Call to undefined function '%z', JX9 is returning NULL.", &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 */
- jx9MemObjRelease(pTos);
- break;
- }
- pFunc = (jx9_user_func *)pEntry->pUserData;
- /* Start collecting function arguments */
- SySetReset(&aArg);
- while( pArg < pTos ){
- SySetPut(&aArg, (const void *)&pArg);
- pArg++;
- }
- /* Assume a null return value */
- jx9MemObjInit(&(*pVm), &sRet);
- /* Init the call context */
- VmInitCallContext(&sCtx, &(*pVm), pFunc, &sRet, 0);
- /* Call the foreign function */
- rc = pFunc->xFunc(&sCtx, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg));
- /* Release the call context */
- VmReleaseCallContext(&sCtx);
- if( rc == JX9_ABORT ){
- goto Abort;
- }
- if( pInstr->iP1 > 0 ){
- /* Pop function name and arguments */
- VmPopOperand(&pTos, pInstr->iP1);
- }
- /* Save foreign function return value */
- jx9MemObjStore(&sRet, pTos);
- jx9MemObjRelease(&sRet);
- }
- break;
- }
- /*
- * OP_CONSUME: P1 * *
- * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack.
- */
- case JX9_OP_CONSUME: {
- jx9_output_consumer *pCons = &pVm->sVmConsumer;
- jx9_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 ){
- jx9MemObjToString(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);
- /* 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 ){
- jx9MemObjRelease(pTos);
- pTos--;
- }
- return JX9_ABORT;
- }
- /*
- * Execute as much of a local JX9 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(jx9_vm *pVm, SySet *pByteCode,jx9_value *pResult)
- {
- jx9_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));
- /* Free the operand stack */
- SyMemBackendFree(&pVm->sAllocator, pStack);
- /* Execution result */
- return rc;
- }
- /*
- * Execute as much of a JX9 bytecode program as we can then return.
- * This function is a wrapper around [VmByteCodeExec()].
- * See block-comment on that function for additional information.
- */
- JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm)
- {
- /* Make sure we are ready to execute this program */
- if( pVm->nMagic != JX9_VM_RUN ){
- return pVm->nMagic == JX9_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */
- }
- /* Set the execution magic number */
- pVm->nMagic = JX9_VM_EXEC;
- /* Execute the program */
- VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pVm->pByteContainer), pVm->aOps, -1, &pVm->sExec);
- /*
- * TICKET 1433-100: Do not remove the JX9_VM_EXEC magic number
- * so that any following call to [jx9_vm_exec()] without calling
- * [jx9_vm_reset()] first would fail.
- */
- return SXRET_OK;
- }
- /*
- * Extract a memory object (i.e. a variable) from the running script.
- * This function must be called after calling jx9_vm_exec(). Otherwise
- * NULL is returned.
- */
- JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar)
- {
- jx9_value *pValue;
- if( pVm->nMagic != JX9_VM_EXEC ){
- /* call jx9_vm_exec() first */
- return 0;
- }
- /* Perform the lookup */
- pValue = VmExtractMemObj(pVm,pVar,FALSE,FALSE);
- /* Lookup result */
- return pValue;
- }
- /*
- * Invoke the installed VM output consumer callback to consume
- * the desired message.
- * Refer to the implementation of [jx9_context_output()] defined
- * in 'api.c' for additional information.
- */
- JX9_PRIVATE sxi32 jx9VmOutputConsume(
- jx9_vm *pVm, /* Target VM */
- SyString *pString /* Message to output */
- )
- {
- jx9_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);
- /* 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 [jx9_context_output_format()] defined
- * in 'api.c' for additional information.
- */
- JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(
- jx9_vm *pVm, /* Target VM */
- const char *zFormat, /* Formatted message to output */
- va_list ap /* Variable list of arguments */
- )
- {
- jx9_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);
- }
- /* Increment output length */
- pVm->nOutputLen += SyBlobLength(&sWorker);
- /* Release the working buffer */
- SyBlobRelease(&sWorker);
- return rc;
- }
- /*
- * Return a string representation of the given JX9 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 JX9_OP_DONE: zOp = "DONE "; break;
- case JX9_OP_HALT: zOp = "HALT "; break;
- case JX9_OP_LOAD: zOp = "LOAD "; break;
- case JX9_OP_LOADC: zOp = "LOADC "; break;
- case JX9_OP_LOAD_MAP: zOp = "LOAD_MAP "; break;
- case JX9_OP_LOAD_IDX: zOp = "LOAD_IDX "; break;
- case JX9_OP_NOOP: zOp = "NOOP "; break;
- case JX9_OP_JMP: zOp = "JMP "; break;
- case JX9_OP_JZ: zOp = "JZ "; break;
- case JX9_OP_JNZ: zOp = "JNZ "; break;
- case JX9_OP_POP: zOp = "POP "; break;
- case JX9_OP_CAT: zOp = "CAT "; break;
- case JX9_OP_CVT_INT: zOp = "CVT_INT "; break;
- case JX9_OP_CVT_STR: zOp = "CVT_STR "; break;
- case JX9_OP_CVT_REAL: zOp = "CVT_REAL "; break;
- case JX9_OP_CALL: zOp = "CALL "; break;
- case JX9_OP_UMINUS: zOp = "UMINUS "; break;
- case JX9_OP_UPLUS: zOp = "UPLUS "; break;
- case JX9_OP_BITNOT: zOp = "BITNOT "; break;
- case JX9_OP_LNOT: zOp = "LOGNOT "; break;
- case JX9_OP_MUL: zOp = "MUL "; break;
- case JX9_OP_DIV: zOp = "DIV "; break;
- case JX9_OP_MOD: zOp = "MOD "; break;
- case JX9_OP_ADD: zOp = "ADD "; break;
- case JX9_OP_SUB: zOp = "SUB "; break;
- case JX9_OP_SHL: zOp = "SHL "; break;
- case JX9_OP_SHR: zOp = "SHR "; break;
- case JX9_OP_LT: zOp = "LT "; break;
- case JX9_OP_LE: zOp = "LE "; break;
- case JX9_OP_GT: zOp = "GT "; break;
- case JX9_OP_GE: zOp = "GE "; break;
- case JX9_OP_EQ: zOp = "EQ "; break;
- case JX9_OP_NEQ: zOp = "NEQ "; break;
- case JX9_OP_TEQ: zOp = "TEQ "; break;
- case JX9_OP_TNE: zOp = "TNE "; break;
- case JX9_OP_BAND: zOp = "BITAND "; break;
- case JX9_OP_BXOR: zOp = "BITXOR "; break;
- case JX9_OP_BOR: zOp = "BITOR "; break;
- case JX9_OP_LAND: zOp = "LOGAND "; break;
- case JX9_OP_LOR: zOp = "LOGOR "; break;
- case JX9_OP_LXOR: zOp = "LOGXOR "; break;
- case JX9_OP_STORE: zOp = "STORE "; break;
- case JX9_OP_STORE_IDX: zOp = "STORE_IDX "; break;
- case JX9_OP_PULL: zOp = "PULL "; break;
- case JX9_OP_SWAP: zOp = "SWAP "; break;
- case JX9_OP_YIELD: zOp = "YIELD "; break;
- case JX9_OP_CVT_BOOL: zOp = "CVT_BOOL "; break;
- case JX9_OP_CVT_NULL: zOp = "CVT_NULL "; break;
- case JX9_OP_CVT_ARRAY: zOp = "CVT_JSON "; break;
- case JX9_OP_CVT_NUMC: zOp = "CVT_NUMC "; break;
- case JX9_OP_INCR: zOp = "INCR "; break;
- case JX9_OP_DECR: zOp = "DECR "; break;
- case JX9_OP_ADD_STORE: zOp = "ADD_STORE "; break;
- case JX9_OP_SUB_STORE: zOp = "SUB_STORE "; break;
- case JX9_OP_MUL_STORE: zOp = "MUL_STORE "; break;
- case JX9_OP_DIV_STORE: zOp = "DIV_STORE "; break;
- case JX9_OP_MOD_STORE: zOp = "MOD_STORE "; break;
- case JX9_OP_CAT_STORE: zOp = "CAT_STORE "; break;
- case JX9_OP_SHL_STORE: zOp = "SHL_STORE "; break;
- case JX9_OP_SHR_STORE: zOp = "SHR_STORE "; break;
- case JX9_OP_BAND_STORE: zOp = "BAND_STORE "; break;
- case JX9_OP_BOR_STORE: zOp = "BOR_STORE "; break;
- case JX9_OP_BXOR_STORE: zOp = "BXOR_STORE "; break;
- case JX9_OP_CONSUME: zOp = "CONSUME "; break;
- case JX9_OP_MEMBER: zOp = "MEMBER "; break;
- case JX9_OP_UPLINK: zOp = "UPLINK "; break;
- case JX9_OP_SWITCH: zOp = "SWITCH "; break;
- case JX9_OP_FOREACH_INIT:
- zOp = "4EACH_INIT "; break;
- case JX9_OP_FOREACH_STEP:
- zOp = "4EACH_STEP "; break;
- default:
- break;
- }
- return zOp;
- }
- /*
- * Dump JX9 bytecodes instructions to a human readable format.
- * The xConsumer() callback which is an used defined function
- * is responsible of consuming the generated dump.
- */
- JX9_PRIVATE sxi32 jx9VmDump(
- jx9_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 object body [i.e: global or function scope].
- * Refer to the implementation of [JX9_CompileConstant()] defined
- * in 'compile.c' for additional information.
- */
- JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData)
- {
- SySet *pByteCode = (SySet *)pUserData;
- /* Evaluate and expand constant value */
- VmLocalExec((jx9_vm *)SySetGetUserData(pByteCode), pByteCode, (jx9_value *)pVal);
- }
- /*
- * Section:
- * Function handling functions.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- VmFrame *pFrame;
- jx9_vm *pVm;
- /* Point to the target VM */
- pVm = pCtx->pVm;
- /* Current frame */
- pFrame = pVm->pFrame;
- if( pFrame->pParent == 0 ){
- SXUNUSED(nArg);
- SXUNUSED(apArg);
- /* Global frame, return -1 */
- jx9_result_int(pCtx, -1);
- return SXRET_OK;
- }
- /* Total number of arguments passed to the enclosing function */
- nArg = (int)SySetUsed(&pFrame->sArg);
- jx9_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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_value *pObj = 0;
- VmSlot *pSlot = 0;
- VmFrame *pFrame;
- jx9_vm *pVm;
- /* Point to the target VM */
- pVm = pCtx->pVm;
- /* Current frame */
- pFrame = pVm->pFrame;
- if( nArg < 1 || pFrame->pParent == 0 ){
- /* Global frame or Missing arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
- jx9_result_bool(pCtx, 0);
- return SXRET_OK;
- }
- /* Extract the desired index */
- nArg = jx9_value_to_int(apArg[0]);
- if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){
- /* Invalid index, return FALSE */
- jx9_result_bool(pCtx, 0);
- return SXRET_OK;
- }
- /* Extract the desired argument */
- if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0 ){
- if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0 ){
- /* Return the desired argument */
- jx9_result_value(pCtx, (jx9_value *)pObj);
- }else{
- /* No such argument, return false */
- jx9_result_bool(pCtx, 0);
- }
- }else{
- /* CAN'T HAPPEN */
- jx9_result_bool(pCtx, 0);
- }
- 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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_value *pObj = 0;
- jx9_value *pArray;
- VmFrame *pFrame;
- VmSlot *aSlot;
- sxu32 n;
- /* Point to the current frame */
- pFrame = pCtx->pVm->pFrame;
- if( pFrame->pParent == 0 ){
- /* Global frame, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
- jx9_result_bool(pCtx, 0);
- return SXRET_OK;
- }
- /* Create a new array */
- pArray = jx9_context_new_array(pCtx);
- if( pArray == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- jx9_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 = (jx9_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx);
- if( pObj ){
- jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pObj);
- }
- }
- /* Return the freshly created array */
- jx9_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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zName;
- jx9_vm *pVm;
- int nLen;
- int res;
- if( nArg < 1 ){
- /* Missing argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return SXRET_OK;
- }
- /* Point to the target VM */
- pVm = pCtx->pVm;
- /* Extract the function name */
- zName = jx9_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;
- }
- jx9_result_bool(pCtx, res);
- return SXRET_OK;
- }
- /*
- * 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.
- */
- JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue)
- {
- int res = 0;
- if( pValue->iFlags & MEMOBJ_STRING ){
- const char *zName;
- int nLen;
- /* Extract the name */
- zName = jx9_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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_vm *pVm;
- int res;
- if( nArg < 1 ){
- /* Missing arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return SXRET_OK;
- }
- /* Point to the target VM */
- pVm = pCtx->pVm;
- /* Perform the requested operation */
- res = jx9VmIsCallable(pVm, apArg[0]);
- jx9_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)
- {
- jx9_value *pArray = (jx9_value *)pUserData;
- jx9_value sName;
- sxi32 rc;
- /* Prepare the function name for insertion */
- jx9MemObjInitFromString(pArray->pVm, &sName, 0);
- jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
- /* Perform the insertion */
- rc = jx9_array_add_elem(pArray, 0/* Automatic index assign */, &sName); /* Will make it's own copy */
- jx9MemObjRelease(&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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_value *pArray;
- /* 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 = jx9_context_new_array(pCtx);
- if( pArray == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- /* Return NULL */
- jx9_result_null(pCtx);
- return SXRET_OK;
- }
- /* Fill with the appropriate information */
- SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pArray);
- /* Fill with the appropriate information */
- SyHashForEach(&pCtx->pVm->hFunction, VmHashFuncStep,pArray);
- /* Return a copy of the array array */
- jx9_result_value(pCtx, pArray);
- return SXRET_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.
- */
- JX9_PRIVATE sxi32 jx9VmCallUserFunction(
- jx9_vm *pVm, /* Target VM */
- jx9_value *pFunc, /* Callback name */
- int nArg, /* Total number of given arguments */
- jx9_value **apArg, /* Callback arguments */
- jx9_value *pResult /* Store callback return value here. NULL otherwise */
- )
- {
- jx9_value *aStack;
- VmInstr aInstr[2];
- int i;
- if((pFunc->iFlags & (MEMOBJ_STRING)) == 0 ){
- /* Don't bother processing, it's invalid anyway */
- if( pResult ){
- /* Assume a null return value */
- jx9MemObjRelease(pResult);
- }
- return SXERR_INVALID;
- }
- /* Create a new operand stack */
- aStack = VmNewOperandStack(&(*pVm), 1+nArg);
- if( aStack == 0 ){
- jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
- "JX9 is running out of memory while invoking user callback");
- if( pResult ){
- /* Assume a null return value */
- jx9MemObjRelease(pResult);
- }
- return SXERR_MEM;
- }
- /* Fill the operand stack with the given arguments */
- for( i = 0 ; i < nArg ; i++ ){
- jx9MemObjLoad(apArg[i], &aStack[i]);
- aStack[i].nIdx = apArg[i]->nIdx;
- }
- /* Push the function name */
- jx9MemObjLoad(pFunc, &aStack[i]);
- aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */
- /* Emit the CALL istruction */
- aInstr[0].iOp = JX9_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 = JX9_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);
- /* Clean up the mess left behind */
- SyMemBackendFree(&pVm->sAllocator, aStack);
- return JX9_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.
- */
- JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(
- jx9_vm *pVm, /* Target VM */
- jx9_value *pFunc, /* Callback name */
- jx9_value *pResult, /* Store callback return value here. NULL otherwise */
- ... /* 0 (Zero) or more Callback arguments */
- )
- {
- jx9_value *pArg;
- SySet aArg;
- va_list ap;
- sxi32 rc;
- SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
- /* Copy arguments one after one */
- va_start(ap, pResult);
- for(;;){
- pArg = va_arg(ap, jx9_value *);
- if( pArg == 0 ){
- break;
- }
- SySetPut(&aArg, (const void *)&pArg);
- }
- /* Call the core routine */
- rc = jx9VmCallUserFunction(&(*pVm), pFunc, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg), pResult);
- /* Cleanup */
- SySetRelease(&aArg);
- return rc;
- }
- /*
- * 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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zName;
- int nLen = 0;
- int res = 0;
- if( nArg < 1 ){
- /* Missing constant name, return FALSE */
- jx9_context_throw_error(pCtx,JX9_CTX_NOTICE,"Missing constant name");
- jx9_result_bool(pCtx, 0);
- return SXRET_OK;
- }
- /* Extract constant name */
- zName = jx9_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;
- }
- jx9_result_bool(pCtx, res);
- return SXRET_OK;
- }
- /*
- * Hash walker callback used by the [get_defined_constants()] function
- * defined below.
- */
- static int VmHashConstStep(SyHashEntry *pEntry, void *pUserData)
- {
- jx9_value *pArray = (jx9_value *)pUserData;
- jx9_value sName;
- sxi32 rc;
- /* Prepare the constant name for insertion */
- jx9MemObjInitFromString(pArray->pVm, &sName, 0);
- jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
- /* Perform the insertion */
- rc = jx9_array_add_elem(pArray, 0, &sName); /* Will make it's own copy */
- jx9MemObjRelease(&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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_value *pArray;
- /* Create the array first*/
- pArray = jx9_context_new_array(pCtx);
- if( pArray == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- /* Return NULL */
- jx9_result_null(pCtx);
- return SXRET_OK;
- }
- /* Fill the array with the defined constants */
- SyHashForEach(&pCtx->pVm->hConstant, VmHashConstStep, pArray);
- /* Return the created array */
- jx9_result_value(pCtx, pArray);
- return SXRET_OK;
- }
- /*
- * Section:
- * Random numbers/string generators.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.symisc.net
- * Status:
- * Stable.
- */
- /*
- * Generate a random 32-bit unsigned integer.
- * JX9 use it's own private PRNG which is based on the one
- * used by te SQLite3 library.
- */
- JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_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.
- * JX9 use it's own private PRNG which is based on the one used
- * by te SQLite3 library.
- */
- JX9_PRIVATE void jx9VmRandomString(jx9_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()
- * 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:
- * JX9 use it's own private PRNG which is based on the one used
- * by te SQLite3 library.
- */
- static int vm_builtin_rand(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- sxu32 iNum;
- /* Generate the random number */
- iNum = jx9VmRandomNum(pCtx->pVm);
- if( nArg > 1 ){
- sxu32 iMin, iMax;
- iMin = (sxu32)jx9_value_to_int(apArg[0]);
- iMax = (sxu32)jx9_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 */
- jx9_result_int64(pCtx, (jx9_int64)iNum);
- return SXRET_OK;
- }
- /*
- * int getrandmax(void)
- * Show largest possible random value
- * Return
- * The largest possible random value returned by rand() which is in
- * this implementation 0xFFFFFFFF.
- * Note:
- * JX9 use it's own private PRNG which is based on the one used
- * by te SQLite3 library.
- */
- static int vm_builtin_getrandmax(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- jx9_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:
- * JX9 use it's own private PRNG which is based on the one used
- * by te SQLite3 library.
- */
- static int vm_builtin_rand_str(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- char zString[1024];
- int iLen = 0x10;
- if( nArg > 0 ){
- /* Get the desired length */
- iLen = jx9_value_to_int(apArg[0]);
- if( iLen < 1 || iLen > 1024 ){
- /* Default length */
- iLen = 0x10;
- }
- }
- /* Generate the random string */
- jx9VmRandomString(pCtx->pVm, zString, iLen);
- /* Return the generated string */
- jx9_result_string(pCtx, zString, iLen); /* Will make it's own copy */
- return SXRET_OK;
- }
- /*
- * Section:
- * Language construct implementation as foreign functions.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.symisc.net
- * Status:
- * Stable.
- */
- /*
- * void print($string...)
- * Output one or more messages.
- * Parameters
- * $string
- * Message to output.
- * Return
- * NULL.
- */
- static int vm_builtin_print(jx9_context *pCtx, int nArg,jx9_value **apArg)
- {
- const char *zData;
- int nDataLen = 0;
- jx9_vm *pVm;
- int i, rc;
- /* Point to the target VM */
- pVm = pCtx->pVm;
- /* Output */
- for( i = 0 ; i < nArg ; ++i ){
- zData = jx9_value_to_string(apArg[i], &nDataLen);
- if( nDataLen > 0 ){
- rc = pVm->sVmConsumer.xConsumer((const void *)zData, (unsigned int)nDataLen, pVm->sVmConsumer.pUserData);
- /* Increment output length */
- pVm->nOutputLen += nDataLen;
- if( rc == SXERR_ABORT ){
- /* Output consumer callback request an operation abort */
- return JX9_ABORT;
- }
- }
- }
- 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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- if( nArg > 0 ){
- if( jx9_value_is_string(apArg[0]) ){
- const char *zData;
- int iLen = 0;
- /* Print exit message */
- zData = jx9_value_to_string(apArg[0], &iLen);
- jx9_context_output(pCtx, zData, iLen);
- }else if(jx9_value_is_int(apArg[0]) ){
- sxi32 iExitStatus;
- /* Record exit status code */
- iExitStatus = jx9_value_to_int(apArg[0]);
- pCtx->pVm->iExitStatus = iExitStatus;
- }
- }
- /* Abort processing immediately */
- return JX9_ABORT;
- }
- /*
- * Unset a memory object [i.e: a jx9_value].
- */
- JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm,sxu32 nObjIdx)
- {
- jx9_value *pObj;
- pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nObjIdx);
- if( pObj ){
- VmSlot sFree;
- /* Release the object */
- jx9MemObjRelease(pObj);
- /* Restore to the free list */
- sFree.nIdx = nObjIdx;
- sFree.pUserData = 0;
- SySetPut(&pVm->aFreeObj, (const void *)&sFree);
- }
- return SXRET_OK;
- }
- /*
- * string 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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zType = "null";
- if( nArg > 0 ){
- zType = jx9MemObjTypeDump(apArg[0]);
- }
- /* Return the variable type */
- jx9_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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE*/
- jx9_result_bool(pCtx, 0);
- return SXRET_OK;
- }
- jx9_result_string_format(pCtx, "resID_%#x", apArg[0]->x.pOther);
- return SXRET_OK;
- }
- /*
- * void dump(expression, ....)
- * dump — Dumps information about a variable
- * Parameters
- * One or more expression to dump.
- * Returns
- * Nothing.
- */
- static int vm_builtin_dump(jx9_context *pCtx, int nArg, jx9_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++ ){
- jx9_value *pObj = apArg[i];
- /* Reset the working buffer */
- SyBlobReset(&sDump);
- /* Dump the given expression */
- jx9MemObjDump(&sDump,pObj);
- /* Output */
- if( SyBlobLength(&sDump) > 0 ){
- jx9_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
- }
- }
- /* Release the working buffer */
- SyBlobRelease(&sDump);
- return SXRET_OK;
- }
- /*
- * Section:
- * Version, Credits and Copyright related functions.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.symisc.net
- * Stable.
- */
- /*
- * string jx9_version(void)
- * string jx9_credits(void)
- * Returns the running version of the jx9 version.
- * Parameters
- * None
- * Return
- * Current jx9 version.
- */
- static int vm_builtin_jx9_version(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- SXUNUSED(nArg);
- SXUNUSED(apArg); /* cc warning */
- /* Current engine version, signature and cipyright notice */
- jx9_result_string_format(pCtx,"%s %s, %s",JX9_VERSION,JX9_SIG,JX9_COPYRIGHT);
- return JX9_OK;
- }
- /*
- * Section:
- * URL related routines.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.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 JX9_URL_SCHEME, JX9_URL_HOST, JX9_URL_PORT, JX9_URL_USER
- * JX9_URL_PASS, JX9_URL_PATH, JX9_URL_QUERY or JX9_URL_FRAGMENT to retrieve
- * just a specific URL component as a string (except when JX9_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 JX9 engine.
- */
- static int vm_builtin_parse_url(jx9_context *pCtx, int nArg, jx9_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 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the given URI */
- zStr = jx9_value_to_string(apArg[0], &nLen);
- if( nLen < 1 ){
- /* Nothing to process, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Get a parse */
- rc = VmHttpSplitURI(&sURI, zStr, (sxu32)nLen);
- if( rc != SXRET_OK ){
- /* Malformed input, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- if( nArg > 1 ){
- int nComponent = jx9_value_to_int(apArg[1]);
- /* Refer to constant.c for constants values */
- switch(nComponent){
- case 1: /* JX9_URL_SCHEME */
- pComp = &sURI.sScheme;
- if( pComp->nByte < 1 ){
- /* No available value, return NULL */
- jx9_result_null(pCtx);
- }else{
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }
- break;
- case 2: /* JX9_URL_HOST */
- pComp = &sURI.sHost;
- if( pComp->nByte < 1 ){
- /* No available value, return NULL */
- jx9_result_null(pCtx);
- }else{
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }
- break;
- case 3: /* JX9_URL_PORT */
- pComp = &sURI.sPort;
- if( pComp->nByte < 1 ){
- /* No available value, return NULL */
- jx9_result_null(pCtx);
- }else{
- int iPort = 0;
- /* Cast the value to integer */
- SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
- jx9_result_int(pCtx, iPort);
- }
- break;
- case 4: /* JX9_URL_USER */
- pComp = &sURI.sUser;
- if( pComp->nByte < 1 ){
- /* No available value, return NULL */
- jx9_result_null(pCtx);
- }else{
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }
- break;
- case 5: /* JX9_URL_PASS */
- pComp = &sURI.sPass;
- if( pComp->nByte < 1 ){
- /* No available value, return NULL */
- jx9_result_null(pCtx);
- }else{
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }
- break;
- case 7: /* JX9_URL_QUERY */
- pComp = &sURI.sQuery;
- if( pComp->nByte < 1 ){
- /* No available value, return NULL */
- jx9_result_null(pCtx);
- }else{
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }
- break;
- case 8: /* JX9_URL_FRAGMENT */
- pComp = &sURI.sFragment;
- if( pComp->nByte < 1 ){
- /* No available value, return NULL */
- jx9_result_null(pCtx);
- }else{
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }
- break;
- case 6: /* JX9_URL_PATH */
- pComp = &sURI.sPath;
- if( pComp->nByte < 1 ){
- /* No available value, return NULL */
- jx9_result_null(pCtx);
- }else{
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }
- break;
- default:
- /* No such entry, return NULL */
- jx9_result_null(pCtx);
- break;
- }
- }else{
- jx9_value *pArray, *pValue;
- /* Return an associative array */
- pArray = jx9_context_new_array(pCtx); /* Empty array */
- pValue = jx9_context_new_scalar(pCtx); /* Array value */
- if( pArray == 0 || pValue == 0 ){
- /* Out of memory */
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "jx9 engine is running out of memory");
- /* Return false */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Fill the array */
- pComp = &sURI.sScheme;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- jx9_array_add_strkey_elem(pArray, "scheme", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- pComp = &sURI.sHost;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- jx9_array_add_strkey_elem(pArray, "host", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_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);
- jx9_value_int(pValue, iPort);
- jx9_array_add_strkey_elem(pArray, "port", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- pComp = &sURI.sUser;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- jx9_array_add_strkey_elem(pArray, "user", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- pComp = &sURI.sPass;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- jx9_array_add_strkey_elem(pArray, "pass", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- pComp = &sURI.sPath;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- jx9_array_add_strkey_elem(pArray, "path", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- pComp = &sURI.sQuery;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- jx9_array_add_strkey_elem(pArray, "query", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- pComp = &sURI.sFragment;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- jx9_array_add_strkey_elem(pArray, "fragment", pValue); /* Will make it's own copy */
- }
- /* Return the created array */
- jx9_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 JX9_OK;
- }
- /*
- * Section:
- * Array related routines.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.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 [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
- {
- jx9_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(jx9_value *pKey, jx9_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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- extract_aux_data sAux;
- jx9_hashmap *pMap;
- if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
- /* Missing/Invalid arguments, return 0 */
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target hashmap */
- pMap = (jx9_hashmap *)apArg[0]->x.pOther;
- if( pMap->nEntry < 1 ){
- /* Empty map, return 0 */
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Prepare the aux data */
- SyZero(&sAux, sizeof(extract_aux_data)-sizeof(sAux.zWorker));
- if( nArg > 1 ){
- sAux.iFlags = jx9_value_to_int(apArg[1]);
- if( nArg > 2 ){
- sAux.zPrefix = jx9_value_to_string(apArg[2], &sAux.Prefixlen);
- }
- }
- sAux.pVm = pCtx->pVm;
- /* Invoke the worker callback */
- jx9HashmapWalk(pMap, VmExtractCallback, &sAux);
- /* Number of variables successfully imported */
- jx9_result_int(pCtx, sAux.iCount);
- return JX9_OK;
- }
- /*
- * Worker callback for the [extract()] function defined
- * below.
- */
- static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
- {
- extract_aux_data *pAux = (extract_aux_data *)pUserData;
- int iFlags = pAux->iFlags;
- jx9_vm *pVm = pAux->pVm;
- jx9_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 */
- jx9MemObjToString(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 = 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 */
- jx9MemObjStore(pValue, pObj);
- /* Increment counter */
- pAux->iCount++;
- }
- return SXRET_OK;
- }
- /*
- * Compile and evaluate a JX9 chunk at run-time.
- * Refer to the include language construct implementation for more
- * information.
- */
- static sxi32 VmEvalChunk(
- jx9_vm *pVm, /* Underlying Virtual Machine */
- jx9_context *pCtx, /* Call Context */
- SyString *pChunk, /* JX9 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;
- }
- jx9ResetCodeGenerator(pVm, xErr, pErrData);
- /* Swap bytecode container */
- pByteCode = pVm->pByteContainer;
- pVm->pByteContainer = &aByteCode;
- /* Compile the chunk */
- jx9CompileScript(pVm, pChunk, iFlags);
- if( pVm->sCodeGen.nErr > 0 ){
- /* Compilation error, return false */
- if( pCtx ){
- jx9_result_bool(pCtx, 0);
- }
- }else{
- jx9_value sResult; /* Return value */
- if( SXRET_OK != jx9VmEmitInstr(pVm, JX9_OP_DONE, 0, 0, 0, 0) ){
- /* Out of memory */
- if( pCtx ){
- jx9_result_bool(pCtx, 0);
- }
- goto Cleanup;
- }
- if( bTrueReturn ){
- /* Assume a boolean true return value */
- jx9MemObjInitFromBool(pVm, &sResult, 1);
- }else{
- /* Assume a null return value */
- jx9MemObjInit(pVm, &sResult);
- }
- /* Execute the compiled chunk */
- VmLocalExec(pVm, &aByteCode, &sResult);
- if( pCtx ){
- /* Set the execution result */
- jx9_result_value(pCtx, &sResult);
- }
- jx9MemObjRelease(&sResult);
- }
- Cleanup:
- /* Cleanup the mess left behind */
- pVm->pByteContainer = pByteCode;
- SySetRelease(&aByteCode);
- return SXRET_OK;
- }
- /*
- * Check if a file path is already included.
- */
- static int VmIsIncludedFile(jx9_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.
- */
- JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_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.jx9
- * becomes
- * path\to\file.jx9
- */
- 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 JX9 script at run-time.
- * SXRET_OK is returned on sucessful evaluation.Any other return values
- * indicates failure.
- * Note that the JX9 script to evaluate can be a local or remote file.In
- * either cases the [jx9StreamReadWholeFile()] function handle all the underlying
- * operations.
- * If the [jJX9_DISABLE_BUILTIN_FUNC] compile-time directive is defined, then
- * this function is a no-op.
- * Refer to the implementation of the include(), import() language
- * constructs for more information.
- */
- static sxi32 VmExecIncludedFile(
- jx9_context *pCtx, /* Call Context */
- SyString *pPath, /* Script path or URL*/
- int IncludeOnce /* TRUE if called from import() or require_once() */
- )
- {
- sxi32 rc;
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- const jx9_io_stream *pStream;
- SyBlob sContents;
- void *pHandle;
- jx9_vm *pVm;
- int isNew;
- /* Initialize fields */
- pVm = pCtx->pVm;
- SyBlobInit(&sContents, &pVm->sAllocator);
- isNew = 0;
- /* Extract the associated stream */
- pStream = jx9VmGetStreamDevice(pVm, &pPath->zString, pPath->nByte);
- /*
- * Open the file or the URL [i.e: http://jx9.symisc.net/example/hello.jx9.txt"]
- * in a read-only mode.
- */
- pHandle = jx9StreamOpenHandle(pVm, pStream,pPath->zString, JX9_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 = jx9StreamReadWholeFile(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 */
- jx9StreamCloseHandle(pStream, pHandle);
- /* Release the working buffer */
- SyBlobRelease(&sContents);
- #else
- pCtx = 0; /* cc warning */
- pPath = 0;
- IncludeOnce = 0;
- rc = SXERR_IO;
- #endif /* JX9_DISABLE_BUILTIN_FUNC */
- return rc;
- }
- /* * include:
- * According to the JX9 reference manual.
- * The include() function includes and evaluates the specified file.
- * Files are included based on the file path given or, if none is given
- * the include_path specified.If the file isn't found in the include_path
- * include() will finally check in the calling script's own directory
- * and the current working directory before failing. The include()
- * construct will emit a warning if it cannot find a file; this is different
- * behavior from require(), which will emit a fatal error.
- * If a path is defined — whether absolute (starting with a drive letter
- * or \ on Windows, or / on Unix/Linux systems) or relative to the current
- * directory (starting with . or ..) — the include_path will be ignored altogether.
- * For example, if a filename begins with ../, the parser will look in the parent
- * directory to find the requested file.
- * When a file is included, the code it contains inherits the variable scope
- * of the line on which the include occurs. Any variables available at that line
- * in the calling file will be available within the called file, from that point forward.
- * However, all functions and objectes defined in the included file have the global scope.
- */
- static int vm_builtin_include(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- SyString sFile;
- sxi32 rc;
- if( nArg < 1 ){
- /* Nothing to evaluate, return NULL */
- jx9_result_null(pCtx);
- return SXRET_OK;
- }
- /* File to include */
- sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
- if( sFile.nByte < 1 ){
- /* Empty string, return NULL */
- jx9_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 */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
- jx9_result_bool(pCtx, 0);
- }
- return SXRET_OK;
- }
- /*
- * import:
- * According to the JX9 reference manual.
- * The import() 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_import(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- SyString sFile;
- sxi32 rc;
- if( nArg < 1 ){
- /* Nothing to evaluate, return NULL */
- jx9_result_null(pCtx);
- return SXRET_OK;
- }
- /* File to include */
- sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
- if( sFile.nByte < 1 ){
- /* Empty string, return NULL */
- jx9_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 */
- jx9_result_bool(pCtx, 1);
- return SXRET_OK;
- }
- if( rc != SXRET_OK ){
- /* Emit a warning and return false */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
- jx9_result_bool(pCtx, 0);
- }
- return SXRET_OK;
- }
- /*
- * Section:
- * Command line arguments processing.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.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 */
- jx9_value *pWorker; /* Worker variable*/
- jx9_value *pArray; /* getopt() return value */
- jx9_context *pCtx; /* Call Context */
- };
- /* Forward declaration */
- static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData);
- /*
- * Extract short or long argument option values.
- */
- static void VmExtractOptArgValue(
- jx9_value *pArray, /* getopt() return value */
- jx9_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 */
- jx9_context *pCtx, /* Call Context */
- const char *zName /* Option name */)
- {
- jx9_value_bool(pWorker, 0);
- if( !need_val ){
- /*
- * Option does not need arguments.
- * Insert the option name and a boolean FALSE.
- */
- jx9_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.
- */
- jx9_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 */
- jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
- if( zArg < zArgEnd ){ zArg++; }
- }else{
- while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
- zArg++;
- }
- /* Save the value */
- jx9_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] != '-' ){
- jx9_value *pOptArg; /* Array of option arguments */
- pOptArg = jx9_context_new_array(pCtx);
- if( pOptArg == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- }else{
- /* Insert the first value */
- jx9_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 */
- jx9_value_reset_string_cursor(pWorker);
- /* Save the value */
- jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
- /* Insert */
- jx9_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 */
- jx9_array_add_strkey_elem(pArray, (const char *)zName, pOptArg); /* Will make it's own copy */
- /* Safely release */
- jx9_context_release_value(pCtx, pOptArg);
- }
- }else{
- /* Single value */
- jx9_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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zIn, *zEnd, *zArg, *zArgIn, *zArgEnd;
- struct getopt_long_opt sLong;
- jx9_value *pArray, *pWorker;
- SyBlob *pArg;
- int nByte;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Missing/Invalid option arguments");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract option arguments */
- zIn = jx9_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 = jx9_context_new_array(pCtx);
- pWorker = jx9_context_new_scalar(pCtx);
- if( pArray == 0 || pWorker == 0 ){
- jx9_context_throw_error(pCtx,JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- if( SyBlobLength(pArg) < 1 ){
- /* Empty command line, return the empty array*/
- jx9_result_value(pCtx, pArray);
- /* Everything will be released automatically when we return
- * from this function.
- */
- return JX9_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 && jx9_value_is_json_array(apArg[1]) && jx9_array_count(apArg[1]) > 0 ){
- /* Process long options */
- jx9_array_walk(apArg[1], VmProcessLongOpt, &sLong);
- }
- /* Return the option array */
- jx9_result_value(pCtx, pArray);
- /*
- * Don't worry about freeing memory, everything will be released
- * automatically as soon we return from this foreign function.
- */
- return JX9_OK;
- }
- /*
- * Array walker callback used for processing long options values.
- */
- static int VmProcessLongOpt(jx9_value *pKey, jx9_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( !jx9_value_is_string(pValue) ){
- /* Simply ignore */
- return JX9_OK;
- }
- zOpt = jx9_value_to_string(pValue, &nByte);
- if( nByte < 1 ){
- /* Empty string, ignore */
- return JX9_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 JX9_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 JX9_OK;
- }
- /* Try to extract a value */
- VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt);
- return JX9_OK;
- }
- /*
- * 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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const unsigned char *zIn, *zEnd;
- int nByte, c, e;
- if( nArg < 1 ){
- /* Missing arguments, return null */
- jx9_result_null(pCtx);
- return JX9_OK;
- }
- /* Extract the target string */
- zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
- if( nByte < 1 ){
- /* Empty string, return null */
- jx9_result_null(pCtx);
- return JX9_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);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- }else if( c<0x00800 ){
- e = 0xC0 + ((c>>6)&0x1F);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- e = 0x80 + (c & 0x3F);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- }else if( c<0x10000 ){
- e = 0xE0 + ((c>>12)&0x0F);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- e = 0x80 + ((c>>6) & 0x3F);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- e = 0x80 + (c & 0x3F);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- }else{
- e = 0xF0 + ((c>>18) & 0x07);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- e = 0x80 + ((c>>12) & 0x3F);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- e = 0x80 + ((c>>6) & 0x3F);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- e = 0x80 + (c & 0x3F);
- jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
- }
- }
- /* All done */
- return JX9_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; } \
- }
- JX9_PRIVATE int jx9Utf8Read(
- 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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const unsigned char *zIn, *zEnd;
- int nByte, c;
- if( nArg < 1 ){
- /* Missing arguments, return null */
- jx9_result_null(pCtx);
- return JX9_OK;
- }
- /* Extract the target string */
- zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
- if( nByte < 1 ){
- /* Empty string, return null */
- jx9_result_null(pCtx);
- return JX9_OK;
- }
- zEnd = &zIn[nByte];
- /* Start the decoding process */
- while( zIn < zEnd ){
- c = jx9Utf8Read(zIn, zEnd, &zIn);
- if( c == 0x0 ){
- break;
- }
- jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
- }
- return JX9_OK;
- }
- /*
- * string json_encode(mixed $value)
- * Returns a string containing the JSON representation of value.
- * Parameters
- * $value
- * The value being encoded. Can be any type except a resource.
- * Return
- * Returns a JSON encoded string on success. FALSE otherwise
- */
- static int vm_builtin_json_encode(jx9_context *pCtx,int nArg,jx9_value **apArg)
- {
- SyBlob sBlob;
- if( nArg < 1 ){
- /* Missing arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Init the working buffer */
- SyBlobInit(&sBlob,&pCtx->pVm->sAllocator);
- /* Perform the encoding operation */
- jx9JsonSerialize(apArg[0],&sBlob);
- /* Return the serialized value */
- jx9_result_string(pCtx,(const char *)SyBlobData(&sBlob),(int)SyBlobLength(&sBlob));
- /* Cleanup */
- SyBlobRelease(&sBlob);
- /* All done */
- return JX9_OK;
- }
- /*
- * mixed json_decode(string $json)
- * Takes a JSON encoded string and converts it into a JX9 variable.
- * Parameters
- * $json
- * The json string being decoded.
- * Return
- * The value encoded in json in appropriate JX9 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(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zJSON;
- int nByte;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return NULL */
- jx9_result_null(pCtx);
- return JX9_OK;
- }
- /* Extract the JSON string */
- zJSON = jx9_value_to_string(apArg[0], &nByte);
- if( nByte < 1 ){
- /* Empty string, return NULL */
- jx9_result_null(pCtx);
- return JX9_OK;
- }
- /* Decode the raw JSON */
- jx9JsonDecode(pCtx,zJSON,nByte);
- return JX9_OK;
- }
- /* Table of built-in VM functions. */
- static const jx9_builtin_func aVmFunc[] = {
- /* JSON Encoding/Decoding */
- { "json_encode", vm_builtin_json_encode },
- { "json_decode", vm_builtin_json_decode },
- /* Functions calls */
- { "func_num_args" , vm_builtin_func_num_args },
- { "func_get_arg" , vm_builtin_func_get_arg },
- { "func_get_args" , vm_builtin_func_get_args },
- { "function_exists", vm_builtin_func_exists },
- { "is_callable" , vm_builtin_is_callable },
- { "get_defined_functions", vm_builtin_get_defined_func },
- /* Constants management */
- { "defined", vm_builtin_defined },
- { "get_defined_constants", vm_builtin_get_defined_constants },
- /* Random numbers/strings generators */
- { "rand", vm_builtin_rand },
- { "rand_str", vm_builtin_rand_str },
- { "getrandmax", vm_builtin_getrandmax },
- /* Language constructs functions */
- { "print", vm_builtin_print },
- { "exit", vm_builtin_exit },
- { "die", vm_builtin_exit },
- /* Variable handling functions */
- { "gettype", vm_builtin_gettype },
- { "get_resource_type", vm_builtin_get_resource_type},
- /* Variable dumping */
- { "dump", vm_builtin_dump },
- /* Release info */
- {"jx9_version", vm_builtin_jx9_version },
- {"jx9_credits", vm_builtin_jx9_version },
- {"jx9_info", vm_builtin_jx9_version },
- {"jx9_copyright", vm_builtin_jx9_version },
- /* hashmap */
- {"extract", vm_builtin_extract },
- /* URL related function */
- {"parse_url", vm_builtin_parse_url },
- /* UTF-8 encoding/decoding */
- {"utf8_encode", vm_builtin_utf8_encode},
- {"utf8_decode", vm_builtin_utf8_decode},
- /* Command line processing */
- {"getopt", vm_builtin_getopt },
- /* Files/URI inclusion facility */
- { "include", vm_builtin_include },
- { "import", vm_builtin_import }
- };
- /*
- * Register the built-in VM functions defined above.
- */
- static sxi32 VmRegisterSpecialFunction(jx9_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 = jx9_create_function(&(*pVm), aVmFunc[n].zName, aVmFunc[n].xFunc, &(*pVm));
- if( rc != SXRET_OK ){
- return rc;
- }
- }
- return SXRET_OK;
- }
- #ifndef JX9_DISABLE_BUILTIN_FUNC
- /*
- * Extract the IO stream device associated with a given scheme.
- * Return a pointer to an instance of jx9_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.
- */
- JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(
- jx9_vm *pVm, /* Target VM */
- const char **pzDevice, /* Full path, URI, ... */
- int nByte /* *pzDevice length*/
- )
- {
- const char *zIn, *zEnd, *zCur, *zNext;
- jx9_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 = (jx9_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 /* JX9_DISABLE_BUILTIN_FUNC */
- /*
- * Section:
- * HTTP/URI related routines.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.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(
- jx9_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;
- jx9_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), jx9VmBlobConsumer, 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), jx9VmBlobConsumer, 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((jx9_hashmap *)pGet->x.pOther,
- sName.zString, (int)sName.nByte,
- sValue.zString, (int)sValue.nByte
- );
- }
- if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){
- VmHashmapInsert((jx9_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(jx9_vm *pVm, SyBlob *pWorker, const char *zIn, sxu32 nByte)
- {
- const char *zPtr, *zDelimiter, *zEnd = &zIn[nByte];
- SyString sName, sValue;
- jx9_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), jx9VmBlobConsumer, 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), jx9VmBlobConsumer, 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((jx9_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(jx9_vm *pVm, const char *zRequest, int nByte)
- {
- SyString *pName, *pValue, sRequest; /* Raw HTTP request */
- jx9_value *pHeaderArray; /* $_HEADER superglobal (Symisc eXtension to the JX9 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 */
- jx9_vm_config(pVm,
- JX9_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 */
- jx9_vm_config(pVm,
- JX9_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 */
- jx9_vm_config(pVm,
- JX9_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;
- jx9_vm_config(pVm,
- JX9_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/jx9/path_info.jx9/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain
- * /some/stuff.
- */
- pValue = &sUri.sPath;
- jx9_vm_config(pVm,
- JX9_VM_CONFIG_SERVER_ATTR,
- "PATH_INFO",
- pValue->zString,
- pValue->nByte
- );
- jx9_vm_config(pVm,
- JX9_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 ){
- jx9_vm_config(pVm,
- JX9_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 ){
- jx9_vm_config(pVm,
- JX9_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 ){
- jx9_vm_config(pVm,
- JX9_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 ){
- jx9_vm_config(pVm,
- JX9_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 ){
- jx9_vm_config(pVm,
- JX9_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 ){
- jx9_vm_config(pVm,
- JX9_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 ){
- jx9_vm_config(pVm,
- JX9_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 ){
- jx9_vm_config(pVm,
- JX9_VM_CONFIG_SERVER_ATTR,
- "HTTP_USER_AGENT",
- pValue->zString,
- pValue->nByte
- );
- }
- /* 'JX9_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 ){
- jx9_vm_config(pVm,
- JX9_VM_CONFIG_SERVER_ATTR,
- "JX9_AUTH_DIGEST",
- pValue->zString,
- pValue->nByte
- );
- jx9_vm_config(pVm,
- JX9_VM_CONFIG_SERVER_ATTR,
- "JX9_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((jx9_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: 6042c53b2e870f191bd3e1a2a6ae032e
- * ----------------------------------------------------------
- */
- /*
- * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
- * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
- * Version 1.7.2
- * 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://jx9.symisc.net/
- */
- /* $SymiscID: vfs.c v2.1 Ubuntu 2012-12-13 00:013 stable <chm@symisc.net> $ */
- #ifndef JX9_AMALGAMATION
- #include "jx9Int.h"
- #endif
- /*
- * This file implement a virtual file systems (VFS) for the JX9 engine.
- */
- /*
- * Given a string containing the path of a file or directory, this function
- * return the parent directory's path.
- */
- JX9_PRIVATE const char * jx9ExtractDirName(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 JX9_DISABLE_BUILTIN_FUNC directive is defined.
- */
- #ifndef JX9_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 jx9Vfs_chdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xChdir == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xChdir(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_chroot(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xChroot == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xChroot(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_getcwd(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_vfs *pVfs;
- int rc;
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xGetcwd == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- jx9_result_string(pCtx, "", 0);
- /* Perform the requested operation */
- rc = pVfs->xGetcwd(pCtx);
- if( rc != JX9_OK ){
- /* Error, return FALSE */
- jx9_result_bool(pCtx, 0);
- }
- return JX9_OK;
- }
- /*
- * bool rmdir(string $directory)
- * Removes directory.
- * Parameters
- * $directory
- * The path to the directory
- * Return
- * TRUE on success or FALSE on failure.
- */
- static int jx9Vfs_rmdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xRmdir == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xRmdir(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_is_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xIsdir == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xIsdir(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_OK;
- }
- /*
- * bool mkdir(string $pathname[, int $mode = 0777])
- * 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().
- * Return
- * TRUE on success or FALSE on failure.
- */
- static int jx9Vfs_mkdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- int iRecursive = 0;
- const char *zPath;
- jx9_vfs *pVfs;
- int iMode, rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xMkdir == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- #ifdef __WINNT__
- iMode = 0;
- #else
- /* Assume UNIX */
- iMode = 0777;
- #endif
- if( nArg > 1 ){
- iMode = jx9_value_to_int(apArg[1]);
- if( nArg > 2 ){
- iRecursive = jx9_value_to_bool(apArg[2]);
- }
- }
- /* Perform the requested operation */
- rc = pVfs->xMkdir(zPath, iMode, iRecursive);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_rename(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zOld, *zNew;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xRename == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- zOld = jx9_value_to_string(apArg[0], 0);
- zNew = jx9_value_to_string(apArg[1], 0);
- rc = pVfs->xRename(zOld, zNew);
- /* IO result */
- jx9_result_bool(pCtx, rc == JX9_OK );
- return JX9_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 jx9Vfs_realpath(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xRealpath == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Set an empty string untnil the underlying OS interface change that */
- jx9_result_string(pCtx, "", 0);
- /* Perform the requested operation */
- zPath = jx9_value_to_string(apArg[0], 0);
- rc = pVfs->xRealpath(zPath, pCtx);
- if( rc != JX9_OK ){
- jx9_result_bool(pCtx, 0);
- }
- return JX9_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 jx9Vfs_sleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_vfs *pVfs;
- int rc, nSleep;
- if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xSleep == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Amount to sleep */
- nSleep = jx9_value_to_int(apArg[0]);
- if( nSleep < 0 ){
- /* Invalid value, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation (Microseconds) */
- rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC));
- if( rc != JX9_OK ){
- /* Return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Return zero */
- jx9_result_int(pCtx, 0);
- }
- return JX9_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 jx9Vfs_usleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_vfs *pVfs;
- int nSleep;
- if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
- /* Missing/Invalid argument, return immediately */
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xSleep == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS",
- jx9_function_name(pCtx)
- );
- return JX9_OK;
- }
- /* Amount to sleep */
- nSleep = jx9_value_to_int(apArg[0]);
- if( nSleep < 0 ){
- /* Invalid value, return immediately */
- return JX9_OK;
- }
- /* Perform the requested operation (Microseconds) */
- pVfs->xSleep((unsigned int)nSleep);
- return JX9_OK;
- }
- /*
- * bool unlink (string $filename)
- * Delete a file.
- * Parameters
- * $filename
- * Path to the file.
- * Return
- * TRUE on success or FALSE on failure.
- */
- static int jx9Vfs_unlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xUnlink == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xUnlink(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_chmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int iMode;
- int rc;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xChmod == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Extract the mode */
- iMode = jx9_value_to_int(apArg[1]);
- /* Perform the requested operation */
- rc = pVfs->xChmod(zPath, iMode);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_chown(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath, *zUser;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xChown == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Extract the user */
- zUser = jx9_value_to_string(apArg[1], 0);
- /* Perform the requested operation */
- rc = pVfs->xChown(zPath, zUser);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_chgrp(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath, *zGroup;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xChgrp == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Extract the user */
- zGroup = jx9_value_to_string(apArg[1], 0);
- /* Perform the requested operation */
- rc = pVfs->xChgrp(zPath, zGroup);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_disk_free_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_int64 iSize;
- jx9_vfs *pVfs;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xFreeSpace == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- iSize = pVfs->xFreeSpace(zPath);
- /* IO return value */
- jx9_result_int64(pCtx, iSize);
- return JX9_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 jx9Vfs_disk_total_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_int64 iSize;
- jx9_vfs *pVfs;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xTotalSpace == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- iSize = pVfs->xTotalSpace(zPath);
- /* IO return value */
- jx9_result_int64(pCtx, iSize);
- return JX9_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 jx9Vfs_file_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xFileExists == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xFileExists(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_file_size(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_int64 iSize;
- jx9_vfs *pVfs;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xFileSize == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- iSize = pVfs->xFileSize(zPath);
- /* IO return value */
- jx9_result_int64(pCtx, iSize);
- return JX9_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 jx9Vfs_file_atime(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_int64 iTime;
- jx9_vfs *pVfs;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xFileAtime == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- iTime = pVfs->xFileAtime(zPath);
- /* IO return value */
- jx9_result_int64(pCtx, iTime);
- return JX9_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 jx9Vfs_file_mtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_int64 iTime;
- jx9_vfs *pVfs;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xFileMtime == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- iTime = pVfs->xFileMtime(zPath);
- /* IO return value */
- jx9_result_int64(pCtx, iTime);
- return JX9_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 jx9Vfs_file_ctime(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_int64 iTime;
- jx9_vfs *pVfs;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xFileCtime == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- iTime = pVfs->xFileCtime(zPath);
- /* IO return value */
- jx9_result_int64(pCtx, iTime);
- return JX9_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 jx9Vfs_is_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xIsfile == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xIsfile(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_is_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xIslink == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xIslink(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_is_readable(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xReadable == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xReadable(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_is_writable(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xWritable == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xWritable(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_is_executable(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xExecutable == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xExecutable(zPath);
- /* IO return value */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Vfs_filetype(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- jx9_vfs *pVfs;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return 'unknown' */
- jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xFiletype == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the desired directory */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Set the empty string as the default return value */
- jx9_result_string(pCtx, "", 0);
- /* Perform the requested operation */
- pVfs->xFiletype(zPath, pCtx);
- return JX9_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 jx9Vfs_stat(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_value *pArray, *pValue;
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xStat == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Create the array and the working value */
- pArray = jx9_context_new_array(pCtx);
- pValue = jx9_context_new_scalar(pCtx);
- if( pArray == 0 || pValue == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xStat(zPath, pArray, pValue);
- if( rc != JX9_OK ){
- /* IO error, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Return the associative array */
- jx9_result_value(pCtx, pArray);
- }
- /* Don't worry about freeing memory here, everything will be released
- * automatically as soon we return from this function. */
- return JX9_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 jx9Vfs_lstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_value *pArray, *pValue;
- const char *zPath;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xlStat == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Create the array and the working value */
- pArray = jx9_context_new_array(pCtx);
- pValue = jx9_context_new_scalar(pCtx);
- if( pArray == 0 || pValue == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zPath = jx9_value_to_string(apArg[0], 0);
- /* Perform the requested operation */
- rc = pVfs->xlStat(zPath, pArray, pValue);
- if( rc != JX9_OK ){
- /* IO error, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Return the associative array */
- jx9_result_value(pCtx, pArray);
- }
- /* Don't worry about freeing memory here, everything will be released
- * automatically as soon we return from this function. */
- return JX9_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 jx9Vfs_getenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zEnv;
- jx9_vfs *pVfs;
- int iLen;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xGetenv == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the environment variable */
- zEnv = jx9_value_to_string(apArg[0], &iLen);
- /* Set a boolean FALSE as the default return value */
- jx9_result_bool(pCtx, 0);
- if( iLen < 1 ){
- /* Empty string */
- return JX9_OK;
- }
- /* Perform the requested operation */
- pVfs->xGetenv(zEnv, pCtx);
- return JX9_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 jx9Vfs_putenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zName, *zValue;
- char *zSettings, *zEnd;
- jx9_vfs *pVfs;
- int iLen, rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the setting variable */
- zSettings = (char *)jx9_value_to_string(apArg[0], &iLen);
- if( iLen < 1 ){
- /* Empty string, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_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 */
- jx9_result_bool(pCtx, 0);
- if( zSettings < zEnd ){
- zSettings[0] = '=';
- }
- return JX9_OK;
- }
- jx9_vm_config(pCtx->pVm, JX9_VM_CONFIG_ENV_ATTR, zName, zValue, (int)(zEnd-zValue));
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xSetenv == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- zSettings[0] = '=';
- return JX9_OK;
- }
- /* Perform the requested operation */
- rc = pVfs->xSetenv(zName, zValue);
- jx9_result_bool(pCtx, rc == JX9_OK );
- zSettings[0] = '=';
- return JX9_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 jx9Vfs_touch(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_int64 nTime, nAccess;
- const char *zFile;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xTouch == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- nTime = nAccess = -1;
- zFile = jx9_value_to_string(apArg[0], 0);
- if( nArg > 1 ){
- nTime = jx9_value_to_int64(apArg[1]);
- if( nArg > 2 ){
- nAccess = jx9_value_to_int64(apArg[1]);
- }else{
- nAccess = nTime;
- }
- }
- rc = pVfs->xTouch(zFile, nTime, nAccess);
- /* IO result */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_OK;
- }
- /*
- * Path processing functions that do not need access to the VFS layer
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.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 jx9Builtin_dirname(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath, *zDir;
- int iLen, iDirlen;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return the empty string */
- jx9_result_string(pCtx, "", 0);
- return JX9_OK;
- }
- /* Point to the target path */
- zPath = jx9_value_to_string(apArg[0], &iLen);
- if( iLen < 1 ){
- /* Reuturn "." */
- jx9_result_string(pCtx, ".", sizeof(char));
- return JX9_OK;
- }
- /* Perform the requested operation */
- zDir = jx9ExtractDirName(zPath, iLen, &iDirlen);
- /* Return directory name */
- jx9_result_string(pCtx, zDir, iDirlen);
- return JX9_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 jx9Builtin_basename(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath, *zBase, *zEnd;
- int c, d, iLen;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return the empty string */
- jx9_result_string(pCtx, "", 0);
- return JX9_OK;
- }
- c = d = '/';
- #ifdef __WINNT__
- d = '\\';
- #endif
- /* Point to the target path */
- zPath = jx9_value_to_string(apArg[0], &iLen);
- if( iLen < 1 ){
- /* Empty string */
- jx9_result_string(pCtx, "", 0);
- return JX9_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 && jx9_value_is_string(apArg[1]) ){
- const char *zSuffix;
- int nSuffix;
- /* Strip suffix */
- zSuffix = jx9_value_to_string(apArg[1], &nSuffix);
- if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix], zSuffix, nSuffix) == 0 ){
- zEnd -= nSuffix;
- }
- }
- /* Store the basename */
- jx9_result_string(pCtx, zBase, (int)(zEnd-zBase));
- return JX9_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 jx9Builtin_pathinfo(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zPath;
- path_info sInfo;
- SyString *pComp;
- int iLen;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid argument, return the empty string */
- jx9_result_string(pCtx, "", 0);
- return JX9_OK;
- }
- /* Point to the target path */
- zPath = jx9_value_to_string(apArg[0], &iLen);
- if( iLen < 1 ){
- /* Empty string */
- jx9_result_string(pCtx, "", 0);
- return JX9_OK;
- }
- /* Extract path info */
- ExtractPathInfo(zPath, iLen, &sInfo);
- if( nArg > 1 && jx9_value_is_int(apArg[1]) ){
- /* Return path component */
- int nComp = jx9_value_to_int(apArg[1]);
- switch(nComp){
- case 1: /* PATHINFO_DIRNAME */
- pComp = &sInfo.sDir;
- if( pComp->nByte > 0 ){
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }else{
- /* Expand the empty string */
- jx9_result_string(pCtx, "", 0);
- }
- break;
- case 2: /*PATHINFO_BASENAME*/
- pComp = &sInfo.sBasename;
- if( pComp->nByte > 0 ){
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }else{
- /* Expand the empty string */
- jx9_result_string(pCtx, "", 0);
- }
- break;
- case 3: /*PATHINFO_EXTENSION*/
- pComp = &sInfo.sExtension;
- if( pComp->nByte > 0 ){
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }else{
- /* Expand the empty string */
- jx9_result_string(pCtx, "", 0);
- }
- break;
- case 4: /*PATHINFO_FILENAME*/
- pComp = &sInfo.sFilename;
- if( pComp->nByte > 0 ){
- jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
- }else{
- /* Expand the empty string */
- jx9_result_string(pCtx, "", 0);
- }
- break;
- default:
- /* Expand the empty string */
- jx9_result_string(pCtx, "", 0);
- break;
- }
- }else{
- /* Return an associative array */
- jx9_value *pArray, *pValue;
- pArray = jx9_context_new_array(pCtx);
- pValue = jx9_context_new_scalar(pCtx);
- if( pArray == 0 || pValue == 0 ){
- /* Out of mem, return NULL */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* dirname */
- pComp = &sInfo.sDir;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- /* Perform the insertion */
- jx9_array_add_strkey_elem(pArray, "dirname", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- /* basername */
- pComp = &sInfo.sBasename;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- /* Perform the insertion */
- jx9_array_add_strkey_elem(pArray, "basename", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- /* extension */
- pComp = &sInfo.sExtension;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- /* Perform the insertion */
- jx9_array_add_strkey_elem(pArray, "extension", pValue); /* Will make it's own copy */
- }
- /* Reset the string cursor */
- jx9_value_reset_string_cursor(pValue);
- /* filename */
- pComp = &sInfo.sFilename;
- if( pComp->nByte > 0 ){
- jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
- /* Perform the insertion */
- jx9_array_add_strkey_elem(pArray, "filename", pValue); /* Will make it's own copy */
- }
- /* Return the created array */
- jx9_result_value(pCtx, pArray);
- /* Don't worry about freeing memory, everything will be released
- * automatically as soon we return from this foreign function.
- */
- }
- return JX9_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 = jx9Utf8Read(zPattern, 0, &zPattern))!=0 ){
- if( !prevEscape && c==matchAll ){
- while( (c= jx9Utf8Read(zPattern, 0, &zPattern)) == matchAll
- || c == matchOne ){
- if( c==matchOne && jx9Utf8Read(zString, 0, &zString)==0 ){
- return 0;
- }
- }
- if( c==0 ){
- return 1;
- }else if( c==esc ){
- c = jx9Utf8Read(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 = jx9Utf8Read(zString, 0, &zString))!=0 ){
- if( noCase ){
- GlogUpperToLower(c2);
- GlogUpperToLower(c);
- while( c2 != 0 && c2 != c ){
- c2 = jx9Utf8Read(zString, 0, &zString);
- GlogUpperToLower(c2);
- }
- }else{
- while( c2 != 0 && c2 != c ){
- c2 = jx9Utf8Read(zString, 0, &zString);
- }
- }
- if( c2==0 ) return 0;
- if( patternCompare(zPattern, zString, esc, noCase) ) return 1;
- }
- return 0;
- }else if( !prevEscape && c==matchOne ){
- if( jx9Utf8Read(zString, 0, &zString)==0 ){
- return 0;
- }
- }else if( c==matchSet ){
- int prior_c = 0;
- if( esc == 0 ) return 0;
- seen = 0;
- invert = 0;
- c = jx9Utf8Read(zString, 0, &zString);
- if( c==0 ) return 0;
- c2 = jx9Utf8Read(zPattern, 0, &zPattern);
- if( c2=='^' ){
- invert = 1;
- c2 = jx9Utf8Read(zPattern, 0, &zPattern);
- }
- if( c2==']' ){
- if( c==']' ) seen = 1;
- c2 = jx9Utf8Read(zPattern, 0, &zPattern);
- }
- while( c2 && c2!=']' ){
- if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
- c2 = jx9Utf8Read(zPattern, 0, &zPattern);
- if( c>=prior_c && c<=c2 ) seen = 1;
- prior_c = 0;
- }else{
- if( c==c2 ){
- seen = 1;
- }
- prior_c = c2;
- }
- c2 = jx9Utf8Read(zPattern, 0, &zPattern);
- }
- if( c2==0 || (seen ^ invert)==0 ){
- return 0;
- }
- }else if( esc==c && !prevEscape ){
- prevEscape = 1;
- }else{
- c2 = jx9Utf8Read(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 jx9Builtin_fnmatch(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zString, *zPattern;
- int iEsc = '\\';
- int noCase = 0;
- int rc;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the pattern and the string */
- zPattern = jx9_value_to_string(apArg[0], 0);
- zString = jx9_value_to_string(apArg[1], 0);
- /* Extract the flags if avaialble */
- if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
- rc = jx9_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 */
- jx9_result_bool(pCtx, rc);
- return JX9_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.
- */
- static int jx9Builtin_strglob(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zString, *zPattern;
- int iEsc = '\\';
- int rc;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the pattern and the string */
- zPattern = jx9_value_to_string(apArg[0], 0);
- zString = jx9_value_to_string(apArg[1], 0);
- /* Go globbing */
- rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, 0);
- /* Globbing result */
- jx9_result_bool(pCtx, rc);
- return JX9_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 jx9Vfs_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zTarget, *zLink;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xLink == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the given arguments */
- zTarget = jx9_value_to_string(apArg[0], 0);
- zLink = jx9_value_to_string(apArg[1], 0);
- /* Perform the requested operation */
- rc = pVfs->xLink(zTarget, zLink, 0/*Not a symbolic link */);
- /* IO result */
- jx9_result_bool(pCtx, rc == JX9_OK );
- return JX9_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 jx9Vfs_symlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zTarget, *zLink;
- jx9_vfs *pVfs;
- int rc;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xLink == 0 ){
- /* IO routine not implemented, return NULL */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
- jx9_function_name(pCtx)
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the given arguments */
- zTarget = jx9_value_to_string(apArg[0], 0);
- zLink = jx9_value_to_string(apArg[1], 0);
- /* Perform the requested operation */
- rc = pVfs->xLink(zTarget, zLink, 1/*A symbolic link */);
- /* IO result */
- jx9_result_bool(pCtx, rc == JX9_OK );
- return JX9_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 jx9Vfs_umask(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- int iOld, iNew;
- jx9_vfs *pVfs;
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xUmask == 0 ){
- /* IO routine not implemented, return -1 */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS",
- jx9_function_name(pCtx)
- );
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- iNew = 0;
- if( nArg > 0 ){
- iNew = jx9_value_to_int(apArg[0]);
- }
- /* Perform the requested operation */
- iOld = pVfs->xUmask(iNew);
- /* Old mask */
- jx9_result_int(pCtx, iOld);
- return JX9_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 jx9Vfs_sys_get_temp_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_vfs *pVfs;
- /* Set the empty string as the default return value */
- jx9_result_string(pCtx, "", 0);
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xTempDir == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- /* IO routine not implemented, return "" */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS",
- jx9_function_name(pCtx)
- );
- return JX9_OK;
- }
- /* Perform the requested operation */
- pVfs->xTempDir(pCtx);
- return JX9_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 jx9Vfs_get_current_user(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_vfs *pVfs;
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xUsername == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- /* IO routine not implemented */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS",
- jx9_function_name(pCtx)
- );
- /* Set a dummy username */
- jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
- return JX9_OK;
- }
- /* Perform the requested operation */
- pVfs->xUsername(pCtx);
- return JX9_OK;
- }
- /*
- * int64 getmypid()
- * Gets process ID.
- * Parameters
- * None
- * Return
- * Returns the process ID.
- */
- static int jx9Vfs_getmypid(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_int64 nProcessId;
- jx9_vfs *pVfs;
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xProcessId == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- /* IO routine not implemented, return -1 */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS",
- jx9_function_name(pCtx)
- );
- jx9_result_int(pCtx, -1);
- return JX9_OK;
- }
- /* Perform the requested operation */
- nProcessId = (jx9_int64)pVfs->xProcessId();
- /* Set the result */
- jx9_result_int64(pCtx, nProcessId);
- return JX9_OK;
- }
- /*
- * int getmyuid()
- * Get user ID.
- * Parameters
- * None
- * Return
- * Returns the user ID.
- */
- static int jx9Vfs_getmyuid(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_vfs *pVfs;
- int nUid;
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xUid == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- /* IO routine not implemented, return -1 */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS",
- jx9_function_name(pCtx)
- );
- jx9_result_int(pCtx, -1);
- return JX9_OK;
- }
- /* Perform the requested operation */
- nUid = pVfs->xUid();
- /* Set the result */
- jx9_result_int(pCtx, nUid);
- return JX9_OK;
- }
- /*
- * int getmygid()
- * Get group ID.
- * Parameters
- * None
- * Return
- * Returns the group ID.
- */
- static int jx9Vfs_getmygid(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_vfs *pVfs;
- int nGid;
- /* Point to the underlying vfs */
- pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
- if( pVfs == 0 || pVfs->xGid == 0 ){
- SXUNUSED(nArg); /* cc warning */
- SXUNUSED(apArg);
- /* IO routine not implemented, return -1 */
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying VFS",
- jx9_function_name(pCtx)
- );
- jx9_result_int(pCtx, -1);
- return JX9_OK;
- }
- /* Perform the requested operation */
- nGid = pVfs->xGid();
- /* Set the result */
- jx9_result_int(pCtx, nGid);
- return JX9_OK;
- }
- #ifdef __WINNT__
- #include <Windows.h>
- #elif defined(__UNIXES__)
- #include <sys/utsname.h>
- #endif
- /*
- * string 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 jx9Vfs_uname(jx9_context *pCtx, int nArg, jx9_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 && jx9_value_is_string(apArg[0]) ){
- /* Extract the desired mode */
- zMode = jx9_value_to_string(apArg[0], 0);
- }
- #if defined(__WINNT__)
- sVer.dwOSVersionInfoSize = sizeof(sVer);
- if( TRUE != GetVersionExW(&sVer)){
- jx9_result_string(pCtx, zName, -1);
- return JX9_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 8"; break;
- default: break;
- }
- }
- }
- switch(zMode[0]){
- case 's':
- /* Operating system name */
- jx9_result_string(pCtx, zName, -1/* Compute length automatically*/);
- break;
- case 'n':
- /* Host name */
- jx9_result_string(pCtx, "localhost", (int)sizeof("localhost")-1);
- break;
- case 'r':
- case 'v':
- /* Version information. */
- jx9_result_string_format(pCtx, "%u.%u build %u",
- sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
- );
- break;
- case 'm':
- /* Machine name */
- jx9_result_string(pCtx, "x86", (int)sizeof("x86")-1);
- break;
- default:
- jx9_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 ){
- jx9_result_string(pCtx, "Unix", (int)sizeof("Unix")-1);
- return JX9_OK;
- }
- switch(zMode[0]){
- case 's':
- /* Operating system name */
- jx9_result_string(pCtx, sName.sysname, -1/* Compute length automatically*/);
- break;
- case 'n':
- /* Host name */
- jx9_result_string(pCtx, sName.nodename, -1/* Compute length automatically*/);
- break;
- case 'r':
- /* Release information */
- jx9_result_string(pCtx, sName.release, -1/* Compute length automatically*/);
- break;
- case 'v':
- /* Version information. */
- jx9_result_string(pCtx, sName.version, -1/* Compute length automatically*/);
- break;
- case 'm':
- /* Machine name */
- jx9_result_string(pCtx, sName.machine, -1/* Compute length automatically*/);
- break;
- default:
- jx9_result_string_format(pCtx,
- "%s %s %s %s %s",
- sName.sysname,
- sName.release,
- sName.version,
- sName.nodename,
- sName.machine
- );
- break;
- }
- #else
- jx9_result_string(pCtx, "Host Operating System/localhost", (int)sizeof("Host Operating System/localhost")-1);
- #endif
- return JX9_OK;
- }
- /*
- * Section:
- * IO stream implementation.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.symisc.net
- * Status:
- * Stable.
- */
- typedef struct io_private io_private;
- struct io_private
- {
- const jx9_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 jx9Builtin_ftruncate(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- int rc;
- if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xTrunc == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- rc = pStream->xTrunc(pDev->pHandle, jx9_value_to_int64(apArg[1]));
- if( rc == JX9_OK ){
- /* Discard buffered data */
- ResetIOPrivate(pDev);
- }
- /* IO result */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Builtin_fseek(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- jx9_int64 iOfft;
- int whence;
- int rc;
- if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_int(pCtx, -1);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_int(pCtx, -1);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xSeek == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_int(pCtx, -1);
- return JX9_OK;
- }
- /* Extract the offset */
- iOfft = jx9_value_to_int64(apArg[1]);
- whence = 0;/* SEEK_SET */
- if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
- whence = jx9_value_to_int(apArg[2]);
- }
- /* Perform the requested operation */
- rc = pStream->xSeek(pDev->pHandle, iOfft, whence);
- if( rc == JX9_OK ){
- /* Ignore buffered data */
- ResetIOPrivate(pDev);
- }
- /* IO result */
- jx9_result_int(pCtx, rc == JX9_OK ? 0 : - 1);
- return JX9_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 jx9Builtin_ftell(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- jx9_int64 iOfft;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xTell == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- iOfft = pStream->xTell(pDev->pHandle);
- /* IO result */
- jx9_result_int64(pCtx, iOfft);
- return JX9_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 jx9Builtin_rewind(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- int rc;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xSeek == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- rc = pStream->xSeek(pDev->pHandle, 0, 0/*SEEK_SET*/);
- if( rc == JX9_OK ){
- /* Ignore buffered data */
- ResetIOPrivate(pDev);
- }
- /* IO result */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Builtin_fflush(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- int rc;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xSync == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- rc = pStream->xSync(pDev->pHandle);
- /* IO result */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Builtin_feof(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- int rc;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 1);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 1);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 1);
- return JX9_OK;
- }
- rc = SXERR_EOF;
- /* Perform the requested operation */
- if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
- /* Data is available */
- rc = JX9_OK;
- }else{
- char zBuf[4096];
- jx9_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 = JX9_OK;
- }
- }
- /* EOF or not */
- jx9_result_bool(pCtx, rc == SXERR_EOF);
- return JX9_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 jx9_int64 StreamRead(io_private *pDev, void *pBuf, jx9_int64 nLen)
- {
- const jx9_io_stream *pStream = pDev->pStream;
- char *zBuf = (char *)pBuf;
- jx9_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, jx9_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 JX9 specification */
- *pLen = (jx9_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 jx9_int64 StreamReadLine(io_private *pDev, const char **pzData, jx9_int64 nMaxLen)
- {
- const jx9_io_stream *pStream = pDev->pStream;
- char zBuf[8192];
- jx9_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 JX9 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 JX9 IO streams looks like JX9 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.
- */
- JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
- int iFlags, int use_include, jx9_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 == JX9_OK ){
- if( bPushInclude ){
- /* Mark as included */
- jx9VmPushFilePath(pVm, (const char *)SyBlobData(&sWorker), SyBlobLength(&sWorker), FALSE, pNew);
- }
- break;
- }
- /* Reset the working buffer */
- SyBlobReset(&sWorker);
- /* Check the next path */
- }
- SyBlobRelease(&sWorker);
- }
- if( rc == JX9_OK ){
- if( bPushInclude ){
- /* Mark as included */
- jx9VmPushFilePath(pVm, sFile.zString, sFile.nByte, FALSE, pNew);
- }
- }
- }else{
- /* Open the URI direcly */
- rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
- }
- if( rc != JX9_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.
- */
- JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut)
- {
- jx9_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..].
- */
- JX9_PRIVATE void jx9StreamCloseHandle(const jx9_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 jx9Builtin_fgetc(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- int c, n;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- n = (int)StreamRead(pDev, (void *)&c, sizeof(char));
- /* IO result */
- if( n < 1 ){
- /* EOF or error, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Return the string holding the character */
- jx9_result_string(pCtx, (const char *)&c, sizeof(char));
- }
- return JX9_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 jx9Builtin_fgets(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- const char *zLine;
- io_private *pDev;
- jx9_int64 n, nLen;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- nLen = -1;
- if( nArg > 1 ){
- /* Maximum data to read */
- nLen = jx9_value_to_int64(apArg[1]);
- }
- /* Perform the requested operation */
- n = StreamReadLine(pDev, &zLine, nLen);
- if( n < 1 ){
- /* EOF or IO error, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Return the freshly extracted line */
- jx9_result_string(pCtx, zLine, (int)n);
- }
- return JX9_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 jx9Builtin_fread(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- jx9_int64 nRead;
- void *pBuf;
- int nLen;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- nLen = 4096;
- if( nArg > 1 ){
- nLen = jx9_value_to_int(apArg[1]);
- if( nLen < 1 ){
- /* Invalid length, set a default length */
- nLen = 4096;
- }
- }
- /* Allocate enough buffer */
- pBuf = jx9_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE);
- if( pBuf == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- nRead = StreamRead(pDev, pBuf, (jx9_int64)nLen);
- if( nRead < 1 ){
- /* Nothing read, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Make a copy of the data just read */
- jx9_result_string(pCtx, (const char *)pBuf, (int)nRead);
- }
- /* Release the buffer */
- jx9_context_free_chunk(pCtx, pBuf);
- return JX9_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 jx9Builtin_fgetcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- const char *zLine;
- io_private *pDev;
- jx9_int64 n, nLen;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- nLen = -1;
- if( nArg > 1 ){
- /* Maximum data to read */
- nLen = jx9_value_to_int64(apArg[1]);
- }
- /* Perform the requested operation */
- n = StreamReadLine(pDev, &zLine, nLen);
- if( n < 1 ){
- /* EOF or IO error, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- jx9_value *pArray;
- int delim = ','; /* Delimiter */
- int encl = '"' ; /* Enclosure */
- int escape = '\\'; /* Escape character */
- if( nArg > 2 ){
- const char *zPtr;
- int i;
- if( jx9_value_is_string(apArg[2]) ){
- /* Extract the delimiter */
- zPtr = jx9_value_to_string(apArg[2], &i);
- if( i > 0 ){
- delim = zPtr[0];
- }
- }
- if( nArg > 3 ){
- if( jx9_value_is_string(apArg[3]) ){
- /* Extract the enclosure */
- zPtr = jx9_value_to_string(apArg[3], &i);
- if( i > 0 ){
- encl = zPtr[0];
- }
- }
- if( nArg > 4 ){
- if( jx9_value_is_string(apArg[4]) ){
- /* Extract the escape character */
- zPtr = jx9_value_to_string(apArg[4], &i);
- if( i > 0 ){
- escape = zPtr[0];
- }
- }
- }
- }
- }
- /* Create our array */
- pArray = jx9_context_new_array(pCtx);
- if( pArray == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_null(pCtx);
- return JX9_OK;
- }
- /* Parse the raw input */
- jx9ProcessCsv(zLine, (int)n, delim, encl, escape, jx9CsvConsumer, pArray);
- /* Return the freshly created array */
- jx9_result_value(pCtx, pArray);
- }
- return JX9_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 JX9 code stripped. If an error occurs, returns FALSE.
- */
- static int jx9Builtin_fgetss(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- const char *zLine;
- io_private *pDev;
- jx9_int64 n, nLen;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- nLen = -1;
- if( nArg > 1 ){
- /* Maximum data to read */
- nLen = jx9_value_to_int64(apArg[1]);
- }
- /* Perform the requested operation */
- n = StreamReadLine(pDev, &zLine, nLen);
- if( n < 1 ){
- /* EOF or IO error, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- const char *zTaglist = 0;
- int nTaglen = 0;
- if( nArg > 2 && jx9_value_is_string(apArg[2]) ){
- /* Allowed tag */
- zTaglist = jx9_value_to_string(apArg[2], &nTaglen);
- }
- /* Process data just read */
- jx9StripTagsFromString(pCtx, zLine, (int)n, zTaglist, nTaglen);
- }
- return JX9_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 jx9Builtin_readdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- int rc;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xReadDir == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- jx9_result_bool(pCtx, 0);
- /* Perform the requested operation */
- rc = pStream->xReadDir(pDev->pHandle, pCtx);
- if( rc != JX9_OK ){
- /* Return FALSE */
- jx9_result_bool(pCtx, 0);
- }
- return JX9_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 jx9Builtin_rewinddir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xRewindDir == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- pStream->xRewindDir(pDev->pHandle);
- return JX9_OK;
- }
- /* Forward declaration */
- static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut);
- static void ReleaseIOPrivate(jx9_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 jx9Builtin_closedir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xCloseDir == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- pStream->xCloseDir(pDev->pHandle);
- /* Release the private stucture */
- ReleaseIOPrivate(pCtx, pDev);
- jx9MemObjRelease(apArg[0]);
- return JX9_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 jx9Builtin_opendir(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- const char *zPath;
- io_private *pDev;
- int iLen, rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a directory path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the target path */
- zPath = jx9_value_to_string(apArg[0], &iLen);
- /* Try to extract a stream */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zPath, iLen);
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "No stream device is associated with the given path(%s)", zPath);
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- if( pStream->xOpenDir == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device",
- jx9_function_name(pCtx), pStream->zName
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Allocate a new IO private instance */
- pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
- if( pDev == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_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 != JX9_OK ){
- /* IO error, return FALSE */
- ReleaseIOPrivate(pCtx, pDev);
- jx9_result_bool(pCtx, 0);
- }else{
- /* Return the handle as a resource */
- jx9_result_resource(pCtx, pDev);
- }
- return JX9_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 jx9Builtin_readfile(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- int use_include = FALSE;
- const jx9_io_stream *pStream;
- jx9_int64 n, nRead;
- const char *zFile;
- char zBuf[8192];
- void *pHandle;
- int rc, nLen;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pStream == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- if( nArg > 1 ){
- use_include = jx9_value_to_bool(apArg[1]);
- }
- /* Try to open the file in read-only mode */
- pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY,
- use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
- if( pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- return JX9_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 = jx9_context_output(pCtx, zBuf, (int)n);
- if( rc == JX9_ABORT ){
- break;
- }
- /* Increment counter */
- nRead += n;
- }
- /* Close the stream */
- jx9StreamCloseHandle(pStream, pHandle);
- /* Total number of bytes readen */
- jx9_result_int64(pCtx, nRead);
- return JX9_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 jx9Builtin_file_get_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- jx9_int64 n, nRead, nMaxlen;
- int use_include = FALSE;
- const char *zFile;
- char zBuf[8192];
- void *pHandle;
- int nLen;
-
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pStream == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- nMaxlen = -1;
- if( nArg > 1 ){
- use_include = jx9_value_to_bool(apArg[1]);
- }
- /* Try to open the file in read-only mode */
- pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
- if( pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- if( nArg > 3 ){
- /* Extract the offset */
- n = jx9_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 = jx9_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 */
- jx9_result_string(pCtx, zBuf, (int)n);
- /* Increment read counter */
- nRead += n;
- if( nMaxlen > 0 && nRead >= nMaxlen ){
- /* Read limit reached */
- break;
- }
- }
- /* Close the stream */
- jx9StreamCloseHandle(pStream, pHandle);
- /* Check if we have read something */
- if( jx9_context_result_buf_length(pCtx) < 1 ){
- /* Nothing read, return FALSE */
- jx9_result_bool(pCtx, 0);
- }
- return JX9_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 jx9Builtin_file_put_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- int use_include = FALSE;
- const jx9_io_stream *pStream;
- const char *zFile;
- const char *zData;
- int iOpenFlags;
- void *pHandle;
- int iFlags;
- int nLen;
-
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pStream == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Data to write */
- zData = jx9_value_to_string(apArg[1], &nLen);
- if( nLen < 1 ){
- /* Nothing to write, return immediately */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Try to open the file in read-write mode */
- iOpenFlags = JX9_IO_OPEN_CREATE|JX9_IO_OPEN_RDWR|JX9_IO_OPEN_TRUNC;
- /* Extract the flags */
- iFlags = 0;
- if( nArg > 2 ){
- iFlags = jx9_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 &= ~JX9_IO_OPEN_TRUNC;
- /* Append mode */
- iOpenFlags |= JX9_IO_OPEN_APPEND;
- }
- }
- pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, iOpenFlags, use_include,
- nArg > 3 ? apArg[3] : 0, FALSE, FALSE);
- if( pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- if( pStream->xWrite ){
- jx9_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 */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Total number of bytes written */
- jx9_result_int64(pCtx, n);
- }
- }else{
- /* Read-only stream */
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR,
- "Read-only stream(%s): Cannot perform write operation",
- pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- }
- /* Close the handle */
- jx9StreamCloseHandle(pStream, pHandle);
- return JX9_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 jx9Builtin_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const char *zFile, *zPtr, *zEnd, *zBuf;
- jx9_value *pArray, *pLine;
- const jx9_io_stream *pStream;
- int use_include = 0;
- io_private *pDev;
- jx9_int64 n;
- int iFlags;
- int nLen;
-
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pStream == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Allocate a new IO private instance */
- pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
- if( pDev == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Initialize the structure */
- InitIOPrivate(pCtx->pVm, pStream, pDev);
- iFlags = 0;
- if( nArg > 1 ){
- iFlags = jx9_value_to_int(apArg[1]);
- }
- if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){
- use_include = TRUE;
- }
- /* Create the array and the working value */
- pArray = jx9_context_new_array(pCtx);
- pLine = jx9_context_new_scalar(pCtx);
- if( pArray == 0 || pLine == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Try to open the file in read-only mode */
- pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
- if( pDev->pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- /* Don't worry about freeing memory, everything will be released automatically
- * as soon we return from this function.
- */
- return JX9_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 */
- jx9_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;
- }
- }
- jx9_value_string(pLine, zBuf, (int)(zEnd-zBuf));
- /* Insert line */
- jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pLine);
- }
- /* Close the stream */
- jx9StreamCloseHandle(pStream, pDev->pHandle);
- /* Release the io_private instance */
- ReleaseIOPrivate(pCtx, pDev);
- /* Return the created array */
- jx9_result_value(pCtx, pArray);
- return JX9_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 jx9Builtin_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pSin, *pSout;
- const char *zFile;
- char zBuf[8192];
- void *pIn, *pOut;
- jx9_int64 n;
- int nLen;
- if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1])){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a source and a destination path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the source name */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pSin = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pSin == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Try to open the source file in a read-only mode */
- pIn = jx9StreamOpenHandle(pCtx->pVm, pSin, zFile, JX9_IO_OPEN_RDONLY, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
- if( pIn == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening source: '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the destination name */
- zFile = jx9_value_to_string(apArg[1], &nLen);
- /* Point to the target IO stream device */
- pSout = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pSout == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- jx9StreamCloseHandle(pSin, pIn);
- return JX9_OK;
- }
- if( pSout->xWrite == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pSin->zName
- );
- jx9_result_bool(pCtx, 0);
- jx9StreamCloseHandle(pSin, pIn);
- return JX9_OK;
- }
- /* Try to open the destination file in a read-write mode */
- pOut = jx9StreamOpenHandle(pCtx->pVm, pSout, zFile,
- JX9_IO_OPEN_CREATE|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_RDWR, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
- if( pOut == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening destination: '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- jx9StreamCloseHandle(pSin, pIn);
- return JX9_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 */
- jx9StreamCloseHandle(pSin, pIn);
- jx9StreamCloseHandle(pSout, pOut);
- /* Return TRUE */
- jx9_result_bool(pCtx, 1);
- return JX9_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 jx9Builtin_fstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- jx9_value *pArray, *pValue;
- const jx9_io_stream *pStream;
- io_private *pDev;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xStat == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Create the array and the working value */
- pArray = jx9_context_new_array(pCtx);
- pValue = jx9_context_new_scalar(pCtx);
- if( pArray == 0 || pValue == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- pStream->xStat(pDev->pHandle, pArray, pValue);
- /* Return the freshly created array */
- jx9_result_value(pCtx, pArray);
- /* Don't worry about freeing memory here, everything will be
- * released automatically as soon we return from this function.
- */
- return JX9_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 jx9Builtin_fwrite(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- const char *zString;
- io_private *pDev;
- int nLen, n;
- if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xWrite == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the data to write */
- zString = jx9_value_to_string(apArg[1], &nLen);
- if( nArg > 2 ){
- /* Maximum data length to write */
- n = jx9_value_to_int(apArg[2]);
- if( n >= 0 && n < nLen ){
- nLen = n;
- }
- }
- if( nLen < 1 ){
- /* Nothing to write */
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Perform the requested operation */
- n = (int)pStream->xWrite(pDev->pHandle, (const void *)zString, nLen);
- if( n < 0 ){
- /* IO error, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* #Bytes written */
- jx9_result_int(pCtx, n);
- }
- return JX9_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 jx9Builtin_flock(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- int nLock;
- int rc;
- if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xLock == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Requested lock operation */
- nLock = jx9_value_to_int(apArg[1]);
- /* Lock operation */
- rc = pStream->xLock(pDev->pHandle, nLock);
- /* IO result */
- jx9_result_bool(pCtx, rc == JX9_OK);
- return JX9_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 jx9Builtin_fpassthru(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- jx9_int64 n, nRead;
- char zBuf[8192];
- int rc;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_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 = jx9_context_output(pCtx, zBuf, (int)nRead /* FIXME: 64-bit issues */);
- if( rc == JX9_ABORT ){
- /* Consumer callback request an operation abort */
- break;
- }
- }
- /* Total number of bytes readen */
- jx9_result_int64(pCtx, nRead);
- return JX9_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(jx9_value *pKey, jx9_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 = jx9_value_to_string(pValue, &nLen);
- if( nLen < 1 ){
- /* Nothing to write */
- return JX9_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, (jx9_int64)nLen) < 1 ){
- SXUNUSED(pKey); /* cc warning */
- return JX9_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 JX9_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 jx9Builtin_fputcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- struct csv_data sCsv;
- io_private *pDev;
- char *zEol;
- int eolen;
- if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Missing/Invalid arguments");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 || pStream->xWrite == 0){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_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 = jx9_value_to_string(apArg[2], &n);
- if( n > 0 ){
- sCsv.delimiter = z[0];
- }
- if( nArg > 3 ){
- z = jx9_value_to_string(apArg[3], &n);
- if( n > 0 ){
- sCsv.enclosure = z[0];
- }
- }
- }
- /* Iterate throw array entries and write csv data */
- jx9_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 JX9_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 */
- jx9_int64 nCount; /* Total number of bytes written */
- };
- /*
- * Callback [i.e: Formatted input consumer] for the fprintf function.
- */
- static int fprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
- {
- fprintf_data *pFdata = (fprintf_data *)pUserData;
- jx9_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 JX9_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 jx9Builtin_fprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- fprintf_data sFdata;
- const char *zFormat;
- io_private *pDev;
- int nLen;
- if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
- /* Missing/Invalid arguments, return zero */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device",
- jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
- );
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the string format */
- zFormat = jx9_value_to_string(apArg[1], &nLen);
- if( nLen < 1 ){
- /* Empty string, return zero */
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Prepare our private data */
- sFdata.nCount = 0;
- sFdata.pIO = pDev;
- /* Format the string */
- jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, nArg - 1, &apArg[1], (void *)&sFdata, FALSE);
- /* Return total number of bytes written */
- jx9_result_int64(pCtx, sFdata.nCount);
- return JX9_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 jx9Builtin_vfprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- fprintf_data sFdata;
- const char *zFormat;
- jx9_hashmap *pMap;
- io_private *pDev;
- SySet sArg;
- int n, nLen;
- if( nArg < 3 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) || !jx9_value_is_json_array(apArg[2]) ){
- /* Missing/Invalid arguments, return zero */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device",
- jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
- );
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the string format */
- zFormat = jx9_value_to_string(apArg[1], &nLen);
- if( nLen < 1 ){
- /* Empty string, return zero */
- jx9_result_int(pCtx, 0);
- return JX9_OK;
- }
- /* Point to hashmap */
- pMap = (jx9_hashmap *)apArg[2]->x.pOther;
- /* Extract arguments from the hashmap */
- n = jx9HashmapValuesToSet(pMap, &sArg);
- /* Prepare our private data */
- sFdata.nCount = 0;
- sFdata.pIO = pDev;
- /* Format the string */
- jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&sFdata, TRUE);
- /* Return total number of bytes written*/
- jx9_result_int64(pCtx, sFdata.nCount);
- SySetRelease(&sArg);
- return JX9_OK;
- }
- /*
- * Convert open modes (string passed to the fopen() function) [i.e: 'r', 'w+', 'a', ...] into JX9 flags.
- * According to the JX9 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(jx9_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 JX9_IO_OPEN_RDONLY;
- }
- c = zMode[0];
- if( c == 'r' || c == 'R' ){
- /* Read-only access */
- iFlag = JX9_IO_OPEN_RDONLY;
- zMode++; /* Advance */
- if( zMode < zEnd ){
- c = zMode[0];
- if( c == '+' || c == 'w' || c == 'W' ){
- /* Read+Write access */
- iFlag = JX9_IO_OPEN_RDWR;
- }
- }
- }else if( c == 'w' || c == 'W' ){
- /* Overwrite mode.
- * If the file does not exists, try to create it
- */
- iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_CREATE;
- zMode++; /* Advance */
- if( zMode < zEnd ){
- c = zMode[0];
- if( c == '+' || c == 'r' || c == 'R' ){
- /* Read+Write access */
- iFlag &= ~JX9_IO_OPEN_WRONLY;
- iFlag |= JX9_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 = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_APPEND|JX9_IO_OPEN_CREATE;
- zMode++; /* Advance */
- if( zMode < zEnd ){
- c = zMode[0];
- if( c == '+' ){
- /* Read-Write access */
- iFlag &= ~JX9_IO_OPEN_WRONLY;
- iFlag |= JX9_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 = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_EXCL;
- zMode++; /* Advance */
- if( zMode < zEnd ){
- c = zMode[0];
- if( c == '+' || c == 'r' || c == 'R' ){
- /* Read-Write access */
- iFlag &= ~JX9_IO_OPEN_WRONLY;
- iFlag |= JX9_IO_OPEN_RDWR;
- }
- }
- }else if( c == 'c' || c == 'C' ){
- /* Overwrite mode.Create the file if it does not exists.*/
- iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_CREATE;
- zMode++; /* Advance */
- if( zMode < zEnd ){
- c = zMode[0];
- if( c == '+' ){
- /* Read-Write access */
- iFlag &= ~JX9_IO_OPEN_WRONLY;
- iFlag |= JX9_IO_OPEN_RDWR;
- }
- }
- }else{
- /* Invalid mode. Assume a read only open */
- jx9_context_throw_error(pCtx, JX9_CTX_NOTICE, "Invalid open mode, JX9 is assuming a Read-Only open");
- iFlag = JX9_IO_OPEN_RDONLY;
- }
- while( zMode < zEnd ){
- c = zMode[0];
- if( c == 'b' || c == 'B' ){
- iFlag &= ~JX9_IO_OPEN_TEXT;
- iFlag |= JX9_IO_OPEN_BINARY;
- }else if( c == 't' || c == 'T' ){
- iFlag &= ~JX9_IO_OPEN_BINARY;
- iFlag |= JX9_IO_OPEN_TEXT;
- }
- zMode++;
- }
- return iFlag;
- }
- /*
- * Initialize the IO private structure.
- */
- static void InitIOPrivate(jx9_vm *pVm, const jx9_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(jx9_context *pCtx, io_private *pDev)
- {
- SyBlobRelease(&pDev->sBuffer);
- pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */
- /* Release the whole structure */
- jx9_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_jx9_stream(const jx9_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 JX9 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 jx9Builtin_fopen(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- const char *zUri, *zMode;
- jx9_value *pResource;
- io_private *pDev;
- int iLen, imLen;
- int iOpenFlags;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path or URL");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the URI and the desired access mode */
- zUri = jx9_value_to_string(apArg[0], &iLen);
- if( nArg > 1 ){
- zMode = jx9_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 = jx9VmGetStreamDevice(pCtx->pVm, &zUri, iLen);
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "No stream device is associated with the given URI(%s)", zUri);
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Allocate a new IO private instance */
- pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
- if( pDev == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- pResource = 0;
- if( nArg > 3 ){
- pResource = apArg[3];
- }else if( is_jx9_stream(pStream) ){
- /* TICKET 1433-80: The jx9:// stream need a jx9_value to access the underlying
- * virtual machine.
- */
- pResource = apArg[0];
- }
- /* Initialize the structure */
- InitIOPrivate(pCtx->pVm, pStream, pDev);
- /* Convert open mode to JX9 flags */
- iOpenFlags = StrModeToFlags(pCtx, zMode, imLen);
- /* Try to get a handle */
- pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zUri, iOpenFlags,
- nArg > 2 ? jx9_value_to_bool(apArg[2]) : FALSE, pResource, FALSE, 0);
- if( pDev->pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zUri);
- jx9_result_bool(pCtx, 0);
- jx9_context_free_chunk(pCtx, pDev);
- return JX9_OK;
- }
- /* All done, return the io_private instance as a resource */
- jx9_result_resource(pCtx, pDev);
- return JX9_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 jx9Builtin_fclose(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- io_private *pDev;
- jx9_vm *pVm;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract our private data */
- pDev = (io_private *)jx9_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 */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the target IO stream device */
- pStream = pDev->pStream;
- if( pStream == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
- "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
- jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
- );
- jx9_result_bool(pCtx, 0);
- return JX9_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 */
- jx9StreamCloseHandle(pStream, pDev->pHandle);
- /* Release the IO private structure */
- ReleaseIOPrivate(pCtx, pDev);
- /* Invalidate the resource handle */
- jx9_value_release(apArg[0]);
- }
- /* Return TRUE */
- jx9_result_bool(pCtx, 1);
- return JX9_OK;
- }
- #if !defined(JX9_DISABLE_HASH_FUNC)
- /*
- * MD5/SHA1 digest consumer.
- */
- static int vfsHashConsumer(const void *pData, unsigned int nLen, void *pUserData)
- {
- /* Append hex chunk verbatim */
- jx9_result_string((jx9_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 jx9Builtin_md5_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- unsigned char zDigest[16];
- int raw_output = FALSE;
- const char *zFile;
- MD5Context sCtx;
- char zBuf[8192];
- void *pHandle;
- jx9_int64 n;
- int nLen;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pStream == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- if( nArg > 1 ){
- raw_output = jx9_value_to_bool(apArg[1]);
- }
- /* Try to open the file in read-only mode */
- pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
- if( pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- return JX9_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 */
- jx9StreamCloseHandle(pStream, pHandle);
- /* Extract the digest */
- MD5Final(zDigest, &sCtx);
- if( raw_output ){
- /* Output raw digest */
- jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
- }else{
- /* Perform a binary to hex conversion */
- SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
- }
- return JX9_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 jx9Builtin_sha1_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- unsigned char zDigest[20];
- int raw_output = FALSE;
- const char *zFile;
- SHA1Context sCtx;
- char zBuf[8192];
- void *pHandle;
- jx9_int64 n;
- int nLen;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pStream == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- if( nArg > 1 ){
- raw_output = jx9_value_to_bool(apArg[1]);
- }
- /* Try to open the file in read-only mode */
- pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
- if( pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- return JX9_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 */
- jx9StreamCloseHandle(pStream, pHandle);
- /* Extract the digest */
- SHA1Final(&sCtx, zDigest);
- if( raw_output ){
- /* Output raw digest */
- jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
- }else{
- /* Perform a binary to hex conversion */
- SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
- }
- return JX9_OK;
- }
- #endif /* JX9_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 jx9Builtin_parse_ini_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- const char *zFile;
- SyBlob sContents;
- void *pHandle;
- int nLen;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pStream == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Try to open the file in read-only mode */
- pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
- if( pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- SyBlobInit(&sContents, &pCtx->pVm->sAllocator);
- /* Read the whole file */
- jx9StreamReadWholeFile(pHandle, pStream, &sContents);
- if( SyBlobLength(&sContents) < 1 ){
- /* Empty buffer, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Process the raw INI buffer */
- jx9ParseIniString(pCtx, (const char *)SyBlobData(&sContents), SyBlobLength(&sContents),
- nArg > 1 ? jx9_value_to_bool(apArg[1]) : 0);
- }
- /* Close the stream */
- jx9StreamCloseHandle(pStream, pHandle);
- /* Release the working buffer */
- SyBlobRelease(&sContents);
- return JX9_OK;
- }
- /*
- * Section:
- * ZIP archive processing.
- * Authors:
- * Symisc Systems, devel@symisc.net.
- * Copyright (C) Symisc Systems, http://jx9.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 */
- jx9_int64 nSize; /* Map size */
- const jx9_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 jx9Builtin_zip_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- const jx9_io_stream *pStream;
- SyArchive *pArchive;
- zip_raw_data *pRaw;
- const char *zFile;
- SyBlob *pContents;
- void *pHandle;
- int nLen;
- sxi32 rc;
- if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
- /* Missing/Invalid arguments, return FALSE */
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the file path */
- zFile = jx9_value_to_string(apArg[0], &nLen);
- /* Point to the target IO stream device */
- pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
- if( pStream == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Create an in-memory archive */
- pArchive = (SyArchive *)jx9_context_alloc_chunk(pCtx, sizeof(SyArchive)+sizeof(zip_raw_data), TRUE, FALSE);
- if( pArchive == 0 ){
- jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "JX9 is running out of memory");
- jx9_result_bool(pCtx, 0);
- return JX9_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 jx9_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 = jx9ExportBuiltinVfs();
- if( pVfs && pVfs->xMmap ){
- rc = pVfs->xMmap(zFile, &pRaw->raw.mmap.pMap, &pRaw->raw.mmap.nSize);
- if( rc == JX9_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 */
- jx9_context_free_chunk(pCtx, pArchive);
- /* Something goes wrong with this ZIP archive, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_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 = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
- if( pHandle == 0 ){
- jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- pContents = &pRaw->raw.sBlob;
- SyBlobInit(pContents, &pCtx->pVm->sAllocator);
- /* Read the whole file */
- jx9StreamReadWholeFile(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 */
- jx9StreamCloseHandle(pStream, pHandle);
- if( rc != SXRET_OK ){
- /* Release the working buffer */
- SyBlobRelease(pContents);
- /* Release the allocated chunk */
- jx9_context_free_chunk(pCtx, pArchive);
- /* Something goes wrong with this ZIP archive, return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- success:
- /* Reset the loop cursor */
- SyArchiveResetLoopCursor(pArchive);
- /* Return the in-memory archive as a resource handle */
- jx9_result_resource(pCtx, pArchive);
- return JX9_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 jx9Builtin_zip_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- SyArchive *pArchive;
- zip_raw_data *pRaw;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments */
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
- return JX9_OK;
- }
- /* Point to the in-memory archive */
- pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
- /* Make sure we are dealing with a valid ZIP archive */
- if( SXARCH_INVALID(pArchive) ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
- return JX9_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 jx9_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 */
- jx9_context_free_chunk(pCtx, pArchive);
- return JX9_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 jx9Builtin_zip_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- SyArchiveEntry *pNext = 0; /* cc warning */
- SyArchive *pArchive;
- sxi32 rc;
- if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
- /* Missing/Invalid arguments */
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
- /* return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the in-memory archive */
- pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
- /* Make sure we are dealing with a valid ZIP archive */
- if( SXARCH_INVALID(pArchive) ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
- /* return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Extract the next entry */
- rc = SyArchiveGetNextEntry(pArchive, &pNext);
- if( rc != SXRET_OK ){
- /* No more entries in the central directory, return FALSE */
- jx9_result_bool(pCtx, 0);
- }else{
- /* Return as a resource handle */
- jx9_result_resource(pCtx, pNext);
- /* Point to the ZIP raw data */
- pNext->pUserData = (void *)&pArchive[1];
- }
- return JX9_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 jx9Builtin_zip_entry_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
- {
- SyArchiveEntry *pEntry;
- SyArchive *pArchive;
- if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_resource(apArg[1]) ){
- /* Missing/Invalid arguments */
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
- /* return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Point to the in-memory archive */
- pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
- /* Make sure we are dealing with a valid ZIP archive */
- if( SXARCH_INVALID(pArchive) ){
- jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
- /* return FALSE */
- jx9_result_bool(pCtx, 0);
- return JX9_OK;
- }
- /* Make sure we are dealing with a valid ZIP archive entry */
- pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[1]);
- if(