]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-9775: Committing to get assistance with building unqlite
authorShane Bryldt <astaelan@gmail.com>
Mon, 16 Jan 2017 17:33:41 +0000 (17:33 +0000)
committerMike Jerris <mike@jerris.com>
Wed, 25 Jan 2017 20:59:39 +0000 (14:59 -0600)
libs/libblade/Makefile.am
libs/libblade/src/blade_datastore.c [new file with mode: 0644]
libs/libblade/src/blade_stack.c
libs/libblade/src/include/blade.h
libs/libblade/src/include/blade_datastore.h [new file with mode: 0644]
libs/libblade/src/include/blade_types.h
libs/libblade/src/include/unqlite.h [new file with mode: 0644]
libs/libblade/src/unqlite.c [new file with mode: 0644]

index a1a3ecae11bba8d982f37dd2d0526c8554536df3..e2dde822a118c1f20bdbe244ea9a5a6563577a69 100644 (file)
@@ -6,13 +6,14 @@ AUTOMAKE_OPTIONS = subdir-objects
 AM_CFLAGS    += -I$(top_srcdir)/src -I$(top_srcdir)/src/include
 
 lib_LTLIBRARIES          = libblade.la
-libblade_la_SOURCES  = src/blade.c src/blade_stack.c src/blade_peer.c src/bpcp.c
-libblade_la_CFLAGS       = $(AM_CFLAGS) $(AM_CPPFLAGS)
+libblade_la_SOURCES  = src/blade.c src/blade_stack.c src/blade_peer.c src/bpcp.c src/blade_datastore.c src/unqlite.c
+libblade_la_CFLAGS       = $(AM_CFLAGS) $(AM_CPPFLAGS) -DUNQLITE_ENABLE_THREADS
 libblade_la_LDFLAGS  =  -version-info 0:1:0 -lncurses -lpthread -lm $(AM_LDFLAGS)
 
 library_includedir     = $(prefix)/include
 library_include_HEADERS = src/include/blade.h src/include/blade_types.h src/include/blade_stack.h src/include/blade_peer.h src/include/bpcp.h
-library_include_HEADERS += test/tap.h
+library_include_HEADERS += src/include/blade_datastore.h
+library_include_HEADERS += src/include/unqlite.h test/tap.h
 
 tests: libblade.la
        $(MAKE) -C test tests
diff --git a/libs/libblade/src/blade_datastore.c b/libs/libblade/src/blade_datastore.c
new file mode 100644 (file)
index 0000000..5798221
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2007-2014, Anthony Minessale II
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ * * 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.
+ * 
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS 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.
+ */
+
+#include "blade.h"
+
+
+typedef enum {
+       BDS_NONE = 0,
+       BDS_MYPOOL = (1 << 0),
+} bdspvt_flag_t;
+
+struct blade_datastore_s {
+       bdspvt_flag_t flags;
+       ks_pool_t *pool;
+};
+
+
+KS_DECLARE(ks_status_t) blade_datastore_destroy(blade_datastore_t **bdsP)
+{
+       blade_datastore_t *bds = NULL;
+       bdspvt_flag_t flags;
+       ks_pool_t *pool;
+
+       ks_assert(bdsP);
+
+       bds = *bdsP;
+       *bdsP = NULL;
+
+       ks_assert(bds);
+
+       flags = bds->flags;
+       pool = bds->pool;
+
+       ks_pool_free(bds->pool, &bds);
+
+       if (pool && (flags & BDS_MYPOOL)) ks_pool_close(&pool);
+
+       return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool_t *pool)
+{
+       bdspvt_flag_t newflags = BDS_NONE;
+       blade_datastore_t *bds = NULL;
+
+       if (!pool) {
+               newflags |= BDS_MYPOOL;
+               ks_pool_open(&pool);
+               ks_assert(pool);
+       }
+
+       bds = ks_pool_alloc(pool, sizeof(*bds));
+       bds->flags = newflags;
+       bds->pool = pool;
+       *bdsP = bds;
+
+       return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(void) blade_datastore_pulse(blade_datastore_t *bds, int32_t timeout)
+{
+       ks_assert(bds);
+       ks_assert(timeout >= 0);
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+ */
index 7e4bcf01d9ce19f704912081add1e0bb5f33ddbb..311d368a3aa14f8808b780ecc8beddababd7d8a2 100644 (file)
@@ -44,6 +44,7 @@ struct blade_handle_s {
        ks_pool_t *pool;
        ks_thread_pool_t *tpool;
        blade_peer_t *peer;
+       blade_datastore_t *datastore;
 };
 
 
@@ -63,6 +64,8 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP)
        flags = bh->flags;
        pool = bh->pool;
 
+       if (bh->datastore) blade_datastore_destroy(&bh->datastore);
+
        blade_peer_destroy(&bh->peer);
     if (bh->tpool && (flags & BH_MYTPOOL)) ks_thread_pool_destroy(&bh->tpool);
 
@@ -146,6 +149,17 @@ KS_DECLARE(void) blade_handle_pulse(blade_handle_t *bh, int32_t timeout)
        ks_assert(timeout >= 0);
 
        blade_peer_pulse(bh->peer, timeout);
+       if (bh->datastore) blade_datastore_pulse(bh->datastore, timeout);
+}
+
+
+KS_DECLARE(void) blade_handle_datastore_start(blade_handle_t *bh)
+{
+       ks_assert(bh);
+
+       if (bh->datastore) return;
+
+       blade_datastore_create(&bh->datastore, bh->pool);
 }
 
 
index 8df818f7b9afd07aaaf0baa1d722a49b83f0b970..4d44ea07fbb730a8a36731131a4e2cd1c01f74f2 100644 (file)
 #include <ks.h>
 #include <ks_dht.h>
 #include <sodium.h>
+#include "unqlite.h"
 #include "blade_types.h"
 #include "blade_stack.h"
 #include "blade_peer.h"
+#include "blade_datastore.h"
 #include "bpcp.h"
 
 KS_BEGIN_EXTERN_C
diff --git a/libs/libblade/src/include/blade_datastore.h b/libs/libblade/src/include/blade_datastore.h
new file mode 100644 (file)
index 0000000..f51864c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2007-2014, Anthony Minessale II
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ * * 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.
+ * 
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS 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.
+ */
+
+#ifndef _BLADE_DATASTORE_H_
+#define _BLADE_DATASTORE_H_
+#include <blade.h>
+
+KS_BEGIN_EXTERN_C
+KS_DECLARE(ks_status_t) blade_datastore_create(blade_datastore_t **bdsP, ks_pool_t *pool);
+KS_DECLARE(ks_status_t) blade_datastore_destroy(blade_datastore_t **bdsP);
+KS_DECLARE(void) blade_datastore_pulse(blade_datastore_t *bds, int32_t timeout);
+KS_END_EXTERN_C
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+ */
index fe5228b2ed16919337f9e05fd53facef6d74cd37..5367bbbf38aff27894fabecd47fbc9c49cb2f121 100644 (file)
@@ -39,6 +39,7 @@ KS_BEGIN_EXTERN_C
 
 typedef struct blade_handle_s blade_handle_t;
 typedef struct blade_peer_s blade_peer_t;
+typedef struct blade_datastore_s blade_datastore_t;
 
 KS_END_EXTERN_C
 
diff --git a/libs/libblade/src/include/unqlite.h b/libs/libblade/src/include/unqlite.h
new file mode 100644 (file)
index 0000000..dd7805e
--- /dev/null
@@ -0,0 +1,950 @@
+/* This file was automatically generated.  Do not edit (Except for compile time directives)! */ 
+#ifndef _UNQLITE_H_
+#define _UNQLITE_H_
+/*
+ * Symisc UnQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2016, Symisc Systems http://unqlite.org/
+ * Version 1.1.7
+ * 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://unqlite.org/licensing.html
+ */
+/*
+ * Copyright (C) 2012, 2016 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
+ * 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.
+ *
+ * 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: unqlite.h v1.2 Win10 2106-12-02 00:04:12 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 [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and
+ * [unqlite_lib_copyright()] for more information.
+ */
+/*
+ * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal
+ * that is the unqlite 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 UNQLITE_VERSION "1.1.7"
+/*
+ * The UNQLITE_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 [UNQLITE_VERSION].
+ */
+#define UNQLITE_VERSION_NUMBER 1001007
+/*
+ * The UNQLITE_SIG C preprocessor macro evaluates to a string
+ * literal which is the public signature of the unqlite engine.
+ * This signature could be included for example in a host-application
+ * generated Server MIME header as follows:
+ *   Server: YourWebServer/x.x unqlite/x.x.x \r\n
+ */
+#define UNQLITE_SIG "unqlite/1.1.7"
+/*
+ * UnQLite 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 unqlite.
+ */
+#define UNQLITE_IDENT "unqlite:b172a1e2c3f62fb35c8e1fb2795121f82356cad6"
+/*
+ * Copyright notice.
+ * If you have any questions about the licensing situation, please
+ * visit http://unqlite.org/licensing.html
+ * or contact Symisc Systems via:
+ *   legal@symisc.net
+ *   licensing@symisc.net
+ *   contact@symisc.net
+ */
+#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine <chm@symisc.net>] 2012-2016, http://unqlite.org/"
+
+/* Forward declaration to public objects */
+typedef struct unqlite_io_methods unqlite_io_methods;
+typedef struct unqlite_kv_methods unqlite_kv_methods;
+typedef struct unqlite_kv_engine unqlite_kv_engine;
+typedef struct jx9_io_stream unqlite_io_stream;
+typedef struct jx9_context unqlite_context;
+typedef struct jx9_value unqlite_value;
+typedef struct unqlite_vfs unqlite_vfs;
+typedef struct unqlite_vm unqlite_vm;
+typedef struct unqlite unqlite;
+/*
+ * ------------------------------
+ * Compile time directives
+ * ------------------------------
+ * For most purposes, UnQLite can be built just fine using the default compilation options.
+ * However, if required, the compile-time options documented below can be used to omit UnQLite
+ * 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.
+ *
+ * UNQLITE_ENABLE_THREADS
+ *  This option controls whether or not code is included in UnQLite to enable it to operate
+ *  safely in a multithreaded environment. The default is not. All mutexing code is omitted
+ *  and it is unsafe to use UnQLite in a multithreaded program. When compiled with the
+ *  UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program
+ *  and it is safe to share the same virtual machine and engine handle between two or more threads.
+ *  The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe()
+ *  interface.
+ *  When UnQLite has been compiled with threading support then the threading mode can be altered
+ * at run-time using the unqlite_lib_config() interface together with one of these verbs:
+ *    UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE
+ *    UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
+ *  Platforms others than Windows and UNIX systems must install their own mutex subsystem via 
+ *  unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX.
+ *  Otherwise the library is not threadsafe.
+ *  Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread).
+ *
+ * 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 UnQLite 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 is shipped with more than 312 built-in functions suitable for most purposes like 
+ *  string and INI processing, ZIP extracting, Base64 encoding/decoding, JSON encoding/decoding
+ *  and so forth.
+ *  If this directive is enabled, then all built-in Jx9 functions are omitted from the build.
+ *  Note that special functions such as db_create(), db_store(), db_fetch(), 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 UnQLite with the math library in same
+ *  Linux/BSD flavor (i.e: -lm).
+ *
+ * JX9_DISABLE_DISK_IO
+ *  If this directive is enabled, built-in VFS functions such as chdir(), mkdir(), chroot(), unlink(),
+ *  sleep(), etc. are omitted from the build.
+ *
+ * UNQLITE_ENABLE_JX9_HASH_IO
+ * If this directive is enabled, built-in hash functions such as md5(), sha1(), md5_file(), crc32(), etc.
+ * are included in the build.
+ */
+/* 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 
+/*
+ * Determine if we are dealing with WindowsCE - which has a much
+ * reduced API.
+ */
+#if defined(_WIN32_WCE)
+#ifndef __WIN_CE__
+#define __WIN_CE__
+#endif /* __WIN_CE__ */
+#endif /* _WIN32_WCE */
+#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 */
+/* 
+ * Marker for exported interfaces. 
+ */
+#define UNQLITE_APIEXPORT SX_APIEXPORT
+/*
+ * If compiling for a processor that lacks floating point
+ * support, substitute integer for floating-point.
+ */
+#ifdef UNQLITE_OMIT_FLOATING_POINT
+typedef sxi64 uqlite_real;
+#else
+typedef double unqlite_real;
+#endif
+typedef sxi64 unqlite_int64;
+/* Standard UnQLite return values */
+#define UNQLITE_OK      SXRET_OK      /* Successful result */
+/* Beginning of error codes */
+#define UNQLITE_NOMEM    SXERR_MEM     /* Out of memory */
+#define UNQLITE_ABORT    SXERR_ABORT   /* Another thread have released this instance */
+#define UNQLITE_IOERR    SXERR_IO      /* IO error */
+#define UNQLITE_CORRUPT  SXERR_CORRUPT /* Corrupt pointer */
+#define UNQLITE_LOCKED   SXERR_LOCKED  /* Forbidden Operation */ 
+#define UNQLITE_BUSY    SXERR_BUSY    /* The database file is locked */
+#define UNQLITE_DONE    SXERR_DONE    /* Operation done */
+#define UNQLITE_PERM     SXERR_PERM    /* Permission error */
+#define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */
+#define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */
+#define UNQLITE_NOOP     SXERR_NOOP     /* No such method */
+#define UNQLITE_INVALID  SXERR_INVALID  /* Invalid parameter */
+#define UNQLITE_EOF      SXERR_EOF      /* End Of Input */
+#define UNQLITE_UNKNOWN  SXERR_UNKNOWN  /* Unknown configuration option */
+#define UNQLITE_LIMIT    SXERR_LIMIT    /* Database limit reached */
+#define UNQLITE_EXISTS   SXERR_EXISTS   /* Record exists */
+#define UNQLITE_EMPTY    SXERR_EMPTY    /* Empty record */
+#define UNQLITE_COMPILE_ERR (-70)       /* Compilation error */
+#define UNQLITE_VM_ERR      (-71)       /* Virtual machine error */
+#define UNQLITE_FULL        (-73)       /* Full database (unlikely) */
+#define UNQLITE_CANTOPEN    (-74)       /* Unable to open the database file */
+#define UNQLITE_READ_ONLY   (-75)       /* Read only Key/Value storage engine */
+#define UNQLITE_LOCKERR     (-76)       /* Locking protocol error */
+/* end-of-error-codes */
+/*
+ * Database Handle Configuration Commands.
+ *
+ * The following set of constants are the available configuration verbs that can
+ * be used by the host-application to configure an UnQLite database handle.
+ * These constants must be passed as the second argument to [unqlite_config()].
+ *
+ * Each options require a variable number of arguments.
+ * The [unqlite_config()] interface will return UNQLITE_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://unqlite.org/c_api/unqlite_config.html
+ */
+#define UNQLITE_CONFIG_JX9_ERR_LOG         1  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
+#define UNQLITE_CONFIG_MAX_PAGE_CACHE      2  /* ONE ARGUMENT: int nMaxPage */
+#define UNQLITE_CONFIG_ERR_LOG             3  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
+#define UNQLITE_CONFIG_KV_ENGINE           4  /* ONE ARGUMENT: const char *zKvName */
+#define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5  /* NO ARGUMENTS */
+#define UNQLITE_CONFIG_GET_KV_NAME         6  /* ONE ARGUMENT: const char **pzPtr */
+/*
+ * UnQLite/Jx9 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 (Via UnQLite) Virtual machine.
+ * These constants must be passed as the second argument to the [unqlite_vm_config()] 
+ * interface.
+ * Each options require a variable number of arguments.
+ * The [unqlite_vm_config()] interface will return UNQLITE_OK on success, any other return
+ * value indicates failure.
+ * There are many options but the most importants are: UNQLITE_VM_CONFIG_OUTPUT which install
+ * a VM output consumer callback, UNQLITE_VM_CONFIG_HTTP_REQUEST which parse and register
+ * a HTTP request and UNQLITE_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://unqlite.org/c_api/unqlite_vm_config.html
+ */
+#define UNQLITE_VM_CONFIG_OUTPUT           1  /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
+#define UNQLITE_VM_CONFIG_IMPORT_PATH      2  /* ONE ARGUMENT: const char *zIncludePath */
+#define UNQLITE_VM_CONFIG_ERR_REPORT       3  /* NO ARGUMENTS: Report all run-time errors in the VM output */
+#define UNQLITE_VM_CONFIG_RECURSION_DEPTH  4  /* ONE ARGUMENT: int nMaxDepth */
+#define UNQLITE_VM_OUTPUT_LENGTH           5  /* ONE ARGUMENT: unsigned int *pLength */
+#define UNQLITE_VM_CONFIG_CREATE_VAR       6  /* TWO ARGUMENTS: const char *zName, unqlite_value *pValue */
+#define UNQLITE_VM_CONFIG_HTTP_REQUEST     7  /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
+#define UNQLITE_VM_CONFIG_SERVER_ATTR      8  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
+#define UNQLITE_VM_CONFIG_ENV_ATTR         9  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
+#define UNQLITE_VM_CONFIG_EXEC_VALUE      10  /* ONE ARGUMENT: unqlite_value **ppValue */
+#define UNQLITE_VM_CONFIG_IO_STREAM       11  /* ONE ARGUMENT: const unqlite_io_stream *pStream */
+#define UNQLITE_VM_CONFIG_ARGV_ENTRY      12  /* ONE ARGUMENT: const char *zValue */
+#define UNQLITE_VM_CONFIG_EXTRACT_OUTPUT  13  /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
+/*
+ * Storage engine configuration commands.
+ *
+ * The following set of constants are the available configuration verbs that can
+ * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree).
+ * These constants must be passed as the first argument to [unqlite_kv_config()].
+ * Each options require a variable number of arguments.
+ * The [unqlite_kv_config()] interface will return UNQLITE_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://unqlite.org/c_api/unqlite_kv_config.html
+ */
+#define UNQLITE_KV_CONFIG_HASH_FUNC  1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */
+#define UNQLITE_KV_CONFIG_CMP_FUNC   2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */
+/*
+ * 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 [unqlite_lib_config()].
+ *
+ * Each options require a variable number of arguments.
+ * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return
+ * value indicates failure.
+ * Notes:
+ * The default configuration is recommended for most applications and so the call to
+ * [unqlite_lib_config()] is usually not necessary. It is provided to support rare 
+ * applications with unusual needs. 
+ * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that
+ * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()]
+ * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library
+ * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown
+ * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()]
+ * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED.
+ * For a full discussion on the configuration verbs and their expected parameters, please
+ * refer to this page:
+ *      http://unqlite.org/c_api/unqlite_lib.html
+ */
+#define UNQLITE_LIB_CONFIG_USER_MALLOC            1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ 
+#define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK       2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
+#define UNQLITE_LIB_CONFIG_USER_MUTEX             3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ 
+#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE    4 /* NO ARGUMENTS */ 
+#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI     5 /* NO ARGUMENTS */ 
+#define UNQLITE_LIB_CONFIG_VFS                    6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */
+#define UNQLITE_LIB_CONFIG_STORAGE_ENGINE         7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */
+#define UNQLITE_LIB_CONFIG_PAGE_SIZE              8 /* ONE ARGUMENT: int iPageSize */
+/*
+ * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface
+ * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object.
+ */
+#define UNQLITE_OPEN_READONLY         0x00000001  /* Read only mode. Ok for [unqlite_open] */
+#define UNQLITE_OPEN_READWRITE        0x00000002  /* Ok for [unqlite_open] */
+#define UNQLITE_OPEN_CREATE           0x00000004  /* Ok for [unqlite_open] */
+#define UNQLITE_OPEN_EXCLUSIVE        0x00000008  /* VFS only */
+#define UNQLITE_OPEN_TEMP_DB          0x00000010  /* VFS only */
+#define UNQLITE_OPEN_NOMUTEX          0x00000020  /* Ok for [unqlite_open] */
+#define UNQLITE_OPEN_OMIT_JOURNALING  0x00000040  /* Omit journaling for this database. Ok for [unqlite_open] */
+#define UNQLITE_OPEN_IN_MEMORY        0x00000080  /* An in memory database. Ok for [unqlite_open]*/
+#define UNQLITE_OPEN_MMAP             0x00000100  /* Obtain a memory view of the whole file. Ok for [unqlite_open] */
+/*
+ * Synchronization Type Flags
+ *
+ * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses
+ * a combination of these integer values as the second argument.
+ *
+ * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only
+ * needs to flush data to mass storage.  Inode information need not be flushed.
+ * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal
+ * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use
+ * Mac OS X style fullsync instead of fsync().
+ */
+#define UNQLITE_SYNC_NORMAL        0x00002
+#define UNQLITE_SYNC_FULL          0x00003
+#define UNQLITE_SYNC_DATAONLY      0x00010
+/*
+ * File Locking Levels
+ *
+ * UnQLite uses one of these integer values as the second
+ * argument to calls it makes to the xLock() and xUnlock() methods
+ * of an [unqlite_io_methods] object.
+ */
+#define UNQLITE_LOCK_NONE          0
+#define UNQLITE_LOCK_SHARED        1
+#define UNQLITE_LOCK_RESERVED      2
+#define UNQLITE_LOCK_PENDING       3
+#define UNQLITE_LOCK_EXCLUSIVE     4
+/*
+ * CAPIREF: OS Interface: Open File Handle
+ *
+ * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface
+ * layer.
+ * Individual OS interface implementations will want to subclass this object by appending
+ * additional fields for their own use. The pMethods entry is a pointer to an
+ * [unqlite_io_methods] object that defines methods for performing
+ * I/O operations on the open file.
+*/
+typedef struct unqlite_file unqlite_file;
+struct unqlite_file {
+  const unqlite_io_methods *pMethods;  /* Methods for an open file. MUST BE FIRST */
+};
+/*
+ * CAPIREF: OS Interface: File Methods Object
+ *
+ * Every file opened by the [unqlite_vfs] xOpen method populates an
+ * [unqlite_file] object (or, more commonly, a subclass of the
+ * [unqlite_file] object) with a pointer to an instance of this object.
+ * This object defines the methods used to perform various operations
+ * against the open file represented by the [unqlite_file] object.
+ *
+ * If the xOpen method sets the unqlite_file.pMethods element 
+ * to a non-NULL pointer, then the unqlite_io_methods.xClose method
+ * may be invoked even if the xOpen reported that it failed.  The
+ * only way to prevent a call to xClose following a failed xOpen
+ * is for the xOpen to set the unqlite_file.pMethods element to NULL.
+ *
+ * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or
+ * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync().
+ * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY]
+ * flag may be ORed in to indicate that only the data of the file
+ * and not its inode needs to be synced.
+ *
+ * The integer values to xLock() and xUnlock() are one of
+ *
+ * UNQLITE_LOCK_NONE
+ * UNQLITE_LOCK_SHARED
+ * UNQLITE_LOCK_RESERVED
+ * UNQLITE_LOCK_PENDING
+ * UNQLITE_LOCK_EXCLUSIVE
+ * 
+ * xLock() increases the lock. xUnlock() decreases the lock.
+ * The xCheckReservedLock() method checks whether any database connection,
+ * either in this process or in some other process, is holding a RESERVED,
+ * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists
+ * and false otherwise.
+ * 
+ * The xSectorSize() method returns the sector size of the device that underlies
+ * the file. The sector size is the minimum write that can be performed without
+ * disturbing other bytes in the file.
+ *
+ */
+struct unqlite_io_methods {
+  int iVersion;                 /* Structure version number (currently 1) */
+  int (*xClose)(unqlite_file*);
+  int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
+  int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
+  int (*xTruncate)(unqlite_file*, unqlite_int64 size);
+  int (*xSync)(unqlite_file*, int flags);
+  int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize);
+  int (*xLock)(unqlite_file*, int);
+  int (*xUnlock)(unqlite_file*, int);
+  int (*xCheckReservedLock)(unqlite_file*, int *pResOut);
+  int (*xSectorSize)(unqlite_file*);
+};
+/*
+ * CAPIREF: OS Interface Object
+ *
+ * An instance of the unqlite_vfs object defines the interface between
+ * the UnQLite core and the underlying operating system.  The "vfs"
+ * in the name of the object stands for "Virtual File System".
+ *
+ * Only a single vfs can be registered within the UnQLite core.
+ * Vfs registration is done using the [unqlite_lib_config()] interface
+ * with a configuration verb set to UNQLITE_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 UnQLite
+ * come with a built-in vfs for these platforms that implements most the methods
+ * defined below.
+ *
+ * Clients running on exotic systems (ie: Other than Windows and UNIX systems)
+ * must register their own vfs in order to be able to use the UnQLite library.
+ *
+ * The value of the iVersion field is initially 1 but may be larger in
+ * future versions of UnQLite. 
+ *
+ * The szOsFile field is the size of the subclassed [unqlite_file] structure
+ * used by this VFS. mxPathname is the maximum length of a pathname in this VFS.
+ * 
+ * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file]
+ * structure passed as the third argument to xOpen. The xOpen method does not have to
+ * allocate the structure; it should just fill it in. Note that the xOpen method must
+ * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL.
+ * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods
+ * element will be valid after xOpen returns regardless of the success or failure of the
+ * xOpen call.
+ *
+ */
+struct unqlite_vfs {
+  const char *zName;       /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */
+  int iVersion;            /* Structure version number (currently 1) */
+  int szOsFile;            /* Size of subclassed unqlite_file */
+  int mxPathname;          /* Maximum file pathname length */
+  int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags);
+  int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir);
+  int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut);
+  int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf);
+  int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len);
+  int (*xSleep)(unqlite_vfs*, int microseconds);
+  int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut);
+  int (*xGetLastError)(unqlite_vfs*, int, char *);
+};
+/*
+ * Flags for the xAccess VFS method
+ *
+ * These integer constants can be used as the third parameter to
+ * the xAccess method of an [unqlite_vfs] object.  They determine
+ * what kind of permissions the xAccess method is looking for.
+ * With UNQLITE_ACCESS_EXISTS, the xAccess method
+ * simply checks whether the file exists.
+ * With UNQLITE_ACCESS_READWRITE, the xAccess method
+ * checks whether the named directory is both readable and writable
+ * (in other words, if files can be added, removed, and renamed within
+ * the directory).
+ * The UNQLITE_ACCESS_READWRITE constant is currently used only by the
+ * [temp_store_directory pragma], though this could change in a future
+ * release of UnQLite.
+ * With UNQLITE_ACCESS_READ, the xAccess method
+ * checks whether the file is readable.  The UNQLITE_ACCESS_READ constant is
+ * currently unused, though it might be used in a future release of
+ * UnQLite.
+ */
+#define UNQLITE_ACCESS_EXISTS    0
+#define UNQLITE_ACCESS_READWRITE 1   
+#define UNQLITE_ACCESS_READ      2 
+/*
+ * The type used to represent a page number.  The first page in a file
+ * is called page 1.  0 is used to represent "not a page".
+ * A page number is an unsigned 64-bit integer.
+ */
+typedef sxu64 pgno;
+/*
+ * A database disk page is represented by an instance
+ * of the follwoing structure.
+ */
+typedef struct unqlite_page unqlite_page;
+struct unqlite_page
+{
+  unsigned char *zData;       /* Content of this page */
+  void *pUserData;            /* Extra content */
+  pgno pgno;                  /* Page number for this page */
+};
+/*
+ * UnQLite handle to the underlying Key/Value Storage Engine (See below).
+ */
+typedef void * unqlite_kv_handle;
+/*
+ * UnQLite pager IO methods.
+ *
+ * An instance of the following structure define the exported methods of the UnQLite pager
+ * to the underlying Key/Value storage engine.
+ */
+typedef struct unqlite_kv_io unqlite_kv_io;
+struct unqlite_kv_io
+{
+       unqlite_kv_handle  pHandle;     /* UnQLite handle passed as the first parameter to the
+                                                                        * method defined below.
+                                                                        */
+       unqlite_kv_methods *pMethods;   /* Underlying storage engine */
+       /* Pager methods */
+       int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **);
+       int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **);
+       int (*xNew)(unqlite_kv_handle,unqlite_page **);
+       int (*xWrite)(unqlite_page *);
+       int (*xDontWrite)(unqlite_page *);
+       int (*xDontJournal)(unqlite_page *);
+       int (*xDontMkHot)(unqlite_page *);
+       int (*xPageRef)(unqlite_page *);
+       int (*xPageUnref)(unqlite_page *);
+       int (*xPageSize)(unqlite_kv_handle);
+       int (*xReadOnly)(unqlite_kv_handle);
+       unsigned char * (*xTmpPage)(unqlite_kv_handle);
+       void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *)); 
+       void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *));
+       void (*xErr)(unqlite_kv_handle,const char *);
+};
+/*
+ * Key/Value Storage Engine Cursor Object
+ *
+ * An instance of a subclass of the following object defines a cursor
+ * used to scan through a key-value storage engine.
+ */
+typedef struct unqlite_kv_cursor unqlite_kv_cursor;
+struct unqlite_kv_cursor
+{
+  unqlite_kv_engine *pStore; /* Must be first */
+  /* Subclasses will typically add additional fields */
+};
+/*
+ * Possible seek positions.
+ */
+#define UNQLITE_CURSOR_MATCH_EXACT  1
+#define UNQLITE_CURSOR_MATCH_LE     2
+#define UNQLITE_CURSOR_MATCH_GE     3
+/*
+ * Key/Value Storage Engine.
+ *
+ * A Key-Value storage engine is defined by an instance of the following
+ * object.
+ * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.).
+ * The storage engine works with key/value pairs where both the key
+ * and the value are byte arrays of arbitrary length and with no restrictions on content.
+ * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage
+ * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory
+ * hash-table or Red-black tree storage engine is used for in-memory databases.
+ * Future versions of UnQLite might add other built-in storage engines (i.e. LSM). 
+ * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
+ * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
+ */
+struct unqlite_kv_engine
+{
+  const unqlite_kv_io *pIo; /* IO methods: MUST be first */
+   /* Subclasses will typically add additional fields */
+};
+/*
+ * Key/Value Storage Engine Virtual Method Table.
+ *
+ * Key/Value storage engine methods is defined by an instance of the following
+ * object.
+ * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
+ * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
+ */
+struct unqlite_kv_methods
+{
+  const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/
+  int szKv;          /* 'unqlite_kv_engine' subclass size */
+  int szCursor;      /* 'unqlite_kv_cursor' subclass size */
+  int iVersion;      /* Structure version, currently 1 */
+  /* Storage engine methods */
+  int (*xInit)(unqlite_kv_engine *,int iPageSize);
+  void (*xRelease)(unqlite_kv_engine *);
+  int (*xConfig)(unqlite_kv_engine *,int op,va_list ap);
+  int (*xOpen)(unqlite_kv_engine *,pgno);
+  int (*xReplace)(
+         unqlite_kv_engine *,
+         const void *pKey,int nKeyLen,
+         const void *pData,unqlite_int64 nDataLen
+         ); 
+    int (*xAppend)(
+         unqlite_kv_engine *,
+         const void *pKey,int nKeyLen,
+         const void *pData,unqlite_int64 nDataLen
+         );
+  void (*xCursorInit)(unqlite_kv_cursor *);
+  int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */
+  int (*xFirst)(unqlite_kv_cursor *);
+  int (*xLast)(unqlite_kv_cursor *);
+  int (*xValid)(unqlite_kv_cursor *);
+  int (*xNext)(unqlite_kv_cursor *);
+  int (*xPrev)(unqlite_kv_cursor *);
+  int (*xDelete)(unqlite_kv_cursor *);
+  int (*xKeyLength)(unqlite_kv_cursor *,int *);
+  int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+  int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *);
+  int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+  void (*xReset)(unqlite_kv_cursor *);
+  void (*xCursorRelease)(unqlite_kv_cursor *);
+};
+/*
+ * UnQLite journal file suffix.
+ */
+#ifndef UNQLITE_JOURNAL_FILE_SUFFIX
+#define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal"
+#endif
+/*
+ * Call Context - Error Message Serverity Level.
+ *
+ * The following constans are the allowed severity level that can
+ * passed as the second argument to the [unqlite_context_throw_error()] or
+ * [unqlite_context_throw_error_format()] interfaces.
+ * Refer to the official documentation for additional information.
+ */
+#define UNQLITE_CTX_ERR       1 /* Call context error such as unexpected number of arguments, invalid types and so on. */
+#define UNQLITE_CTX_WARNING   2 /* Call context Warning */
+#define UNQLITE_CTX_NOTICE    3 /* Call context Notice */
+/* 
+ * C-API-REF: Please refer to the official documentation for interfaces
+ * purpose and expected parameters. 
+ */ 
+
+/* Database Engine Handle */
+UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode);
+UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...);
+UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb);
+
+
+/* Key/Value (KV) Store Interfaces */
+UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
+UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
+UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
+UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
+UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen);
+UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,
+                           int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen);
+UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...);
+
+/* Document (JSON) Store Interfaces powered by the Jx9 Scripting Language */
+UNQLITE_APIEXPORT int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut);
+UNQLITE_APIEXPORT int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut);
+UNQLITE_APIEXPORT int unqlite_vm_config(unqlite_vm *pVm,int iOp,...);
+UNQLITE_APIEXPORT int unqlite_vm_exec(unqlite_vm *pVm);
+UNQLITE_APIEXPORT int unqlite_vm_reset(unqlite_vm *pVm);
+UNQLITE_APIEXPORT int unqlite_vm_release(unqlite_vm *pVm);
+UNQLITE_APIEXPORT int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);
+UNQLITE_APIEXPORT unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname);
+
+/*  Cursor Iterator Interfaces */
+UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor);
+
+/* Manual Transaction Manager */
+UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb);
+UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb);
+UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb);
+
+/* Utility interfaces */
+UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize);
+UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize);
+UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size);
+UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb);
+
+/* In-process extending interfaces */
+UNQLITE_APIEXPORT int unqlite_create_function(unqlite_vm *pVm,const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_delete_function(unqlite_vm *pVm, const char *zName);
+UNQLITE_APIEXPORT int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_delete_constant(unqlite_vm *pVm, const char *zName);
+
+/* On Demand Object allocation interfaces */
+UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm);
+UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm);
+UNQLITE_APIEXPORT int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue);
+UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx);
+UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_array(unqlite_context *pCtx);
+UNQLITE_APIEXPORT void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue);
+
+/* Dynamically Typed Value Object Management Interfaces */
+UNQLITE_APIEXPORT int unqlite_value_int(unqlite_value *pVal, int iValue);
+UNQLITE_APIEXPORT int unqlite_value_int64(unqlite_value *pVal, unqlite_int64 iValue);
+UNQLITE_APIEXPORT int unqlite_value_bool(unqlite_value *pVal, int iBool);
+UNQLITE_APIEXPORT int unqlite_value_null(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_double(unqlite_value *pVal, double Value);
+UNQLITE_APIEXPORT int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen);
+UNQLITE_APIEXPORT int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...);
+UNQLITE_APIEXPORT int unqlite_value_reset_string_cursor(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_resource(unqlite_value *pVal, void *pUserData);
+UNQLITE_APIEXPORT int unqlite_value_release(unqlite_value *pVal);
+
+/* Foreign Function Parameter Values */
+UNQLITE_APIEXPORT int unqlite_value_to_int(unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_value_to_bool(unqlite_value *pValue);
+UNQLITE_APIEXPORT unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue);
+UNQLITE_APIEXPORT double unqlite_value_to_double(unqlite_value *pValue);
+UNQLITE_APIEXPORT const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen);
+UNQLITE_APIEXPORT void * unqlite_value_to_resource(unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict);
+
+/* Setting The Result Of A Foreign Function */
+UNQLITE_APIEXPORT int unqlite_result_int(unqlite_context *pCtx, int iValue);
+UNQLITE_APIEXPORT int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue);
+UNQLITE_APIEXPORT int unqlite_result_bool(unqlite_context *pCtx, int iBool);
+UNQLITE_APIEXPORT int unqlite_result_double(unqlite_context *pCtx, double Value);
+UNQLITE_APIEXPORT int unqlite_result_null(unqlite_context *pCtx);
+UNQLITE_APIEXPORT int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen);
+UNQLITE_APIEXPORT int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...);
+UNQLITE_APIEXPORT int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_result_resource(unqlite_context *pCtx, void *pUserData);
+
+/* Dynamically Typed Value Object Query Interfaces */
+UNQLITE_APIEXPORT int unqlite_value_is_int(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_float(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_bool(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_string(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_null(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_numeric(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_callable(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_scalar(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_json_array(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_json_object(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_resource(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_empty(unqlite_value *pVal);
+
+/* JSON Array/Object Management Interfaces */
+UNQLITE_APIEXPORT unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte);
+UNQLITE_APIEXPORT int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData);
+UNQLITE_APIEXPORT int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_array_count(unqlite_value *pArray);
+
+/* Call Context Handling Interfaces */
+UNQLITE_APIEXPORT int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen);
+UNQLITE_APIEXPORT int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...);
+UNQLITE_APIEXPORT int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr);
+UNQLITE_APIEXPORT int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...);
+UNQLITE_APIEXPORT unsigned int unqlite_context_random_num(unqlite_context *pCtx);
+UNQLITE_APIEXPORT int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen);
+UNQLITE_APIEXPORT void * unqlite_context_user_data(unqlite_context *pCtx);
+UNQLITE_APIEXPORT int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData);
+UNQLITE_APIEXPORT void * unqlite_context_peek_aux_data(unqlite_context *pCtx);
+UNQLITE_APIEXPORT unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx);
+UNQLITE_APIEXPORT const char * unqlite_function_name(unqlite_context *pCtx);
+
+/* Call Context Memory Management Interfaces */
+UNQLITE_APIEXPORT void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease);
+UNQLITE_APIEXPORT void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte);
+UNQLITE_APIEXPORT void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk);
+
+/* Global Library Management Interfaces */
+UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...);
+UNQLITE_APIEXPORT int unqlite_lib_init(void);
+UNQLITE_APIEXPORT int unqlite_lib_shutdown(void);
+UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void);
+UNQLITE_APIEXPORT const char * unqlite_lib_version(void);
+UNQLITE_APIEXPORT const char * unqlite_lib_signature(void);
+UNQLITE_APIEXPORT const char * unqlite_lib_ident(void);
+UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void);
+
+#endif /* _UNQLITE_H_ */
diff --git a/libs/libblade/src/unqlite.c b/libs/libblade/src/unqlite.c
new file mode 100644 (file)
index 0000000..3e6538b
--- /dev/null
@@ -0,0 +1,60236 @@
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2016, Symisc Systems http://unqlite.org/
+ * Copyright (C) 2014, Yuras Shumovich <shumovichy@gmail.com>
+ * Version 1.1.7
+ * 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://unqlite.org/licensing.html
+ */
+/*
+ * Copyright (C) 2012, 2016 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
+ * 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.
+ *
+ * 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: unqlite.c v1.1.7 Win10 2106-12-02 00:04:12 stable <chm@symisc.net> $ 
+ */
+/* This file is an amalgamation of many separate C source files from unqlite version 1.1.6
+ * 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 unqlite is compiled as a single translation unit.
+ *
+ * This file is all you need to compile unqlite. To use unqlite in other programs, you need
+ * this file and the "unqlite.h" header file that defines the programming interface to the 
+ * unqlite engine.(If you do not have the "unqlite.h" header file at hand, you will find
+ * a copy embedded within the text of this file.Search for "Header file: <unqlite.h>" to find
+ * the start of the embedded unqlite.h header file.) Additional code files may be needed if
+ * you want a wrapper to interface unqlite with your choice of programming language.
+ * To get the official documentation, please visit http://unqlite.org/
+ */
+ /*
+  * Make the sure the following directive is defined in the amalgamation build.
+  */
+ #ifndef UNQLITE_AMALGAMATION
+ #define UNQLITE_AMALGAMATION
+ #define JX9_AMALGAMATION
+ /* Marker for routines not intended for external use */
+ #define JX9_PRIVATE static
+ #endif /* UNQLITE_AMALGAMATION */
+/*
+ * Embedded header file for unqlite: <unqlite.h>
+ */
+/*
+ * ----------------------------------------------------------
+ * File: unqlite.h
+ * MD5: d26e9847c6587edbbb183d0115d172cb
+ * ----------------------------------------------------------
+ */
+/* This file was automatically generated.  Do not edit (Except for compile time directives)! */ 
+#ifndef _UNQLITE_H_
+#define _UNQLITE_H_
+/*
+ * Symisc UnQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2016, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+/*
+ * Copyright (C) 2012, 2016 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
+ * 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.
+ *
+ * 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: unqlite.h v1.2 Win10 2106-12-02 00:04:12 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 [unqlite_lib_version()], [unqlite_lib_signature()], [unqlite_lib_ident()] and
+ * [unqlite_lib_copyright()] for more information.
+ */
+/*
+ * The UNQLITE_VERSION C preprocessor macroevaluates to a string literal
+ * that is the unqlite 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 UNQLITE_VERSION "1.1.7"
+/*
+ * The UNQLITE_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 [UNQLITE_VERSION].
+ */
+#define UNQLITE_VERSION_NUMBER 1001007
+/*
+ * The UNQLITE_SIG C preprocessor macro evaluates to a string
+ * literal which is the public signature of the unqlite engine.
+ * This signature could be included for example in a host-application
+ * generated Server MIME header as follows:
+ *   Server: YourWebServer/x.x unqlite/x.x.x \r\n
+ */
+#define UNQLITE_SIG "unqlite/1.1.7"
+/*
+ * UnQLite 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 unqlite.
+ */
+#define UNQLITE_IDENT "unqlite:b172a1e2c3f62fb35c8e1fb2795121f82356cad6"
+/*
+ * Copyright notice.
+ * If you have any questions about the licensing situation, please
+ * visit http://unqlite.org/licensing.html
+ * or contact Symisc Systems via:
+ *   legal@symisc.net
+ *   licensing@symisc.net
+ *   contact@symisc.net
+ */
+#define UNQLITE_COPYRIGHT "Copyright (C) Symisc Systems, S.U.A.R.L [Mrad Chems Eddine <chm@symisc.net>] 2012-2016, http://unqlite.org/"
+/* Make sure we can call this stuff from C++ */
+#ifdef __cplusplus
+extern "C" { 
+#endif
+/* Forward declaration to public objects */
+typedef struct unqlite_io_methods unqlite_io_methods;
+typedef struct unqlite_kv_methods unqlite_kv_methods;
+typedef struct unqlite_kv_engine unqlite_kv_engine;
+typedef struct jx9_io_stream unqlite_io_stream;
+typedef struct jx9_context unqlite_context;
+typedef struct jx9_value unqlite_value;
+typedef struct unqlite_vfs unqlite_vfs;
+typedef struct unqlite_vm unqlite_vm;
+typedef struct unqlite unqlite;
+/*
+ * ------------------------------
+ * Compile time directives
+ * ------------------------------
+ * For most purposes, UnQLite can be built just fine using the default compilation options.
+ * However, if required, the compile-time options documented below can be used to omit UnQLite
+ * 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.
+ *
+ * UNQLITE_ENABLE_THREADS
+ *  This option controls whether or not code is included in UnQLite to enable it to operate
+ *  safely in a multithreaded environment. The default is not. All mutexing code is omitted
+ *  and it is unsafe to use UnQLite in a multithreaded program. When compiled with the
+ *  UNQLITE_ENABLE_THREADS directive enabled, UnQLite can be used in a multithreaded program
+ *  and it is safe to share the same virtual machine and engine handle between two or more threads.
+ *  The value of UNQLITE_ENABLE_THREADS can be determined at run-time using the unqlite_lib_is_threadsafe()
+ *  interface.
+ *  When UnQLite has been compiled with threading support then the threading mode can be altered
+ * at run-time using the unqlite_lib_config() interface together with one of these verbs:
+ *    UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE
+ *    UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
+ *  Platforms others than Windows and UNIX systems must install their own mutex subsystem via 
+ *  unqlite_lib_config() with a configuration verb set to UNQLITE_LIB_CONFIG_USER_MUTEX.
+ *  Otherwise the library is not threadsafe.
+ *  Note that you must link UnQLite with the POSIX threads library under UNIX systems (i.e: -lpthread).
+ *
+ * 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 UnQLite 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 is shipped with more than 312 built-in functions suitable for most purposes like 
+ *  string and INI processing, ZIP extracting, Base64 encoding/decoding, JSON encoding/decoding
+ *  and so forth.
+ *  If this directive is enabled, then all built-in Jx9 functions are omitted from the build.
+ *  Note that special functions such as db_create(), db_store(), db_fetch(), 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 UnQLite with the math library in same
+ *  Linux/BSD flavor (i.e: -lm).
+ *
+ * JX9_DISABLE_DISK_IO
+ *  If this directive is enabled, built-in VFS functions such as chdir(), mkdir(), chroot(), unlink(),
+ *  sleep(), etc. are omitted from the build.
+ *
+ * UNQLITE_ENABLE_JX9_HASH_IO
+ * If this directive is enabled, built-in hash functions such as md5(), sha1(), md5_file(), crc32(), etc.
+ * are included in the build.
+ */
+/* 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 
+/*
+ * Determine if we are dealing with WindowsCE - which has a much
+ * reduced API.
+ */
+#if defined(_WIN32_WCE)
+#ifndef __WIN_CE__
+#define __WIN_CE__
+#endif /* __WIN_CE__ */
+#endif /* _WIN32_WCE */
+#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 */
+/* 
+ * Marker for exported interfaces. 
+ */
+#define UNQLITE_APIEXPORT SX_APIEXPORT
+/*
+ * If compiling for a processor that lacks floating point
+ * support, substitute integer for floating-point.
+ */
+#ifdef UNQLITE_OMIT_FLOATING_POINT
+typedef sxi64 uqlite_real;
+#else
+typedef double unqlite_real;
+#endif
+typedef sxi64 unqlite_int64;
+/* Standard UnQLite return values */
+#define UNQLITE_OK      SXRET_OK      /* Successful result */
+/* Beginning of error codes */
+#define UNQLITE_NOMEM    SXERR_MEM     /* Out of memory */
+#define UNQLITE_ABORT    SXERR_ABORT   /* Another thread have released this instance */
+#define UNQLITE_IOERR    SXERR_IO      /* IO error */
+#define UNQLITE_CORRUPT  SXERR_CORRUPT /* Corrupt pointer */
+#define UNQLITE_LOCKED   SXERR_LOCKED  /* Forbidden Operation */ 
+#define UNQLITE_BUSY    SXERR_BUSY    /* The database file is locked */
+#define UNQLITE_DONE    SXERR_DONE    /* Operation done */
+#define UNQLITE_PERM     SXERR_PERM    /* Permission error */
+#define UNQLITE_NOTIMPLEMENTED SXERR_NOTIMPLEMENTED /* Method not implemented by the underlying Key/Value storage engine */
+#define UNQLITE_NOTFOUND SXERR_NOTFOUND /* No such record */
+#define UNQLITE_NOOP     SXERR_NOOP     /* No such method */
+#define UNQLITE_INVALID  SXERR_INVALID  /* Invalid parameter */
+#define UNQLITE_EOF      SXERR_EOF      /* End Of Input */
+#define UNQLITE_UNKNOWN  SXERR_UNKNOWN  /* Unknown configuration option */
+#define UNQLITE_LIMIT    SXERR_LIMIT    /* Database limit reached */
+#define UNQLITE_EXISTS   SXERR_EXISTS   /* Record exists */
+#define UNQLITE_EMPTY    SXERR_EMPTY    /* Empty record */
+#define UNQLITE_COMPILE_ERR (-70)       /* Compilation error */
+#define UNQLITE_VM_ERR      (-71)       /* Virtual machine error */
+#define UNQLITE_FULL        (-73)       /* Full database (unlikely) */
+#define UNQLITE_CANTOPEN    (-74)       /* Unable to open the database file */
+#define UNQLITE_READ_ONLY   (-75)       /* Read only Key/Value storage engine */
+#define UNQLITE_LOCKERR     (-76)       /* Locking protocol error */
+/* end-of-error-codes */
+/*
+ * Database Handle Configuration Commands.
+ *
+ * The following set of constants are the available configuration verbs that can
+ * be used by the host-application to configure an UnQLite database handle.
+ * These constants must be passed as the second argument to [unqlite_config()].
+ *
+ * Each options require a variable number of arguments.
+ * The [unqlite_config()] interface will return UNQLITE_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://unqlite.org/c_api/unqlite_config.html
+ */
+#define UNQLITE_CONFIG_JX9_ERR_LOG         1  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
+#define UNQLITE_CONFIG_MAX_PAGE_CACHE      2  /* ONE ARGUMENT: int nMaxPage */
+#define UNQLITE_CONFIG_ERR_LOG             3  /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
+#define UNQLITE_CONFIG_KV_ENGINE           4  /* ONE ARGUMENT: const char *zKvName */
+#define UNQLITE_CONFIG_DISABLE_AUTO_COMMIT 5  /* NO ARGUMENTS */
+#define UNQLITE_CONFIG_GET_KV_NAME         6  /* ONE ARGUMENT: const char **pzPtr */
+/*
+ * UnQLite/Jx9 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 (Via UnQLite) Virtual machine.
+ * These constants must be passed as the second argument to the [unqlite_vm_config()] 
+ * interface.
+ * Each options require a variable number of arguments.
+ * The [unqlite_vm_config()] interface will return UNQLITE_OK on success, any other return
+ * value indicates failure.
+ * There are many options but the most importants are: UNQLITE_VM_CONFIG_OUTPUT which install
+ * a VM output consumer callback, UNQLITE_VM_CONFIG_HTTP_REQUEST which parse and register
+ * a HTTP request and UNQLITE_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://unqlite.org/c_api/unqlite_vm_config.html
+ */
+#define UNQLITE_VM_CONFIG_OUTPUT           1  /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
+#define UNQLITE_VM_CONFIG_IMPORT_PATH      2  /* ONE ARGUMENT: const char *zIncludePath */
+#define UNQLITE_VM_CONFIG_ERR_REPORT       3  /* NO ARGUMENTS: Report all run-time errors in the VM output */
+#define UNQLITE_VM_CONFIG_RECURSION_DEPTH  4  /* ONE ARGUMENT: int nMaxDepth */
+#define UNQLITE_VM_OUTPUT_LENGTH           5  /* ONE ARGUMENT: unsigned int *pLength */
+#define UNQLITE_VM_CONFIG_CREATE_VAR       6  /* TWO ARGUMENTS: const char *zName, unqlite_value *pValue */
+#define UNQLITE_VM_CONFIG_HTTP_REQUEST     7  /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
+#define UNQLITE_VM_CONFIG_SERVER_ATTR      8  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
+#define UNQLITE_VM_CONFIG_ENV_ATTR         9  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
+#define UNQLITE_VM_CONFIG_EXEC_VALUE      10  /* ONE ARGUMENT: unqlite_value **ppValue */
+#define UNQLITE_VM_CONFIG_IO_STREAM       11  /* ONE ARGUMENT: const unqlite_io_stream *pStream */
+#define UNQLITE_VM_CONFIG_ARGV_ENTRY      12  /* ONE ARGUMENT: const char *zValue */
+#define UNQLITE_VM_CONFIG_EXTRACT_OUTPUT  13  /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
+/*
+ * Storage engine configuration commands.
+ *
+ * The following set of constants are the available configuration verbs that can
+ * be used by the host-application to configure the underlying storage engine (i.e Hash, B+tree, R+tree).
+ * These constants must be passed as the first argument to [unqlite_kv_config()].
+ * Each options require a variable number of arguments.
+ * The [unqlite_kv_config()] interface will return UNQLITE_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://unqlite.org/c_api/unqlite_kv_config.html
+ */
+#define UNQLITE_KV_CONFIG_HASH_FUNC  1 /* ONE ARGUMENT: unsigned int (*xHash)(const void *,unsigned int) */
+#define UNQLITE_KV_CONFIG_CMP_FUNC   2 /* ONE ARGUMENT: int (*xCmp)(const void *,const void *,unsigned int) */
+/*
+ * 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 [unqlite_lib_config()].
+ *
+ * Each options require a variable number of arguments.
+ * The [unqlite_lib_config()] interface will return UNQLITE_OK on success, any other return
+ * value indicates failure.
+ * Notes:
+ * The default configuration is recommended for most applications and so the call to
+ * [unqlite_lib_config()] is usually not necessary. It is provided to support rare 
+ * applications with unusual needs. 
+ * The [unqlite_lib_config()] interface is not threadsafe. The application must insure that
+ * no other [unqlite_*()] interfaces are invoked by other threads while [unqlite_lib_config()]
+ * is running. Furthermore, [unqlite_lib_config()] may only be invoked prior to library
+ * initialization using [unqlite_lib_init()] or [unqlite_init()] or after shutdown
+ * by [unqlite_lib_shutdown()]. If [unqlite_lib_config()] is called after [unqlite_lib_init()]
+ * or [unqlite_init()] and before [unqlite_lib_shutdown()] then it will return UNQLITE_LOCKED.
+ * For a full discussion on the configuration verbs and their expected parameters, please
+ * refer to this page:
+ *      http://unqlite.org/c_api/unqlite_lib.html
+ */
+#define UNQLITE_LIB_CONFIG_USER_MALLOC            1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */ 
+#define UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK       2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
+#define UNQLITE_LIB_CONFIG_USER_MUTEX             3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */ 
+#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE    4 /* NO ARGUMENTS */ 
+#define UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI     5 /* NO ARGUMENTS */ 
+#define UNQLITE_LIB_CONFIG_VFS                    6 /* ONE ARGUMENT: const unqlite_vfs *pVfs */
+#define UNQLITE_LIB_CONFIG_STORAGE_ENGINE         7 /* ONE ARGUMENT: unqlite_kv_methods *pStorage */
+#define UNQLITE_LIB_CONFIG_PAGE_SIZE              8 /* ONE ARGUMENT: int iPageSize */
+/*
+ * These bit values are intended for use in the 3rd parameter to the [unqlite_open()] interface
+ * and in the 4th parameter to the xOpen method of the [unqlite_vfs] object.
+ */
+#define UNQLITE_OPEN_READONLY         0x00000001  /* Read only mode. Ok for [unqlite_open] */
+#define UNQLITE_OPEN_READWRITE        0x00000002  /* Ok for [unqlite_open] */
+#define UNQLITE_OPEN_CREATE           0x00000004  /* Ok for [unqlite_open] */
+#define UNQLITE_OPEN_EXCLUSIVE        0x00000008  /* VFS only */
+#define UNQLITE_OPEN_TEMP_DB          0x00000010  /* VFS only */
+#define UNQLITE_OPEN_NOMUTEX          0x00000020  /* Ok for [unqlite_open] */
+#define UNQLITE_OPEN_OMIT_JOURNALING  0x00000040  /* Omit journaling for this database. Ok for [unqlite_open] */
+#define UNQLITE_OPEN_IN_MEMORY        0x00000080  /* An in memory database. Ok for [unqlite_open]*/
+#define UNQLITE_OPEN_MMAP             0x00000100  /* Obtain a memory view of the whole file. Ok for [unqlite_open] */
+/*
+ * Synchronization Type Flags
+ *
+ * When UnQLite invokes the xSync() method of an [unqlite_io_methods] object it uses
+ * a combination of these integer values as the second argument.
+ *
+ * When the UNQLITE_SYNC_DATAONLY flag is used, it means that the sync operation only
+ * needs to flush data to mass storage.  Inode information need not be flushed.
+ * If the lower four bits of the flag equal UNQLITE_SYNC_NORMAL, that means to use normal
+ * fsync() semantics. If the lower four bits equal UNQLITE_SYNC_FULL, that means to use
+ * Mac OS X style fullsync instead of fsync().
+ */
+#define UNQLITE_SYNC_NORMAL        0x00002
+#define UNQLITE_SYNC_FULL          0x00003
+#define UNQLITE_SYNC_DATAONLY      0x00010
+/*
+ * File Locking Levels
+ *
+ * UnQLite uses one of these integer values as the second
+ * argument to calls it makes to the xLock() and xUnlock() methods
+ * of an [unqlite_io_methods] object.
+ */
+#define UNQLITE_LOCK_NONE          0
+#define UNQLITE_LOCK_SHARED        1
+#define UNQLITE_LOCK_RESERVED      2
+#define UNQLITE_LOCK_PENDING       3
+#define UNQLITE_LOCK_EXCLUSIVE     4
+/*
+ * CAPIREF: OS Interface: Open File Handle
+ *
+ * An [unqlite_file] object represents an open file in the [unqlite_vfs] OS interface
+ * layer.
+ * Individual OS interface implementations will want to subclass this object by appending
+ * additional fields for their own use. The pMethods entry is a pointer to an
+ * [unqlite_io_methods] object that defines methods for performing
+ * I/O operations on the open file.
+*/
+typedef struct unqlite_file unqlite_file;
+struct unqlite_file {
+  const unqlite_io_methods *pMethods;  /* Methods for an open file. MUST BE FIRST */
+};
+/*
+ * CAPIREF: OS Interface: File Methods Object
+ *
+ * Every file opened by the [unqlite_vfs] xOpen method populates an
+ * [unqlite_file] object (or, more commonly, a subclass of the
+ * [unqlite_file] object) with a pointer to an instance of this object.
+ * This object defines the methods used to perform various operations
+ * against the open file represented by the [unqlite_file] object.
+ *
+ * If the xOpen method sets the unqlite_file.pMethods element 
+ * to a non-NULL pointer, then the unqlite_io_methods.xClose method
+ * may be invoked even if the xOpen reported that it failed.  The
+ * only way to prevent a call to xClose following a failed xOpen
+ * is for the xOpen to set the unqlite_file.pMethods element to NULL.
+ *
+ * The flags argument to xSync may be one of [UNQLITE_SYNC_NORMAL] or
+ * [UNQLITE_SYNC_FULL]. The first choice is the normal fsync().
+ * The second choice is a Mac OS X style fullsync. The [UNQLITE_SYNC_DATAONLY]
+ * flag may be ORed in to indicate that only the data of the file
+ * and not its inode needs to be synced.
+ *
+ * The integer values to xLock() and xUnlock() are one of
+ *
+ * UNQLITE_LOCK_NONE
+ * UNQLITE_LOCK_SHARED
+ * UNQLITE_LOCK_RESERVED
+ * UNQLITE_LOCK_PENDING
+ * UNQLITE_LOCK_EXCLUSIVE
+ * 
+ * xLock() increases the lock. xUnlock() decreases the lock.
+ * The xCheckReservedLock() method checks whether any database connection,
+ * either in this process or in some other process, is holding a RESERVED,
+ * PENDING, or EXCLUSIVE lock on the file. It returns true if such a lock exists
+ * and false otherwise.
+ * 
+ * The xSectorSize() method returns the sector size of the device that underlies
+ * the file. The sector size is the minimum write that can be performed without
+ * disturbing other bytes in the file.
+ *
+ */
+struct unqlite_io_methods {
+  int iVersion;                 /* Structure version number (currently 1) */
+  int (*xClose)(unqlite_file*);
+  int (*xRead)(unqlite_file*, void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
+  int (*xWrite)(unqlite_file*, const void*, unqlite_int64 iAmt, unqlite_int64 iOfst);
+  int (*xTruncate)(unqlite_file*, unqlite_int64 size);
+  int (*xSync)(unqlite_file*, int flags);
+  int (*xFileSize)(unqlite_file*, unqlite_int64 *pSize);
+  int (*xLock)(unqlite_file*, int);
+  int (*xUnlock)(unqlite_file*, int);
+  int (*xCheckReservedLock)(unqlite_file*, int *pResOut);
+  int (*xSectorSize)(unqlite_file*);
+};
+/*
+ * CAPIREF: OS Interface Object
+ *
+ * An instance of the unqlite_vfs object defines the interface between
+ * the UnQLite core and the underlying operating system.  The "vfs"
+ * in the name of the object stands for "Virtual File System".
+ *
+ * Only a single vfs can be registered within the UnQLite core.
+ * Vfs registration is done using the [unqlite_lib_config()] interface
+ * with a configuration verb set to UNQLITE_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 UnQLite
+ * come with a built-in vfs for these platforms that implements most the methods
+ * defined below.
+ *
+ * Clients running on exotic systems (ie: Other than Windows and UNIX systems)
+ * must register their own vfs in order to be able to use the UnQLite library.
+ *
+ * The value of the iVersion field is initially 1 but may be larger in
+ * future versions of UnQLite. 
+ *
+ * The szOsFile field is the size of the subclassed [unqlite_file] structure
+ * used by this VFS. mxPathname is the maximum length of a pathname in this VFS.
+ * 
+ * At least szOsFile bytes of memory are allocated by UnQLite to hold the [unqlite_file]
+ * structure passed as the third argument to xOpen. The xOpen method does not have to
+ * allocate the structure; it should just fill it in. Note that the xOpen method must
+ * set the unqlite_file.pMethods to either a valid [unqlite_io_methods] object or to NULL.
+ * xOpen must do this even if the open fails. UnQLite expects that the unqlite_file.pMethods
+ * element will be valid after xOpen returns regardless of the success or failure of the
+ * xOpen call.
+ *
+ */
+struct unqlite_vfs {
+  const char *zName;       /* Name of this virtual file system [i.e: Windows, UNIX, etc.] */
+  int iVersion;            /* Structure version number (currently 1) */
+  int szOsFile;            /* Size of subclassed unqlite_file */
+  int mxPathname;          /* Maximum file pathname length */
+  int (*xOpen)(unqlite_vfs*, const char *zName, unqlite_file*,unsigned int flags);
+  int (*xDelete)(unqlite_vfs*, const char *zName, int syncDir);
+  int (*xAccess)(unqlite_vfs*, const char *zName, int flags, int *pResOut);
+  int (*xFullPathname)(unqlite_vfs*, const char *zName,int buf_len,char *zBuf);
+  int (*xTmpDir)(unqlite_vfs*,char *zBuf,int buf_len);
+  int (*xSleep)(unqlite_vfs*, int microseconds);
+  int (*xCurrentTime)(unqlite_vfs*,Sytm *pOut);
+  int (*xGetLastError)(unqlite_vfs*, int, char *);
+};
+/*
+ * Flags for the xAccess VFS method
+ *
+ * These integer constants can be used as the third parameter to
+ * the xAccess method of an [unqlite_vfs] object.  They determine
+ * what kind of permissions the xAccess method is looking for.
+ * With UNQLITE_ACCESS_EXISTS, the xAccess method
+ * simply checks whether the file exists.
+ * With UNQLITE_ACCESS_READWRITE, the xAccess method
+ * checks whether the named directory is both readable and writable
+ * (in other words, if files can be added, removed, and renamed within
+ * the directory).
+ * The UNQLITE_ACCESS_READWRITE constant is currently used only by the
+ * [temp_store_directory pragma], though this could change in a future
+ * release of UnQLite.
+ * With UNQLITE_ACCESS_READ, the xAccess method
+ * checks whether the file is readable.  The UNQLITE_ACCESS_READ constant is
+ * currently unused, though it might be used in a future release of
+ * UnQLite.
+ */
+#define UNQLITE_ACCESS_EXISTS    0
+#define UNQLITE_ACCESS_READWRITE 1   
+#define UNQLITE_ACCESS_READ      2 
+/*
+ * The type used to represent a page number.  The first page in a file
+ * is called page 1.  0 is used to represent "not a page".
+ * A page number is an unsigned 64-bit integer.
+ */
+typedef sxu64 pgno;
+/*
+ * A database disk page is represented by an instance
+ * of the follwoing structure.
+ */
+typedef struct unqlite_page unqlite_page;
+struct unqlite_page
+{
+  unsigned char *zData;       /* Content of this page */
+  void *pUserData;            /* Extra content */
+  pgno pgno;                  /* Page number for this page */
+};
+/*
+ * UnQLite handle to the underlying Key/Value Storage Engine (See below).
+ */
+typedef void * unqlite_kv_handle;
+/*
+ * UnQLite pager IO methods.
+ *
+ * An instance of the following structure define the exported methods of the UnQLite pager
+ * to the underlying Key/Value storage engine.
+ */
+typedef struct unqlite_kv_io unqlite_kv_io;
+struct unqlite_kv_io
+{
+       unqlite_kv_handle  pHandle;     /* UnQLite handle passed as the first parameter to the
+                                                                        * method defined below.
+                                                                        */
+       unqlite_kv_methods *pMethods;   /* Underlying storage engine */
+       /* Pager methods */
+       int (*xGet)(unqlite_kv_handle,pgno,unqlite_page **);
+       int (*xLookup)(unqlite_kv_handle,pgno,unqlite_page **);
+       int (*xNew)(unqlite_kv_handle,unqlite_page **);
+       int (*xWrite)(unqlite_page *);
+       int (*xDontWrite)(unqlite_page *);
+       int (*xDontJournal)(unqlite_page *);
+       int (*xDontMkHot)(unqlite_page *);
+       int (*xPageRef)(unqlite_page *);
+       int (*xPageUnref)(unqlite_page *);
+       int (*xPageSize)(unqlite_kv_handle);
+       int (*xReadOnly)(unqlite_kv_handle);
+       unsigned char * (*xTmpPage)(unqlite_kv_handle);
+       void (*xSetUnpin)(unqlite_kv_handle,void (*xPageUnpin)(void *)); 
+       void (*xSetReload)(unqlite_kv_handle,void (*xPageReload)(void *));
+       void (*xErr)(unqlite_kv_handle,const char *);
+};
+/*
+ * Key/Value Storage Engine Cursor Object
+ *
+ * An instance of a subclass of the following object defines a cursor
+ * used to scan through a key-value storage engine.
+ */
+typedef struct unqlite_kv_cursor unqlite_kv_cursor;
+struct unqlite_kv_cursor
+{
+  unqlite_kv_engine *pStore; /* Must be first */
+  /* Subclasses will typically add additional fields */
+};
+/*
+ * Possible seek positions.
+ */
+#define UNQLITE_CURSOR_MATCH_EXACT  1
+#define UNQLITE_CURSOR_MATCH_LE     2
+#define UNQLITE_CURSOR_MATCH_GE     3
+/*
+ * Key/Value Storage Engine.
+ *
+ * A Key-Value storage engine is defined by an instance of the following
+ * object.
+ * UnQLite works with run-time interchangeable storage engines (i.e. Hash, B+Tree, R+Tree, LSM, etc.).
+ * The storage engine works with key/value pairs where both the key
+ * and the value are byte arrays of arbitrary length and with no restrictions on content.
+ * UnQLite come with two built-in KV storage engine: A Virtual Linear Hash (VLH) storage
+ * engine is used for persistent on-disk databases with O(1) lookup time and an in-memory
+ * hash-table or Red-black tree storage engine is used for in-memory databases.
+ * Future versions of UnQLite might add other built-in storage engines (i.e. LSM). 
+ * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
+ * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
+ */
+struct unqlite_kv_engine
+{
+  const unqlite_kv_io *pIo; /* IO methods: MUST be first */
+   /* Subclasses will typically add additional fields */
+};
+/*
+ * Key/Value Storage Engine Virtual Method Table.
+ *
+ * Key/Value storage engine methods is defined by an instance of the following
+ * object.
+ * Registration of a Key/Value storage engine at run-time is done via [unqlite_lib_config()]
+ * with a configuration verb set to UNQLITE_LIB_CONFIG_STORAGE_ENGINE.
+ */
+struct unqlite_kv_methods
+{
+  const char *zName; /* Storage engine name [i.e. Hash, B+tree, LSM, R-tree, Mem, etc.]*/
+  int szKv;          /* 'unqlite_kv_engine' subclass size */
+  int szCursor;      /* 'unqlite_kv_cursor' subclass size */
+  int iVersion;      /* Structure version, currently 1 */
+  /* Storage engine methods */
+  int (*xInit)(unqlite_kv_engine *,int iPageSize);
+  void (*xRelease)(unqlite_kv_engine *);
+  int (*xConfig)(unqlite_kv_engine *,int op,va_list ap);
+  int (*xOpen)(unqlite_kv_engine *,pgno);
+  int (*xReplace)(
+         unqlite_kv_engine *,
+         const void *pKey,int nKeyLen,
+         const void *pData,unqlite_int64 nDataLen
+         ); 
+    int (*xAppend)(
+         unqlite_kv_engine *,
+         const void *pKey,int nKeyLen,
+         const void *pData,unqlite_int64 nDataLen
+         );
+  void (*xCursorInit)(unqlite_kv_cursor *);
+  int (*xSeek)(unqlite_kv_cursor *,const void *pKey,int nByte,int iPos); /* Mandatory */
+  int (*xFirst)(unqlite_kv_cursor *);
+  int (*xLast)(unqlite_kv_cursor *);
+  int (*xValid)(unqlite_kv_cursor *);
+  int (*xNext)(unqlite_kv_cursor *);
+  int (*xPrev)(unqlite_kv_cursor *);
+  int (*xDelete)(unqlite_kv_cursor *);
+  int (*xKeyLength)(unqlite_kv_cursor *,int *);
+  int (*xKey)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+  int (*xDataLength)(unqlite_kv_cursor *,unqlite_int64 *);
+  int (*xData)(unqlite_kv_cursor *,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+  void (*xReset)(unqlite_kv_cursor *);
+  void (*xCursorRelease)(unqlite_kv_cursor *);
+};
+/*
+ * UnQLite journal file suffix.
+ */
+#ifndef UNQLITE_JOURNAL_FILE_SUFFIX
+#define UNQLITE_JOURNAL_FILE_SUFFIX "_unqlite_journal"
+#endif
+/*
+ * Call Context - Error Message Serverity Level.
+ *
+ * The following constans are the allowed severity level that can
+ * passed as the second argument to the [unqlite_context_throw_error()] or
+ * [unqlite_context_throw_error_format()] interfaces.
+ * Refer to the official documentation for additional information.
+ */
+#define UNQLITE_CTX_ERR       1 /* Call context error such as unexpected number of arguments, invalid types and so on. */
+#define UNQLITE_CTX_WARNING   2 /* Call context Warning */
+#define UNQLITE_CTX_NOTICE    3 /* Call context Notice */
+/* 
+ * C-API-REF: Please refer to the official documentation for interfaces
+ * purpose and expected parameters. 
+ */ 
+
+/* Database Engine Handle */
+UNQLITE_APIEXPORT int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode);
+UNQLITE_APIEXPORT int unqlite_config(unqlite *pDb,int nOp,...);
+UNQLITE_APIEXPORT int unqlite_close(unqlite *pDb);
+
+/* Key/Value (KV) Store Interfaces */
+UNQLITE_APIEXPORT int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
+UNQLITE_APIEXPORT int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen);
+UNQLITE_APIEXPORT int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
+UNQLITE_APIEXPORT int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...);
+UNQLITE_APIEXPORT int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 /* in|out */*pBufLen);
+UNQLITE_APIEXPORT int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,
+                           int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen);
+UNQLITE_APIEXPORT int unqlite_kv_config(unqlite *pDb,int iOp,...);
+
+/* Document (JSON) Store Interfaces powered by the Jx9 Scripting Language */
+UNQLITE_APIEXPORT int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut);
+UNQLITE_APIEXPORT int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut);
+UNQLITE_APIEXPORT int unqlite_vm_config(unqlite_vm *pVm,int iOp,...);
+UNQLITE_APIEXPORT int unqlite_vm_exec(unqlite_vm *pVm);
+UNQLITE_APIEXPORT int unqlite_vm_reset(unqlite_vm *pVm);
+UNQLITE_APIEXPORT int unqlite_vm_release(unqlite_vm *pVm);
+UNQLITE_APIEXPORT int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);
+UNQLITE_APIEXPORT unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname);
+
+/*  Cursor Iterator Interfaces */
+UNQLITE_APIEXPORT int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnData);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor);
+UNQLITE_APIEXPORT int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor);
+
+/* Manual Transaction Manager */
+UNQLITE_APIEXPORT int unqlite_begin(unqlite *pDb);
+UNQLITE_APIEXPORT int unqlite_commit(unqlite *pDb);
+UNQLITE_APIEXPORT int unqlite_rollback(unqlite *pDb);
+
+/* Utility interfaces */
+UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize);
+UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize);
+UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size);
+UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb);
+
+/* In-process extending interfaces */
+UNQLITE_APIEXPORT int unqlite_create_function(unqlite_vm *pVm,const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_delete_function(unqlite_vm *pVm, const char *zName);
+UNQLITE_APIEXPORT int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData);
+UNQLITE_APIEXPORT int unqlite_delete_constant(unqlite_vm *pVm, const char *zName);
+
+/* On Demand Object allocation interfaces */
+UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm);
+UNQLITE_APIEXPORT unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm);
+UNQLITE_APIEXPORT int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue);
+UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx);
+UNQLITE_APIEXPORT unqlite_value * unqlite_context_new_array(unqlite_context *pCtx);
+UNQLITE_APIEXPORT void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue);
+
+/* Dynamically Typed Value Object Management Interfaces */
+UNQLITE_APIEXPORT int unqlite_value_int(unqlite_value *pVal, int iValue);
+UNQLITE_APIEXPORT int unqlite_value_int64(unqlite_value *pVal, unqlite_int64 iValue);
+UNQLITE_APIEXPORT int unqlite_value_bool(unqlite_value *pVal, int iBool);
+UNQLITE_APIEXPORT int unqlite_value_null(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_double(unqlite_value *pVal, double Value);
+UNQLITE_APIEXPORT int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen);
+UNQLITE_APIEXPORT int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...);
+UNQLITE_APIEXPORT int unqlite_value_reset_string_cursor(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_resource(unqlite_value *pVal, void *pUserData);
+UNQLITE_APIEXPORT int unqlite_value_release(unqlite_value *pVal);
+
+/* Foreign Function Parameter Values */
+UNQLITE_APIEXPORT int unqlite_value_to_int(unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_value_to_bool(unqlite_value *pValue);
+UNQLITE_APIEXPORT unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue);
+UNQLITE_APIEXPORT double unqlite_value_to_double(unqlite_value *pValue);
+UNQLITE_APIEXPORT const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen);
+UNQLITE_APIEXPORT void * unqlite_value_to_resource(unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict);
+
+/* Setting The Result Of A Foreign Function */
+UNQLITE_APIEXPORT int unqlite_result_int(unqlite_context *pCtx, int iValue);
+UNQLITE_APIEXPORT int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue);
+UNQLITE_APIEXPORT int unqlite_result_bool(unqlite_context *pCtx, int iBool);
+UNQLITE_APIEXPORT int unqlite_result_double(unqlite_context *pCtx, double Value);
+UNQLITE_APIEXPORT int unqlite_result_null(unqlite_context *pCtx);
+UNQLITE_APIEXPORT int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen);
+UNQLITE_APIEXPORT int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...);
+UNQLITE_APIEXPORT int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_result_resource(unqlite_context *pCtx, void *pUserData);
+
+/* Dynamically Typed Value Object Query Interfaces */
+UNQLITE_APIEXPORT int unqlite_value_is_int(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_float(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_bool(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_string(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_null(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_numeric(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_callable(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_scalar(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_json_array(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_json_object(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_resource(unqlite_value *pVal);
+UNQLITE_APIEXPORT int unqlite_value_is_empty(unqlite_value *pVal);
+
+/* JSON Array/Object Management Interfaces */
+UNQLITE_APIEXPORT unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte);
+UNQLITE_APIEXPORT int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData);
+UNQLITE_APIEXPORT int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue);
+UNQLITE_APIEXPORT int unqlite_array_count(unqlite_value *pArray);
+
+/* Call Context Handling Interfaces */
+UNQLITE_APIEXPORT int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen);
+UNQLITE_APIEXPORT int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...);
+UNQLITE_APIEXPORT int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr);
+UNQLITE_APIEXPORT int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...);
+UNQLITE_APIEXPORT unsigned int unqlite_context_random_num(unqlite_context *pCtx);
+UNQLITE_APIEXPORT int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen);
+UNQLITE_APIEXPORT void * unqlite_context_user_data(unqlite_context *pCtx);
+UNQLITE_APIEXPORT int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData);
+UNQLITE_APIEXPORT void * unqlite_context_peek_aux_data(unqlite_context *pCtx);
+UNQLITE_APIEXPORT unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx);
+UNQLITE_APIEXPORT const char * unqlite_function_name(unqlite_context *pCtx);
+
+/* Call Context Memory Management Interfaces */
+UNQLITE_APIEXPORT void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease);
+UNQLITE_APIEXPORT void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte);
+UNQLITE_APIEXPORT void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk);
+
+/* Global Library Management Interfaces */
+UNQLITE_APIEXPORT int unqlite_lib_config(int nConfigOp,...);
+UNQLITE_APIEXPORT int unqlite_lib_init(void);
+UNQLITE_APIEXPORT int unqlite_lib_shutdown(void);
+UNQLITE_APIEXPORT int unqlite_lib_is_threadsafe(void);
+UNQLITE_APIEXPORT const char * unqlite_lib_version(void);
+UNQLITE_APIEXPORT const char * unqlite_lib_signature(void);
+UNQLITE_APIEXPORT const char * unqlite_lib_ident(void);
+UNQLITE_APIEXPORT const char * unqlite_lib_copyright(void);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _UNQLITE_H_ */
+/*
+ * ----------------------------------------------------------
+ * File: jx9.h
+ * MD5: d23a1e182f596794001533e1d6aa16a0
+ * ----------------------------------------------------------
+ */
+/* 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 "unqlite.h"
+/*
+ * 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/"
+
+/* 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;
+
+#include "unqlite.h"
+
+#if !defined( UNQLITE_ENABLE_JX9_HASH_FUNC )
+#define JX9_DISABLE_HASH_FUNC
+#endif /* UNQLITE_ENABLE_JX9_HASH_FUNC */
+#ifdef UNQLITE_ENABLE_THREADS
+#define JX9_ENABLE_THREADS
+#endif /* UNQLITE_ENABLE_THREADS */
+/* Standard JX9 return values */
+#define JX9_OK      SXRET_OK      /* Successful result */
+/* beginning-of-error-codes */
+#define JX9_NOMEM   UNQLITE_NOMEM     /* Out of memory */
+#define JX9_ABORT   UNQLITE_ABORT   /* Foreign Function request operation abort/Another thread have released this instance */
+#define JX9_IO_ERR  UNQLITE_IOERR      /* IO error */
+#define JX9_CORRUPT UNQLITE_CORRUPT /* Corrupt pointer/Unknown configuration option */
+#define JX9_LOOKED  UNQLITE_LOCKED  /* Forbidden Operation */ 
+#define JX9_COMPILE_ERR UNQLITE_COMPILE_ERR /* Compilation error */
+#define JX9_VM_ERR      UNQLITE_VM_ERR      /* 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;
+/*
+ * 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           UNQLITE_VM_CONFIG_OUTPUT  /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
+#define JX9_VM_CONFIG_IMPORT_PATH      UNQLITE_VM_CONFIG_IMPORT_PATH  /* ONE ARGUMENT: const char *zIncludePath */
+#define JX9_VM_CONFIG_ERR_REPORT       UNQLITE_VM_CONFIG_ERR_REPORT  /* NO ARGUMENTS: Report all run-time errors in the VM output */
+#define JX9_VM_CONFIG_RECURSION_DEPTH  UNQLITE_VM_CONFIG_RECURSION_DEPTH  /* ONE ARGUMENT: int nMaxDepth */
+#define JX9_VM_OUTPUT_LENGTH           UNQLITE_VM_OUTPUT_LENGTH  /* ONE ARGUMENT: unsigned int *pLength */
+#define JX9_VM_CONFIG_CREATE_VAR       UNQLITE_VM_CONFIG_CREATE_VAR  /* TWO ARGUMENTS: const char *zName, jx9_value *pValue */
+#define JX9_VM_CONFIG_HTTP_REQUEST     UNQLITE_VM_CONFIG_HTTP_REQUEST  /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
+#define JX9_VM_CONFIG_SERVER_ATTR      UNQLITE_VM_CONFIG_SERVER_ATTR  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
+#define JX9_VM_CONFIG_ENV_ATTR         UNQLITE_VM_CONFIG_ENV_ATTR  /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
+#define JX9_VM_CONFIG_EXEC_VALUE       UNQLITE_VM_CONFIG_EXEC_VALUE  /* ONE ARGUMENT: jx9_value **ppValue */
+#define JX9_VM_CONFIG_IO_STREAM        UNQLITE_VM_CONFIG_IO_STREAM  /* ONE ARGUMENT: const jx9_io_stream *pStream */
+#define JX9_VM_CONFIG_ARGV_ENTRY       UNQLITE_VM_CONFIG_ARGV_ENTRY  /* ONE ARGUMENT: const char *zValue */
+#define JX9_VM_CONFIG_EXTRACT_OUTPUT   UNQLITE_VM_CONFIG_EXTRACT_OUTPUT  /* 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.
+ */
+#define JX9_CTX_ERR      UNQLITE_CTX_ERR      /* Call context error such as unexpected number of arguments, invalid types and so on. */
+#define JX9_CTX_WARNING  UNQLITE_CTX_WARNING  /* Call context Warning */
+#define JX9_CTX_NOTICE   UNQLITE_CTX_NOTICE   /* 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_PRIVATE int jx9_init(jx9 **ppEngine);
+/*JX9_PRIVATE int jx9_config(jx9 *pEngine, int nConfigOp, ...);*/
+JX9_PRIVATE int jx9_release(jx9 *pEngine);
+/* Compile Interfaces */
+JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm);
+JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm);
+/* Virtual Machine Handling Interfaces */
+JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...);
+/*JX9_PRIVATE int jx9_vm_exec(jx9_vm *pVm, int *pExitStatus);*/
+/*JX9_PRIVATE jx9_value * jx9_vm_extract_variable(jx9_vm *pVm,const char *zVarname);*/
+/*JX9_PRIVATE int jx9_vm_reset(jx9_vm *pVm);*/
+JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm);
+/*JX9_PRIVATE int jx9_vm_dump_v2(jx9_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);*/
+/* In-process Extending Interfaces */
+JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData);
+/*JX9_PRIVATE int jx9_delete_function(jx9_vm *pVm, const char *zName);*/
+JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData);
+/*JX9_PRIVATE int jx9_delete_constant(jx9_vm *pVm, const char *zName);*/
+/* Foreign Function Parameter Values */
+JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue);
+JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue);
+JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue);
+JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue);
+JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen);
+JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue);
+JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict);
+/* Setting The Result Of A Foreign Function */
+JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue);
+JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue);
+JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool);
+JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value);
+JX9_PRIVATE int jx9_result_null(jx9_context *pCtx);
+JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen);
+JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...);
+JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue);
+JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData);
+/* Call Context Handling Interfaces */
+JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen);
+/*JX9_PRIVATE int jx9_context_output_format(jx9_context *pCtx, const char *zFormat, ...);*/
+JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr);
+JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...);
+JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx);
+JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen);
+JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx);
+JX9_PRIVATE int    jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData);
+JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx);
+JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx);
+JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx);
+JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx);
+/* Call Context Memory Management Interfaces */
+JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease);
+JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte);
+JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk);
+/* On Demand Dynamically Typed Value Object allocation interfaces */
+JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm);
+JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm);
+JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue);
+JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx);
+JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx);
+JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue);
+/* Dynamically Typed Value Object Management Interfaces */
+JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue);
+JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue);
+JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool);
+JX9_PRIVATE int jx9_value_null(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value);
+JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen);
+JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...);
+JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData);
+JX9_PRIVATE int jx9_value_release(jx9_value *pVal);
+/* JSON Array/Object Management Interfaces */
+JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte);
+JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
+JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue);
+JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue);
+JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray);
+/* Dynamically Typed Value Object Query Interfaces */
+JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal);
+JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal);
+/* Global Library Management Interfaces */
+/*JX9_PRIVATE int jx9_lib_init(void);*/
+JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...);
+JX9_PRIVATE int jx9_lib_shutdown(void);
+/*JX9_PRIVATE int jx9_lib_is_threadsafe(void);*/
+/*JX9_PRIVATE const char * jx9_lib_version(void);*/
+JX9_PRIVATE const char * jx9_lib_signature(void);
+/*JX9_PRIVATE const char * jx9_lib_ident(void);*/
+/*JX9_PRIVATE const char * jx9_lib_copyright(void);*/
+
+#endif /* _JX9H_ */
+
+/*
+ * ----------------------------------------------------------
+ * File: jx9Int.h
+ * MD5: fb8dffc8ba1425a139091aa145067e16
+ * ----------------------------------------------------------
+ */
+/*
+ * 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
+#ifndef JX9_PRIVATE
+/* Marker for routines not intended for external use */
+#define JX9_PRIVATE static
+#endif /* JX9_PRIVATE */
+#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 this 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_ABORT             = -1,  /* deadliness error, should halt script execution. */
+       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 */
+
+/* api.c */
+JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap);
+JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName);
+JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName);
+/* 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 jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode);
+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 sxu32 SyBinHash(const void *pSrc, sxu32 nLen);
+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 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen);
+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,const 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);
+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);
+JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen);
+#if !defined(JX9_DISABLE_BUILTIN_FUNC) || defined(__APPLE__)
+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
+JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb);
+JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB);
+JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb);
+JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB);
+JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64);
+JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64);
+JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64);
+JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32);
+JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16);
+JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut);
+JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut);
+#endif /* __JX9INT_H__ */
+
+/*
+ * ----------------------------------------------------------
+ * File: unqliteInt.h
+ * MD5: 325816ce05f6adbaab2c39a41875dedd
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: unqliteInt.h v1.7 FreeBSD 2012-11-02 11:25 devel <chm@symisc.net> $ */
+#ifndef __UNQLITEINT_H__
+#define __UNQLITEINT_H__
+/* Internal interface definitions for UnQLite. */
+#ifdef UNQLITE_AMALGAMATION
+/* Marker for routines not intended for external use */
+#define UNQLITE_PRIVATE static
+#define JX9_AMALGAMATION
+#else
+#define UNQLITE_PRIVATE
+#include "unqlite.h"
+#include "jx9Int.h"
+#endif 
+/* forward declaration */
+typedef struct unqlite_db unqlite_db;
+/*
+** The following values may be passed as the second argument to
+** UnqliteOsLock(). The various locks exhibit the following semantics:
+**
+** SHARED:    Any number of processes may hold a SHARED lock simultaneously.
+** RESERVED:  A single process may hold a RESERVED lock on a file at
+**            any time. Other processes may hold and obtain new SHARED locks.
+** PENDING:   A single process may hold a PENDING lock on a file at
+**            any one time. Existing SHARED locks may persist, but no new
+**            SHARED locks may be obtained by other processes.
+** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
+**
+** PENDING_LOCK may not be passed directly to UnqliteOsLock(). Instead, a
+** process that requests an EXCLUSIVE lock may actually obtain a PENDING
+** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
+** UnqliteOsLock().
+*/
+#define NO_LOCK         0
+#define SHARED_LOCK     1
+#define RESERVED_LOCK   2
+#define PENDING_LOCK    3
+#define EXCLUSIVE_LOCK  4
+/*
+ * UnQLite Locking Strategy (Same as SQLite3)
+ *
+ * The following #defines specify the range of bytes used for locking.
+ * SHARED_SIZE is the number of bytes available in the pool from which
+ * a random byte is selected for a shared lock.  The pool of bytes for
+ * shared locks begins at SHARED_FIRST. 
+ *
+ * The same locking strategy and byte ranges are used for Unix and Windows.
+ * This leaves open the possiblity of having clients on winNT, and
+ * unix all talking to the same shared file and all locking correctly.
+ * To do so would require that samba (or whatever
+ * tool is being used for file sharing) implements locks correctly between
+ * windows and unix.  I'm guessing that isn't likely to happen, but by
+ * using the same locking range we are at least open to the possibility.
+ *
+ * Locking in windows is mandatory.  For this reason, we cannot store
+ * actual data in the bytes used for locking.  The pager never allocates
+ * the pages involved in locking therefore.  SHARED_SIZE is selected so
+ * that all locks will fit on a single page even at the minimum page size.
+ * PENDING_BYTE defines the beginning of the locks.  By default PENDING_BYTE
+ * is set high so that we don't have to allocate an unused page except
+ * for very large databases.  But one should test the page skipping logic 
+ * by setting PENDING_BYTE low and running the entire regression suite.
+ *
+ * Changing the value of PENDING_BYTE results in a subtly incompatible
+ * file format.  Depending on how it is changed, you might not notice
+ * the incompatibility right away, even running a full regression test.
+ * The default location of PENDING_BYTE is the first byte past the
+ * 1GB boundary.
+ */
+#define PENDING_BYTE     (0x40000000)
+#define RESERVED_BYTE    (PENDING_BYTE+1)
+#define SHARED_FIRST     (PENDING_BYTE+2)
+#define SHARED_SIZE      510
+/*
+ * The default size of a disk sector in bytes.
+ */
+#ifndef UNQLITE_DEFAULT_SECTOR_SIZE
+#define UNQLITE_DEFAULT_SECTOR_SIZE 512
+#endif
+/*
+ * Each open database file is managed by a separate instance
+ * of the "Pager" structure.
+ */
+typedef struct Pager Pager;
+/*
+ * Each database file to be accessed by the system is an instance
+ * of the following structure.
+ */
+struct unqlite_db
+{
+       Pager *pPager;              /* Pager and Transaction manager */
+       jx9 *pJx9;                  /* Jx9 Engine handle */
+       unqlite_kv_cursor *pCursor; /* Database cursor for common usage */
+};
+/*
+ * Each database connection is an instance of the following structure.
+ */
+struct unqlite
+{
+       SyMemBackend sMem;              /* Memory allocator subsystem */
+       SyBlob sErr;                    /* Error log */
+       unqlite_db sDB;                 /* Storage backend */
+#if defined(UNQLITE_ENABLE_THREADS)
+       const SyMutexMethods *pMethods;  /* Mutex methods */
+       SyMutex *pMutex;                 /* Per-handle mutex */
+#endif
+       unqlite_vm *pVms;                /* List of active VM */
+       sxi32 iVm;                       /* Total number of active VM */
+       sxi32 iFlags;                    /* Control flags (See below)  */
+       unqlite *pNext,*pPrev;           /* List of active DB handles */
+       sxu32 nMagic;                    /* Sanity check against misuse */
+};
+#define UNQLITE_FL_DISABLE_AUTO_COMMIT   0x001 /* Disable auto-commit on close */
+/*
+ * VM control flags (Mostly related to collection handling).
+ */
+#define UNQLITE_VM_COLLECTION_CREATE     0x001 /* Create a new collection */
+#define UNQLITE_VM_COLLECTION_EXISTS     0x002 /* Exists old collection */
+#define UNQLITE_VM_AUTO_LOAD             0x004 /* Auto load a collection from the vfs */
+/* Forward declaration */
+typedef struct unqlite_col_record unqlite_col_record;
+typedef struct unqlite_col unqlite_col;
+/*
+ * Each an in-memory collection record is stored in an instance
+ * of the following structure.
+ */
+struct unqlite_col_record
+{
+       unqlite_col *pCol;                      /* Collecion this record belong */
+       jx9_int64 nId;                          /* Unique record ID */
+       jx9_value sValue;                       /* In-memory value of the record */
+       unqlite_col_record *pNextCol,*pPrevCol; /* Collision chain */
+       unqlite_col_record *pNext,*pPrev;       /* Linked list of records */
+};
+/* 
+ * Magic number to identify a valid collection on disk.
+ */
+#define UNQLITE_COLLECTION_MAGIC 0x611E /* sizeof(unsigned short) 2 bytes */
+/*
+ * A loaded collection is identified by an instance of the following structure.
+ */
+struct unqlite_col
+{
+       unqlite_vm *pVm;   /* VM that own this instance */
+       SyString sName;    /* ID of the collection */
+       sxu32 nHash;       /* sName hash */
+       jx9_value sSchema; /* Collection schema */
+       sxu32 nSchemaOfft; /* Shema offset in sHeader */
+       SyBlob sWorker;    /* General purpose working buffer */
+       SyBlob sHeader;    /* Collection binary header */
+       jx9_int64 nLastid; /* Last collection record ID */
+       jx9_int64 nCurid;  /* Current record ID */
+       jx9_int64 nTotRec; /* Total number of records in the collection */
+       int iFlags;        /* Control flags (see below) */
+       unqlite_col_record **apRecord; /* Hashtable of loaded records */
+       unqlite_col_record *pList;     /* Linked list of records */
+       sxu32 nRec;        /* Total number of records in apRecord[] */     
+       sxu32 nRecSize;    /* apRecord[] size */
+       Sytm sCreation;    /* Colleation creation time */
+       unqlite_kv_cursor *pCursor; /* Cursor pointing to the raw binary data */
+       unqlite_col *pNext,*pPrev;  /* Next and previous collection in the chain */
+       unqlite_col *pNextCol,*pPrevCol; /* Collision chain */
+};
+/*
+ * Each unQLite Virtual Machine resulting from successful compilation of
+ * a Jx9 script is represented by an instance of the following structure.
+ */
+struct unqlite_vm
+{
+       unqlite *pDb;              /* Database handle that own this instance */
+       SyMemBackend sAlloc;       /* Private memory allocator */
+#if defined(UNQLITE_ENABLE_THREADS)
+       SyMutex *pMutex;           /* Recursive mutex associated with this VM. */
+#endif
+       unqlite_col **apCol;       /* Table of loaded collections */
+       unqlite_col *pCol;         /* List of loaded collections */
+       sxu32 iCol;                /* Total number of loaded collections */
+       sxu32 iColSize;            /* apCol[] size  */
+       jx9_vm *pJx9Vm;            /* Compiled Jx9 script*/
+       unqlite_vm *pNext,*pPrev;  /* Linked list of active unQLite VM */
+       sxu32 nMagic;              /* Magic number to avoid misuse */
+};
+/* 
+ * Database signature to identify a valid database image.
+ */
+#define UNQLITE_DB_SIG "unqlite"
+/*
+ * Database magic number (4 bytes).
+ */
+#define UNQLITE_DB_MAGIC   0xDB7C2712
+/*
+ * Maximum page size in bytes.
+ */
+#ifdef UNQLITE_MAX_PAGE_SIZE
+# undef UNQLITE_MAX_PAGE_SIZE
+#endif
+#define UNQLITE_MAX_PAGE_SIZE 65536 /* 65K */
+/*
+ * Minimum page size in bytes.
+ */
+#ifdef UNQLITE_MIN_PAGE_SIZE
+# undef UNQLITE_MIN_PAGE_SIZE
+#endif
+#define UNQLITE_MIN_PAGE_SIZE 512
+/*
+ * The default size of a database page.
+ */
+#ifndef UNQLITE_DEFAULT_PAGE_SIZE
+# undef UNQLITE_DEFAULT_PAGE_SIZE
+#endif
+# define UNQLITE_DEFAULT_PAGE_SIZE 4096 /* 4K */
+/* Forward declaration */
+typedef struct Bitvec Bitvec;
+/* Private library functions */
+/* api.c */
+UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void);
+UNQLITE_PRIVATE int unqliteDataConsumer(
+       const void *pOut,   /* Data to consume */
+       unsigned int nLen,  /* Data length */
+       void *pUserData     /* User private data */
+       );
+UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore(
+       const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */
+       sxu32 nByte        /* zName length */
+       );
+UNQLITE_PRIVATE int unqliteGetPageSize(void);
+UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr);
+UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...);
+UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb);
+/* unql_vm.c */
+UNQLITE_PRIVATE int unqliteExistsCollection(unqlite_vm *pVm, SyString *pName);
+UNQLITE_PRIVATE int unqliteCreateCollection(unqlite_vm *pVm,SyString *pName);
+UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol);
+UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol);
+UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(unqlite_col *pCol,jx9_int64 nId);
+UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol);
+UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol);
+UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue);
+UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(unqlite_col *pCol,jx9_int64 nId,jx9_value *pValue);
+UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(unqlite_vm *pVm,SyString *pCol,int iFlag);
+UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue);
+UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag);
+UNQLITE_PRIVATE int unqliteCollectionDropRecord(unqlite_col *pCol,jx9_int64 nId,int wr_header,int log_err);
+UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol);
+/* unql_jx9.c */
+UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm);
+/* fastjson.c */
+UNQLITE_PRIVATE sxi32 FastJsonEncode(
+       jx9_value *pValue, /* Value to encode */
+       SyBlob *pOut,      /* Store encoded value here */
+       int iNest          /* Nesting limit */ 
+       );
+UNQLITE_PRIVATE sxi32 FastJsonDecode(
+       const void *pIn, /* Binary JSON  */
+       sxu32 nByte,     /* Chunk delimiter */
+       jx9_value *pOut, /* Decoded value */
+       const unsigned char **pzPtr,
+       int iNest /* Nesting limit */
+       );
+/* vfs.c [io_win.c, io_unix.c ] */
+UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void);
+/* mem_kv.c */
+UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void);
+/* lhash_kv.c */
+UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void);
+/* os.c */
+UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset);
+UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset);
+UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size);
+UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags);
+UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize);
+UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType);
+UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType);
+UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut);
+UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id);
+UNQLITE_PRIVATE int unqliteOsOpen(
+  unqlite_vfs *pVfs,
+  SyMemBackend *pAlloc,
+  const char *zPath, 
+  unqlite_file **ppOut, 
+  unsigned int flags
+);
+UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId);
+UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync);
+UNQLITE_PRIVATE int unqliteOsAccess(unqlite_vfs *pVfs,const char *zPath,int flags,int *pResOut);
+/* bitmap.c */
+UNQLITE_PRIVATE Bitvec *unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize);
+UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i);
+UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i);
+UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p);
+/* pager.c */
+UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut);
+UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur);
+UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage);
+UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager);
+UNQLITE_PRIVATE int unqlitePagerOpen(
+  unqlite_vfs *pVfs,       /* The virtual file system to use */
+  unqlite *pDb,            /* Database handle */
+  const char *zFilename,   /* Name of the database file to open */
+  unsigned int iFlags      /* flags controlling this file */
+  );
+UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods);
+UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb);
+UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager);
+UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager);
+UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine);
+UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen);
+UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager);
+#endif /* __UNQLITEINT_H__ */
+/*
+ * ----------------------------------------------------------
+ * File: api.c
+ * MD5: d79e8404e50dacd0ea75635c1ebe553a
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: api.c v2.0 FreeBSD 2012-11-08 23:07 stable <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* This file implement the public interfaces presented to host-applications.
+ * Routines in other files are for internal use by UnQLite and should not be
+ * accessed by users of the library.
+ */
+#define UNQLITE_DB_MISUSE(DB) (DB == 0 || DB->nMagic != UNQLITE_DB_MAGIC)
+#define UNQLITE_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
+/* If another thread have released a working instance, the following macros
+ * evaluates to true. These macros are only used when the library
+ * is built with threading support enabled.
+ */
+#define UNQLITE_THRD_DB_RELEASE(DB) (DB->nMagic != UNQLITE_DB_MAGIC)
+#define UNQLITE_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
+/* IMPLEMENTATION: unqlite@embedded@symisc 118-09-4785 */
+/*
+ * All global variables are collected in the structure named "sUnqlMPGlobal".
+ * That way it is clear in the code when we are using static variable because
+ * its name start with sUnqlMPGlobal.
+ */
+static struct unqlGlobal_Data
+{
+       SyMemBackend sAllocator;                /* Global low level memory allocator */
+#if defined(UNQLITE_ENABLE_THREADS)
+       const SyMutexMethods *pMutexMethods;   /* Mutex methods */
+       SyMutex *pMutex;                       /* Global mutex */
+       sxu32 nThreadingLevel;                 /* Threading level: 0 == Single threaded/1 == Multi-Threaded 
+                                                                                   * The threading level can be set using the [unqlite_lib_config()]
+                                                                                       * interface with a configuration verb set to
+                                                                                       * UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE or 
+                                                                                       * UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI
+                                                                                       */
+#endif
+       SySet kv_storage;                      /* Installed KV storage engines */
+       int iPageSize;                         /* Default Page size */
+       unqlite_vfs *pVfs;                     /* Underlying virtual file system (Vfs) */
+       sxi32 nDB;                             /* Total number of active DB handles */
+       unqlite *pDB;                          /* List of active DB handles */
+       sxu32 nMagic;                          /* Sanity check against library misuse */
+}sUnqlMPGlobal = {
+       {0, 0, 0, 0, 0, 0, 0, 0, {0}}, 
+#if defined(UNQLITE_ENABLE_THREADS)
+       0, 
+       0, 
+       0, 
+#endif
+       {0, 0, 0, 0, 0, 0, 0 },
+       UNQLITE_DEFAULT_PAGE_SIZE,
+       0, 
+       0, 
+       0, 
+       0
+};
+#define UNQLITE_LIB_MAGIC  0xEA1495BA
+#define UNQLITE_LIB_MISUSE (sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC)
+/*
+ * Supported threading level.
+ * These options have meaning only when the library is compiled with multi-threading
+ * support. That is, the UNQLITE_ENABLE_THREADS compile time directive must be defined
+ * when UnQLite is built.
+ * UNQLITE_THREAD_LEVEL_SINGLE:
+ *  In this mode, mutexing is disabled and the library can only be used by a single thread.
+ * UNQLITE_THREAD_LEVEL_MULTI
+ *  In this mode, all mutexes including the recursive mutexes on [unqlite] objects
+ *  are enabled so that the application is free to share the same database handle
+ *  between different threads at the same time.
+ */
+#define UNQLITE_THREAD_LEVEL_SINGLE 1 
+#define UNQLITE_THREAD_LEVEL_MULTI  2
+/*
+ * Find a Key Value storage engine from the set of installed engines.
+ * Return a pointer to the storage engine methods on success. NULL on failure.
+ */
+UNQLITE_PRIVATE unqlite_kv_methods * unqliteFindKVStore(
+       const char *zName, /* Storage engine name [i.e. Hash, B+tree, LSM, etc.] */
+       sxu32 nByte /* zName length */
+       )
+{
+       unqlite_kv_methods **apStore,*pEntry;
+       sxu32 n,nMax;
+       /* Point to the set of installed engines */
+       apStore = (unqlite_kv_methods **)SySetBasePtr(&sUnqlMPGlobal.kv_storage);
+       nMax = SySetUsed(&sUnqlMPGlobal.kv_storage);
+       for( n = 0 ; n < nMax; ++n ){
+               pEntry = apStore[n];
+               if( nByte == SyStrlen(pEntry->zName) && SyStrnicmp(pEntry->zName,zName,nByte) == 0 ){
+                       /* Storage engine found */
+                       return pEntry;
+               }
+       }
+       /* No such entry, return NULL */
+       return 0;
+}
+/*
+ * Configure the UnQLite library.
+ * Return UNQLITE_OK on success. Any other return value indicates failure.
+ * Refer to [unqlite_lib_config()].
+ */
+static sxi32 unqliteCoreConfigure(sxi32 nOp, va_list ap)
+{
+       int rc = UNQLITE_OK;
+       switch(nOp){
+           case UNQLITE_LIB_CONFIG_PAGE_SIZE: {
+                       /* Default page size: Must be a power of two */
+                       int iPage = va_arg(ap,int);
+                       if( iPage >= UNQLITE_MIN_PAGE_SIZE && iPage <= UNQLITE_MAX_PAGE_SIZE ){
+                               if( !(iPage & (iPage - 1)) ){
+                                       sUnqlMPGlobal.iPageSize = iPage;
+                               }else{
+                                       /* Invalid page size */
+                                       rc = UNQLITE_INVALID;
+                               }
+                       }else{
+                               /* Invalid page size */
+                               rc = UNQLITE_INVALID;
+                       }
+                       break;
+                                                                                  }
+           case UNQLITE_LIB_CONFIG_STORAGE_ENGINE: {
+                       /* Install a key value storage engine */
+                       unqlite_kv_methods *pMethods = va_arg(ap,unqlite_kv_methods *);
+                       /* Make sure we are delaing with a valid methods */
+                       if( pMethods == 0 || SX_EMPTY_STR(pMethods->zName) || pMethods->xSeek == 0 || pMethods->xData == 0
+                               || pMethods->xKey == 0 || pMethods->xDataLength == 0 || pMethods->xKeyLength == 0 
+                               || pMethods->szKv < (int)sizeof(unqlite_kv_engine) ){
+                                       rc = UNQLITE_INVALID;
+                                       break;
+                       }
+                       /* Install it */
+                       rc = SySetPut(&sUnqlMPGlobal.kv_storage,(const void *)&pMethods);
+                       break;
+                                                                                               }
+           case UNQLITE_LIB_CONFIG_VFS:{
+                       /* Install a virtual file system */
+                       unqlite_vfs *pVfs = va_arg(ap,unqlite_vfs *);
+                       if( pVfs ){
+                        sUnqlMPGlobal.pVfs = pVfs;
+                       }
+                       break;
+                                                               }
+               case UNQLITE_LIB_CONFIG_USER_MALLOC: {
+                       /* Use an alternative low-level memory allocation routines */
+                       const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
+                       /* Save the memory failure callback (if available) */
+                       ProcMemError xMemErr = sUnqlMPGlobal.sAllocator.xMemError;
+                       void *pMemErr = sUnqlMPGlobal.sAllocator.pUserData;
+                       if( pMethods == 0 ){
+                               /* Use the built-in memory allocation subsystem */
+                               rc = SyMemBackendInit(&sUnqlMPGlobal.sAllocator, xMemErr, pMemErr);
+                       }else{
+                               rc = SyMemBackendInitFromOthers(&sUnqlMPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
+                       }
+                       break;
+                                                                                 }
+               case UNQLITE_LIB_CONFIG_MEM_ERR_CALLBACK: {
+                       /* Memory failure callback */
+                       ProcMemError xMemErr = va_arg(ap, ProcMemError);
+                       void *pUserData = va_arg(ap, void *);
+                       sUnqlMPGlobal.sAllocator.xMemError = xMemErr;
+                       sUnqlMPGlobal.sAllocator.pUserData = pUserData;
+                       break;
+                                                                                                }        
+               case UNQLITE_LIB_CONFIG_USER_MUTEX: {
+#if defined(UNQLITE_ENABLE_THREADS)
+                       /* Use an alternative low-level mutex subsystem */
+                       const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
+#if defined (UNTRUST)
+                       if( pMethods == 0 ){
+                               rc = UNQLITE_CORRUPT;
+                       }
+#endif
+                       /* Sanity check */
+                       if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
+                               /* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
+                               rc = UNQLITE_CORRUPT;
+                               break;
+                       }
+                       if( sUnqlMPGlobal.pMutexMethods ){
+                               /* Overwrite the previous mutex subsystem */
+                               SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex);
+                               if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){
+                                       sUnqlMPGlobal.pMutexMethods->xGlobalRelease();
+                               }
+                               sUnqlMPGlobal.pMutex = 0;
+                       }
+                       /* Initialize and install the new mutex subsystem */
+                       if( pMethods->xGlobalInit ){
+                               rc = pMethods->xGlobalInit();
+                               if ( rc != UNQLITE_OK ){
+                                       break;
+                               }
+                       }
+                       /* Create the global mutex */
+                       sUnqlMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
+                       if( sUnqlMPGlobal.pMutex == 0 ){
+                               /*
+                                * If the supplied mutex subsystem is so sick that we are unable to
+                                * create a single mutex, there is no much we can do here.
+                                */
+                               if( pMethods->xGlobalRelease ){
+                                       pMethods->xGlobalRelease();
+                               }
+                               rc = UNQLITE_CORRUPT;
+                               break;
+                       }
+                       sUnqlMPGlobal.pMutexMethods = pMethods;                 
+                       if( sUnqlMPGlobal.nThreadingLevel == 0 ){
+                               /* Set a default threading level */
+                               sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI; 
+                       }
+#endif
+                       break;
+                                                                                  }
+               case UNQLITE_LIB_CONFIG_THREAD_LEVEL_SINGLE:
+#if defined(UNQLITE_ENABLE_THREADS)
+                       /* Single thread mode (Only one thread is allowed to play with the library) */
+                       sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_SINGLE;
+                       jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE);
+#endif
+                       break;
+               case UNQLITE_LIB_CONFIG_THREAD_LEVEL_MULTI:
+#if defined(UNQLITE_ENABLE_THREADS)
+                       /* Multi-threading mode (library is thread safe and database handles and virtual machines
+                        * may be shared between multiple threads).
+                        */
+                       sUnqlMPGlobal.nThreadingLevel = UNQLITE_THREAD_LEVEL_MULTI;
+                       jx9_lib_config(JX9_LIB_CONFIG_THREAD_LEVEL_MULTI);
+#endif
+                       break;
+               default:
+                       /* Unknown configuration option */
+                       rc = UNQLITE_CORRUPT;
+                       break;
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_lib_config()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_lib_config(int nConfigOp,...)
+{
+       va_list ap;
+       int rc;
+       if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){
+               /* Library is already initialized, this operation is forbidden */
+               return UNQLITE_LOCKED;
+       }
+       va_start(ap,nConfigOp);
+       rc = unqliteCoreConfigure(nConfigOp,ap);
+       va_end(ap);
+       return rc;
+}
+/*
+ * Global library initialization
+ * Refer to [unqlite_lib_init()]
+ * This routine must be called to initialize the memory allocation subsystem, the mutex 
+ * subsystem prior to doing any serious work with the library. The first thread to call
+ * this routine does the initialization process and set the magic number so no body later
+ * can re-initialize the library. If subsequent threads call this  routine before the first
+ * thread have finished the initialization process, then the subsequent threads must block 
+ * until the initialization process is done.
+ */
+static sxi32 unqliteCoreInitialize(void)
+{
+       const unqlite_kv_methods *pMethods;
+       const unqlite_vfs *pVfs; /* Built-in vfs */
+#if defined(UNQLITE_ENABLE_THREADS)
+       const SyMutexMethods *pMutexMethods = 0;
+       SyMutex *pMaster = 0;
+#endif
+       int rc;
+       /*
+        * If the library is already initialized, then a call to this routine
+        * is a no-op.
+        */
+       if( sUnqlMPGlobal.nMagic == UNQLITE_LIB_MAGIC ){
+               return UNQLITE_OK; /* Already initialized */
+       }
+       if( sUnqlMPGlobal.pVfs == 0 ){
+               /* Point to the built-in vfs */
+               pVfs = unqliteExportBuiltinVfs();
+               /* Install it */
+               unqlite_lib_config(UNQLITE_LIB_CONFIG_VFS, pVfs);
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+       if( sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_SINGLE ){
+               pMutexMethods = sUnqlMPGlobal.pMutexMethods;
+               if( pMutexMethods == 0 ){
+                       /* Use the built-in mutex subsystem */
+                       pMutexMethods = SyMutexExportMethods();
+                       if( pMutexMethods == 0 ){
+                               return UNQLITE_CORRUPT; /* Can't happen */
+                       }
+                       /* Install the mutex subsystem */
+                       rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MUTEX, pMutexMethods);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+               }
+               /* Obtain a static mutex so we can initialize the library without calling malloc() */
+               pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
+               if( pMaster == 0 ){
+                       return UNQLITE_CORRUPT; /* Can't happen */
+               }
+       }
+       /* Lock the master mutex */
+       rc = UNQLITE_OK;
+       SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
+       if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
+#endif
+               if( sUnqlMPGlobal.sAllocator.pMethods == 0 ){
+                       /* Install a memory subsystem */
+                       rc = unqlite_lib_config(UNQLITE_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
+                       if( rc != UNQLITE_OK ){
+                               /* If we are unable to initialize the memory backend, there is no much we can do here.*/
+                               goto End;
+                       }
+               }
+#if defined(UNQLITE_ENABLE_THREADS)
+               if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
+                       /* Protect the memory allocation subsystem */
+                       rc = SyMemBackendMakeThreadSafe(&sUnqlMPGlobal.sAllocator, sUnqlMPGlobal.pMutexMethods);
+                       if( rc != UNQLITE_OK ){
+                               goto End;
+                       }
+               }
+#endif
+               SySetInit(&sUnqlMPGlobal.kv_storage,&sUnqlMPGlobal.sAllocator,sizeof(unqlite_kv_methods *));
+               /* Install the built-in Key Value storage engines */
+               pMethods = unqliteExportMemKvStorage(); /* In-memory storage */
+               unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods);
+               /* Default disk key/value storage engine */
+               pMethods = unqliteExportDiskKvStorage(); /* Disk storage */
+               unqlite_lib_config(UNQLITE_LIB_CONFIG_STORAGE_ENGINE,pMethods);
+               /* Default page size */
+               if( sUnqlMPGlobal.iPageSize < UNQLITE_MIN_PAGE_SIZE ){
+                       unqlite_lib_config(UNQLITE_LIB_CONFIG_PAGE_SIZE,UNQLITE_DEFAULT_PAGE_SIZE);
+               }
+               /* Our library is initialized, set the magic number */
+               sUnqlMPGlobal.nMagic = UNQLITE_LIB_MAGIC;
+               rc = UNQLITE_OK;
+#if defined(UNQLITE_ENABLE_THREADS)
+       } /* sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC */
+#endif
+End:
+#if defined(UNQLITE_ENABLE_THREADS)
+       /* Unlock the master mutex */
+       SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
+#endif
+       return rc;
+}
+/* Forward declaration */
+static int unqliteVmRelease(unqlite_vm *pVm);
+/*
+ * Release a single instance of an unqlite database handle.
+ */
+static int unqliteDbRelease(unqlite *pDb)
+{
+       unqlite_db *pStore = &pDb->sDB;
+       unqlite_vm *pVm,*pNext;
+       int rc = UNQLITE_OK;
+       if( (pDb->iFlags & UNQLITE_FL_DISABLE_AUTO_COMMIT) == 0 ){
+               /* Commit any outstanding transaction */
+               rc = unqlitePagerCommit(pStore->pPager);
+               if( rc != UNQLITE_OK ){
+                       /* Rollback the transaction */
+                       rc = unqlitePagerRollback(pStore->pPager,FALSE);
+               }
+       }else{
+               /* Rollback any outstanding transaction */
+               rc = unqlitePagerRollback(pStore->pPager,FALSE);
+       }
+       /* Close the pager */
+       unqlitePagerClose(pStore->pPager);
+       /* Release any active VM's */
+       pVm = pDb->pVms;
+       for(;;){
+               if( pDb->iVm < 1 ){
+                       break;
+               }
+               /* Point to the next entry */
+               pNext = pVm->pNext;
+               unqliteVmRelease(pVm);
+               pVm = pNext;
+               pDb->iVm--;
+       }
+       /* Release the Jx9 handle */
+       jx9_release(pStore->pJx9);
+       /* Set a dummy magic number */
+       pDb->nMagic = 0x7250;
+       /* Release the whole memory subsystem */
+       SyMemBackendRelease(&pDb->sMem);
+       /* Commit or rollback result */
+       return rc;
+}
+/*
+ * Release all resources consumed by the library.
+ * Note: This call is not thread safe. Refer to [unqlite_lib_shutdown()].
+ */
+static void unqliteCoreShutdown(void)
+{
+       unqlite *pDb, *pNext;
+       /* Release all active databases handles */
+       pDb = sUnqlMPGlobal.pDB;
+       for(;;){
+               if( sUnqlMPGlobal.nDB < 1 ){
+                       break;
+               }
+               pNext = pDb->pNext;
+               unqliteDbRelease(pDb); 
+               pDb = pNext;
+               sUnqlMPGlobal.nDB--;
+       }
+       /* Release the storage methods container */
+       SySetRelease(&sUnqlMPGlobal.kv_storage);
+#if defined(UNQLITE_ENABLE_THREADS)
+       /* Release the mutex subsystem */
+       if( sUnqlMPGlobal.pMutexMethods ){
+               if( sUnqlMPGlobal.pMutex ){
+                       SyMutexRelease(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex);
+                       sUnqlMPGlobal.pMutex = 0;
+               }
+               if( sUnqlMPGlobal.pMutexMethods->xGlobalRelease ){
+                       sUnqlMPGlobal.pMutexMethods->xGlobalRelease();
+               }
+               sUnqlMPGlobal.pMutexMethods = 0;
+       }
+       sUnqlMPGlobal.nThreadingLevel = 0;
+#endif
+       if( sUnqlMPGlobal.sAllocator.pMethods ){
+               /* Release the memory backend */
+               SyMemBackendRelease(&sUnqlMPGlobal.sAllocator);
+       }
+       sUnqlMPGlobal.nMagic = 0x1764;
+       /* Finally, shutdown the Jx9 library */
+       jx9_lib_shutdown();
+}
+/*
+ * [CAPIREF: unqlite_lib_init()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_lib_init(void)
+{
+       int rc;
+       rc = unqliteCoreInitialize();
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_lib_shutdown()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_lib_shutdown(void)
+{
+       if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
+               /* Already shut */
+               return UNQLITE_OK;
+       }
+       unqliteCoreShutdown();
+       return UNQLITE_OK;
+}
+/*
+ * [CAPIREF: unqlite_lib_is_threadsafe()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_lib_is_threadsafe(void)
+{
+       if( sUnqlMPGlobal.nMagic != UNQLITE_LIB_MAGIC ){
+               return 0;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+               if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
+                       /* Muli-threading support is enabled */
+                       return 1;
+               }else{
+                       /* Single-threading */
+                       return 0;
+               }
+#else
+       return 0;
+#endif
+}
+/*
+ *
+ * [CAPIREF: unqlite_lib_version()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+const char * unqlite_lib_version(void)
+{
+       return UNQLITE_VERSION;
+}
+/*
+ *
+ * [CAPIREF: unqlite_lib_signature()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+const char * unqlite_lib_signature(void)
+{
+       return UNQLITE_SIG;
+}
+/*
+ *
+ * [CAPIREF: unqlite_lib_ident()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+const char * unqlite_lib_ident(void)
+{
+       return UNQLITE_IDENT;
+}
+/*
+ *
+ * [CAPIREF: unqlite_lib_copyright()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+const char * unqlite_lib_copyright(void)
+{
+       return UNQLITE_COPYRIGHT;
+}
+/*
+ * Remove harmfull and/or stale flags passed to the [unqlite_open()] interface.
+ */
+static unsigned int unqliteSanityzeFlag(unsigned int iFlags)
+{
+       iFlags &= ~UNQLITE_OPEN_EXCLUSIVE; /* Reserved flag */
+       if( iFlags & UNQLITE_OPEN_TEMP_DB ){
+               /* Omit journaling for temporary database */
+               iFlags |= UNQLITE_OPEN_OMIT_JOURNALING|UNQLITE_OPEN_CREATE;
+       }
+       if( (iFlags & (UNQLITE_OPEN_READONLY|UNQLITE_OPEN_READWRITE)) == 0 ){
+               /* Auto-append the R+W flag */
+               iFlags |= UNQLITE_OPEN_READWRITE;
+       }
+       if( iFlags & UNQLITE_OPEN_CREATE ){
+               iFlags &= ~(UNQLITE_OPEN_MMAP|UNQLITE_OPEN_READONLY);
+               /* Auto-append the R+W flag */
+               iFlags |= UNQLITE_OPEN_READWRITE;
+       }else{
+               if( iFlags & UNQLITE_OPEN_READONLY ){
+                       iFlags &= ~UNQLITE_OPEN_READWRITE;
+               }else if( iFlags & UNQLITE_OPEN_READWRITE ){
+                       iFlags &= ~UNQLITE_OPEN_MMAP;
+               }
+       }
+       return iFlags;
+}
+/*
+ * This routine does the work of initializing a database handle on behalf
+ * of [unqlite_open()].
+ */
+static int unqliteInitDatabase(
+       unqlite *pDB,            /* Database handle */
+       SyMemBackend *pParent,   /* Master memory backend */
+       const char *zFilename,   /* Target database */
+       unsigned int iFlags      /* Open flags */
+       )
+{
+       unqlite_db *pStorage = &pDB->sDB;
+       int rc;
+       /* Initialiaze the memory subsystem */
+       SyMemBackendInitFromParent(&pDB->sMem,pParent);
+//#if defined(UNQLITE_ENABLE_THREADS)
+//     /* No need for internal mutexes */
+//     SyMemBackendDisbaleMutexing(&pDB->sMem);
+//#endif
+       SyBlobInit(&pDB->sErr,&pDB->sMem);
+       /* Sanityze flags */
+       iFlags = unqliteSanityzeFlag(iFlags);
+       /* Init the pager and the transaction manager */
+       rc = unqlitePagerOpen(sUnqlMPGlobal.pVfs,pDB,zFilename,iFlags);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Allocate a new Jx9 engine handle */
+       rc = jx9_init(&pStorage->pJx9);
+       if( rc != JX9_OK ){
+               return rc;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Allocate and initialize a new UnQLite Virtual Mahcine and attach it
+ * to the compiled Jx9 script.
+ */
+static int unqliteInitVm(unqlite *pDb,jx9_vm *pJx9Vm,unqlite_vm **ppOut)
+{
+       unqlite_vm *pVm;
+
+       *ppOut = 0;
+       /* Allocate a new VM instance */
+       pVm = (unqlite_vm *)SyMemBackendPoolAlloc(&pDb->sMem,sizeof(unqlite_vm));
+       if( pVm == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pVm,sizeof(unqlite_vm));
+       /* Initialize */
+       SyMemBackendInitFromParent(&pVm->sAlloc,&pDb->sMem);
+       /* Allocate a new collection table */
+       pVm->apCol = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc,32 * sizeof(unqlite_col *)); 
+       if( pVm->apCol == 0 ){
+               goto fail;
+       }
+       pVm->iColSize = 32; /* Must be a power of two */
+       /* Zero the table */
+       SyZero((void *)pVm->apCol,pVm->iColSize * sizeof(unqlite_col *));
+#if defined(UNQLITE_ENABLE_THREADS)
+       if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE ){
+                /* Associate a recursive mutex with this instance */
+                pVm->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
+                if( pVm->pMutex == 0 ){
+                        goto fail;
+                }
+        }
+#endif
+       /* Link the VM to the list of active virtual machines */
+       pVm->pJx9Vm = pJx9Vm;
+       pVm->pDb = pDb;
+       MACRO_LD_PUSH(pDb->pVms,pVm);
+       pDb->iVm++;
+       /* Register Jx9 functions */
+       unqliteRegisterJx9Functions(pVm);
+       /* Set the magic number */
+       pVm->nMagic = JX9_VM_INIT; /* Same magic number as Jx9 */
+       /* All done */
+       *ppOut = pVm;
+       return UNQLITE_OK;
+fail:
+       SyMemBackendRelease(&pVm->sAlloc);
+       SyMemBackendPoolFree(&pDb->sMem,pVm);
+       return UNQLITE_NOMEM;
+}
+/*
+ * Release an active VM.
+ */
+static int unqliteVmRelease(unqlite_vm *pVm)
+{
+       /* Release the Jx9 VM */
+       jx9_vm_release(pVm->pJx9Vm);
+       /* Release the private memory backend */
+       SyMemBackendRelease(&pVm->sAlloc);
+       /* Upper layer will discard this VM from the list
+        * of active VM.
+        */
+       return UNQLITE_OK;
+}
+/*
+ * Return the default page size.
+ */
+UNQLITE_PRIVATE int unqliteGetPageSize(void)
+{
+       int iSize =  sUnqlMPGlobal.iPageSize;
+       if( iSize < UNQLITE_MIN_PAGE_SIZE || iSize > UNQLITE_MAX_PAGE_SIZE ){
+               iSize = UNQLITE_DEFAULT_PAGE_SIZE;
+       }
+       return iSize;
+}
+/*
+ * Generate an error message.
+ */
+UNQLITE_PRIVATE int unqliteGenError(unqlite *pDb,const char *zErr)
+{
+       int rc;
+       /* Append the error message */
+       rc = SyBlobAppend(&pDb->sErr,(const void *)zErr,SyStrlen(zErr));
+       /* Append a new line */
+       SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char));
+       return rc;
+}
+/*
+ * Generate an error message (Printf like).
+ */
+UNQLITE_PRIVATE int unqliteGenErrorFormat(unqlite *pDb,const char *zFmt,...)
+{
+       va_list ap;
+       int rc;
+       va_start(ap,zFmt);
+       rc = SyBlobFormatAp(&pDb->sErr,zFmt,ap);
+       va_end(ap);
+       /* Append a new line */
+       SyBlobAppend(&pDb->sErr,(const void *)"\n",sizeof(char));
+       return rc;
+}
+/*
+ * Generate an error message (Out of memory).
+ */
+UNQLITE_PRIVATE int unqliteGenOutofMem(unqlite *pDb)
+{
+       int rc;
+       rc = unqliteGenError(pDb,"unQLite is running out of memory");
+       return rc;
+}
+/*
+ * Configure a working UnQLite database handle.
+ */
+static int unqliteConfigure(unqlite *pDb,int nOp,va_list ap)
+{
+       int rc = UNQLITE_OK;
+       switch(nOp){
+       case UNQLITE_CONFIG_JX9_ERR_LOG:
+               /* Jx9 compile-time error log */
+               rc = jx9EngineConfig(pDb->sDB.pJx9,JX9_CONFIG_ERR_LOG,ap);
+               break;
+       case UNQLITE_CONFIG_MAX_PAGE_CACHE: {
+               int max_page = va_arg(ap,int);
+               /* Maximum number of page to cache (Simple hint). */
+               rc = unqlitePagerSetCachesize(pDb->sDB.pPager,max_page);
+               break;
+                                                                               }
+       case UNQLITE_CONFIG_ERR_LOG: {
+               /* Database error log if any */
+               const char **pzPtr = va_arg(ap, const char **);
+               int *pLen = va_arg(ap, int *);
+               if( pzPtr == 0 ){
+                       rc = JX9_CORRUPT;
+                       break;
+               }
+               /* NULL terminate the error-log buffer */
+               SyBlobNullAppend(&pDb->sErr);
+               /* Point to the error-log buffer */
+               *pzPtr = (const char *)SyBlobData(&pDb->sErr);
+               if( pLen ){
+                       if( SyBlobLength(&pDb->sErr) > 1 /* NULL '\0' terminator */ ){
+                               *pLen = (int)SyBlobLength(&pDb->sErr);
+                       }else{
+                               *pLen = 0;
+                       }
+               }
+               break;
+                                                                }
+       case UNQLITE_CONFIG_DISABLE_AUTO_COMMIT:{
+               /* Disable auto-commit */
+               pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
+               break;
+                                                                                       }
+       case UNQLITE_CONFIG_GET_KV_NAME: {
+               /* Name of the underlying KV storage engine */
+               const char **pzPtr = va_arg(ap,const char **);
+               if( pzPtr ){
+                       unqlite_kv_engine *pEngine;
+                       pEngine = unqlitePagerGetKvEngine(pDb);
+                       /* Point to the name */
+                       *pzPtr = pEngine->pIo->pMethods->zName;
+               }
+               break;
+                                                                        }
+       default:
+               /* Unknown configuration option */
+               rc = UNQLITE_UNKNOWN;
+               break;
+       }
+       return rc;
+}
+/*
+ * Export the global (master) memory allocator to submodules.
+ */
+UNQLITE_PRIVATE const SyMemBackend * unqliteExportMemBackend(void)
+{
+       return &sUnqlMPGlobal.sAllocator;
+}
+/*
+ * [CAPIREF: unqlite_open()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_open(unqlite **ppDB,const char *zFilename,unsigned int iMode)
+{
+       unqlite *pHandle;
+       int rc;
+#if defined(UNTRUST)
+       if( ppDB == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       *ppDB = 0;
+       /* One-time automatic library initialization */
+       rc = unqliteCoreInitialize();
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Allocate a new database handle */
+       pHandle = (unqlite *)SyMemBackendPoolAlloc(&sUnqlMPGlobal.sAllocator, sizeof(unqlite));
+       if( pHandle == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pHandle,sizeof(unqlite));
+       if( iMode < 1 ){
+               /* Assume a read-only database */
+               iMode = UNQLITE_OPEN_READONLY|UNQLITE_OPEN_MMAP;
+       }
+       /* Init the database */
+       rc = unqliteInitDatabase(pHandle,&sUnqlMPGlobal.sAllocator,zFilename,iMode);
+       if( rc != UNQLITE_OK ){
+               goto Release;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+       if( !(iMode & UNQLITE_OPEN_NOMUTEX) && (sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE) ){
+                /* Associate a recursive mutex with this instance */
+                pHandle->pMutex = SyMutexNew(sUnqlMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
+                if( pHandle->pMutex == 0 ){
+                        rc = UNQLITE_NOMEM;
+                        goto Release;
+                }
+        }
+#endif
+       /* Link to the list of active DB handles */
+#if defined(UNQLITE_ENABLE_THREADS)
+       /* Enter the global mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
+#endif
+        MACRO_LD_PUSH(sUnqlMPGlobal.pDB,pHandle);
+        sUnqlMPGlobal.nDB++;
+#if defined(UNQLITE_ENABLE_THREADS)
+       /* Leave the global mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
+#endif
+       /* Set the magic number to identify a valid DB handle */
+        pHandle->nMagic = UNQLITE_DB_MAGIC;
+       /* Make the handle available to the caller */
+       *ppDB = pHandle;
+       return UNQLITE_OK;
+Release:
+       SyMemBackendRelease(&pHandle->sMem);
+       SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pHandle);
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_config()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_config(unqlite *pDb,int nConfigOp,...)
+{
+       va_list ap;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        va_start(ap, nConfigOp);
+        rc = unqliteConfigure(&(*pDb),nConfigOp, ap);
+        va_end(ap);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_close()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_close(unqlite *pDb)
+{
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Release the database handle */
+       rc = unqliteDbRelease(pDb);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        /* Release DB mutex */
+        SyMutexRelease(sUnqlMPGlobal.pMutexMethods, pDb->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+#if defined(UNQLITE_ENABLE_THREADS)
+       /* Enter the global mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
+#endif
+       /* Unlink from the list of active database handles */
+        MACRO_LD_REMOVE(sUnqlMPGlobal.pDB, pDb);
+       sUnqlMPGlobal.nDB--;
+#if defined(UNQLITE_ENABLE_THREADS)
+       /* Leave the global mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods, sUnqlMPGlobal.pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel == UNQLITE_THREAD_LEVEL_SINGLE */
+#endif
+       /* Release the memory chunk allocated to this handle */
+       SyMemBackendPoolFree(&sUnqlMPGlobal.sAllocator,pDb);
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_compile()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_compile(unqlite *pDb,const char *zJx9,int nByte,unqlite_vm **ppOut)
+{
+       jx9_vm *pVm;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT;
+        }
+#endif
+        /* Compile the Jx9 script first */
+        rc = jx9_compile(pDb->sDB.pJx9,zJx9,nByte,&pVm);
+        if( rc == JX9_OK ){
+                /* Allocate a new unqlite VM instance */
+                rc = unqliteInitVm(pDb,pVm,ppOut);
+                if( rc != UNQLITE_OK ){
+                        /* Release the Jx9 VM */
+                        jx9_vm_release(pVm);
+                }
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_compile_file()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_compile_file(unqlite *pDb,const char *zPath,unqlite_vm **ppOut)
+{
+       jx9_vm *pVm;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT;
+        }
+#endif
+        /* Compile the Jx9 script first */
+       rc = jx9_compile_file(pDb->sDB.pJx9,zPath,&pVm);
+       if( rc == JX9_OK ){
+               /* Allocate a new unqlite VM instance */
+               rc = unqliteInitVm(pDb,pVm,ppOut);
+               if( rc != UNQLITE_OK ){
+                       /* Release the Jx9 VM */
+                       jx9_vm_release(pVm);
+               }
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * Configure an unqlite virtual machine (Mostly Jx9 VM) instance.
+ */
+static int unqliteVmConfig(unqlite_vm *pVm,sxi32 iOp,va_list ap)
+{
+       int rc;
+       rc = jx9VmConfigure(pVm->pJx9Vm,iOp,ap);
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_vm_config()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_vm_config(unqlite_vm *pVm,int iOp,...)
+{
+       va_list ap;
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        va_start(ap,iOp);
+        rc = unqliteVmConfig(pVm,iOp,ap);
+        va_end(ap);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_vm_exec()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_vm_exec(unqlite_vm *pVm)
+{
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Execute the Jx9 bytecode program */
+        rc = jx9VmByteCodeExec(pVm->pJx9Vm);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_vm_release()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_vm_release(unqlite_vm *pVm)
+{
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Release the VM */
+        rc = unqliteVmRelease(pVm);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave VM mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        /* Release VM mutex */
+        SyMutexRelease(sUnqlMPGlobal.pMutexMethods,pVm->pMutex) /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        if( rc == UNQLITE_OK ){
+                unqlite *pDb = pVm->pDb;
+                /* Unlink from the list of active VM's */
+#if defined(UNQLITE_ENABLE_THREADS)
+                       /* Acquire DB mutex */
+                       SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+                       if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                               UNQLITE_THRD_DB_RELEASE(pDb) ){
+                                       return UNQLITE_ABORT; /* Another thread have released this instance */
+                       }
+#endif
+               MACRO_LD_REMOVE(pDb->pVms, pVm);
+               pDb->iVm--;
+               /* Release the memory chunk allocated to this instance */
+               SyMemBackendPoolFree(&pDb->sMem,pVm);
+#if defined(UNQLITE_ENABLE_THREADS)
+                       /* Leave DB mutex */
+                       SyMutexLeave(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        }
+        return rc;
+}
+/*
+ * [CAPIREF: unqlite_vm_reset()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_vm_reset(unqlite_vm *pVm)
+{
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Reset the Jx9 VM */
+        rc = jx9VmReset(pVm->pJx9Vm);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_vm_dump()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_vm_dump(unqlite_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData)
+{
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Dump the Jx9 VM */
+        rc = jx9VmDump(pVm->pJx9Vm,xConsumer,pUserData);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_vm_extract_variable()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unqlite_value * unqlite_vm_extract_variable(unqlite_vm *pVm,const char *zVarname)
+{
+       unqlite_value *pValue;
+       SyString sVariable;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return 0;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return 0; /* Another thread have released this instance */
+        }
+#endif
+        /* Extract the target variable */
+       SyStringInitFromBuf(&sVariable,zVarname,SyStrlen(zVarname));
+       pValue = jx9VmExtractVariable(pVm->pJx9Vm,&sVariable);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return pValue;
+}
+/*
+ * [CAPIREF: unqlite_create_function()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_create_function(unqlite_vm *pVm, const char *zName,int (*xFunc)(unqlite_context *,int,unqlite_value **),void *pUserData)
+{
+       SyString sName;
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+       SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
+       /* Remove leading and trailing white spaces */
+       SyStringFullTrim(&sName);
+       /* Ticket 1433-003: NULL values are not allowed */
+       if( sName.nByte < 1 || xFunc == 0 ){
+               return UNQLITE_INVALID;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Install the foreign function */
+        rc = jx9VmInstallForeignFunction(pVm->pJx9Vm,&sName,xFunc,pUserData);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_delete_function()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_delete_function(unqlite_vm *pVm, const char *zName)
+{
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Unlink the foreign function */
+        rc = jx9DeleteFunction(pVm->pJx9Vm,zName);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_create_constant()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_create_constant(unqlite_vm *pVm,const char *zName,void (*xExpand)(unqlite_value *, void *),void *pUserData)
+{
+       SyString sName;
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+       SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
+       /* Remove leading and trailing white spaces */
+       SyStringFullTrim(&sName);
+       if( sName.nByte < 1 ){
+               /* Empty constant name */
+               return UNQLITE_INVALID;
+       }
+       /* TICKET 1433-003: NULL pointer is harmless operation */
+       if( xExpand == 0 ){
+               return UNQLITE_INVALID;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Install the foreign constant */
+        rc = jx9VmRegisterConstant(pVm->pJx9Vm,&sName,xExpand,pUserData);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_delete_constant()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_delete_constant(unqlite_vm *pVm, const char *zName)
+{
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Unlink the foreign constant */
+        rc = Jx9DeleteConstant(pVm->pJx9Vm,zName);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_value_int()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_int(unqlite_value *pVal, int iValue)
+{
+       return jx9_value_int(pVal,iValue);
+}
+/*
+ * [CAPIREF: unqlite_value_int64()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_int64(unqlite_value *pVal,unqlite_int64 iValue)
+{
+       return jx9_value_int64(pVal,iValue);
+}
+/*
+ * [CAPIREF: unqlite_value_bool()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_bool(unqlite_value *pVal, int iBool)
+{
+       return jx9_value_bool(pVal,iBool);
+}
+/*
+ * [CAPIREF: unqlite_value_null()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_null(unqlite_value *pVal)
+{
+       return jx9_value_null(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_double()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_double(unqlite_value *pVal, double Value)
+{
+       return jx9_value_double(pVal,Value);
+}
+/*
+ * [CAPIREF: unqlite_value_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_string(unqlite_value *pVal, const char *zString, int nLen)
+{
+       return jx9_value_string(pVal,zString,nLen);
+}
+/*
+ * [CAPIREF: unqlite_value_string_format()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_string_format(unqlite_value *pVal, const char *zFormat,...)
+{
+       va_list ap;
+       int rc;
+       if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
+               /* Invalidate any prior representation */
+               jx9MemObjRelease(pVal);
+               MemObjSetType(pVal, MEMOBJ_STRING);
+       }
+       va_start(ap, zFormat);
+       rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
+       va_end(ap);
+       return UNQLITE_OK;
+}
+/*
+ * [CAPIREF: unqlite_value_reset_string_cursor()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_reset_string_cursor(unqlite_value *pVal)
+{
+       return jx9_value_reset_string_cursor(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_resource()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_resource(unqlite_value *pVal,void *pUserData)
+{
+       return jx9_value_resource(pVal,pUserData);
+}
+/*
+ * [CAPIREF: unqlite_value_release()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_release(unqlite_value *pVal)
+{
+       return jx9_value_release(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_to_int()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_to_int(unqlite_value *pValue)
+{
+       return jx9_value_to_int(pValue);
+}
+/*
+ * [CAPIREF: unqlite_value_to_bool()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_to_bool(unqlite_value *pValue)
+{
+       return jx9_value_to_bool(pValue);
+}
+/*
+ * [CAPIREF: unqlite_value_to_int64()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unqlite_int64 unqlite_value_to_int64(unqlite_value *pValue)
+{
+       return jx9_value_to_int64(pValue);
+}
+/*
+ * [CAPIREF: unqlite_value_to_double()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+double unqlite_value_to_double(unqlite_value *pValue)
+{
+       return jx9_value_to_double(pValue);
+}
+/*
+ * [CAPIREF: unqlite_value_to_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+const char * unqlite_value_to_string(unqlite_value *pValue, int *pLen)
+{
+       return jx9_value_to_string(pValue,pLen);
+}
+/*
+ * [CAPIREF: unqlite_value_to_resource()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+void * unqlite_value_to_resource(unqlite_value *pValue)
+{
+       return jx9_value_to_resource(pValue);
+}
+/*
+ * [CAPIREF: unqlite_value_compare()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_compare(unqlite_value *pLeft, unqlite_value *pRight, int bStrict)
+{
+       return jx9_value_compare(pLeft,pRight,bStrict);
+}
+/*
+ * [CAPIREF: unqlite_result_int()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_int(unqlite_context *pCtx, int iValue)
+{
+       return jx9_result_int(pCtx,iValue);
+}
+/*
+ * [CAPIREF: unqlite_result_int64()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_int64(unqlite_context *pCtx, unqlite_int64 iValue)
+{
+       return jx9_result_int64(pCtx,iValue);
+}
+/*
+ * [CAPIREF: unqlite_result_bool()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_bool(unqlite_context *pCtx, int iBool)
+{
+       return jx9_result_bool(pCtx,iBool);
+}
+/*
+ * [CAPIREF: unqlite_result_double()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_double(unqlite_context *pCtx, double Value)
+{
+       return jx9_result_double(pCtx,Value);
+}
+/*
+ * [CAPIREF: unqlite_result_null()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_null(unqlite_context *pCtx)
+{
+       return jx9_result_null(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_result_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_string(unqlite_context *pCtx, const char *zString, int nLen)
+{
+       return jx9_result_string(pCtx,zString,nLen);
+}
+/*
+ * [CAPIREF: unqlite_result_string_format()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_string_format(unqlite_context *pCtx, const char *zFormat, ...)
+{
+       jx9_value *p;
+       va_list ap;
+       int rc;
+       p = pCtx->pRet;
+       if( (p->iFlags & MEMOBJ_STRING) == 0 ){
+               /* Invalidate any prior representation */
+               jx9MemObjRelease(p);
+               MemObjSetType(p, MEMOBJ_STRING);
+       }
+       /* Format the given string */
+       va_start(ap, zFormat);
+       rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
+       va_end(ap);
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_result_value()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_value(unqlite_context *pCtx, unqlite_value *pValue)
+{
+       return jx9_result_value(pCtx,pValue);
+}
+/*
+ * [CAPIREF: unqlite_result_resource()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_result_resource(unqlite_context *pCtx, void *pUserData)
+{
+       return jx9_result_resource(pCtx,pUserData);
+}
+/*
+ * [CAPIREF: unqlite_value_is_int()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_int(unqlite_value *pVal)
+{
+       return jx9_value_is_int(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_float()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_float(unqlite_value *pVal)
+{
+       return jx9_value_is_float(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_bool()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_bool(unqlite_value *pVal)
+{
+       return jx9_value_is_bool(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_string(unqlite_value *pVal)
+{
+       return jx9_value_is_string(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_null()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_null(unqlite_value *pVal)
+{
+       return jx9_value_is_null(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_numeric()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_numeric(unqlite_value *pVal)
+{
+       return jx9_value_is_numeric(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_callable()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_callable(unqlite_value *pVal)
+{
+       return jx9_value_is_callable(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_scalar()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_scalar(unqlite_value *pVal)
+{
+       return jx9_value_is_scalar(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_json_array()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_json_array(unqlite_value *pVal)
+{
+       return jx9_value_is_json_array(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_json_object()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_json_object(unqlite_value *pVal)
+{
+       return jx9_value_is_json_object(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_resource()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_resource(unqlite_value *pVal)
+{
+       return jx9_value_is_resource(pVal);
+}
+/*
+ * [CAPIREF: unqlite_value_is_empty()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_value_is_empty(unqlite_value *pVal)
+{
+       return jx9_value_is_empty(pVal);
+}
+/*
+ * [CAPIREF: unqlite_array_fetch()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unqlite_value * unqlite_array_fetch(unqlite_value *pArray, const char *zKey, int nByte)
+{
+       return jx9_array_fetch(pArray,zKey,nByte);
+}
+/*
+ * [CAPIREF: unqlite_array_walk()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_array_walk(unqlite_value *pArray, int (*xWalk)(unqlite_value *, unqlite_value *, void *), void *pUserData)
+{
+       return jx9_array_walk(pArray,xWalk,pUserData);
+}
+/*
+ * [CAPIREF: unqlite_array_add_elem()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_array_add_elem(unqlite_value *pArray, unqlite_value *pKey, unqlite_value *pValue)
+{
+       return jx9_array_add_elem(pArray,pKey,pValue);
+}
+/*
+ * [CAPIREF: unqlite_array_add_strkey_elem()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_array_add_strkey_elem(unqlite_value *pArray, const char *zKey, unqlite_value *pValue)
+{
+       return jx9_array_add_strkey_elem(pArray,zKey,pValue);
+}
+/*
+ * [CAPIREF: unqlite_array_count()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_array_count(unqlite_value *pArray)
+{
+       return (int)jx9_array_count(pArray);
+}
+/*
+ * [CAPIREF: unqlite_vm_new_scalar()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unqlite_value * unqlite_vm_new_scalar(unqlite_vm *pVm)
+{
+       unqlite_value *pValue;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return 0;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return 0; /* Another thread have released this instance */
+        }
+#endif
+        pValue = jx9_new_scalar(pVm->pJx9Vm);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return pValue;
+}
+/*
+ * [CAPIREF: unqlite_vm_new_array()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unqlite_value * unqlite_vm_new_array(unqlite_vm *pVm)
+{
+       unqlite_value *pValue;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return 0;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return 0; /* Another thread have released this instance */
+        }
+#endif
+        pValue = jx9_new_array(pVm->pJx9Vm);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return pValue;
+}
+/*
+ * [CAPIREF: unqlite_vm_release_value()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_vm_release_value(unqlite_vm *pVm,unqlite_value *pValue)
+{
+       int rc;
+       if( UNQLITE_VM_MISUSE(pVm) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_VM_RELEASE(pVm) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        rc = jx9_release_value(pVm->pJx9Vm,pValue);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pVm->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_context_output()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_context_output(unqlite_context *pCtx, const char *zString, int nLen)
+{
+       return jx9_context_output(pCtx,zString,nLen);
+}
+/*
+ * [CAPIREF: unqlite_context_output_format()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_context_output_format(unqlite_context *pCtx,const char *zFormat, ...)
+{
+       va_list ap;
+       int rc;
+       va_start(ap, zFormat);
+       rc = jx9VmOutputConsumeAp(pCtx->pVm,zFormat, ap);
+       va_end(ap);
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_context_throw_error()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_context_throw_error(unqlite_context *pCtx, int iErr, const char *zErr)
+{
+       return jx9_context_throw_error(pCtx,iErr,zErr);
+}
+/*
+ * [CAPIREF: unqlite_context_throw_error_format()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_context_throw_error_format(unqlite_context *pCtx, int iErr, const char *zFormat, ...)
+{
+       va_list ap;
+       int rc;
+       if( zFormat == 0){
+               return JX9_OK;
+       }
+       va_start(ap, zFormat);
+       rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
+       va_end(ap);
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_context_random_num()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unsigned int unqlite_context_random_num(unqlite_context *pCtx)
+{
+       return jx9_context_random_num(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_context_random_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_context_random_string(unqlite_context *pCtx, char *zBuf, int nBuflen)
+{
+       return jx9_context_random_string(pCtx,zBuf,nBuflen);
+}
+/*
+ * [CAPIREF: unqlite_context_user_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+void * unqlite_context_user_data(unqlite_context *pCtx)
+{
+       return jx9_context_user_data(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_context_push_aux_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_context_push_aux_data(unqlite_context *pCtx, void *pUserData)
+{
+       return jx9_context_push_aux_data(pCtx,pUserData);
+}
+/*
+ * [CAPIREF: unqlite_context_peek_aux_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+void * unqlite_context_peek_aux_data(unqlite_context *pCtx)
+{
+       return jx9_context_peek_aux_data(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_context_pop_aux_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+void * unqlite_context_pop_aux_data(unqlite_context *pCtx)
+{
+       return jx9_context_pop_aux_data(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_context_result_buf_length()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unsigned int unqlite_context_result_buf_length(unqlite_context *pCtx)
+{
+       return jx9_context_result_buf_length(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_function_name()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+const char * unqlite_function_name(unqlite_context *pCtx)
+{
+       return jx9_function_name(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_context_new_scalar()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unqlite_value * unqlite_context_new_scalar(unqlite_context *pCtx)
+{
+       return jx9_context_new_scalar(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_context_new_array()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+unqlite_value * unqlite_context_new_array(unqlite_context *pCtx)
+{
+       return jx9_context_new_array(pCtx);
+}
+/*
+ * [CAPIREF: unqlite_context_release_value()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+void unqlite_context_release_value(unqlite_context *pCtx,unqlite_value *pValue)
+{
+       jx9_context_release_value(pCtx,pValue);
+}
+/*
+ * [CAPIREF: unqlite_context_alloc_chunk()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+void * unqlite_context_alloc_chunk(unqlite_context *pCtx,unsigned int nByte,int ZeroChunk,int AutoRelease)
+{
+       return jx9_context_alloc_chunk(pCtx,nByte,ZeroChunk,AutoRelease);
+}
+/*
+ * [CAPIREF: unqlite_context_realloc_chunk()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+void * unqlite_context_realloc_chunk(unqlite_context *pCtx,void *pChunk,unsigned int nByte)
+{
+       return jx9_context_realloc_chunk(pCtx,pChunk,nByte);
+}
+/*
+ * [CAPIREF: unqlite_context_free_chunk()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+void unqlite_context_free_chunk(unqlite_context *pCtx,void *pChunk)
+{
+       jx9_context_free_chunk(pCtx,pChunk);
+}
+/*
+ * [CAPIREF: unqlite_kv_store()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_store(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen)
+{
+       unqlite_kv_engine *pEngine;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Point to the underlying storage engine */
+        pEngine = unqlitePagerGetKvEngine(pDb);
+        if( pEngine->pIo->pMethods->xReplace == 0 ){
+                /* Storage engine does not implement such method */
+                unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine");
+                rc = UNQLITE_NOTIMPLEMENTED;
+        }else{
+                if( nKeyLen < 0 ){
+                        /* Assume a null terminated string and compute it's length */
+                        nKeyLen = SyStrlen((const char *)pKey);
+                }
+                if( !nKeyLen ){
+                        unqliteGenError(pDb,"Empty key");
+                        rc = UNQLITE_EMPTY;
+                }else{
+                        /* Perform the requested operation */
+                        rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,pData,nDataLen);
+                }
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_store_fmt()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_store_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...)
+{
+       unqlite_kv_engine *pEngine;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Point to the underlying storage engine */
+        pEngine = unqlitePagerGetKvEngine(pDb);
+        if( pEngine->pIo->pMethods->xReplace == 0 ){
+                /* Storage engine does not implement such method */
+                unqliteGenError(pDb,"xReplace() method not implemented in the underlying storage engine");
+                rc = UNQLITE_NOTIMPLEMENTED;
+        }else{
+                if( nKeyLen < 0 ){
+                        /* Assume a null terminated string and compute it's length */
+                        nKeyLen = SyStrlen((const char *)pKey);
+                }
+                if( !nKeyLen ){
+                        unqliteGenError(pDb,"Empty key");
+                        rc = UNQLITE_EMPTY;
+                }else{
+                        SyBlob sWorker; /* Working buffer */
+                        va_list ap;
+                        SyBlobInit(&sWorker,&pDb->sMem);
+                        /* Format the data */
+                        va_start(ap,zFormat);
+                        SyBlobFormatAp(&sWorker,zFormat,ap);
+                        va_end(ap);
+                        /* Perform the requested operation */
+                        rc = pEngine->pIo->pMethods->xReplace(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker));
+                        /* Clean up */
+                        SyBlobRelease(&sWorker);
+                }
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_append()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_append(unqlite *pDb,const void *pKey,int nKeyLen,const void *pData,unqlite_int64 nDataLen)
+{
+       unqlite_kv_engine *pEngine;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Point to the underlying storage engine */
+        pEngine = unqlitePagerGetKvEngine(pDb);
+        if( pEngine->pIo->pMethods->xAppend == 0 ){
+                /* Storage engine does not implement such method */
+                unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine");
+                rc = UNQLITE_NOTIMPLEMENTED;
+        }else{
+                if( nKeyLen < 0 ){
+                        /* Assume a null terminated string and compute it's length */
+                        nKeyLen = SyStrlen((const char *)pKey);
+                }
+                if( !nKeyLen ){
+                        unqliteGenError(pDb,"Empty key");
+                        rc = UNQLITE_EMPTY;
+                }else{
+                        /* Perform the requested operation */
+                        rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,pData,nDataLen);
+                }
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_append_fmt()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_append_fmt(unqlite *pDb,const void *pKey,int nKeyLen,const char *zFormat,...)
+{
+       unqlite_kv_engine *pEngine;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Point to the underlying storage engine */
+        pEngine = unqlitePagerGetKvEngine(pDb);
+        if( pEngine->pIo->pMethods->xAppend == 0 ){
+                /* Storage engine does not implement such method */
+                unqliteGenError(pDb,"xAppend() method not implemented in the underlying storage engine");
+                rc = UNQLITE_NOTIMPLEMENTED;
+        }else{
+                if( nKeyLen < 0 ){
+                        /* Assume a null terminated string and compute it's length */
+                        nKeyLen = SyStrlen((const char *)pKey);
+                }
+                if( !nKeyLen ){
+                        unqliteGenError(pDb,"Empty key");
+                        rc = UNQLITE_EMPTY;
+                }else{
+                        SyBlob sWorker; /* Working buffer */
+                        va_list ap;
+                        SyBlobInit(&sWorker,&pDb->sMem);
+                        /* Format the data */
+                        va_start(ap,zFormat);
+                        SyBlobFormatAp(&sWorker,zFormat,ap);
+                        va_end(ap);
+                        /* Perform the requested operation */
+                        rc = pEngine->pIo->pMethods->xAppend(pEngine,pKey,nKeyLen,SyBlobData(&sWorker),SyBlobLength(&sWorker));
+                        /* Clean up */
+                        SyBlobRelease(&sWorker);
+                }
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_fetch()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_fetch(unqlite *pDb,const void *pKey,int nKeyLen,void *pBuf,unqlite_int64 *pBufLen)
+{
+       unqlite_kv_methods *pMethods;
+       unqlite_kv_engine *pEngine;
+       unqlite_kv_cursor *pCur;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Point to the underlying storage engine */
+        pEngine = unqlitePagerGetKvEngine(pDb);
+        pMethods = pEngine->pIo->pMethods;
+        pCur = pDb->sDB.pCursor;
+        if( nKeyLen < 0 ){
+                /* Assume a null terminated string and compute it's length */
+                nKeyLen = SyStrlen((const char *)pKey);
+        }
+        if( !nKeyLen ){
+                 unqliteGenError(pDb,"Empty key");
+                 rc = UNQLITE_EMPTY;
+        }else{
+                 /* Seek to the record position */
+                 rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
+        }
+        if( rc == UNQLITE_OK ){
+                if( pBuf == 0 ){
+                        /* Data length only */
+                        rc = pMethods->xDataLength(pCur,pBufLen);
+                }else{
+                        SyBlob sBlob;
+                        /* Initialize the data consumer */
+                        SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)*pBufLen);
+                        /* Consume the data */
+                        rc = pMethods->xData(pCur,unqliteDataConsumer,&sBlob);
+                        /* Data length */
+                        *pBufLen = (unqlite_int64)SyBlobLength(&sBlob);
+                        /* Cleanup */
+                        SyBlobRelease(&sBlob);
+                }
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_fetch_callback()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_fetch_callback(unqlite *pDb,const void *pKey,int nKeyLen,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
+{
+       unqlite_kv_methods *pMethods;
+       unqlite_kv_engine *pEngine;
+       unqlite_kv_cursor *pCur;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Point to the underlying storage engine */
+        pEngine = unqlitePagerGetKvEngine(pDb);
+        pMethods = pEngine->pIo->pMethods;
+        pCur = pDb->sDB.pCursor;
+        if( nKeyLen < 0 ){
+                /* Assume a null terminated string and compute it's length */
+                nKeyLen = SyStrlen((const char *)pKey);
+        }
+        if( !nKeyLen ){
+                unqliteGenError(pDb,"Empty key");
+                rc = UNQLITE_EMPTY;
+        }else{
+                /* Seek to the record position */
+                rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
+        }
+        if( rc == UNQLITE_OK && xConsumer ){
+                /* Consume the data directly */
+                rc = pMethods->xData(pCur,xConsumer,pUserData);         
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_delete()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_delete(unqlite *pDb,const void *pKey,int nKeyLen)
+{
+       unqlite_kv_methods *pMethods;
+       unqlite_kv_engine *pEngine;
+       unqlite_kv_cursor *pCur;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Point to the underlying storage engine */
+        pEngine = unqlitePagerGetKvEngine(pDb);
+        pMethods = pEngine->pIo->pMethods;
+        pCur = pDb->sDB.pCursor;
+        if( pMethods->xDelete == 0 ){
+                /* Storage engine does not implement such method */
+                unqliteGenError(pDb,"xDelete() method not implemented in the underlying storage engine");
+                rc = UNQLITE_NOTIMPLEMENTED;
+        }else{
+                if( nKeyLen < 0 ){
+                        /* Assume a null terminated string and compute it's length */
+                        nKeyLen = SyStrlen((const char *)pKey);
+                }
+                if( !nKeyLen ){
+                        unqliteGenError(pDb,"Empty key");
+                        rc = UNQLITE_EMPTY;
+                }else{
+                        /* Seek to the record position */
+                        rc = pMethods->xSeek(pCur,pKey,nKeyLen,UNQLITE_CURSOR_MATCH_EXACT);
+                }
+                if( rc == UNQLITE_OK ){
+                        /* Exact match found, delete the entry */
+                        rc = pMethods->xDelete(pCur);
+                }
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_config()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_config(unqlite *pDb,int iOp,...)
+{
+       unqlite_kv_engine *pEngine;
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Point to the underlying storage engine */
+        pEngine = unqlitePagerGetKvEngine(pDb);
+        if( pEngine->pIo->pMethods->xConfig == 0 ){
+                /* Storage engine does not implements such method */
+                unqliteGenError(pDb,"xConfig() method not implemented in the underlying storage engine");
+                rc = UNQLITE_NOTIMPLEMENTED;
+        }else{
+                va_list ap;
+                /* Configure the storage engine */
+                va_start(ap,iOp);
+                rc = pEngine->pIo->pMethods->xConfig(pEngine,iOp,ap);
+                va_end(ap);
+        }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_init()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_init(unqlite *pDb,unqlite_kv_cursor **ppOut)
+{
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) || ppOut == 0 /* Noop */){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Allocate a new cursor */
+        rc = unqliteInitCursor(pDb,ppOut);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_release()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_release(unqlite *pDb,unqlite_kv_cursor *pCur)
+{
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) || pCur == 0 /* Noop */){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Release the cursor */
+        rc = unqliteReleaseCursor(pDb,pCur);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_first_entry()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_first_entry(unqlite_kv_cursor *pCursor)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Check if the requested method is implemented by the underlying storage engine */
+       if( pCursor->pStore->pIo->pMethods->xFirst == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+       }else{
+               /* Seek to the first entry */
+               rc = pCursor->pStore->pIo->pMethods->xFirst(pCursor);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_last_entry()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_last_entry(unqlite_kv_cursor *pCursor)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Check if the requested method is implemented by the underlying storage engine */
+       if( pCursor->pStore->pIo->pMethods->xLast == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+       }else{
+               /* Seek to the last entry */
+               rc = pCursor->pStore->pIo->pMethods->xLast(pCursor);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_valid_entry()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_valid_entry(unqlite_kv_cursor *pCursor)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Check if the requested method is implemented by the underlying storage engine */
+       if( pCursor->pStore->pIo->pMethods->xValid == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+       }else{
+               rc = pCursor->pStore->pIo->pMethods->xValid(pCursor);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_next_entry()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_next_entry(unqlite_kv_cursor *pCursor)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Check if the requested method is implemented by the underlying storage engine */
+       if( pCursor->pStore->pIo->pMethods->xNext == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+       }else{
+               /* Seek to the next entry */
+               rc = pCursor->pStore->pIo->pMethods->xNext(pCursor);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_prev_entry()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_prev_entry(unqlite_kv_cursor *pCursor)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Check if the requested method is implemented by the underlying storage engine */
+       if( pCursor->pStore->pIo->pMethods->xPrev == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+       }else{
+               /* Seek to the previous entry */
+               rc = pCursor->pStore->pIo->pMethods->xPrev(pCursor);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_delete_entry()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_delete_entry(unqlite_kv_cursor *pCursor)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Check if the requested method is implemented by the underlying storage engine */
+       if( pCursor->pStore->pIo->pMethods->xDelete == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+       }else{
+               /* Delete the entry */
+               rc = pCursor->pStore->pIo->pMethods->xDelete(pCursor);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_reset()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_reset(unqlite_kv_cursor *pCursor)
+{
+       int rc = UNQLITE_OK;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Check if the requested method is implemented by the underlying storage engine */
+       if( pCursor->pStore->pIo->pMethods->xReset == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+       }else{
+               /* Reset */
+               pCursor->pStore->pIo->pMethods->xReset(pCursor);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_seek()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_seek(unqlite_kv_cursor *pCursor,const void *pKey,int nKeyLen,int iPos)
+{
+       int rc = UNQLITE_OK;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       if( nKeyLen < 0 ){
+               /* Assume a null terminated string and compute it's length */
+               nKeyLen = SyStrlen((const char *)pKey);
+       }
+       if( !nKeyLen ){
+               rc = UNQLITE_EMPTY;
+       }else{
+               /* Seek to the desired location */
+               rc = pCursor->pStore->pIo->pMethods->xSeek(pCursor,pKey,nKeyLen,iPos);
+       }
+       return rc;
+}
+/*
+ * Default data consumer callback. That is, all retrieved is redirected to this
+ * routine which store the output in an internal blob.
+ */
+UNQLITE_PRIVATE int unqliteDataConsumer(
+       const void *pOut,   /* Data to consume */
+       unsigned int nLen,  /* Data length */
+       void *pUserData     /* User private data */
+       )
+{
+        sxi32 rc;
+        /* Store the output in an internal BLOB */
+        rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
+        return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_data_callback()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_key_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Consume the key directly */
+       rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,xConsumer,pUserData);
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_key()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_key(unqlite_kv_cursor *pCursor,void *pBuf,int *pnByte)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       if( pBuf == 0 ){
+               /* Key length only */
+               rc = pCursor->pStore->pIo->pMethods->xKeyLength(pCursor,pnByte);
+       }else{
+               SyBlob sBlob;
+               if( (*pnByte) < 0 ){
+                       return UNQLITE_CORRUPT;
+               }
+               /* Initialize the data consumer */
+               SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte));
+               /* Consume the key */
+               rc = pCursor->pStore->pIo->pMethods->xKey(pCursor,unqliteDataConsumer,&sBlob);
+                /* Key length */
+               *pnByte = SyBlobLength(&sBlob);
+               /* Cleanup */
+               SyBlobRelease(&sBlob);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_data_callback()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_data_callback(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       /* Consume the data directly */
+       rc = pCursor->pStore->pIo->pMethods->xData(pCursor,xConsumer,pUserData);
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_kv_cursor_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_kv_cursor_data(unqlite_kv_cursor *pCursor,void *pBuf,unqlite_int64 *pnByte)
+{
+       int rc;
+#ifdef UNTRUST
+       if( pCursor == 0 ){
+               return UNQLITE_CORRUPT;
+       }
+#endif
+       if( pBuf == 0 ){
+               /* Data length only */
+               rc = pCursor->pStore->pIo->pMethods->xDataLength(pCursor,pnByte);
+       }else{
+               SyBlob sBlob;
+               if( (*pnByte) < 0 ){
+                       return UNQLITE_CORRUPT;
+               }
+               /* Initialize the data consumer */
+               SyBlobInitFromBuf(&sBlob,pBuf,(sxu32)(*pnByte));
+               /* Consume the data */
+               rc = pCursor->pStore->pIo->pMethods->xData(pCursor,unqliteDataConsumer,&sBlob);
+               /* Data length */
+               *pnByte = SyBlobLength(&sBlob);
+               /* Cleanup */
+               SyBlobRelease(&sBlob);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_begin()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_begin(unqlite *pDb)
+{
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Begin the write transaction */
+        rc = unqlitePagerBegin(pDb->sDB.pPager);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        return rc;
+}
+/*
+ * [CAPIREF: unqlite_commit()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_commit(unqlite *pDb)
+{
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Commit the transaction */
+        rc = unqlitePagerCommit(pDb->sDB.pPager);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        return rc;
+}
+/*
+ * [CAPIREF: unqlite_rollback()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+int unqlite_rollback(unqlite *pDb)
+{
+       int rc;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Rollback the transaction */
+        rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        return rc;
+}
+/*
+ * [CAPIREF: unqlite_util_load_mmaped_file()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+UNQLITE_APIEXPORT int unqlite_util_load_mmaped_file(const char *zFile,void **ppMap,unqlite_int64 *pFileSize)
+{
+       const jx9_vfs *pVfs;
+       int rc;
+       if( SX_EMPTY_STR(zFile) || ppMap == 0 || pFileSize == 0){
+               /* Sanity check */
+               return UNQLITE_CORRUPT;
+       }
+       *ppMap = 0;
+       /* Extract the Jx9 Vfs */
+       pVfs = jx9ExportBuiltinVfs();
+       /*
+        * Check if the underlying vfs implement the memory map routines
+        * [i.e: mmap() under UNIX/MapViewOfFile() under windows].
+        */
+       if( pVfs == 0 || pVfs->xMmap == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+        }else{ 
+                /* Try to get a read-only memory view of the whole file */
+                rc = pVfs->xMmap(zFile,ppMap,pFileSize);
+        }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_util_release_mmaped_file()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+UNQLITE_APIEXPORT int unqlite_util_release_mmaped_file(void *pMap,unqlite_int64 iFileSize)
+{
+       const jx9_vfs *pVfs;
+       int rc = UNQLITE_OK;
+       if( pMap == 0 ){
+               return UNQLITE_OK;
+       }
+       /* Extract the Jx9 Vfs */
+       pVfs = jx9ExportBuiltinVfs();
+       if( pVfs == 0 || pVfs->xUnmap == 0 ){
+               rc = UNQLITE_NOTIMPLEMENTED;
+        }else{ 
+                pVfs->xUnmap(pMap,iFileSize);
+        }
+       return rc;
+}
+/*
+ * [CAPIREF: unqlite_util_random_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+UNQLITE_APIEXPORT int unqlite_util_random_string(unqlite *pDb,char *zBuf,unsigned int buf_size)
+{
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return UNQLITE_CORRUPT;
+       }
+       if( zBuf == 0 || buf_size < 3 ){
+               /* Buffer must be long enough to hold three bytes */
+               return UNQLITE_INVALID;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return UNQLITE_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /* Generate the random string */
+        unqlitePagerRandomString(pDb->sDB.pPager,zBuf,buf_size);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        return UNQLITE_OK;
+}
+/*
+ * [CAPIREF: unqlite_util_random_num()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+UNQLITE_APIEXPORT unsigned int unqlite_util_random_num(unqlite *pDb)
+{
+       sxu32 iNum;
+       if( UNQLITE_DB_MISUSE(pDb) ){
+               return 0;
+       }
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Acquire DB mutex */
+        SyMutexEnter(sUnqlMPGlobal.pMutexMethods, pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+        if( sUnqlMPGlobal.nThreadingLevel > UNQLITE_THREAD_LEVEL_SINGLE && 
+                UNQLITE_THRD_DB_RELEASE(pDb) ){
+                        return 0; /* Another thread have released this instance */
+        }
+#endif
+        /* Generate the random number */
+        iNum = unqlitePagerRandomNum(pDb->sDB.pPager);
+#if defined(UNQLITE_ENABLE_THREADS)
+        /* Leave DB mutex */
+        SyMutexLeave(sUnqlMPGlobal.pMutexMethods,pDb->pMutex); /* NO-OP if sUnqlMPGlobal.nThreadingLevel != UNQLITE_THREAD_LEVEL_MULTI */
+#endif
+        return iNum;
+}
+/*
+ * ----------------------------------------------------------
+ * File: bitvec.c
+ * MD5: 7e3376710d8454ebcf8c77baacca880f
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: bitvec.c v1.0 Win7 2013-02-27 15:16 stable <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+
+/** This file implements an object that represents a dynmaic
+** bitmap.
+**
+** A bitmap is used to record which pages of a database file have been
+** journalled during a transaction, or which pages have the "dont-write"
+** property.  Usually only a few pages are meet either condition.
+** So the bitmap is usually sparse and has low cardinality.
+*/
+/*
+ * Actually, this is not a bitmap but a simple hashtable where page 
+ * number (64-bit unsigned integers) are used as the lookup keys.
+ */
+typedef struct bitvec_rec bitvec_rec;
+struct bitvec_rec
+{
+       pgno iPage;                  /* Page number */
+       bitvec_rec *pNext,*pNextCol; /* Collison link */
+};
+struct Bitvec
+{
+       SyMemBackend *pAlloc; /* Memory allocator */
+       sxu32 nRec;           /* Total number of records */
+       sxu32 nSize;          /* Table size */
+       bitvec_rec **apRec;   /* Record table */
+       bitvec_rec *pList;    /* List of records */
+};
+/* 
+ * Allocate a new bitvec instance.
+*/
+UNQLITE_PRIVATE Bitvec * unqliteBitvecCreate(SyMemBackend *pAlloc,pgno iSize)
+{
+       bitvec_rec **apNew;
+       Bitvec *p;
+       
+       p = (Bitvec *)SyMemBackendAlloc(pAlloc,sizeof(*p) );
+       if( p == 0 ){
+               SXUNUSED(iSize); /* cc warning */
+               return 0;
+       }
+       /* Zero the structure */
+       SyZero(p,sizeof(Bitvec));
+       /* Allocate a new table */
+       p->nSize = 64; /* Must be a power of two */
+       apNew = (bitvec_rec **)SyMemBackendAlloc(pAlloc,p->nSize * sizeof(bitvec_rec *));
+       if( apNew == 0 ){
+               SyMemBackendFree(pAlloc,p);
+               return 0;
+       }
+       /* Zero the new table */
+       SyZero((void *)apNew,p->nSize * sizeof(bitvec_rec *));
+       /* Fill-in */
+       p->apRec = apNew;
+       p->pAlloc = pAlloc;
+       return p;
+}
+/*
+ * Check if the given page number is already installed in the table.
+ * Return true if installed. False otherwise.
+ */
+UNQLITE_PRIVATE int unqliteBitvecTest(Bitvec *p,pgno i)
+{  
+       bitvec_rec *pRec;
+       /* Point to the desired bucket */
+       pRec = p->apRec[i & (p->nSize - 1)];
+       for(;;){
+               if( pRec == 0 ){ break; }
+               if( pRec->iPage == i ){
+                       /* Page found */
+                       return 1;
+               }
+               /* Point to the next entry */
+               pRec = pRec->pNextCol;
+
+               if( pRec == 0 ){ break; }
+               if( pRec->iPage == i ){
+                       /* Page found */
+                       return 1;
+               }
+               /* Point to the next entry */
+               pRec = pRec->pNextCol;
+
+
+               if( pRec == 0 ){ break; }
+               if( pRec->iPage == i ){
+                       /* Page found */
+                       return 1;
+               }
+               /* Point to the next entry */
+               pRec = pRec->pNextCol;
+
+
+               if( pRec == 0 ){ break; }
+               if( pRec->iPage == i ){
+                       /* Page found */
+                       return 1;
+               }
+               /* Point to the next entry */
+               pRec = pRec->pNextCol;
+       }
+       /* No such entry */
+       return 0;
+}
+/*
+ * Install a given page number in our bitmap (Actually, our hashtable).
+ */
+UNQLITE_PRIVATE int unqliteBitvecSet(Bitvec *p,pgno i)
+{
+       bitvec_rec *pRec;
+       sxi32 iBuck;
+       /* Allocate a new instance */
+       pRec = (bitvec_rec *)SyMemBackendPoolAlloc(p->pAlloc,sizeof(bitvec_rec));
+       if( pRec == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pRec,sizeof(bitvec_rec));
+       /* Fill-in */
+       pRec->iPage = i;
+       iBuck = i & (p->nSize - 1);
+       pRec->pNextCol = p->apRec[iBuck];
+       p->apRec[iBuck] = pRec;
+       pRec->pNext = p->pList;
+       p->pList = pRec;
+       p->nRec++;
+       if( p->nRec >= (p->nSize * 3) && p->nRec < 100000 ){
+               /* Grow the hashtable */
+               sxu32 nNewSize = p->nSize << 1;
+               bitvec_rec *pEntry,**apNew;
+               sxu32 n;
+               apNew = (bitvec_rec **)SyMemBackendAlloc(p->pAlloc, nNewSize * sizeof(bitvec_rec *));
+               if( apNew ){
+                       sxu32 iBucket;
+                       /* Zero the new table */
+                       SyZero((void *)apNew, nNewSize * sizeof(bitvec_rec *));
+                       /* Rehash all entries */
+                       n = 0;
+                       pEntry = p->pList;
+                       for(;;){
+                               /* Loop one */
+                               if( n >= p->nRec ){
+                                       break;
+                               }
+                               pEntry->pNextCol = 0;
+                               /* Install in the new bucket */
+                               iBucket = pEntry->iPage & (nNewSize - 1);
+                               pEntry->pNextCol = apNew[iBucket];
+                               apNew[iBucket] = pEntry;
+                               /* Point to the next entry */
+                               pEntry = pEntry->pNext;
+                               n++;
+                       }
+                       /* Release the old table and reflect the change */
+                       SyMemBackendFree(p->pAlloc,(void *)p->apRec);
+                       p->apRec = apNew;
+                       p->nSize  = nNewSize;
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Destroy a bitvec instance. Reclaim all memory used.
+ */
+UNQLITE_PRIVATE void unqliteBitvecDestroy(Bitvec *p)
+{
+       bitvec_rec *pNext,*pRec = p->pList;
+       SyMemBackend *pAlloc = p->pAlloc;
+       
+       for(;;){
+               if( p->nRec < 1 ){
+                       break;
+               }
+               pNext = pRec->pNext;
+               SyMemBackendPoolFree(pAlloc,(void *)pRec);
+               pRec = pNext;
+               p->nRec--;
+
+               if( p->nRec < 1 ){
+                       break;
+               }
+               pNext = pRec->pNext;
+               SyMemBackendPoolFree(pAlloc,(void *)pRec);
+               pRec = pNext;
+               p->nRec--;
+
+
+               if( p->nRec < 1 ){
+                       break;
+               }
+               pNext = pRec->pNext;
+               SyMemBackendPoolFree(pAlloc,(void *)pRec);
+               pRec = pNext;
+               p->nRec--;
+
+
+               if( p->nRec < 1 ){
+                       break;
+               }
+               pNext = pRec->pNext;
+               SyMemBackendPoolFree(pAlloc,(void *)pRec);
+               pRec = pNext;
+               p->nRec--;
+       }
+       SyMemBackendFree(pAlloc,(void *)p->apRec);
+       SyMemBackendFree(pAlloc,p);
+}
+/*
+ * ----------------------------------------------------------
+ * File: fastjson.c
+ * MD5: 3693c0022edc7d37b65124d7aef68397
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: fastjson.c v1.1 FreeBSD 2012-12-05 22:52 stable <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* JSON binary encoding, decoding and stuff like that */
+#ifndef UNQLITE_FAST_JSON_NEST_LIMIT
+#if defined(__WINNT__) || defined(__UNIXES__)
+#define UNQLITE_FAST_JSON_NEST_LIMIT 64 /* Nesting limit */
+#else
+#define UNQLITE_FAST_JSON_NEST_LIMIT 32 /* Nesting limit */
+#endif
+#endif /* UNQLITE_FAST_JSON_NEST_LIMIT */
+/* 
+ * JSON to Binary using the FastJSON implementation (BigEndian).
+ */
+/*
+ * FastJSON implemented binary token.
+ */
+#define FJSON_DOC_START    1 /* { */
+#define FJSON_DOC_END      2 /* } */
+#define FJSON_ARRAY_START  3 /* [ */
+#define FJSON_ARRAY_END    4 /* ] */
+#define FJSON_COLON        5 /* : */
+#define FJSON_COMMA        6 /* , */
+#define FJSON_ID           7 /* ID + 4 Bytes length */
+#define FJSON_STRING       8 /* String + 4 bytes length */
+#define FJSON_BYTE         9 /* Byte */
+#define FJSON_INT64       10 /* Integer 64 + 8 bytes */
+#define FJSON_REAL        18 /* Floating point value + 2 bytes */
+#define FJSON_NULL        23 /* NULL */
+#define FJSON_TRUE        24 /* TRUE */
+#define FJSON_FALSE       25 /* FALSE */
+/*
+ * Encode a Jx9 value to binary JSON.
+ */
+UNQLITE_PRIVATE sxi32 FastJsonEncode(
+       jx9_value *pValue, /* Value to encode */
+       SyBlob *pOut,      /* Store encoded value here */
+       int iNest          /* Nesting limit */ 
+       )
+{
+       sxi32 iType = pValue ? pValue->iFlags : MEMOBJ_NULL;
+       sxi32 rc = SXRET_OK;
+       int c;
+       if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){
+               /* Nesting limit reached */
+               return SXERR_LIMIT;
+       }
+       if( iType & (MEMOBJ_NULL|MEMOBJ_RES) ){
+               /*
+                * Resources are encoded as null also.
+                */
+               c = FJSON_NULL;
+               rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+       }else if( iType & MEMOBJ_BOOL ){
+               c = pValue->x.iVal ? FJSON_TRUE : FJSON_FALSE;
+               rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+       }else if( iType & MEMOBJ_STRING ){
+               unsigned char zBuf[sizeof(sxu32)]; /* String length */
+               c = FJSON_STRING;
+               SyBigEndianPack32(zBuf,SyBlobLength(&pValue->sBlob));
+               rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+               if( rc == SXRET_OK ){
+                       rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf));
+                       if( rc == SXRET_OK ){
+                               rc = SyBlobAppend(pOut,SyBlobData(&pValue->sBlob),SyBlobLength(&pValue->sBlob));
+                       }
+               }
+       }else if( iType & MEMOBJ_INT ){
+               unsigned char zBuf[8];
+               /* 64bit big endian integer */
+               c = FJSON_INT64;
+               rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+               if( rc == SXRET_OK ){
+                       SyBigEndianPack64(zBuf,(sxu64)pValue->x.iVal);
+                       rc = SyBlobAppend(pOut,(const void *)zBuf,sizeof(zBuf));
+               }
+       }else if( iType & MEMOBJ_REAL ){
+               /* Real number */
+               c = FJSON_REAL;
+               rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+               if( rc == SXRET_OK ){
+                       sxu32 iOfft = SyBlobLength(pOut);
+                       rc = SyBlobAppendBig16(pOut,0);
+                       if( rc == SXRET_OK ){
+                               unsigned char *zBlob;
+                               SyBlobFormat(pOut,"%.15g",pValue->x.rVal);
+                               zBlob = (unsigned char *)SyBlobDataAt(pOut,iOfft);
+                               SyBigEndianPack16(zBlob,(sxu16)(SyBlobLength(pOut) - ( 2 + iOfft)));
+                       }
+               }
+       }else if( iType & MEMOBJ_HASHMAP ){
+               /* A JSON object or array */
+               jx9_hashmap *pMap = (jx9_hashmap *)pValue->x.pOther;
+               jx9_hashmap_node *pNode;
+               jx9_value *pEntry;
+               /* Reset the hashmap loop cursor */
+               jx9HashmapResetLoopCursor(pMap);
+               if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
+                       jx9_value sKey;
+                       /* A JSON object */
+                       c = FJSON_DOC_START; /* { */
+                       rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+                       if( rc == SXRET_OK ){
+                               jx9MemObjInit(pMap->pVm,&sKey);
+                               /* Encode object entries */
+                               while((pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){
+                                       /* Extract the key */
+                                       jx9HashmapExtractNodeKey(pNode,&sKey);
+                                       /* Encode it */
+                                       rc = FastJsonEncode(&sKey,pOut,iNest+1);
+                                       if( rc != SXRET_OK ){
+                                               break;
+                                       }
+                                       c = FJSON_COLON;
+                                       rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+                                       if( rc != SXRET_OK ){
+                                               break;
+                                       }
+                                       /* Extract the value */
+                                       pEntry = jx9HashmapGetNodeValue(pNode);
+                                       /* Encode it */
+                                       rc = FastJsonEncode(pEntry,pOut,iNest+1);
+                                       if( rc != SXRET_OK ){
+                                               break;
+                                       }
+                                       /* Delimit the entry */
+                                       c = FJSON_COMMA;
+                                       rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+                                       if( rc != SXRET_OK ){
+                                               break;
+                                       }
+                               }
+                               jx9MemObjRelease(&sKey);
+                               if( rc == SXRET_OK ){
+                                       c = FJSON_DOC_END; /* } */
+                                       rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+                               }
+                       }
+               }else{
+                       /* A JSON array */
+                       c = FJSON_ARRAY_START; /* [ */
+                       rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+                       if( rc == SXRET_OK ){
+                               /* Encode array entries */
+                               while( (pNode = jx9HashmapGetNextEntry(pMap)) != 0 ){
+                                       /* Extract the value */
+                                       pEntry = jx9HashmapGetNodeValue(pNode);
+                                       /* Encode it */
+                                       rc = FastJsonEncode(pEntry,pOut,iNest+1);
+                                       if( rc != SXRET_OK ){
+                                               break;
+                                       }
+                                       /* Delimit the entry */
+                                       c = FJSON_COMMA;
+                                       rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+                                       if( rc != SXRET_OK ){
+                                               break;
+                                       }
+                               }
+                               if( rc == SXRET_OK ){
+                                       c = FJSON_ARRAY_END; /* ] */
+                                       rc = SyBlobAppend(pOut,(const void *)&c,sizeof(char));
+                               }
+                       }
+               }
+       }
+       return rc;
+}
+/*
+ * Decode a FastJSON binary blob.
+ */
+UNQLITE_PRIVATE sxi32 FastJsonDecode(
+       const void *pIn,  /* Binary JSON  */
+       sxu32 nByte,      /* Chunk delimiter */
+       jx9_value *pOut,  /* Decoded value */
+       const unsigned char **pzPtr,
+       int iNest /* Nesting limit */
+       )
+{
+       const unsigned char *zIn = (const unsigned char *)pIn;
+       const unsigned char *zEnd = &zIn[nByte];
+       sxi32 rc = SXRET_OK;
+       int c;
+       if( iNest >= UNQLITE_FAST_JSON_NEST_LIMIT ){
+               /* Nesting limit reached */
+               return SXERR_LIMIT;
+       }
+       c = zIn[0];
+       /* Advance the stream cursor */
+       zIn++;
+       /* Process the binary token */
+       switch(c){
+       case FJSON_NULL:
+               /* null */
+               jx9_value_null(pOut);
+               break;
+       case FJSON_FALSE:
+               /* Boolean FALSE */
+               jx9_value_bool(pOut,0);
+               break;
+       case FJSON_TRUE:
+               /* Boolean TRUE */
+               jx9_value_bool(pOut,1);
+               break;
+       case FJSON_INT64: {
+               /* 64Bit integer */
+               sxu64 iVal;
+               /* Sanity check */
+               if( &zIn[8] >= zEnd ){
+                       /* Corrupt chunk */
+                       rc = SXERR_CORRUPT;
+                       break;
+               }
+               SyBigEndianUnpack64(zIn,&iVal);
+               /* Advance the pointer */
+               zIn += 8;
+               jx9_value_int64(pOut,(jx9_int64)iVal);
+               break;
+                                         }
+       case FJSON_REAL: {
+               /* Real number */
+               double iVal = 0; /* cc warning */
+               sxu16 iLen;
+               /* Sanity check */
+               if( &zIn[2] >= zEnd ){
+                       /* Corrupt chunk */
+                       rc = SXERR_CORRUPT;
+                       break;
+               }
+               SyBigEndianUnpack16(zIn,&iLen);
+               if( &zIn[iLen] >= zEnd ){
+                       /* Corrupt chunk */
+                       rc = SXERR_CORRUPT;
+                       break;
+               }
+               zIn += 2;
+               SyStrToReal((const char *)zIn,(sxu32)iLen,&iVal,0);
+               /* Advance the pointer */
+               zIn += iLen;
+               jx9_value_double(pOut,iVal);
+               break;
+                                        }
+       case FJSON_STRING: {
+               /* UTF-8/Binary chunk */
+               sxu32 iLength;
+               /* Sanity check */
+               if( &zIn[4] >= zEnd ){
+                       /* Corrupt chunk */
+                       rc = SXERR_CORRUPT;
+                       break;
+               }
+               SyBigEndianUnpack32(zIn,&iLength);
+               if( &zIn[iLength] >= zEnd ){
+                       /* Corrupt chunk */
+                       rc = SXERR_CORRUPT;
+                       break;
+               }
+               zIn += 4;
+               /* Invalidate any prior representation */
+               if( pOut->iFlags & MEMOBJ_STRING ){
+                       /* Reset the string cursor */
+                       SyBlobReset(&pOut->sBlob);
+               }
+               rc = jx9MemObjStringAppend(pOut,(const char *)zIn,iLength);
+               /* Update pointer */
+               zIn += iLength;
+               break;
+                                          }
+       case FJSON_ARRAY_START: {
+               /* Binary JSON array */
+               jx9_hashmap *pMap;
+               jx9_value sVal;
+               /* Allocate a new hashmap */
+               pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0);
+               if( pMap == 0 ){
+                       rc = SXERR_MEM;
+                       break;
+               }
+               jx9MemObjInit(pOut->pVm,&sVal);
+               jx9MemObjRelease(pOut);
+               MemObjSetType(pOut,MEMOBJ_HASHMAP);
+               pOut->x.pOther = pMap;
+               rc = SXRET_OK;
+               for(;;){
+                       /* Jump leading binary commas */
+                       while (zIn < zEnd && zIn[0] == FJSON_COMMA ){
+                               zIn++;
+                       }
+                       if( zIn >= zEnd || zIn[0] == FJSON_ARRAY_END ){
+                               if( zIn < zEnd ){
+                                       zIn++; /* Jump the trailing binary ] */
+                               }
+                               break;
+                       }
+                       /* Decode the value */
+                       rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1);
+                       if( rc != SXRET_OK ){
+                               break;
+                       }
+                       /* Insert the decoded value */
+                       rc = jx9HashmapInsert(pMap,0,&sVal);
+                       if( rc != UNQLITE_OK ){
+                               break;
+                       }
+               }
+               if( rc != SXRET_OK ){
+                       jx9MemObjRelease(pOut);
+               }
+               jx9MemObjRelease(&sVal);
+               break;
+                                                       }
+       case FJSON_DOC_START: {
+               /* Binary JSON object */
+               jx9_value sVal,sKey;
+               jx9_hashmap *pMap;
+               /* Allocate a new hashmap */
+               pMap = (jx9_hashmap *)jx9NewHashmap(pOut->pVm,0,0);
+               if( pMap == 0 ){
+                       rc = SXERR_MEM;
+                       break;
+               }
+               jx9MemObjInit(pOut->pVm,&sVal);
+               jx9MemObjInit(pOut->pVm,&sKey);
+               jx9MemObjRelease(pOut);
+               MemObjSetType(pOut,MEMOBJ_HASHMAP);
+               pOut->x.pOther = pMap;
+               rc = SXRET_OK;
+               for(;;){
+                       /* Jump leading binary commas */
+                       while (zIn < zEnd && zIn[0] == FJSON_COMMA ){
+                               zIn++;
+                       }
+                       if( zIn >= zEnd || zIn[0] == FJSON_DOC_END ){
+                               if( zIn < zEnd ){
+                                       zIn++; /* Jump the trailing binary } */
+                               }
+                               break;
+                       }
+                       /* Extract the key */
+                       rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sKey,&zIn,iNest+1);
+                       if( rc != UNQLITE_OK ){
+                               break;
+                       }
+                       if( zIn >= zEnd || zIn[0] != FJSON_COLON ){
+                               rc = UNQLITE_CORRUPT;
+                               break;
+                       }
+                       zIn++; /* Jump the binary colon ':' */
+                       if( zIn >= zEnd ){
+                               rc = UNQLITE_CORRUPT;
+                               break;
+                       }
+                       /* Decode the value */
+                       rc = FastJsonDecode((const void *)zIn,(sxu32)(zEnd-zIn),&sVal,&zIn,iNest+1);
+                       if( rc != SXRET_OK ){
+                               break;
+                       }
+                       /* Insert the key and its associated value */
+                       rc = jx9HashmapInsert(pMap,&sKey,&sVal);
+                       if( rc != UNQLITE_OK ){
+                               break;
+                       }
+               }
+               if( rc != SXRET_OK ){
+                       jx9MemObjRelease(pOut);
+               }
+               jx9MemObjRelease(&sVal);
+               jx9MemObjRelease(&sKey);
+               break;
+                                                 }
+       default:
+               /* Corrupt data */
+               rc = SXERR_CORRUPT;
+               break;
+       }
+       if( pzPtr ){
+               *pzPtr = zIn;
+       }
+       return rc;
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_api.c
+ * MD5: 73cba599c009cee0ff878666d0543438
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: api.c v1.7 FreeBSD 2012-12-18 06:54 stable <chm@symisc.net> $ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+/* This file implement the public interfaces presented to host-applications.
+ * Routines in other files are for internal use by JX9 and should not be
+ * accessed by users of the library.
+ */
+#define JX9_ENGINE_MAGIC 0xF874BCD7
+#define JX9_ENGINE_MISUSE(ENGINE) (ENGINE == 0 || ENGINE->nMagic != JX9_ENGINE_MAGIC)
+#define JX9_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
+/* If another thread have released a working instance, the following macros
+ * evaluates to true. These macros are only used when the library
+ * is built with threading support enabled which is not the case in
+ * the default built.
+ */
+#define JX9_THRD_ENGINE_RELEASE(ENGINE) (ENGINE->nMagic != JX9_ENGINE_MAGIC)
+#define JX9_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
+/* IMPLEMENTATION: jx9@embedded@symisc 311-12-32 */
+/*
+ * All global variables are collected in the structure named "sJx9MPGlobal".
+ * That way it is clear in the code when we are using static variable because
+ * its name start with sJx9MPGlobal.
+ */
+static struct Jx9Global_Data
+{
+       SyMemBackend sAllocator;                /* Global low level memory allocator */
+#if defined(JX9_ENABLE_THREADS)
+       const SyMutexMethods *pMutexMethods;   /* Mutex methods */
+       SyMutex *pMutex;                       /* Global mutex */
+       sxu32 nThreadingLevel;                 /* Threading level: 0 == Single threaded/1 == Multi-Threaded 
+                                                                                   * The threading level can be set using the [jx9_lib_config()]
+                                                                                       * interface with a configuration verb set to
+                                                                                       * JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE or 
+                                                                                       * JX9_LIB_CONFIG_THREAD_LEVEL_MULTI
+                                                                                       */
+#endif
+       const jx9_vfs *pVfs;                    /* Underlying virtual file system */
+       sxi32 nEngine;                          /* Total number of active engines */
+       jx9 *pEngines;                          /* List of active engine */
+       sxu32 nMagic;                           /* Sanity check against library misuse */
+}sJx9MPGlobal = {
+       {0, 0, 0, 0, 0, 0, 0, 0, {0}}, 
+#if defined(JX9_ENABLE_THREADS)
+       0, 
+       0, 
+       0, 
+#endif
+       0, 
+       0, 
+       0, 
+       0
+};
+#define JX9_LIB_MAGIC  0xEA1495BA
+#define JX9_LIB_MISUSE (sJx9MPGlobal.nMagic != JX9_LIB_MAGIC)
+/*
+ * Supported threading level.
+ * These options have meaning only when the library is compiled with multi-threading
+ * support.That is, the JX9_ENABLE_THREADS compile time directive must be defined
+ * when JX9 is built.
+ * JX9_THREAD_LEVEL_SINGLE:
+ * In this mode, mutexing is disabled and the library can only be used by a single thread.
+ * JX9_THREAD_LEVEL_MULTI
+ * In this mode, all mutexes including the recursive mutexes on [jx9] objects
+ * are enabled so that the application is free to share the same engine
+ * between different threads at the same time.
+ */
+#define JX9_THREAD_LEVEL_SINGLE 1 
+#define JX9_THREAD_LEVEL_MULTI  2
+/*
+ * Configure a running JX9 engine instance.
+ * return JX9_OK on success.Any other return
+ * value indicates failure.
+ * Refer to [jx9_config()].
+ */
+JX9_PRIVATE sxi32 jx9EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap)
+{
+       jx9_conf *pConf = &pEngine->xConf;
+       int rc = JX9_OK;
+       /* Perform the requested operation */
+       switch(nOp){                                                                     
+       case JX9_CONFIG_ERR_LOG:{
+               /* Extract compile-time error log if any */
+               const char **pzPtr = va_arg(ap, const char **);
+               int *pLen = va_arg(ap, int *);
+               if( pzPtr == 0 ){
+                       rc = JX9_CORRUPT;
+                       break;
+               }
+               /* NULL terminate the error-log buffer */
+               SyBlobNullAppend(&pConf->sErrConsumer);
+               /* Point to the error-log buffer */
+               *pzPtr = (const char *)SyBlobData(&pConf->sErrConsumer);
+               if( pLen ){
+                       if( SyBlobLength(&pConf->sErrConsumer) > 1 /* NULL '\0' terminator */ ){
+                               *pLen = (int)SyBlobLength(&pConf->sErrConsumer);
+                       }else{
+                               *pLen = 0;
+                       }
+               }
+               break;
+                                                       }
+       case JX9_CONFIG_ERR_ABORT:
+               /* Reserved for future use */
+               break;
+       default:
+               /* Unknown configuration verb */
+               rc = JX9_CORRUPT;
+               break;
+       } /* Switch() */
+       return rc;
+}
+/*
+ * Configure the JX9 library.
+ * Return JX9_OK on success. Any other return value indicates failure.
+ * Refer to [jx9_lib_config()].
+ */
+static sxi32 Jx9CoreConfigure(sxi32 nOp, va_list ap)
+{
+       int rc = JX9_OK;
+       switch(nOp){
+           case JX9_LIB_CONFIG_VFS:{
+                       /* Install a virtual file system */
+                       const jx9_vfs *pVfs = va_arg(ap, const jx9_vfs *);
+                       sJx9MPGlobal.pVfs = pVfs;
+                       break;
+                                                               }
+               case JX9_LIB_CONFIG_USER_MALLOC: {
+                       /* Use an alternative low-level memory allocation routines */
+                       const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
+                       /* Save the memory failure callback (if available) */
+                       ProcMemError xMemErr = sJx9MPGlobal.sAllocator.xMemError;
+                       void *pMemErr = sJx9MPGlobal.sAllocator.pUserData;
+                       if( pMethods == 0 ){
+                               /* Use the built-in memory allocation subsystem */
+                               rc = SyMemBackendInit(&sJx9MPGlobal.sAllocator, xMemErr, pMemErr);
+                       }else{
+                               rc = SyMemBackendInitFromOthers(&sJx9MPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
+                       }
+                       break;
+                                                                                 }
+               case JX9_LIB_CONFIG_MEM_ERR_CALLBACK: {
+                       /* Memory failure callback */
+                       ProcMemError xMemErr = va_arg(ap, ProcMemError);
+                       void *pUserData = va_arg(ap, void *);
+                       sJx9MPGlobal.sAllocator.xMemError = xMemErr;
+                       sJx9MPGlobal.sAllocator.pUserData = pUserData;
+                       break;
+                                                                                                }        
+               case JX9_LIB_CONFIG_USER_MUTEX: {
+#if defined(JX9_ENABLE_THREADS)
+                       /* Use an alternative low-level mutex subsystem */
+                       const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
+#if defined (UNTRUST)
+                       if( pMethods == 0 ){
+                               rc = JX9_CORRUPT;
+                       }
+#endif
+                       /* Sanity check */
+                       if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
+                               /* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
+                               rc = JX9_CORRUPT;
+                               break;
+                       }
+                       if( sJx9MPGlobal.pMutexMethods ){
+                               /* Overwrite the previous mutex subsystem */
+                               SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex);
+                               if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){
+                                       sJx9MPGlobal.pMutexMethods->xGlobalRelease();
+                               }
+                               sJx9MPGlobal.pMutex = 0;
+                       }
+                       /* Initialize and install the new mutex subsystem */
+                       if( pMethods->xGlobalInit ){
+                               rc = pMethods->xGlobalInit();
+                               if ( rc != JX9_OK ){
+                                       break;
+                               }
+                       }
+                       /* Create the global mutex */
+                       sJx9MPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
+                       if( sJx9MPGlobal.pMutex == 0 ){
+                               /*
+                                * If the supplied mutex subsystem is so sick that we are unable to
+                                * create a single mutex, there is no much we can do here.
+                                */
+                               if( pMethods->xGlobalRelease ){
+                                       pMethods->xGlobalRelease();
+                               }
+                               rc = JX9_CORRUPT;
+                               break;
+                       }
+                       sJx9MPGlobal.pMutexMethods = pMethods;                  
+                       if( sJx9MPGlobal.nThreadingLevel == 0 ){
+                               /* Set a default threading level */
+                               sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI; 
+                       }
+#endif
+                       break;
+                                                                                  }
+               case JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE:
+#if defined(JX9_ENABLE_THREADS)
+                       /* Single thread mode(Only one thread is allowed to play with the library) */
+                       sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_SINGLE;
+#endif
+                       break;
+               case JX9_LIB_CONFIG_THREAD_LEVEL_MULTI:
+#if defined(JX9_ENABLE_THREADS)
+                       /* Multi-threading mode (library is thread safe and JX9 engines and virtual machines
+                        * may be shared between multiple threads).
+                        */
+                       sJx9MPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI;
+#endif
+                       break;
+               default:
+                       /* Unknown configuration option */
+                       rc = JX9_CORRUPT;
+                       break;
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_lib_config()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_lib_config(int nConfigOp, ...)
+{
+       va_list ap;
+       int rc;
+       if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){
+               /* Library is already initialized, this operation is forbidden */
+               return JX9_LOOKED;
+       }
+       va_start(ap, nConfigOp);
+       rc = Jx9CoreConfigure(nConfigOp, ap);
+       va_end(ap);
+       return rc;
+}
+/*
+ * Global library initialization
+ * Refer to [jx9_lib_init()]
+ * This routine must be called to initialize the memory allocation subsystem, the mutex 
+ * subsystem prior to doing any serious work with the library.The first thread to call
+ * this routine does the initialization process and set the magic number so no body later
+ * can re-initialize the library.If subsequent threads call this  routine before the first
+ * thread have finished the initialization process, then the subsequent threads must block 
+ * until the initialization process is done.
+ */
+static sxi32 Jx9CoreInitialize(void)
+{
+       const jx9_vfs *pVfs; /* Built-in vfs */
+#if defined(JX9_ENABLE_THREADS)
+       const SyMutexMethods *pMutexMethods = 0;
+       SyMutex *pMaster = 0;
+#endif
+       int rc;
+       /*
+        * If the library is already initialized, then a call to this routine
+        * is a no-op.
+        */
+       if( sJx9MPGlobal.nMagic == JX9_LIB_MAGIC ){
+               return JX9_OK; /* Already initialized */
+       }
+       if( sJx9MPGlobal.pVfs == 0 ){
+               /* Point to the built-in vfs */
+               pVfs = jx9ExportBuiltinVfs();
+               /* Install it */
+               jx9_lib_config(JX9_LIB_CONFIG_VFS, pVfs);
+       }
+#if defined(JX9_ENABLE_THREADS)
+       if( sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_SINGLE ){
+               pMutexMethods = sJx9MPGlobal.pMutexMethods;
+               if( pMutexMethods == 0 ){
+                       /* Use the built-in mutex subsystem */
+                       pMutexMethods = SyMutexExportMethods();
+                       if( pMutexMethods == 0 ){
+                               return JX9_CORRUPT; /* Can't happen */
+                       }
+                       /* Install the mutex subsystem */
+                       rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MUTEX, pMutexMethods);
+                       if( rc != JX9_OK ){
+                               return rc;
+                       }
+               }
+               /* Obtain a static mutex so we can initialize the library without calling malloc() */
+               pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
+               if( pMaster == 0 ){
+                       return JX9_CORRUPT; /* Can't happen */
+               }
+       }
+       /* Lock the master mutex */
+       rc = JX9_OK;
+       SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
+       if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){
+#endif
+               if( sJx9MPGlobal.sAllocator.pMethods == 0 ){
+                       /* Install a memory subsystem */
+                       rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
+                       if( rc != JX9_OK ){
+                               /* If we are unable to initialize the memory backend, there is no much we can do here.*/
+                               goto End;
+                       }
+               }
+#if defined(JX9_ENABLE_THREADS)
+               if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
+                       /* Protect the memory allocation subsystem */
+                       rc = SyMemBackendMakeThreadSafe(&sJx9MPGlobal.sAllocator, sJx9MPGlobal.pMutexMethods);
+                       if( rc != JX9_OK ){
+                               goto End;
+                       }
+               }
+#endif
+               /* Our library is initialized, set the magic number */
+               sJx9MPGlobal.nMagic = JX9_LIB_MAGIC;
+               rc = JX9_OK;
+#if defined(JX9_ENABLE_THREADS)
+       } /* sJx9MPGlobal.nMagic != JX9_LIB_MAGIC */
+#endif
+End:
+#if defined(JX9_ENABLE_THREADS)
+       /* Unlock the master mutex */
+       SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
+#endif
+       return rc;
+}
+/*
+ * Release an active JX9 engine and it's associated active virtual machines.
+ */
+static sxi32 EngineRelease(jx9 *pEngine)
+{
+       jx9_vm *pVm, *pNext;
+       /* Release all active VM */
+       pVm = pEngine->pVms;
+       for(;;){
+               if( pEngine->iVm < 1 ){
+                       break;
+               }
+               pNext = pVm->pNext;
+               jx9VmRelease(pVm);
+               pVm = pNext;
+               pEngine->iVm--;
+       }
+       /* Set a dummy magic number */
+       pEngine->nMagic = 0x7635;
+       /* Release the private memory subsystem */
+       SyMemBackendRelease(&pEngine->sAllocator); 
+       return JX9_OK;
+}
+/*
+ * Release all resources consumed by the library.
+ * If JX9 is already shut when this routine is invoked then this
+ * routine is a harmless no-op.
+ * Note: This call is not thread safe. Refer to [jx9_lib_shutdown()].
+ */
+static void JX9CoreShutdown(void)
+{
+       jx9 *pEngine, *pNext;
+       /* Release all active engines first */
+       pEngine = sJx9MPGlobal.pEngines;
+       for(;;){
+               if( sJx9MPGlobal.nEngine < 1 ){
+                       break;
+               }
+               pNext = pEngine->pNext;
+               EngineRelease(pEngine); 
+               pEngine = pNext;
+               sJx9MPGlobal.nEngine--;
+       }
+#if defined(JX9_ENABLE_THREADS)
+       /* Release the mutex subsystem */
+       if( sJx9MPGlobal.pMutexMethods ){
+               if( sJx9MPGlobal.pMutex ){
+                       SyMutexRelease(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex);
+                       sJx9MPGlobal.pMutex = 0;
+               }
+               if( sJx9MPGlobal.pMutexMethods->xGlobalRelease ){
+                       sJx9MPGlobal.pMutexMethods->xGlobalRelease();
+               }
+               sJx9MPGlobal.pMutexMethods = 0;
+       }
+       sJx9MPGlobal.nThreadingLevel = 0;
+#endif
+       if( sJx9MPGlobal.sAllocator.pMethods ){
+               /* Release the memory backend */
+               SyMemBackendRelease(&sJx9MPGlobal.sAllocator);
+       }
+       sJx9MPGlobal.nMagic = 0x1928;   
+}
+/*
+ * [CAPIREF: jx9_lib_shutdown()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_lib_shutdown(void)
+{
+       if( sJx9MPGlobal.nMagic != JX9_LIB_MAGIC ){
+               /* Already shut */
+               return JX9_OK;
+       }
+       JX9CoreShutdown();
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_lib_signature()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE const char * jx9_lib_signature(void)
+{
+       return JX9_SIG;
+}
+/*
+ * [CAPIREF: jx9_init()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_init(jx9 **ppEngine)
+{
+       jx9 *pEngine;
+       int rc;
+#if defined(UNTRUST)
+       if( ppEngine == 0 ){
+               return JX9_CORRUPT;
+       }
+#endif
+       *ppEngine = 0;
+       /* One-time automatic library initialization */
+       rc = Jx9CoreInitialize();
+       if( rc != JX9_OK ){
+               return rc;
+       }
+       /* Allocate a new engine */
+       pEngine = (jx9 *)SyMemBackendPoolAlloc(&sJx9MPGlobal.sAllocator, sizeof(jx9));
+       if( pEngine == 0 ){
+               return JX9_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pEngine, sizeof(jx9));
+       /* Initialize engine fields */
+       pEngine->nMagic = JX9_ENGINE_MAGIC;
+       rc = SyMemBackendInitFromParent(&pEngine->sAllocator, &sJx9MPGlobal.sAllocator);
+       if( rc != JX9_OK ){
+               goto Release;
+       }
+//#if defined(JX9_ENABLE_THREADS)
+//     SyMemBackendDisbaleMutexing(&pEngine->sAllocator);
+//#endif
+       /* Default configuration */
+       SyBlobInit(&pEngine->xConf.sErrConsumer, &pEngine->sAllocator);
+       /* Install a default compile-time error consumer routine */
+       pEngine->xConf.xErr = jx9VmBlobConsumer;
+       pEngine->xConf.pErrData = &pEngine->xConf.sErrConsumer;
+       /* Built-in vfs */
+       pEngine->pVfs = sJx9MPGlobal.pVfs;
+#if defined(JX9_ENABLE_THREADS)
+       if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
+                /* Associate a recursive mutex with this instance */
+                pEngine->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
+                if( pEngine->pMutex == 0 ){
+                        rc = JX9_NOMEM;
+                        goto Release;
+                }
+        }
+#endif
+       /* Link to the list of active engines */
+#if defined(JX9_ENABLE_THREADS)
+       /* Enter the global mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
+#endif
+       MACRO_LD_PUSH(sJx9MPGlobal.pEngines, pEngine);
+       sJx9MPGlobal.nEngine++;
+#if defined(JX9_ENABLE_THREADS)
+       /* Leave the global mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
+#endif
+       /* Write a pointer to the new instance */
+       *ppEngine = pEngine;
+       return JX9_OK;
+Release:
+       SyMemBackendRelease(&pEngine->sAllocator);
+       SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator,pEngine);
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_release()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_release(jx9 *pEngine)
+{
+       int rc;
+       if( JX9_ENGINE_MISUSE(pEngine) ){
+               return JX9_CORRUPT;
+       }
+#if defined(JX9_ENABLE_THREADS)
+        /* Acquire engine mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
+                JX9_THRD_ENGINE_RELEASE(pEngine) ){
+                        return JX9_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Release the engine */
+       rc = EngineRelease(&(*pEngine));
+#if defined(JX9_ENABLE_THREADS)
+        /* Leave engine mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        /* Release engine mutex */
+        SyMutexRelease(sJx9MPGlobal.pMutexMethods, pEngine->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+#endif
+#if defined(JX9_ENABLE_THREADS)
+       /* Enter the global mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
+#endif
+       /* Unlink from the list of active engines */
+       MACRO_LD_REMOVE(sJx9MPGlobal.pEngines, pEngine);
+       sJx9MPGlobal.nEngine--;
+#if defined(JX9_ENABLE_THREADS)
+       /* Leave the global mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, sJx9MPGlobal.pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
+#endif
+       /* Release the memory chunk allocated to this engine */
+       SyMemBackendPoolFree(&sJx9MPGlobal.sAllocator, pEngine);
+       return rc;
+}
+/*
+ * Compile a raw JX9 script.
+ * To execute a JX9 code, it must first be compiled into a bytecode program using this routine.
+ * If something goes wrong [i.e: compile-time error], your error log [i.e: error consumer callback]
+ * should  display the appropriate error message and this function set ppVm to null and return
+ * an error code that is different from JX9_OK. Otherwise when the script is successfully compiled
+ * ppVm should hold the JX9 bytecode and it's safe to call [jx9_vm_exec(), jx9_vm_reset(), etc.].
+ * This API does not actually evaluate the JX9 code. It merely compile and prepares the JX9 script
+ * for evaluation.
+ */
+static sxi32 ProcessScript(
+       jx9 *pEngine,          /* Running JX9 engine */
+       jx9_vm **ppVm,         /* OUT: A pointer to the virtual machine */
+       SyString *pScript,     /* Raw JX9 script to compile */
+       sxi32 iFlags,          /* Compile-time flags */
+       const char *zFilePath  /* File path if script come from a file. NULL otherwise */
+       )
+{
+       jx9_vm *pVm;
+       int rc;
+       /* Allocate a new virtual machine */
+       pVm = (jx9_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator, sizeof(jx9_vm));
+       if( pVm == 0 ){
+               /* If the supplied memory subsystem is so sick that we are unable to allocate
+                * a tiny chunk of memory, there is no much we can do here. */
+               if( ppVm ){
+                       *ppVm = 0;
+               }
+               return JX9_NOMEM;
+       }
+       if( iFlags < 0 ){
+               /* Default compile-time flags */
+               iFlags = 0;
+       }
+       /* Initialize the Virtual Machine */
+       rc = jx9VmInit(pVm, &(*pEngine));
+       if( rc != JX9_OK ){
+               SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
+               if( ppVm ){
+                       *ppVm = 0;
+               }
+               return JX9_VM_ERR;
+       }
+       if( zFilePath ){
+               /* Push processed file path */
+               jx9VmPushFilePath(pVm, zFilePath, -1, TRUE, 0);
+       }
+       /* Reset the error message consumer */
+       SyBlobReset(&pEngine->xConf.sErrConsumer);
+       /* Compile the script */
+       jx9CompileScript(pVm, &(*pScript), iFlags);
+       if( pVm->sCodeGen.nErr > 0 || pVm == 0){
+               sxu32 nErr = pVm->sCodeGen.nErr;
+               /* Compilation error or null ppVm pointer, release this VM */
+               SyMemBackendRelease(&pVm->sAllocator);
+               SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
+               if( ppVm ){
+                       *ppVm = 0;
+               }
+               return nErr > 0 ? JX9_COMPILE_ERR : JX9_OK;
+       }
+       /* Prepare the virtual machine for bytecode execution */
+       rc = jx9VmMakeReady(pVm);
+       if( rc != JX9_OK ){
+               goto Release;
+       }
+       /* Install local import path which is the current directory */
+       jx9_vm_config(pVm, JX9_VM_CONFIG_IMPORT_PATH, "./");
+#if defined(JX9_ENABLE_THREADS)
+       if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
+                /* Associate a recursive mutex with this instance */
+                pVm->pMutex = SyMutexNew(sJx9MPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
+                if( pVm->pMutex == 0 ){
+                        goto Release;
+                }
+        }
+#endif
+       /* Script successfully compiled, link to the list of active virtual machines */
+       MACRO_LD_PUSH(pEngine->pVms, pVm);
+       pEngine->iVm++;
+       /* Point to the freshly created VM */
+       *ppVm = pVm;
+       /* Ready to execute JX9 bytecode */
+       return JX9_OK;
+Release:
+       SyMemBackendRelease(&pVm->sAllocator);
+       SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
+       *ppVm = 0;
+       return JX9_VM_ERR;
+}
+/*
+ * [CAPIREF: jx9_compile()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm)
+{
+       SyString sScript;
+       int rc;
+       if( JX9_ENGINE_MISUSE(pEngine) ){
+               return JX9_CORRUPT;
+       }
+       if( zSource == 0 ){
+               /* Empty Jx9 statement ';' */
+               zSource = ";";
+               nLen = (int)sizeof(char);
+       }
+       if( nLen < 0 ){
+               /* Compute input length automatically */
+               nLen = (int)SyStrlen(zSource);
+       }
+       SyStringInitFromBuf(&sScript, zSource, nLen);
+#if defined(JX9_ENABLE_THREADS)
+        /* Acquire engine mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
+                JX9_THRD_ENGINE_RELEASE(pEngine) ){
+                        return JX9_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Compile the script */
+       rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,0,0);
+#if defined(JX9_ENABLE_THREADS)
+        /* Leave engine mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+#endif
+       /* Compilation result */
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_compile_file()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm)
+{
+       const jx9_vfs *pVfs;
+       int rc;
+       if( ppOutVm ){
+               *ppOutVm = 0;
+       }
+       rc = JX9_OK; /* cc warning */
+       if( JX9_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath) ){
+               return JX9_CORRUPT;
+       }
+#if defined(JX9_ENABLE_THREADS)
+        /* Acquire engine mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
+                JX9_THRD_ENGINE_RELEASE(pEngine) ){
+                        return JX9_ABORT; /* Another thread have released this instance */
+        }
+#endif
+        /*
+         * Check if the underlying vfs implement the memory map
+         * [i.e: mmap() under UNIX/MapViewOfFile() under windows] function.
+         */
+        pVfs = pEngine->pVfs;
+        if( pVfs == 0 || pVfs->xMmap == 0 ){
+                /* Memory map routine not implemented */
+                rc = JX9_IO_ERR;
+        }else{
+                void *pMapView = 0; /* cc warning */
+                jx9_int64 nSize = 0; /* cc warning */
+                SyString sScript;
+                /* Try to get a memory view of the whole file */
+                rc = pVfs->xMmap(zFilePath, &pMapView, &nSize);
+                if( rc != JX9_OK ){
+                        /* Assume an IO error */
+                        rc = JX9_IO_ERR;
+                }else{
+                        /* Compile the file */
+                        SyStringInitFromBuf(&sScript, pMapView, nSize);
+                        rc = ProcessScript(&(*pEngine), ppOutVm, &sScript,0,zFilePath);
+                        /* Release the memory view of the whole file */
+                        if( pVfs->xUnmap ){
+                                pVfs->xUnmap(pMapView, nSize);
+                        }
+                }
+        }
+#if defined(JX9_ENABLE_THREADS)
+        /* Leave engine mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+#endif
+       /* Compilation result */
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_vm_config()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...)
+{
+       va_list ap;
+       int rc;
+       /* Ticket 1433-002: NULL VM is harmless operation */
+       if ( JX9_VM_MISUSE(pVm) ){
+               return JX9_CORRUPT;
+       }
+#if defined(JX9_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
+                JX9_THRD_VM_RELEASE(pVm) ){
+                        return JX9_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Confiugure the virtual machine */
+       va_start(ap, iConfigOp);
+       rc = jx9VmConfigure(&(*pVm), iConfigOp, ap);
+       va_end(ap);
+#if defined(JX9_ENABLE_THREADS)
+        /* Leave VM mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_vm_release()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_vm_release(jx9_vm *pVm)
+{
+       jx9 *pEngine;
+       int rc;
+       /* Ticket 1433-002: NULL VM is harmless operation */
+       if ( JX9_VM_MISUSE(pVm) ){
+               return JX9_CORRUPT;
+       }
+#if defined(JX9_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
+                JX9_THRD_VM_RELEASE(pVm) ){
+                        return JX9_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       pEngine = pVm->pEngine;
+       rc = jx9VmRelease(&(*pVm));
+#if defined(JX9_ENABLE_THREADS)
+        /* Leave VM mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        /* Release VM mutex */
+        SyMutexRelease(sJx9MPGlobal.pMutexMethods, pVm->pMutex) /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+#endif
+       if( rc == JX9_OK ){
+               /* Unlink from the list of active VM */
+#if defined(JX9_ENABLE_THREADS)
+                       /* Acquire engine mutex */
+                       SyMutexEnter(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+                       if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
+                               JX9_THRD_ENGINE_RELEASE(pEngine) ){
+                                       return JX9_ABORT; /* Another thread have released this instance */
+                       }
+#endif
+               MACRO_LD_REMOVE(pEngine->pVms, pVm);
+               pEngine->iVm--;
+               /* Release the memory chunk allocated to this VM */
+               SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
+#if defined(JX9_ENABLE_THREADS)
+                       /* Leave engine mutex */
+                       SyMutexLeave(sJx9MPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+#endif 
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_create_function()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData)
+{
+       SyString sName;
+       int rc;
+       /* Ticket 1433-002: NULL VM is harmless operation */
+       if ( JX9_VM_MISUSE(pVm) ){
+               return JX9_CORRUPT;
+       }
+       SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
+       /* Remove leading and trailing white spaces */
+       SyStringFullTrim(&sName);
+       /* Ticket 1433-003: NULL values are not allowed */
+       if( sName.nByte < 1 || xFunc == 0 ){
+               return JX9_CORRUPT;
+       }
+#if defined(JX9_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
+                JX9_THRD_VM_RELEASE(pVm) ){
+                        return JX9_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Install the foreign function */
+       rc = jx9VmInstallForeignFunction(&(*pVm), &sName, xFunc, pUserData); 
+#if defined(JX9_ENABLE_THREADS)
+        /* Leave VM mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+#endif
+       return rc;
+}
+JX9_PRIVATE int jx9DeleteFunction(jx9_vm *pVm,const char *zName)
+{
+       jx9_user_func *pFunc = 0; /* cc warning */
+       int rc;
+       /* Perform the deletion */
+       rc = SyHashDeleteEntry(&pVm->hHostFunction, (const void *)zName, SyStrlen(zName), (void **)&pFunc);
+       if( rc == JX9_OK ){
+               /* Release internal fields */
+               SySetRelease(&pFunc->aAux);
+               SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
+               SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_create_constant()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData)
+{
+       SyString sName;
+       int rc;
+       /* Ticket 1433-002: NULL VM is harmless operation */
+       if ( JX9_VM_MISUSE(pVm) ){
+               return JX9_CORRUPT;
+       }
+       SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
+       /* Remove leading and trailing white spaces */
+       SyStringFullTrim(&sName);
+       if( sName.nByte < 1 ){
+               /* Empty constant name */
+               return JX9_CORRUPT;
+       }
+       /* TICKET 1433-003: NULL pointer is harmless operation */
+       if( xExpand == 0 ){
+               return JX9_CORRUPT;
+       }
+#if defined(JX9_ENABLE_THREADS)
+        /* Acquire VM mutex */
+        SyMutexEnter(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+        if( sJx9MPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE && 
+                JX9_THRD_VM_RELEASE(pVm) ){
+                        return JX9_ABORT; /* Another thread have released this instance */
+        }
+#endif
+       /* Perform the registration */
+       rc = jx9VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData);
+#if defined(JX9_ENABLE_THREADS)
+        /* Leave VM mutex */
+        SyMutexLeave(sJx9MPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sJx9MPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
+#endif
+        return rc;
+}
+JX9_PRIVATE int Jx9DeleteConstant(jx9_vm *pVm,const char *zName)
+{
+       jx9_constant *pCons;
+       int rc;
+       /* Query the constant hashtable */
+        rc = SyHashDeleteEntry(&pVm->hConstant, (const void *)zName, SyStrlen(zName), (void **)&pCons);
+        if( rc == JX9_OK ){
+                /* Perform the deletion */
+                SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pCons->sName));
+                SyMemBackendPoolFree(&pVm->sAllocator, pCons);
+        }
+        return rc;
+}
+/*
+ * [CAPIREF: jx9_new_scalar()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE jx9_value * jx9_new_scalar(jx9_vm *pVm)
+{
+       jx9_value *pObj;
+       /* Ticket 1433-002: NULL VM is harmless operation */
+       if ( JX9_VM_MISUSE(pVm) ){
+               return 0;
+       }
+       /* Allocate a new scalar variable */
+       pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
+       if( pObj == 0 ){
+               return 0;
+       }
+       /* Nullify the new scalar */
+       jx9MemObjInit(pVm, pObj);
+       return pObj;
+}
+/*
+ * [CAPIREF: jx9_new_array()] 
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE jx9_value * jx9_new_array(jx9_vm *pVm)
+{
+       jx9_hashmap *pMap;
+       jx9_value *pObj;
+       /* Ticket 1433-002: NULL VM is harmless operation */
+       if ( JX9_VM_MISUSE(pVm) ){
+               return 0;
+       }
+       /* Create a new hashmap first */
+       pMap = jx9NewHashmap(&(*pVm), 0, 0);
+       if( pMap == 0 ){
+               return 0;
+       }
+       /* Associate a new jx9_value with this hashmap */
+       pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
+       if( pObj == 0 ){
+               jx9HashmapRelease(pMap, TRUE);
+               return 0;
+       }
+       jx9MemObjInitFromArray(pVm, pObj, pMap);
+       return pObj;
+}
+/*
+ * [CAPIREF: jx9_release_value()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_release_value(jx9_vm *pVm, jx9_value *pValue)
+{
+       /* Ticket 1433-002: NULL VM is a harmless operation */
+       if ( JX9_VM_MISUSE(pVm) ){
+               return JX9_CORRUPT;
+       }
+       if( pValue ){
+               /* Release the value */
+               jx9MemObjRelease(pValue);
+               SyMemBackendPoolFree(&pVm->sAllocator, pValue);
+       }
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_to_int()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_to_int(jx9_value *pValue)
+{
+       int rc;
+       rc = jx9MemObjToInteger(pValue);
+       if( rc != JX9_OK ){
+               return 0;
+       }
+       return (int)pValue->x.iVal;
+}
+/*
+ * [CAPIREF: jx9_value_to_bool()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_to_bool(jx9_value *pValue)
+{
+       int rc;
+       rc = jx9MemObjToBool(pValue);
+       if( rc != JX9_OK ){
+               return 0;
+       }
+       return (int)pValue->x.iVal;
+}
+/*
+ * [CAPIREF: jx9_value_to_int64()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE jx9_int64 jx9_value_to_int64(jx9_value *pValue)
+{
+       int rc;
+       rc = jx9MemObjToInteger(pValue);
+       if( rc != JX9_OK ){
+               return 0;
+       }
+       return pValue->x.iVal;
+}
+/*
+ * [CAPIREF: jx9_value_to_double()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE double jx9_value_to_double(jx9_value *pValue)
+{
+       int rc;
+       rc = jx9MemObjToReal(pValue);
+       if( rc != JX9_OK ){
+               return (double)0;
+       }
+       return (double)pValue->x.rVal;
+}
+/*
+ * [CAPIREF: jx9_value_to_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE const char * jx9_value_to_string(jx9_value *pValue, int *pLen)
+{
+       jx9MemObjToString(pValue);
+       if( SyBlobLength(&pValue->sBlob) > 0 ){
+               SyBlobNullAppend(&pValue->sBlob);
+               if( pLen ){
+                       *pLen = (int)SyBlobLength(&pValue->sBlob);
+               }
+               return (const char *)SyBlobData(&pValue->sBlob);
+       }else{
+               /* Return the empty string */
+               if( pLen ){
+                       *pLen = 0;
+               }
+               return "";
+       }
+}
+/*
+ * [CAPIREF: jx9_value_to_resource()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE void * jx9_value_to_resource(jx9_value *pValue)
+{
+       if( (pValue->iFlags & MEMOBJ_RES) == 0 ){
+               /* Not a resource, return NULL */
+               return 0;
+       }
+       return pValue->x.pOther;
+}
+/*
+ * [CAPIREF: jx9_value_compare()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict)
+{
+       int rc;
+       if( pLeft == 0 || pRight == 0 ){
+               /* TICKET 1433-24: NULL values is harmless operation */
+               return 1;
+       }
+       /* Perform the comparison */
+       rc = jx9MemObjCmp(&(*pLeft), &(*pRight), bStrict, 0);
+       /* Comparison result */
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_result_int()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_int(jx9_context *pCtx, int iValue)
+{
+       return jx9_value_int(pCtx->pRet, iValue);
+}
+/*
+ * [CAPIREF: jx9_result_int64()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue)
+{
+       return jx9_value_int64(pCtx->pRet, iValue);
+}
+/*
+ * [CAPIREF: jx9_result_bool()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_bool(jx9_context *pCtx, int iBool)
+{
+       return jx9_value_bool(pCtx->pRet, iBool);
+}
+/*
+ * [CAPIREF: jx9_result_double()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_double(jx9_context *pCtx, double Value)
+{
+       return jx9_value_double(pCtx->pRet, Value);
+}
+/*
+ * [CAPIREF: jx9_result_null()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_null(jx9_context *pCtx)
+{
+       /* Invalidate any prior representation and set the NULL flag */
+       jx9MemObjRelease(pCtx->pRet);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_result_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen)
+{
+       return jx9_value_string(pCtx->pRet, zString, nLen);
+}
+/*
+ * [CAPIREF: jx9_result_string_format()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...)
+{
+       jx9_value *p;
+       va_list ap;
+       int rc;
+       p = pCtx->pRet;
+       if( (p->iFlags & MEMOBJ_STRING) == 0 ){
+               /* Invalidate any prior representation */
+               jx9MemObjRelease(p);
+               MemObjSetType(p, MEMOBJ_STRING);
+       }
+       /* Format the given string */
+       va_start(ap, zFormat);
+       rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
+       va_end(ap);
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_result_value()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_value(jx9_context *pCtx, jx9_value *pValue)
+{
+       int rc = JX9_OK;
+       if( pValue == 0 ){
+               jx9MemObjRelease(pCtx->pRet);
+       }else{
+               rc = jx9MemObjStore(pValue, pCtx->pRet);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_result_resource()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_result_resource(jx9_context *pCtx, void *pUserData)
+{
+       return jx9_value_resource(pCtx->pRet, pUserData);
+}
+/*
+ * [CAPIREF: jx9_context_new_scalar()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE jx9_value * jx9_context_new_scalar(jx9_context *pCtx)
+{
+       jx9_value *pVal;
+       pVal = jx9_new_scalar(pCtx->pVm);
+       if( pVal ){
+               /* Record value address so it can be freed automatically
+                * when the calling function returns. 
+                */
+               SySetPut(&pCtx->sVar, (const void *)&pVal);
+       }
+       return pVal;
+}
+/*
+ * [CAPIREF: jx9_context_new_array()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE jx9_value * jx9_context_new_array(jx9_context *pCtx)
+{
+       jx9_value *pVal;
+       pVal = jx9_new_array(pCtx->pVm);
+       if( pVal ){
+               /* Record value address so it can be freed automatically
+                * when the calling function returns. 
+                */
+               SySetPut(&pCtx->sVar, (const void *)&pVal);
+       }
+       return pVal;
+}
+/*
+ * [CAPIREF: jx9_context_release_value()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue)
+{
+       jx9VmReleaseContextValue(&(*pCtx), pValue);
+}
+/*
+ * [CAPIREF: jx9_context_alloc_chunk()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease)
+{
+       void *pChunk;
+       pChunk = SyMemBackendAlloc(&pCtx->pVm->sAllocator, nByte);
+       if( pChunk ){
+               if( ZeroChunk ){
+                       /* Zero the memory chunk */
+                       SyZero(pChunk, nByte);
+               }
+               if( AutoRelease ){
+                       jx9_aux_data sAux;
+                       /* Track the chunk so that it can be released automatically 
+                        * upon this context is destroyed.
+                        */
+                       sAux.pAuxData = pChunk;
+                       SySetPut(&pCtx->sChunk, (const void *)&sAux);
+               }
+       }
+       return pChunk;
+}
+/*
+ * Check if the given chunk address is registered in the call context
+ * chunk container.
+ * Return TRUE if registered.FALSE otherwise.
+ * Refer to [jx9_context_realloc_chunk(), jx9_context_free_chunk()].
+ */
+static jx9_aux_data * ContextFindChunk(jx9_context *pCtx, void *pChunk)
+{
+       jx9_aux_data *aAux, *pAux;
+       sxu32 n;
+       if( SySetUsed(&pCtx->sChunk) < 1 ){
+               /* Don't bother processing, the container is empty */
+               return 0;
+       }
+       /* Perform the lookup */
+       aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
+       for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
+               pAux = &aAux[n];
+               if( pAux->pAuxData == pChunk ){
+                       /* Chunk found */
+                       return pAux;
+               }
+       }
+       /* No such allocated chunk */
+       return 0;
+}
+/*
+ * [CAPIREF: jx9_context_realloc_chunk()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte)
+{
+       jx9_aux_data *pAux;
+       void *pNew;
+       pNew = SyMemBackendRealloc(&pCtx->pVm->sAllocator, pChunk, nByte);
+       if( pNew ){
+               pAux = ContextFindChunk(pCtx, pChunk);
+               if( pAux ){
+                       pAux->pAuxData = pNew;
+               }
+       }
+       return pNew;
+}
+/*
+ * [CAPIREF: jx9_context_free_chunk()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk)
+{
+       jx9_aux_data *pAux;
+       if( pChunk == 0 ){
+               /* TICKET-1433-93: NULL chunk is a harmless operation */
+               return;
+       }
+       pAux = ContextFindChunk(pCtx, pChunk);
+       if( pAux ){
+               /* Mark as destroyed */
+               pAux->pAuxData = 0;
+       }
+       SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
+}
+/*
+ * [CAPIREF: jx9_array_fetch()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte)
+{
+       jx9_hashmap_node *pNode;
+       jx9_value *pValue;
+       jx9_value skey;
+       int rc;
+       /* Make sure we are dealing with a valid hashmap */
+       if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
+               return 0;
+       }
+       if( nByte < 0 ){
+               nByte = (int)SyStrlen(zKey);
+       }
+       /* Convert the key to a jx9_value  */
+       jx9MemObjInit(pArray->pVm, &skey);
+       jx9MemObjStringAppend(&skey, zKey, (sxu32)nByte);
+       /* Perform the lookup */
+       rc = jx9HashmapLookup((jx9_hashmap *)pArray->x.pOther, &skey, &pNode);
+       jx9MemObjRelease(&skey);
+       if( rc != JX9_OK ){
+               /* No such entry */
+               return 0;
+       }
+       /* Extract the target value */
+       pValue = (jx9_value *)SySetAt(&pArray->pVm->aMemObj, pNode->nValIdx);
+       return pValue;
+}
+/*
+ * [CAPIREF: jx9_array_walk()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *pValue, jx9_value *, void *), void *pUserData)
+{
+       int rc;
+       if( xWalk == 0 ){
+               return JX9_CORRUPT;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
+               return JX9_CORRUPT;
+       }
+       /* Start the walk process */
+       rc = jx9HashmapWalk((jx9_hashmap *)pArray->x.pOther, xWalk, pUserData);
+       return rc != JX9_OK ? JX9_ABORT /* User callback request an operation abort*/ : JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_array_add_elem()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue)
+{
+       int rc;
+       /* Make sure we are dealing with a valid hashmap */
+       if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
+               return JX9_CORRUPT;
+       }
+       /* Perform the insertion */
+       rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &(*pKey), &(*pValue));
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_array_add_strkey_elem()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue)
+{
+       int rc;
+       /* Make sure we are dealing with a valid hashmap */
+       if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
+               return JX9_CORRUPT;
+       }
+       /* Perform the insertion */
+       if( SX_EMPTY_STR(zKey) ){
+               /* Empty key, assign an automatic index */
+               rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, 0, &(*pValue));
+       }else{
+               jx9_value sKey;
+               jx9MemObjInitFromString(pArray->pVm, &sKey, 0);
+               jx9MemObjStringAppend(&sKey, zKey, (sxu32)SyStrlen(zKey));
+               rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &sKey, &(*pValue));
+               jx9MemObjRelease(&sKey);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_array_count()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE unsigned int jx9_array_count(jx9_value *pArray)
+{
+       jx9_hashmap *pMap;
+       /* Make sure we are dealing with a valid hashmap */
+       if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
+               return 0;
+       }
+       /* Point to the internal representation of the hashmap */
+       pMap = (jx9_hashmap *)pArray->x.pOther;
+       return pMap->nEntry;
+}
+/*
+ * [CAPIREF: jx9_context_output()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen)
+{
+       SyString sData;
+       int rc;
+       if( nLen < 0 ){
+               nLen = (int)SyStrlen(zString);
+       }
+       SyStringInitFromBuf(&sData, zString, nLen);
+       rc = jx9VmOutputConsume(pCtx->pVm, &sData);
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_context_throw_error()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr)
+{
+       int rc = JX9_OK;
+       if( zErr ){
+               rc = jx9VmThrowError(pCtx->pVm, &pCtx->pFunc->sName, iErr, zErr);
+       }
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_context_throw_error_format()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...)
+{
+       va_list ap;
+       int rc;
+       if( zFormat == 0){
+               return JX9_OK;
+       }
+       va_start(ap, zFormat);
+       rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
+       va_end(ap);
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_context_random_num()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE unsigned int jx9_context_random_num(jx9_context *pCtx)
+{
+       sxu32 n;
+       n = jx9VmRandomNum(pCtx->pVm);
+       return n;
+}
+/*
+ * [CAPIREF: jx9_context_random_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen)
+{
+       if( nBuflen < 3 ){
+               return JX9_CORRUPT;
+       }
+       jx9VmRandomString(pCtx->pVm, zBuf, nBuflen);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_context_user_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE void * jx9_context_user_data(jx9_context *pCtx)
+{
+       return pCtx->pFunc->pUserData;
+}
+/*
+ * [CAPIREF: jx9_context_push_aux_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData)
+{
+       jx9_aux_data sAux;
+       int rc;
+       sAux.pAuxData = pUserData;
+       rc = SySetPut(&pCtx->pFunc->aAux, (const void *)&sAux);
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_context_peek_aux_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE void * jx9_context_peek_aux_data(jx9_context *pCtx)
+{
+       jx9_aux_data *pAux;
+       pAux = (jx9_aux_data *)SySetPeek(&pCtx->pFunc->aAux);
+       return pAux ? pAux->pAuxData : 0;
+}
+/*
+ * [CAPIREF: jx9_context_pop_aux_data()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE void * jx9_context_pop_aux_data(jx9_context *pCtx)
+{
+       jx9_aux_data *pAux;
+       pAux = (jx9_aux_data *)SySetPop(&pCtx->pFunc->aAux);
+       return pAux ? pAux->pAuxData : 0;
+}
+/*
+ * [CAPIREF: jx9_context_result_buf_length()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE unsigned int jx9_context_result_buf_length(jx9_context *pCtx)
+{
+       return SyBlobLength(&pCtx->pRet->sBlob);
+}
+/*
+ * [CAPIREF: jx9_function_name()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE const char * jx9_function_name(jx9_context *pCtx)
+{
+       SyString *pName;
+       pName = &pCtx->pFunc->sName;
+       return pName->zString;
+}
+/*
+ * [CAPIREF: jx9_value_int()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_int(jx9_value *pVal, int iValue)
+{
+       /* Invalidate any prior representation */
+       jx9MemObjRelease(pVal);
+       pVal->x.iVal = (jx9_int64)iValue;
+       MemObjSetType(pVal, MEMOBJ_INT);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_int64()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue)
+{
+       /* Invalidate any prior representation */
+       jx9MemObjRelease(pVal);
+       pVal->x.iVal = iValue;
+       MemObjSetType(pVal, MEMOBJ_INT);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_bool()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_bool(jx9_value *pVal, int iBool)
+{
+       /* Invalidate any prior representation */
+       jx9MemObjRelease(pVal);
+       pVal->x.iVal = iBool ? 1 : 0;
+       MemObjSetType(pVal, MEMOBJ_BOOL);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_null()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_null(jx9_value *pVal)
+{
+       /* Invalidate any prior representation and set the NULL flag */
+       jx9MemObjRelease(pVal);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_double()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_double(jx9_value *pVal, double Value)
+{
+       /* Invalidate any prior representation */
+       jx9MemObjRelease(pVal);
+       pVal->x.rVal = (jx9_real)Value;
+       MemObjSetType(pVal, MEMOBJ_REAL);
+       /* Try to get an integer representation also */
+       jx9MemObjTryInteger(pVal);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_string(jx9_value *pVal, const char *zString, int nLen)
+{
+       if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
+               /* Invalidate any prior representation */
+               jx9MemObjRelease(pVal);
+               MemObjSetType(pVal, MEMOBJ_STRING);
+       }
+       if( zString ){
+               if( nLen < 0 ){
+                       /* Compute length automatically */
+                       nLen = (int)SyStrlen(zString);
+               }
+               SyBlobAppend(&pVal->sBlob, (const void *)zString, (sxu32)nLen);
+       }
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_string_format()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...)
+{
+       va_list ap;
+       int rc;
+       if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
+               /* Invalidate any prior representation */
+               jx9MemObjRelease(pVal);
+               MemObjSetType(pVal, MEMOBJ_STRING);
+       }
+       va_start(ap, zFormat);
+       rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
+       va_end(ap);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_reset_string_cursor()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_reset_string_cursor(jx9_value *pVal)
+{
+       /* Reset the string cursor */
+       SyBlobReset(&pVal->sBlob);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_resource()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_resource(jx9_value *pVal, void *pUserData)
+{
+       /* Invalidate any prior representation */
+       jx9MemObjRelease(pVal);
+       /* Reflect the new type */
+       pVal->x.pOther = pUserData;
+       MemObjSetType(pVal, MEMOBJ_RES);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_release()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_release(jx9_value *pVal)
+{
+       jx9MemObjRelease(pVal);
+       return JX9_OK;
+}
+/*
+ * [CAPIREF: jx9_value_is_int()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_int(jx9_value *pVal)
+{
+       return (pVal->iFlags & MEMOBJ_INT) ? TRUE : FALSE;
+}
+/*
+ * [CAPIREF: jx9_value_is_float()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_float(jx9_value *pVal)
+{
+       return (pVal->iFlags & MEMOBJ_REAL) ? TRUE : FALSE;
+}
+/*
+ * [CAPIREF: jx9_value_is_bool()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_bool(jx9_value *pVal)
+{
+       return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE;
+}
+/*
+ * [CAPIREF: jx9_value_is_string()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_string(jx9_value *pVal)
+{
+       return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE;
+}
+/*
+ * [CAPIREF: jx9_value_is_null()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_null(jx9_value *pVal)
+{
+       return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE;
+}
+/*
+ * [CAPIREF: jx9_value_is_numeric()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_numeric(jx9_value *pVal)
+{
+       int rc;
+       rc = jx9MemObjIsNumeric(pVal);
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_value_is_callable()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_callable(jx9_value *pVal)
+{
+       int rc;
+       rc = jx9VmIsCallable(pVal->pVm, pVal);
+       return rc;
+}
+/*
+ * [CAPIREF: jx9_value_is_scalar()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_scalar(jx9_value *pVal)
+{
+       return (pVal->iFlags & MEMOBJ_SCALAR) ? TRUE : FALSE;
+}
+/*
+ * [CAPIREF: jx9_value_is_json_array()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_json_array(jx9_value *pVal)
+{
+       return (pVal->iFlags & MEMOBJ_HASHMAP) ? TRUE : FALSE;
+}
+/*
+ * [CAPIREF: jx9_value_is_json_object()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_json_object(jx9_value *pVal)
+{
+       jx9_hashmap *pMap;
+       if( (pVal->iFlags & MEMOBJ_HASHMAP) == 0 ){
+               return FALSE;
+       }
+       pMap = (jx9_hashmap *)pVal->x.pOther;
+       if( (pMap->iFlags & HASHMAP_JSON_OBJECT) == 0 ){
+               return FALSE;
+       }
+       return TRUE;
+}
+/*
+ * [CAPIREF: jx9_value_is_resource()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_resource(jx9_value *pVal)
+{
+       return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE;
+}
+/*
+ * [CAPIREF: jx9_value_is_empty()]
+ * Please refer to the official documentation for function purpose and expected parameters.
+ */
+JX9_PRIVATE int jx9_value_is_empty(jx9_value *pVal)
+{
+       int rc;
+       rc = jx9MemObjIsEmpty(pVal);
+       return rc;
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_builtin.c
+ * MD5: 97ae6ddf8ded9fe14634060675e12f80
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: builtin.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+/* This file implement built-in 'foreign' functions for the JX9 engine */
+/*
+ * Section:
+ *    Variable handling Functions.
+ * Authors:
+ *    Symisc Systems, devel@symisc.net.
+ *    Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Stable.
+ */
+/*
+ * bool is_bool($var)
+ *  Finds out whether a variable is a boolean.
+ * Parameters
+ *   $var: The variable being evaluated.
+ * Return
+ *  TRUE if var is a boolean. False otherwise.
+ */
+static int jx9Builtin_is_bool(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_bool(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_float($var)
+ * bool is_real($var)
+ * bool is_double($var)
+ *  Finds out whether a variable is a float.
+ * Parameters
+ *   $var: The variable being evaluated.
+ * Return
+ *  TRUE if var is a float. False otherwise.
+ */
+static int jx9Builtin_is_float(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_float(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_int($var)
+ * bool is_integer($var)
+ * bool is_long($var)
+ *  Finds out whether a variable is an integer.
+ * Parameters
+ *   $var: The variable being evaluated.
+ * Return
+ *  TRUE if var is an integer. False otherwise.
+ */
+static int jx9Builtin_is_int(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_int(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_string($var)
+ *  Finds out whether a variable is a string.
+ * Parameters
+ *   $var: The variable being evaluated.
+ * Return
+ *  TRUE if var is string. False otherwise.
+ */
+static int jx9Builtin_is_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_string(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_null($var)
+ *  Finds out whether a variable is NULL.
+ * Parameters
+ *   $var: The variable being evaluated.
+ * Return
+ *  TRUE if var is NULL. False otherwise.
+ */
+static int jx9Builtin_is_null(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_null(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_numeric($var)
+ *  Find out whether a variable is NULL.
+ * Parameters
+ *  $var: The variable being evaluated.
+ * Return
+ *  True if var is numeric. False otherwise.
+ */
+static int jx9Builtin_is_numeric(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_numeric(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_scalar($var)
+ *  Find out whether a variable is a scalar.
+ * Parameters
+ *  $var: The variable being evaluated.
+ * Return
+ *  True if var is scalar. False otherwise.
+ */
+static int jx9Builtin_is_scalar(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_scalar(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_array($var)
+ *  Find out whether a variable is an array.
+ * Parameters
+ *  $var: The variable being evaluated.
+ * Return
+ *  True if var is an array. False otherwise.
+ */
+static int jx9Builtin_is_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_json_array(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_object($var)
+ *  Find out whether a variable is an object.
+ * Parameters
+ *  $var: The variable being evaluated.
+ * Return
+ *  True if var is an object. False otherwise.
+ */
+static int jx9Builtin_is_object(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_json_object(apArg[0]);
+       }
+       /* Query result */
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * bool is_resource($var)
+ *  Find out whether a variable is a resource.
+ * Parameters
+ *  $var: The variable being evaluated.
+ * Return
+ *  True if a resource. False otherwise.
+ */
+static int jx9Builtin_is_resource(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 0; /* Assume false by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_resource(apArg[0]);
+       }
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * float floatval($var)
+ *  Get float value of a variable.
+ * Parameter
+ *  $var: The variable being processed.
+ * Return
+ *  the float value of a variable.
+ */
+static int jx9Builtin_floatval(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       if( nArg < 1 ){
+               /* return 0.0 */
+               jx9_result_double(pCtx, 0);
+       }else{
+               double dval;
+               /* Perform the cast */
+               dval = jx9_value_to_double(apArg[0]);
+               jx9_result_double(pCtx, dval);
+       }
+       return JX9_OK;
+}
+/*
+ * int intval($var)
+ *  Get integer value of a variable.
+ * Parameter
+ *  $var: The variable being processed.
+ * Return
+ *  the int value of a variable.
+ */
+static int jx9Builtin_intval(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       if( nArg < 1 ){
+               /* return 0 */
+               jx9_result_int(pCtx, 0);
+       }else{
+               sxi64 iVal;
+               /* Perform the cast */
+               iVal = jx9_value_to_int64(apArg[0]);
+               jx9_result_int64(pCtx, iVal);
+       }
+       return JX9_OK;
+}
+/*
+ * string strval($var)
+ *  Get the string representation of a variable.
+ * Parameter
+ *  $var: The variable being processed.
+ * Return
+ *  the string value of a variable.
+ */
+static int jx9Builtin_strval(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       if( nArg < 1 ){
+               /* return NULL */
+               jx9_result_null(pCtx);
+       }else{
+               const char *zVal;
+               int iLen = 0; /* cc -O6 warning */
+               /* Perform the cast */
+               zVal = jx9_value_to_string(apArg[0], &iLen);
+               jx9_result_string(pCtx, zVal, iLen);
+       }
+       return JX9_OK;
+}
+/*
+ * bool empty($var)
+ *  Determine whether a variable is empty.
+ * Parameters
+ *   $var: The variable being checked.
+ * Return
+ *  0 if var has a non-empty and non-zero value.1 otherwise.
+ */
+static int jx9Builtin_empty(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int res = 1; /* Assume empty by default */
+       if( nArg > 0 ){
+               res = jx9_value_is_empty(apArg[0]);
+       }
+       jx9_result_bool(pCtx, res);
+       return JX9_OK;
+       
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifdef JX9_ENABLE_MATH_FUNC
+/*
+ * Section:
+ *    Math Functions.
+ * Authors:
+ *    Symisc Systems, devel@symisc.net.
+ *    Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Stable.
+ */
+#include <stdlib.h> /* abs */
+#include <math.h>
+/*
+ * float sqrt(float $arg )
+ *  Square root of the given number.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The square root of arg or the special value Nan of failure.
+ */
+static int jx9Builtin_sqrt(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = sqrt(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float exp(float $arg )
+ *  Calculates the exponent of e.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  'e' raised to the power of arg.
+ */
+static int jx9Builtin_exp(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = exp(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float floor(float $arg )
+ *  Round fractions down.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  Returns the next lowest integer value by rounding down value if necessary.
+ */
+static int jx9Builtin_floor(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = floor(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float cos(float $arg )
+ *  Cosine.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The cosine of arg.
+ */
+static int jx9Builtin_cos(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = cos(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float acos(float $arg )
+ *  Arc cosine.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The arc cosine of arg.
+ */
+static int jx9Builtin_acos(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = acos(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float cosh(float $arg )
+ *  Hyperbolic cosine.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The hyperbolic cosine of arg.
+ */
+static int jx9Builtin_cosh(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = cosh(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float sin(float $arg )
+ *  Sine.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The sine of arg.
+ */
+static int jx9Builtin_sin(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = sin(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float asin(float $arg )
+ *  Arc sine.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The arc sine of arg.
+ */
+static int jx9Builtin_asin(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = asin(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float sinh(float $arg )
+ *  Hyperbolic sine.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The hyperbolic sine of arg.
+ */
+static int jx9Builtin_sinh(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = sinh(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float ceil(float $arg )
+ *  Round fractions up.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The next highest integer value by rounding up value if necessary.
+ */
+static int jx9Builtin_ceil(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = ceil(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float tan(float $arg )
+ *  Tangent.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The tangent of arg.
+ */
+static int jx9Builtin_tan(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = tan(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float atan(float $arg )
+ *  Arc tangent.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The arc tangent of arg.
+ */
+static int jx9Builtin_atan(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = atan(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float tanh(float $arg )
+ *  Hyperbolic tangent.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The Hyperbolic tangent of arg.
+ */
+static int jx9Builtin_tanh(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = tanh(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float atan2(float $y, float $x)
+ *  Arc tangent of two variable.
+ * Parameter
+ *  $y = Dividend parameter.
+ *  $x = Divisor parameter.
+ * Return
+ *  The arc tangent of y/x in radian.
+ */
+static int jx9Builtin_atan2(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x, y;
+       if( nArg < 2 ){
+               /* Missing arguments, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       y = jx9_value_to_double(apArg[0]);
+       x = jx9_value_to_double(apArg[1]);
+       /* Perform the requested operation */
+       r = atan2(y, x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float/int64 abs(float/int64 $arg )
+ *  Absolute value.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The absolute value of number.
+ */
+static int jx9Builtin_abs(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int is_float;   
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       is_float = jx9_value_is_float(apArg[0]);
+       if( is_float ){
+               double r, x;
+               x = jx9_value_to_double(apArg[0]);
+               /* Perform the requested operation */
+               r = fabs(x);
+               jx9_result_double(pCtx, r);
+       }else{
+               int r, x;
+               x = jx9_value_to_int(apArg[0]);
+               /* Perform the requested operation */
+               r = abs(x);
+               jx9_result_int(pCtx, r);
+       }
+       return JX9_OK;
+}
+/*
+ * float log(float $arg, [int/float $base])
+ *  Natural logarithm.
+ * Parameter
+ *  $arg: The number to process.
+ *  $base: The optional logarithmic base to use. (only base-10 is supported)
+ * Return
+ *  The logarithm of arg to base, if given, or the natural logarithm.
+ * Note: 
+ *  only Natural log and base-10 log are supported. 
+ */
+static int jx9Builtin_log(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       if( nArg == 2 && jx9_value_is_numeric(apArg[1]) && jx9_value_to_int(apArg[1]) == 10 ){
+               /* Base-10 log */
+               r = log10(x);
+       }else{
+               r = log(x);
+       }
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float log10(float $arg )
+ *  Base-10 logarithm.
+ * Parameter
+ *  The number to process.
+ * Return
+ *  The Base-10 logarithm of the given number.
+ */
+static int jx9Builtin_log10(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       /* Perform the requested operation */
+       r = log10(x);
+       /* store the result back */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * number pow(number $base, number $exp)
+ *  Exponential expression.
+ * Parameter
+ *  base
+ *  The base to use.
+ * exp
+ *  The exponent.
+ * Return
+ *  base raised to the power of exp.
+ *  If the result can be represented as integer it will be returned
+ *  as type integer, else it will be returned as type float. 
+ */
+static int jx9Builtin_pow(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double r, x, y;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       x = jx9_value_to_double(apArg[0]);
+       y = jx9_value_to_double(apArg[1]);
+       /* Perform the requested operation */
+       r = pow(x, y);
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float pi(void)
+ *  Returns an approximation of pi. 
+ * Note
+ *  you can use the M_PI constant which yields identical results to pi(). 
+ * Return
+ *  The value of pi as float.
+ */
+static int jx9Builtin_pi(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SXUNUSED(nArg); /* cc warning */
+       SXUNUSED(apArg);
+       jx9_result_double(pCtx, JX9_PI);
+       return JX9_OK;
+}
+/*
+ * float fmod(float $x, float $y)
+ *  Returns the floating point remainder (modulo) of the division of the arguments. 
+ * Parameters
+ * $x
+ *  The dividend
+ * $y
+ *  The divisor
+ * Return
+ *  The floating point remainder of x/y.
+ */
+static int jx9Builtin_fmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double x, y, r; 
+       if( nArg < 2 ){
+               /* Missing arguments */
+               jx9_result_double(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract given arguments */
+       x = jx9_value_to_double(apArg[0]);
+       y = jx9_value_to_double(apArg[1]);
+       /* Perform the requested operation */
+       r = fmod(x, y);
+       /* Processing result */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+/*
+ * float hypot(float $x, float $y)
+ *  Calculate the length of the hypotenuse of a right-angle triangle . 
+ * Parameters
+ * $x
+ *  Length of first side
+ * $y
+ *  Length of first side
+ * Return
+ *  Calculated length of the hypotenuse.
+ */
+static int jx9Builtin_hypot(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       double x, y, r; 
+       if( nArg < 2 ){
+               /* Missing arguments */
+               jx9_result_double(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract given arguments */
+       x = jx9_value_to_double(apArg[0]);
+       y = jx9_value_to_double(apArg[1]);
+       /* Perform the requested operation */
+       r = hypot(x, y);
+       /* Processing result */
+       jx9_result_double(pCtx, r);
+       return JX9_OK;
+}
+#endif /* JX9_ENABLE_MATH_FUNC */
+/*
+ * float round ( float $val [, int $precision = 0 [, int $mode = JX9_ROUND_HALF_UP ]] )
+ *  Exponential expression.
+ * Parameter
+ *  $val
+ *   The value to round.
+ * $precision
+ *   The optional number of decimal digits to round to.
+ * $mode
+ *   One of JX9_ROUND_HALF_UP, JX9_ROUND_HALF_DOWN, JX9_ROUND_HALF_EVEN, or JX9_ROUND_HALF_ODD.
+ *   (not supported).
+ * Return
+ *  The rounded value.
+ */
+static int jx9Builtin_round(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int n = 0;
+       double r;
+       if( nArg < 1 ){
+               /* Missing argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the precision if available */
+       if( nArg > 1 ){
+               n = jx9_value_to_int(apArg[1]);
+               if( n>30 ){
+                       n = 30;
+               }
+               if( n<0 ){
+                       n = 0;
+               }
+       }
+       r = jx9_value_to_double(apArg[0]);
+       /* If Y==0 and X will fit in a 64-bit int, 
+     * handle the rounding directly.Otherwise 
+        * use our own cutsom printf [i.e:SyBufferFormat()].
+     */
+  if( n==0 && r>=0 && r<LARGEST_INT64-1 ){
+    r = (double)((jx9_int64)(r+0.5));
+  }else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
+    r = -(double)((jx9_int64)((-r)+0.5));
+  }else{
+         char zBuf[256];
+         sxu32 nLen;
+         nLen = SyBufferFormat(zBuf, sizeof(zBuf), "%.*f", n, r);
+         /* Convert the string to real number */
+         SyStrToReal(zBuf, nLen, (void *)&r, 0);
+  }
+  /* Return thr rounded value */
+  jx9_result_double(pCtx, r);
+  return JX9_OK;
+}
+/*
+ * string dechex(int $number)
+ *  Decimal to hexadecimal.
+ * Parameters
+ *  $number
+ *   Decimal value to convert
+ * Return
+ *  Hexadecimal string representation of number
+ */
+static int jx9Builtin_dechex(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int iVal;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the given number */
+       iVal = jx9_value_to_int(apArg[0]);
+       /* Format */
+       jx9_result_string_format(pCtx, "%x", iVal);
+       return JX9_OK;
+}
+/*
+ * string decoct(int $number)
+ *  Decimal to Octal.
+ * Parameters
+ *  $number
+ *   Decimal value to convert
+ * Return
+ *  Octal string representation of number
+ */
+static int jx9Builtin_decoct(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int iVal;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the given number */
+       iVal = jx9_value_to_int(apArg[0]);
+       /* Format */
+       jx9_result_string_format(pCtx, "%o", iVal);
+       return JX9_OK;
+}
+/*
+ * string decbin(int $number)
+ *  Decimal to binary.
+ * Parameters
+ *  $number
+ *   Decimal value to convert
+ * Return
+ *  Binary string representation of number
+ */
+static int jx9Builtin_decbin(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int iVal;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the given number */
+       iVal = jx9_value_to_int(apArg[0]);
+       /* Format */
+       jx9_result_string_format(pCtx, "%B", iVal);
+       return JX9_OK;
+}
+/*
+ * int64 hexdec(string $hex_string)
+ *  Hexadecimal to decimal.
+ * Parameters
+ *  $hex_string
+ *   The hexadecimal string to convert
+ * Return
+ *  The decimal representation of hex_string 
+ */
+static int jx9Builtin_hexdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString, *zEnd;
+       jx9_int64 iVal;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return -1 */
+               jx9_result_int(pCtx, -1);
+               return JX9_OK;
+       }
+       iVal = 0;
+       if( jx9_value_is_string(apArg[0]) ){
+               /* Extract the given string */
+               zString = jx9_value_to_string(apArg[0], &nLen);
+               /* Delimit the string */
+               zEnd = &zString[nLen];
+               /* Ignore non hex-stream */
+               while( zString < zEnd ){
+                       if( (unsigned char)zString[0] >= 0xc0 ){
+                               /* UTF-8 stream */
+                               zString++;
+                               while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){
+                                       zString++;
+                               }
+                       }else{
+                               if( SyisHex(zString[0]) ){
+                                       break;
+                               }
+                               /* Ignore */
+                               zString++;
+                       }
+               }
+               if( zString < zEnd ){
+                       /* Cast */
+                       SyHexStrToInt64(zString, (sxu32)(zEnd-zString), (void *)&iVal, 0);
+               }
+       }else{
+               /* Extract as a 64-bit integer */
+               iVal = jx9_value_to_int64(apArg[0]);
+       }
+       /* Return the number */
+       jx9_result_int64(pCtx, iVal);
+       return JX9_OK;
+}
+/*
+ * int64 bindec(string $bin_string)
+ *  Binary to decimal.
+ * Parameters
+ *  $bin_string
+ *   The binary string to convert
+ * Return
+ *  Returns the decimal equivalent of the binary number represented by the binary_string argument.  
+ */
+static int jx9Builtin_bindec(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString;
+       jx9_int64 iVal;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return -1 */
+               jx9_result_int(pCtx, -1);
+               return JX9_OK;
+       }
+       iVal = 0;
+       if( jx9_value_is_string(apArg[0]) ){
+               /* Extract the given string */
+               zString = jx9_value_to_string(apArg[0], &nLen);
+               if( nLen > 0 ){
+                       /* Perform a binary cast */
+                       SyBinaryStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
+               }
+       }else{
+               /* Extract as a 64-bit integer */
+               iVal = jx9_value_to_int64(apArg[0]);
+       }
+       /* Return the number */
+       jx9_result_int64(pCtx, iVal);
+       return JX9_OK;
+}
+/*
+ * int64 octdec(string $oct_string)
+ *  Octal to decimal.
+ * Parameters
+ *  $oct_string
+ *   The octal string to convert
+ * Return
+ *  Returns the decimal equivalent of the octal number represented by the octal_string argument.  
+ */
+static int jx9Builtin_octdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString;
+       jx9_int64 iVal;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return -1 */
+               jx9_result_int(pCtx, -1);
+               return JX9_OK;
+       }
+       iVal = 0;
+       if( jx9_value_is_string(apArg[0]) ){
+               /* Extract the given string */
+               zString = jx9_value_to_string(apArg[0], &nLen);
+               if( nLen > 0 ){
+                       /* Perform the cast */
+                       SyOctalStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
+               }
+       }else{
+               /* Extract as a 64-bit integer */
+               iVal = jx9_value_to_int64(apArg[0]);
+       }
+       /* Return the number */
+       jx9_result_int64(pCtx, iVal);
+       return JX9_OK;
+}
+/*
+ * string base_convert(string $number, int $frombase, int $tobase)
+ *  Convert a number between arbitrary bases.
+ * Parameters
+ * $number
+ *  The number to convert
+ * $frombase
+ *  The base number is in
+ * $tobase
+ *  The base to convert number to
+ * Return
+ *  Number converted to base tobase 
+ */
+static int jx9Builtin_base_convert(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int nLen, iFbase, iTobase;
+       const char *zNum;
+       jx9_int64 iNum;
+       if( nArg < 3 ){
+               /* Return the empty string*/
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Base numbers */
+       iFbase  = jx9_value_to_int(apArg[1]);
+       iTobase = jx9_value_to_int(apArg[2]);
+       if( jx9_value_is_string(apArg[0]) ){
+               /* Extract the target number */
+               zNum = jx9_value_to_string(apArg[0], &nLen);
+               if( nLen < 1 ){
+                       /* Return the empty string*/
+                       jx9_result_string(pCtx, "", 0);
+                       return JX9_OK;
+               }
+               /* Base conversion */
+               switch(iFbase){
+               case 16:
+                       /* Hex */
+                       SyHexStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
+                       break;
+               case 8:
+                       /* Octal */
+                       SyOctalStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
+                       break;
+               case 2:
+                       /* Binary */
+                       SyBinaryStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
+                       break;
+               default:
+                       /* Decimal */
+                       SyStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
+                       break;
+               }
+       }else{
+               iNum = jx9_value_to_int64(apArg[0]);
+       }
+       switch(iTobase){
+       case 16:
+               /* Hex */
+               jx9_result_string_format(pCtx, "%qx", iNum); /* Quad hex */
+               break;
+       case 8:
+               /* Octal */
+               jx9_result_string_format(pCtx, "%qo", iNum); /* Quad octal */
+               break;
+       case 2:
+               /* Binary */
+               jx9_result_string_format(pCtx, "%qB", iNum); /* Quad binary */
+               break;
+       default:
+               /* Decimal */
+               jx9_result_string_format(pCtx, "%qd", iNum); /* Quad decimal */
+               break;
+       }
+       return JX9_OK;
+}
+/*
+ * Section:
+ *    String handling Functions.
+ * Authors:
+ *    Symisc Systems, devel@symisc.net.
+ *    Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Stable.
+ */
+/*
+ * string substr(string $string, int $start[, int $length ])
+ *  Return part of a string.
+ * Parameters
+ *  $string
+ *   The input string. Must be one character or longer.
+ * $start
+ *   If start is non-negative, the returned string will start at the start'th position
+ *   in string, counting from zero. For instance, in the string 'abcdef', the character
+ *   at position 0 is 'a', the character at position 2 is 'c', and so forth.
+ *   If start is negative, the returned string will start at the start'th character
+ *   from the end of string.
+ *   If string is less than or equal to start characters long, FALSE will be returned.
+ * $length
+ *   If length is given and is positive, the string returned will contain at most length
+ *   characters beginning from start (depending on the length of string).
+ *   If length is given and is negative, then that many characters will be omitted from
+ *   the end of string (after the start position has been calculated when a start is negative).
+ *   If start denotes the position of this truncation or beyond, false will be returned.
+ *   If length is given and is 0, FALSE or NULL an empty string will be returned.
+ *   If length is omitted, the substring starting from start until the end of the string
+ *   will be returned. 
+ * Return
+ *  Returns the extracted part of string, or FALSE on failure or an empty string.
+ */
+static int jx9Builtin_substr(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zSource, *zOfft;
+       int nOfft, nLen, nSrcLen;       
+       if( nArg < 2 ){
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zSource = jx9_value_to_string(apArg[0], &nSrcLen);
+       if( nSrcLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       nLen = nSrcLen; /* cc warning */
+       /* Extract the offset */
+       nOfft = jx9_value_to_int(apArg[1]);
+       if( nOfft < 0 ){
+               zOfft = &zSource[nSrcLen+nOfft]; 
+               if( zOfft < zSource ){
+                       /* Invalid offset */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               nLen = (int)(&zSource[nSrcLen]-zOfft);
+               nOfft = (int)(zOfft-zSource);
+       }else if( nOfft >= nSrcLen ){
+               /* Invalid offset */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }else{
+               zOfft = &zSource[nOfft];
+               nLen = nSrcLen - nOfft;
+       }
+       if( nArg > 2 ){
+               /* Extract the length */
+               nLen = jx9_value_to_int(apArg[2]);
+               if( nLen == 0 ){
+                       /* Invalid length, return an empty string */
+                       jx9_result_string(pCtx, "", 0);
+                       return JX9_OK;
+               }else if( nLen < 0 ){
+                       nLen = nSrcLen + nLen - nOfft;
+                       if( nLen < 1 ){
+                               /* Invalid  length */
+                               nLen = nSrcLen - nOfft;
+                       }
+               }
+               if( nLen + nOfft > nSrcLen ){
+                       /* Invalid length */
+                       nLen = nSrcLen - nOfft;
+               }
+       }
+       /* Return the substring */
+       jx9_result_string(pCtx, zOfft, nLen);
+       return JX9_OK;
+}
+/*
+ * int substr_compare(string $main_str, string $str , int $offset[, int $length[, bool $case_insensitivity = false ]])
+ *  Binary safe comparison of two strings from an offset, up to length characters.
+ * Parameters
+ *  $main_str
+ *  The main string being compared.
+ *  $str
+ *   The secondary string being compared.
+ * $offset
+ *  The start position for the comparison. If negative, it starts counting from
+ *  the end of the string.
+ * $length
+ *  The length of the comparison. The default value is the largest of the length 
+ *  of the str compared to the length of main_str less the offset.
+ * $case_insensitivity
+ *  If case_insensitivity is TRUE, comparison is case insensitive.
+ * Return
+ *  Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than
+ *  str, and 0 if they are equal. If offset is equal to or greater than the length of main_str
+ *  or length is set and is less than 1, substr_compare() prints a warning and returns FALSE. 
+ */
+static int jx9Builtin_substr_compare(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zSource, *zOfft, *zSub;
+       int nOfft, nLen, nSrcLen, nSublen;
+       int iCase = 0;
+       int rc;
+       if( nArg < 3 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zSource = jx9_value_to_string(apArg[0], &nSrcLen);
+       if( nSrcLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       nLen = nSrcLen; /* cc warning */
+       /* Extract the substring */
+       zSub = jx9_value_to_string(apArg[1], &nSublen);
+       if( nSublen < 1 || nSublen > nSrcLen){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the offset */
+       nOfft = jx9_value_to_int(apArg[2]);
+       if( nOfft < 0 ){
+               zOfft = &zSource[nSrcLen+nOfft]; 
+               if( zOfft < zSource ){
+                       /* Invalid offset */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               nLen = (int)(&zSource[nSrcLen]-zOfft);
+               nOfft = (int)(zOfft-zSource);
+       }else if( nOfft >= nSrcLen ){
+               /* Invalid offset */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }else{
+               zOfft = &zSource[nOfft];
+               nLen = nSrcLen - nOfft;
+       }
+       if( nArg > 3 ){
+               /* Extract the length */
+               nLen = jx9_value_to_int(apArg[3]);
+               if( nLen < 1 ){
+                       /* Invalid  length */
+                       jx9_result_int(pCtx, 1);
+                       return JX9_OK;
+               }else if( nLen + nOfft > nSrcLen ){
+                       /* Invalid length */
+                       nLen = nSrcLen - nOfft;
+               }
+               if( nArg > 4 ){
+                       /* Case-sensitive or not */
+                       iCase = jx9_value_to_bool(apArg[4]);
+               }
+       }
+       /* Perform the comparison */
+       if( iCase ){
+               rc = SyStrnicmp(zOfft, zSub, (sxu32)nLen);
+       }else{
+               rc = SyStrncmp(zOfft, zSub, (sxu32)nLen);
+       }
+       /* Comparison result */
+       jx9_result_int(pCtx, rc);
+       return JX9_OK;
+}
+/*
+ * int substr_count(string $haystack, string $needle[, int $offset = 0 [, int $length ]])
+ *  Count the number of substring occurrences.
+ * Parameters
+ * $haystack
+ *   The string to search in
+ * $needle
+ *   The substring to search for
+ * $offset
+ *  The offset where to start counting
+ * $length (NOT USED)
+ *  The maximum length after the specified offset to search for the substring.
+ *  It outputs a warning if the offset plus the length is greater than the haystack length.
+ * Return
+ *  Toral number of substring occurrences.
+ */
+static int jx9Builtin_substr_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zText, *zPattern, *zEnd;
+       int nTextlen, nPatlen;
+       int iCount = 0;
+       sxu32 nOfft;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the haystack */
+       zText = jx9_value_to_string(apArg[0], &nTextlen);
+       /* Point to the neddle */
+       zPattern = jx9_value_to_string(apArg[1], &nPatlen);
+       if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){
+               /* NOOP, return zero */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       if( nArg > 2 ){
+               int nOfft;
+               /* Extract the offset */
+               nOfft = jx9_value_to_int(apArg[2]);
+               if( nOfft < 0 || nOfft > nTextlen ){
+                       /* Invalid offset, return zero */
+                       jx9_result_int(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Point to the desired offset */
+               zText = &zText[nOfft];
+               /* Adjust length */
+               nTextlen -= nOfft;
+       }
+       /* Point to the end of the string */
+       zEnd = &zText[nTextlen];
+       if( nArg > 3 ){
+               int nLen;
+               /* Extract the length */
+               nLen = jx9_value_to_int(apArg[3]);
+               if( nLen < 0 || nLen > nTextlen ){
+                       /* Invalid length, return 0 */
+                       jx9_result_int(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Adjust pointer */
+               nTextlen = nLen;
+               zEnd = &zText[nTextlen];
+       }
+       /* Perform the search */
+       for(;;){
+               rc = SyBlobSearch((const void *)zText, (sxu32)(zEnd-zText), (const void *)zPattern, nPatlen, &nOfft);
+               if( rc != SXRET_OK ){
+                       /* Pattern not found, break immediately */
+                       break;
+               }
+               /* Increment counter and update the offset */
+               iCount++;
+               zText += nOfft + nPatlen;
+               if( zText >= zEnd ){
+                       break;
+               }
+       }
+       /* Pattern count */
+       jx9_result_int(pCtx, iCount);
+       return JX9_OK;
+}
+/*
+ * string chunk_split(string $body[, int $chunklen = 76 [, string $end = "\r\n" ]])
+ *   Split a string into smaller chunks.
+ * Parameters
+ *  $body
+ *   The string to be chunked.
+ * $chunklen
+ *   The chunk length.
+ * $end
+ *   The line ending sequence.
+ * Return
+ *  The chunked string or NULL on failure.
+ */
+static int jx9Builtin_chunk_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn, *zEnd, *zSep = "\r\n";
+       int nSepLen, nChunkLen, nLen;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Nothing to split, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* initialize/Extract arguments */
+       nSepLen = (int)sizeof("\r\n") - 1;
+       nChunkLen = 76;
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nArg > 1 ){
+               /* Chunk length */
+               nChunkLen = jx9_value_to_int(apArg[1]);
+               if( nChunkLen < 1 ){
+                       /* Switch back to the default length */
+                       nChunkLen = 76;
+               }
+               if( nArg > 2 ){
+                       /* Separator */
+                       zSep = jx9_value_to_string(apArg[2], &nSepLen);
+                       if( nSepLen < 1 ){
+                               /* Switch back to the default separator */
+                               zSep = "\r\n";
+                               nSepLen = (int)sizeof("\r\n") - 1;
+                       }
+               }
+       }
+       /* Perform the requested operation */
+       if( nChunkLen > nLen ){
+               /* Nothing to split, return the string and the separator */
+               jx9_result_string_format(pCtx, "%.*s%.*s", nLen, zIn, nSepLen, zSep);
+               return JX9_OK;
+       }
+       while( zIn < zEnd ){
+               if( nChunkLen > (int)(zEnd-zIn) ){
+                       nChunkLen = (int)(zEnd - zIn);
+               }
+               /* Append the chunk and the separator */
+               jx9_result_string_format(pCtx, "%.*s%.*s", nChunkLen, zIn, nSepLen, zSep);
+               /* Point beyond the chunk */
+               zIn += nChunkLen;
+       }
+       return JX9_OK;
+}
+/*
+ * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]])
+ *  HTML escaping of special characters.
+ *  The translations performed are:
+ *   '&' (ampersand) ==> '&amp;'
+ *   '"' (double quote) ==> '&quot;' when ENT_NOQUOTES is not set.
+ *   "'" (single quote) ==> '&#039;' only when ENT_QUOTES is set.
+ *   '<' (less than) ==> '&lt;'
+ *   '>' (greater than) ==> '&gt;'
+ * Parameters
+ *  $string
+ *   The string being converted.
+ * $flags
+ *   A bitmask of one or more of the following flags, which specify how to handle quotes.
+ *   The default is ENT_COMPAT | ENT_HTML401.
+ *   ENT_COMPAT        Will convert double-quotes and leave single-quotes alone.
+ *   ENT_QUOTES        Will convert both double and single quotes.
+ *   ENT_NOQUOTES      Will leave both double and single quotes unconverted.
+ *   ENT_IGNORE        Silently discard invalid code unit sequences instead of returning an empty string.
+ * $charset
+ *  Defines character set used in conversion. The default character set is ISO-8859-1. (Not used)
+ * Return
+ *  The escaped string or NULL on failure.
+ */
+static int jx9Builtin_htmlspecialchars(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zCur, *zIn, *zEnd;
+       int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */
+       int nLen, c;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       /* Extract the flags if available */
+       if( nArg > 1 ){
+               iFlags = jx9_value_to_int(apArg[1]);
+               if( iFlags < 0 ){
+                       iFlags = 0x01|0x40;
+               }
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       break;
+               }
+               zCur = zIn;
+               while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){
+                       zIn++;
+               }
+               if( zCur < zIn ){
+                       /* Append the raw string verbatim */
+                       jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
+               }
+               if( zIn >= zEnd ){
+                       break;
+               }
+               c = zIn[0];
+               if( c == '&' ){
+                       /* Expand '&amp;' */
+                       jx9_result_string(pCtx, "&amp;", (int)sizeof("&amp;")-1);
+               }else if( c == '<' ){
+                       /* Expand '&lt;' */
+                       jx9_result_string(pCtx, "&lt;", (int)sizeof("&lt;")-1);
+               }else if( c == '>' ){
+                       /* Expand '&gt;' */
+                       jx9_result_string(pCtx, "&gt;", (int)sizeof("&gt;")-1);
+               }else if( c == '\'' ){
+                       if( iFlags & 0x02 /*ENT_QUOTES*/ ){
+                               /* Expand '&#039;' */
+                               jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
+                       }else{
+                               /* Leave the single quote untouched */
+                               jx9_result_string(pCtx, "'", (int)sizeof(char));
+                       }
+               }else if( c == '"' ){
+                       if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
+                               /* Expand '&quot;' */
+                               jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
+                       }else{
+                               /* Leave the double quote untouched */
+                               jx9_result_string(pCtx, "\"", (int)sizeof(char));
+                       }
+               }
+               /* Ignore the unsafe HTML character */
+               zIn++;
+       }
+       return JX9_OK;
+}
+/*
+ * string htmlspecialchars_decode(string $string[, int $quote_style = ENT_COMPAT ])
+ *  Unescape HTML entities.
+ * Parameters
+ *  $string
+ *   The string to decode
+ *  $quote_style
+ *    The quote style. One of the following constants:
+ *   ENT_COMPAT        Will convert double-quotes and leave single-quotes alone (default)
+ *   ENT_QUOTES        Will convert both double and single quotes
+ *   ENT_NOQUOTES      Will leave both double and single quotes unconverted
+ * Return
+ *  The unescaped string or NULL on failure.
+ */
+static int jx9Builtin_htmlspecialchars_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zCur, *zIn, *zEnd;
+       int iFlags = 0x01; /* ENT_COMPAT */
+       int nLen, nJump;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       /* Extract the flags if available */
+       if( nArg > 1 ){
+               iFlags = jx9_value_to_int(apArg[1]);
+               if( iFlags < 0 ){
+                       iFlags = 0x01;
+               }
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       break;
+               }
+               zCur = zIn;
+               while( zIn < zEnd && zIn[0] != '&' ){
+                       zIn++;
+               }
+               if( zCur < zIn ){
+                       /* Append the raw string verbatim */
+                       jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
+               }
+               nLen = (int)(zEnd-zIn);
+               nJump = (int)sizeof(char);
+               if( nLen >= (int)sizeof("&amp;")-1 && SyStrnicmp(zIn, "&amp;", sizeof("&amp;")-1) == 0 ){
+                       /* &amp; ==> '&' */
+                       jx9_result_string(pCtx, "&", (int)sizeof(char));
+                       nJump = (int)sizeof("&amp;")-1;
+               }else if( nLen >= (int)sizeof("&lt;")-1 && SyStrnicmp(zIn, "&lt;", sizeof("&lt;")-1) == 0 ){
+                       /* &lt; ==> < */
+                       jx9_result_string(pCtx, "<", (int)sizeof(char));
+                       nJump = (int)sizeof("&lt;")-1; 
+               }else if( nLen >= (int)sizeof("&gt;")-1 && SyStrnicmp(zIn, "&gt;", sizeof("&gt;")-1) == 0 ){
+                       /* &gt; ==> '>' */
+                       jx9_result_string(pCtx, ">", (int)sizeof(char));
+                       nJump = (int)sizeof("&gt;")-1; 
+               }else if( nLen >= (int)sizeof("&quot;")-1 && SyStrnicmp(zIn, "&quot;", sizeof("&quot;")-1) == 0 ){
+                       /* &quot; ==> '"' */
+                       if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
+                               jx9_result_string(pCtx, "\"", (int)sizeof(char));
+                       }else{
+                               /* Leave untouched */
+                               jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
+                       }
+                       nJump = (int)sizeof("&quot;")-1;
+               }else if( nLen >= (int)sizeof("&#039;")-1 && SyStrnicmp(zIn, "&#039;", sizeof("&#039;")-1) == 0 ){
+                       /* &#039; ==> ''' */
+                       if( iFlags & 0x02 /*ENT_QUOTES*/ ){
+                               /* Expand ''' */
+                               jx9_result_string(pCtx, "'", (int)sizeof(char));
+                       }else{
+                               /* Leave untouched */
+                               jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
+                       }
+                       nJump = (int)sizeof("&#039;")-1;
+               }else if( nLen >= (int)sizeof(char) ){
+                       /* expand '&' */
+                       jx9_result_string(pCtx, "&", (int)sizeof(char));
+               }else{
+                       /* No more input to process */
+                       break;
+               }
+               zIn += nJump;
+       }
+       return JX9_OK;
+}
+/* HTML encoding/Decoding table 
+ * Source: Symisc RunTime API.[chm@symisc.net]
+ */
+static const char *azHtmlEscape[] = {
+       "&lt;", "<", "&gt;", ">", "&amp;", "&", "&quot;", "\"", "&#39;", "'", 
+       "&#33;", "!", "&#36;", "$", "&#35;", "#", "&#37;", "%", "&#40;", "(", 
+       "&#41;", ")", "&#123;", "{", "&#125;", "}", "&#61;", "=", "&#43;", "+", 
+       "&#63;", "?", "&#91;", "[", "&#93;", "]", "&#64;", "@", "&#44;", "," 
+ };
+/*
+ * array get_html_translation_table(void)
+ *  Returns the translation table used by htmlspecialchars() and htmlentities().
+ * Parameters
+ *  None
+ * Return
+ *  The translation table as an array or NULL on failure.
+ */
+static int jx9Builtin_get_html_translation_table(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_value *pArray, *pValue;
+       sxu32 n;
+       /* Element value */
+       pValue = jx9_context_new_scalar(pCtx);
+       if( pValue == 0 ){
+               SXUNUSED(nArg); /* cc warning */
+               SXUNUSED(apArg);
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Make the table */
+       for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
+               /* Prepare the value */
+               jx9_value_string(pValue, azHtmlEscape[n], -1 /* Compute length automatically */);
+               /* Insert the value */
+               jx9_array_add_strkey_elem(pArray, azHtmlEscape[n+1], pValue);
+               /* Reset the string cursor */
+               jx9_value_reset_string_cursor(pValue);
+       }
+       /* 
+        * Return the array.
+        * Don't worry about freeing memory, everything will be automatically
+        * released upon we return from this function.
+        */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]);
+ *   Convert all applicable characters to HTML entities
+ * Parameters
+ * $string
+ *   The input string.
+ * $flags
+ *  A bitmask of one or more of the flags (see block-comment on jx9Builtin_htmlspecialchars())
+ * Return
+ * The encoded string.
+ */
+static int jx9Builtin_htmlentities(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int iFlags = 0x01; /* ENT_COMPAT */
+       const char *zIn, *zEnd;
+       int nLen, c;
+       sxu32 n;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       /* Extract the flags if available */
+       if( nArg > 1 ){
+               iFlags = jx9_value_to_int(apArg[1]);
+               if( iFlags < 0 ){
+                       iFlags = 0x01;
+               }
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               c = zIn[0];
+               /* Perform a linear lookup on the decoding table */
+               for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
+                       if( azHtmlEscape[n+1][0] == c ){
+                               /* Got one */
+                               break;
+                       }
+               }
+               if( n < SX_ARRAYSIZE(azHtmlEscape) ){
+                       /* Output the safe sequence [i.e: '<' ==> '&lt;"] */
+                       if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
+                               /* Expand the double quote verbatim */
+                               jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
+                       }else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
+                               /* expand single quote verbatim */
+                               jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
+                       }else{
+                               jx9_result_string(pCtx, azHtmlEscape[n], -1/*Compute length automatically */);
+                       }
+               }else{
+                       /* Output character verbatim */
+                       jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
+               }
+               zIn++;
+       }
+       return JX9_OK;
+}
+/*
+ * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]])
+ *   Perform the reverse operation of html_entity_decode().
+ * Parameters
+ * $string
+ *   The input string.
+ * $flags
+ *  A bitmask of one or more of the flags (see comment on jx9Builtin_htmlspecialchars())
+ * Return
+ * The decoded string.
+ */
+static int jx9Builtin_html_entity_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zCur, *zIn, *zEnd;
+       int iFlags = 0x01; /* ENT_COMPAT  */
+       int nLen;
+       sxu32 n;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       /* Extract the flags if available */
+       if( nArg > 1 ){
+               iFlags = jx9_value_to_int(apArg[1]);
+               if( iFlags < 0 ){
+                       iFlags = 0x01;
+               }
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               zCur = zIn;
+               while( zIn < zEnd && zIn[0] != '&' ){
+                       zIn++;
+               }
+               if( zCur < zIn ){
+                       /* Append raw string verbatim */
+                       jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
+               }
+               if( zIn >= zEnd ){
+                       break;
+               }
+               nLen = (int)(zEnd-zIn);
+               /* Find an encoded sequence */
+               for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
+                       int iLen = (int)SyStrlen(azHtmlEscape[n]);
+                       if( nLen >= iLen && SyStrnicmp(zIn, azHtmlEscape[n], (sxu32)iLen) == 0 ){
+                               /* Got one */
+                               zIn += iLen;
+                               break;
+                       }
+               }
+               if( n < SX_ARRAYSIZE(azHtmlEscape) ){
+                       int c = azHtmlEscape[n+1][0];
+                       /* Output the decoded character */
+                       if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/)  ){
+                               /* Do not process single quotes */
+                               jx9_result_string(pCtx, azHtmlEscape[n], -1);
+                       }else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
+                               /* Do not process double quotes */
+                               jx9_result_string(pCtx, azHtmlEscape[n], -1);
+                       }else{
+                               jx9_result_string(pCtx, azHtmlEscape[n+1], -1); /* Compute length automatically */
+                       }
+               }else{
+                       /* Append '&' */
+                       jx9_result_string(pCtx, "&", (int)sizeof(char));
+                       zIn++;
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * int strlen($string)
+ *  return the length of the given string.
+ * Parameter
+ *  string: The string being measured for length.
+ * Return
+ *  length of the given string.
+ */
+static int jx9Builtin_strlen(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int iLen = 0;
+       if( nArg > 0 ){
+               jx9_value_to_string(apArg[0], &iLen);
+       }
+       /* String length */
+       jx9_result_int(pCtx, iLen);
+       return JX9_OK;
+}
+/*
+ * int strcmp(string $str1, string $str2)
+ *  Perform a binary safe string comparison.
+ * Parameter
+ *  str1: The first string
+ *  str2: The second string
+ * Return
+ *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater 
+ *  than str2, and 0 if they are equal.
+ */
+static int jx9Builtin_strcmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *z1, *z2;
+       int n1, n2;
+       int res;
+       if( nArg < 2 ){
+               res = nArg == 0 ? 0 : 1;
+               jx9_result_int(pCtx, res);
+               return JX9_OK;
+       }
+       /* Perform the comparison */
+       z1 = jx9_value_to_string(apArg[0], &n1);
+       z2 = jx9_value_to_string(apArg[1], &n2);
+       res = SyStrncmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
+       /* Comparison result */
+       jx9_result_int(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * int strncmp(string $str1, string $str2, int n)
+ *  Perform a binary safe string comparison of the first n characters.
+ * Parameter
+ *  str1: The first string
+ *  str2: The second string
+ * Return
+ *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater 
+ *  than str2, and 0 if they are equal.
+ */
+static int jx9Builtin_strncmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *z1, *z2;
+       int res;
+       int n;
+       if( nArg < 3 ){
+               /* Perform a standard comparison */
+               return jx9Builtin_strcmp(pCtx, nArg, apArg);
+       }
+       /* Desired comparison length */
+       n  = jx9_value_to_int(apArg[2]);
+       if( n < 0 ){
+               /* Invalid length */
+               jx9_result_int(pCtx, -1);
+               return JX9_OK;
+       }
+       /* Perform the comparison */
+       z1 = jx9_value_to_string(apArg[0], 0);
+       z2 = jx9_value_to_string(apArg[1], 0);
+       res = SyStrncmp(z1, z2, (sxu32)n);
+       /* Comparison result */
+       jx9_result_int(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * int strcasecmp(string $str1, string $str2, int n)
+ *  Perform a binary safe case-insensitive string comparison.
+ * Parameter
+ *  str1: The first string
+ *  str2: The second string
+ * Return
+ *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater 
+ *  than str2, and 0 if they are equal.
+ */
+static int jx9Builtin_strcasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *z1, *z2;
+       int n1, n2;
+       int res;
+       if( nArg < 2 ){
+               res = nArg == 0 ? 0 : 1;
+               jx9_result_int(pCtx, res);
+               return JX9_OK;
+       }
+       /* Perform the comparison */
+       z1 = jx9_value_to_string(apArg[0], &n1);
+       z2 = jx9_value_to_string(apArg[1], &n2);
+       res = SyStrnicmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
+       /* Comparison result */
+       jx9_result_int(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * int strncasecmp(string $str1, string $str2, int n)
+ *  Perform a binary safe case-insensitive string comparison of the first n characters.
+ * Parameter
+ *  $str1: The first string
+ *  $str2: The second string
+ *  $len:  The length of strings to be used in the comparison.
+ * Return
+ *  Returns < 0 if str1 is less than str2; > 0 if str1 is greater 
+ *  than str2, and 0 if they are equal.
+ */
+static int jx9Builtin_strncasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *z1, *z2;
+       int res;
+       int n;
+       if( nArg < 3 ){
+               /* Perform a standard comparison */
+               return jx9Builtin_strcasecmp(pCtx, nArg, apArg);
+       }
+       /* Desired comparison length */
+       n  = jx9_value_to_int(apArg[2]);
+       if( n < 0 ){
+               /* Invalid length */
+               jx9_result_int(pCtx, -1);
+               return JX9_OK;
+       }
+       /* Perform the comparison */
+       z1 = jx9_value_to_string(apArg[0], 0);
+       z2 = jx9_value_to_string(apArg[1], 0);
+       res = SyStrnicmp(z1, z2, (sxu32)n);
+       /* Comparison result */
+       jx9_result_int(pCtx, res);
+       return JX9_OK;
+}
+/*
+ * Implode context [i.e: it's private data].
+ * A pointer to the following structure is forwarded
+ * verbatim to the array walker callback defined below.
+ */
+struct implode_data {
+       jx9_context *pCtx;    /* Call context */
+       int bRecursive;       /* TRUE if recursive implode [this is a symisc eXtension] */
+       const char *zSep;     /* Arguments separator if any */
+       int nSeplen;          /* Separator length */
+       int bFirst;           /* TRUE if first call */
+       int nRecCount;        /* Recursion count to avoid infinite loop */
+};
+/*
+ * Implode walker callback for the [jx9_array_walk()] interface.
+ * The following routine is invoked for each array entry passed
+ * to the implode() function.
+ */
+static int implode_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
+{
+       struct implode_data *pData = (struct implode_data *)pUserData;
+       const char *zData;
+       int nLen;
+       if( pData->bRecursive && jx9_value_is_json_array(pValue) && pData->nRecCount < 32 ){
+               if( pData->nSeplen > 0 ){
+                       if( !pData->bFirst ){
+                               /* append the separator first */
+                               jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
+                       }else{
+                               pData->bFirst = 0;
+                       }
+               }
+               /* Recurse */
+               pData->bFirst = 1;
+               pData->nRecCount++;
+               jx9HashmapWalk((jx9_hashmap *)pValue->x.pOther, implode_callback, pData);
+               pData->nRecCount--;
+               return JX9_OK;
+       }
+       /* Extract the string representation of the entry value */
+       zData = jx9_value_to_string(pValue, &nLen);
+       if( nLen > 0 ){
+               if( pData->nSeplen > 0 ){
+                       if( !pData->bFirst ){
+                               /* append the separator first */
+                               jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
+                       }else{
+                               pData->bFirst = 0;
+                       }
+               }
+               jx9_result_string(pData->pCtx, zData, nLen);
+       }else{
+               SXUNUSED(pKey); /* cc warning */
+       }
+       return JX9_OK;
+}
+/*
+ * string implode(string $glue, array $pieces, ...)
+ * string implode(array $pieces, ...)
+ *  Join array elements with a string.
+ * $glue
+ *   Defaults to an empty string. This is not the preferred usage of implode() as glue
+ *   would be the second parameter and thus, the bad prototype would be used.
+ * $pieces
+ *   The array of strings to implode.
+ * Return
+ *  Returns a string containing a string representation of all the array elements in the same
+ *  order, with the glue string between each element. 
+ */
+static int jx9Builtin_implode(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       struct implode_data imp_data;
+       int i = 1;
+       if( nArg < 1 ){
+               /* Missing argument, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Prepare the implode context */
+       imp_data.pCtx = pCtx;
+       imp_data.bRecursive = 0;
+       imp_data.bFirst = 1;
+       imp_data.nRecCount = 0;
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
+       }else{
+               imp_data.zSep = 0;
+               imp_data.nSeplen = 0;
+               i = 0;
+       }
+       jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
+       /* Start the 'join' process */
+       while( i < nArg ){
+               if( jx9_value_is_json_array(apArg[i]) ){
+                       /* Iterate throw array entries */
+                       jx9_array_walk(apArg[i], implode_callback, &imp_data);
+               }else{
+                       const char *zData;
+                       int nLen;
+                       /* Extract the string representation of the jx9 value */
+                       zData = jx9_value_to_string(apArg[i], &nLen);
+                       if( nLen > 0 ){
+                               if( imp_data.nSeplen > 0 ){
+                                       if( !imp_data.bFirst ){
+                                               /* append the separator first */
+                                               jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
+                                       }else{
+                                               imp_data.bFirst = 0;
+                                       }
+                               }
+                               jx9_result_string(pCtx, zData, nLen);
+                       }
+               }
+               i++;
+       }
+       return JX9_OK;
+}
+/*
+ * string implode_recursive(string $glue, array $pieces, ...)
+ * Purpose
+ *  Same as implode() but recurse on arrays.
+ * Example:
+ *   $a = array('usr', array('home', 'dean'));
+ *   print implode_recursive("/", $a);
+ *   Will output
+ *     usr/home/dean.
+ *   While the standard implode would produce.
+ *    usr/Array.
+ * Parameter
+ *  Refer to implode().
+ * Return
+ *  Refer to implode().
+ */
+static int jx9Builtin_implode_recursive(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       struct implode_data imp_data;
+       int i = 1;
+       if( nArg < 1 ){
+               /* Missing argument, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Prepare the implode context */
+       imp_data.pCtx = pCtx;
+       imp_data.bRecursive = 1;
+       imp_data.bFirst = 1;
+       imp_data.nRecCount = 0;
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
+       }else{
+               imp_data.zSep = 0;
+               imp_data.nSeplen = 0;
+               i = 0;
+       }
+       jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
+       /* Start the 'join' process */
+       while( i < nArg ){
+               if( jx9_value_is_json_array(apArg[i]) ){
+                       /* Iterate throw array entries */
+                       jx9_array_walk(apArg[i], implode_callback, &imp_data);
+               }else{
+                       const char *zData;
+                       int nLen;
+                       /* Extract the string representation of the jx9 value */
+                       zData = jx9_value_to_string(apArg[i], &nLen);
+                       if( nLen > 0 ){
+                               if( imp_data.nSeplen > 0 ){
+                                       if( !imp_data.bFirst ){
+                                               /* append the separator first */
+                                               jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
+                                       }else{
+                                               imp_data.bFirst = 0;
+                                       }
+                               }
+                               jx9_result_string(pCtx, zData, nLen);
+                       }
+               }
+               i++;
+       }
+       return JX9_OK;
+}
+/*
+ * array explode(string $delimiter, string $string[, int $limit ])
+ *  Returns an array of strings, each of which is a substring of string 
+ *  formed by splitting it on boundaries formed by the string delimiter. 
+ * Parameters
+ *  $delimiter
+ *   The boundary string.
+ * $string
+ *   The input string.
+ * $limit
+ *   If limit is set and positive, the returned array will contain a maximum
+ *   of limit elements with the last element containing the rest of string.
+ *   If the limit parameter is negative, all fields except the last -limit are returned.
+ *   If the limit parameter is zero, then this is treated as 1.
+ * Returns
+ *  Returns an array of strings created by splitting the string parameter
+ *  on boundaries formed by the delimiter.
+ *  If delimiter is an empty string (""), explode() will return FALSE. 
+ *  If delimiter contains a value that is not contained in string and a negative
+ *  limit is used, then an empty array will be returned, otherwise an array containing string
+ *  will be returned. 
+ * NOTE:
+ *  Negative limit is not supported.
+ */
+static int jx9Builtin_explode(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zDelim, *zString, *zCur, *zEnd;
+       int nDelim, nStrlen, iLimit;
+       jx9_value *pArray;
+       jx9_value *pValue;
+       sxu32 nOfft;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the delimiter */
+       zDelim = jx9_value_to_string(apArg[0], &nDelim);
+       if( nDelim < 1 ){
+               /* Empty delimiter, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the string */
+       zString = jx9_value_to_string(apArg[1], &nStrlen);
+       if( nStrlen < 1 ){
+               /* Empty delimiter, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the end of the string */
+       zEnd = &zString[nStrlen];
+       /* Create the array */
+       pArray =  jx9_context_new_array(pCtx);
+       pValue = jx9_context_new_scalar(pCtx);
+       if( pArray == 0 || pValue == 0 ){
+               /* Out of memory, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Set a defualt limit */
+       iLimit = SXI32_HIGH;
+       if( nArg > 2 ){
+               iLimit = jx9_value_to_int(apArg[2]);
+                if( iLimit < 0 ){
+                       iLimit = -iLimit;
+               }
+               if( iLimit == 0 ){
+                       iLimit = 1;
+               }
+               iLimit--;
+       }
+       /* Start exploding */
+       for(;;){
+               if( zString >= zEnd ){
+                       /* No more entry to process */
+                       break;
+               }
+               rc = SyBlobSearch(zString, (sxu32)(zEnd-zString), zDelim, nDelim, &nOfft);
+               if( rc != SXRET_OK || iLimit <= (int)jx9_array_count(pArray) ){
+                       /* Limit reached, insert the rest of the string and break */
+                       if( zEnd > zString ){
+                               jx9_value_string(pValue, zString, (int)(zEnd-zString));
+                               jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
+                       }
+                       break;
+               }
+               /* Point to the desired offset */
+               zCur = &zString[nOfft];
+               if( zCur > zString ){
+                       /* Perform the store operation */
+                       jx9_value_string(pValue, zString, (int)(zCur-zString));
+                       jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
+               }
+               /* Point beyond the delimiter */
+               zString = &zCur[nDelim];
+               /* Reset the cursor */
+               jx9_value_reset_string_cursor(pValue);
+       }
+       /* Return the freshly created array */
+       jx9_result_value(pCtx, pArray);
+       /* NOTE that every allocated jx9_value will be automatically 
+        * released as soon we return from this foregin function.
+        */
+       return JX9_OK;
+}
+/*
+ * string trim(string $str[, string $charlist ])
+ *  Strip whitespace (or other characters) from the beginning and end of a string.
+ * Parameters
+ *  $str
+ *   The string that will be trimmed.
+ * $charlist
+ *   Optionally, the stripped characters can also be specified using the charlist parameter.
+ *   Simply list all characters that you want to be stripped.
+ *   With .. you can specify a range of characters.
+ * Returns.
+ *  Thr processed string.
+ */
+static int jx9Builtin_trim(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string, return */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Start the trim process */
+       if( nArg < 2 ){
+               SyString sStr;
+               /* Remove white spaces and NUL bytes */
+               SyStringInitFromBuf(&sStr, zString, nLen);
+               SyStringFullTrimSafe(&sStr);
+               jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
+       }else{
+               /* Char list */
+               const char *zList;
+               int nListlen;
+               zList = jx9_value_to_string(apArg[1], &nListlen);
+               if( nListlen < 1 ){
+                       /* Return the string unchanged */
+                       jx9_result_string(pCtx, zString, nLen);
+               }else{
+                       const char *zEnd = &zString[nLen];
+                       const char *zCur = zString;
+                       const char *zPtr;
+                       int i;
+                       /* Left trim */
+                       for(;;){
+                               if( zCur >= zEnd ){
+                                       break;
+                               }
+                               zPtr = zCur;
+                               for( i = 0 ; i < nListlen ; i++ ){
+                                       if( zCur < zEnd && zCur[0] == zList[i] ){
+                                               zCur++;
+                                       }
+                               }
+                               if( zCur == zPtr ){
+                                       /* No match, break immediately */
+                                       break;
+                               }
+                       }
+                       /* Right trim */
+                       zEnd--;
+                       for(;;){
+                               if( zEnd <= zCur ){
+                                       break;
+                               }
+                               zPtr = zEnd;
+                               for( i = 0 ; i < nListlen ; i++ ){
+                                       if( zEnd > zCur && zEnd[0] == zList[i] ){
+                                               zEnd--;
+                                       }
+                               }
+                               if( zEnd == zPtr ){
+                                       break;
+                               }
+                       }
+                       if( zCur >= zEnd ){
+                               /* Return the empty string */
+                               jx9_result_string(pCtx, "", 0);
+                       }else{
+                               zEnd++;
+                               jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
+                       }
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * string rtrim(string $str[, string $charlist ])
+ *  Strip whitespace (or other characters) from the end of a string.
+ * Parameters
+ *  $str
+ *   The string that will be trimmed.
+ * $charlist
+ *   Optionally, the stripped characters can also be specified using the charlist parameter.
+ *   Simply list all characters that you want to be stripped.
+ *   With .. you can specify a range of characters.
+ * Returns.
+ *  Thr processed string.
+ */
+static int jx9Builtin_rtrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string, return */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Start the trim process */
+       if( nArg < 2 ){
+               SyString sStr;
+               /* Remove white spaces and NUL bytes*/
+               SyStringInitFromBuf(&sStr, zString, nLen);
+               SyStringRightTrimSafe(&sStr);
+               jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
+       }else{
+               /* Char list */
+               const char *zList;
+               int nListlen;
+               zList = jx9_value_to_string(apArg[1], &nListlen);
+               if( nListlen < 1 ){
+                       /* Return the string unchanged */
+                       jx9_result_string(pCtx, zString, nLen);
+               }else{
+                       const char *zEnd = &zString[nLen - 1];
+                       const char *zCur = zString;
+                       const char *zPtr;
+                       int i;
+                       /* Right trim */
+                       for(;;){
+                               if( zEnd <= zCur ){
+                                       break;
+                               }
+                               zPtr = zEnd;
+                               for( i = 0 ; i < nListlen ; i++ ){
+                                       if( zEnd > zCur && zEnd[0] == zList[i] ){
+                                               zEnd--;
+                                       }
+                               }
+                               if( zEnd == zPtr ){
+                                       break;
+                               }
+                       }
+                       if( zEnd <= zCur ){
+                               /* Return the empty string */
+                               jx9_result_string(pCtx, "", 0);
+                       }else{
+                               zEnd++;
+                               jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
+                       }
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * string ltrim(string $str[, string $charlist ])
+ *  Strip whitespace (or other characters) from the beginning and end of a string.
+ * Parameters
+ *  $str
+ *   The string that will be trimmed.
+ * $charlist
+ *   Optionally, the stripped characters can also be specified using the charlist parameter.
+ *   Simply list all characters that you want to be stripped.
+ *   With .. you can specify a range of characters.
+ * Returns.
+ *  The processed string.
+ */
+static int jx9Builtin_ltrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string, return */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Start the trim process */
+       if( nArg < 2 ){
+               SyString sStr;
+               /* Remove white spaces and NUL byte */
+               SyStringInitFromBuf(&sStr, zString, nLen);
+               SyStringLeftTrimSafe(&sStr);
+               jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
+       }else{
+               /* Char list */
+               const char *zList;
+               int nListlen;
+               zList = jx9_value_to_string(apArg[1], &nListlen);
+               if( nListlen < 1 ){
+                       /* Return the string unchanged */
+                       jx9_result_string(pCtx, zString, nLen);
+               }else{
+                       const char *zEnd = &zString[nLen];
+                       const char *zCur = zString;
+                       const char *zPtr;
+                       int i;
+                       /* Left trim */
+                       for(;;){
+                               if( zCur >= zEnd ){
+                                       break;
+                               }
+                               zPtr = zCur;
+                               for( i = 0 ; i < nListlen ; i++ ){
+                                       if( zCur < zEnd && zCur[0] == zList[i] ){
+                                               zCur++;
+                                       }
+                               }
+                               if( zCur == zPtr ){
+                                       /* No match, break immediately */
+                                       break;
+                               }
+                       }
+                       if( zCur >= zEnd ){
+                               /* Return the empty string */
+                               jx9_result_string(pCtx, "", 0);
+                       }else{
+                               jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
+                       }
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * string strtolower(string $str)
+ *  Make a string lowercase.
+ * Parameters
+ *  $str
+ *   The input string.
+ * Returns.
+ *  The lowercased string.
+ */
+static int jx9Builtin_strtolower(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString, *zCur, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string, return */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       zEnd = &zString[nLen];
+       for(;;){
+               if( zString >= zEnd ){
+                       /* No more input, break immediately */
+                       break;
+               }
+               if( (unsigned char)zString[0] >= 0xc0 ){
+                       /* UTF-8 stream, output verbatim */
+                       zCur = zString;
+                       zString++;
+                       while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
+                               zString++;
+                       }
+                       /* Append UTF-8 stream */
+                       jx9_result_string(pCtx, zCur, (int)(zString-zCur));
+               }else{
+                       int c = zString[0];
+                       if( SyisUpper(c) ){
+                               c = SyToLower(zString[0]);
+                       }
+                       /* Append character */
+                       jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
+                       /* Advance the cursor */
+                       zString++;
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * string strtolower(string $str)
+ *  Make a string uppercase.
+ * Parameters
+ *  $str
+ *   The input string.
+ * Returns.
+ *  The uppercased string.
+ */
+static int jx9Builtin_strtoupper(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString, *zCur, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string, return */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       zEnd = &zString[nLen];
+       for(;;){
+               if( zString >= zEnd ){
+                       /* No more input, break immediately */
+                       break;
+               }
+               if( (unsigned char)zString[0] >= 0xc0 ){
+                       /* UTF-8 stream, output verbatim */
+                       zCur = zString;
+                       zString++;
+                       while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
+                               zString++;
+                       }
+                       /* Append UTF-8 stream */
+                       jx9_result_string(pCtx, zCur, (int)(zString-zCur));
+               }else{
+                       int c = zString[0];
+                       if( SyisLower(c) ){
+                               c = SyToUpper(zString[0]);
+                       }
+                       /* Append character */
+                       jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
+                       /* Advance the cursor */
+                       zString++;
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * int ord(string $string)
+ *  Returns the ASCII value of the first character of string.
+ * Parameters
+ *  $str
+ *   The input string.
+ * Returns.
+ *  The ASCII value as an integer.
+ */
+static int jx9Builtin_ord(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString;
+       int nLen, c;
+       if( nArg < 1 ){
+               /* Missing arguments, return -1 */
+               jx9_result_int(pCtx, -1);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string, return -1 */
+               jx9_result_int(pCtx, -1);
+               return JX9_OK;
+       }
+       /* Extract the ASCII value of the first character */
+       c = zString[0];
+       /* Return that value */
+       jx9_result_int(pCtx, c);
+       return JX9_OK;
+}
+/*
+ * string chr(int $ascii)
+ *  Returns a one-character string containing the character specified by ascii.
+ * Parameters
+ *  $ascii
+ *   The ascii code.
+ * Returns.
+ *  The specified character.
+ */
+static int jx9Builtin_chr(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int c;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the ASCII value */
+       c = jx9_value_to_int(apArg[0]);
+       /* Return the specified character */
+       jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
+       return JX9_OK;
+}
+/*
+ * Binary to hex consumer callback.
+ * This callback is the default consumer used by the hash functions
+ * [i.e: bin2hex(), md5(), sha1(), md5_file() ... ] defined below.
+ */
+static int HashConsumer(const void *pData, unsigned int nLen, void *pUserData)
+{
+       /* Append hex chunk verbatim */
+       jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
+       return SXRET_OK;
+}
+/*
+ * string bin2hex(string $str)
+ *  Convert binary data into hexadecimal representation.
+ * Parameters
+ *  $str
+ *   The input string.
+ * Returns.
+ *  Returns the hexadecimal representation of the given string.
+ */
+static int jx9Builtin_bin2hex(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string, return */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       SyBinToHexConsumer((const void *)zString, (sxu32)nLen, HashConsumer, pCtx);
+       return JX9_OK;
+}
+/* Search callback signature */
+typedef sxi32 (*ProcStringMatch)(const void *, sxu32, const void *, sxu32, sxu32 *);
+/*
+ * Case-insensitive pattern match.
+ * Brute force is the default search method used here.
+ * This is due to the fact that brute-forcing works quite
+ * well for short/medium texts on modern hardware.
+ */
+static sxi32 iPatternMatch(const void *pText, sxu32 nLen, const void *pPattern, sxu32 iPatLen, sxu32 *pOfft)
+{
+       const char *zpIn = (const char *)pPattern;
+       const char *zIn = (const char *)pText;
+       const char *zpEnd = &zpIn[iPatLen];
+       const char *zEnd = &zIn[nLen];
+       const char *zPtr, *zPtr2;
+       int c, d;
+       if( iPatLen > nLen ){
+               /* Don't bother processing */
+               return SXERR_NOTFOUND;
+       }
+       for(;;){
+               if( zIn >= zEnd ){
+                       break;
+               }
+               c = SyToLower(zIn[0]);
+               d = SyToLower(zpIn[0]);
+               if( c == d ){
+                       zPtr   = &zIn[1];
+                       zPtr2  = &zpIn[1];
+                       for(;;){
+                               if( zPtr2 >= zpEnd ){
+                                       /* Pattern found */
+                                       if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); }
+                                       return SXRET_OK;
+                               }
+                               if( zPtr >= zEnd ){
+                                       break;
+                               }
+                               c = SyToLower(zPtr[0]);
+                               d = SyToLower(zPtr2[0]);
+                               if( c != d ){
+                                       break;
+                               }
+                               zPtr++; zPtr2++;
+                       }
+               }
+               zIn++;
+       }
+       /* Pattern not found */
+       return SXERR_NOTFOUND;
+}
+/*
+ * string strstr(string $haystack, string $needle[, bool $before_needle = false ])
+ *  Find the first occurrence of a string.
+ * Parameters
+ *  $haystack
+ *   The input string.
+ * $needle
+ *   Search pattern (must be a string).
+ * $before_needle
+ *   If TRUE, strstr() returns the part of the haystack before the first occurrence 
+ *   of the needle (excluding the needle).
+ * Return
+ *  Returns the portion of string, or FALSE if needle is not found.
+ */
+static int jx9Builtin_strstr(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
+       const char *zBlob, *zPattern;
+       int nLen, nPatLen;
+       sxu32 nOfft;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the needle and the haystack */
+       zBlob = jx9_value_to_string(apArg[0], &nLen);
+       zPattern = jx9_value_to_string(apArg[1], &nPatLen);
+       nOfft = 0; /* cc warning */
+       if( nLen > 0 && nPatLen > 0 ){
+               int before = 0;
+               /* Perform the lookup */
+               rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
+               if( rc != SXRET_OK ){
+                       /* Pattern not found, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Return the portion of the string */
+               if( nArg > 2 ){
+                       before = jx9_value_to_int(apArg[2]);
+               }
+               if( before ){
+                       jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
+               }else{
+                       jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
+               }
+       }else{
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * string stristr(string $haystack, string $needle[, bool $before_needle = false ])
+ *  Case-insensitive strstr().
+ * Parameters
+ *  $haystack
+ *   The input string.
+ * $needle
+ *   Search pattern (must be a string).
+ * $before_needle
+ *   If TRUE, strstr() returns the part of the haystack before the first occurrence 
+ *   of the needle (excluding the needle).
+ * Return
+ *  Returns the portion of string, or FALSE if needle is not found.
+ */
+static int jx9Builtin_stristr(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
+       const char *zBlob, *zPattern;
+       int nLen, nPatLen;
+       sxu32 nOfft;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the needle and the haystack */
+       zBlob = jx9_value_to_string(apArg[0], &nLen);
+       zPattern = jx9_value_to_string(apArg[1], &nPatLen);
+       nOfft = 0; /* cc warning */
+       if( nLen > 0 && nPatLen > 0 ){
+               int before = 0;
+               /* Perform the lookup */
+               rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
+               if( rc != SXRET_OK ){
+                       /* Pattern not found, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Return the portion of the string */
+               if( nArg > 2 ){
+                       before = jx9_value_to_int(apArg[2]);
+               }
+               if( before ){
+                       jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
+               }else{
+                       jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
+               }
+       }else{
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * int strpos(string $haystack, string $needle [, int $offset = 0 ] )
+ *  Returns the numeric position of the first occurrence of needle in the haystack string.
+ * Parameters
+ *  $haystack
+ *   The input string.
+ * $needle
+ *   Search pattern (must be a string).
+ * $offset
+ *   This optional offset parameter allows you to specify which character in haystack
+ *   to start searching. The position returned is still relative to the beginning
+ *   of haystack.
+ * Return
+ *  Returns the position as an integer.If needle is not found, strpos() will return FALSE.
+ */
+static int jx9Builtin_strpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
+       const char *zBlob, *zPattern;
+       int nLen, nPatLen, nStart;
+       sxu32 nOfft;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the needle and the haystack */
+       zBlob = jx9_value_to_string(apArg[0], &nLen);
+       zPattern = jx9_value_to_string(apArg[1], &nPatLen);
+       nOfft = 0; /* cc warning */
+       nStart = 0;
+       /* Peek the starting offset if available */
+       if( nArg > 2 ){
+               nStart = jx9_value_to_int(apArg[2]);
+               if( nStart < 0 ){
+                       nStart = -nStart;
+               }
+               if( nStart >= nLen ){
+                       /* Invalid offset */
+                       nStart = 0;
+               }else{
+                       zBlob += nStart;
+                       nLen -= nStart;
+               }
+       }
+       if( nLen > 0 && nPatLen > 0 ){
+               /* Perform the lookup */
+               rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
+               if( rc != SXRET_OK ){
+                       /* Pattern not found, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Return the pattern position */
+               jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
+       }else{
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * int stripos(string $haystack, string $needle [, int $offset = 0 ] )
+ *  Case-insensitive strpos.
+ * Parameters
+ *  $haystack
+ *   The input string.
+ * $needle
+ *   Search pattern (must be a string).
+ * $offset
+ *   This optional offset parameter allows you to specify which character in haystack
+ *   to start searching. The position returned is still relative to the beginning
+ *   of haystack.
+ * Return
+ *  Returns the position as an integer.If needle is not found, strpos() will return FALSE.
+ */
+static int jx9Builtin_stripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
+       const char *zBlob, *zPattern;
+       int nLen, nPatLen, nStart;
+       sxu32 nOfft;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the needle and the haystack */
+       zBlob = jx9_value_to_string(apArg[0], &nLen);
+       zPattern = jx9_value_to_string(apArg[1], &nPatLen);
+       nOfft = 0; /* cc warning */
+       nStart = 0;
+       /* Peek the starting offset if available */
+       if( nArg > 2 ){
+               nStart = jx9_value_to_int(apArg[2]);
+               if( nStart < 0 ){
+                       nStart = -nStart;
+               }
+               if( nStart >= nLen ){
+                       /* Invalid offset */
+                       nStart = 0;
+               }else{
+                       zBlob += nStart;
+                       nLen -= nStart;
+               }
+       }
+       if( nLen > 0 && nPatLen > 0 ){
+               /* Perform the lookup */
+               rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
+               if( rc != SXRET_OK ){
+                       /* Pattern not found, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Return the pattern position */
+               jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
+       }else{
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * int strrpos(string $haystack, string $needle [, int $offset = 0 ] )
+ *  Find the numeric position of the last occurrence of needle in the haystack string.
+ * Parameters
+ *  $haystack
+ *   The input string.
+ * $needle
+ *   Search pattern (must be a string).
+ * $offset
+ *   If specified, search will start this number of characters counted from the beginning
+ *   of the string. If the value is negative, search will instead start from that many 
+ *   characters from the end of the string, searching backwards.
+ * Return
+ *  Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
+ */
+static int jx9Builtin_strrpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
+       ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
+       int nLen, nPatLen;
+       sxu32 nOfft;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the needle and the haystack */
+       zBlob = jx9_value_to_string(apArg[0], &nLen);
+       zPattern = jx9_value_to_string(apArg[1], &nPatLen);
+       /* Point to the end of the pattern */
+       zPtr = &zBlob[nLen - 1];
+       zEnd = &zBlob[nLen];
+       /* Save the starting posistion */
+       zStart = zBlob;
+       nOfft = 0; /* cc warning */
+       /* Peek the starting offset if available */
+       if( nArg > 2 ){
+               int nStart;
+               nStart = jx9_value_to_int(apArg[2]);
+               if( nStart < 0 ){
+                       nStart = -nStart;
+                       if( nStart >= nLen ){
+                               /* Invalid offset */
+                               jx9_result_bool(pCtx, 0);
+                               return JX9_OK;
+                       }else{
+                               nLen -= nStart;
+                               zPtr = &zBlob[nLen - 1];
+                               zEnd = &zBlob[nLen];
+                       }
+               }else{
+                       if( nStart >= nLen ){
+                               /* Invalid offset */
+                               jx9_result_bool(pCtx, 0);
+                               return JX9_OK;
+                       }else{
+                               zBlob += nStart;
+                               nLen -= nStart;
+                       }
+               }
+       }
+       if( nLen > 0 && nPatLen > 0 ){
+               /* Perform the lookup */
+               for(;;){
+                       if( zBlob >= zPtr ){
+                               break;
+                       }
+                       rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
+                       if( rc == SXRET_OK ){
+                               /* Pattern found, return it's position */
+                               jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
+                               return JX9_OK;
+                       }
+                       zPtr--;
+               }
+               /* Pattern not found, return FALSE */
+               jx9_result_bool(pCtx, 0);
+       }else{
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * int strripos(string $haystack, string $needle [, int $offset = 0 ] )
+ *  Case-insensitive strrpos.
+ * Parameters
+ *  $haystack
+ *   The input string.
+ * $needle
+ *   Search pattern (must be a string).
+ * $offset
+ *   If specified, search will start this number of characters counted from the beginning
+ *   of the string. If the value is negative, search will instead start from that many 
+ *   characters from the end of the string, searching backwards.
+ * Return
+ *  Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
+ */
+static int jx9Builtin_strripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
+       ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
+       int nLen, nPatLen;
+       sxu32 nOfft;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the needle and the haystack */
+       zBlob = jx9_value_to_string(apArg[0], &nLen);
+       zPattern = jx9_value_to_string(apArg[1], &nPatLen);
+       /* Point to the end of the pattern */
+       zPtr = &zBlob[nLen - 1];
+       zEnd = &zBlob[nLen];
+       /* Save the starting posistion */
+       zStart = zBlob;
+       nOfft = 0; /* cc warning */
+       /* Peek the starting offset if available */
+       if( nArg > 2 ){
+               int nStart;
+               nStart = jx9_value_to_int(apArg[2]);
+               if( nStart < 0 ){
+                       nStart = -nStart;
+                       if( nStart >= nLen ){
+                               /* Invalid offset */
+                               jx9_result_bool(pCtx, 0);
+                               return JX9_OK;
+                       }else{
+                               nLen -= nStart;
+                               zPtr = &zBlob[nLen - 1];
+                               zEnd = &zBlob[nLen];
+                       }
+               }else{
+                       if( nStart >= nLen ){
+                               /* Invalid offset */
+                               jx9_result_bool(pCtx, 0);
+                               return JX9_OK;
+                       }else{
+                               zBlob += nStart;
+                               nLen -= nStart;
+                       }
+               }
+       }
+       if( nLen > 0 && nPatLen > 0 ){
+               /* Perform the lookup */
+               for(;;){
+                       if( zBlob >= zPtr ){
+                               break;
+                       }
+                       rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
+                       if( rc == SXRET_OK ){
+                               /* Pattern found, return it's position */
+                               jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
+                               return JX9_OK;
+                       }
+                       zPtr--;
+               }
+               /* Pattern not found, return FALSE */
+               jx9_result_bool(pCtx, 0);
+       }else{
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * int strrchr(string $haystack, mixed $needle)
+ *  Find the last occurrence of a character in a string.
+ * Parameters
+ *  $haystack
+ *   The input string.
+ * $needle
+ *  If needle contains more than one character, only the first is used.
+ *  This behavior is different from that of strstr().
+ *  If needle is not a string, it is converted to an integer and applied
+ *  as the ordinal value of a character.
+ * Return
+ *  This function returns the portion of string, or FALSE if needle is not found.
+ */
+static int jx9Builtin_strrchr(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zBlob;
+       int nLen, c;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the haystack */
+       zBlob = jx9_value_to_string(apArg[0], &nLen);
+       c = 0; /* cc warning */
+       if( nLen > 0 ){
+               sxu32 nOfft;
+               sxi32 rc;
+               if( jx9_value_is_string(apArg[1]) ){
+                       const char *zPattern;
+                       zPattern = jx9_value_to_string(apArg[1], 0); /* Never fail, so there is no need to check
+                                                                                                                * for NULL pointer.
+                                                                                                                */
+                       c = zPattern[0];
+               }else{
+                       /* Int cast */
+                       c = jx9_value_to_int(apArg[1]);
+               }
+               /* Perform the lookup */
+               rc = SyByteFind2(zBlob, (sxu32)nLen, c, &nOfft);
+               if( rc != SXRET_OK ){
+                       /* No such entry, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Return the string portion */
+               jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
+       }else{
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * string strrev(string $string)
+ *  Reverse a string.
+ * Parameters
+ *  $string
+ *   String to be reversed.
+ * Return
+ *  The reversed string.
+ */
+static int jx9Builtin_strrev(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn, *zEnd;
+       int nLen, c;
+       if( nArg < 1 ){
+               /* Missing arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string Return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       zEnd = &zIn[nLen - 1];
+       for(;;){
+               if( zEnd < zIn ){
+                       /* No more input to process */
+                       break;
+               }
+               /* Append current character */
+               c = zEnd[0];
+               jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
+               zEnd--;
+       }
+       return JX9_OK;
+}
+/*
+ * string str_repeat(string $input, int $multiplier)
+ *  Returns input repeated multiplier times.
+ * Parameters
+ *  $string
+ *   String to be repeated.
+ * $multiplier
+ *  Number of time the input string should be repeated.
+ *  multiplier has to be greater than or equal to 0. If the multiplier is set
+ *  to 0, the function will return an empty string.
+ * Return
+ *  The repeated string.
+ */
+static int jx9Builtin_str_repeat(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn;
+       int nLen, nMul;
+       int rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string.Return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the multiplier */
+       nMul = jx9_value_to_int(apArg[1]);
+       if( nMul < 1 ){
+               /* Return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( nMul < 1 ){
+                       break;
+               }
+               /* Append the copy */
+               rc = jx9_result_string(pCtx, zIn, nLen);
+               if( rc != JX9_OK ){
+                       /* Out of memory, break immediately */
+                       break;
+               }
+               nMul--;
+       }
+       return JX9_OK;
+}
+/*
+ * string nl2br(string $string[, bool $is_xhtml = true ])
+ *  Inserts HTML line breaks before all newlines in a string.
+ * Parameters
+ *  $string
+ *   The input string.
+ * $is_xhtml
+ *   Whenever to use XHTML compatible line breaks or not.
+ * Return
+ *  The processed string.
+ */
+static int jx9Builtin_nl2br(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn, *zCur, *zEnd;
+       int is_xhtml = 0;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       if( nArg > 1 ){
+               is_xhtml = jx9_value_to_bool(apArg[1]);
+       }
+       zEnd = &zIn[nLen];
+       /* Perform the requested operation */
+       for(;;){
+               zCur = zIn;
+               /* Delimit the string */
+               while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){
+                       zIn++;
+               }
+               if( zCur < zIn ){
+                       /* Output chunk verbatim */
+                       jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
+               }
+               if( zIn >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               /* Output the HTML line break */
+               if( is_xhtml ){
+                       jx9_result_string(pCtx, "<br>", (int)sizeof("<br>")-1);
+               }else{
+                       jx9_result_string(pCtx, "<br/>", (int)sizeof("<br/>")-1);
+               }
+               zCur = zIn;
+               /* Append trailing line */
+               while( zIn < zEnd && (zIn[0] == '\n'  || zIn[0] == '\r') ){
+                       zIn++;
+               }
+               if( zCur < zIn ){
+                       /* Output chunk verbatim */
+                       jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * Format a given string and invoke the given callback on each processed chunk.
+ *  According to the JX9 reference manual.
+ * The format string is composed of zero or more directives: ordinary characters
+ * (excluding %) that are copied directly to the result, and conversion 
+ * specifications, each of which results in fetching its own parameter.
+ * This applies to both sprintf() and printf().
+ * Each conversion specification consists of a percent sign (%), followed by one
+ * or more of these elements, in order:
+ *   An optional sign specifier that forces a sign (- or +) to be used on a number.
+ *   By default, only the - sign is used on a number if it's negative. This specifier forces
+ *   positive numbers to have the + sign attached as well.
+ *   An optional padding specifier that says what character will be used for padding
+ *   the results to the right string size. This may be a space character or a 0 (zero character).
+ *   The default is to pad with spaces. An alternate padding character can be specified by prefixing
+ *   it with a single quote ('). See the examples below.
+ *   An optional alignment specifier that says if the result should be left-justified or right-justified.
+ *   The default is right-justified; a - character here will make it left-justified.
+ *   An optional number, a width specifier that says how many characters (minimum) this conversion
+ *   should result in.
+ *   An optional precision specifier in the form of a period (`.') followed by an optional decimal
+ *   digit string that says how many decimal digits should be displayed for floating-point numbers.
+ *   When using this specifier on a string, it acts as a cutoff point, setting a maximum character
+ *   limit to the string.
+ *  A type specifier that says what type the argument data should be treated as. Possible types:
+ *       % - a literal percent character. No argument is required.
+ *       b - the argument is treated as an integer, and presented as a binary number.
+ *       c - the argument is treated as an integer, and presented as the character with that ASCII value.
+ *       d - the argument is treated as an integer, and presented as a (signed) decimal number.
+ *       e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands 
+ *          for the number of digits after the decimal point.
+ *       E - like %e but uses uppercase letter (e.g. 1.2E+2).
+ *       u - the argument is treated as an integer, and presented as an unsigned decimal number.
+ *       f - the argument is treated as a float, and presented as a floating-point number (locale aware).
+ *       F - the argument is treated as a float, and presented as a floating-point number (non-locale aware).
+ *       g - shorter of %e and %f.
+ *       G - shorter of %E and %f.
+ *       o - the argument is treated as an integer, and presented as an octal number.
+ *       s - the argument is treated as and presented as a string.
+ *       x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters).
+ *       X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters).
+ */
+/*
+ * This implementation is based on the one found in the SQLite3 source tree.
+ */
+#define JX9_FMT_BUFSIZ 1024 /* Conversion buffer size */
+/*
+** Conversion types fall into various categories as defined by the
+** following enumeration.
+*/
+#define JX9_FMT_RADIX       1 /* Integer types.%d, %x, %o, and so forth */
+#define JX9_FMT_FLOAT       2 /* Floating point.%f */
+#define JX9_FMT_EXP         3 /* Exponentional notation.%e and %E */
+#define JX9_FMT_GENERIC     4 /* Floating or exponential, depending on exponent.%g */
+#define JX9_FMT_SIZE        5 /* Total number of characters processed so far.%n */
+#define JX9_FMT_STRING      6 /* Strings.%s */
+#define JX9_FMT_PERCENT     7 /* Percent symbol.%% */
+#define JX9_FMT_CHARX       8 /* Characters.%c */
+#define JX9_FMT_ERROR       9 /* Used to indicate no such conversion type */
+/*
+** Allowed values for jx9_fmt_info.flags
+*/
+#define JX9_FMT_FLAG_SIGNED      0x01
+#define JX9_FMT_FLAG_UNSIGNED 0x02
+/*
+** Each builtin conversion character (ex: the 'd' in "%d") is described
+** by an instance of the following structure
+*/
+typedef struct jx9_fmt_info jx9_fmt_info;
+struct jx9_fmt_info
+{
+  char fmttype;  /* The format field code letter [i.e: 'd', 's', 'x'] */
+  sxu8 base;     /* The base for radix conversion */
+  int flags;    /* One or more of JX9_FMT_FLAG_ constants below */
+  sxu8 type;     /* Conversion paradigm */
+  char *charset; /* The character set for conversion */
+  char *prefix;  /* Prefix on non-zero values in alt format */
+};
+#ifndef JX9_OMIT_FLOATING_POINT
+/*
+** "*val" is a double such that 0.1 <= *val < 10.0
+** Return the ascii code for the leading digit of *val, then
+** multiply "*val" by 10.0 to renormalize.
+**
+** Example:
+**     input:     *val = 3.14159
+**     output:    *val = 1.4159    function return = '3'
+**
+** The counter *cnt is incremented each time.  After counter exceeds
+** 16 (the number of significant digits in a 64-bit float) '0' is
+** always returned.
+*/
+static int vxGetdigit(sxlongreal *val, int *cnt)
+{
+  sxlongreal d;
+  int digit;
+
+  if( (*cnt)++ >= 16 ){
+         return '0';
+  }
+  digit = (int)*val;
+  d = digit;
+   *val = (*val - d)*10.0;
+  return digit + '0' ;
+}
+#endif /* JX9_OMIT_FLOATING_POINT */
+/*
+ * The following table is searched linearly, so it is good to put the most frequently
+ * used conversion types first.
+ */
+static const jx9_fmt_info aFmt[] = {
+  {  'd', 10, JX9_FMT_FLAG_SIGNED, JX9_FMT_RADIX, "0123456789", 0    }, 
+  {  's',  0, 0, JX9_FMT_STRING,     0,                  0    }, 
+  {  'c',  0, 0, JX9_FMT_CHARX,      0,                  0    }, 
+  {  'x', 16, 0, JX9_FMT_RADIX,      "0123456789abcdef", "x0" }, 
+  {  'X', 16, 0, JX9_FMT_RADIX,      "0123456789ABCDEF", "X0" }, 
+  {  'b',  2, 0, JX9_FMT_RADIX,      "01",                "b0"}, 
+  {  'o',  8, 0, JX9_FMT_RADIX,      "01234567",         "0"  }, 
+  {  'u', 10, 0, JX9_FMT_RADIX,      "0123456789",       0    }, 
+  {  'f',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT,        0,    0    }, 
+  {  'F',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT,        0,    0    }, 
+  {  'e',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP,        "e",    0    }, 
+  {  'E',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP,        "E",    0    }, 
+  {  'g',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC,    "e",    0    }, 
+  {  'G',  0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC,    "E",    0    }, 
+  {  '%',  0, 0, JX9_FMT_PERCENT,    0,                  0    }
+};
+/*
+ * Format a given string.
+ * The root program.  All variations call this core.
+ * INPUTS:
+ *   xConsumer   This is a pointer to a function taking four arguments
+ *            1. A pointer to the call context.
+ *            2. A pointer to the list of characters to be output
+ *               (Note, this list is NOT null terminated.)
+ *            3. An integer number of characters to be output.
+ *               (Note: This number might be zero.)
+ *            4. Upper layer private data.
+ *   zIn       This is the format string, as in the usual print.
+ *   apArg     This is a pointer to a list of arguments.
+ */
+JX9_PRIVATE sxi32 jx9InputFormat(
+       int (*xConsumer)(jx9_context *, const char *, int, void *), /* Format consumer */
+       jx9_context *pCtx,  /* call context */
+       const char *zIn,    /* Format string */
+       int nByte,          /* Format string length */
+       int nArg,           /* Total argument of the given arguments */
+       jx9_value **apArg,  /* User arguments */
+       void *pUserData,    /* Last argument to xConsumer() */
+       int vf              /* TRUE if called from vfprintf, vsprintf context */ 
+       )
+{
+       char spaces[] = "                                                  ";
+#define etSPACESIZE ((int)sizeof(spaces)-1)
+       const char *zCur, *zEnd = &zIn[nByte];
+       char *zBuf, zWorker[JX9_FMT_BUFSIZ];       /* Working buffer */
+       const jx9_fmt_info *pInfo;  /* Pointer to the appropriate info structure */
+       int flag_alternateform; /* True if "#" flag is present */
+       int flag_leftjustify;   /* True if "-" flag is present */
+       int flag_blanksign;     /* True if " " flag is present */
+       int flag_plussign;      /* True if "+" flag is present */
+       int flag_zeropad;       /* True if field width constant starts with zero */
+       jx9_value *pArg;         /* Current processed argument */
+       jx9_int64 iVal;
+       int precision;           /* Precision of the current field */
+       char *zExtra;  
+       int c, rc, n;
+       int length;              /* Length of the field */
+       int prefix;
+       sxu8 xtype;              /* Conversion paradigm */
+       int width;               /* Width of the current field */
+       int idx;
+       n = (vf == TRUE) ? 0 : 1;
+#define NEXT_ARG       ( n < nArg ? apArg[n++] : 0 )
+       /* Start the format process */
+       for(;;){
+               zCur = zIn;
+               while( zIn < zEnd && zIn[0] != '%' ){
+                       zIn++;
+               }
+               if( zCur < zIn ){
+                       /* Consume chunk verbatim */
+                       rc = xConsumer(pCtx, zCur, (int)(zIn-zCur), pUserData);
+                       if( rc == SXERR_ABORT ){
+                               /* Callback request an operation abort */
+                               break;
+                       }
+               }
+               if( zIn >= zEnd ){
+                       /* No more input to process, break immediately */
+                       break;
+               }
+               /* Find out what flags are present */
+               flag_leftjustify = flag_plussign = flag_blanksign = 
+                       flag_alternateform = flag_zeropad = 0;
+               zIn++; /* Jump the precent sign */
+               do{
+                       c = zIn[0];
+                       switch( c ){
+                       case '-':   flag_leftjustify = 1;     c = 0;   break;
+                       case '+':   flag_plussign = 1;        c = 0;   break;
+                       case ' ':   flag_blanksign = 1;       c = 0;   break;
+                       case '#':   flag_alternateform = 1;   c = 0;   break;
+                       case '0':   flag_zeropad = 1;         c = 0;   break;
+                       case '\'':
+                               zIn++;
+                               if( zIn < zEnd ){
+                                       /* An alternate padding character can be specified by prefixing it with a single quote (') */
+                                       c = zIn[0];
+                                       for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
+                                               spaces[idx] = (char)c;
+                                       }
+                                       c = 0;
+                               }
+                               break;
+                       default:                                       break;
+                       }
+               }while( c==0 && (zIn++ < zEnd) );
+               /* Get the field width */
+               width = 0;
+               while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
+                       width = width*10 + (zIn[0] - '0');
+                       zIn++;
+               }
+               if( zIn < zEnd && zIn[0] == '$' ){
+                       /* Position specifer */
+                       if( width > 0 ){
+                               n = width;
+                               if( vf && n > 0 ){ 
+                                       n--;
+                               }
+                       }
+                       zIn++;
+                       width = 0;
+                       if( zIn < zEnd && zIn[0] == '0' ){
+                               flag_zeropad = 1;
+                               zIn++;
+                       }
+                       while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
+                               width = width*10 + (zIn[0] - '0');
+                               zIn++;
+                       }
+               }
+               if( width > JX9_FMT_BUFSIZ-10 ){
+                       width = JX9_FMT_BUFSIZ-10;
+               }
+               /* Get the precision */
+               precision = -1;
+               if( zIn < zEnd && zIn[0] == '.' ){
+                       precision = 0;
+                       zIn++;
+                       while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
+                               precision = precision*10 + (zIn[0] - '0');
+                               zIn++;
+                       }
+               }
+               if( zIn >= zEnd ){
+                       /* No more input */
+                       break;
+               }
+               /* Fetch the info entry for the field */
+               pInfo = 0;
+               xtype = JX9_FMT_ERROR;
+               c = zIn[0];
+               zIn++; /* Jump the format specifer */
+               for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
+                       if( c==aFmt[idx].fmttype ){
+                               pInfo = &aFmt[idx];
+                               xtype = pInfo->type;
+                               break;
+                       }
+               }
+               zBuf = zWorker; /* Point to the working buffer */
+               length = 0;
+               zExtra = 0;
+                /*
+                 ** At this point, variables are initialized as follows:
+                 **
+                 **   flag_alternateform          TRUE if a '#' is present.
+                 **   flag_plussign               TRUE if a '+' is present.
+                 **   flag_leftjustify            TRUE if a '-' is present or if the
+                 **                               field width was negative.
+                 **   flag_zeropad                TRUE if the width began with 0.
+                 **                               the conversion character.
+                 **   flag_blanksign              TRUE if a ' ' is present.
+                 **   width                       The specified field width.  This is
+                 **                               always non-negative.  Zero is the default.
+                 **   precision                   The specified precision.  The default
+                 **                               is -1.
+                 */
+               switch(xtype){
+               case JX9_FMT_PERCENT:
+                       /* A literal percent character */
+                       zWorker[0] = '%';
+                       length = (int)sizeof(char);
+                       break;
+               case JX9_FMT_CHARX:
+                       /* The argument is treated as an integer, and presented as the character
+                        * with that ASCII value
+                        */
+                       pArg = NEXT_ARG;
+                       if( pArg == 0 ){
+                               c = 0;
+                       }else{
+                               c = jx9_value_to_int(pArg);
+                       }
+                       /* NUL byte is an acceptable value */
+                       zWorker[0] = (char)c;
+                       length = (int)sizeof(char);
+                       break;
+               case JX9_FMT_STRING:
+                       /* the argument is treated as and presented as a string */
+                       pArg = NEXT_ARG;
+                       if( pArg == 0 ){
+                               length = 0;
+                       }else{
+                               zBuf = (char *)jx9_value_to_string(pArg, &length);
+                       }
+                       if( length < 1 ){
+                               zBuf = " ";
+                               length = (int)sizeof(char);
+                       }
+                       if( precision>=0 && precision<length ){
+                               length = precision;
+                       }
+                       if( flag_zeropad ){
+                               /* zero-padding works on strings too */
+                               for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
+                                       spaces[idx] = '0';
+                               }
+                       }
+                       break;
+               case JX9_FMT_RADIX:
+                       pArg = NEXT_ARG;
+                       if( pArg == 0 ){
+                               iVal = 0;
+                       }else{
+                               iVal = jx9_value_to_int64(pArg);
+                       }
+                       /* Limit the precision to prevent overflowing buf[] during conversion */
+                       if( precision>JX9_FMT_BUFSIZ-40 ){
+                               precision = JX9_FMT_BUFSIZ-40;
+                       }
+#if 1
+        /* For the format %#x, the value zero is printed "0" not "0x0".
+        ** I think this is stupid.*/
+        if( iVal==0 ) flag_alternateform = 0;
+#else
+        /* More sensible: turn off the prefix for octal (to prevent "00"), 
+        ** but leave the prefix for hex.*/
+        if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0;
+#endif
+        if( pInfo->flags & JX9_FMT_FLAG_SIGNED ){
+          if( iVal<0 ){ 
+            iVal = -iVal;
+                       /* Ticket 1433-003 */
+                       if( iVal < 0 ){
+                               /* Overflow */
+                               iVal= 0x7FFFFFFFFFFFFFFF;
+                       }
+            prefix = '-';
+          }else if( flag_plussign )  prefix = '+';
+          else if( flag_blanksign )  prefix = ' ';
+          else                       prefix = 0;
+        }else{
+                       if( iVal<0 ){
+                               iVal = -iVal;
+                               /* Ticket 1433-003 */
+                               if( iVal < 0 ){
+                                       /* Overflow */
+                                       iVal= 0x7FFFFFFFFFFFFFFF;
+                               }
+                       }
+                       prefix = 0;
+               }
+        if( flag_zeropad && precision<width-(prefix!=0) ){
+          precision = width-(prefix!=0);
+        }
+        zBuf = &zWorker[JX9_FMT_BUFSIZ-1];
+        {
+          register char *cset;      /* Use registers for speed */
+          register int base;
+          cset = pInfo->charset;
+          base = pInfo->base;
+          do{                                           /* Convert to ascii */
+            *(--zBuf) = cset[iVal%base];
+            iVal = iVal/base;
+          }while( iVal>0 );
+        }
+        length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
+        for(idx=precision-length; idx>0; idx--){
+          *(--zBuf) = '0';                             /* Zero pad */
+        }
+        if( prefix ) *(--zBuf) = (char)prefix;               /* Add sign */
+        if( flag_alternateform && pInfo->prefix ){      /* Add "0" or "0x" */
+          char *pre, x;
+          pre = pInfo->prefix;
+          if( *zBuf!=pre[0] ){
+            for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x;
+          }
+        }
+        length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
+               break;
+               case JX9_FMT_FLOAT:
+               case JX9_FMT_EXP:
+               case JX9_FMT_GENERIC:{
+#ifndef JX9_OMIT_FLOATING_POINT
+               long double realvalue;
+               int  exp;                /* exponent of real numbers */
+               double rounder;          /* Used for rounding floating point values */
+               int flag_dp;            /* True if decimal point should be shown */
+               int flag_rtz;           /* True if trailing zeros should be removed */
+               int flag_exp;           /* True to force display of the exponent */
+               int nsd;                 /* Number of significant digits returned */
+               pArg = NEXT_ARG;
+               if( pArg == 0 ){
+                       realvalue = 0;
+               }else{
+                       realvalue = jx9_value_to_double(pArg);
+               }
+        if( precision<0 ) precision = 6;         /* Set default precision */
+        if( precision>JX9_FMT_BUFSIZ-40) precision = JX9_FMT_BUFSIZ-40;
+        if( realvalue<0.0 ){
+          realvalue = -realvalue;
+          prefix = '-';
+        }else{
+          if( flag_plussign )          prefix = '+';
+          else if( flag_blanksign )    prefix = ' ';
+          else                         prefix = 0;
+        }
+        if( pInfo->type==JX9_FMT_GENERIC && precision>0 ) precision--;
+        rounder = 0.0;
+#if 0
+        /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
+        for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
+#else
+        /* It makes more sense to use 0.5 */
+        for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
+#endif
+        if( pInfo->type==JX9_FMT_FLOAT ) realvalue += rounder;
+        /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
+        exp = 0;
+        if( realvalue>0.0 ){
+          while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
+          while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+          while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
+          while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
+          if( exp>350 || exp<-350 ){
+            zBuf = "NaN";
+            length = 3;
+            break;
+          }
+        }
+        zBuf = zWorker;
+        /*
+        ** If the field type is etGENERIC, then convert to either etEXP
+        ** or etFLOAT, as appropriate.
+        */
+        flag_exp = xtype==JX9_FMT_EXP;
+        if( xtype!=JX9_FMT_FLOAT ){
+          realvalue += rounder;
+          if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
+        }
+        if( xtype==JX9_FMT_GENERIC ){
+          flag_rtz = !flag_alternateform;
+          if( exp<-4 || exp>precision ){
+            xtype = JX9_FMT_EXP;
+          }else{
+            precision = precision - exp;
+            xtype = JX9_FMT_FLOAT;
+          }
+        }else{
+          flag_rtz = 0;
+        }
+        /*
+        ** The "exp+precision" test causes output to be of type etEXP if
+        ** the precision is too large to fit in buf[].
+        */
+        nsd = 0;
+        if( xtype==JX9_FMT_FLOAT && exp+precision<JX9_FMT_BUFSIZ-30 ){
+          flag_dp = (precision>0 || flag_alternateform);
+          if( prefix ) *(zBuf++) = (char)prefix;         /* Sign */
+          if( exp<0 )  *(zBuf++) = '0';            /* Digits before "." */
+          else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
+          if( flag_dp ) *(zBuf++) = '.';           /* The decimal point */
+          for(exp++; exp<0 && precision>0; precision--, exp++){
+            *(zBuf++) = '0';
+          }
+          while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
+          *(zBuf--) = 0;                           /* Null terminate */
+          if( flag_rtz && flag_dp ){     /* Remove trailing zeros and "." */
+            while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
+            if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
+          }
+          zBuf++;                            /* point to next free slot */
+        }else{    /* etEXP or etGENERIC */
+          flag_dp = (precision>0 || flag_alternateform);
+          if( prefix ) *(zBuf++) = (char)prefix;   /* Sign */
+          *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);  /* First digit */
+          if( flag_dp ) *(zBuf++) = '.';     /* Decimal point */
+          while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
+          zBuf--;                            /* point to last digit */
+          if( flag_rtz && flag_dp ){          /* Remove tail zeros */
+            while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
+            if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
+          }
+          zBuf++;                            /* point to next free slot */
+          if( exp || flag_exp ){
+            *(zBuf++) = pInfo->charset[0];
+            if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */
+            else       { *(zBuf++) = '+'; }
+            if( exp>=100 ){
+              *(zBuf++) = (char)((exp/100)+'0');                /* 100's digit */
+              exp %= 100;
+            }
+            *(zBuf++) = (char)(exp/10+'0');                     /* 10's digit */
+            *(zBuf++) = (char)(exp%10+'0');                     /* 1's digit */
+          }
+        }
+        /* The converted number is in buf[] and zero terminated.Output it.
+        ** Note that the number is in the usual order, not reversed as with
+        ** integer conversions.*/
+        length = (int)(zBuf-zWorker);
+        zBuf = zWorker;
+        /* Special case:  Add leading zeros if the flag_zeropad flag is
+        ** set and we are not left justified */
+        if( flag_zeropad && !flag_leftjustify && length < width){
+          int i;
+          int nPad = width - length;
+          for(i=width; i>=nPad; i--){
+            zBuf[i] = zBuf[i-nPad];
+          }
+          i = prefix!=0;
+          while( nPad-- ) zBuf[i++] = '0';
+          length = width;
+        }
+#else
+         zBuf = " ";
+                length = (int)sizeof(char);
+#endif /* JX9_OMIT_FLOATING_POINT */
+                break;
+                                                        }
+               default:
+                       /* Invalid format specifer */
+                       zWorker[0] = '?';
+                       length = (int)sizeof(char);
+                       break;
+               }
+                /*
+                ** The text of the conversion is pointed to by "zBuf" and is
+                ** "length" characters long.The field width is "width".Do
+                ** the output.
+                */
+    if( !flag_leftjustify ){
+      register int nspace;
+      nspace = width-length;
+      if( nspace>0 ){
+        while( nspace>=etSPACESIZE ){
+                       rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
+                       if( rc != SXRET_OK ){
+                               return SXERR_ABORT; /* Consumer routine request an operation abort */
+                       }
+                       nspace -= etSPACESIZE;
+        }
+        if( nspace>0 ){
+                       rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
+                       if( rc != SXRET_OK ){
+                               return SXERR_ABORT; /* Consumer routine request an operation abort */
+                       }
+               }
+      }
+    }
+    if( length>0 ){
+               rc = xConsumer(pCtx, zBuf, (unsigned int)length, pUserData);
+               if( rc != SXRET_OK ){
+                 return SXERR_ABORT; /* Consumer routine request an operation abort */
+               }
+    }
+    if( flag_leftjustify ){
+      register int nspace;
+      nspace = width-length;
+      if( nspace>0 ){
+        while( nspace>=etSPACESIZE ){
+                       rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
+                       if( rc != SXRET_OK ){
+                               return SXERR_ABORT; /* Consumer routine request an operation abort */
+                       }
+                       nspace -= etSPACESIZE;
+        }
+        if( nspace>0 ){
+                       rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
+                       if( rc != SXRET_OK ){
+                               return SXERR_ABORT; /* Consumer routine request an operation abort */
+                       }
+               }
+      }
+    }
+ }/* for(;;) */
+       return SXRET_OK;
+}
+/*
+ * Callback [i.e: Formatted input consumer] of the sprintf function.
+ */
+static int sprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
+{
+       /* Consume directly */
+       jx9_result_string(pCtx, zInput, nLen);
+       SXUNUSED(pUserData); /* cc warning */
+       return JX9_OK;
+}
+/*
+ * string sprintf(string $format[, mixed $args [, mixed $... ]])
+ *  Return a formatted string.
+ * Parameters
+ *  $format 
+ *    The format string (see block comment above)
+ * Return
+ *  A string produced according to the formatting string format. 
+ */
+static int jx9Builtin_sprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zFormat;
+       int nLen;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid arguments, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Extract the string format */
+       zFormat = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Format the string */
+       jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, nArg, apArg, 0, FALSE);
+       return JX9_OK;
+}
+/*
+ * Callback [i.e: Formatted input consumer] of the printf function.
+ */
+static int printfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
+{
+       jx9_int64 *pCounter = (jx9_int64 *)pUserData;
+       /* Call the VM output consumer directly */
+       jx9_context_output(pCtx, zInput, nLen);
+       /* Increment counter */
+       *pCounter += nLen;
+       return JX9_OK;
+}
+/*
+ * int64 printf(string $format[, mixed $args[, mixed $... ]])
+ *  Output a formatted string.
+ * Parameters
+ *  $format
+ *   See sprintf() for a description of format.
+ * Return
+ *  The length of the outputted string.
+ */
+static int jx9Builtin_printf(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_int64 nCounter = 0;
+       const char *zFormat;
+       int nLen;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid arguments, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the string format */
+       zFormat = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Format the string */
+       jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, nArg, apArg, (void *)&nCounter, FALSE);
+       /* Return the length of the outputted string */
+       jx9_result_int64(pCtx, nCounter);
+       return JX9_OK;
+}
+/*
+ * int vprintf(string $format, array $args)
+ *  Output a formatted string.
+ * Parameters
+ *  $format
+ *   See sprintf() for a description of format.
+ * Return
+ *  The length of the outputted string.
+ */
+static int jx9Builtin_vprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_int64 nCounter = 0;
+       const char *zFormat;
+       jx9_hashmap *pMap;
+       SySet sArg;
+       int nLen, n;
+       if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
+               /* Missing/Invalid arguments, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the string format */
+       zFormat = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the hashmap */
+       pMap = (jx9_hashmap *)apArg[1]->x.pOther;
+       /* Extract arguments from the hashmap */
+       n = jx9HashmapValuesToSet(pMap, &sArg);
+       /* Format the string */
+       jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&nCounter, TRUE);
+       /* Return the length of the outputted string */
+       jx9_result_int64(pCtx, nCounter);
+       /* Release the container */
+       SySetRelease(&sArg);
+       return JX9_OK;
+}
+/*
+ * int vsprintf(string $format, array $args)
+ *  Output a formatted string.
+ * Parameters
+ *  $format
+ *   See sprintf() for a description of format.
+ * Return
+ *  A string produced according to the formatting string format.
+ */
+static int jx9Builtin_vsprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zFormat;
+       jx9_hashmap *pMap;
+       SySet sArg;
+       int nLen, n;
+       if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
+               /* Missing/Invalid arguments, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Extract the string format */
+       zFormat = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Point to hashmap */
+       pMap = (jx9_hashmap *)apArg[1]->x.pOther;
+       /* Extract arguments from the hashmap */
+       n = jx9HashmapValuesToSet(pMap, &sArg);
+       /* Format the string */
+       jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), 0, TRUE);
+       /* Release the container */
+       SySetRelease(&sArg);
+       return JX9_OK;
+}
+/*
+ * string size_format(int64 $size)
+ *  Return a smart string represenation of the given size [i.e: 64-bit integer]
+ *  Example:
+ *    print size_format(1*1024*1024*1024);// 1GB
+ *    print size_format(512*1024*1024); // 512 MB
+ *    print size_format(file_size(/path/to/my/file_8192)); //8KB
+ * Parameter
+ *  $size
+ *    Entity size in bytes.
+ * Return
+ *   Formatted string representation of the given size.
+ */
+static int jx9Builtin_size_format(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       /*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/
+       static const char zUnit[] = {"KMGTPEZ"};
+       sxi32 nRest, i_32;
+       jx9_int64 iSize;
+       int c = -1; /* index in zUnit[] */
+
+       if( nArg < 1 ){
+               /* Missing argument, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Extract the given size */
+       iSize = jx9_value_to_int64(apArg[0]);
+       if( iSize < 100 /* Bytes */ ){
+               /* Don't bother formatting, return immediately */
+               jx9_result_string(pCtx, "0.1 KB", (int)sizeof("0.1 KB")-1);
+               return JX9_OK;
+       }
+       for(;;){
+               nRest = (sxi32)(iSize & 0x3FF); 
+               iSize >>= 10;
+               c++;
+               if( (iSize & (~0 ^ 1023)) == 0 ){
+                       break;
+               }
+       }
+       nRest /= 100;
+       if( nRest > 9 ){
+               nRest = 9;
+       }
+       if( iSize > 999 ){
+               c++;
+               nRest = 9;
+               iSize = 0;
+       }
+       i_32 = (sxi32)iSize;
+       /* Format */
+       jx9_result_string_format(pCtx, "%d.%d %cB", i_32, nRest, zUnit[c]);
+       return JX9_OK;
+}
+#if !defined(JX9_DISABLE_HASH_FUNC)
+/*
+ * string md5(string $str[, bool $raw_output = false])
+ *   Calculate the md5 hash of a string.
+ * Parameter
+ *  $str
+ *   Input string
+ * $raw_output
+ *   If the optional raw_output is set to TRUE, then the md5 digest
+ *   is instead returned in raw binary format with a length of 16.
+ * Return
+ *  MD5 Hash as a 32-character hexadecimal string.
+ */
+static int jx9Builtin_md5(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       unsigned char zDigest[16];
+       int raw_output = FALSE;
+       const void *pIn;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Extract the input string */
+       pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       if( nArg > 1 && jx9_value_is_bool(apArg[1])){
+               raw_output = jx9_value_to_bool(apArg[1]);
+       }
+       /* Compute the MD5 digest */
+       SyMD5Compute(pIn, (sxu32)nLen, zDigest);
+       if( raw_output ){
+               /* Output raw digest */
+               jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
+       }else{
+               /* Perform a binary to hex conversion */
+               SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
+       }
+       return JX9_OK;
+}
+/*
+ * string sha1(string $str[, bool $raw_output = false])
+ *   Calculate the sha1 hash of a string.
+ * Parameter
+ *  $str
+ *   Input string
+ * $raw_output
+ *   If the optional raw_output is set to TRUE, then the md5 digest
+ *   is instead returned in raw binary format with a length of 16.
+ * Return
+ *  SHA1 Hash as a 40-character hexadecimal string.
+ */
+static int jx9Builtin_sha1(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       unsigned char zDigest[20];
+       int raw_output = FALSE;
+       const void *pIn;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Extract the input string */
+       pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       if( nArg > 1 && jx9_value_is_bool(apArg[1])){
+               raw_output = jx9_value_to_bool(apArg[1]);
+       }
+       /* Compute the SHA1 digest */
+       SySha1Compute(pIn, (sxu32)nLen, zDigest);
+       if( raw_output ){
+               /* Output raw digest */
+               jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
+       }else{
+               /* Perform a binary to hex conversion */
+               SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
+       }
+       return JX9_OK;
+}
+/*
+ * int64 crc32(string $str)
+ *   Calculates the crc32 polynomial of a strin.
+ * Parameter
+ *  $str
+ *   Input string
+ * Return
+ *  CRC32 checksum of the given input (64-bit integer).
+ */
+static int jx9Builtin_crc32(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const void *pIn;
+       sxu32 nCRC;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the input string */
+       pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Empty string */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Calculate the sum */
+       nCRC = SyCrc32(pIn, (sxu32)nLen);
+       /* Return the CRC32 as 64-bit integer */
+       jx9_result_int64(pCtx, (jx9_int64)nCRC^ 0xFFFFFFFF);
+       return JX9_OK;
+}
+#endif /* JX9_DISABLE_HASH_FUNC */
+/*
+ * Parse a CSV string and invoke the supplied callback for each processed xhunk.
+ */
+JX9_PRIVATE sxi32 jx9ProcessCsv(
+       const char *zInput, /* Raw input */
+       int nByte,  /* Input length */
+       int delim,  /* Delimiter */
+       int encl,   /* Enclosure */
+       int escape,  /* Escape character */
+       sxi32 (*xConsumer)(const char *, int, void *), /* User callback */
+       void *pUserData /* Last argument to xConsumer() */
+       )
+{
+       const char *zEnd = &zInput[nByte];
+       const char *zIn = zInput;
+       const char *zPtr;
+       int isEnc;
+       /* Start processing */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               isEnc = 0;
+               zPtr = zIn;
+               /* Find the first delimiter */
+               while( zIn < zEnd ){
+                       if( zIn[0] == delim && !isEnc){
+                               /* Delimiter found, break imediately */
+                               break;
+                       }else if( zIn[0] == encl ){
+                               /* Inside enclosure? */
+                               isEnc = !isEnc;
+                       }else if( zIn[0] == escape ){
+                               /* Escape sequence */
+                               zIn++;
+                       }
+                       /* Advance the cursor */
+                       zIn++;
+               }
+               if( zIn > zPtr ){
+                       int nByte = (int)(zIn-zPtr);
+                       sxi32 rc;
+                       /* Invoke the supllied callback */
+                       if( zPtr[0] == encl ){
+                               zPtr++;
+                               nByte-=2;
+                       }
+                       if( nByte > 0 ){
+                               rc = xConsumer(zPtr, nByte, pUserData);
+                               if( rc == SXERR_ABORT ){
+                                       /* User callback request an operation abort */
+                                       break;
+                               }
+                       }
+               }
+               /* Ignore trailing delimiter */
+               while( zIn < zEnd && zIn[0] == delim ){
+                       zIn++;
+               }
+       }
+       return SXRET_OK;
+}
+/*
+ * Default consumer callback for the CSV parsing routine defined above.
+ * All the processed input is insereted into an array passed as the last
+ * argument to this callback.
+ */
+JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData)
+{
+       jx9_value *pArray = (jx9_value *)pUserData;
+       jx9_value sEntry;
+       SyString sToken;
+       /* Insert the token in the given array */
+       SyStringInitFromBuf(&sToken, zToken, nTokenLen);
+       /* Remove trailing and leading white spcaces and null bytes */
+       SyStringFullTrimSafe(&sToken);
+       if( sToken.nByte < 1){
+               return SXRET_OK;
+       }
+       jx9MemObjInitFromString(pArray->pVm, &sEntry, &sToken);
+       jx9_array_add_elem(pArray, 0, &sEntry);
+       jx9MemObjRelease(&sEntry);
+       return SXRET_OK;
+}
+/*
+ * array str_getcsv(string $input[, string $delimiter = ', '[, string $enclosure = '"' [, string $escape='\\']]])
+ *  Parse a CSV string into an array.
+ * Parameters
+ *  $input
+ *   The string to parse.
+ *  $delimiter
+ *   Set the field delimiter (one character only).
+ *  $enclosure
+ *   Set the field enclosure character (one character only).
+ *  $escape
+ *   Set the escape character (one character only). Defaults as a backslash (\)
+ * Return
+ *  An indexed array containing the CSV fields or NULL on failure.
+ */
+static int jx9Builtin_str_getcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zInput, *zPtr;
+       jx9_value *pArray;
+       int delim  = ',';   /* Delimiter */
+       int encl   = '"' ;  /* Enclosure */
+       int escape = '\\';  /* Escape character */
+       int nLen;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the raw input */
+       zInput = jx9_value_to_string(apArg[0], &nLen);
+       if( nArg > 1 ){
+               int i;
+               if( jx9_value_is_string(apArg[1]) ){
+                       /* Extract the delimiter */
+                       zPtr = jx9_value_to_string(apArg[1], &i);
+                       if( i > 0 ){
+                               delim = zPtr[0];
+                       }
+               }
+               if( nArg > 2 ){
+                       if( jx9_value_is_string(apArg[2]) ){
+                               /* Extract the enclosure */
+                               zPtr = jx9_value_to_string(apArg[2], &i);
+                               if( i > 0 ){
+                                       encl = zPtr[0];
+                               }
+                       }
+                       if( nArg > 3 ){
+                               if( jx9_value_is_string(apArg[3]) ){
+                                       /* Extract the escape character */
+                                       zPtr = jx9_value_to_string(apArg[3], &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(zInput, nLen, delim, encl, escape, jx9CsvConsumer, pArray);
+       /* Return the freshly created array */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * Extract a tag name from a raw HTML input and insert it in the given
+ * container.
+ * Refer to [strip_tags()].
+ */
+static sxi32 AddTag(SySet *pSet, const char *zTag, int nByte)
+{
+       const char *zEnd = &zTag[nByte];
+       const char *zPtr;
+       SyString sEntry;
+       /* Strip tags */
+       for(;;){
+               while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?'
+                       || zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
+                               zTag++;
+               }
+               if( zTag >= zEnd ){
+                       break;
+               }
+               zPtr = zTag;
+               /* Delimit the tag */
+               while(zTag < zEnd ){
+                       if( (unsigned char)zTag[0] >= 0xc0 ){
+                               /* UTF-8 stream */
+                               zTag++;
+                               SX_JMP_UTF8(zTag, zEnd);
+                       }else if( !SyisAlphaNum(zTag[0]) ){
+                               break;
+                       }else{
+                               zTag++;
+                       }
+               }
+               if( zTag > zPtr ){
+                       /* Perform the insertion */
+                       SyStringInitFromBuf(&sEntry, zPtr, (int)(zTag-zPtr));
+                       SyStringFullTrim(&sEntry);
+                       SySetPut(pSet, (const void *)&sEntry);
+               }
+               /* Jump the trailing '>' */
+               zTag++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Check if the given HTML tag name is present in the given container.
+ * Return SXRET_OK if present.SXERR_NOTFOUND otherwise.
+ * Refer to [strip_tags()].
+ */
+static sxi32 FindTag(SySet *pSet, const char *zTag, int nByte)
+{
+       if( SySetUsed(pSet) > 0 ){
+               const char *zCur, *zEnd = &zTag[nByte];
+               SyString sTag;
+               while( zTag < zEnd &&  (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' ||
+                       ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
+                       zTag++;
+               }
+               /* Delimit the tag */
+               zCur = zTag;
+               while(zTag < zEnd ){
+                       if( (unsigned char)zTag[0] >= 0xc0 ){
+                               /* UTF-8 stream */
+                               zTag++;
+                               SX_JMP_UTF8(zTag, zEnd);
+                       }else if( !SyisAlphaNum(zTag[0]) ){
+                               break;
+                       }else{
+                               zTag++;
+                       }
+               }
+               SyStringInitFromBuf(&sTag, zCur, zTag-zCur);
+               /* Trim leading white spaces and null bytes */
+               SyStringLeftTrimSafe(&sTag);
+               if( sTag.nByte > 0 ){
+                       SyString *aEntry, *pEntry;
+                       sxi32 rc;
+                       sxu32 n;
+                       /* Perform the lookup */
+                       aEntry = (SyString *)SySetBasePtr(pSet);
+                       for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
+                               pEntry = &aEntry[n];
+                               /* Do the comparison */
+                               rc = SyStringCmp(pEntry, &sTag, SyStrnicmp);
+                               if( !rc ){
+                                       return SXRET_OK;
+                               }
+                       }
+               }
+       }
+       /* No such tag */
+       return SXERR_NOTFOUND;
+}
+/*
+ * This function tries to return a string [i.e: in the call context result buffer]
+ * with all NUL bytes, HTML and JX9 tags stripped from a given string.
+ * Refer to [strip_tags()].
+ */
+JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen)
+{
+       const char *zEnd = &zIn[nByte];
+       const char *zPtr, *zTag;
+       SySet sSet;
+       /* initialize the set of allowed tags */
+       SySetInit(&sSet, &pCtx->pVm->sAllocator, sizeof(SyString));
+       if( nTaglen > 0 ){
+               /* Set of allowed tags */
+               AddTag(&sSet, zTaglist, nTaglen);
+       }
+       /* Set the empty string */
+       jx9_result_string(pCtx, "", 0);
+       /* Start processing */
+       for(;;){
+               if(zIn >= zEnd){
+                       /* No more input to process */
+                       break;
+               }
+               zPtr = zIn;
+               /* Find a tag */
+               while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){
+                       zIn++;
+               }
+               if( zIn > zPtr ){
+                       /* Consume raw input */
+                       jx9_result_string(pCtx, zPtr, (int)(zIn-zPtr));
+               }
+               /* Ignore trailing null bytes */
+               while( zIn < zEnd && zIn[0] == 0 ){
+                       zIn++;
+               }
+               if(zIn >= zEnd){
+                       /* No more input to process */
+                       break;
+               }
+               if( zIn[0] == '<' ){
+                       sxi32 rc;
+                       zTag = zIn++;
+                       /* Delimit the tag */
+                       while( zIn < zEnd && zIn[0] != '>' ){
+                               zIn++;
+                       }
+                       if( zIn < zEnd ){
+                               zIn++; /* Ignore the trailing closing tag */
+                       }
+                       /* Query the set */
+                       rc = FindTag(&sSet, zTag, (int)(zIn-zTag));
+                       if( rc == SXRET_OK ){
+                               /* Keep the tag */
+                               jx9_result_string(pCtx, zTag, (int)(zIn-zTag));
+                       }
+               }
+       }
+       /* Cleanup */
+       SySetRelease(&sSet);
+       return SXRET_OK;
+}
+/*
+ * string strip_tags(string $str[, string $allowable_tags])
+ *   Strip HTML and JX9 tags from a string.
+ * Parameters
+ *  $str
+ *  The input string.
+ * $allowable_tags
+ *  You can use the optional second parameter to specify tags which should not be stripped. 
+ * Return
+ *  Returns the stripped string.
+ */
+static int jx9Builtin_strip_tags(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zTaglist = 0;
+       const char *zString;
+       int nTaglen = 0;
+       int nLen;
+       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 raw string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
+               /* Allowed tag */
+               zTaglist = jx9_value_to_string(apArg[1], &nTaglen);             
+       }
+       /* Process input */
+       jx9StripTagsFromString(pCtx, zString, nLen, zTaglist, nTaglen);
+       return JX9_OK;
+}
+/*
+ * array str_split(string $string[, int $split_length = 1 ])
+ *  Convert a string to an array.
+ * Parameters
+ * $str
+ *  The input string.
+ * $split_length
+ *  Maximum length of the chunk.
+ * Return
+ *  If the optional split_length parameter is specified, the returned array
+ *  will be broken down into chunks with each being split_length in length, otherwise
+ *  each chunk will be one character in length. FALSE is returned if split_length is less than 1.
+ *  If the split_length length exceeds the length of string, the entire string is returned 
+ *  as the first (and only) array element.
+ */
+static int jx9Builtin_str_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString, *zEnd;
+       jx9_value *pArray, *pValue;
+       int split_len;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the target string */
+       zString = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Nothing to process, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       split_len = (int)sizeof(char);
+       if( nArg > 1 ){
+               /* Split length */
+               split_len = jx9_value_to_int(apArg[1]);
+               if( split_len < 1 ){
+                       /* Invalid length, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               if( split_len > nLen ){
+                       split_len = nLen;
+               }
+       }
+       /* Create the array and the scalar value */
+       pArray = jx9_context_new_array(pCtx);
+       /*Chunk value */
+       pValue = jx9_context_new_scalar(pCtx);
+       if( pValue == 0 || pArray == 0 ){
+               /* Return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the end of the string */
+       zEnd = &zString[nLen];
+       /* Perform the requested operation */
+       for(;;){
+               int nMax;
+               if( zString >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               nMax = (int)(zEnd-zString);
+               if( nMax < split_len ){
+                       split_len = nMax;
+               }
+               /* Copy the current chunk */
+               jx9_value_string(pValue, zString, split_len);
+               /* Insert it */
+               jx9_array_add_elem(pArray, 0, pValue); /* Will make it's own copy */
+               /* reset the string cursor */
+               jx9_value_reset_string_cursor(pValue);
+               /* Update position */
+               zString += split_len;
+       }
+       /* 
+        * Return the array.
+        * Don't worry about freeing memory, everything will be automatically released
+        * upon we return from this function.
+        */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * Tokenize a raw string and extract the first non-space token.
+ * Refer to [strspn()].
+ */
+static sxi32 ExtractNonSpaceToken(const char **pzIn, const char *zEnd, SyString *pOut)
+{
+       const char *zIn = *pzIn;
+       const char *zPtr;
+       /* Ignore leading white spaces */
+       while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
+               zIn++;
+       }
+       if( zIn >= zEnd ){
+               /* End of input */
+               return SXERR_EOF;
+       }
+       zPtr = zIn;
+       /* Extract the token */
+       while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){
+               zIn++;
+       }
+       SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
+       /* Synchronize pointers */
+       *pzIn = zIn;
+       /* Return to the caller */
+       return SXRET_OK;
+}
+/*
+ * Check if the given string contains only characters from the given mask.
+ * return the longest match.
+ * Refer to [strspn()].
+ */
+static int LongestStringMask(const char *zString, int nLen, const char *zMask, int nMaskLen)
+{
+       const char *zEnd = &zString[nLen];
+       const char *zIn = zString;
+       int i, c;
+       for(;;){
+               if( zString >= zEnd ){
+                       break;
+               }
+               /* Extract current character */
+               c = zString[0];
+               /* Perform the lookup */
+               for( i = 0 ; i < nMaskLen ; i++ ){
+                       if( c == zMask[i] ){
+                               /* Character found */
+                               break;
+                       }
+               }
+               if( i >= nMaskLen ){
+                       /* Character not in the current mask, break immediately */
+                       break;
+               }
+               /* Advance cursor */
+               zString++;
+       }
+       /* Longest match */
+       return (int)(zString-zIn);
+}
+/*
+ * Do the reverse operation of the previous function [i.e: LongestStringMask()].
+ * Refer to [strcspn()].
+ */
+static int LongestStringMask2(const char *zString, int nLen, const char *zMask, int nMaskLen)
+{
+       const char *zEnd = &zString[nLen];
+       const char *zIn = zString;
+       int i, c;
+       for(;;){
+               if( zString >= zEnd ){
+                       break;
+               }
+               /* Extract current character */
+               c = zString[0];
+               /* Perform the lookup */
+               for( i = 0 ; i < nMaskLen ; i++ ){
+                       if( c == zMask[i] ){
+                               break;
+                       }
+               }
+               if( i < nMaskLen ){
+                       /* Character in the current mask, break immediately */
+                       break;
+               }
+               /* Advance cursor */
+               zString++;
+       }
+       /* Longest match */
+       return (int)(zString-zIn);
+}
+/*
+ * int strspn(string $str, string $mask[, int $start[, int $length]])
+ *  Finds the length of the initial segment of a string consisting entirely
+ *  of characters contained within a given mask.
+ * Parameters
+ * $str
+ *  The input string.
+ * $mask
+ *  The list of allowable characters.
+ * $start
+ *  The position in subject to start searching.
+ *  If start is given and is non-negative, then strspn() will begin examining 
+ *  subject at the start'th position. For instance, in the string 'abcdef', the character
+ *  at position 0 is 'a', the character at position 2 is 'c', and so forth.
+ *  If start is given and is negative, then strspn() will begin examining subject at the
+ *  start'th position from the end of subject.
+ * $length
+ *  The length of the segment from subject to examine.
+ *  If length is given and is non-negative, then subject will be examined for length
+ *  characters after the starting position.
+ *  If lengthis given and is negative, then subject will be examined from the starting
+ *  position up to length characters from the end of subject.
+ * Return
+ * Returns the length of the initial segment of subject which consists entirely of characters
+ * in mask.
+ */
+static int jx9Builtin_strspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString, *zMask, *zEnd;
+       int iMasklen, iLen;
+       SyString sToken;
+       int iCount = 0;
+       int rc;
+       if( nArg < 2 ){
+               /* Missing agruments, return zero */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &iLen);
+       /* Extract the mask */
+       zMask = jx9_value_to_string(apArg[1], &iMasklen);
+       if( iLen < 1 || iMasklen < 1 ){
+               /* Nothing to process, return zero */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       if( nArg > 2 ){
+               int nOfft;
+               /* Extract the offset */
+               nOfft = jx9_value_to_int(apArg[2]);
+               if( nOfft < 0 ){
+                       const char *zBase = &zString[iLen + nOfft];
+                       if( zBase > zString ){
+                               iLen = (int)(&zString[iLen]-zBase);
+                               zString = zBase;        
+                       }else{
+                               /* Invalid offset */
+                               jx9_result_int(pCtx, 0);
+                               return JX9_OK;
+                       }
+               }else{
+                       if( nOfft >= iLen ){
+                               /* Invalid offset */
+                               jx9_result_int(pCtx, 0);
+                               return JX9_OK;
+                       }else{
+                               /* Update offset */
+                               zString += nOfft;
+                               iLen -= nOfft;
+                       }
+               }
+               if( nArg > 3 ){
+                       int iUserlen;
+                       /* Extract the desired length */
+                       iUserlen = jx9_value_to_int(apArg[3]);
+                       if( iUserlen > 0 && iUserlen < iLen ){
+                               iLen = iUserlen;
+                       }
+               }
+       }
+       /* Point to the end of the string */
+       zEnd = &zString[iLen];
+       /* Extract the first non-space token */
+       rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
+       if( rc == SXRET_OK && sToken.nByte > 0 ){
+               /* Compare against the current mask */
+               iCount = LongestStringMask(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
+       }
+       /* Longest match */
+       jx9_result_int(pCtx, iCount);
+       return JX9_OK;
+}
+/*
+ * int strcspn(string $str, string $mask[, int $start[, int $length]])
+ *  Find length of initial segment not matching mask.
+ * Parameters
+ * $str
+ *  The input string.
+ * $mask
+ *  The list of not allowed characters.
+ * $start
+ *  The position in subject to start searching.
+ *  If start is given and is non-negative, then strspn() will begin examining 
+ *  subject at the start'th position. For instance, in the string 'abcdef', the character
+ *  at position 0 is 'a', the character at position 2 is 'c', and so forth.
+ *  If start is given and is negative, then strspn() will begin examining subject at the
+ *  start'th position from the end of subject.
+ * $length
+ *  The length of the segment from subject to examine.
+ *  If length is given and is non-negative, then subject will be examined for length
+ *  characters after the starting position.
+ *  If lengthis given and is negative, then subject will be examined from the starting
+ *  position up to length characters from the end of subject.
+ * Return
+ *  Returns the length of the segment as an integer.
+ */
+static int jx9Builtin_strcspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString, *zMask, *zEnd;
+       int iMasklen, iLen;
+       SyString sToken;
+       int iCount = 0;
+       int rc;
+       if( nArg < 2 ){
+               /* Missing agruments, return zero */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zString = jx9_value_to_string(apArg[0], &iLen);
+       /* Extract the mask */
+       zMask = jx9_value_to_string(apArg[1], &iMasklen);
+       if( iLen < 1 ){
+               /* Nothing to process, return zero */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       if( iMasklen < 1 ){
+               /* No given mask, return the string length */
+               jx9_result_int(pCtx, iLen);
+               return JX9_OK;
+       }
+       if( nArg > 2 ){
+               int nOfft;
+               /* Extract the offset */
+               nOfft = jx9_value_to_int(apArg[2]);
+               if( nOfft < 0 ){
+                       const char *zBase = &zString[iLen + nOfft];
+                       if( zBase > zString ){
+                               iLen = (int)(&zString[iLen]-zBase);
+                               zString = zBase;        
+                       }else{
+                               /* Invalid offset */
+                               jx9_result_int(pCtx, 0);
+                               return JX9_OK;
+                       }
+               }else{
+                       if( nOfft >= iLen ){
+                               /* Invalid offset */
+                               jx9_result_int(pCtx, 0);
+                               return JX9_OK;
+                       }else{
+                               /* Update offset */
+                               zString += nOfft;
+                               iLen -= nOfft;
+                       }
+               }
+               if( nArg > 3 ){
+                       int iUserlen;
+                       /* Extract the desired length */
+                       iUserlen = jx9_value_to_int(apArg[3]);
+                       if( iUserlen > 0 && iUserlen < iLen ){
+                               iLen = iUserlen;
+                       }
+               }
+       }
+       /* Point to the end of the string */
+       zEnd = &zString[iLen];
+       /* Extract the first non-space token */
+       rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
+       if( rc == SXRET_OK && sToken.nByte > 0 ){
+               /* Compare against the current mask */
+               iCount = LongestStringMask2(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
+       }
+       /* Longest match */
+       jx9_result_int(pCtx, iCount);
+       return JX9_OK;
+}
+/*
+ * string strpbrk(string $haystack, string $char_list)
+ *  Search a string for any of a set of characters.
+ * Parameters
+ *  $haystack
+ *   The string where char_list is looked for.
+ *  $char_list
+ *   This parameter is case sensitive.
+ * Return
+ *  Returns a string starting from the character found, or FALSE if it is not found.
+ */
+static int jx9Builtin_strpbrk(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zString, *zList, *zEnd;
+       int iLen, iListLen, i, c;
+       sxu32 nOfft, nMax;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the haystack and the char list */
+       zString = jx9_value_to_string(apArg[0], &iLen);
+       zList = jx9_value_to_string(apArg[1], &iListLen);
+       if( iLen < 1 ){
+               /* Nothing to process, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the end of the string */
+       zEnd = &zString[iLen];
+       nOfft = nMax = SXU32_HIGH;
+       /* perform the requested operation */
+       for( i = 0 ; i < iListLen ; i++ ){
+               c = zList[i];
+               rc = SyByteFind(zString, (sxu32)iLen, c, &nMax);
+               if( rc == SXRET_OK ){
+                       if( nMax < nOfft ){
+                               nOfft = nMax;
+                       }
+               }
+       }
+       if( nOfft == SXU32_HIGH ){
+               /* No such substring, return FALSE */
+               jx9_result_bool(pCtx, 0);
+       }else{
+               /* Return the substring */
+               jx9_result_string(pCtx, &zString[nOfft], (int)(zEnd-&zString[nOfft]));
+       }
+       return JX9_OK;
+}
+/*
+ * string soundex(string $str)
+ *  Calculate the soundex key of a string.
+ * Parameters
+ *  $str
+ *   The input string.
+ * Return
+ *  Returns the soundex key as a string.
+ * Note:
+ *  This implementation is based on the one found in the SQLite3
+ * source tree.
+ */
+static int jx9Builtin_soundex(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn;
+       char zResult[8];
+       int i, j;
+       static const unsigned char iCode[] = {
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+               0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 
+               1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
+               0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 
+               1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
+       };
+       if( nArg < 1 ){
+               /* Missing arguments, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       zIn = (unsigned char *)jx9_value_to_string(apArg[0], 0);
+       for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){}
+       if( zIn[i] ){
+               unsigned char prevcode = iCode[zIn[i]&0x7f];
+               zResult[0] = (char)SyToUpper(zIn[i]);
+               for(j=1; j<4 && zIn[i]; i++){
+                       int code = iCode[zIn[i]&0x7f];
+                       if( code>0 ){
+                               if( code!=prevcode ){
+                                       prevcode = (unsigned char)code;
+                                       zResult[j++] = (char)code + '0';
+                               }
+                       }else{
+                               prevcode = 0;
+                       }
+               }
+               while( j<4 ){
+                       zResult[j++] = '0';
+               }
+               jx9_result_string(pCtx, zResult, 4);
+       }else{
+         jx9_result_string(pCtx, "?000", 4);
+       }
+       return JX9_OK;
+}
+/*
+ * string wordwrap(string $str[, int $width = 75[, string $break = "\n"]])
+ *  Wraps a string to a given number of characters.
+ * Parameters
+ *  $str
+ *   The input string.
+ * $width
+ *  The column width.
+ * $break
+ *  The line is broken using the optional break parameter.
+ * Return
+ *  Returns the given string wrapped at the specified column. 
+ */
+static int jx9Builtin_wordwrap(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn, *zEnd, *zBreak;
+       int iLen, iBreaklen, iChunk;
+       if( nArg < 1 ){
+               /* Missing arguments, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Extract the input string */
+       zIn = jx9_value_to_string(apArg[0], &iLen);
+       if( iLen < 1 ){
+               /* Nothing to process, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Chunk length */
+       iChunk = 75;
+       iBreaklen = 0;
+       zBreak = ""; /* cc warning */
+       if( nArg > 1 ){
+               iChunk = jx9_value_to_int(apArg[1]);
+               if( iChunk < 1 ){
+                       iChunk = 75;
+               }
+               if( nArg > 2 ){
+                       zBreak = jx9_value_to_string(apArg[2], &iBreaklen);
+               }
+       }
+       if( iBreaklen < 1 ){
+               /* Set a default column break */
+#ifdef __WINNT__
+               zBreak = "\r\n";
+               iBreaklen = (int)sizeof("\r\n")-1;
+#else
+               zBreak = "\n";
+               iBreaklen = (int)sizeof(char);
+#endif
+       }
+       /* Perform the requested operation */
+       zEnd = &zIn[iLen];
+       for(;;){
+               int nMax;
+               if( zIn >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               nMax = (int)(zEnd-zIn);
+               if( iChunk > nMax ){
+                       iChunk = nMax;
+               }
+               /* Append the column first */
+               jx9_result_string(pCtx, zIn, iChunk); /* Will make it's own copy */
+               /* Advance the cursor */
+               zIn += iChunk;
+               if( zIn < zEnd ){
+                       /* Append the line break */
+                       jx9_result_string(pCtx, zBreak, iBreaklen);
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * Check if the given character is a member of the given mask.
+ * Return TRUE on success. FALSE otherwise.
+ * Refer to [strtok()].
+ */
+static int CheckMask(int c, const char *zMask, int nMasklen, int *pOfft)
+{
+       int i;
+       for( i = 0 ; i < nMasklen ; ++i ){
+               if( c == zMask[i] ){
+                       if( pOfft ){
+                               *pOfft = i;
+                       }
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+/*
+ * Extract a single token from the input stream.
+ * Refer to [strtok()].
+ */
+static sxi32 ExtractToken(const char **pzIn, const char *zEnd, const char *zMask, int nMasklen, SyString *pOut)
+{
+       const char *zIn = *pzIn;
+       const char *zPtr;
+       /* Ignore leading delimiter */
+       while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0], zMask, nMasklen, 0) ){
+               zIn++;
+       }
+       if( zIn >= zEnd ){
+               /* End of input */
+               return SXERR_EOF;
+       }
+       zPtr = zIn;
+       /* Extract the token */
+       while( zIn < zEnd ){
+               if( (unsigned char)zIn[0] >= 0xc0 ){
+                       /* UTF-8 stream */
+                       zIn++;
+                       SX_JMP_UTF8(zIn, zEnd);
+               }else{
+                       if( CheckMask(zIn[0], zMask, nMasklen, 0) ){
+                               break;
+                       }
+                       zIn++;
+               }
+       }
+       SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
+       /* Update the cursor */
+       *pzIn = zIn;
+       /* Return to the caller */
+       return SXRET_OK;
+}
+/* strtok auxiliary private data */
+typedef struct strtok_aux_data strtok_aux_data;
+struct strtok_aux_data
+{
+       const char *zDup;  /* Complete duplicate of the input */
+       const char *zIn;   /* Current input stream */
+       const char *zEnd;  /* End of input */
+};
+/*
+ * string strtok(string $str, string $token)
+ * string strtok(string $token)
+ *  strtok() splits a string (str) into smaller strings (tokens), with each token
+ *  being delimited by any character from token. That is, if you have a string like
+ *  "This is an example string" you could tokenize this string into its individual
+ *  words by using the space character as the token.
+ *  Note that only the first call to strtok uses the string argument. Every subsequent
+ *  call to strtok only needs the token to use, as it keeps track of where it is in 
+ *  the current string. To start over, or to tokenize a new string you simply call strtok
+ *  with the string argument again to initialize it. Note that you may put multiple tokens
+ *  in the token parameter. The string will be tokenized when any one of the characters in 
+ *  the argument are found. 
+ * Parameters
+ *  $str
+ *  The string being split up into smaller strings (tokens).
+ * $token
+ *  The delimiter used when splitting up str.
+ * Return
+ *   Current token or FALSE on EOF.
+ */
+static int jx9Builtin_strtok(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       strtok_aux_data *pAux;
+       const char *zMask;
+       SyString sToken; 
+       int nMasklen;
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Extract top aux data */
+               pAux = (strtok_aux_data *)jx9_context_peek_aux_data(pCtx);
+               if( pAux == 0 ){
+                       /* No aux data, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               nMasklen = 0;
+               zMask = ""; /* cc warning */
+               if( nArg > 0 ){
+                       /* Extract the mask */
+                       zMask = jx9_value_to_string(apArg[0], &nMasklen);
+               }
+               if( nMasklen < 1 ){
+                       /* Invalid mask, return FALSE */
+                       jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
+                       jx9_context_free_chunk(pCtx, pAux);
+                       (void)jx9_context_pop_aux_data(pCtx);
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Extract the token */
+               rc = ExtractToken(&pAux->zIn, pAux->zEnd, zMask, nMasklen, &sToken);
+               if( rc != SXRET_OK ){
+                       /* EOF , discard the aux data */
+                       jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
+                       jx9_context_free_chunk(pCtx, pAux);
+                       (void)jx9_context_pop_aux_data(pCtx);
+                       jx9_result_bool(pCtx, 0);
+               }else{
+                       /* Return the extracted token */
+                       jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
+               }
+       }else{
+               const char *zInput, *zCur;
+               char *zDup;
+               int nLen;
+               /* Extract the raw input */
+               zCur = zInput = jx9_value_to_string(apArg[0], &nLen);
+               if( nLen < 1 ){
+                       /* Empty input, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+               /* Extract the mask */
+               zMask = jx9_value_to_string(apArg[1], &nMasklen);
+               if( nMasklen < 1 ){
+                       /* Set a default mask */
+#define TOK_MASK " \n\t\r\f" 
+                       zMask = TOK_MASK;
+                       nMasklen = (int)sizeof(TOK_MASK) - 1;
+#undef TOK_MASK
+               }
+               /* Extract a single token */
+               rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken);
+               if( rc != SXRET_OK ){
+                       /* Empty input */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }else{
+                       /* Return the extracted token */
+                       jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
+               }
+               /* Create our auxilliary data and copy the input */
+               pAux = (strtok_aux_data *)jx9_context_alloc_chunk(pCtx, sizeof(strtok_aux_data), TRUE, FALSE);
+               if( pAux ){
+                       nLen -= (int)(zInput-zCur);
+                       if( nLen < 1 ){
+                               jx9_context_free_chunk(pCtx, pAux);
+                               return JX9_OK;
+                       }
+                       /* Duplicate input */
+                       zDup = (char *)jx9_context_alloc_chunk(pCtx, (unsigned int)(nLen+1), TRUE, FALSE);
+                       if( zDup  ){
+                               SyMemcpy(zInput, zDup, (sxu32)nLen);
+                               /* Register the aux data */
+                               pAux->zDup = pAux->zIn = zDup;
+                               pAux->zEnd = &zDup[nLen];
+                               jx9_context_push_aux_data(pCtx, pAux);
+                       }
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * string str_pad(string $input, int $pad_length[, string $pad_string = " " [, int $pad_type = STR_PAD_RIGHT]])
+ *  Pad a string to a certain length with another string
+ * Parameters
+ *  $input
+ *   The input string.
+ * $pad_length
+ *   If the value of pad_length is negative, less than, or equal to the length of the input 
+ *   string, no padding takes place.
+ * $pad_string
+ *   Note:
+ *    The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly
+ *    divided by the pad_string's length.
+ * $pad_type
+ *    Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type
+ *    is not specified it is assumed to be STR_PAD_RIGHT.
+ * Return
+ *  The padded string.
+ */
+static int jx9Builtin_str_pad(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int iLen, iPadlen, iType, i, iDiv, iStrpad, iRealPad, jPad;
+       const char *zIn, *zPad;
+       if( nArg < 2 ){
+               /* Missing arguments, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn = jx9_value_to_string(apArg[0], &iLen);
+       /* Padding length */
+       iRealPad = iPadlen = jx9_value_to_int(apArg[1]);
+       if( iPadlen > 0 ){
+               iPadlen -= iLen;
+       }
+       if( iPadlen < 1  ){
+               /* Return the string verbatim */
+               jx9_result_string(pCtx, zIn, iLen);
+               return JX9_OK;
+       }
+       zPad = " "; /* Whitespace padding */
+       iStrpad = (int)sizeof(char);
+       iType = 1 ; /* STR_PAD_RIGHT */
+       if( nArg > 2 ){
+               /* Padding string */
+               zPad = jx9_value_to_string(apArg[2], &iStrpad);
+               if( iStrpad < 1 ){
+                       /* Empty string */
+                       zPad = " "; /* Whitespace padding */
+                       iStrpad = (int)sizeof(char);
+               }
+               if( nArg > 3 ){
+                       /* Padd type */
+                       iType = jx9_value_to_int(apArg[3]);
+                       if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){
+                               iType = 1 ; /* STR_PAD_RIGHT */
+                       }
+               }
+       }
+       iDiv = 1;
+       if( iType == 2 ){
+               iDiv = 2; /* STR_PAD_BOTH */
+       }
+       /* Perform the requested operation */
+       if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){
+               jPad = iStrpad;
+               for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){
+                       /* Padding */
+                       if( (int)jx9_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){
+                               break;
+                       }
+                       jx9_result_string(pCtx, zPad, jPad);
+               }
+               if( iType == 0 /* STR_PAD_LEFT */ ){
+                       while( (int)jx9_context_result_buf_length(pCtx) + iLen < iRealPad ){
+                               jPad = iRealPad - (iLen + (int)jx9_context_result_buf_length(pCtx) );
+                               if( jPad > iStrpad ){
+                                       jPad = iStrpad;
+                               }
+                               if( jPad < 1){
+                                       break;
+                               }
+                               jx9_result_string(pCtx, zPad, jPad);
+                       }
+               }
+       }
+       if( iLen > 0 ){
+               /* Append the input string */
+               jx9_result_string(pCtx, zIn, iLen);
+       }
+       if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){
+               for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){
+                       /* Padding */
+                       if( (int)jx9_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){
+                               break;
+                       }
+                       jx9_result_string(pCtx, zPad, iStrpad);
+               }
+               while( (int)jx9_context_result_buf_length(pCtx) < iRealPad ){
+                       jPad = iRealPad - (int)jx9_context_result_buf_length(pCtx);
+                       if( jPad > iStrpad ){
+                               jPad = iStrpad;
+                       }
+                       if( jPad < 1){
+                               break;
+                       }
+                       jx9_result_string(pCtx, zPad, jPad);
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * String replacement private data.
+ */
+typedef struct str_replace_data str_replace_data;
+struct str_replace_data
+{
+       /* The following two fields are only used by the strtr function */
+       SyBlob *pWorker;         /* Working buffer */
+       ProcStringMatch xMatch;  /* Pattern match routine */
+       /* The following two fields are only used by the str_replace function */
+       SySet *pCollector;  /* Argument collector*/
+       jx9_context *pCtx;  /* Call context */
+};
+/*
+ * Remove a substring.
+ */
+#define STRDEL(SRC, SLEN, OFFT, ILEN){\
+       for(;;){\
+               if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\
+       }\
+}
+/*
+ * Shift right and insert algorithm.
+ */
+#define SHIFTRANDINSERT(SRC, LEN, OFFT, ENTRY, ELEN){\
+       sxu32 INLEN = LEN - OFFT;\
+       for(;;){\
+         if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \
+       }\
+       for(;;){\
+               if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\
+       }\
+} 
+/*
+ * Replace all occurrences of the search string at offset (nOfft) with the given 
+ * replacement string [i.e: zReplace].
+ */
+static int StringReplace(SyBlob *pWorker, sxu32 nOfft, int nLen, const char *zReplace, int nReplen)
+{
+       char *zInput = (char *)SyBlobData(pWorker);
+       sxu32 n, m;
+       n = SyBlobLength(pWorker);
+       m = nOfft;
+       /* Delete the old entry */
+       STRDEL(zInput, n, m, nLen);
+       SyBlobLength(pWorker) -= nLen;
+       if( nReplen > 0 ){
+               sxi32 iRep = nReplen;
+               sxi32 rc;
+               /*
+                * Make sure the working buffer is big enough to hold the replacement
+                * string.
+                */
+               rc = SyBlobAppend(pWorker, 0/* Grow without an append operation*/, (sxu32)nReplen);
+               if( rc != SXRET_OK ){
+                       /* Simply ignore any memory failure problem */
+                       return SXRET_OK;
+               }
+               /* Perform the insertion now */
+               zInput = (char *)SyBlobData(pWorker);
+               n = SyBlobLength(pWorker);
+               SHIFTRANDINSERT(zInput, n, nOfft, zReplace, iRep);
+               SyBlobLength(pWorker) += nReplen;
+       }       
+       return SXRET_OK;
+}
+/*
+ * String replacement walker callback.
+ * The following callback is invoked for each array entry that hold
+ * the replace string.
+ * Refer to the strtr() implementation for more information.
+ */
+static int StringReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
+{
+       str_replace_data *pRepData = (str_replace_data *)pUserData;
+       const char *zTarget, *zReplace;
+       SyBlob *pWorker;
+       int tLen, nLen;
+       sxu32 nOfft;
+       sxi32 rc;
+       /* Point to the working buffer */
+       pWorker = pRepData->pWorker;
+       if( !jx9_value_is_string(pKey) ){
+               /* Target and replace must be a string */
+               return JX9_OK;
+       }
+       /* Extract the target and the replace */
+       zTarget = jx9_value_to_string(pKey, &tLen);
+       if( tLen < 1 ){
+               /* Empty target, return immediately */
+               return JX9_OK;
+       }
+       /* Perform a pattern search */
+       rc = pRepData->xMatch(SyBlobData(pWorker), SyBlobLength(pWorker), (const void *)zTarget, (sxu32)tLen, &nOfft);
+       if( rc != SXRET_OK ){
+               /* Pattern not found */
+               return JX9_OK;
+       }
+       /* Extract the replace string */
+       zReplace = jx9_value_to_string(pData, &nLen);
+       /* Perform the replace process */
+       StringReplace(pWorker, nOfft, tLen, zReplace, nLen);
+       /* All done */
+       return JX9_OK;
+}
+/*
+ * The following walker callback is invoked by the str_rplace() function inorder
+ * to collect search/replace string.
+ * This callback is invoked only if the given argument is of type array.
+ */
+static int StrReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
+{
+       str_replace_data *pRep = (str_replace_data *)pUserData;
+       SyString sWorker;
+       const char *zIn;
+       int nByte;
+       /* Extract a string representation of the given argument */
+       zIn = jx9_value_to_string(pData, &nByte);
+       SyStringInitFromBuf(&sWorker, 0, 0);
+       if( nByte > 0 ){
+               char *zDup;
+               /* Duplicate the chunk */
+               zDup = (char *)jx9_context_alloc_chunk(pRep->pCtx, (unsigned int)nByte, FALSE, 
+                       TRUE /* Release the chunk automatically, upon this context is destroyd */
+                       );
+               if( zDup == 0 ){
+                       /* Ignore any memory failure problem */
+                       jx9_context_throw_error(pRep->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
+                       return JX9_OK;
+               }
+               SyMemcpy(zIn, zDup, (sxu32)nByte);
+               /* Save the chunk */
+               SyStringInitFromBuf(&sWorker, zDup, nByte);
+       }
+       /* Save for later processing */
+       SySetPut(pRep->pCollector, (const void *)&sWorker);
+       /* All done */
+       SXUNUSED(pKey); /* cc warning */
+       return JX9_OK;
+}
+/*
+ * mixed str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
+ * mixed str_ireplace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
+ *  Replace all occurrences of the search string with the replacement string.
+ * Parameters
+ *  If search and replace are arrays, then str_replace() takes a value from each
+ *  array and uses them to search and replace on subject. If replace has fewer values
+ *  than search, then an empty string is used for the rest of replacement values.
+ *  If search is an array and replace is a string, then this replacement string is used
+ *  for every value of search. The converse would not make sense, though.
+ *  If search or replace are arrays, their elements are processed first to last.
+ * $search
+ *  The value being searched for, otherwise known as the needle. An array may be used
+ *  to designate multiple needles.
+ * $replace
+ *  The replacement value that replaces found search values. An array may be used
+ *  to designate multiple replacements.
+ * $subject
+ *  The string or array being searched and replaced on, otherwise known as the haystack.
+ *  If subject is an array, then the search and replace is performed with every entry 
+ *  of subject, and the return value is an array as well.
+ * $count (Not used)
+ *  If passed, this will be set to the number of replacements performed.
+ * Return
+ * This function returns a string or an array with the replaced values.
+ */
+static int jx9Builtin_str_replace(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SyString sTemp, *pSearch, *pReplace;
+       ProcStringMatch xMatch;
+       const char *zIn, *zFunc;
+       str_replace_data sRep;
+       SyBlob sWorker;
+       SySet sReplace;
+       SySet sSearch;
+       int rep_str;
+       int nByte;
+       sxi32 rc;
+       if( nArg < 3 ){
+               /* Missing/Invalid arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Initialize fields */
+       SySetInit(&sSearch, &pCtx->pVm->sAllocator, sizeof(SyString));
+       SySetInit(&sReplace, &pCtx->pVm->sAllocator, sizeof(SyString));
+       SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
+       SyZero(&sRep, sizeof(str_replace_data));
+       sRep.pCtx = pCtx;
+       sRep.pCollector = &sSearch;
+       rep_str = 0;
+       /* Extract the subject */
+       zIn = jx9_value_to_string(apArg[2], &nByte);
+       if( nByte < 1 ){
+               /* Nothing to replace, return the empty string */
+               jx9_result_string(pCtx, "", 0);
+               return JX9_OK;
+       }
+       /* Copy the subject */
+       SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nByte);
+       /* Search string */
+       if( jx9_value_is_json_array(apArg[0]) ){
+               /* Collect search string */
+               jx9_array_walk(apArg[0], StrReplaceWalker, &sRep);
+       }else{
+               /* Single pattern */
+               zIn = jx9_value_to_string(apArg[0], &nByte);
+               if( nByte < 1 ){
+                       /* Return the subject untouched since no search string is available */
+                       jx9_result_value(pCtx, apArg[2]/* Subject as thrird argument*/);
+                       return JX9_OK;
+               }
+               SyStringInitFromBuf(&sTemp, zIn, nByte);
+               /* Save for later processing */
+               SySetPut(&sSearch, (const void *)&sTemp);
+       }
+       /* Replace string */
+       if( jx9_value_is_json_array(apArg[1]) ){
+               /* Collect replace string */
+               sRep.pCollector = &sReplace;
+               jx9_array_walk(apArg[1], StrReplaceWalker, &sRep);
+       }else{
+               /* Single needle */
+               zIn = jx9_value_to_string(apArg[1], &nByte);
+               rep_str = 1;
+               SyStringInitFromBuf(&sTemp, zIn, nByte);
+               /* Save for later processing */
+               SySetPut(&sReplace, (const void *)&sTemp);
+       }
+       /* Reset loop cursors */
+       SySetResetCursor(&sSearch);
+       SySetResetCursor(&sReplace);
+       pReplace = pSearch = 0; /* cc warning */
+       SyStringInitFromBuf(&sTemp, "", 0);
+       /* Extract function name */
+       zFunc = jx9_function_name(pCtx);
+       /* Set the default pattern match routine */
+       xMatch = SyBlobSearch;
+       if( SyStrncmp(zFunc, "str_ireplace", sizeof("str_ireplace") - 1) ==  0 ){
+               /* Case insensitive pattern match */
+               xMatch = iPatternMatch;
+       }
+       /* Start the replace process */
+       while( SXRET_OK == SySetGetNextEntry(&sSearch, (void **)&pSearch) ){
+               sxu32 nCount, nOfft;
+               if( pSearch->nByte <  1 ){
+                       /* Empty string, ignore */
+                       continue;
+               }
+               /* Extract the replace string */
+               if( rep_str ){
+                       pReplace = (SyString *)SySetPeek(&sReplace);
+               }else{
+                       if( SXRET_OK != SySetGetNextEntry(&sReplace, (void **)&pReplace) ){
+                               /* Sepecial case when 'replace set' has fewer values than the search set.
+                                * An empty string is used for the rest of replacement values
+                                */
+                               pReplace = 0;
+                       }
+               }
+               if( pReplace == 0 ){
+                       /* Use an empty string instead */
+                       pReplace = &sTemp;
+               }
+               nOfft = nCount = 0;
+               for(;;){
+                       if( nCount >= SyBlobLength(&sWorker) ){
+                               break;
+                       }
+                       /* Perform a pattern lookup */
+                       rc = xMatch(SyBlobDataAt(&sWorker, nCount), SyBlobLength(&sWorker) - nCount, (const void *)pSearch->zString, 
+                               pSearch->nByte, &nOfft);
+                       if( rc != SXRET_OK ){
+                               /* Pattern not found */
+                               break;
+                       }
+                       /* Perform the replace operation */
+                       StringReplace(&sWorker, nCount+nOfft, (int)pSearch->nByte, pReplace->zString, (int)pReplace->nByte);
+                       /* Increment offset counter */
+                       nCount += nOfft + pReplace->nByte;
+               }
+       }
+       /* All done, clean-up the mess left behind */
+       jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker));
+       SySetRelease(&sSearch);
+       SySetRelease(&sReplace);
+       SyBlobRelease(&sWorker);
+       return JX9_OK;
+}
+/*
+ * string strtr(string $str, string $from, string $to)
+ * string strtr(string $str, array $replace_pairs)
+ *  Translate characters or replace substrings.
+ * Parameters
+ *  $str
+ *  The string being translated.
+ * $from
+ *  The string being translated to to.
+ * $to
+ *  The string replacing from.
+ * $replace_pairs
+ *  The replace_pairs parameter may be used instead of to and 
+ *  from, in which case it's an array in the form array('from' => 'to', ...).
+ * Return
+ *  The translated string.
+ *  If replace_pairs contains a key which is an empty string (""), FALSE will be returned.
+ */
+static int jx9Builtin_strtr(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn;
+       int nLen;
+       if( nArg < 1 ){
+               /* Nothing to replace, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 || nArg < 2 ){
+               /* Invalid arguments */
+               jx9_result_string(pCtx, zIn, nLen);
+               return JX9_OK;
+       }
+       if( nArg == 2 && jx9_value_is_json_array(apArg[1]) ){
+               str_replace_data sRepData;
+               SyBlob sWorker;
+               /* Initilaize the working buffer */
+               SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
+               /* Copy raw string */
+               SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nLen);
+               /* Init our replace data instance */
+               sRepData.pWorker = &sWorker;
+               sRepData.xMatch = SyBlobSearch;
+               /* Iterate throw array entries and perform the replace operation.*/
+               jx9_array_walk(apArg[1], StringReplaceWalker, &sRepData);
+               /* All done, return the result string */
+               jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), 
+                       (int)SyBlobLength(&sWorker)); /* Will make it's own copy */
+               /* Clean-up */
+               SyBlobRelease(&sWorker);
+       }else{
+               int i, flen, tlen, c, iOfft;
+               const char *zFrom, *zTo;
+               if( nArg < 3 ){
+                       /* Nothing to replace */
+                       jx9_result_string(pCtx, zIn, nLen);
+                       return JX9_OK;
+               }
+               /* Extract given arguments */
+               zFrom = jx9_value_to_string(apArg[1], &flen);
+               zTo = jx9_value_to_string(apArg[2], &tlen);
+               if( flen < 1 || tlen < 1 ){
+                       /* Nothing to replace */
+                       jx9_result_string(pCtx, zIn, nLen);
+                       return JX9_OK;
+               }
+               /* Start the replace process */
+               for( i = 0 ; i < nLen ; ++i ){
+                       c = zIn[i];
+                       if( CheckMask(c, zFrom, flen, &iOfft) ){
+                               if ( iOfft < tlen ){
+                                       c = zTo[iOfft];
+                               }
+                       }
+                       jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
+                       
+               }
+       }
+       return JX9_OK;
+}
+/*
+ * Parse an INI string.
+ * According to wikipedia
+ *  The INI file format is an informal standard for configuration files for some platforms or software.
+ *  INI files are simple text files with a basic structure composed of "sections" and "properties".
+ *  Format
+*    Properties
+*     The basic element contained in an INI file is the property. Every property has a name and a value
+*     delimited by an equals sign (=). The name appears to the left of the equals sign.
+*     Example:
+*      name=value
+*    Sections
+*     Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself
+*     in square brackets ([ and ]). All properties after the section declaration are associated with that section.
+*     There is no explicit "end of section" delimiter; sections end at the next section declaration
+*     or the end of the file. Sections may not be nested.
+*     Example:
+*      [section]
+*   Comments
+*    Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
+* This function return an array holding parsed values on success.FALSE otherwise.
+*/
+JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection)
+{
+       jx9_value *pCur, *pArray, *pSection, *pWorker, *pValue;
+       const char *zCur, *zEnd = &zIn[nByte];
+       SyHashEntry *pEntry;
+       SyString sEntry;
+       SyHash sHash;
+       int c;
+       /* Create an empty array and worker variables */
+       pArray = jx9_context_new_array(pCtx);
+       pWorker = jx9_context_new_scalar(pCtx);
+       pValue = jx9_context_new_scalar(pCtx);
+       if( pArray == 0 || pWorker == 0 || pValue == 0){
+               /* Out of memory */
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
+               /* Return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0);
+       pCur = pArray;
+       /* Start the parse process */
+       for(;;){
+               /* Ignore leading white spaces */
+               while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){
+                       zIn++;
+               }
+               if( zIn >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               if( zIn[0] == ';' || zIn[0] == '#' ){
+                       /* Comment til the end of line */
+                       zIn++;
+                       while(zIn < zEnd && zIn[0] != '\n' ){
+                               zIn++;
+                       }
+                       continue;
+               }
+               /* Reset the string cursor of the working variable */
+               jx9_value_reset_string_cursor(pWorker);
+               if( zIn[0] == '[' ){
+                       /* Section: Extract the section name */
+                       zIn++;
+                       zCur = zIn;
+                       while( zIn < zEnd && zIn[0] != ']' ){
+                               zIn++;
+                       }
+                       if( zIn > zCur && bProcessSection ){
+                               /* Save the section name */
+                               SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
+                               SyStringFullTrim(&sEntry);
+                               jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
+                               if( sEntry.nByte > 0 ){
+                                       /* Associate an array with the section */
+                                       pSection = jx9_context_new_array(pCtx);
+                                       if( pSection ){
+                                               jx9_array_add_elem(pArray, pWorker/*Section name*/, pSection);
+                                               pCur = pSection;
+                                       }
+                               }
+                       }
+                       zIn++; /* Trailing square brackets ']' */
+               }else{
+                       jx9_value *pOldCur;
+                       int is_array;
+                       int iLen;
+                       /* Properties */
+                       is_array = 0;
+                       zCur = zIn;
+                       iLen = 0; /* cc warning */
+                       pOldCur = pCur;
+                       while( zIn < zEnd && zIn[0] != '=' ){
+                               if( zIn[0] == '[' && !is_array ){
+                                       /* Array */
+                                       iLen = (int)(zIn-zCur);
+                                       is_array = 1;
+                                       if( iLen > 0 ){
+                                               jx9_value *pvArr = 0; /* cc warning */
+                                               /* Query the hashtable */
+                                               SyStringInitFromBuf(&sEntry, zCur, iLen);
+                                               SyStringFullTrim(&sEntry);
+                                               pEntry = SyHashGet(&sHash, (const void *)sEntry.zString, sEntry.nByte);
+                                               if( pEntry ){
+                                                       pvArr = (jx9_value *)SyHashEntryGetUserData(pEntry);
+                                               }else{
+                                                       /* Create an empty array */
+                                                       pvArr = jx9_context_new_array(pCtx);
+                                                       if( pvArr ){
+                                                               /* Save the entry */
+                                                               SyHashInsert(&sHash, (const void *)sEntry.zString, sEntry.nByte, pvArr);
+                                                               /* Insert the entry */
+                                                               jx9_value_reset_string_cursor(pWorker);
+                                                               jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
+                                                               jx9_array_add_elem(pCur, pWorker, pvArr);
+                                                               jx9_value_reset_string_cursor(pWorker);
+                                                       }
+                                               }
+                                               if( pvArr ){
+                                                       pCur = pvArr;
+                                               }
+                                       }
+                                       while ( zIn < zEnd && zIn[0] != ']' ){
+                                               zIn++;
+                                       }
+                               }
+                               zIn++;
+                       }
+                       if( !is_array ){
+                               iLen = (int)(zIn-zCur);
+                       }
+                       /* Trim the key */
+                       SyStringInitFromBuf(&sEntry, zCur, iLen);
+                       SyStringFullTrim(&sEntry);
+                       if( sEntry.nByte > 0 ){
+                               if( !is_array ){
+                                       /* Save the key name */
+                                       jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
+                               }
+                               /* extract key value */
+                               jx9_value_reset_string_cursor(pValue);
+                               zIn++; /* '=' */
+                               while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
+                                       zIn++;
+                               }
+                               if( zIn < zEnd ){
+                                       zCur = zIn;
+                                       c = zIn[0];
+                                       if( c == '"' || c == '\'' ){
+                                               zIn++;
+                                               /* Delimit the value */
+                                               while( zIn < zEnd ){
+                                                       if ( zIn[0] == c && zIn[-1] != '\\' ){
+                                                               break;
+                                                       }
+                                                       zIn++;
+                                               }
+                                               if( zIn < zEnd ){
+                                                       zIn++;
+                                               }
+                                       }else{
+                                               while( zIn < zEnd ){
+                                                       if( zIn[0] == '\n' ){
+                                                               if( zIn[-1] != '\\' ){
+                                                                       break;
+                                                               }
+                                                       }else if( zIn[0] == ';' || zIn[0] == '#' ){
+                                                               /* Inline comments */
+                                                               break;
+                                                       }
+                                                       zIn++;
+                                               }
+                                       }
+                                       /* Trim the value */
+                                       SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
+                                       SyStringFullTrim(&sEntry);
+                                       if( c == '"' || c == '\'' ){
+                                               SyStringTrimLeadingChar(&sEntry, c);
+                                               SyStringTrimTrailingChar(&sEntry, c);
+                                       }
+                                       if( sEntry.nByte > 0 ){
+                                               jx9_value_string(pValue, sEntry.zString, (int)sEntry.nByte);
+                                       }
+                                       /* Insert the key and it's value */
+                                       jx9_array_add_elem(pCur, is_array ? 0 /*Automatic index assign */: pWorker, pValue);
+                               }
+                       }else{
+                               while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){
+                                       zIn++;
+                               }
+                       }
+                       pCur = pOldCur;
+               }
+       }
+       SyHashRelease(&sHash);
+       /* Return the parse of the INI string */
+       jx9_result_value(pCtx, pArray);
+       return SXRET_OK;
+}
+/*
+ * array parse_ini_string(string $ini[, bool $process_sections = false[, int $scanner_mode = INI_SCANNER_NORMAL ]])
+ *  Parse a configuration string.
+ * Parameters
+ *  $ini
+ *   The contents of the ini file being parsed.
+ *  $process_sections
+ *   By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names
+ *   and settings included. The default for process_sections is FALSE.
+ *  $scanner_mode (Not used)
+ *   Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied
+ *   then option values will not be parsed.
+ * Return
+ *  The settings are returned as an associative array on success, and FALSE on failure.
+ */
+static int jx9Builtin_parse_ini_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIni;
+       int nByte;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid arguments, return FALSE*/
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the raw INI buffer */
+       zIni = jx9_value_to_string(apArg[0], &nByte);
+       /* Process the INI buffer*/
+       jx9ParseIniString(pCtx, zIni, (sxu32)nByte, (nArg > 1) ? jx9_value_to_bool(apArg[1]) : 0);
+       return JX9_OK;
+}
+/*
+ * Ctype Functions.
+ * Authors:
+ *    Symisc Systems, devel@symisc.net.
+ *    Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Stable.
+ */
+/*
+ * bool ctype_alnum(string $text)
+ *  Checks if all of the characters in the provided string, text, are alphanumeric.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *   TRUE if every character in text is either a letter or a digit, FALSE otherwise.
+ */
+static int jx9Builtin_ctype_alnum(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( !SyisAlphaNum(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_alpha(string $text)
+ *  Checks if all of the characters in the provided string, text, are alphabetic.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  TRUE if every character in text is a letter from the current locale, FALSE otherwise.
+ */
+static int jx9Builtin_ctype_alpha(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( !SyisAlpha(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_cntrl(string $text)
+ *  Checks if all of the characters in the provided string, text, are control characters.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  TRUE if every character in text is a control characters, FALSE otherwise.
+ */
+static int jx9Builtin_ctype_cntrl(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( zIn[0] >= 0xc0 ){
+                       /* UTF-8 stream  */
+                       break;
+               }
+               if( !SyisCtrl(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_digit(string $text)
+ *  Checks if all of the characters in the provided string, text, are numerical.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  TRUE if every character in the string text is a decimal digit, FALSE otherwise.
+ */
+static int jx9Builtin_ctype_digit(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( zIn[0] >= 0xc0 ){
+                       /* UTF-8 stream  */
+                       break;
+               }
+               if( !SyisDigit(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_xdigit(string $text)
+ *  Check for character(s) representing a hexadecimal digit.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  Returns TRUE if every character in text is a hexadecimal 'digit', that is
+ * a decimal digit or a character from [A-Fa-f] , FALSE otherwise. 
+ */
+static int jx9Builtin_ctype_xdigit(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( zIn[0] >= 0xc0 ){
+                       /* UTF-8 stream  */
+                       break;
+               }
+               if( !SyisHex(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_graph(string $text)
+ *  Checks if all of the characters in the provided string, text, creates visible output.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  Returns TRUE if every character in text is printable and actually creates visible output
+ * (no white space), FALSE otherwise. 
+ */
+static int jx9Builtin_ctype_graph(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( zIn[0] >= 0xc0 ){
+                       /* UTF-8 stream  */
+                       break;
+               }
+               if( !SyisGraph(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_print(string $text)
+ *  Checks if all of the characters in the provided string, text, are printable.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  Returns TRUE if every character in text will actually create output (including blanks).
+ *  Returns FALSE if text contains control characters or characters that do not have any output
+ *  or control function at all. 
+ */
+static int jx9Builtin_ctype_print(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( zIn[0] >= 0xc0 ){
+                       /* UTF-8 stream  */
+                       break;
+               }
+               if( !SyisPrint(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_punct(string $text)
+ *  Checks if all of the characters in the provided string, text, are punctuation character.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  Returns TRUE if every character in text is printable, but neither letter
+ *  digit or blank, FALSE otherwise.
+ */
+static int jx9Builtin_ctype_punct(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( zIn[0] >= 0xc0 ){
+                       /* UTF-8 stream  */
+                       break;
+               }
+               if( !SyisPunct(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_space(string $text)
+ *  Checks if all of the characters in the provided string, text, creates whitespace.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  Returns TRUE if every character in text creates some sort of white space, FALSE otherwise.
+ *  Besides the blank character this also includes tab, vertical tab, line feed, carriage return
+ *  and form feed characters. 
+ */
+static int jx9Builtin_ctype_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( zIn[0] >= 0xc0 ){
+                       /* UTF-8 stream  */
+                       break;
+               }
+               if( !SyisSpace(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_lower(string $text)
+ *  Checks if all of the characters in the provided string, text, are lowercase letters.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  Returns TRUE if every character in text is a lowercase letter in the current locale. 
+ */
+static int jx9Builtin_ctype_lower(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( !SyisLower(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * bool ctype_upper(string $text)
+ *  Checks if all of the characters in the provided string, text, are uppercase letters.
+ * Parameters
+ *  $text
+ *   The tested string.
+ * Return
+ *  Returns TRUE if every character in text is a uppercase letter in the current locale. 
+ */
+static int jx9Builtin_ctype_upper(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const unsigned char *zIn, *zEnd;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the target string */
+       zIn  = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
+       zEnd = &zIn[nLen];
+       if( nLen < 1 ){
+               /* Empty string, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* If we reach the end of the string, then the test succeeded. */
+                       jx9_result_bool(pCtx, 1);
+                       return JX9_OK;
+               }
+               if( !SyisUpper(zIn[0]) ){
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       /* The test failed, return FALSE */
+       jx9_result_bool(pCtx, 0);
+       return JX9_OK;
+}
+/*
+ * Date/Time functions
+ * Authors:
+ *    Symisc Systems, devel@symisc.net.
+ *    Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Devel.
+ */
+#include <time.h>
+#ifdef __WINNT__
+/* GetSystemTime() */
+#include <Windows.h> 
+#ifdef _WIN32_WCE
+/*
+** WindowsCE does not have a localtime() function.  So create a
+** substitute.
+** Taken from the SQLite3 source tree.
+** Status: Public domain
+*/
+struct tm *__cdecl localtime(const time_t *t)
+{
+  static struct tm y;
+  FILETIME uTm, lTm;
+  SYSTEMTIME pTm;
+  jx9_int64 t64;
+  t64 = *t;
+  t64 = (t64 + 11644473600)*10000000;
+  uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
+  uTm.dwHighDateTime= (DWORD)(t64 >> 32);
+  FileTimeToLocalFileTime(&uTm, &lTm);
+  FileTimeToSystemTime(&lTm, &pTm);
+  y.tm_year = pTm.wYear - 1900;
+  y.tm_mon = pTm.wMonth - 1;
+  y.tm_wday = pTm.wDayOfWeek;
+  y.tm_mday = pTm.wDay;
+  y.tm_hour = pTm.wHour;
+  y.tm_min = pTm.wMinute;
+  y.tm_sec = pTm.wSecond;
+  return &y;
+}
+#endif /*_WIN32_WCE */
+#elif defined(__UNIXES__)
+#include <sys/time.h>
+#endif /* __WINNT__*/
+ /*
+  * int64 time(void)
+  *  Current Unix timestamp
+  * Parameters
+  *  None.
+  * Return
+  *  Returns the current time measured in the number of seconds
+  *  since the Unix Epoch (January 1 1970 00:00:00 GMT).
+  */
+static int jx9Builtin_time(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       time_t tt;
+       SXUNUSED(nArg); /* cc warning */
+       SXUNUSED(apArg);
+       /* Extract the current time */
+       time(&tt);
+       /* Return as 64-bit integer */
+       jx9_result_int64(pCtx, (jx9_int64)tt);
+       return  JX9_OK;
+}
+/*
+  * string/float microtime([ bool $get_as_float = false ])
+  *  microtime() returns the current Unix timestamp with microseconds.
+  * Parameters
+  *  $get_as_float
+  *   If used and set to TRUE, microtime() will return a float instead of a string
+  *   as described in the return values section below.
+  * Return
+  *  By default, microtime() returns a string in the form "msec sec", where sec 
+  *  is the current time measured in the number of seconds since the Unix 
+  *  epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds
+  *  that have elapsed since sec expressed in seconds.
+  *  If get_as_float is set to TRUE, then microtime() returns a float, which represents
+  *  the current time in seconds since the Unix epoch accurate to the nearest microsecond. 
+  */
+static int jx9Builtin_microtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int bFloat = 0;
+       sytime sTime;   
+#if defined(__UNIXES__)
+       struct timeval tv;
+       gettimeofday(&tv, 0);
+       sTime.tm_sec  = (long)tv.tv_sec;
+       sTime.tm_usec = (long)tv.tv_usec;
+#else
+       time_t tt;
+       time(&tt);
+       sTime.tm_sec  = (long)tt;
+       sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
+#endif /* __UNIXES__ */
+       if( nArg > 0 ){
+               bFloat = jx9_value_to_bool(apArg[0]);
+       }
+       if( bFloat ){
+               /* Return as float */
+               jx9_result_double(pCtx, (double)sTime.tm_sec);
+       }else{
+               /* Return as string */
+               jx9_result_string_format(pCtx, "%ld %ld", sTime.tm_usec, sTime.tm_sec);
+       }
+       return JX9_OK;
+}
+/*
+ * array getdate ([ int $timestamp = time() ])
+ *  Get date/time information.
+ * Parameter
+ *  $timestamp: The optional timestamp parameter is an integer Unix timestamp
+ *     that defaults to the current local time if a timestamp is not given.
+ *     In other words, it defaults to the value of time().
+ * Returns
+ *  Returns an associative array of information related to the timestamp.
+ *  Elements from the returned associative array are as follows: 
+ *   KEY                                                         VALUE
+ * ---------                                                    -------
+ * "seconds"   Numeric representation of seconds                   0 to 59
+ * "minutes"   Numeric representation of minutes                   0 to 59
+ * "hours"         Numeric representation of hours                 0 to 23
+ * "mday"          Numeric representation of the day of the month      1 to 31
+ * "wday"          Numeric representation of the day of the week       0 (for Sunday) through 6 (for Saturday)
+ * "mon"           Numeric representation of a month               1 through 12
+ * "year"          A full numeric representation of a year,        4 digits    Examples: 1999 or 2003
+ * "yday"          Numeric representation of the day of the year   0 through 365
+ * "weekday"   A full textual representation of the day of the week    Sunday through Saturday
+ * "month"         A full textual representation of a month, such as January or March  January through December
+ * 0           Seconds since the Unix Epoch, similar to the values returned by time() and used by date(). 
+ * NOTE:
+ *   NULL is returned on failure.
+ */
+static int jx9Builtin_getdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_value *pValue, *pArray;
+       Sytm sTm;
+       if( nArg < 1 ){
+#ifdef __WINNT__
+               SYSTEMTIME sOS;
+               GetSystemTime(&sOS);
+               SYSTEMTIME_TO_SYTM(&sOS, &sTm);
+#else
+               struct tm *pTm;
+               time_t t;
+               time(&t);
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+#endif
+       }else{
+               /* Use the given timestamp */
+               time_t t;
+               struct tm *pTm;
+#ifdef __WINNT__
+#ifdef _MSC_VER
+#if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
+#pragma warning(disable:4996) /* _CRT_SECURE...*/
+#endif
+#endif
+#endif
+               if( jx9_value_is_int(apArg[0]) ){
+                       t = (time_t)jx9_value_to_int64(apArg[0]);
+                       pTm = localtime(&t);
+                       if( pTm == 0 ){
+                               time(&t);
+                       }
+               }else{
+                       time(&t);
+               }
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+       }
+       /* Element value */
+       pValue = jx9_context_new_scalar(pCtx);
+       if( pValue == 0 ){
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Fill the array */
+       /* Seconds */
+       jx9_value_int(pValue, sTm.tm_sec);
+       jx9_array_add_strkey_elem(pArray, "seconds", pValue);
+       /* Minutes */
+       jx9_value_int(pValue, sTm.tm_min);
+       jx9_array_add_strkey_elem(pArray, "minutes", pValue);
+       /* Hours */
+       jx9_value_int(pValue, sTm.tm_hour);
+       jx9_array_add_strkey_elem(pArray, "hours", pValue);
+       /* mday */
+       jx9_value_int(pValue, sTm.tm_mday);
+       jx9_array_add_strkey_elem(pArray, "mday", pValue);
+       /* wday */
+       jx9_value_int(pValue, sTm.tm_wday);
+       jx9_array_add_strkey_elem(pArray, "wday", pValue);
+       /* mon */
+       jx9_value_int(pValue, sTm.tm_mon+1);
+       jx9_array_add_strkey_elem(pArray, "mon", pValue);
+       /* year */
+       jx9_value_int(pValue, sTm.tm_year);
+       jx9_array_add_strkey_elem(pArray, "year", pValue);
+       /* yday */
+       jx9_value_int(pValue, sTm.tm_yday);
+       jx9_array_add_strkey_elem(pArray, "yday", pValue);
+       /* Weekday */
+       jx9_value_string(pValue, SyTimeGetDay(sTm.tm_wday), -1);
+       jx9_array_add_strkey_elem(pArray, "weekday", pValue);
+       /* Month */
+       jx9_value_reset_string_cursor(pValue);
+       jx9_value_string(pValue, SyTimeGetMonth(sTm.tm_mon), -1);
+       jx9_array_add_strkey_elem(pArray, "month", pValue);
+       /* Seconds since the epoch */
+       jx9_value_int64(pValue, (jx9_int64)time(0));
+       jx9_array_add_elem(pArray, 0 /* Index zero */, pValue);
+       /* Return the freshly created array */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * mixed gettimeofday([ bool $return_float = false ] )
+ *   Returns an associative array containing the data returned from the system call.
+ * Parameters
+ *  $return_float
+ *   When set to TRUE, a float instead of an array is returned.
+ * Return
+ *   By default an array is returned. If return_float is set, then
+ *   a float is returned. 
+ */
+static int jx9Builtin_gettimeofday(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int bFloat = 0;
+       sytime sTime;
+#if defined(__UNIXES__)
+       struct timeval tv;
+       gettimeofday(&tv, 0);
+       sTime.tm_sec  = (long)tv.tv_sec;
+       sTime.tm_usec = (long)tv.tv_usec;
+#else
+       time_t tt;
+       time(&tt);
+       sTime.tm_sec  = (long)tt;
+       sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
+#endif /* __UNIXES__ */
+       if( nArg > 0 ){
+               bFloat = jx9_value_to_bool(apArg[0]);
+       }
+       if( bFloat ){
+               /* Return as float */
+               jx9_result_double(pCtx, (double)sTime.tm_sec);
+       }else{
+               /* Return an associative array */
+               jx9_value *pValue, *pArray;
+               /* Create a new array */
+               pArray = jx9_context_new_array(pCtx);
+               /* Element value */
+               pValue = jx9_context_new_scalar(pCtx);
+               if( pValue == 0 || pArray == 0 ){
+                       /* Return NULL */
+                       jx9_result_null(pCtx);
+                       return JX9_OK;
+               }
+               /* Fill the array */
+               /* sec */
+               jx9_value_int64(pValue, sTime.tm_sec);
+               jx9_array_add_strkey_elem(pArray, "sec", pValue);
+               /* usec */
+               jx9_value_int64(pValue, sTime.tm_usec);
+               jx9_array_add_strkey_elem(pArray, "usec", pValue);
+               /* Return the array */
+               jx9_result_value(pCtx, pArray);
+       }
+       return JX9_OK;
+}
+/* Check if the given year is leap or not */
+#define IS_LEAP_YEAR(YEAR)     (YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1)
+/* ISO-8601 numeric representation of the day of the week */
+static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
+/*
+ * Format a given date string.
+ * Supported format: (Taken from JX9 online docs)
+ * character   Description
+ * d          Day of the month 
+ * D          A textual representation of a days
+ * j          Day of the month without leading zeros
+ * l          A full textual representation of the day of the week     
+ * N          ISO-8601 numeric representation of the day of the week 
+ * w          Numeric representation of the day of the week
+ * z          The day of the year (starting from 0)    
+ * F          A full textual representation of a month, such as January or March
+ * m          Numeric representation of a month, with leading zeros    01 through 12
+ * M          A short textual representation of a month, three letters         Jan through Dec
+ * n          Numeric representation of a month, without leading zeros         1 through 12
+ * t          Number of days in the given month        28 through 31
+ * L          Whether it's a leap year         1 if it is a leap year, 0 otherwise.
+ * o          ISO-8601 year number. This has the same value as Y, except that if the ISO week number
+ *            (W) belongs to the previous or next year, that year is used instead. (added in JX9 5.1.0) Examples: 1999 or 2003
+ * Y          A full numeric representation of a year, 4 digits        Examples: 1999 or 2003
+ * y          A two digit representation of a year     Examples: 99 or 03
+ * a          Lowercase Ante meridiem and Post meridiem        am or pm
+ * A          Uppercase Ante meridiem and Post meridiem        AM or PM
+ * g          12-hour format of an hour without leading zeros  1 through 12
+ * G          24-hour format of an hour without leading zeros  0 through 23
+ * h          12-hour format of an hour with leading zeros     01 through 12
+ * H          24-hour format of an hour with leading zeros     00 through 23
+ * i          Minutes with leading zeros       00 to 59
+ * s          Seconds, with leading zeros      00 through 59
+ * u          Microseconds Example: 654321
+ * e          Timezone identifier      Examples: UTC, GMT, Atlantic/Azores
+ * I          (capital i) Whether or not the date is in daylight saving time   1 if Daylight Saving Time, 0 otherwise.
+ * r          RFC 2822 formatted date  Example: Thu, 21 Dec 2000 16:01:07 +0200
+ * U          Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
+ * S          English ordinal suffix for the day of the month, 2 characters
+ * O          Difference to Greenwich time (GMT) in hours
+ * Z          Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those
+ *            east of UTC is always positive.
+ * c         ISO 8601 date
+ */
+static sxi32 DateFormat(jx9_context *pCtx, const char *zIn, int nLen, Sytm *pTm)
+{
+       const char *zEnd = &zIn[nLen];
+       const char *zCur;
+       /* Start the format process */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               switch(zIn[0]){
+               case 'd':
+                       /* Day of the month, 2 digits with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_mday);
+                       break;
+               case 'D':
+                       /*A textual representation of a day, three letters*/
+                       zCur = SyTimeGetDay(pTm->tm_wday);
+                       jx9_result_string(pCtx, zCur, 3);
+                       break;
+               case 'j':
+                       /*      Day of the month without leading zeros */
+                       jx9_result_string_format(pCtx, "%d", pTm->tm_mday);
+                       break;
+               case 'l':
+                       /* A full textual representation of the day of the week */
+                       zCur = SyTimeGetDay(pTm->tm_wday);
+                       jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
+                       break;
+               case 'N':{
+                       /* ISO-8601 numeric representation of the day of the week */
+                       jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
+                       break;
+                                }
+               case 'w':
+                       /*Numeric representation of the day of the week*/
+                       jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
+                       break;
+               case 'z':
+                       /*The day of the year*/
+                       jx9_result_string_format(pCtx, "%d", pTm->tm_yday);
+                       break;
+               case 'F':
+                       /*A full textual representation of a month, such as January or March*/
+                       zCur = SyTimeGetMonth(pTm->tm_mon);
+                       jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
+                       break;
+               case 'm':
+                       /*Numeric representation of a month, with leading zeros*/
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
+                       break;
+               case 'M':
+                       /*A short textual representation of a month, three letters*/
+                       zCur = SyTimeGetMonth(pTm->tm_mon);
+                       jx9_result_string(pCtx, zCur, 3);
+                       break;
+               case 'n':
+                       /*Numeric representation of a month, without leading zeros*/
+                       jx9_result_string_format(pCtx, "%d", pTm->tm_mon + 1);
+                       break;
+               case 't':{
+                       static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+                       int nDays = aMonDays[pTm->tm_mon % 12 ];
+                       if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){
+                               nDays = 28;
+                       }
+                       /*Number of days in the given month*/
+                       jx9_result_string_format(pCtx, "%d", nDays);
+                       break;
+                                }
+               case 'L':{
+                       int isLeap = IS_LEAP_YEAR(pTm->tm_year);
+                       /* Whether it's a leap year */
+                       jx9_result_string_format(pCtx, "%d", isLeap);
+                       break;
+                                }
+               case 'o':
+                       /* ISO-8601 year number.*/
+                       jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
+                       break;
+               case 'Y':
+                       /*      A full numeric representation of a year, 4 digits */
+                       jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
+                       break;
+               case 'y':
+                       /*A two digit representation of a year*/
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_year%100);
+                       break;
+               case 'a':
+                       /*      Lowercase Ante meridiem and Post meridiem */
+                       jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", 2);
+                       break;
+               case 'A':
+                       /*      Uppercase Ante meridiem and Post meridiem */
+                       jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", 2);
+                       break;
+               case 'g':
+                       /*      12-hour format of an hour without leading zeros*/
+                       jx9_result_string_format(pCtx, "%d", 1+(pTm->tm_hour%12));
+                       break;
+               case 'G':
+                       /* 24-hour format of an hour without leading zeros */
+                       jx9_result_string_format(pCtx, "%d", pTm->tm_hour);
+                       break;
+               case 'h':
+                       /* 12-hour format of an hour with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
+                       break;
+               case 'H':
+                       /*      24-hour format of an hour with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
+                       break;
+               case 'i':
+                       /*      Minutes with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
+                       break;
+               case 's':
+                       /*      second with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
+                       break;
+               case 'u':
+                       /*      Microseconds */
+                       jx9_result_string_format(pCtx, "%u", pTm->tm_sec * SX_USEC_PER_SEC);
+                       break;
+               case 'S':{
+                       /* English ordinal suffix for the day of the month, 2 characters */
+                       static const char zSuffix[] = "thstndrdthththththth";
+                       int v = pTm->tm_mday;
+                       jx9_result_string(pCtx, &zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)], (int)sizeof(char) * 2);
+                       break;
+                                }
+               case 'e':
+                       /*      Timezone identifier */
+                       zCur = pTm->tm_zone;
+                       if( zCur == 0 ){
+                               /* Assume GMT */
+                               zCur = "GMT";
+                       }
+                       jx9_result_string(pCtx, zCur, -1);
+                       break;
+               case 'I':
+                       /* Whether or not the date is in daylight saving time */
+#ifdef __WINNT__
+#ifdef _MSC_VER
+#ifndef _WIN32_WCE
+                       _get_daylight(&pTm->tm_isdst);
+#endif
+#endif
+#endif
+                       jx9_result_string_format(pCtx, "%d", pTm->tm_isdst == 1);
+                       break;
+               case 'r':
+                       /* RFC 2822 formatted date      Example: Thu, 21 Dec 2000 16:01:07 */
+                       jx9_result_string_format(pCtx, "%.3s, %02d %.3s %4d %02d:%02d:%02d", 
+                               SyTimeGetDay(pTm->tm_wday), 
+                               pTm->tm_mday, 
+                               SyTimeGetMonth(pTm->tm_mon), 
+                               pTm->tm_year, 
+                               pTm->tm_hour, 
+                               pTm->tm_min, 
+                               pTm->tm_sec
+                               );
+                       break;
+               case 'U':{
+                       time_t tt;
+                       /* Seconds since the Unix Epoch */
+                       time(&tt);
+                       jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
+                       break;
+                                }
+               case 'O':
+               case 'P':
+                       /* Difference to Greenwich time (GMT) in hours */
+                       jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
+                       break;
+               case 'Z':
+                       /* Timezone offset in seconds. The offset for timezones west of UTC
+                        * is always negative, and for those east of UTC is always positive.
+                        */
+                       jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
+                       break;
+               case 'c':
+                       /*      ISO 8601 date */
+                       jx9_result_string_format(pCtx, "%4d-%02d-%02dT%02d:%02d:%02d%+05d", 
+                               pTm->tm_year, 
+                               pTm->tm_mon+1, 
+                               pTm->tm_mday, 
+                               pTm->tm_hour, 
+                               pTm->tm_min, 
+                               pTm->tm_sec, 
+                               pTm->tm_gmtoff
+                               );
+                       break;
+               case '\\':
+                       zIn++;
+                       /* Expand verbatim */
+                       if( zIn < zEnd ){
+                               jx9_result_string(pCtx, zIn, (int)sizeof(char));
+                       }
+                       break;
+               default:
+                       /* Unknown format specifer, expand verbatim */
+                       jx9_result_string(pCtx, zIn, (int)sizeof(char));
+                       break;
+               }
+               /* Point to the next character */
+               zIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * JX9 implementation of the strftime() function.
+ * The following formats are supported:
+ * %a  An abbreviated textual representation of the day
+ * %A  A full textual representation of the day
+ * %d  Two-digit day of the month (with leading zeros)
+ * %e  Day of the month, with a space preceding single digits.
+ * %j  Day of the year, 3 digits with leading zeros 
+ * %u  ISO-8601 numeric representation of the day of the week  1 (for Monday) though 7 (for Sunday)
+ * %w  Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
+ * %U  Week number of the given year, starting with the first Sunday as the first week
+ * %V  ISO-8601:1988 week number of the given year, starting with the first week of the year with at least
+ *   4 weekdays, with Monday being the start of the week.
+ * %W  A numeric representation of the week of the year
+ * %b  Abbreviated month name, based on the locale
+ * %B  Full month name, based on the locale
+ * %h  Abbreviated month name, based on the locale (an alias of %b)
+ * %m  Two digit representation of the month
+ * %C  Two digit representation of the century (year divided by 100, truncated to an integer)
+ * %g  Two digit representation of the year going by ISO-8601:1988 standards (see %V)
+ * %G  The full four-digit version of %g
+ * %y  Two digit representation of the year
+ * %Y  Four digit representation for the year
+ * %H  Two digit representation of the hour in 24-hour format
+ * %I  Two digit representation of the hour in 12-hour format
+ * %l (lower-case 'L')         Hour in 12-hour format, with a space preceeding single digits
+ * %M  Two digit representation of the minute
+ * %p  UPPER-CASE 'AM' or 'PM' based on the given time
+ * %P  lower-case 'am' or 'pm' based on the given time
+ * %r  Same as "%I:%M:%S %p"
+ * %R  Same as "%H:%M"
+ * %S  Two digit representation of the second
+ * %T  Same as "%H:%M:%S"
+ * %X  Preferred time representation based on locale, without the date
+ * %z  Either the time zone offset from UTC or the abbreviation
+ * %Z  The time zone offset/abbreviation option NOT given by %z
+ * %c  Preferred date and time stamp based on local
+ * %D  Same as "%m/%d/%y"
+ * %F  Same as "%Y-%m-%d"
+ * %s  Unix Epoch Time timestamp (same as the time() function)
+ * %x  Preferred date representation based on locale, without the time
+ * %n  A newline character ("\n")
+ * %t  A Tab character ("\t")
+ * %%  A literal percentage character ("%")
+ */
+static int jx9Strftime(
+       jx9_context *pCtx,  /* Call context */
+       const char *zIn,    /* Input string */
+       int nLen,           /* Input length */
+       Sytm *pTm           /* Parse of the given time */
+       )
+{
+       const char *zCur, *zEnd = &zIn[nLen];
+       int c;
+       /* Start the format process */
+       for(;;){
+               zCur = zIn;
+               while(zIn < zEnd && zIn[0] != '%' ){
+                       zIn++;
+               }
+               if( zIn > zCur ){
+                       /* Consume input verbatim */
+                       jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
+               }
+               zIn++; /* Jump the percent sign */
+               if( zIn >= zEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               c = zIn[0];
+               /* Act according to the current specifer */
+               switch(c){
+               case '%':
+                       /* A literal percentage character ("%") */
+                       jx9_result_string(pCtx, "%", (int)sizeof(char));
+                       break;
+               case 't':
+                       /* A Tab character */
+                       jx9_result_string(pCtx, "\t", (int)sizeof(char));
+                       break;
+               case 'n':
+                       /* A newline character */
+                       jx9_result_string(pCtx, "\n", (int)sizeof(char));
+                       break;
+               case 'a':
+                       /* An abbreviated textual representation of the day */
+                       jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), (int)sizeof(char)*3);
+                       break;
+               case 'A':
+                       /* A full textual representation of the day */
+                       jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), -1/*Compute length automatically*/);
+                       break;
+               case 'e':
+                       /* Day of the month, 2 digits with leading space for single digit*/
+                       jx9_result_string_format(pCtx, "%2d", pTm->tm_mday);
+                       break;
+               case 'd':
+                       /* Two-digit day of the month (with leading zeros) */
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_mon+1);
+                       break;
+               case 'j':
+                       /*The day of the year, 3 digits with leading zeros*/
+                       jx9_result_string_format(pCtx, "%03d", pTm->tm_yday);
+                       break;
+               case 'u':
+                       /* ISO-8601 numeric representation of the day of the week */
+                       jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
+                       break;
+               case 'w':
+                       /* Numeric representation of the day of the week */
+                       jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
+                       break;
+               case 'b':
+               case 'h':
+                       /*A short textual representation of a month, three letters (Not based on locale)*/
+                       jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), (int)sizeof(char)*3);
+                       break;
+               case 'B':
+                       /* Full month name (Not based on locale) */
+                       jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), -1/*Compute length automatically*/);
+                       break;
+               case 'm':
+                       /*Numeric representation of a month, with leading zeros*/
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
+                       break;
+               case 'C':
+                       /* Two digit representation of the century */
+                       jx9_result_string_format(pCtx, "%2d", pTm->tm_year/100);
+                       break;
+               case 'y':
+               case 'g':
+                       /* Two digit representation of the year */
+                       jx9_result_string_format(pCtx, "%2d", pTm->tm_year%100);
+                       break;
+               case 'Y':
+               case 'G':
+                       /* Four digit representation of the year */
+                       jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
+                       break;
+               case 'I':
+                       /* 12-hour format of an hour with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
+                       break;
+               case 'l':
+                       /* 12-hour format of an hour with leading space */
+                       jx9_result_string_format(pCtx, "%2d", 1+(pTm->tm_hour%12));
+                       break;
+               case 'H':
+                       /* 24-hour format of an hour with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
+                       break;
+               case 'M':
+                       /* Minutes with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
+                       break;
+               case 'S':
+                       /* Seconds with leading zeros */
+                       jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
+                       break;
+               case 'z':
+               case 'Z':
+                       /*      Timezone identifier */
+                       zCur = pTm->tm_zone;
+                       if( zCur == 0 ){
+                               /* Assume GMT */
+                               zCur = "GMT";
+                       }
+                       jx9_result_string(pCtx, zCur, -1);
+                       break;
+               case 'T':
+               case 'X':
+                       /* Same as "%H:%M:%S" */
+                       jx9_result_string_format(pCtx, "%02d:%02d:%02d", pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
+                       break;
+               case 'R':
+                       /* Same as "%H:%M" */
+                       jx9_result_string_format(pCtx, "%02d:%02d", pTm->tm_hour, pTm->tm_min);
+                       break;
+               case 'P':
+                       /*      Lowercase Ante meridiem and Post meridiem */
+                       jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", (int)sizeof(char)*2);
+                       break;
+               case 'p':
+                       /*      Uppercase Ante meridiem and Post meridiem */
+                       jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", (int)sizeof(char)*2);
+                       break;
+               case 'r':
+                       /* Same as "%I:%M:%S %p" */
+                       jx9_result_string_format(pCtx, "%02d:%02d:%02d %s", 
+                               1+(pTm->tm_hour%12), 
+                               pTm->tm_min, 
+                               pTm->tm_sec, 
+                               pTm->tm_hour > 12 ? "PM" : "AM"
+                               );
+                       break;
+               case 'D':
+               case 'x':
+                       /* Same as "%m/%d/%y" */
+                       jx9_result_string_format(pCtx, "%02d/%02d/%02d", 
+                               pTm->tm_mon+1, 
+                               pTm->tm_mday, 
+                               pTm->tm_year%100
+                               );
+                       break;
+               case 'F':
+                       /* Same as "%Y-%m-%d" */
+                       jx9_result_string_format(pCtx, "%d-%02d-%02d", 
+                               pTm->tm_year, 
+                               pTm->tm_mon+1, 
+                               pTm->tm_mday
+                               );
+                       break;
+               case 'c':
+                       jx9_result_string_format(pCtx, "%d-%02d-%02d %02d:%02d:%02d", 
+                               pTm->tm_year, 
+                               pTm->tm_mon+1, 
+                               pTm->tm_mday, 
+                               pTm->tm_hour, 
+                               pTm->tm_min, 
+                               pTm->tm_sec
+                               );
+                       break;
+               case 's':{
+                       time_t tt;
+                       /* Seconds since the Unix Epoch */
+                       time(&tt);
+                       jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
+                       break;
+                                }
+               default:
+                       /* unknown specifer, simply ignore*/
+                       break;
+               }
+               /* Advance the cursor */
+               zIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * string date(string $format [, int $timestamp = time() ] )
+ *  Returns a string formatted according to the given format string using
+ *  the given integer timestamp or the current time if no timestamp is given.
+ *  In other words, timestamp is optional and defaults to the value of time(). 
+ * Parameters
+ *  $format
+ *   The format of the outputted date string (See code above)
+ * $timestamp
+ *   The optional timestamp parameter is an integer Unix timestamp
+ *   that defaults to the current local time if a timestamp is not given.
+ *   In other words, it defaults to the value of time(). 
+ * Return
+ *  A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
+ */
+static int jx9Builtin_date(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zFormat;
+       int nLen;
+       Sytm sTm;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       zFormat = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Don't bother processing return the empty string */
+               jx9_result_string(pCtx, "", 0);
+       }
+       if( nArg < 2 ){
+#ifdef __WINNT__
+               SYSTEMTIME sOS;
+               GetSystemTime(&sOS);
+               SYSTEMTIME_TO_SYTM(&sOS, &sTm);
+#else
+               struct tm *pTm;
+               time_t t;
+               time(&t);
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+#endif
+       }else{
+               /* Use the given timestamp */
+               time_t t;
+               struct tm *pTm;
+               if( jx9_value_is_int(apArg[1]) ){
+                       t = (time_t)jx9_value_to_int64(apArg[1]);
+                       pTm = localtime(&t);
+                       if( pTm == 0 ){
+                               time(&t);
+                       }
+               }else{
+                       time(&t);
+               }
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+       }
+       /* Format the given string */
+       DateFormat(pCtx, zFormat, nLen, &sTm);
+       return JX9_OK;
+}
+/*
+ * string strftime(string $format [, int $timestamp = time() ] )
+ *  Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE)
+ * Parameters
+ *  $format
+ *   The format of the outputted date string (See code above)
+ * $timestamp
+ *   The optional timestamp parameter is an integer Unix timestamp
+ *   that defaults to the current local time if a timestamp is not given.
+ *   In other words, it defaults to the value of time(). 
+ * Return
+ * Returns a string formatted according format using the given timestamp
+ * or the current local time if no timestamp is given.
+ */
+static int jx9Builtin_strftime(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zFormat;
+       int nLen;
+       Sytm sTm;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       zFormat = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Don't bother processing return FALSE */
+               jx9_result_bool(pCtx, 0);
+       }
+       if( nArg < 2 ){
+#ifdef __WINNT__
+               SYSTEMTIME sOS;
+               GetSystemTime(&sOS);
+               SYSTEMTIME_TO_SYTM(&sOS, &sTm);
+#else
+               struct tm *pTm;
+               time_t t;
+               time(&t);
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+#endif
+       }else{
+               /* Use the given timestamp */
+               time_t t;
+               struct tm *pTm;
+               if( jx9_value_is_int(apArg[1]) ){
+                       t = (time_t)jx9_value_to_int64(apArg[1]);
+                       pTm = localtime(&t);
+                       if( pTm == 0 ){
+                               time(&t);
+                       }
+               }else{
+                       time(&t);
+               }
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+       }
+       /* Format the given string */
+       jx9Strftime(pCtx, zFormat, nLen, &sTm);
+       if( jx9_context_result_buf_length(pCtx) < 1 ){
+               /* Nothing was formatted, return FALSE */
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * string gmdate(string $format [, int $timestamp = time() ] )
+ *  Identical to the date() function except that the time returned
+ *  is Greenwich Mean Time (GMT).
+ * Parameters
+ *  $format
+ *  The format of the outputted date string (See code above)
+ *  $timestamp
+ *   The optional timestamp parameter is an integer Unix timestamp
+ *   that defaults to the current local time if a timestamp is not given.
+ *   In other words, it defaults to the value of time(). 
+ * Return
+ *  A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
+ */
+static int jx9Builtin_gmdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zFormat;
+       int nLen;
+       Sytm sTm;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       zFormat = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Don't bother processing return the empty string */
+               jx9_result_string(pCtx, "", 0);
+       }
+       if( nArg < 2 ){
+#ifdef __WINNT__
+               SYSTEMTIME sOS;
+               GetSystemTime(&sOS);
+               SYSTEMTIME_TO_SYTM(&sOS, &sTm);
+#else
+               struct tm *pTm;
+               time_t t;
+               time(&t);
+               pTm = gmtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+#endif
+       }else{
+               /* Use the given timestamp */
+               time_t t;
+               struct tm *pTm;
+               if( jx9_value_is_int(apArg[1]) ){
+                       t = (time_t)jx9_value_to_int64(apArg[1]);
+                       pTm = gmtime(&t);
+                       if( pTm == 0 ){
+                               time(&t);
+                       }
+               }else{
+                       time(&t);
+               }
+               pTm = gmtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+       }
+       /* Format the given string */
+       DateFormat(pCtx, zFormat, nLen, &sTm);
+       return JX9_OK;
+}
+/*
+ * array localtime([ int $timestamp = time() [, bool $is_associative = false ]])
+ *  Return the local time.
+ * Parameter
+ *  $timestamp: The optional timestamp parameter is an integer Unix timestamp
+ *     that defaults to the current local time if a timestamp is not given.
+ *     In other words, it defaults to the value of time().
+ * $is_associative
+ *   If set to FALSE or not supplied then the array is returned as a regular, numerically
+ *   indexed array. If the argument is set to TRUE then localtime() returns an associative
+ *   array containing all the different elements of the structure returned by the C function
+ *   call to localtime. The names of the different keys of the associative array are as follows:
+ *      "tm_sec" - seconds, 0 to 59
+ *      "tm_min" - minutes, 0 to 59
+ *      "tm_hour" - hours, 0 to 23
+ *      "tm_mday" - day of the month, 1 to 31
+ *      "tm_mon" - month of the year, 0 (Jan) to 11 (Dec)
+ *      "tm_year" - years since 1900
+ *      "tm_wday" - day of the week, 0 (Sun) to 6 (Sat)
+ *      "tm_yday" - day of the year, 0 to 365
+ *      "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown.
+ * Returns
+ *  An associative array of information related to the timestamp.
+ */
+static int jx9Builtin_localtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_value *pValue, *pArray;
+       int isAssoc = 0;
+       Sytm sTm;
+       if( nArg < 1 ){
+#ifdef __WINNT__
+               SYSTEMTIME sOS;
+               GetSystemTime(&sOS); /* TODO(chems): GMT not local */
+               SYSTEMTIME_TO_SYTM(&sOS, &sTm);
+#else
+               struct tm *pTm;
+               time_t t;
+               time(&t);
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+#endif
+       }else{
+               /* Use the given timestamp */
+               time_t t;
+               struct tm *pTm;
+               if( jx9_value_is_int(apArg[0]) ){
+                       t = (time_t)jx9_value_to_int64(apArg[0]);
+                       pTm = localtime(&t);
+                       if( pTm == 0 ){
+                               time(&t);
+                       }
+               }else{
+                       time(&t);
+               }
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+       }
+       /* Element value */
+       pValue = jx9_context_new_scalar(pCtx);
+       if( pValue == 0 ){
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       if( nArg > 1 ){
+               isAssoc = jx9_value_to_bool(apArg[1]); 
+       }
+       /* Fill the array */
+       /* Seconds */
+       jx9_value_int(pValue, sTm.tm_sec);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_sec", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* Minutes */
+       jx9_value_int(pValue, sTm.tm_min);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_min", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* Hours */
+       jx9_value_int(pValue, sTm.tm_hour);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_hour", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* mday */
+       jx9_value_int(pValue, sTm.tm_mday);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_mday", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* mon */
+       jx9_value_int(pValue, sTm.tm_mon);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_mon", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* year since 1900 */
+       jx9_value_int(pValue, sTm.tm_year-1900);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_year", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* wday */
+       jx9_value_int(pValue, sTm.tm_wday);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_wday", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* yday */
+       jx9_value_int(pValue, sTm.tm_yday);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_yday", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* isdst */
+#ifdef __WINNT__
+#ifdef _MSC_VER
+#ifndef _WIN32_WCE
+                       _get_daylight(&sTm.tm_isdst);
+#endif
+#endif
+#endif
+       jx9_value_int(pValue, sTm.tm_isdst);
+       if( isAssoc ){
+               jx9_array_add_strkey_elem(pArray, "tm_isdst", pValue);
+       }else{
+               jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
+       }
+       /* Return the array */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * int idate(string $format [, int $timestamp = time() ])
+ *  Returns a number formatted according to the given format string
+ *  using the given integer timestamp or the current local time if 
+ *  no timestamp is given. In other words, timestamp is optional and defaults
+ *  to the value of time().
+ *  Unlike the function date(), idate() accepts just one char in the format
+ *  parameter.
+ * $Parameters
+ *  Supported format
+ *   d         Day of the month
+ *   h         Hour (12 hour format)
+ *   H         Hour (24 hour format)
+ *   i         Minutes
+ *   I (uppercase i)1 if DST is activated, 0 otherwise
+ *   L (uppercase l) returns 1 for leap year, 0 otherwise
+ *   m         Month number
+ *   s         Seconds
+ *   t         Days in current month
+ *   U         Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time()
+ *   w         Day of the week (0 on Sunday)
+ *   W         ISO-8601 week number of year, weeks starting on Monday
+ *   y         Year (1 or 2 digits - check note below)
+ *   Y         Year (4 digits)
+ *   z         Day of the year
+ *   Z         Timezone offset in seconds
+ * $timestamp
+ *  The optional timestamp parameter is an integer Unix timestamp that defaults
+ *  to the current local time if a timestamp is not given. In other words, it defaults
+ *  to the value of time(). 
+ * Return
+ *  An integer. 
+ */
+static int jx9Builtin_idate(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zFormat;
+       jx9_int64 iVal = 0;
+       int nLen;
+       Sytm sTm;
+       if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
+               /* Missing/Invalid argument, return -1 */
+               jx9_result_int(pCtx, -1);
+               return JX9_OK;
+       }
+       zFormat = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Don't bother processing return -1*/
+               jx9_result_int(pCtx, -1);
+       }
+       if( nArg < 2 ){
+#ifdef __WINNT__
+               SYSTEMTIME sOS;
+               GetSystemTime(&sOS);
+               SYSTEMTIME_TO_SYTM(&sOS, &sTm);
+#else
+               struct tm *pTm;
+               time_t t;
+               time(&t);
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+#endif
+       }else{
+               /* Use the given timestamp */
+               time_t t;
+               struct tm *pTm;
+               if( jx9_value_is_int(apArg[1]) ){
+                       t = (time_t)jx9_value_to_int64(apArg[1]);
+                       pTm = localtime(&t);
+                       if( pTm == 0 ){
+                               time(&t);
+                       }
+               }else{
+                       time(&t);
+               }
+               pTm = localtime(&t);
+               STRUCT_TM_TO_SYTM(pTm, &sTm);
+       }
+       /* Perform the requested operation */
+       switch(zFormat[0]){
+       case 'd':
+               /* Day of the month */
+               iVal = sTm.tm_mday;
+               break;
+       case 'h':
+               /*      Hour (12 hour format)*/
+               iVal = 1 + (sTm.tm_hour % 12);
+               break;
+       case 'H':
+               /* Hour (24 hour format)*/
+               iVal = sTm.tm_hour;
+               break;
+       case 'i':
+               /*Minutes*/
+               iVal = sTm.tm_min;
+               break;
+       case 'I':
+               /*      returns 1 if DST is activated, 0 otherwise */
+#ifdef __WINNT__
+#ifdef _MSC_VER
+#ifndef _WIN32_WCE
+                       _get_daylight(&sTm.tm_isdst);
+#endif
+#endif
+#endif
+               iVal = sTm.tm_isdst;
+               break;
+       case 'L':
+               /*      returns 1 for leap year, 0 otherwise */
+               iVal = IS_LEAP_YEAR(sTm.tm_year);
+               break;
+       case 'm':
+               /* Month number*/
+               iVal = sTm.tm_mon;
+               break;
+       case 's':
+               /*Seconds*/
+               iVal = sTm.tm_sec;
+               break;
+       case 't':{
+               /*Days in current month*/
+               static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+               int nDays = aMonDays[sTm.tm_mon % 12 ];
+               if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){
+                       nDays = 28;
+               }
+               iVal = nDays;
+               break;
+                        }
+       case 'U':
+               /*Seconds since the Unix Epoch*/
+               iVal = (jx9_int64)time(0);
+               break;
+       case 'w':
+               /*      Day of the week (0 on Sunday) */
+               iVal = sTm.tm_wday;
+               break;
+       case 'W': {
+               /* ISO-8601 week number of year, weeks starting on Monday */
+               static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
+               iVal = aISO8601[sTm.tm_wday % 7 ];
+               break;
+                         }
+       case 'y':
+               /* Year (2 digits) */
+               iVal = sTm.tm_year % 100;
+               break;
+       case 'Y':
+               /* Year (4 digits) */
+               iVal = sTm.tm_year;
+               break;
+       case 'z':
+               /* Day of the year */
+               iVal = sTm.tm_yday;
+               break;
+       case 'Z':
+               /*Timezone offset in seconds*/
+               iVal = sTm.tm_gmtoff;
+               break;
+       default:
+               /* unknown format, throw a warning */
+               jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Unknown date format token");
+               break;
+       }
+       /* Return the time value */
+       jx9_result_int64(pCtx, iVal);
+       return JX9_OK;
+}
+/*
+ * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") 
+ *  [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )
+ *  Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer 
+ *  containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time
+ *  specified.
+ *  Arguments may be left out in order from right to left; any arguments thus omitted will be set to
+ *  the current value according to the local date and time.
+ * Parameters
+ * $hour
+ *  The number of the hour relevant to the start of the day determined by month, day and year.
+ *  Negative values reference the hour before midnight of the day in question. Values greater
+ *  than 23 reference the appropriate hour in the following day(s).
+ * $minute
+ *  The number of the minute relevant to the start of the hour. Negative values reference
+ *  the minute in the previous hour. Values greater than 59 reference the appropriate minute
+ *  in the following hour(s).
+ * $second
+ *  The number of seconds relevant to the start of the minute. Negative values reference 
+ *  the second in the previous minute. Values greater than 59 reference the appropriate 
+ * second in the following minute(s).
+ * $month
+ *  The number of the month relevant to the end of the previous year. Values 1 to 12 reference
+ *  the normal calendar months of the year in question. Values less than 1 (including negative values)
+ *  reference the months in the previous year in reverse order, so 0 is December, -1 is November)...
+ * $day
+ *  The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31 
+ *  (depending upon the month) reference the normal days in the relevant month. Values less than 1
+ *  (including negative values) reference the days in the previous month, so 0 is the last day 
+ *  of the previous month, -1 is the day before that, etc. Values greater than the number of days
+ *  in the relevant month reference the appropriate day in the following month(s).
+ * $year
+ *  The number of the year, may be a two or four digit value, with values between 0-69 mapping
+ *  to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as 
+ *  most common today, the valid range for year is somewhere between 1901 and 2038.
+ * $is_dst
+ *  This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not, 
+ *  or -1 (the default) if it is unknown whether the time is within daylight savings time or not. 
+ * Return
+ *   mktime() returns the Unix timestamp of the arguments given. 
+ *   If the arguments are invalid, the function returns FALSE
+ */
+static int jx9Builtin_mktime(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zFunction;
+       jx9_int64 iVal = 0;
+       struct tm *pTm;
+       time_t t;
+       /* Extract function name */
+       zFunction = jx9_function_name(pCtx);
+       /* Get the current time */
+       time(&t);
+       if( zFunction[0] == 'g' /* gmmktime */ ){
+               pTm = gmtime(&t);
+       }else{
+               /* localtime */
+               pTm = localtime(&t);
+       }
+       if( nArg > 0 ){
+               int iVal;
+               /* Hour */
+               iVal = jx9_value_to_int(apArg[0]);
+               pTm->tm_hour = iVal;
+               if( nArg > 1 ){
+                       /* Minutes */
+                       iVal = jx9_value_to_int(apArg[1]);
+                       pTm->tm_min = iVal;
+                       if( nArg > 2 ){
+                               /* Seconds */
+                               iVal = jx9_value_to_int(apArg[2]);
+                               pTm->tm_sec = iVal;
+                               if( nArg > 3 ){
+                                       /* Month */
+                                       iVal = jx9_value_to_int(apArg[3]);
+                                       pTm->tm_mon = iVal - 1;
+                                       if( nArg > 4 ){
+                                               /* mday */
+                                               iVal = jx9_value_to_int(apArg[4]);
+                                               pTm->tm_mday = iVal;
+                                               if( nArg > 5 ){
+                                                       /* Year */
+                                                       iVal = jx9_value_to_int(apArg[5]);
+                                                       if( iVal > 1900 ){
+                                                               iVal -= 1900;
+                                                       }
+                                                       pTm->tm_year = iVal;
+                                                       if( nArg > 6 ){
+                                                               /* is_dst */
+                                                               iVal = jx9_value_to_bool(apArg[6]);
+                                                               pTm->tm_isdst = iVal;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       /* Make the time */
+       iVal = (jx9_int64)mktime(pTm);
+       /* Return the timesatmp as a 64bit integer */
+       jx9_result_int64(pCtx, iVal);
+       return JX9_OK;
+}
+/*
+ * Section:
+ *    URL handling Functions.
+ * Authors:
+ *    Symisc Systems, devel@symisc.net.
+ *    Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Stable.
+ */
+/*
+ * Output consumer callback for the standard Symisc routines.
+ * [i.e: SyBase64Encode(), SyBase64Decode(), SyUriEncode(), ...].
+ */
+static int Consumer(const void *pData, unsigned int nLen, void *pUserData)
+{
+       /* Store in the call context result buffer */
+       jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
+       return SXRET_OK;
+}
+/*
+ * string base64_encode(string $data)
+ * string convert_uuencode(string $data)  
+ *  Encodes data with MIME base64
+ * Parameter
+ *  $data
+ *    Data to encode
+ * Return
+ *  Encoded data or FALSE on failure.
+ */
+static int jx9Builtin_base64_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the input string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Nothing to process, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the BASE64 encoding */
+       SyBase64Encode(zIn, (sxu32)nLen, Consumer, pCtx);
+       return JX9_OK;
+}
+/*
+ * string base64_decode(string $data)
+ * string convert_uudecode(string $data)
+ *  Decodes data encoded with MIME base64
+ * Parameter
+ *  $data
+ *    Encoded data.
+ * Return
+ *  Returns the original data or FALSE on failure.
+ */
+static int jx9Builtin_base64_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the input string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Nothing to process, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the BASE64 decoding */
+       SyBase64Decode(zIn, (sxu32)nLen, Consumer, pCtx);
+       return JX9_OK;
+}
+/*
+ * string urlencode(string $str)
+ *  URL encoding
+ * Parameter
+ *  $data
+ *   Input string.
+ * Return
+ *  Returns a string in which all non-alphanumeric characters except -_. have
+ *  been replaced with a percent (%) sign followed by two hex digits and spaces
+ *  encoded as plus (+) signs.
+ */
+static int jx9Builtin_urlencode(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the input string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Nothing to process, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the URL encoding */
+       SyUriEncode(zIn, (sxu32)nLen, Consumer, pCtx);
+       return JX9_OK;
+}
+/*
+ * string urldecode(string $str)
+ *  Decodes any %## encoding in the given string.
+ *  Plus symbols ('+') are decoded to a space character. 
+ * Parameter
+ *  $data
+ *    Input string.
+ * Return
+ *  Decoded URL or FALSE on failure.
+ */
+static int jx9Builtin_urldecode(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       const char *zIn;
+       int nLen;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Extract the input string */
+       zIn = jx9_value_to_string(apArg[0], &nLen);
+       if( nLen < 1 ){
+               /* Nothing to process, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the URL decoding */
+       SyUriDecode(zIn, (sxu32)nLen, Consumer, pCtx, TRUE);
+       return JX9_OK;
+}
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+/* Table of the built-in functions */
+static const jx9_builtin_func aBuiltInFunc[] = {
+          /* Variable handling functions */
+       { "is_bool"    , jx9Builtin_is_bool     }, 
+       { "is_float"   , jx9Builtin_is_float    }, 
+       { "is_real"    , jx9Builtin_is_float    }, 
+       { "is_double"  , jx9Builtin_is_float    }, 
+       { "is_int"     , jx9Builtin_is_int      }, 
+       { "is_integer" , jx9Builtin_is_int      }, 
+       { "is_long"    , jx9Builtin_is_int      }, 
+       { "is_string"  , jx9Builtin_is_string   }, 
+       { "is_null"    , jx9Builtin_is_null     }, 
+       { "is_numeric" , jx9Builtin_is_numeric  }, 
+       { "is_scalar"  , jx9Builtin_is_scalar   }, 
+       { "is_array"   , jx9Builtin_is_array    }, 
+       { "is_object"  , jx9Builtin_is_object   }, 
+       { "is_resource", jx9Builtin_is_resource }, 
+       { "douleval"   , jx9Builtin_floatval    }, 
+       { "floatval"   , jx9Builtin_floatval    }, 
+       { "intval"     , jx9Builtin_intval      }, 
+       { "strval"     , jx9Builtin_strval      }, 
+       { "empty"      , jx9Builtin_empty       }, 
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifdef JX9_ENABLE_MATH_FUNC
+          /* Math functions */
+       { "abs"  ,    jx9Builtin_abs          }, 
+       { "sqrt" ,    jx9Builtin_sqrt         }, 
+       { "exp"  ,    jx9Builtin_exp          }, 
+       { "floor",    jx9Builtin_floor        }, 
+       { "cos"  ,    jx9Builtin_cos          }, 
+       { "sin"  ,    jx9Builtin_sin          }, 
+       { "acos" ,    jx9Builtin_acos         }, 
+       { "asin" ,    jx9Builtin_asin         }, 
+       { "cosh" ,    jx9Builtin_cosh         }, 
+       { "sinh" ,    jx9Builtin_sinh         }, 
+       { "ceil" ,    jx9Builtin_ceil         }, 
+       { "tan"  ,    jx9Builtin_tan          }, 
+       { "tanh" ,    jx9Builtin_tanh         }, 
+       { "atan" ,    jx9Builtin_atan         }, 
+       { "atan2",    jx9Builtin_atan2        }, 
+       { "log"  ,    jx9Builtin_log          }, 
+       { "log10" ,   jx9Builtin_log10        }, 
+       { "pow"  ,    jx9Builtin_pow          }, 
+       { "pi",       jx9Builtin_pi           }, 
+       { "fmod",     jx9Builtin_fmod         }, 
+       { "hypot",    jx9Builtin_hypot        }, 
+#endif /* JX9_ENABLE_MATH_FUNC */
+       { "round",    jx9Builtin_round        }, 
+       { "dechex", jx9Builtin_dechex         }, 
+       { "decoct", jx9Builtin_decoct         }, 
+       { "decbin", jx9Builtin_decbin         }, 
+       { "hexdec", jx9Builtin_hexdec         }, 
+       { "bindec", jx9Builtin_bindec         }, 
+       { "octdec", jx9Builtin_octdec         }, 
+       { "base_convert", jx9Builtin_base_convert }, 
+          /* String handling functions */
+       { "substr",          jx9Builtin_substr     }, 
+       { "substr_compare",  jx9Builtin_substr_compare }, 
+       { "substr_count",    jx9Builtin_substr_count }, 
+       { "chunk_split",     jx9Builtin_chunk_split}, 
+       { "htmlspecialchars", jx9Builtin_htmlspecialchars }, 
+       { "htmlspecialchars_decode", jx9Builtin_htmlspecialchars_decode }, 
+       { "get_html_translation_table", jx9Builtin_get_html_translation_table }, 
+       { "htmlentities", jx9Builtin_htmlentities}, 
+       { "html_entity_decode", jx9Builtin_html_entity_decode}, 
+       { "strlen"     , jx9Builtin_strlen     }, 
+       { "strcmp"     , jx9Builtin_strcmp     }, 
+       { "strcoll"    , jx9Builtin_strcmp     }, 
+       { "strncmp"    , jx9Builtin_strncmp    }, 
+       { "strcasecmp" , jx9Builtin_strcasecmp }, 
+       { "strncasecmp", jx9Builtin_strncasecmp}, 
+       { "implode"    , jx9Builtin_implode    }, 
+       { "join"       , jx9Builtin_implode    }, 
+       { "implode_recursive" , jx9Builtin_implode_recursive }, 
+       { "join_recursive"    , jx9Builtin_implode_recursive }, 
+       { "explode"     , jx9Builtin_explode    }, 
+       { "trim"        , jx9Builtin_trim       }, 
+       { "rtrim"       , jx9Builtin_rtrim      }, 
+       { "chop"        , jx9Builtin_rtrim      }, 
+       { "ltrim"       , jx9Builtin_ltrim      }, 
+       { "strtolower",   jx9Builtin_strtolower }, 
+       { "mb_strtolower", jx9Builtin_strtolower }, /* Only UTF-8 encoding is supported */
+       { "strtoupper",   jx9Builtin_strtoupper }, 
+       { "mb_strtoupper", jx9Builtin_strtoupper }, /* Only UTF-8 encoding is supported */
+       { "ord",          jx9Builtin_ord        }, 
+       { "chr",          jx9Builtin_chr        }, 
+       { "bin2hex",      jx9Builtin_bin2hex    }, 
+       { "strstr",       jx9Builtin_strstr     }, 
+       { "stristr",      jx9Builtin_stristr    }, 
+       { "strchr",       jx9Builtin_strstr     }, 
+       { "strpos",       jx9Builtin_strpos     }, 
+       { "stripos",      jx9Builtin_stripos    }, 
+       { "strrpos",      jx9Builtin_strrpos    }, 
+       { "strripos",     jx9Builtin_strripos   }, 
+       { "strrchr",      jx9Builtin_strrchr    }, 
+       { "strrev",       jx9Builtin_strrev     }, 
+       { "str_repeat",   jx9Builtin_str_repeat }, 
+       { "nl2br",        jx9Builtin_nl2br      }, 
+       { "sprintf",      jx9Builtin_sprintf    }, 
+       { "printf",       jx9Builtin_printf     }, 
+       { "vprintf",      jx9Builtin_vprintf    }, 
+       { "vsprintf",     jx9Builtin_vsprintf   }, 
+       { "size_format",  jx9Builtin_size_format}, 
+#if !defined(JX9_DISABLE_HASH_FUNC)
+       { "md5",          jx9Builtin_md5       }, 
+       { "sha1",         jx9Builtin_sha1      }, 
+       { "crc32",        jx9Builtin_crc32     }, 
+#endif /* JX9_DISABLE_HASH_FUNC */
+       { "str_getcsv",   jx9Builtin_str_getcsv }, 
+       { "strip_tags",   jx9Builtin_strip_tags }, 
+       { "str_split",    jx9Builtin_str_split  }, 
+       { "strspn",       jx9Builtin_strspn     }, 
+       { "strcspn",      jx9Builtin_strcspn    }, 
+       { "strpbrk",      jx9Builtin_strpbrk    }, 
+       { "soundex",      jx9Builtin_soundex    }, 
+       { "wordwrap",     jx9Builtin_wordwrap   }, 
+       { "strtok",       jx9Builtin_strtok     }, 
+       { "str_pad",      jx9Builtin_str_pad    }, 
+       { "str_replace",  jx9Builtin_str_replace}, 
+       { "str_ireplace", jx9Builtin_str_replace}, 
+       { "strtr",        jx9Builtin_strtr      }, 
+       { "parse_ini_string", jx9Builtin_parse_ini_string}, 
+                /* Ctype functions */
+       { "ctype_alnum", jx9Builtin_ctype_alnum }, 
+       { "ctype_alpha", jx9Builtin_ctype_alpha }, 
+       { "ctype_cntrl", jx9Builtin_ctype_cntrl }, 
+       { "ctype_digit", jx9Builtin_ctype_digit }, 
+       { "ctype_xdigit", jx9Builtin_ctype_xdigit}, 
+       { "ctype_graph", jx9Builtin_ctype_graph }, 
+       { "ctype_print", jx9Builtin_ctype_print }, 
+       { "ctype_punct", jx9Builtin_ctype_punct }, 
+       { "ctype_space", jx9Builtin_ctype_space }, 
+       { "ctype_lower", jx9Builtin_ctype_lower }, 
+       { "ctype_upper", jx9Builtin_ctype_upper }, 
+                /* Time functions */
+       { "time"    ,    jx9Builtin_time         }, 
+       { "microtime",   jx9Builtin_microtime    }, 
+       { "getdate" ,    jx9Builtin_getdate      }, 
+       { "gettimeofday", jx9Builtin_gettimeofday }, 
+       { "date",        jx9Builtin_date         }, 
+       { "strftime",    jx9Builtin_strftime     }, 
+       { "idate",       jx9Builtin_idate        }, 
+       { "gmdate",      jx9Builtin_gmdate       }, 
+       { "localtime",   jx9Builtin_localtime    }, 
+       { "mktime",      jx9Builtin_mktime       }, 
+       { "gmmktime",    jx9Builtin_mktime       }, 
+               /* URL functions */
+       { "base64_encode", jx9Builtin_base64_encode }, 
+       { "base64_decode", jx9Builtin_base64_decode }, 
+       { "convert_uuencode", jx9Builtin_base64_encode }, 
+       { "convert_uudecode", jx9Builtin_base64_decode }, 
+       { "urlencode",    jx9Builtin_urlencode }, 
+       { "urldecode",    jx9Builtin_urldecode }, 
+       { "rawurlencode", jx9Builtin_urlencode }, 
+       { "rawurldecode", jx9Builtin_urldecode }, 
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+};
+/*
+ * Register the built-in functions defined above, the array functions 
+ * defined in hashmap.c and the IO functions defined in vfs.c.
+ */
+JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm)
+{
+       sxu32 n;
+       for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){
+               jx9_create_function(&(*pVm), aBuiltInFunc[n].zName, aBuiltInFunc[n].xFunc, 0);
+       }
+       /* Register hashmap functions [i.e: sort(), count(), array_diff(), ...] */
+       jx9RegisterHashmapFunctions(&(*pVm));
+       /* Register IO functions [i.e: fread(), fwrite(), chdir(), mkdir(), file(), ...] */
+       jx9RegisterIORoutine(&(*pVm));
+}
+
+/*
+ * ----------------------------------------------------------
+ * File: jx9_compile.c
+ * MD5: 562e73eb7214f890e71713c6b97a7863
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: compile.c v1.7 FreeBSD 2012-12-11 21:46 stable <chm@symisc.net> $ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+/*
+ * This file implement a thread-safe and full-reentrant compiler for the JX9 engine.
+ * That is, routines defined in this file takes a stream of tokens and output
+ * JX9 bytecode instructions.
+ */
+/* Forward declaration */
+typedef struct LangConstruct LangConstruct;
+typedef struct JumpFixup     JumpFixup;
+/* Block [i.e: set of statements] control flags */
+#define GEN_BLOCK_LOOP        0x001    /* Loop block [i.e: for, while, ...] */
+#define GEN_BLOCK_PROTECTED   0x002    /* Protected block */
+#define GEN_BLOCK_COND        0x004    /* Conditional block [i.e: if(condition){} ]*/
+#define GEN_BLOCK_FUNC        0x008    /* Function body */
+#define GEN_BLOCK_GLOBAL      0x010    /* Global block (always set)*/
+#define GEN_BLOC_NESTED_FUNC  0x020    /* Nested function body */
+#define GEN_BLOCK_EXPR        0x040    /* Expression */
+#define GEN_BLOCK_STD         0x080    /* Standard block */
+#define GEN_BLOCK_SWITCH      0x100    /* Switch statement */
+/*
+ * Compilation of some JX9 constructs such as if, for, while, the logical or
+ * (||) and logical and (&&) operators in expressions requires the
+ * generation of forward jumps.
+ * Since the destination PC target of these jumps isn't known when the jumps
+ * are emitted, we record each forward jump in an instance of the following
+ * structure. Those jumps are fixed later when the jump destination is resolved.
+ */
+struct JumpFixup
+{
+       sxi32 nJumpType;     /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */
+       sxu32 nInstrIdx;     /* Instruction index to fix later when the jump destination is resolved. */
+};
+/*
+ * Each language construct is represented by an instance
+ * of the following structure.
+ */
+struct LangConstruct
+{
+       sxu32 nID;                     /* Language construct ID [i.e: JX9_TKWRD_WHILE, JX9_TKWRD_FOR, JX9_TKWRD_IF...] */
+       ProcLangConstruct xConstruct;  /* C function implementing the language construct */
+};
+/* Compilation flags */
+#define JX9_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */
+/* Token stream synchronization macros */
+#define SWAP_TOKEN_STREAM(GEN, START, END)\
+       pTmp  = GEN->pEnd;\
+       pGen->pIn  = START;\
+       pGen->pEnd = END
+#define UPDATE_TOKEN_STREAM(GEN)\
+       if( GEN->pIn < pTmp ){\
+           GEN->pIn++;\
+       }\
+       GEN->pEnd = pTmp
+#define SWAP_DELIMITER(GEN, START, END)\
+       pTmpIn  = GEN->pIn;\
+       pTmpEnd = GEN->pEnd;\
+       GEN->pIn = START;\
+       GEN->pEnd = END
+#define RE_SWAP_DELIMITER(GEN)\
+       GEN->pIn  = pTmpIn;\
+       GEN->pEnd = pTmpEnd
+/* Flags related to expression compilation */
+#define EXPR_FLAG_LOAD_IDX_STORE    0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */
+#define EXPR_FLAG_RDONLY_LOAD       0x002 /* Read-only load, refer to the 'JX9_OP_LOAD' VM instruction for more information */
+#define EXPR_FLAG_COMMA_STATEMENT   0x004 /* Treat comma expression as a single statement (used by object attributes) */
+/* Forward declaration */
+static sxi32 jx9CompileExpr(
+       jx9_gen_state *pGen, /* Code generator state */
+       sxi32 iFlags,        /* Control flags */
+       sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
+       );
+
+/*
+ * Recover from a compile-time error. In other words synchronize
+ * the token stream cursor with the first semi-colon seen.
+ */
+static sxi32 jx9ErrorRecover(jx9_gen_state *pGen)
+{
+       /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
+       while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI /*';'*/) == 0){
+               pGen->pIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Check if the given identifier name is reserved or not.
+ * Return TRUE if reserved.FALSE otherwise.
+ */
+static int GenStateIsReservedID(SyString *pName)
+{
+       if( pName->nByte == sizeof("null") - 1 ){
+               if( SyStrnicmp(pName->zString, "null", sizeof("null")-1) == 0 ){
+                       return TRUE;
+               }else if( SyStrnicmp(pName->zString, "true", sizeof("true")-1) == 0 ){
+                       return TRUE;
+               }
+       }else if( pName->nByte == sizeof("false") - 1 ){
+               if( SyStrnicmp(pName->zString, "false", sizeof("false")-1) == 0 ){
+                       return TRUE;
+               }
+       }
+       /* Not a reserved constant */
+       return FALSE;
+}
+/*
+ * Check if a given token value is installed in the literal table.
+ */
+static sxi32 GenStateFindLiteral(jx9_gen_state *pGen, const SyString *pValue, sxu32 *pIdx)
+{
+       SyHashEntry *pEntry;
+       pEntry = SyHashGet(&pGen->hLiteral, (const void *)pValue->zString, pValue->nByte);
+       if( pEntry == 0 ){
+               return SXERR_NOTFOUND;
+       }
+       *pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
+       return SXRET_OK;
+}
+/*
+ * Install a given constant index in the literal table.
+ * In order to be installed, the jx9_value must be of type string.
+ */
+static sxi32 GenStateInstallLiteral(jx9_gen_state *pGen,jx9_value *pObj, sxu32 nIdx)
+{
+       if( SyBlobLength(&pObj->sBlob) > 0 ){
+               SyHashInsert(&pGen->hLiteral, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), SX_INT_TO_PTR(nIdx));
+       }
+       return SXRET_OK;
+}
+/*
+ * Generate a fatal error.
+ */
+static sxi32 GenStateOutOfMem(jx9_gen_state *pGen)
+{
+       jx9GenCompileError(pGen,E_ERROR,1,"Fatal, Jx9 compiler is running out of memory");
+       /* Abort compilation immediately */
+       return SXERR_ABORT;
+}
+/*
+ * Fetch a block that correspond to the given criteria from the stack of
+ * compiled blocks.
+ * Return a pointer to that block on success. NULL otherwise.
+ */
+static GenBlock * GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount)
+{
+       GenBlock *pBlock = pCurrent;
+       for(;;){
+               if( pBlock->iFlags & iBlockType ){
+                       iCount--; /* Decrement nesting level */
+                       if( iCount < 1 ){
+                               /* Block meet with the desired criteria */
+                               return pBlock;
+                       }
+               }
+               /* Point to the upper block */
+               pBlock = pBlock->pParent;
+               if( pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC)) ){
+                       /* Forbidden */
+                       break;
+               }
+       }
+       /* No such block */
+       return 0;
+}
+/*
+ * Initialize a freshly allocated block instance.
+ */
+static void GenStateInitBlock(
+       jx9_gen_state *pGen, /* Code generator state */
+       GenBlock *pBlock,    /* Target block */
+       sxi32 iType,         /* Block type [i.e: loop, conditional, function body, etc.]*/
+       sxu32 nFirstInstr,   /* First instruction to compile */
+       void *pUserData      /* Upper layer private data */
+       )
+{
+       /* Initialize block fields */
+       pBlock->nFirstInstr = nFirstInstr;
+       pBlock->pUserData   = pUserData;
+       pBlock->pGen        = pGen;
+       pBlock->iFlags      = iType;
+       pBlock->pParent     = 0;
+       pBlock->bPostContinue = 0;
+       SySetInit(&pBlock->aJumpFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
+       SySetInit(&pBlock->aPostContFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
+}
+/*
+ * Allocate a new block instance.
+ * Return SXRET_OK and write a pointer to the new instantiated block
+ * on success.Otherwise generate a compile-time error and abort
+ * processing on failure.
+ */
+static sxi32 GenStateEnterBlock(
+       jx9_gen_state *pGen,  /* Code generator state */
+       sxi32 iType,          /* Block type [i.e: loop, conditional, function body, etc.]*/
+       sxu32 nFirstInstr,    /* First instruction to compile */
+       void *pUserData,      /* Upper layer private data */
+       GenBlock **ppBlock    /* OUT: instantiated block */
+       )
+{
+       GenBlock *pBlock;
+       /* Allocate a new block instance */
+       pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(GenBlock));
+       if( pBlock == 0 ){
+               /* If the supplied memory subsystem is so sick that we are unable to allocate
+                * a tiny chunk of memory, there is no much we can do here.
+                */
+               return GenStateOutOfMem(pGen);
+       }
+       /* Zero the structure */
+       SyZero(pBlock, sizeof(GenBlock));
+       GenStateInitBlock(&(*pGen), pBlock, iType, nFirstInstr, pUserData);
+       /* Link to the parent block */
+       pBlock->pParent = pGen->pCurrent;
+       /* Mark as the current block */
+       pGen->pCurrent = pBlock;
+       if( ppBlock ){
+               /* Write a pointer to the new instance */
+               *ppBlock = pBlock;
+       }
+       return SXRET_OK;
+}
+/*
+ * Release block fields without freeing the whole instance.
+ */
+static void GenStateReleaseBlock(GenBlock *pBlock)
+{
+       SySetRelease(&pBlock->aPostContFix);
+       SySetRelease(&pBlock->aJumpFix);
+}
+/*
+ * Release a block.
+ */
+static void GenStateFreeBlock(GenBlock *pBlock)
+{
+       jx9_gen_state *pGen = pBlock->pGen;
+       GenStateReleaseBlock(&(*pBlock));
+       /* Free the instance */
+       SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
+}
+/*
+ * POP and release a block from the stack of compiled blocks.
+ */
+static sxi32 GenStateLeaveBlock(jx9_gen_state *pGen, GenBlock **ppBlock)
+{
+       GenBlock *pBlock = pGen->pCurrent;
+       if( pBlock == 0 ){
+               /* No more block to pop */
+               return SXERR_EMPTY;
+       }
+       /* Point to the upper block */
+       pGen->pCurrent = pBlock->pParent;
+       if( ppBlock ){
+               /* Write a pointer to the popped block */
+               *ppBlock = pBlock;
+       }else{
+               /* Safely release the block */
+               GenStateFreeBlock(&(*pBlock));  
+       }
+       return SXRET_OK;
+}
+/*
+ * Emit a forward jump.
+ * Notes on forward jumps
+ *  Compilation of some JX9 constructs such as if, for, while and the logical or
+ *  (||) and logical and (&&) operators in expressions requires the
+ *  generation of forward jumps.
+ *  Since the destination PC target of these jumps isn't known when the jumps
+ *  are emitted, we record each forward jump in an instance of the following
+ *  structure. Those jumps are fixed later when the jump destination is resolved.
+ */
+static sxi32 GenStateNewJumpFixup(GenBlock *pBlock, sxi32 nJumpType, sxu32 nInstrIdx)
+{
+       JumpFixup sJumpFix;
+       sxi32 rc;
+       /* Init the JumpFixup structure */
+       sJumpFix.nJumpType = nJumpType;
+       sJumpFix.nInstrIdx = nInstrIdx;
+       /* Insert in the jump fixup table */
+       rc = SySetPut(&pBlock->aJumpFix,(const void *)&sJumpFix);
+       return rc;
+}
+/*
+ * Fix a forward jump now the jump destination is resolved.
+ * Return the total number of fixed jumps.
+ * Notes on forward jumps:
+ *  Compilation of some JX9 constructs such as if, for, while and the logical or
+ *  (||) and logical and (&&) operators in expressions requires the
+ *  generation of forward jumps.
+ *  Since the destination PC target of these jumps isn't known when the jumps
+ *  are emitted, we record each forward jump in an instance of the following
+ *  structure.Those jumps are fixed later when the jump destination is resolved.
+ */
+static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest)
+{
+       JumpFixup *aFix;
+       VmInstr *pInstr;
+       sxu32 nFixed; 
+       sxu32 n;
+       /* Point to the jump fixup table */
+       aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix);
+       /* Fix the desired jumps */
+       for( nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n ){
+               if( aFix[n].nJumpType < 0 ){
+                       /* Already fixed */
+                       continue;
+               }
+               if( nJumpType > 0 && aFix[n].nJumpType != nJumpType ){
+                       /* Not of our interest */
+                       continue;
+               }
+               /* Point to the instruction to fix */
+               pInstr = jx9VmGetInstr(pBlock->pGen->pVm, aFix[n].nInstrIdx);
+               if( pInstr ){
+                       pInstr->iP2 = nJumpDest;
+                       nFixed++;
+                       /* Mark as fixed */
+                       aFix[n].nJumpType = -1;
+               }
+       }
+       /* Total number of fixed jumps */
+       return nFixed;
+}
+/*
+ * Reserve a room for a numeric constant [i.e: 64-bit integer or real number]
+ * in the constant table.
+ */
+static jx9_value * GenStateInstallNumLiteral(jx9_gen_state *pGen, sxu32 *pIdx)
+{
+       jx9_value *pObj;
+       sxu32 nIdx = 0; /* cc warning */
+       /* Reserve a new constant */
+       pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+       if( pObj == 0 ){
+               GenStateOutOfMem(pGen);
+               return 0;
+       }
+       *pIdx = nIdx;
+       /* TODO(chems): Create a numeric table (64bit int keys) same as 
+        * the constant string iterals table [optimization purposes].
+        */
+       return pObj;
+}
+/*
+ * Compile a numeric [i.e: integer or real] literal.
+ * Notes on the integer type.
+ *  According to the JX9 language reference manual
+ *  Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8)
+ *  or binary (base 2) notation, optionally preceded by a sign (- or +). 
+ *  To use octal notation, precede the number with a 0 (zero). To use hexadecimal 
+ *  notation precede the number with 0x. To use binary notation precede the number with 0b.
+ */
+static sxi32 jx9CompileNumLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
+{
+       SyToken *pToken = pGen->pIn; /* Raw token */
+       sxu32 nIdx = 0;
+       if( pToken->nType & JX9_TK_INTEGER ){
+               jx9_value *pObj;
+               sxi64 iValue;
+               iValue = jx9TokenValueToInt64(&pToken->sData);
+               pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx);
+               if( pObj == 0 ){
+                       SXUNUSED(iCompileFlag); /* cc warning */
+                       return SXERR_ABORT;
+               }
+               jx9MemObjInitFromInt(pGen->pVm, pObj, iValue);
+       }else{
+               /* Real number */
+               jx9_value *pObj;
+               /* Reserve a new constant */
+               pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+               if( pObj == 0 ){
+                       return GenStateOutOfMem(pGen);
+               }
+               jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
+               jx9MemObjToReal(pObj);
+       }
+       /* Emit the load constant instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile a nowdoc string.
+ * According to the JX9 language reference manual:
+ *
+ *  Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
+ *  A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
+ *  The construct is ideal for embedding JX9 code or other large blocks of text without the
+ *  need for escaping. It shares some features in common with the SGML <![CDATA[ ]]> 
+ *  construct, in that it declares a block of text which is not for parsing.
+ *  A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier
+ *  which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc 
+ *  identifiers also apply to nowdoc identifiers, especially those regarding the appearance
+ *  of the closing identifier. 
+ */
+static sxi32 jx9CompileNowdoc(jx9_gen_state *pGen,sxi32 iCompileFlag)
+{
+       SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
+       jx9_value *pObj;
+       sxu32 nIdx;
+       nIdx = 0; /* Prevent compiler warning */
+       if( pStr->nByte <= 0 ){
+               /* Empty string, load NULL */
+               jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC, 0, 0, 0, 0);
+               return SXRET_OK;
+       }
+       /* Reserve a new constant */
+       pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+       if( pObj == 0 ){
+               jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "JX9 engine is running out of memory");
+               SXUNUSED(iCompileFlag); /* cc warning */
+               return SXERR_ABORT;
+       }
+       /* No processing is done here, simply a memcpy() operation */
+       jx9MemObjInitFromString(pGen->pVm, pObj, pStr);
+       /* Emit the load constant instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile a single quoted string.
+ * According to the JX9 language reference manual:
+ *
+ *   The simplest way to specify a string is to enclose it in single quotes (the character ' ).
+ *   To specify a literal single quote, escape it with a backslash (\). To specify a literal
+ *   backslash, double it (\\). All other instances of backslash will be treated as a literal
+ *   backslash: this means that the other escape sequences you might be used to, such as \r 
+ *   or \n, will be output literally as specified rather than having any special meaning.
+ * 
+ */
+JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag)
+{
+       SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
+       const char *zIn, *zCur, *zEnd;
+       jx9_value *pObj;
+       sxu32 nIdx;
+       nIdx = 0; /* Prevent compiler warning */
+       /* Delimit the string */
+       zIn  = pStr->zString;
+       zEnd = &zIn[pStr->nByte];
+       if( zIn > zEnd ){
+               /* Empty string, load NULL */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
+               return SXRET_OK;
+       }
+       if( SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx) ){
+               /* Already processed, emit the load constant instruction
+                * and return.
+                */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+               return SXRET_OK;
+       }
+       /* Reserve a new constant */
+       pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+       if( pObj == 0 ){
+               jx9GenCompileError(&(*pGen), E_ERROR, 1, "JX9 engine is running out of memory");
+               SXUNUSED(iCompileFlag); /* cc warning */
+               return SXERR_ABORT;
+       }
+       jx9MemObjInitFromString(pGen->pVm, pObj, 0);
+       /* Compile the node */
+       for(;;){
+               if( zIn >= zEnd ){
+                       /* End of input */
+                       break;
+               }
+               zCur = zIn;
+               while( zIn < zEnd && zIn[0] != '\\' ){
+                       zIn++;
+               }
+               if( zIn > zCur ){
+                       /* Append raw contents*/
+                       jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
+               }
+               else
+               {
+                       jx9MemObjStringAppend(pObj, "", 0);
+               }
+               zIn++;
+               if( zIn < zEnd ){
+                       if( zIn[0] == '\\' ){
+                               /* A literal backslash */
+                               jx9MemObjStringAppend(pObj, "\\", sizeof(char));
+                       }else if( zIn[0] == '\'' ){
+                               /* A single quote */
+                               jx9MemObjStringAppend(pObj, "'", sizeof(char));
+                       }else{
+                               /* verbatim copy */
+                               zIn--;
+                               jx9MemObjStringAppend(pObj, zIn, sizeof(char)*2);
+                               zIn++;
+                       }
+               }
+               /* Advance the stream cursor */
+               zIn++;
+       }
+       /* Emit the load constant instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+       if( pStr->nByte < 1024 ){
+               /* Install in the literal table */
+               GenStateInstallLiteral(pGen, pObj, nIdx);
+       }
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Process variable expression [i.e: "$var", "${var}"] embedded in a double quoted/heredoc string.
+ * According to the JX9 language reference manual
+ *   When a string is specified in double quotes or with heredoc, variables are parsed within it.
+ *  There are two types of syntax: a simple one and a complex one. The simple syntax is the most
+ *  common and convenient. It provides a way to embed a variable, an array value, or an object
+ *  property in a string with a minimum of effort.
+ *  Simple syntax
+ *   If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible
+ *   to form a valid variable name. Enclose the variable name in curly braces to explicitly specify
+ *   the end of the name.
+ *   Similarly, an array index or an object property can be parsed. With array indices, the closing
+ *   square bracket (]) marks the end of the index. The same rules apply to object properties
+ *   as to simple variables. 
+ *  Complex (curly) syntax
+ *   This isn't called complex because the syntax is complex, but because it allows for the use 
+ *   of complex expressions.
+ *   Any scalar variable, array element or object property with a string representation can be
+ *   included via this syntax. Simply write the expression the same way as it would appear outside
+ *   the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only
+ *   be recognised when the $ immediately follows the {. Use {\$ to get a literal {$
+ */
+static sxi32 GenStateProcessStringExpression(
+       jx9_gen_state *pGen, /* Code generator state */
+       const char *zIn,     /* Raw expression */
+       const char *zEnd     /* End of the expression */
+       )
+{
+       SyToken *pTmpIn, *pTmpEnd;
+       SySet sToken;
+       sxi32 rc;
+       /* Initialize the token set */
+       SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken));
+       /* Preallocate some slots */
+       SySetAlloc(&sToken, 0x08);
+       /* Tokenize the text */
+       jx9Tokenize(zIn,(sxu32)(zEnd-zIn),&sToken);
+       /* Swap delimiter */
+       pTmpIn  = pGen->pIn;
+       pTmpEnd = pGen->pEnd;
+       pGen->pIn = (SyToken *)SySetBasePtr(&sToken);
+       pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)];
+       /* Compile the expression */
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       /* Restore token stream */
+       pGen->pIn  = pTmpIn;
+       pGen->pEnd = pTmpEnd;
+       /* Release the token set */
+       SySetRelease(&sToken);
+       /* Compilation result */
+       return rc;
+}
+/*
+ * Reserve a new constant for a double quoted/heredoc string.
+ */
+static jx9_value * GenStateNewStrObj(jx9_gen_state *pGen,sxi32 *pCount)
+{
+       jx9_value *pConstObj;
+       sxu32 nIdx = 0;
+       /* Reserve a new constant */
+       pConstObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+       if( pConstObj == 0 ){
+               GenStateOutOfMem(&(*pGen));
+               return 0;
+       }
+       (*pCount)++;
+       jx9MemObjInitFromString(pGen->pVm, pConstObj, 0);
+       /* Emit the load constant instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+       return pConstObj;
+}
+/*
+ * Compile a double quoted/heredoc string.
+ * According to the JX9 language reference manual
+ * Heredoc
+ *  A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
+ *  is provided, then a newline. The string itself follows, and then the same identifier again
+ *  to close the quotation.
+ *  The closing identifier must begin in the first column of the line. Also, the identifier must
+ *  follow the same naming rules as any other label in JX9: it must contain only alphanumeric
+ *  characters and underscores, and must start with a non-digit character or underscore.
+ *  Warning
+ *  It is very important to note that the line with the closing identifier must contain
+ *  no other characters, except possibly a semicolon (;). That means especially that the identifier
+ *  may not be indented, and there may not be any spaces or tabs before or after the semicolon.
+ *  It's also important to realize that the first character before the closing identifier must
+ *  be a newline as defined by the local operating system. This is \n on UNIX systems, including Mac OS X.
+ *  The closing delimiter (possibly followed by a semicolon) must also be followed by a newline.
+ *  If this rule is broken and the closing identifier is not "clean", it will not be considered a closing
+ *  identifier, and JX9 will continue looking for one. If a proper closing identifier is not found before
+ *  the end of the current file, a parse error will result at the last line.
+ *  Heredocs can not be used for initializing object properties. 
+ * Double quoted
+ *  If the string is enclosed in double-quotes ("), JX9 will interpret more escape sequences for special characters:
+ *  Escaped characters Sequence        Meaning
+ *  \n linefeed (LF or 0x0A (10) in ASCII)
+ *  \r carriage return (CR or 0x0D (13) in ASCII)
+ *  \t horizontal tab (HT or 0x09 (9) in ASCII)
+ *  \v vertical tab (VT or 0x0B (11) in ASCII)
+ *  \f form feed (FF or 0x0C (12) in ASCII)
+ *  \\ backslash
+ *  \$ dollar sign
+ *  \" double-quote
+ *  \[0-7]{1, 3}       the sequence of characters matching the regular expression is a character in octal notation
+ *  \x[0-9A-Fa-f]{1, 2}        the sequence of characters matching the regular expression is a character in hexadecimal notation
+ * As in single quoted strings, escaping any other character will result in the backslash being printed too.
+ * The most important feature of double-quoted strings is the fact that variable names will be expanded.
+ * See string parsing for details.
+ */
+static sxi32 GenStateCompileString(jx9_gen_state *pGen)
+{
+       SyString *pStr = &pGen->pIn->sData; /* Raw token value */
+       const char *zIn, *zCur, *zEnd;
+       jx9_value *pObj = 0;
+       sxi32 iCons;    
+       sxi32 rc;
+       /* Delimit the string */
+       zIn  = pStr->zString;
+       zEnd = &zIn[pStr->nByte];
+       if( zIn > zEnd ){
+               /* Empty string, load NULL */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
+               return SXRET_OK;
+       }
+       zCur = 0;
+       /* Compile the node */
+       iCons = 0;
+       for(;;){
+               zCur = zIn;
+               while( zIn < zEnd && zIn[0] != '\\'  ){
+                       if(zIn[0] == '$' && &zIn[1] < zEnd &&
+                               (((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '_')) ){
+                                       break;
+                       }
+                       zIn++;
+               }
+               if( zIn > zCur ){
+                       if( pObj == 0 ){
+                               pObj = GenStateNewStrObj(&(*pGen), &iCons);
+                               if( pObj == 0 ){
+                                       return SXERR_ABORT;
+                               }
+                       }
+                       jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
+               }
+               else
+               {
+                       if( pObj == 0 ){
+                               pObj = GenStateNewStrObj(&(*pGen), &iCons);
+                               if( pObj == 0 ){
+                                       return SXERR_ABORT;
+                               }
+                       }
+                       jx9MemObjStringAppend(pObj, "", 0);
+               }
+               if( zIn >= zEnd ){
+                       break;
+               }
+               if( zIn[0] == '\\' ){
+                       const char *zPtr = 0;
+                       sxu32 n;
+                       zIn++;
+                       if( zIn >= zEnd ){
+                               break;
+                       }
+                       if( pObj == 0 ){
+                               pObj = GenStateNewStrObj(&(*pGen), &iCons);
+                               if( pObj == 0 ){
+                                       return SXERR_ABORT;
+                               }
+                       }
+                       n = sizeof(char); /* size of conversion */
+                       switch( zIn[0] ){
+                       case '$':
+                               /* Dollar sign */
+                               jx9MemObjStringAppend(pObj, "$", sizeof(char));
+                               break;
+                       case '\\':
+                               /* A literal backslash */
+                               jx9MemObjStringAppend(pObj, "\\", sizeof(char));
+                               break;
+                       case 'a':
+                               /* The "alert" character (BEL)[ctrl+g] ASCII code 7 */
+                               jx9MemObjStringAppend(pObj, "\a", sizeof(char));
+                               break;
+                       case 'b':
+                               /* Backspace (BS)[ctrl+h] ASCII code 8 */
+                               jx9MemObjStringAppend(pObj, "\b", sizeof(char));
+                               break;
+                       case 'f':
+                               /* Form-feed (FF)[ctrl+l] ASCII code 12 */
+                               jx9MemObjStringAppend(pObj, "\f", sizeof(char));
+                               break;
+                       case 'n':
+                               /* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */
+                               jx9MemObjStringAppend(pObj, "\n", sizeof(char));
+                               break;
+                       case 'r':
+                               /* Carriage return (CR)[ctrl+m] ASCII code 13 */
+                               jx9MemObjStringAppend(pObj, "\r", sizeof(char));
+                               break;
+                       case 't':
+                               /* Horizontal tab (HT)[ctrl+i] ASCII code 9 */
+                               jx9MemObjStringAppend(pObj, "\t", sizeof(char));
+                               break;
+                       case 'v':
+                               /* Vertical tab(VT)[ctrl+k] ASCII code 11 */
+                               jx9MemObjStringAppend(pObj, "\v", sizeof(char));
+                               break;
+                       case '\'':
+                               /* Single quote */
+                               jx9MemObjStringAppend(pObj, "'", sizeof(char));
+                               break;
+                       case '"':
+                               /* Double quote */
+                               jx9MemObjStringAppend(pObj, "\"", sizeof(char));
+                               break;
+                       case '0':
+                               /* NUL byte */
+                               jx9MemObjStringAppend(pObj, "\0", sizeof(char));
+                               break;
+                       case 'x':
+                               if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1]) ){
+                                       int c;
+                                       /* Hex digit */
+                                       c = SyHexToint(zIn[1]) << 4;
+                                       if( &zIn[2] < zEnd ){
+                                               c +=  SyHexToint(zIn[2]);
+                                       }
+                                       /* Output char */
+                                       jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
+                                       n += sizeof(char) * 2;
+                               }else{
+                                       /* Output literal character  */
+                                       jx9MemObjStringAppend(pObj, "x", sizeof(char));
+                               }
+                               break;
+                       case 'o':
+                               if( &zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8 ){
+                                       /* Octal digit stream */
+                                       int c;
+                                       c = 0;
+                                       zIn++;
+                                       for( zPtr = zIn ; zPtr < &zIn[3*sizeof(char)] ; zPtr++ ){
+                                               if( zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7 ){
+                                                       break;
+                                               }
+                                               c = c * 8 + (zPtr[0] - '0');
+                                       }
+                                       if ( c > 0 ){
+                                               jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
+                                       }
+                                       n = (sxu32)(zPtr-zIn);
+                               }else{
+                                       /* Output literal character  */
+                                       jx9MemObjStringAppend(pObj, "o", sizeof(char));
+                               }
+                               break;
+                       default:
+                               /* Output without a slash */
+                               jx9MemObjStringAppend(pObj, zIn, sizeof(char));
+                               break;
+                       }
+                       /* Advance the stream cursor */
+                       zIn += n;
+                       continue;
+               }
+               if( zIn[0] == '{' ){
+                       /* Curly syntax */
+                       const char *zExpr;
+                       sxi32 iNest = 1;
+                       zIn++;
+                       zExpr = zIn;
+                       /* Synchronize with the next closing curly braces */
+                       while( zIn < zEnd ){
+                               if( zIn[0] == '{' ){
+                                       /* Increment nesting level */
+                                       iNest++;
+                               }else if(zIn[0] == '}' ){
+                                       /* Decrement nesting level */
+                                       iNest--;
+                                       if( iNest <= 0 ){
+                                               break;
+                                       }
+                               }
+                               zIn++;
+                       }
+                       /* Process the expression */
+                       rc = GenStateProcessStringExpression(&(*pGen),zExpr,zIn);
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       if( rc != SXERR_EMPTY ){
+                               ++iCons;
+                       }
+                       if( zIn < zEnd ){
+                               /* Jump the trailing curly */
+                               zIn++;
+                       }
+               }else{
+                       /* Simple syntax */
+                       const char *zExpr = zIn;
+                       /* Assemble variable name */
+                       for(;;){
+                               /* Jump leading dollars */
+                               while( zIn < zEnd && zIn[0] == '$' ){
+                                       zIn++;
+                               }
+                               for(;;){
+                                       while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_' ) ){
+                                               zIn++;
+                                       }
+                                       if((unsigned char)zIn[0] >= 0xc0 ){
+                                               /* UTF-8 stream */
+                                               zIn++;
+                                               while( zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){
+                                                       zIn++;
+                                               }
+                                               continue;
+                                       }
+                                       break;
+                               }
+                               if( zIn >= zEnd ){
+                                       break;
+                               }
+                               if( zIn[0] == '[' ){
+                                       sxi32 iSquare = 1;
+                                       zIn++;
+                                       while( zIn < zEnd ){
+                                               if( zIn[0] == '[' ){
+                                                       iSquare++;
+                                               }else if (zIn[0] == ']' ){
+                                                       iSquare--;
+                                                       if( iSquare <= 0 ){
+                                                               break;
+                                                       }
+                                               }
+                                               zIn++;
+                                       }
+                                       if( zIn < zEnd ){
+                                               zIn++;
+                                       }
+                                       break;
+                               }else if( zIn[0] == '.' ){
+                                       /* Member access operator '.' */
+                                       zIn++;
+                               }else{
+                                       break;
+                               }
+                       }
+                       /* Process the expression */
+                       rc = GenStateProcessStringExpression(&(*pGen),zExpr, zIn);
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       if( rc != SXERR_EMPTY ){
+                               ++iCons;
+                       }
+               }
+               /* Invalidate the previously used constant */
+               pObj = 0;
+       }/*for(;;)*/
+       if( iCons > 1 ){
+               /* Concatenate all compiled constants */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_CAT, iCons, 0, 0, 0);
+       }
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile a double quoted string.
+ *  See the block-comment above for more information.
+ */
+JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag)
+{
+       sxi32 rc;
+       rc = GenStateCompileString(&(*pGen));
+       SXUNUSED(iCompileFlag); /* cc warning */
+       /* Compilation result */
+       return rc;
+}
+/*
+ * Compile a literal which is an identifier(name) for simple values.
+ */
+JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
+{
+       SyToken *pToken = pGen->pIn;
+       jx9_value *pObj;
+       SyString *pStr; 
+       sxu32 nIdx;
+       /* Extract token value */
+       pStr = &pToken->sData;
+       /* Deal with the reserved literals [i.e: null, false, true, ...] first */
+       if( pStr->nByte == sizeof("NULL") - 1 ){
+               if( SyStrnicmp(pStr->zString, "null", sizeof("NULL")-1) == 0 ){
+                       /* NULL constant are always indexed at 0 */
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
+                       return SXRET_OK;
+               }else if( SyStrnicmp(pStr->zString, "true", sizeof("TRUE")-1) == 0 ){
+                       /* TRUE constant are always indexed at 1 */
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1, 0, 0);
+                       return SXRET_OK;
+               }
+       }else if (pStr->nByte == sizeof("FALSE") - 1 &&
+               SyStrnicmp(pStr->zString, "false", sizeof("FALSE")-1) == 0 ){
+                       /* FALSE constant are always indexed at 2 */
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 2, 0, 0);
+                       return SXRET_OK;
+       }else if(pStr->nByte == sizeof("__LINE__") - 1 &&
+               SyMemcmp(pStr->zString, "__LINE__", sizeof("__LINE__")-1) == 0 ){
+                       /* TICKET 1433-004: __LINE__ constant must be resolved at compile time, not run time */
+                       pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+                       if( pObj == 0 ){
+                               SXUNUSED(iCompileFlag); /* cc warning */
+                               return GenStateOutOfMem(pGen);
+                       }
+                       jx9MemObjInitFromInt(pGen->pVm, pObj, pToken->nLine);
+                       /* Emit the load constant instruction */
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+                       return SXRET_OK;
+       }else if( pStr->nByte == sizeof("__FUNCTION__") - 1 &&
+               SyMemcmp(pStr->zString, "__FUNCTION__", sizeof("__FUNCTION__")-1) == 0 ){
+                       GenBlock *pBlock = pGen->pCurrent;
+                       /* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time, not run time */
+                       while( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0 ){
+                               /* Point to the upper block */
+                               pBlock = pBlock->pParent;
+                       }
+                       if( pBlock == 0 ){
+                               /* Called in the global scope, load NULL */
+                               jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
+                       }else{
+                               /* Extract the target function/method */
+                               jx9_vm_func *pFunc = (jx9_vm_func *)pBlock->pUserData;
+                               pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+                               if( pObj == 0 ){
+                                       return GenStateOutOfMem(pGen);
+                               }
+                               jx9MemObjInitFromString(pGen->pVm, pObj, &pFunc->sName);
+                               /* Emit the load constant instruction */
+                               jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+                       }
+                       return SXRET_OK;
+       }
+       /* Query literal table */
+       if( SXRET_OK != GenStateFindLiteral(&(*pGen), &pToken->sData, &nIdx) ){
+               jx9_value *pObj;
+               /* Unknown literal, install it in the literal table */
+               pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+               if( pObj == 0 ){
+                       return GenStateOutOfMem(pGen);
+               }
+               jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
+               GenStateInstallLiteral(&(*pGen), pObj, nIdx);
+       }
+       /* Emit the load constant instruction */
+       jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC,1,nIdx, 0, 0);
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile an array entry whether it is a key or a value.
+ */
+static sxi32 GenStateCompileJSONEntry(
+       jx9_gen_state *pGen, /* Code generator state */
+       SyToken *pIn,        /* Token stream */
+       SyToken *pEnd,       /* End of the token stream */
+       sxi32 iFlags,        /* Compilation flags */
+       sxi32 (*xValidator)(jx9_gen_state *,jx9_expr_node *) /* Expression tree validator callback */
+       )
+{
+       SyToken *pTmpIn, *pTmpEnd;
+       sxi32 rc;
+       /* Swap token stream */
+       SWAP_DELIMITER(pGen, pIn, pEnd);
+       /* Compile the expression*/
+       rc = jx9CompileExpr(&(*pGen), iFlags, xValidator);
+       /* Restore token stream */
+       RE_SWAP_DELIMITER(pGen);
+       return rc;
+}
+/* 
+ * Compile a Jx9 JSON Array.
+ */
+JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag)
+{
+       sxi32 nPair = 0;
+       SyToken *pCur;
+       sxi32 rc;
+
+       pGen->pIn++; /* Jump the open square bracket '['*/
+       pGen->pEnd--;
+       SXUNUSED(iCompileFlag); /* cc warning */
+       for(;;){
+               /* Jump leading commas */
+               while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
+                       pGen->pIn++;
+               }
+               pCur = pGen->pIn;
+               if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
+                       /* No more entry to process */
+                       break;
+               }
+               /* Compile entry */
+               rc = GenStateCompileJSONEntry(&(*pGen),pCur,pGen->pIn,EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+               nPair++;
+       }
+       /* Emit the load map instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP,nPair,0,0,0);
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Node validator for a given JSON key.
+ */
+static sxi32 GenStateJSONObjectKeyNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
+{
+       sxi32 rc = SXRET_OK;
+       if( pRoot->xCode != jx9CompileVariable && pRoot->xCode != jx9CompileString 
+               && pRoot->xCode != jx9CompileSimpleString && pRoot->xCode != jx9CompileLiteral ){
+               /* Unexpected expression */
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pRoot->pStart? pRoot->pStart->nLine : 0, 
+                       "JSON Object: Unexpected expression, key must be of type string, literal or simple variable");
+               if( rc != SXERR_ABORT ){
+                       rc = SXERR_INVALID;
+               }
+       }
+       return rc;
+}
+/* 
+ * Compile a Jx9 JSON Object
+ */
+JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag)
+{
+       SyToken *pKey, *pCur;
+       sxi32 nPair = 0;
+       sxi32 rc;
+
+       pGen->pIn++; /* Jump the open querly braces '{'*/
+       pGen->pEnd--;
+       SXUNUSED(iCompileFlag); /* cc warning */
+       for(;;){
+               /* Jump leading commas */
+               while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
+                       pGen->pIn++;
+               }
+               pCur = pGen->pIn;
+               if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
+                       /* No more entry to process */
+                       break;
+               }
+               /* Compile the key */
+               pKey = pCur;
+               while( pCur < pGen->pIn ){
+                       if( pCur->nType & JX9_TK_COLON /*':'*/  ){
+                               break;
+                       }
+                       pCur++;
+               }
+               rc = SXERR_EMPTY;
+               if( (pCur->nType & JX9_TK_COLON) == 0 ){
+                       rc = jx9GenCompileError(&(*pGen), E_ABORT, pCur->nLine, "JSON Object: Missing colon string \":\"");
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       return SXRET_OK;
+               }
+
+               if( pCur < pGen->pIn ){
+                       if( &pCur[1] >= pGen->pIn ){
+                               /* Missing value */
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "JSON Object: Missing entry value");
+                               if( rc == SXERR_ABORT ){
+                                       return SXERR_ABORT;
+                               }
+                               return SXRET_OK;
+                       }
+                       /* Compile the expression holding the key */
+                       rc = GenStateCompileJSONEntry(&(*pGen), pKey, pCur,
+                               EXPR_FLAG_RDONLY_LOAD                /* Do not create the variable if inexistant */, 
+                               GenStateJSONObjectKeyNodeValidator   /* Node validator callback */
+                               );
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       pCur++; /* Jump the double colon ':'  */
+               }else if( pKey == pCur ){
+                       /* Key is omitted, emit an error */
+                       jx9GenCompileError(&(*pGen),E_ERROR, pCur->nLine, "JSON Object: Missing entry key");
+                       pCur++; /* Jump the double colon ':'  */
+               }else{
+                       /* Reset back the cursor and point to the entry value */
+                       pCur = pKey;
+               }
+               /* Compile indice value */
+               rc = GenStateCompileJSONEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+               nPair++;
+       }
+       /* Emit the load map instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP, nPair * 2, 1, 0, 0);
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile a function [i.e: print, exit(), include(), ...] which is a langauge
+ * construct.
+ */
+JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen,sxi32 iCompileFlag)
+{
+       SyString *pName;
+       sxu32 nKeyID;
+       sxi32 rc;
+       /* Name of the language construct [i.e: print, die...]*/
+       pName = &pGen->pIn->sData;
+       nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
+       pGen->pIn++; /* Jump the language construct keyword */
+       if( nKeyID == JX9_TKWRD_PRINT ){
+               SyToken *pTmp, *pNext = 0;
+               /* Compile arguments one after one */
+               pTmp = pGen->pEnd;
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1 /* Boolean true index */, 0, 0);
+               while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
+                       if( pGen->pIn < pNext ){
+                               pGen->pEnd = pNext;
+                               rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
+                               if( rc == SXERR_ABORT ){
+                                       return SXERR_ABORT;
+                               }
+                               if( rc != SXERR_EMPTY ){
+                                       /* Ticket 1433-008: Optimization #1: Consume input directly 
+                                        * without the overhead of a function call.
+                                        * This is a very powerful optimization that improve
+                                        * performance greatly.
+                                        */
+                                       jx9VmEmitInstr(pGen->pVm,JX9_OP_CONSUME,1,0,0,0);
+                               }
+                       }
+                       /* Jump trailing commas */
+                       while( pNext < pTmp && (pNext->nType & JX9_TK_COMMA) ){
+                               pNext++;
+                       }
+                       pGen->pIn = pNext;
+               }
+               /* Restore token stream */
+               pGen->pEnd = pTmp;      
+       }else{
+               sxi32 nArg = 0;
+               sxu32 nIdx = 0;
+               rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }else if(rc != SXERR_EMPTY ){
+                       nArg = 1;
+               }
+               if( SXRET_OK != GenStateFindLiteral(&(*pGen), pName, &nIdx) ){
+                       jx9_value *pObj;
+                       /* Emit the call instruction */
+                       pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+                       if( pObj == 0 ){
+                               SXUNUSED(iCompileFlag); /* cc warning */
+                               return GenStateOutOfMem(pGen);
+                       }
+                       jx9MemObjInitFromString(pGen->pVm, pObj, pName);
+                       /* Install in the literal table */
+                       GenStateInstallLiteral(&(*pGen), pObj, nIdx);
+               }
+               /* Emit the call instruction */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_CALL, nArg, 0, 0, 0);
+       }
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile a node holding a variable declaration.
+ * According to the J9X language reference
+ *  Variables in JX9 are represented by a dollar sign followed by the name of the variable.
+ *  The variable name is case-sensitive.
+ *  Variable names follow the same rules as other labels in JX9. A valid variable name
+ *  starts with a letter, underscore or any UTF-8 stream, followed by any number of letters
+ *  numbers, or underscores.
+ *  By default, variables are always assigned by value unless the target value is a JSON
+ *  array or a JSON object which is passed by reference.
+ */
+JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen,sxi32 iCompileFlag)
+{
+       sxu32 nLine = pGen->pIn->nLine;
+       SyHashEntry *pEntry;
+       SyString *pName;
+       char *zName = 0;
+       sxi32 iP1;
+       void *p3;
+       sxi32 rc;
+       
+       pGen->pIn++; /* Jump the dollar sign '$' */
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
+               /* Invalid variable name */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Invalid variable name");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               return SXRET_OK;
+       }
+       /* Extract variable name */
+       pName = &pGen->pIn->sData;
+       /* Advance the stream cursor */
+       pGen->pIn++;
+       pEntry = SyHashGet(&pGen->hVar, (const void *)pName->zString, pName->nByte);
+       if( pEntry == 0 ){
+               /* Duplicate name */
+               zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
+               if( zName == 0 ){
+                       return GenStateOutOfMem(pGen);
+               }
+               /* Install in the hashtable */
+               SyHashInsert(&pGen->hVar, zName, pName->nByte, zName);
+       }else{
+               /* Name already available */
+               zName = (char *)pEntry->pUserData;
+       }
+       p3 = (void *)zName;     
+       iP1 = 0;
+       if( iCompileFlag & EXPR_FLAG_RDONLY_LOAD ){
+               if( (iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0 ){
+                       /* Read-only load.In other words do not create the variable if inexistant */
+                       iP1 = 1;
+               }
+       }
+       /* Emit the load instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD, iP1, 0, p3, 0);
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/* Forward declaration */
+static sxi32 GenStateCompileFunc(jx9_gen_state *pGen,SyString *pName,sxi32 iFlags,jx9_vm_func **ppFunc);
+/*
+ * Compile an annoynmous function or a closure.
+ * According to the JX9 language reference
+ *  Anonymous functions, also known as closures, allow the creation of functions
+ *  which have no specified name. They are most useful as the value of callback
+ *  parameters, but they have many other uses. Closures can also be used as
+ *  the values of variables; Assigning a closure to a variable uses the same
+ *  syntax as any other assignment, including the trailing semicolon:
+ *  Example Anonymous function variable assignment example
+ * $greet = function($name)
+ * {
+ *    printf("Hello %s\r\n", $name);
+ * };
+ * $greet('World');
+ * $greet('JX9');
+ * Note that the implementation of annoynmous function and closure under
+ * JX9 is completely different from the one used by the  engine.
+ */
+JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen,sxi32 iCompileFlag)
+{
+       jx9_vm_func *pAnnonFunc; /* Annonymous function body */
+       char zName[512];         /* Unique lambda name */
+       static int iCnt = 1;     /* There is no worry about thread-safety here, because only
+                                                         * one thread is allowed to compile the script.
+                                                     */
+       jx9_value *pObj;
+       SyString sName;
+       sxu32 nIdx;
+       sxu32 nLen;
+       sxi32 rc;
+
+       pGen->pIn++; /* Jump the 'function' keyword */
+       if( pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
+               pGen->pIn++;
+       }
+       /* Reserve a constant for the lambda */
+       pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
+       if( pObj == 0 ){
+               GenStateOutOfMem(pGen);
+               SXUNUSED(iCompileFlag); /* cc warning */
+               return SXERR_ABORT;
+       }
+       /* Generate a unique name */
+       nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
+       /* Make sure the generated name is unique */
+       while( SyHashGet(&pGen->pVm->hFunction, zName, nLen) != 0 && nLen < sizeof(zName) - 2 ){
+               nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
+       }
+       SyStringInitFromBuf(&sName, zName, nLen);
+       jx9MemObjInitFromString(pGen->pVm, pObj, &sName);
+       /* Compile the lambda body */
+       rc = GenStateCompileFunc(&(*pGen),&sName,0,&pAnnonFunc);
+       if( rc == SXERR_ABORT ){
+               return SXERR_ABORT;
+       }
+       /* Emit the load constant instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
+       /* Node successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile the 'continue' statement.
+ * According to the JX9 language reference
+ *  continue is used within looping structures to skip the rest of the current loop iteration
+ *  and continue execution at the condition evaluation and then the beginning of the next
+ *  iteration.
+ *  Note: Note that in JX9 the switch statement is considered a looping structure for
+ *  the purposes of continue. 
+ *  continue accepts an optional numeric argument which tells it how many levels
+ *  of enclosing loops it should skip to the end of.
+ *  Note:
+ *   continue 0; and continue 1; is the same as running continue;. 
+ */
+static sxi32 jx9CompileContinue(jx9_gen_state *pGen)
+{
+       GenBlock *pLoop; /* Target loop */
+       sxi32 iLevel;    /* How many nesting loop to skip */
+       sxu32 nLine;
+       sxi32 rc;
+       nLine = pGen->pIn->nLine;
+       iLevel = 0;
+       /* Jump the 'continue' keyword */
+       pGen->pIn++;
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
+               /* optional numeric argument which tells us how many levels
+                * of enclosing loops we should skip to the end of. 
+                */
+               iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
+               if( iLevel < 2 ){
+                       iLevel = 0;
+               }
+               pGen->pIn++; /* Jump the optional numeric argument */
+       }
+       /* Point to the target loop */
+       pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
+       if( pLoop == 0 ){
+               /* Illegal continue */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+       }else{
+               sxu32 nInstrIdx = 0;
+               /* Emit the unconditional jump to the beginning of the target loop */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
+               if( pLoop->bPostContinue == TRUE ){
+                       JumpFixup sJumpFix;
+                       /* Post-continue */
+                       sJumpFix.nJumpType = JX9_OP_JMP;
+                       sJumpFix.nInstrIdx = nInstrIdx;
+                       SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
+               }
+       }
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
+               /* Not so fatal, emit a warning only */
+               jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement");
+       }
+       /* Statement successfully compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile the 'break' statement.
+ * According to the JX9 language reference
+ *  break ends execution of the current for, foreach, while, do-while or switch
+ *  structure.
+ *  break accepts an optional numeric argument which tells it how many nested
+ *  enclosing structures are to be broken out of. 
+ */
+static sxi32 jx9CompileBreak(jx9_gen_state *pGen)
+{
+       GenBlock *pLoop; /* Target loop */
+       sxi32 iLevel;    /* How many nesting loop to skip */
+       sxu32 nLine;
+       sxi32 rc;
+       nLine = pGen->pIn->nLine;
+       iLevel = 0;
+       /* Jump the 'break' keyword */
+       pGen->pIn++;
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
+               /* optional numeric argument which tells us how many levels
+                * of enclosing loops we should skip to the end of. 
+                */
+               iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
+               if( iLevel < 2 ){
+                       iLevel = 0;
+               }
+               pGen->pIn++; /* Jump the optional numeric argument */
+       }
+       /* Extract the target loop */
+       pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
+       if( pLoop == 0 ){
+               /* Illegal break */
+               rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+       }else{
+               sxu32 nInstrIdx; 
+               rc = jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nInstrIdx);
+               if( rc == SXRET_OK ){
+                       /* Fix the jump later when the jump destination is resolved */
+                       GenStateNewJumpFixup(pLoop, JX9_OP_JMP, nInstrIdx);
+               }
+       }
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
+               /* Not so fatal, emit a warning only */
+               jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement");
+       }
+       /* Statement successfully compiled */
+       return SXRET_OK;
+}
+/* Forward declaration */
+static sxi32 GenStateCompileChunk(jx9_gen_state *pGen,sxi32 iFlags);
+/*
+ * Compile a JX9 block.
+ * A block is simply one or more JX9 statements and expressions to compile
+ * optionally delimited by braces {}.
+ * Return SXRET_OK on success. Any other return value indicates failure
+ * and this function takes care of generating the appropriate error
+ * message.
+ */
+static sxi32 jx9CompileBlock(
+       jx9_gen_state *pGen /* Code generator state */
+       )
+{
+       sxi32 rc;
+       if( pGen->pIn->nType & JX9_TK_OCB /* '{' */ ){
+               sxu32 nLine = pGen->pIn->nLine;
+               rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, jx9VmInstrLength(pGen->pVm), 0, 0);
+               if( rc != SXRET_OK ){
+                       return SXERR_ABORT;
+               }
+               pGen->pIn++;
+               /* Compile until we hit the closing braces '}' */
+               for(;;){
+                       if( pGen->pIn >= pGen->pEnd ){
+                               /* No more token to process. Missing closing braces */
+                               jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing closing braces '}'");
+                               break;
+                       }
+                       if( pGen->pIn->nType & JX9_TK_CCB/*'}'*/ ){
+                               /* Closing braces found, break immediately*/
+                               pGen->pIn++;
+                               break;
+                       }
+                       /* Compile a single statement */
+                       rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+               }
+               GenStateLeaveBlock(&(*pGen), 0);                        
+       }else{
+               /* Compile a single statement */
+               rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+       }
+       /* Jump trailing semi-colons ';' */
+       while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
+               pGen->pIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile the gentle 'while' statement.
+ * According to the JX9 language reference
+ *  while loops are the simplest type of loop in JX9.They behave just like their C counterparts.
+ *  The basic form of a while statement is:
+ *  while (expr)
+ *   statement
+ *  The meaning of a while statement is simple. It tells JX9 to execute the nested statement(s)
+ *  repeatedly, as long as the while expression evaluates to TRUE. The value of the expression
+ *  is checked each time at the beginning of the loop, so even if this value changes during
+ *  the execution of the nested statement(s), execution will not stop until the end of the iteration
+ *  (each time JX9 runs the statements in the loop is one iteration). Sometimes, if the while
+ *  expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once.
+ *  Like with the if statement, you can group multiple statements within the same while loop by surrounding
+ *  a group of statements with curly braces, or by using the alternate syntax:
+ *  while (expr):
+ *    statement
+ *   endwhile;
+ */
+static sxi32 jx9CompileWhile(jx9_gen_state *pGen)
+{ 
+       GenBlock *pWhileBlock = 0;
+       SyToken *pTmp, *pEnd = 0;
+       sxu32 nFalseJump;
+       sxu32 nLine;
+       sxi32 rc;
+       nLine = pGen->pIn->nLine;
+       /* Jump the 'while' keyword */
+       pGen->pIn++;    
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
+               /* Syntax error */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               goto Synchronize;
+       }
+       /* Jump the left parenthesis '(' */
+       pGen->pIn++; 
+       /* Create the loop block */
+       rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pWhileBlock);
+       if( rc != SXRET_OK ){
+               return SXERR_ABORT;
+       }
+       /* Delimit the condition */
+       jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
+       if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
+               /* Empty expression */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+       }
+       /* Swap token streams */
+       pTmp = pGen->pEnd;
+       pGen->pEnd = pEnd;
+       /* Compile the expression */
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       if( rc == SXERR_ABORT ){
+               /* Expression handler request an operation abort [i.e: Out-of-memory] */
+               return SXERR_ABORT;
+       }
+       /* Update token stream */
+       while(pGen->pIn < pEnd ){
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+               pGen->pIn++;
+       }
+       /* Synchronize pointers */
+       pGen->pIn  = &pEnd[1];
+       pGen->pEnd = pTmp;
+       /* Emit the false jump */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
+       /* Save the instruction index so we can fix it later when the jump destination is resolved */
+       GenStateNewJumpFixup(pWhileBlock, JX9_OP_JZ, nFalseJump);
+       /* Compile the loop body */
+       rc = jx9CompileBlock(&(*pGen));
+       if( rc == SXERR_ABORT ){
+               return SXERR_ABORT;
+       }
+       /* Emit the unconditional jump to the start of the loop */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0);
+       /* Fix all jumps now the destination is resolved */
+       GenStateFixJumps(pWhileBlock, -1, jx9VmInstrLength(pGen->pVm));
+       /* Release the loop block */
+       GenStateLeaveBlock(pGen, 0);
+       /* Statement successfully compiled */
+       return SXRET_OK;
+Synchronize:
+       /* Synchronize with the first semi-colon ';' so we can avoid 
+        * compiling this erroneous block.
+        */
+       while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
+               pGen->pIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile the complex and powerful 'for' statement.
+ * According to the JX9 language reference
+ *  for loops are the most complex loops in JX9. They behave like their C counterparts.
+ *  The syntax of a for loop is:
+ *  for (expr1; expr2; expr3)
+ *   statement
+ *  The first expression (expr1) is evaluated (executed) once unconditionally at
+ *  the beginning of the loop.
+ *  In the beginning of each iteration, expr2 is evaluated. If it evaluates to
+ *  TRUE, the loop continues and the nested statement(s) are executed. If it evaluates
+ *  to FALSE, the execution of the loop ends.
+ *  At the end of each iteration, expr3 is evaluated (executed).
+ *  Each of the expressions can be empty or contain multiple expressions separated by commas.
+ *  In expr2, all expressions separated by a comma are evaluated but the result is taken
+ *  from the last part. expr2 being empty means the loop should be run indefinitely
+ *  (JX9 implicitly considers it as TRUE, like C). This may not be as useless as you might
+ *  think, since often you'd want to end the loop using a conditional break statement instead
+ *  of using the for truth expression.
+ */
+static sxi32 jx9CompileFor(jx9_gen_state *pGen)
+{
+       SyToken *pTmp, *pPostStart, *pEnd = 0;
+       GenBlock *pForBlock = 0;
+       sxu32 nFalseJump;
+       sxu32 nLine;
+       sxi32 rc;
+       nLine = pGen->pIn->nLine;
+       /* Jump the 'for' keyword */
+       pGen->pIn++;    
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
+               /* Syntax error */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'for' keyword");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               return SXRET_OK;
+       }
+       /* Jump the left parenthesis '(' */
+       pGen->pIn++; 
+       /* Delimit the init-expr;condition;post-expr */
+       jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
+       if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
+               /* Empty expression */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "for: Invalid expression");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               /* Synchronize */
+               pGen->pIn = pEnd;
+               if( pGen->pIn < pGen->pEnd ){
+                       pGen->pIn++;
+               }
+               return SXRET_OK;
+       }
+       /* Swap token streams */
+       pTmp = pGen->pEnd;
+       pGen->pEnd = pEnd;
+       /* Compile initialization expressions if available */
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       /* Pop operand lvalues */
+       if( rc == SXERR_ABORT ){
+               /* Expression handler request an operation abort [i.e: Out-of-memory] */
+               return SXERR_ABORT;
+       }else if( rc != SXERR_EMPTY ){
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
+       }
+       if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
+               /* Syntax error */
+               rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, 
+                       "for: Expected ';' after initialization expressions");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               return SXRET_OK;
+       }
+       /* Jump the trailing ';' */
+       pGen->pIn++;
+       /* Create the loop block */
+       rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForBlock);
+       if( rc != SXRET_OK ){
+               return SXERR_ABORT;
+       }
+       /* Deffer continue jumps */
+       pForBlock->bPostContinue = TRUE;
+       /* Compile the condition */
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       if( rc == SXERR_ABORT ){
+               /* Expression handler request an operation abort [i.e: Out-of-memory] */
+               return SXERR_ABORT;
+       }else if( rc != SXERR_EMPTY ){
+               /* Emit the false jump */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
+               /* Save the instruction index so we can fix it later when the jump destination is resolved */
+               GenStateNewJumpFixup(pForBlock, JX9_OP_JZ, nFalseJump);
+       }
+       if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
+               /* Syntax error */
+               rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, 
+                       "for: Expected ';' after conditionals expressions");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               return SXRET_OK;
+       }
+       /* Jump the trailing ';' */
+       pGen->pIn++;
+       /* Save the post condition stream */
+       pPostStart = pGen->pIn;
+       /* Compile the loop body */
+       pGen->pIn  = &pEnd[1]; /* Jump the trailing parenthesis ')' */
+       pGen->pEnd = pTmp;
+       rc = jx9CompileBlock(&(*pGen));
+       if( rc == SXERR_ABORT ){
+               return SXERR_ABORT;
+       }
+       /* Fix post-continue jumps */
+       if( SySetUsed(&pForBlock->aPostContFix) > 0 ){
+               JumpFixup *aPost;
+               VmInstr *pInstr;
+               sxu32 nJumpDest;
+               sxu32 n;
+               aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix);
+               nJumpDest = jx9VmInstrLength(pGen->pVm);
+               for( n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n ){
+                       pInstr = jx9VmGetInstr(pGen->pVm, aPost[n].nInstrIdx);
+                       if( pInstr ){
+                               /* Fix jump */
+                               pInstr->iP2 = nJumpDest;
+                       }
+               }
+       }
+       /* compile the post-expressions if available */
+       while( pPostStart < pEnd && (pPostStart->nType & JX9_TK_SEMI) ){
+               pPostStart++;
+       }
+       if( pPostStart < pEnd ){
+               SyToken *pTmpIn, *pTmpEnd;
+               SWAP_DELIMITER(pGen, pPostStart, pEnd);
+               rc = jx9CompileExpr(&(*pGen), 0, 0);
+               if( pGen->pIn < pGen->pEnd ){
+                       /* Syntax error */
+                       rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "for: Expected ')' after post-expressions");
+                       if( rc == SXERR_ABORT ){
+                               /* Error count limit reached, abort immediately */
+                               return SXERR_ABORT;
+                       }
+                       return SXRET_OK;
+               }
+               RE_SWAP_DELIMITER(pGen);
+               if( rc == SXERR_ABORT ){
+                       /* Expression handler request an operation abort [i.e: Out-of-memory] */
+                       return SXERR_ABORT;
+               }else if( rc != SXERR_EMPTY){
+                       /* Pop operand lvalue */
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
+               }
+       }
+       /* Emit the unconditional jump to the start of the loop */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0);
+       /* Fix all jumps now the destination is resolved */
+       GenStateFixJumps(pForBlock, -1, jx9VmInstrLength(pGen->pVm));
+       /* Release the loop block */
+       GenStateLeaveBlock(pGen, 0);
+       /* Statement successfully compiled */
+       return SXRET_OK;
+}
+/* Expression tree validator callback used by the 'foreach' statement.
+ * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...]
+ * are allowed.
+ */
+static sxi32 GenStateForEachNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
+{
+       sxi32 rc = SXRET_OK; /* Assume a valid expression tree */
+       if( pRoot->xCode != jx9CompileVariable ){
+               /* Unexpected expression */
+               rc = jx9GenCompileError(&(*pGen),
+                       E_ERROR,
+                       pRoot->pStart? pRoot->pStart->nLine : 0, 
+                       "foreach: Expecting a variable name"
+                       );
+               if( rc != SXERR_ABORT ){
+                       rc = SXERR_INVALID;
+               }
+       }
+       return rc;
+}
+/*
+ * Compile the 'foreach' statement.
+ * According to the JX9 language reference
+ *  The foreach construct simply gives an easy way to iterate over arrays. foreach works
+ *  only on arrays (and objects), and will issue an error when you try to use it on a variable
+ *  with a different data type or an uninitialized variable. There are two syntaxes; the second
+ *  is a minor but useful extension of the first:
+ *  foreach (json_array_json_object as $value)
+ *    statement
+ *  foreach (json_array_json_objec as $key,$value)
+ *   statement
+ *  The first form loops over the array given by array_expression. On each loop, the value 
+ *  of the current element is assigned to $value and the internal array pointer is advanced
+ *  by one (so on the next loop, you'll be looking at the next element).
+ *  The second form does the same thing, except that the current element's key will be assigned
+ *  to the variable $key on each loop.
+ *  Note:
+ *  When foreach first starts executing, the internal array pointer is automatically reset to the
+ *  first element of the array. This means that you do not need to call reset() before a foreach loop. 
+ */
+static sxi32 jx9CompileForeach(jx9_gen_state *pGen)
+{ 
+       SyToken *pCur, *pTmp, *pEnd = 0;
+       GenBlock *pForeachBlock = 0;
+       jx9_foreach_info *pInfo;
+       sxu32 nFalseJump;
+       VmInstr *pInstr;
+       sxu32 nLine;
+       sxi32 rc;
+       nLine = pGen->pIn->nLine;
+       /* Jump the 'foreach' keyword */
+       pGen->pIn++;    
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
+               /* Syntax error */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Expected '('");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               goto Synchronize;
+       }
+       /* Jump the left parenthesis '(' */
+       pGen->pIn++; 
+       /* Create the loop block */
+       rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForeachBlock);
+       if( rc != SXRET_OK ){
+               return SXERR_ABORT;
+       }
+       /* Delimit the expression */
+       jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
+       if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
+               /* Empty expression */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Missing expression");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               /* Synchronize */
+               pGen->pIn = pEnd;
+               if( pGen->pIn < pGen->pEnd ){
+                       pGen->pIn++;
+               }
+               return SXRET_OK;
+       }
+       /* Compile the array expression */
+       pCur = pGen->pIn;
+       while( pCur < pEnd ){
+               if( pCur->nType & JX9_TK_KEYWORD ){
+                       sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData);
+                       if( nKeywrd == JX9_TKWRD_AS ){
+                               /* Break with the first 'as' found */
+                               break;
+                       }
+               }
+               /* Advance the stream cursor */
+               pCur++;
+       }
+       if( pCur <= pGen->pIn ){
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, 
+                       "foreach: Missing array/object expression");
+               if( rc == SXERR_ABORT ){
+                       /* Don't worry about freeing memory, everything will be released shortly */
+                       return SXERR_ABORT;
+               }
+               goto Synchronize;
+       }
+       /* Swap token streams */
+       pTmp = pGen->pEnd;
+       pGen->pEnd = pCur;
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       if( rc == SXERR_ABORT ){
+               /* Expression handler request an operation abort [i.e: Out-of-memory] */
+               return SXERR_ABORT;
+       }
+       /* Update token stream */
+       while(pGen->pIn < pCur ){
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Unexpected token '%z'", &pGen->pIn->sData);
+               if( rc == SXERR_ABORT ){
+                       /* Don't worry about freeing memory, everything will be released shortly */
+                       return SXERR_ABORT;
+               }
+               pGen->pIn++;
+       }
+       pCur++; /* Jump the 'as' keyword */
+       pGen->pIn = pCur; 
+       if( pGen->pIn >= pEnd ){
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key => $value pair");
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+       }
+       /* Create the foreach context */
+       pInfo = (jx9_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_foreach_info));
+       if( pInfo == 0 ){
+               jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, JX9 engine is running out-of-memory");
+               return SXERR_ABORT;
+       }
+       /* Zero the structure */
+       SyZero(pInfo, sizeof(jx9_foreach_info));
+       /* Initialize structure fields */
+       SySetInit(&pInfo->aStep, &pGen->pVm->sAllocator, sizeof(jx9_foreach_step *));
+       /* Check if we have a key field */
+       while( pCur < pEnd && (pCur->nType & JX9_TK_COMMA) == 0 ){
+               pCur++;
+       }
+       if( pCur < pEnd ){
+               /* Compile the expression holding the key name */
+               if( pGen->pIn >= pCur ){
+                       rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key");
+                       if( rc == SXERR_ABORT ){
+                               /* Don't worry about freeing memory, everything will be released shortly */
+                               return SXERR_ABORT;
+                       }
+               }else{
+                       pGen->pEnd = pCur;
+                       rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
+                       if( rc == SXERR_ABORT ){
+                               /* Don't worry about freeing memory, everything will be released shortly */
+                               return SXERR_ABORT;
+                       }
+                       pInstr = jx9VmPopInstr(pGen->pVm);
+                       if( pInstr->p3 ){
+                               /* Record key name */
+                               SyStringInitFromBuf(&pInfo->sKey, pInstr->p3, SyStrlen((const char *)pInstr->p3));
+                       }
+                       pInfo->iFlags |= JX9_4EACH_STEP_KEY;
+               }
+               pGen->pIn = &pCur[1]; /* Jump the arrow */
+       }
+       pGen->pEnd = pEnd;
+       if( pGen->pIn >= pEnd ){
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $value");
+               if( rc == SXERR_ABORT ){
+                       /* Don't worry about freeing memory, everything will be released shortly */
+                       return SXERR_ABORT;
+               }
+               goto Synchronize;
+       }
+       /* Compile the expression holding the value name */
+       rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
+       if( rc == SXERR_ABORT ){
+               /* Don't worry about freeing memory, everything will be released shortly */
+               return SXERR_ABORT;
+       }
+       pInstr = jx9VmPopInstr(pGen->pVm);
+       if( pInstr->p3 ){
+               /* Record value name */
+               SyStringInitFromBuf(&pInfo->sValue, pInstr->p3, SyStrlen((const char *)pInstr->p3));
+       }
+       /* Emit the 'FOREACH_INIT' instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_INIT, 0, 0, pInfo, &nFalseJump);
+       /* Save the instruction index so we can fix it later when the jump destination is resolved */
+       GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_INIT, nFalseJump);
+       /* Record the first instruction to execute */
+       pForeachBlock->nFirstInstr = jx9VmInstrLength(pGen->pVm);
+       /* Emit the FOREACH_STEP instruction */
+    jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump);
+       /* Save the instruction index so we can fix it later when the jump destination is resolved */
+       GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_STEP, nFalseJump);
+       /* Compile the loop body */
+       pGen->pIn = &pEnd[1];
+       pGen->pEnd = pTmp;
+       rc = jx9CompileBlock(&(*pGen));
+       if( rc == SXERR_ABORT ){
+               /* Don't worry about freeing memory, everything will be released shortly */
+               return SXERR_ABORT;
+       }
+       /* Emit the unconditional jump to the start of the loop */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0);
+       /* Fix all jumps now the destination is resolved */
+       GenStateFixJumps(pForeachBlock, -1,jx9VmInstrLength(pGen->pVm));
+       /* Release the loop block */
+       GenStateLeaveBlock(pGen, 0);
+       /* Statement successfully compiled */
+       return SXRET_OK;
+Synchronize:
+       /* Synchronize with the first semi-colon ';' so we can avoid 
+        * compiling this erroneous block.
+        */
+       while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
+               pGen->pIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile the infamous if/elseif/else if/else statements.
+ * According to the JX9 language reference
+ *  The if construct is one of the most important features of many languages JX9 included.
+ *  It allows for conditional execution of code fragments. JX9 features an if structure 
+ *  that is similar to that of C:
+ *  if (expr)
+ *   statement
+ *  else construct:
+ *   Often you'd want to execute a statement if a certain condition is met, and a different
+ *   statement if the condition is not met. This is what else is for. else extends an if statement
+ *   to execute a statement in case the expression in the if statement evaluates to FALSE.
+ *   For example, the following code would display a is greater than b if $a is greater than
+ *   $b, and a is NOT greater than b otherwise.
+ *   The else statement is only executed if the if expression evaluated to FALSE, and if there
+ *   were any elseif expressions - only if they evaluated to FALSE as well
+ *  elseif
+ *   elseif, as its name suggests, is a combination of if and else. Like else, it extends
+ *   an if statement to execute a different statement in case the original if expression evaluates
+ *   to FALSE. However, unlike else, it will execute that alternative expression only if the elseif
+ *   conditional expression evaluates to TRUE. For example, the following code would display a is bigger
+ *   than b, a equal to b or a is smaller than b:
+ *    if ($a > $b) {
+ *     print "a is bigger than b";
+ *    } elseif ($a == $b) {
+ *     print "a is equal to b";
+ *    } else {
+ *     print "a is smaller than b";
+ *    }
+ */
+static sxi32 jx9CompileIf(jx9_gen_state *pGen)
+{
+       SyToken *pToken, *pTmp, *pEnd = 0;
+       GenBlock *pCondBlock = 0;
+       sxu32 nJumpIdx;
+       sxu32 nKeyID;
+       sxi32 rc;
+       /* Jump the 'if' keyword */
+       pGen->pIn++;
+       pToken = pGen->pIn; 
+       /* Create the conditional block */
+       rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_COND, jx9VmInstrLength(pGen->pVm), 0, &pCondBlock);
+       if( rc != SXRET_OK ){
+               return SXERR_ABORT;
+       }
+       /* Process as many [if/else if/elseif/else] blocks as we can */
+       for(;;){
+               if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_LPAREN) == 0 ){
+                       /* Syntax error */
+                       if( pToken >= pGen->pEnd ){
+                               pToken--;
+                       }
+                       rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing '('");
+                       if( rc == SXERR_ABORT ){
+                               /* Error count limit reached, abort immediately */
+                               return SXERR_ABORT;
+                       }
+                       goto Synchronize;
+               }
+               /* Jump the left parenthesis '(' */
+               pToken++; 
+               /* Delimit the condition */
+               jx9DelimitNestedTokens(pToken, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
+               if( pToken >= pEnd || (pEnd->nType & JX9_TK_RPAREN) == 0 ){
+                       /* Syntax error */
+                       if( pToken >= pGen->pEnd ){
+                               pToken--;
+                       }
+                       rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing ')'");
+                       if( rc == SXERR_ABORT ){
+                               /* Error count limit reached, abort immediately */
+                               return SXERR_ABORT;
+                       }
+                       goto Synchronize;
+               }
+               /* Swap token streams */
+               SWAP_TOKEN_STREAM(pGen, pToken, pEnd);
+               /* Compile the condition */
+               rc = jx9CompileExpr(&(*pGen), 0, 0);
+               /* Update token stream */
+               while(pGen->pIn < pEnd ){
+                       jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
+                       pGen->pIn++;
+               }
+               pGen->pIn  = &pEnd[1];
+               pGen->pEnd = pTmp;
+               if( rc == SXERR_ABORT ){
+                       /* Expression handler request an operation abort [i.e: Out-of-memory] */
+                       return SXERR_ABORT;
+               }
+               /* Emit the false jump */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJumpIdx);
+               /* Save the instruction index so we can fix it later when the jump destination is resolved */
+               GenStateNewJumpFixup(pCondBlock, JX9_OP_JZ, nJumpIdx);
+               /* Compile the body */
+               rc = jx9CompileBlock(&(*pGen));
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+               if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
+                       break;
+               }
+               /* Ensure that the keyword ID is 'else if' or 'else' */
+               nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
+               if( (nKeyID & (JX9_TKWRD_ELSE|JX9_TKWRD_ELIF)) == 0 ){
+                       break;
+               }
+               /* Emit the unconditional jump */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJumpIdx);
+               /* Save the instruction index so we can fix it later when the jump destination is resolved */
+               GenStateNewJumpFixup(pCondBlock, JX9_OP_JMP, nJumpIdx);
+               if( nKeyID & JX9_TKWRD_ELSE ){
+                       pToken = &pGen->pIn[1];
+                       if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_KEYWORD) == 0 ||
+                               SX_PTR_TO_INT(pToken->pUserData) != JX9_TKWRD_IF ){
+                                       break;
+                       }
+                       pGen->pIn++; /* Jump the 'else' keyword */
+               }
+               pGen->pIn++; /* Jump the 'elseif/if' keyword */
+               /* Synchronize cursors */
+               pToken = pGen->pIn;
+               /* Fix the false jump */
+               GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
+       } /* For(;;) */
+       /* Fix the false jump */
+       GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_KEYWORD) &&
+               (SX_PTR_TO_INT(pGen->pIn->pUserData) & JX9_TKWRD_ELSE) ){
+                       /* Compile the else block */
+                       pGen->pIn++;
+                       rc = jx9CompileBlock(&(*pGen));
+                       if( rc == SXERR_ABORT ){
+                               
+                               return SXERR_ABORT;
+                       }
+       }
+       nJumpIdx = jx9VmInstrLength(pGen->pVm);
+       /* Fix all unconditional jumps now the destination is resolved */
+       GenStateFixJumps(pCondBlock, JX9_OP_JMP, nJumpIdx);
+       /* Release the conditional block */
+       GenStateLeaveBlock(pGen, 0);
+       /* Statement successfully compiled */
+       return SXRET_OK;
+Synchronize:
+       /* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block.
+        */
+       while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
+               pGen->pIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile the return statement.
+ * According to the JX9 language reference
+ *  If called from within a function, the return() statement immediately ends execution
+ *  of the current function, and returns its argument as the value of the function call.
+ *  return() will also end the execution of an eval() statement or script file.
+ *  If called from the global scope, then execution of the current script file is ended.
+ *  If the current script file was include()ed or require()ed, then control is passed back
+ *  to the calling file. Furthermore, if the current script file was include()ed, then the value
+ *  given to return() will be returned as the value of the include() call. If return() is called
+ *  from within the main script file, then script execution end.
+ *  Note that since return() is a language construct and not a function, the parentheses
+ *  surrounding its arguments are not required. It is common to leave them out, and you actually
+ *  should do so as JX9 has less work to do in this case. 
+ *  Note: If no parameter is supplied, then the parentheses must be omitted and JX9 is returning NULL instead..
+ */
+static sxi32 jx9CompileReturn(jx9_gen_state *pGen)
+{
+       sxi32 nRet = 0; /* TRUE if there is a return value */
+       sxi32 rc;
+       /* Jump the 'return' keyword */
+       pGen->pIn++;
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
+               /* Compile the expression */
+               rc = jx9CompileExpr(&(*pGen), 0, 0);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }else if(rc != SXERR_EMPTY ){
+                       nRet = 1;
+               }
+       }
+       /* Emit the done instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, nRet, 0, 0, 0);
+       return SXRET_OK;
+}
+/*
+ * Compile the die/exit language construct.
+ * The role of these constructs is to terminate execution of the script.
+ * Shutdown functions will always be executed even if exit() is called.
+ */
+static sxi32 jx9CompileHalt(jx9_gen_state *pGen)
+{
+       sxi32 nExpr = 0;
+       sxi32 rc;
+       /* Jump the die/exit keyword */
+       pGen->pIn++;
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
+               /* Compile the expression */
+               rc = jx9CompileExpr(&(*pGen), 0, 0);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }else if(rc != SXERR_EMPTY ){
+                       nExpr = 1;
+               }
+       }
+       /* Emit the HALT instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_HALT, nExpr, 0, 0, 0);
+       return SXRET_OK;
+}
+/*
+ * Compile the static statement.
+ * According to the JX9 language reference
+ *  Another important feature of variable scoping is the static variable.
+ *  A static variable exists only in a local function scope, but it does not lose its value
+ *  when program execution leaves this scope.
+ *  Static variables also provide one way to deal with recursive functions.
+ */
+static sxi32 jx9CompileStatic(jx9_gen_state *pGen)
+{
+       jx9_vm_func_static_var sStatic; /* Structure describing the static variable */
+       jx9_vm_func *pFunc;             /* Enclosing function */
+       GenBlock *pBlock;
+       SyString *pName;
+       char *zDup;
+       sxu32 nLine;
+       sxi32 rc;
+       /* Jump the static keyword */
+       nLine = pGen->pIn->nLine;
+       pGen->pIn++;
+       /* Extract the enclosing function if any */
+       pBlock = pGen->pCurrent;
+       while( pBlock ){
+               if( pBlock->iFlags & GEN_BLOCK_FUNC){
+                       break;
+               }
+               /* Point to the upper block */
+               pBlock = pBlock->pParent;
+       }
+       if( pBlock == 0 ){
+               /* Static statement, called outside of a function body, treat it as a simple variable. */
+               if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
+                       rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       goto Synchronize;
+               }
+               /* Compile the expression holding the variable */
+               rc = jx9CompileExpr(&(*pGen), 0, 0);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }else if( rc != SXERR_EMPTY ){
+                       /* Emit the POP instruction */
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
+               }
+               return SXRET_OK;
+       }
+       pFunc = (jx9_vm_func *)pBlock->pUserData;
+       /* Make sure we are dealing with a valid statement */
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd ||
+               (pGen->pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
+                       rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       goto Synchronize;
+       }
+       pGen->pIn++;
+       /* Extract variable name */
+       pName = &pGen->pIn->sData;
+       pGen->pIn++; /* Jump the var name */
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_EQUAL/*'='*/)) == 0 ){
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData);
+               goto Synchronize;
+       }
+       /* Initialize the structure describing the static variable */
+       SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
+       sStatic.nIdx = SXU32_HIGH; /* Not yet created */
+       /* Duplicate variable name */
+       zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
+       if( zDup == 0 ){
+               jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, JX9 engine is running out of memory");
+               return SXERR_ABORT;
+       }
+       SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte);
+       /* Check if we have an expression to compile */
+       if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_EQUAL) ){
+               SySet *pInstrContainer;
+               pGen->pIn++; /* Jump the equal '=' sign */
+               /* Swap bytecode container */
+               pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
+               jx9VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode);
+               /* Compile the expression */
+               rc = jx9CompileExpr(&(*pGen), 0, 0);
+               /* Emit the done instruction */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
+               /* Restore default bytecode container */
+               jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
+       }
+       /* Finally save the compiled static variable in the appropriate container */
+       SySetPut(&pFunc->aStatic, (const void *)&sStatic);
+       return SXRET_OK;
+Synchronize:
+       /* Synchronize with the first semi-colon ';', so we can avoid compiling this erroneous
+        * statement. 
+        */
+       while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ==  0 ){
+               pGen->pIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile the 'const' statement.
+ * According to the JX9 language reference
+ *  A constant is an identifier (name) for a simple value. As the name suggests, that value
+ *  cannot change during the execution of the script (except for magic constants, which aren't actually constants).
+ *  A constant is case-sensitive by default. By convention, constant identifiers are always uppercase.
+ *  The name of a constant follows the same rules as any label in JX9. A valid constant name starts
+ *  with a letter or underscore, followed by any number of letters, numbers, or underscores.
+ *  As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]* 
+ *  Syntax
+ *  You can define a constant by using the define()-function or by using the const keyword outside
+ *  a object definition. Once a constant is defined, it can never be changed or undefined.
+ *  You can get the value of a constant by simply specifying its name. Unlike with variables
+ *  you should not prepend a constant with a $. You can also use the function constant() to read
+ *  a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants()
+ *  to get a list of all defined constants.
+ */
+static sxi32 jx9CompileConstant(jx9_gen_state *pGen)
+{
+       SySet *pConsCode, *pInstrContainer;
+       sxu32 nLine = pGen->pIn->nLine;
+       SyString *pName;
+       sxi32 rc;
+       pGen->pIn++; /* Jump the 'const' keyword */
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
+               /* Invalid constant name */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               goto Synchronize;
+       }
+       /* Peek constant name */
+       pName = &pGen->pIn->sData;
+       /* Make sure the constant name isn't reserved */
+       if( GenStateIsReservedID(pName) ){
+               /* Reserved constant */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName);
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               goto Synchronize;
+       }
+       pGen->pIn++;
+       if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_EQUAL /* '=' */) == 0 ){
+               /* Invalid statement*/
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Expected '=' after constant name");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               goto Synchronize;
+       }
+       pGen->pIn++; /*Jump the equal sign */
+       /* Allocate a new constant value container */
+       pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet));
+       if( pConsCode == 0 ){
+               return GenStateOutOfMem(pGen);
+       }
+       SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
+       /* Swap bytecode container */
+       pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
+       jx9VmSetByteCodeContainer(pGen->pVm, pConsCode);
+       /* Compile constant value */
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       /* Emit the done instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
+       jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); 
+       if( rc == SXERR_ABORT ){
+               /* Don't worry about freeing memory, everything will be released shortly */
+               return SXERR_ABORT;
+       }
+       SySetSetUserData(pConsCode, pGen->pVm);
+       /* Register the constant */
+       rc = jx9VmRegisterConstant(pGen->pVm, pName, jx9VmExpandConstantValue, pConsCode);
+       if( rc != SXRET_OK ){
+               SySetRelease(pConsCode);
+               SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode);
+       }
+       return SXRET_OK;
+Synchronize:
+       /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
+       while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
+               pGen->pIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile the uplink construct.
+ * According to the JX9 language reference
+ *  In JX9 global variables must be declared uplink inside a function if they are going
+ *  to be used in that function.
+ *  Example #1 Using global
+ *   $a = 1;
+ *   $b = 2;
+ *   function Sum()
+ *   {
+ *    uplink $a, $b;
+ *    $b = $a + $b;
+ *   } 
+ *   Sum();
+ *   print $b;
+ *  ?>
+ *  The above script will output 3. By declaring $a and $b global within the function
+ *  all references to either variable will refer to the global version. There is no limit
+ *  to the number of global variables that can be manipulated by a function.
+ */
+static sxi32 jx9CompileUplink(jx9_gen_state *pGen)
+{
+       SyToken *pTmp, *pNext = 0;
+       sxi32 nExpr;
+       sxi32 rc;
+       /* Jump the 'uplink' keyword */
+       pGen->pIn++;
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_SEMI) ){
+               /* Nothing to process */
+               return SXRET_OK;
+       }
+       pTmp = pGen->pEnd;
+       nExpr = 0;
+       while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
+               if( pGen->pIn < pNext ){
+                       pGen->pEnd = pNext;
+                       if( (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "uplink: Expected variable name");
+                               if( rc == SXERR_ABORT ){
+                                       return SXERR_ABORT;
+                               }
+                       }else{
+                               pGen->pIn++;
+                               if( pGen->pIn >= pGen->pEnd ){
+                                       /* Emit a warning */
+                                       jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn[-1].nLine, "uplink: Empty variable name");
+                               }else{
+                                       rc = jx9CompileExpr(&(*pGen), 0, 0);
+                                       if( rc == SXERR_ABORT ){
+                                               return SXERR_ABORT;
+                                       }else if(rc != SXERR_EMPTY ){
+                                               nExpr++;
+                                       }
+                               }
+                       }
+               }
+               /* Next expression in the stream */
+               pGen->pIn = pNext;
+               /* Jump trailing commas */
+               while( pGen->pIn < pTmp && (pGen->pIn->nType & JX9_TK_COMMA) ){
+                       pGen->pIn++;
+               }
+       }
+       /* Restore token stream */
+       pGen->pEnd = pTmp;
+       if( nExpr > 0 ){
+               /* Emit the uplink instruction */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_UPLINK, nExpr, 0, 0, 0);
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile a switch block.
+ *  (See block-comment below for more information)
+ */
+static sxi32 GenStateCompileSwitchBlock(jx9_gen_state *pGen,sxu32 *pBlockStart)
+{
+       sxi32 rc = SXRET_OK;
+       while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*':'*/)) == 0 ){
+               /* Unexpected token */
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+               pGen->pIn++;
+       }
+       pGen->pIn++;
+       /* First instruction to execute in this block. */
+       *pBlockStart = jx9VmInstrLength(pGen->pVm);
+       /* Compile the block until we hit a case/default/endswitch keyword
+        * or the '}' token */
+       for(;;){
+               if( pGen->pIn >= pGen->pEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               rc = SXRET_OK;
+               if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
+                       if( pGen->pIn->nType & JX9_TK_CCB /*'}' */ ){
+                               rc = SXERR_EOF;
+                               break;
+                       }
+               }else{
+                       sxi32 nKwrd;
+                       /* Extract the keyword */
+                       nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
+                       if( nKwrd == JX9_TKWRD_CASE || nKwrd == JX9_TKWRD_DEFAULT ){
+                               break;
+                       }
+               }
+               /* Compile block */
+               rc = jx9CompileBlock(&(*pGen));
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+       }
+       return rc;
+}
+/*
+ * Compile a case eXpression.
+ *  (See block-comment below for more information)
+ */
+static sxi32 GenStateCompileCaseExpr(jx9_gen_state *pGen, jx9_case_expr *pExpr)
+{
+       SySet *pInstrContainer;
+       SyToken *pEnd, *pTmp;
+       sxi32 iNest = 0;
+       sxi32 rc;
+       /* Delimit the expression */
+       pEnd = pGen->pIn;
+       while( pEnd < pGen->pEnd ){
+               if( pEnd->nType & JX9_TK_LPAREN /*(*/ ){
+                       /* Increment nesting level */
+                       iNest++;
+               }else if( pEnd->nType & JX9_TK_RPAREN /*)*/ ){
+                       /* Decrement nesting level */
+                       iNest--;
+               }else if( pEnd->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*;'*/) && iNest < 1 ){
+                       break;
+               }
+               pEnd++;
+       }
+       if( pGen->pIn >= pEnd ){
+               rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Empty case expression");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+       }
+       /* Swap token stream */
+       pTmp = pGen->pEnd;
+       pGen->pEnd = pEnd;
+       pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
+       jx9VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode);
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       /* Emit the done instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
+       jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); 
+       /* Update token stream */
+       pGen->pIn  = pEnd;
+       pGen->pEnd = pTmp;
+       if( rc == SXERR_ABORT ){
+               return SXERR_ABORT;
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile the smart switch statement.
+ * According to the JX9 language reference manual
+ *  The switch statement is similar to a series of IF statements on the same expression.
+ *  In many occasions, you may want to compare the same variable (or expression) with many
+ *  different values, and execute a different piece of code depending on which value it equals to.
+ *  This is exactly what the switch statement is for.
+ *  Note: Note that unlike some other languages, the continue statement applies to switch and acts
+ *  similar to break. If you have a switch inside a loop and wish to continue to the next iteration
+ *  of the outer loop, use continue 2. 
+ *  Note that switch/case does loose comparision. 
+ *  It is important to understand how the switch statement is executed in order to avoid mistakes.
+ *  The switch statement executes line by line (actually, statement by statement).
+ *  In the beginning, no code is executed. Only when a case statement is found with a value that
+ *  matches the value of the switch expression does JX9 begin to execute the statements.
+ *  JX9 continues to execute the statements until the end of the switch block, or the first time
+ *  it sees a break statement. If you don't write a break statement at the end of a case's statement list.
+ *  In a switch statement, the condition is evaluated only once and the result is compared to each
+ *  case statement. In an elseif statement, the condition is evaluated again. If your condition
+ *  is more complicated than a simple compare and/or is in a tight loop, a switch may be faster.
+ *  The statement list for a case can also be empty, which simply passes control into the statement
+ *  list for the next case. 
+ *  The case expression may be any expression that evaluates to a simple type, that is, integer
+ *  or floating-point numbers and strings.
+ */
+static sxi32 jx9CompileSwitch(jx9_gen_state *pGen)
+{
+       GenBlock *pSwitchBlock;
+       SyToken *pTmp, *pEnd;
+       jx9_switch *pSwitch;
+       sxu32 nLine;
+       sxi32 rc;
+       nLine = pGen->pIn->nLine;
+       /* Jump the 'switch' keyword */
+       pGen->pIn++;    
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
+               /* Syntax error */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'switch' keyword");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               goto Synchronize;
+       }
+       /* Jump the left parenthesis '(' */
+       pGen->pIn++; 
+       pEnd = 0; /* cc warning */
+       /* Create the loop block */
+       rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP|GEN_BLOCK_SWITCH, 
+               jx9VmInstrLength(pGen->pVm), 0, &pSwitchBlock);
+       if( rc != SXRET_OK ){
+               return SXERR_ABORT;
+       }
+       /* Delimit the condition */
+       jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
+       if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
+               /* Empty expression */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'switch' keyword");
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+       }
+       /* Swap token streams */
+       pTmp = pGen->pEnd;
+       pGen->pEnd = pEnd;
+       /* Compile the expression */
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       if( rc == SXERR_ABORT ){
+               /* Expression handler request an operation abort [i.e: Out-of-memory] */
+               return SXERR_ABORT;
+       }
+       /* Update token stream */
+       while(pGen->pIn < pEnd ){
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, 
+                       "Switch: Unexpected token '%z'", &pGen->pIn->sData);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+               pGen->pIn++;
+       }
+       pGen->pIn  = &pEnd[1];
+       pGen->pEnd = pTmp;
+       if( pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd ||
+               (pGen->pIn->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_COLON/*:*/)) == 0 ){
+                       pTmp = pGen->pIn;
+                       if( pTmp >= pGen->pEnd ){
+                               pTmp--;
+                       }
+                       /* Unexpected token */
+                       rc = jx9GenCompileError(&(*pGen), E_ERROR, pTmp->nLine, "Switch: Unexpected token '%z'", &pTmp->sData);
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       goto Synchronize;
+       }
+       pGen->pIn++; /* Jump the leading curly braces/colons */
+       /* Create the switch blocks container */
+       pSwitch = (jx9_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_switch));
+       if( pSwitch == 0 ){
+               /* Abort compilation */
+               return GenStateOutOfMem(pGen);
+       }
+       /* Zero the structure */
+       SyZero(pSwitch, sizeof(jx9_switch));
+       /* Initialize fields */
+       SySetInit(&pSwitch->aCaseExpr, &pGen->pVm->sAllocator, sizeof(jx9_case_expr));
+       /* Emit the switch instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_SWITCH, 0, 0, pSwitch, 0);
+       /* Compile case blocks */
+       for(;;){
+               sxu32 nKwrd;
+               if( pGen->pIn >= pGen->pEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
+                       if(  (pGen->pIn->nType & JX9_TK_CCB /*}*/) == 0 ){
+                               /* Unexpected token */
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'", 
+                                       &pGen->pIn->sData);
+                               if( rc == SXERR_ABORT ){
+                                       return SXERR_ABORT;
+                               }
+                               /* FALL THROUGH */
+                       }
+                       /* Block compiled */
+                       break;
+               }
+               /* Extract the keyword */
+               nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
+               if( nKwrd == JX9_TKWRD_DEFAULT ){
+                       /*
+                        * Accroding to the JX9 language reference manual
+                        *  A special case is the default case. This case matches anything
+                        *  that wasn't matched by the other cases.
+                        */
+                       if( pSwitch->nDefault > 0 ){
+                               /* Default case already compiled */ 
+                               rc = jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Switch: 'default' case already compiled");
+                               if( rc == SXERR_ABORT ){
+                                       return SXERR_ABORT;
+                               }
+                       }
+                       pGen->pIn++; /* Jump the 'default' keyword */
+                       /* Compile the default block */
+                       rc = GenStateCompileSwitchBlock(pGen,&pSwitch->nDefault);
+                       if( rc == SXERR_ABORT){
+                               return SXERR_ABORT;
+                       }else if( rc == SXERR_EOF ){
+                               break;
+                       }
+               }else if( nKwrd == JX9_TKWRD_CASE ){
+                       jx9_case_expr sCase;
+                       /* Standard case block */
+                       pGen->pIn++; /* Jump the 'case' keyword */
+                       /* initialize the structure */
+                       SySetInit(&sCase.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
+                       /* Compile the case expression */
+                       rc = GenStateCompileCaseExpr(pGen, &sCase);
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       /* Compile the case block */
+                       rc = GenStateCompileSwitchBlock(pGen,&sCase.nStart);
+                       /* Insert in the switch container */
+                       SySetPut(&pSwitch->aCaseExpr, (const void *)&sCase);
+                       if( rc == SXERR_ABORT){
+                               return SXERR_ABORT;
+                       }else if( rc == SXERR_EOF ){
+                               break;
+                       }
+               }else{
+                       /* Unexpected token */
+                       rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'", 
+                               &pGen->pIn->sData);
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       break;
+               }
+       }
+       /* Fix all jumps now the destination is resolved */
+       pSwitch->nOut = jx9VmInstrLength(pGen->pVm);
+       GenStateFixJumps(pSwitchBlock, -1, jx9VmInstrLength(pGen->pVm));
+       /* Release the loop block */
+       GenStateLeaveBlock(pGen, 0);
+       if( pGen->pIn < pGen->pEnd ){
+               /* Jump the trailing curly braces */
+               pGen->pIn++;
+       }
+       /* Statement successfully compiled */
+       return SXRET_OK;
+Synchronize:
+       /* Synchronize with the first semi-colon */
+       while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
+               pGen->pIn++;
+       }
+       return SXRET_OK;
+}
+/*
+ * Process default argument values. That is, a function may define C++-style default value
+ * as follows:
+ * function makecoffee($type = "cappuccino")
+ * {
+ *   return "Making a cup of $type.\n";
+ * }
+ * Some features:
+ *  1 -) Default arguments value can be any complex expression [i.e: function call, annynoymous
+ *      functions, array member, ..]
+ * 2 -) Full type hinting: (Arguments are automatically casted to the desired type)
+ *      Example:
+ *           function a(string $a){} function b(int $a, string $c, float $d){}
+ * 3 -) Function overloading!!
+ *      Example:
+ *      function foo($a) {
+ *       return $a.JX9_EOL;
+ *         }
+ *         function foo($a, $b) {
+ *       return $a + $b;
+ *         }
+ *         print foo(5); // Prints "5"
+ *         print foo(5, 2); // Prints "7"
+ *      // Same arg
+ *        function foo(string $a)
+ *        {
+ *          print "a is a string\n";
+ *          dump($a);
+ *        }
+ *       function foo(int $a)
+ *       {
+ *         print "a is integer\n";
+ *         dump($a);
+ *       }
+ *       function foo(array $a)
+ *       {
+ *         print "a is an array\n";
+ *         dump($a);
+ *       }
+ *       foo('This is a great feature'); // a is a string [first foo]
+ *       foo(52); // a is integer [second foo] 
+ *    foo(array(14, __TIME__, __DATE__)); // a is an array [third foo]
+ * Please refer to the official documentation for more information on the powerful extension
+ * introduced by the JX9 engine.
+ */
+static sxi32 GenStateProcessArgValue(jx9_gen_state *pGen, jx9_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd)
+{
+       SyToken *pTmpIn, *pTmpEnd;
+       SySet *pInstrContainer;
+       sxi32 rc;
+       /* Swap token stream */
+       SWAP_DELIMITER(pGen, pIn, pEnd);
+       pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
+       jx9VmSetByteCodeContainer(pGen->pVm, &pArg->aByteCode);
+       /* Compile the expression holding the argument value */
+       rc = jx9CompileExpr(&(*pGen), 0, 0);
+       /* Emit the done instruction */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
+       jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer); 
+       RE_SWAP_DELIMITER(pGen);
+       if( rc == SXERR_ABORT ){
+               return SXERR_ABORT;
+       }
+       return SXRET_OK;
+}
+/*
+ * Collect function arguments one after one.
+ * According to the JX9 language reference manual.
+ * Information may be passed to functions via the argument list, which is a comma-delimited
+ * list of expressions.
+ * JX9 supports passing arguments by value (the default), passing by reference
+ * and default argument values. Variable-length argument lists are also supported, 
+ * see also the function references for func_num_args(), func_get_arg(), and func_get_args()
+ * for more information.
+ * Example #1 Passing arrays to functions
+ * <?jx9
+ * function takes_array($input)
+ * {
+ *    print "$input[0] + $input[1] = ", $input[0]+$input[1];
+ * }
+ * ?>
+ * Making arguments be passed by reference
+ * By default, function arguments are passed by value (so that if the value of the argument
+ * within the function is changed, it does not get changed outside of the function).
+ * To allow a function to modify its arguments, they must be passed by reference.
+ * To have an argument to a function always passed by reference, prepend an ampersand (&)
+ * to the argument name in the function definition:
+ * Example #2 Passing function parameters by reference
+ * <?jx9
+ * function add_some_extra(&$string)
+ * {
+ *   $string .= 'and something extra.';
+ * }
+ * $str = 'This is a string, ';
+ * add_some_extra($str);
+ * print $str;    // outputs 'This is a string, and something extra.'
+ * ?>
+ *
+ * JX9 have introduced powerful extension including full type hinting, function overloading
+ * complex agrument values.Please refer to the official documentation for more information
+ * on these extension.
+ */
+static sxi32 GenStateCollectFuncArgs(jx9_vm_func *pFunc, jx9_gen_state *pGen, SyToken *pEnd)
+{
+       jx9_vm_func_arg sArg; /* Current processed argument */
+       SyToken *pCur, *pIn;  /* Token stream */
+       SyBlob sSig;         /* Function signature */
+       char *zDup;          /* Copy of argument name */
+       sxi32 rc;
+
+       pIn = pGen->pIn;
+       pCur = 0;
+       SyBlobInit(&sSig, &pGen->pVm->sAllocator);
+       /* Process arguments one after one */
+       for(;;){
+               if( pIn >= pEnd ){
+                       /* No more arguments to process */
+                       break;
+               }
+               SyZero(&sArg, sizeof(jx9_vm_func_arg));
+               SySetInit(&sArg.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
+               if( pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
+                       if( pIn->nType & JX9_TK_KEYWORD ){
+                               sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData));
+                               if( nKey & JX9_TKWRD_BOOL ){
+                                       sArg.nType = MEMOBJ_BOOL;
+                               }else if( nKey & JX9_TKWRD_INT ){
+                                       sArg.nType = MEMOBJ_INT;
+                               }else if( nKey & JX9_TKWRD_STRING ){
+                                       sArg.nType = MEMOBJ_STRING;
+                               }else if( nKey & JX9_TKWRD_FLOAT ){
+                                       sArg.nType = MEMOBJ_REAL;
+                               }else{
+                                       jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, 
+                                               "Invalid argument type '%z', Automatic cast will not be performed", 
+                                               &pIn->sData);
+                               }
+                       }
+                       pIn++;
+               }
+               if( pIn >= pEnd ){
+                       rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name");
+                       return rc;
+               }
+               if( pIn >= pEnd || (pIn->nType & JX9_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
+                       /* Invalid argument */ 
+                       rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid argument name");
+                       return rc;
+               }
+               pIn++; /* Jump the dollar sign */
+               /* Copy argument name */
+               zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData));
+               if( zDup == 0 ){
+                       return GenStateOutOfMem(pGen);
+               }
+               SyStringInitFromBuf(&sArg.sName, zDup, SyStringLength(&pIn->sData));
+               pIn++;
+               if( pIn < pEnd ){
+                       if( pIn->nType & JX9_TK_EQUAL ){
+                               SyToken *pDefend;
+                               sxi32 iNest = 0;
+                               pIn++; /* Jump the equal sign */
+                               pDefend = pIn;
+                               /* Process the default value associated with this argument */
+                               while( pDefend < pEnd ){
+                                       if( (pDefend->nType & JX9_TK_COMMA) && iNest <= 0 ){
+                                               break;
+                                       }
+                                       if( pDefend->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*[*/) ){
+                                               /* Increment nesting level */
+                                               iNest++;
+                                       }else if( pDefend->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*]*/) ){
+                                               /* Decrement nesting level */
+                                               iNest--;
+                                       }
+                                       pDefend++;
+                               }
+                               if( pIn >= pDefend ){
+                                       rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Missing argument default value");
+                                       return rc;
+                               }
+                               /* Process default value */
+                               rc = GenStateProcessArgValue(&(*pGen), &sArg, pIn, pDefend);
+                               if( rc != SXRET_OK ){
+                                       return rc;
+                               }
+                               /* Point beyond the default value */
+                               pIn = pDefend;
+                       }
+                       if( pIn < pEnd && (pIn->nType & JX9_TK_COMMA) == 0 ){
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Unexpected token '%z'", &pIn->sData);
+                               return rc;
+                       }
+                       pIn++; /* Jump the trailing comma */
+               }
+               /* Append argument signature */
+               if( sArg.nType > 0 ){
+                       int c;
+                       c = 'n'; /* cc warning */
+                       /* Type leading character */
+                       switch(sArg.nType){
+                               case MEMOBJ_HASHMAP:
+                                       /* Hashmap aka 'array' */
+                                       c = 'h';
+                                       break;
+                               case MEMOBJ_INT:
+                                       /* Integer */
+                                       c = 'i';
+                                       break;
+                               case MEMOBJ_BOOL:
+                                       /* Bool */
+                                       c = 'b';
+                                       break;
+                               case MEMOBJ_REAL:
+                                       /* Float */
+                                       c = 'f';
+                                       break;
+                               case MEMOBJ_STRING:
+                                       /* String */
+                                       c = 's';
+                                       break;
+                               default:
+                                       break;
+                               }
+                               SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
+               }
+               /* Save in the argument set */
+               SySetPut(&pFunc->aArgs, (const void *)&sArg);
+       }
+       if( SyBlobLength(&sSig) > 0 ){
+               /* Save function signature */
+               SyStringInitFromBuf(&pFunc->sSignature, SyBlobData(&sSig), SyBlobLength(&sSig));
+       }
+       return SXRET_OK;
+}
+/*
+ * Compile function [i.e: standard function, annonymous function or closure ] body.
+ * Return SXRET_OK on success. Any other return value indicates failure
+ * and this routine takes care of generating the appropriate error message.
+ */
+static sxi32 GenStateCompileFuncBody(
+       jx9_gen_state *pGen,  /* Code generator state */
+       jx9_vm_func *pFunc    /* Function state */
+       )
+{
+       SySet *pInstrContainer; /* Instruction container */
+       GenBlock *pBlock;
+       sxi32 rc;
+       /* Attach the new function */
+       rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC,jx9VmInstrLength(pGen->pVm), pFunc, &pBlock);
+       if( rc != SXRET_OK ){
+               return GenStateOutOfMem(pGen);
+       }
+       /* Swap bytecode containers */
+       pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
+       jx9VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode);
+       /* Compile the body */
+       jx9CompileBlock(&(*pGen));
+       /* Emit the final return if not yet done */
+       jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, 0, 0, 0, 0);
+       /* Restore the default container */
+       jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
+       /* Leave function block */
+       GenStateLeaveBlock(&(*pGen), 0);
+       if( rc == SXERR_ABORT ){
+               /* Don't worry about freeing memory, everything will be released shortly */
+               return SXERR_ABORT;
+       }
+       /* All done, function body compiled */
+       return SXRET_OK;
+}
+/*
+ * Compile a JX9 function whether is a Standard or Annonymous function.
+ * According to the JX9 language reference manual.
+ *  Function names follow the same rules as other labels in JX9. A valid function name
+ *  starts with a letter or underscore, followed by any number of letters, numbers, or
+ *  underscores. As a regular expression, it would be expressed thus:
+ *     [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*. 
+ *  Functions need not be defined before they are referenced.
+ *  All functions and objectes in JX9 have the global scope - they can be called outside
+ *  a function even if they were defined inside and vice versa.
+ *  It is possible to call recursive functions in JX9. However avoid recursive function/method
+ *  calls with over 32-64 recursion levels. 
+ * 
+ * JX9 have introduced powerful extension including full type hinting, function overloading, 
+ * complex agrument values and more. Please refer to the official documentation for more information
+ * on these extension.
+ */
+static sxi32 GenStateCompileFunc(
+       jx9_gen_state *pGen, /* Code generator state */
+       SyString *pName,     /* Function name. NULL otherwise */
+       sxi32 iFlags,        /* Control flags */
+       jx9_vm_func **ppFunc /* OUT: function state */
+       )
+{
+       jx9_vm_func *pFunc;
+       SyToken *pEnd;
+       sxu32 nLine;
+       char *zName;
+       sxi32 rc;
+       /* Extract line number */
+       nLine = pGen->pIn->nLine;
+       /* Jump the left parenthesis '(' */
+       pGen->pIn++;
+       /* Delimit the function signature */
+       jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
+       if( pEnd >= pGen->pEnd ){
+               /* Syntax error */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after function '%z' signature", pName);
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               pGen->pIn = pGen->pEnd;
+               return SXRET_OK;
+       }
+       /* Create the function state */
+       pFunc = (jx9_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_vm_func));
+       if( pFunc == 0 ){
+               goto OutOfMem;
+       }
+       /* function ID */
+       zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
+       if( zName == 0 ){
+               /* Don't worry about freeing memory, everything will be released shortly */
+               goto OutOfMem;
+       }
+       /* Initialize the function state */
+       jx9VmInitFuncState(pGen->pVm, pFunc, zName, pName->nByte, iFlags, 0);
+       if( pGen->pIn < pEnd ){
+               /* Collect function arguments */
+               rc = GenStateCollectFuncArgs(pFunc, &(*pGen), pEnd);
+               if( rc == SXERR_ABORT ){
+                       /* Don't worry about freeing memory, everything will be released shortly */
+                       return SXERR_ABORT;
+               }
+       }
+       /* Compile function body */
+       pGen->pIn = &pEnd[1];
+       /* Compile the body */
+       rc = GenStateCompileFuncBody(&(*pGen), pFunc);
+       if( rc == SXERR_ABORT ){
+               return SXERR_ABORT;
+       }
+       if( ppFunc ){
+               *ppFunc = pFunc;
+       }
+       /* Finally register the function */
+       rc = jx9VmInstallUserFunction(pGen->pVm, pFunc, 0);
+       return rc;
+       /* Fall through if something goes wrong */
+OutOfMem:
+       /* If the supplied memory subsystem is so sick that we are unable to allocate
+        * a tiny chunk of memory, there is no much we can do here.
+        */
+       return GenStateOutOfMem(pGen);
+}
+/*
+ * Compile a standard JX9 function.
+ *  Refer to the block-comment above for more information.
+ */
+static sxi32 jx9CompileFunction(jx9_gen_state *pGen)
+{
+       SyString *pName;
+       sxi32 iFlags;
+       sxu32 nLine;
+       sxi32 rc;
+
+       nLine = pGen->pIn->nLine;
+       pGen->pIn++; /* Jump the 'function' keyword */
+       iFlags = 0;
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
+               /* Invalid function name */
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid function name");
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+               /* Sychronize with the next semi-colon or braces*/
+               while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
+                       pGen->pIn++;
+               }
+               return SXRET_OK;
+       }
+       pName = &pGen->pIn->sData;
+       nLine = pGen->pIn->nLine;
+       /* Jump the function name */
+       pGen->pIn++;
+       if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
+               /* Syntax error */
+               rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after function name '%z'", pName);
+               if( rc == SXERR_ABORT ){
+                       /* Error count limit reached, abort immediately */
+                       return SXERR_ABORT;
+               }
+               /* Sychronize with the next semi-colon or '{' */
+               while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
+                       pGen->pIn++;
+               }
+               return SXRET_OK;
+       }
+       /* Compile function body */
+       rc = GenStateCompileFunc(&(*pGen),pName,iFlags,0);
+       return rc;
+}
+/*
+ * Generate bytecode for a given expression tree.
+ * If something goes wrong while generating bytecode
+ * for the expression tree (A very unlikely scenario)
+ * this function takes care of generating the appropriate
+ * error message.
+ */
+static sxi32 GenStateEmitExprCode(
+       jx9_gen_state *pGen,  /* Code generator state */
+       jx9_expr_node *pNode, /* Root of the expression tree */
+       sxi32 iFlags /* Control flags */
+       )
+{
+       VmInstr *pInstr;
+       sxu32 nJmpIdx;
+       sxi32 iP1 = 0;
+       sxu32 iP2 = 0;
+       void *p3  = 0;
+       sxi32 iVmOp;
+       sxi32 rc;
+       if( pNode->xCode ){
+               SyToken *pTmpIn, *pTmpEnd;
+               /* Compile node */
+               SWAP_DELIMITER(pGen, pNode->pStart, pNode->pEnd);
+               rc = pNode->xCode(&(*pGen), iFlags);
+               RE_SWAP_DELIMITER(pGen);
+               return rc;
+       }
+       if( pNode->pOp == 0 ){
+               jx9GenCompileError(&(*pGen), E_ERROR, pNode->pStart->nLine, 
+                       "Invalid expression node, JX9 is aborting compilation");
+               return SXERR_ABORT;
+       }
+       iVmOp = pNode->pOp->iVmOp;
+       if( pNode->pOp->iOp == EXPR_OP_QUESTY ){
+               sxu32 nJz, nJmp;
+               /* Ternary operator require special handling */
+               /* Phase#1: Compile the condition */
+               rc = GenStateEmitExprCode(&(*pGen), pNode->pCond, iFlags);
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+               nJz = nJmp = 0; /* cc -O6 warning */
+               /* Phase#2: Emit the false jump */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJz);
+               if( pNode->pLeft ){
+                       /* Phase#3: Compile the 'then' expression  */
+                       rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
+                       if( rc != SXRET_OK ){
+                               return rc;
+                       }
+               }
+               /* Phase#4: Emit the unconditional jump */
+               jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJmp);
+               /* Phase#5: Fix the false jump now the jump destination is resolved. */
+               pInstr = jx9VmGetInstr(pGen->pVm, nJz);
+               if( pInstr ){
+                       pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
+               }
+               /* Phase#6: Compile the 'else' expression */
+               if( pNode->pRight ){
+                       rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
+                       if( rc != SXRET_OK ){
+                               return rc;
+                       }
+               }
+               if( nJmp > 0 ){
+                       /* Phase#7: Fix the unconditional jump */
+                       pInstr = jx9VmGetInstr(pGen->pVm, nJmp);
+                       if( pInstr ){
+                               pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
+                       }
+               }
+               /* All done */
+               return SXRET_OK;
+       }
+       /* Generate code for the left tree */
+       if( pNode->pLeft ){
+               if( iVmOp == JX9_OP_CALL ){
+                       jx9_expr_node **apNode;
+                       sxi32 n;
+                       /* Recurse and generate bytecodes for function arguments */
+                       apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
+                       /* Read-only load */
+                       iFlags |= EXPR_FLAG_RDONLY_LOAD;
+                       for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
+                               rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
+                               if( rc != SXRET_OK ){
+                                       return rc;
+                               }
+                       }
+                       /* Total number of given arguments */
+                       iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs);
+                       /* Remove stale flags now */
+                       iFlags &= ~EXPR_FLAG_RDONLY_LOAD;
+               }
+               rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+               if( iVmOp == JX9_OP_CALL ){
+                       pInstr = jx9VmPeekInstr(pGen->pVm);
+                       if( pInstr ){
+                               if ( pInstr->iOp == JX9_OP_LOADC ){
+                                       /* Prevent constant expansion */
+                                       pInstr->iP1 = 0;
+                               }else if( pInstr->iOp == JX9_OP_MEMBER /* $a.b(1, 2, 3) */  ){
+                                       /* Annonymous function call, flag that */
+                                       pInstr->iP2 = 1;
+                               }
+                       }
+               }else if( iVmOp == JX9_OP_LOAD_IDX ){
+                       jx9_expr_node **apNode;
+                       sxi32 n;
+                       /* Recurse and generate bytecodes for array index */
+                       apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
+                       for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
+                               rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
+                               if( rc != SXRET_OK ){
+                                       return rc;
+                               }
+                       }
+                       if( SySetUsed(&pNode->aNodeArgs) > 0 ){
+                               iP1 = 1; /* Node have an index associated with it */
+                       }
+                       if( iFlags & EXPR_FLAG_LOAD_IDX_STORE ){
+                               /* Create an empty entry when the desired index is not found */
+                               iP2 = 1;
+                       }
+               }else if( pNode->pOp->iOp == EXPR_OP_COMMA ){
+                       /* POP the left node */
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
+               }
+       }
+       rc = SXRET_OK;
+       nJmpIdx = 0;
+       /* Generate code for the right tree */
+       if( pNode->pRight ){
+               if( iVmOp == JX9_OP_LAND ){
+                       /* Emit the false jump so we can short-circuit the logical and */
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
+               }else if (iVmOp == JX9_OP_LOR ){
+                       /* Emit the true jump so we can short-circuit the logical or*/
+                       jx9VmEmitInstr(pGen->pVm, JX9_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
+               }else if( pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =, '.=', '+=', *=' ...] precedence */ ){
+                       iFlags |= EXPR_FLAG_LOAD_IDX_STORE;
+               }
+               rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
+               if( iVmOp == JX9_OP_STORE ){
+                       pInstr = jx9VmPeekInstr(pGen->pVm);
+                       if( pInstr ){
+                               if(pInstr->iOp == JX9_OP_MEMBER ){
+                                       /* Perform a member store operation [i.e: $this.x = 50] */
+                                       iP2 = 1;
+                               }else{
+                                       if( pInstr->iOp == JX9_OP_LOAD_IDX ){
+                                               /* Transform the STORE instruction to STORE_IDX instruction */
+                                               iVmOp = JX9_OP_STORE_IDX;
+                                               iP1 = pInstr->iP1;
+                                       }else{
+                                               p3 = pInstr->p3;
+                                       }
+                                       /* POP the last dynamic load instruction */
+                                       (void)jx9VmPopInstr(pGen->pVm);
+                               }
+                       }
+               }
+       }
+       if( iVmOp > 0 ){
+               if( iVmOp == JX9_OP_INCR || iVmOp == JX9_OP_DECR ){
+                       if( pNode->iFlags & EXPR_NODE_PRE_INCR ){
+                               /* Pre-increment/decrement operator [i.e: ++$i, --$j ] */
+                               iP1 = 1;
+                       }
+               }
+               /* Finally, emit the VM instruction associated with this operator */
+               jx9VmEmitInstr(pGen->pVm, iVmOp, iP1, iP2, p3, 0);
+               if( nJmpIdx > 0 ){
+                       /* Fix short-circuited jumps now the destination is resolved */
+                       pInstr = jx9VmGetInstr(pGen->pVm, nJmpIdx);
+                       if( pInstr ){
+                               pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
+                       }
+               }
+       }
+       return rc;
+}
+/*
+ * Compile a JX9 expression.
+ * According to the JX9 language reference manual:
+ *  Expressions are the most important building stones of JX9.
+ *  In JX9, almost anything you write is an expression.
+ *  The simplest yet most accurate way to define an expression
+ *  is "anything that has a value". 
+ * If something goes wrong while compiling the expression, this
+ * function takes care of generating the appropriate error
+ * message.
+ */
+static sxi32 jx9CompileExpr(
+       jx9_gen_state *pGen, /* Code generator state */
+       sxi32 iFlags,        /* Control flags */
+       sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
+       )
+{
+       jx9_expr_node *pRoot;
+       SySet sExprNode;
+       SyToken *pEnd;
+       sxi32 nExpr;
+       sxi32 iNest;
+       sxi32 rc;
+       /* Initialize worker variables */
+       nExpr = 0;
+       pRoot = 0;
+       SySetInit(&sExprNode, &pGen->pVm->sAllocator, sizeof(jx9_expr_node *));
+       SySetAlloc(&sExprNode, 0x10);
+       rc = SXRET_OK;
+       /* Delimit the expression */
+       pEnd = pGen->pIn;
+       iNest = 0;
+       while( pEnd < pGen->pEnd ){
+               if( pEnd->nType & JX9_TK_OCB /* '{' */ ){
+                       /* Ticket 1433-30: Annonymous/Closure functions body */
+                       iNest++;
+               }else if(pEnd->nType & JX9_TK_CCB /* '}' */ ){
+                       iNest--;
+               }else if( pEnd->nType & JX9_TK_SEMI /* ';' */ ){
+                       if( iNest <= 0 ){
+                               break;
+                       }
+               }
+               pEnd++;
+       }
+       if( iFlags & EXPR_FLAG_COMMA_STATEMENT ){
+               SyToken *pEnd2 = pGen->pIn;
+               iNest = 0;
+               /* Stop at the first comma */
+               while( pEnd2 < pEnd ){
+                       if( pEnd2->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_LPAREN/*'('*/) ){
+                               iNest++;
+                       }else if(pEnd2->nType & (JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*']'*/|JX9_TK_RPAREN/*')'*/)){
+                               iNest--;
+                       }else if( pEnd2->nType & JX9_TK_COMMA /*','*/ ){
+                               if( iNest <= 0 ){
+                                       break;
+                               }
+                       }
+                       pEnd2++;
+               }
+               if( pEnd2 <pEnd ){
+                       pEnd = pEnd2;
+               }
+       }
+       if( pEnd > pGen->pIn ){
+               SyToken *pTmp = pGen->pEnd;
+               /* Swap delimiter */
+               pGen->pEnd = pEnd;
+               /* Try to get an expression tree */
+               rc = jx9ExprMakeTree(&(*pGen), &sExprNode, &pRoot);
+               if( rc == SXRET_OK && pRoot ){
+                       rc = SXRET_OK;
+                       if( xTreeValidator ){
+                               /* Call the upper layer validator callback */
+                               rc = xTreeValidator(&(*pGen), pRoot);
+                       }
+                       if( rc != SXERR_ABORT ){
+                               /* Generate code for the given tree */
+                               rc = GenStateEmitExprCode(&(*pGen), pRoot, iFlags);
+                       }
+                       nExpr = 1;
+               }
+               /* Release the whole tree */
+               jx9ExprFreeTree(&(*pGen), &sExprNode);
+               /* Synchronize token stream */
+               pGen->pEnd = pTmp;
+               pGen->pIn  = pEnd;
+               if( rc == SXERR_ABORT ){
+                       SySetRelease(&sExprNode);
+                       return SXERR_ABORT;
+               }
+       }
+       SySetRelease(&sExprNode);
+       return nExpr > 0 ? SXRET_OK : SXERR_EMPTY;
+}
+/*
+ * Return a pointer to the node construct handler associated
+ * with a given node type [i.e: string, integer, float, ...].
+ */
+JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType)
+{
+       if( nNodeType & JX9_TK_NUM ){
+               /* Numeric literal: Either real or integer */
+               return jx9CompileNumLiteral;
+       }else if( nNodeType & JX9_TK_DSTR ){
+               /* Double quoted string */
+               return jx9CompileString;
+       }else if( nNodeType & JX9_TK_SSTR ){
+               /* Single quoted string */
+               return jx9CompileSimpleString;
+       }else if( nNodeType & JX9_TK_NOWDOC ){
+               /* Nowdoc */
+               return jx9CompileNowdoc;
+       }
+       return 0;
+}
+/*
+ * Jx9 Language construct table.
+ */
+static const LangConstruct aLangConstruct[] = {
+       { JX9_TKWRD_IF,       jx9CompileIf     },
+       { JX9_TKWRD_FUNCTION, jx9CompileFunction  },
+       { JX9_TKWRD_FOREACH,  jx9CompileForeach },
+       { JX9_TKWRD_WHILE,    jx9CompileWhile  },
+       { JX9_TKWRD_FOR,      jx9CompileFor    },
+       { JX9_TKWRD_SWITCH,   jx9CompileSwitch },
+       { JX9_TKWRD_DIE,      jx9CompileHalt   },
+       { JX9_TKWRD_EXIT,     jx9CompileHalt   },
+       { JX9_TKWRD_RETURN,   jx9CompileReturn },
+       { JX9_TKWRD_BREAK,    jx9CompileBreak  },
+       { JX9_TKWRD_CONTINUE, jx9CompileContinue  },
+       { JX9_TKWRD_STATIC,   jx9CompileStatic    },
+       { JX9_TKWRD_UPLINK,   jx9CompileUplink  },
+       { JX9_TKWRD_CONST,    jx9CompileConstant  },
+};
+/*
+ * Return a pointer to the statement handler routine associated
+ * with a given JX9 keyword [i.e: if, for, while, ...].
+ */
+static ProcLangConstruct GenStateGetStatementHandler(
+       sxu32 nKeywordID   /* Keyword  ID*/
+       )
+{
+       sxu32 n = 0;
+       for(;;){
+               if( n >= SX_ARRAYSIZE(aLangConstruct) ){
+                       break;
+               }
+               if( aLangConstruct[n].nID == nKeywordID ){
+                       /* Return a pointer to the handler.
+                       */
+                       return aLangConstruct[n].xConstruct;
+               }
+               n++;
+       }
+       /* Not a language construct */
+       return 0;
+}
+/*
+ * Compile a jx9 program.
+ * If something goes wrong while compiling the Jx9 chunk, this function
+ * takes care of generating the appropriate error message.
+ */
+static sxi32 GenStateCompileChunk(
+       jx9_gen_state *pGen, /* Code generator state */
+       sxi32 iFlags             /* Compile flags */
+       )
+{
+       ProcLangConstruct xCons;
+       sxi32 rc;
+       rc = SXRET_OK; /* Prevent compiler warning */
+       for(;;){
+               if( pGen->pIn >= pGen->pEnd ){
+                       /* No more input to process */
+                       break;
+               }
+               xCons = 0;
+               if( pGen->pIn->nType & JX9_TK_KEYWORD ){
+                       sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
+                       /* Try to extract a language construct handler */
+                       xCons = GenStateGetStatementHandler(nKeyword);
+                       if( xCons == 0 && !jx9IsLangConstruct(nKeyword) ){
+                               rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
+                                       "Syntax error: Unexpected keyword '%z'",
+                                       &pGen->pIn->sData);
+                               if( rc == SXERR_ABORT ){
+                                       break;
+                               }
+                               /* Synchronize with the first semi-colon and avoid compiling
+                                * this erroneous statement.
+                                */
+                               xCons = jx9ErrorRecover;
+                       }
+               }
+               if( xCons == 0 ){
+                       /* Assume an expression an try to compile it */
+                       rc = jx9CompileExpr(&(*pGen), 0, 0);
+                       if(  rc != SXERR_EMPTY ){
+                               /* Pop l-value */
+                               jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
+                       }
+               }else{
+                       /* Go compile the sucker */
+                       rc = xCons(&(*pGen));
+               }
+               if( rc == SXERR_ABORT ){
+                       /* Request to abort compilation */
+                       break;
+               }
+               /* Ignore trailing semi-colons ';' */
+               while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
+                       pGen->pIn++;
+               }
+               if( iFlags & JX9_COMPILE_SINGLE_STMT ){
+                       /* Compile a single statement and return */
+                       break;
+               }
+               /* LOOP ONE */
+               /* LOOP TWO */
+               /* LOOP THREE */
+               /* LOOP FOUR */
+       }
+       /* Return compilation status */
+       return rc;
+}
+/*
+ * Compile a raw chunk. The raw chunk can contain JX9 code embedded
+ * in HTML, XML and so on. This function handle all the stuff.
+ * This is the only compile interface exported from this file.
+ */
+JX9_PRIVATE sxi32 jx9CompileScript(
+       jx9_vm *pVm,        /* Generate JX9 bytecodes for this Virtual Machine */
+       SyString *pScript,  /* Script to compile */
+       sxi32 iFlags        /* Compile flags */
+       )
+{
+       jx9_gen_state *pGen;
+       SySet aToken;
+       sxi32 rc;
+       if( pScript->nByte < 1 ){
+               /* Nothing to compile */
+               return JX9_OK;
+       }
+       /* Initialize the tokens containers */
+       SySetInit(&aToken, &pVm->sAllocator, sizeof(SyToken));
+       SySetAlloc(&aToken, 0xc0);
+       pGen = &pVm->sCodeGen;
+       rc = JX9_OK;
+       /* Tokenize the JX9 chunk first */
+       jx9Tokenize(pScript->zString,pScript->nByte,&aToken);
+       if( SySetUsed(&aToken) < 1 ){
+               return SXERR_EMPTY;
+       }
+       /* Point to the head and tail of the token stream. */
+       pGen->pIn  = (SyToken *)SySetBasePtr(&aToken);
+       pGen->pEnd = &pGen->pIn[SySetUsed(&aToken)];
+       /* Compile the chunk */
+       rc = GenStateCompileChunk(pGen,iFlags);
+       /* Cleanup */
+       SySetRelease(&aToken);
+       return rc;
+}
+/*
+ * Utility routines.Initialize the code generator.
+ */
+JX9_PRIVATE sxi32 jx9InitCodeGenerator(
+       jx9_vm *pVm,       /* Target VM */
+       ProcConsumer xErr, /* Error log consumer callabck  */
+       void *pErrData     /* Last argument to xErr() */
+       )
+{
+       jx9_gen_state *pGen = &pVm->sCodeGen;
+       /* Zero the structure */
+       SyZero(pGen, sizeof(jx9_gen_state));
+       /* Initial state */
+       pGen->pVm  = &(*pVm);
+       pGen->xErr = xErr;
+       pGen->pErrData = pErrData;
+       SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0);
+       SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0);
+       /* Create the global scope */
+       GenStateInitBlock(pGen, &pGen->sGlobal,GEN_BLOCK_GLOBAL,jx9VmInstrLength(&(*pVm)), 0);
+       /* Point to the global scope */
+       pGen->pCurrent = &pGen->sGlobal;
+       return SXRET_OK;
+}
+/*
+ * Utility routines. Reset the code generator to it's initial state.
+ */
+JX9_PRIVATE sxi32 jx9ResetCodeGenerator(
+       jx9_vm *pVm,       /* Target VM */
+       ProcConsumer xErr, /* Error log consumer callabck  */
+       void *pErrData     /* Last argument to xErr() */
+       )
+{
+       jx9_gen_state *pGen = &pVm->sCodeGen;
+       GenBlock *pBlock, *pParent;
+       /* Point to the global scope */
+       pBlock = pGen->pCurrent;
+       while( pBlock->pParent != 0 ){
+               pParent = pBlock->pParent;
+               GenStateFreeBlock(pBlock);
+               pBlock = pParent;
+       }
+       pGen->xErr = xErr;
+       pGen->pErrData = pErrData;
+       pGen->pCurrent = &pGen->sGlobal;
+       pGen->pIn = pGen->pEnd = 0;
+       pGen->nErr = 0;
+       return SXRET_OK;
+}
+/*
+ * Generate a compile-time error message.
+ * If the error count limit is reached (usually 15 error message)
+ * this function return SXERR_ABORT.In that case upper-layers must
+ * abort compilation immediately.
+ */
+JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...)
+{
+       SyBlob *pWorker = &pGen->pVm->pEngine->xConf.sErrConsumer;
+       const char *zErr = "Error";
+       va_list ap;
+       if( nErrType == E_ERROR ){
+               /* Increment the error counter */
+               pGen->nErr++;
+               if( pGen->nErr > 15 ){
+                       /* Error count limit reached */
+                       SyBlobFormat(pWorker, "%u Error count limit reached, JX9 is aborting compilation\n", nLine);    
+                       /* Abort immediately */
+                       return SXERR_ABORT;
+               }
+       }
+       switch(nErrType){
+       case E_WARNING: zErr = "Warning";     break;
+       case E_PARSE:   zErr = "Parse error"; break;
+       case E_NOTICE:  zErr = "Notice";      break;
+       default:
+               break;
+       }
+       /* Format the error message */
+       SyBlobFormat(pWorker, "%u %s: ", nLine, zErr);
+       va_start(ap, zFormat);
+       SyBlobFormatAp(pWorker, zFormat, ap);
+       va_end(ap);
+       /* Append a new line */
+       SyBlobAppend(pWorker, (const void *)"\n", sizeof(char));
+       return JX9_OK;
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_const.c
+ * MD5: f3980b00dd1eda0bb2b749424a8dfffe
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: const.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+/* This file implement built-in constants for the JX9 engine. */
+/*
+ * JX9_VERSION
+ * __JX9__
+ *   Expand the current version of the JX9 engine.
+ */
+static void JX9_VER_Const(jx9_value *pVal, void *pUnused)
+{
+       SXUNUSED(pUnused);
+       jx9_value_string(pVal, jx9_lib_signature(), -1/*Compute length automatically*/);
+}
+#ifdef __WINNT__
+#include <Windows.h>
+#elif defined(__UNIXES__)
+#include <sys/utsname.h>
+#endif
+/*
+ * JX9_OS
+ * __OS__
+ *  Expand the name of the host Operating System.
+ */
+static void JX9_OS_Const(jx9_value *pVal, void *pUnused)
+{
+#if defined(__WINNT__)
+       jx9_value_string(pVal, "WinNT", (int)sizeof("WinNT")-1);
+#elif defined(__UNIXES__)
+       struct utsname sInfo;
+       if( uname(&sInfo) != 0 ){
+               jx9_value_string(pVal, "Unix", (int)sizeof("Unix")-1);
+       }else{
+               jx9_value_string(pVal, sInfo.sysname, -1);
+       }
+#else
+       jx9_value_string(pVal,"Host OS", (int)sizeof("Host OS")-1);
+#endif
+       SXUNUSED(pUnused);
+}
+/*
+ * JX9_EOL
+ *  Expand the correct 'End Of Line' symbol for this platform.
+ */
+static void JX9_EOL_Const(jx9_value *pVal, void *pUnused)
+{
+       SXUNUSED(pUnused);
+#ifdef __WINNT__
+       jx9_value_string(pVal, "\r\n", (int)sizeof("\r\n")-1);
+#else
+       jx9_value_string(pVal, "\n", (int)sizeof(char));
+#endif
+}
+/*
+ * JX9_INT_MAX
+ * Expand the largest integer supported.
+ * Note that JX9 deals with 64-bit integer for all platforms.
+ */
+static void JX9_INTMAX_Const(jx9_value *pVal, void *pUnused)
+{
+       SXUNUSED(pUnused);
+       jx9_value_int64(pVal, SXI64_HIGH);
+}
+/*
+ * JX9_INT_SIZE
+ * Expand the size in bytes of a 64-bit integer.
+ */
+static void JX9_INTSIZE_Const(jx9_value *pVal, void *pUnused)
+{
+       SXUNUSED(pUnused);
+       jx9_value_int64(pVal, sizeof(sxi64));
+}
+/*
+ * DIRECTORY_SEPARATOR.
+ * Expand the directory separator character.
+ */
+static void JX9_DIRSEP_Const(jx9_value *pVal, void *pUnused)
+{
+       SXUNUSED(pUnused);
+#ifdef __WINNT__
+       jx9_value_string(pVal, "\\", (int)sizeof(char));
+#else
+       jx9_value_string(pVal, "/", (int)sizeof(char));
+#endif
+}
+/*
+ * PATH_SEPARATOR.
+ * Expand the path separator character.
+ */
+static void JX9_PATHSEP_Const(jx9_value *pVal, void *pUnused)
+{
+       SXUNUSED(pUnused);
+#ifdef __WINNT__
+       jx9_value_string(pVal, ";", (int)sizeof(char));
+#else
+       jx9_value_string(pVal, ":", (int)sizeof(char));
+#endif
+}
+#ifndef __WINNT__
+#include <time.h>
+#endif
+/*
+ * __TIME__
+ *  Expand the current time (GMT).
+ */
+static void JX9_TIME_Const(jx9_value *pVal, void *pUnused)
+{
+       Sytm sTm;
+#ifdef __WINNT__
+       SYSTEMTIME sOS;
+       GetSystemTime(&sOS);
+       SYSTEMTIME_TO_SYTM(&sOS, &sTm);
+#else
+       struct tm *pTm;
+       time_t t;
+       time(&t);
+       pTm = gmtime(&t);
+       STRUCT_TM_TO_SYTM(pTm, &sTm);
+#endif
+       SXUNUSED(pUnused); /* cc warning */
+       /* Expand */
+       jx9_value_string_format(pVal, "%02d:%02d:%02d", sTm.tm_hour, sTm.tm_min, sTm.tm_sec);
+}
+/*
+ * __DATE__
+ *  Expand the current date in the ISO-8601 format.
+ */
+static void JX9_DATE_Const(jx9_value *pVal, void *pUnused)
+{
+       Sytm sTm;
+#ifdef __WINNT__
+       SYSTEMTIME sOS;
+       GetSystemTime(&sOS);
+       SYSTEMTIME_TO_SYTM(&sOS, &sTm);
+#else
+       struct tm *pTm;
+       time_t t;
+       time(&t);
+       pTm = gmtime(&t);
+       STRUCT_TM_TO_SYTM(pTm, &sTm);
+#endif
+       SXUNUSED(pUnused); /* cc warning */
+       /* Expand */
+       jx9_value_string_format(pVal, "%04d-%02d-%02d", sTm.tm_year, sTm.tm_mon+1, sTm.tm_mday);
+}
+/*
+ * __FILE__
+ *  Path of the processed script.
+ */
+static void JX9_FILE_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_vm *pVm = (jx9_vm *)pUserData;
+       SyString *pFile;
+       /* Peek the top entry */
+       pFile = (SyString *)SySetPeek(&pVm->aFiles);
+       if( pFile == 0 ){
+               /* Expand the magic word: ":MEMORY:" */
+               jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
+       }else{
+               jx9_value_string(pVal, pFile->zString, pFile->nByte);
+       }
+}
+/*
+ * __DIR__
+ *  Directory holding the processed script.
+ */
+static void JX9_DIR_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_vm *pVm = (jx9_vm *)pUserData;
+       SyString *pFile;
+       /* Peek the top entry */
+       pFile = (SyString *)SySetPeek(&pVm->aFiles);
+       if( pFile == 0 ){
+               /* Expand the magic word: ":MEMORY:" */
+               jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
+       }else{
+               if( pFile->nByte > 0 ){
+                       const char *zDir;
+                       int nLen;
+                       zDir = jx9ExtractDirName(pFile->zString, (int)pFile->nByte, &nLen);
+                       jx9_value_string(pVal, zDir, nLen);
+               }else{
+                       /* Expand '.' as the current directory*/
+                       jx9_value_string(pVal, ".", (int)sizeof(char));
+               }
+       }
+}
+/*
+ * E_ERROR
+ *  Expands 1
+ */
+static void JX9_E_ERROR_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 1);
+       SXUNUSED(pUserData);
+}
+/*
+ * E_WARNING
+ *  Expands 2
+ */
+static void JX9_E_WARNING_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 2);
+       SXUNUSED(pUserData);
+}
+/*
+ * E_PARSE
+ *  Expands 4
+ */
+static void JX9_E_PARSE_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 4);
+       SXUNUSED(pUserData);
+}
+/*
+ * E_NOTICE
+ * Expands 8
+ */
+static void JX9_E_NOTICE_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 8);
+       SXUNUSED(pUserData);
+}
+/*
+ * CASE_LOWER
+ *  Expands 0.
+ */
+static void JX9_CASE_LOWER_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 0);
+       SXUNUSED(pUserData);
+}
+/*
+ * CASE_UPPER
+ *  Expands 1.
+ */
+static void JX9_CASE_UPPER_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 1);
+       SXUNUSED(pUserData);
+}
+/*
+ * STR_PAD_LEFT
+ *  Expands 0.
+ */
+static void JX9_STR_PAD_LEFT_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 0);
+       SXUNUSED(pUserData);
+}
+/*
+ * STR_PAD_RIGHT
+ *  Expands 1.
+ */
+static void JX9_STR_PAD_RIGHT_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 1);
+       SXUNUSED(pUserData);
+}
+/*
+ * STR_PAD_BOTH
+ *  Expands 2.
+ */
+static void JX9_STR_PAD_BOTH_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 2);
+       SXUNUSED(pUserData);
+}
+/*
+ * COUNT_NORMAL
+ *  Expands 0
+ */
+static void JX9_COUNT_NORMAL_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 0);
+       SXUNUSED(pUserData);
+}
+/*
+ * COUNT_RECURSIVE
+ *  Expands 1.
+ */
+static void JX9_COUNT_RECURSIVE_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 1);
+       SXUNUSED(pUserData);
+}
+/*
+ * SORT_ASC
+ *  Expands 1.
+ */
+static void JX9_SORT_ASC_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 1);
+       SXUNUSED(pUserData);
+}
+/*
+ * SORT_DESC
+ *  Expands 2.
+ */
+static void JX9_SORT_DESC_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 2);
+       SXUNUSED(pUserData);
+}
+/*
+ * SORT_REGULAR
+ *  Expands 3.
+ */
+static void JX9_SORT_REG_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 3);
+       SXUNUSED(pUserData);
+}
+/*
+ * SORT_NUMERIC
+ *  Expands 4.
+ */
+static void JX9_SORT_NUMERIC_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 4);
+       SXUNUSED(pUserData);
+}
+/*
+ * SORT_STRING
+ *  Expands 5.
+ */
+static void JX9_SORT_STRING_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 5);
+       SXUNUSED(pUserData);
+}
+/*
+ * JX9_ROUND_HALF_UP
+ *  Expands 1.
+ */
+static void JX9_JX9_ROUND_HALF_UP_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 1);
+       SXUNUSED(pUserData);
+}
+/*
+ * SJX9_ROUND_HALF_DOWN
+ *  Expands 2.
+ */
+static void JX9_JX9_ROUND_HALF_DOWN_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 2);
+       SXUNUSED(pUserData);
+}
+/*
+ * JX9_ROUND_HALF_EVEN
+ *  Expands 3.
+ */
+static void JX9_JX9_ROUND_HALF_EVEN_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 3);
+       SXUNUSED(pUserData);
+}
+/*
+ * JX9_ROUND_HALF_ODD
+ *  Expands 4.
+ */
+static void JX9_JX9_ROUND_HALF_ODD_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_value_int(pVal, 4);
+       SXUNUSED(pUserData);
+}
+#ifdef JX9_ENABLE_MATH_FUNC
+/*
+ * PI
+ *  Expand the value of pi.
+ */
+static void JX9_M_PI_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, JX9_PI);
+}
+/*
+ * M_E
+ *  Expand 2.7182818284590452354
+ */
+static void JX9_M_E_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 2.7182818284590452354);
+}
+/*
+ * M_LOG2E
+ *  Expand 2.7182818284590452354
+ */
+static void JX9_M_LOG2E_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 1.4426950408889634074);
+}
+/*
+ * M_LOG10E
+ *  Expand 0.4342944819032518276
+ */
+static void JX9_M_LOG10E_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 0.4342944819032518276);
+}
+/*
+ * M_LN2
+ *  Expand     0.69314718055994530942
+ */
+static void JX9_M_LN2_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 0.69314718055994530942);
+}
+/*
+ * M_LN10
+ *  Expand     2.30258509299404568402
+ */
+static void JX9_M_LN10_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 2.30258509299404568402);
+}
+/*
+ * M_PI_2
+ *  Expand     1.57079632679489661923
+ */
+static void JX9_M_PI_2_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 1.57079632679489661923);
+}
+/*
+ * M_PI_4
+ *  Expand     0.78539816339744830962
+ */
+static void JX9_M_PI_4_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 0.78539816339744830962);
+}
+/*
+ * M_1_PI
+ *  Expand     0.31830988618379067154
+ */
+static void JX9_M_1_PI_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 0.31830988618379067154);
+}
+/*
+ * M_2_PI
+ *  Expand 0.63661977236758134308
+ */
+static void JX9_M_2_PI_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 0.63661977236758134308);
+}
+/*
+ * M_SQRTPI
+ *  Expand 1.77245385090551602729
+ */
+static void JX9_M_SQRTPI_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 1.77245385090551602729);
+}
+/*
+ * M_2_SQRTPI
+ *  Expand     1.12837916709551257390
+ */
+static void JX9_M_2_SQRTPI_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 1.12837916709551257390);
+}
+/*
+ * M_SQRT2
+ *  Expand     1.41421356237309504880
+ */
+static void JX9_M_SQRT2_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 1.41421356237309504880);
+}
+/*
+ * M_SQRT3
+ *  Expand     1.73205080756887729352
+ */
+static void JX9_M_SQRT3_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 1.73205080756887729352);
+}
+/*
+ * M_SQRT1_2
+ *  Expand     0.70710678118654752440
+ */
+static void JX9_M_SQRT1_2_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 0.70710678118654752440);
+}
+/*
+ * M_LNPI
+ *  Expand     1.14472988584940017414
+ */
+static void JX9_M_LNPI_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 1.14472988584940017414);
+}
+/*
+ * M_EULER
+ *  Expand  0.57721566490153286061
+ */
+static void JX9_M_EULER_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_double(pVal, 0.57721566490153286061);
+}
+#endif /* JX9_DISABLE_BUILTIN_MATH */
+/*
+ * DATE_ATOM
+ *  Expand Atom (example: 2005-08-15T15:52:01+00:00) 
+ */
+static void JX9_DATE_ATOM_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_COOKIE
+ *  HTTP Cookies (example: Monday, 15-Aug-05 15:52:01 UTC)  
+ */
+static void JX9_DATE_COOKIE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_ISO8601
+ *  ISO-8601 (example: 2005-08-15T15:52:01+0000) 
+ */
+static void JX9_DATE_ISO8601_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "Y-m-d\\TH:i:sO", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_RFC822
+ *  RFC 822 (example: Mon, 15 Aug 05 15:52:01 +0000) 
+ */
+static void JX9_DATE_RFC822_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_RFC850
+ *  RFC 850 (example: Monday, 15-Aug-05 15:52:01 UTC) 
+ */
+static void JX9_DATE_RFC850_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_RFC1036
+ *  RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000) 
+ */
+static void JX9_DATE_RFC1036_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_RFC1123
+ *  RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000)  
+ */
+static void JX9_DATE_RFC1123_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_RFC2822
+ *  RFC 2822 (Mon, 15 Aug 2005 15:52:01 +0000)  
+ */
+static void JX9_DATE_RFC2822_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_RSS
+ *  RSS (Mon, 15 Aug 2005 15:52:01 +0000) 
+ */
+static void JX9_DATE_RSS_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
+}
+/*
+ * DATE_W3C
+ *  World Wide Web Consortium (example: 2005-08-15T15:52:01+00:00) 
+ */
+static void JX9_DATE_W3C_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
+}
+/*
+ * ENT_COMPAT
+ *  Expand 0x01 (Must be a power of two)
+ */
+static void JX9_ENT_COMPAT_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x01);
+}
+/*
+ * ENT_QUOTES
+ *  Expand 0x02 (Must be a power of two)
+ */
+static void JX9_ENT_QUOTES_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x02);
+}
+/*
+ * ENT_NOQUOTES
+ *  Expand 0x04 (Must be a power of two)
+ */
+static void JX9_ENT_NOQUOTES_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x04);
+}
+/*
+ * ENT_IGNORE
+ *  Expand 0x08 (Must be a power of two)
+ */
+static void JX9_ENT_IGNORE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x08);
+}
+/*
+ * ENT_SUBSTITUTE
+ *  Expand 0x10 (Must be a power of two)
+ */
+static void JX9_ENT_SUBSTITUTE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x10);
+}
+/*
+ * ENT_DISALLOWED
+ *  Expand 0x20 (Must be a power of two)
+ */
+static void JX9_ENT_DISALLOWED_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x20);
+}
+/*
+ * ENT_HTML401
+ *  Expand 0x40 (Must be a power of two)
+ */
+static void JX9_ENT_HTML401_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x40);
+}
+/*
+ * ENT_XML1
+ *  Expand 0x80 (Must be a power of two)
+ */
+static void JX9_ENT_XML1_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x80);
+}
+/*
+ * ENT_XHTML
+ *  Expand 0x100 (Must be a power of two)
+ */
+static void JX9_ENT_XHTML_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x100);
+}
+/*
+ * ENT_HTML5
+ *  Expand 0x200 (Must be a power of two)
+ */
+static void JX9_ENT_HTML5_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x200);
+}
+/*
+ * ISO-8859-1
+ * ISO_8859_1
+ *   Expand 1
+ */
+static void JX9_ISO88591_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * UTF-8
+ * UTF8
+ *  Expand 2
+ */
+static void JX9_UTF8_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * HTML_ENTITIES
+ *  Expand 1
+ */
+static void JX9_HTML_ENTITIES_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * HTML_SPECIALCHARS
+ *  Expand 2
+ */
+static void JX9_HTML_SPECIALCHARS_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 2);
+}
+/*
+ * JX9_URL_SCHEME.
+ * Expand 1
+ */
+static void JX9_JX9_URL_SCHEME_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * JX9_URL_HOST.
+ * Expand 2
+ */
+static void JX9_JX9_URL_HOST_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 2);
+}
+/*
+ * JX9_URL_PORT.
+ * Expand 3
+ */
+static void JX9_JX9_URL_PORT_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 3);
+}
+/*
+ * JX9_URL_USER.
+ * Expand 4
+ */
+static void JX9_JX9_URL_USER_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 4);
+}
+/*
+ * JX9_URL_PASS.
+ * Expand 5
+ */
+static void JX9_JX9_URL_PASS_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 5);
+}
+/*
+ * JX9_URL_PATH.
+ * Expand 6
+ */
+static void JX9_JX9_URL_PATH_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 6);
+}
+/*
+ * JX9_URL_QUERY.
+ * Expand 7
+ */
+static void JX9_JX9_URL_QUERY_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 7);
+}
+/*
+ * JX9_URL_FRAGMENT.
+ * Expand 8
+ */
+static void JX9_JX9_URL_FRAGMENT_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 8);
+}
+/*
+ * JX9_QUERY_RFC1738
+ * Expand 1
+ */
+static void JX9_JX9_QUERY_RFC1738_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * JX9_QUERY_RFC3986
+ * Expand 1
+ */
+static void JX9_JX9_QUERY_RFC3986_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 2);
+}
+/*
+ * FNM_NOESCAPE
+ *  Expand 0x01 (Must be a power of two)
+ */
+static void JX9_FNM_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x01);
+}
+/*
+ * FNM_PATHNAME
+ *  Expand 0x02 (Must be a power of two)
+ */
+static void JX9_FNM_PATHNAME_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x02);
+}
+/*
+ * FNM_PERIOD
+ *  Expand 0x04 (Must be a power of two)
+ */
+static void JX9_FNM_PERIOD_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x04);
+}
+/*
+ * FNM_CASEFOLD
+ *  Expand 0x08 (Must be a power of two)
+ */
+static void JX9_FNM_CASEFOLD_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x08);
+}
+/*
+ * PATHINFO_DIRNAME
+ *  Expand 1.
+ */
+static void JX9_PATHINFO_DIRNAME_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * PATHINFO_BASENAME
+ *  Expand 2.
+ */
+static void JX9_PATHINFO_BASENAME_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 2);
+}
+/*
+ * PATHINFO_EXTENSION
+ *  Expand 3.
+ */
+static void JX9_PATHINFO_EXTENSION_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 3);
+}
+/*
+ * PATHINFO_FILENAME
+ *  Expand 4.
+ */
+static void JX9_PATHINFO_FILENAME_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 4);
+}
+/*
+ * ASSERT_ACTIVE.
+ *  Expand the value of JX9_ASSERT_ACTIVE defined in jx9Int.h
+ */
+static void JX9_ASSERT_ACTIVE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, JX9_ASSERT_DISABLE);
+}
+/*
+ * ASSERT_WARNING.
+ *  Expand the value of JX9_ASSERT_WARNING defined in jx9Int.h
+ */
+static void JX9_ASSERT_WARNING_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, JX9_ASSERT_WARNING);
+}
+/*
+ * ASSERT_BAIL.
+ *  Expand the value of JX9_ASSERT_BAIL defined in jx9Int.h
+ */
+static void JX9_ASSERT_BAIL_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, JX9_ASSERT_BAIL);
+}
+/*
+ * ASSERT_QUIET_EVAL.
+ *  Expand the value of JX9_ASSERT_QUIET_EVAL defined in jx9Int.h
+ */
+static void JX9_ASSERT_QUIET_EVAL_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, JX9_ASSERT_QUIET_EVAL);
+}
+/*
+ * ASSERT_CALLBACK.
+ *  Expand the value of JX9_ASSERT_CALLBACK defined in jx9Int.h
+ */
+static void JX9_ASSERT_CALLBACK_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, JX9_ASSERT_CALLBACK);
+}
+/*
+ * SEEK_SET.
+ *  Expand 0
+ */
+static void JX9_SEEK_SET_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0);
+}
+/*
+ * SEEK_CUR.
+ *  Expand 1
+ */
+static void JX9_SEEK_CUR_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * SEEK_END.
+ *  Expand 2
+ */
+static void JX9_SEEK_END_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 2);
+}
+/*
+ * LOCK_SH.
+ *  Expand 2
+ */
+static void JX9_LOCK_SH_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * LOCK_NB.
+ *  Expand 5
+ */
+static void JX9_LOCK_NB_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 5);
+}
+/*
+ * LOCK_EX.
+ *  Expand 0x01 (MUST BE A POWER OF TWO)
+ */
+static void JX9_LOCK_EX_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x01);
+}
+/*
+ * LOCK_UN.
+ *  Expand 0
+ */
+static void JX9_LOCK_UN_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0);
+}
+/*
+ * FILE_USE_INC_PATH
+ *  Expand 0x01 (Must be a power of two)
+ */
+static void JX9_FILE_USE_INCLUDE_PATH_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x1);
+}
+/*
+ * FILE_IGN_NL
+ *  Expand 0x02 (Must be a power of two)
+ */
+static void JX9_FILE_IGNORE_NEW_LINES_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x2);
+}
+/*
+ * FILE_SKIP_EL
+ *  Expand 0x04 (Must be a power of two)
+ */
+static void JX9_FILE_SKIP_EMPTY_LINES_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x4);
+}
+/*
+ * FILE_APPEND
+ *  Expand 0x08 (Must be a power of two)
+ */
+static void JX9_FILE_APPEND_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x08);
+}
+/*
+ * SCANDIR_SORT_ASCENDING
+ *  Expand 0
+ */
+static void JX9_SCANDIR_SORT_ASCENDING_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0);
+}
+/*
+ * SCANDIR_SORT_DESCENDING
+ *  Expand 1
+ */
+static void JX9_SCANDIR_SORT_DESCENDING_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * SCANDIR_SORT_NONE
+ *  Expand 2
+ */
+static void JX9_SCANDIR_SORT_NONE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 2);
+}
+/*
+ * GLOB_MARK
+ *  Expand 0x01 (must be a power of two)
+ */
+static void JX9_GLOB_MARK_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x01);
+}
+/*
+ * GLOB_NOSORT
+ *  Expand 0x02 (must be a power of two)
+ */
+static void JX9_GLOB_NOSORT_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x02);
+}
+/*
+ * GLOB_NOCHECK
+ *  Expand 0x04 (must be a power of two)
+ */
+static void JX9_GLOB_NOCHECK_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x04);
+}
+/*
+ * GLOB_NOESCAPE
+ *  Expand 0x08 (must be a power of two)
+ */
+static void JX9_GLOB_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x08);
+}
+/*
+ * GLOB_BRACE
+ *  Expand 0x10 (must be a power of two)
+ */
+static void JX9_GLOB_BRACE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x10);
+}
+/*
+ * GLOB_ONLYDIR
+ *  Expand 0x20 (must be a power of two)
+ */
+static void JX9_GLOB_ONLYDIR_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x20);
+}
+/*
+ * GLOB_ERR
+ *  Expand 0x40 (must be a power of two)
+ */
+static void JX9_GLOB_ERR_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x40);
+}
+/*
+ * STDIN
+ *  Expand the STDIN handle as a resource.
+ */
+static void JX9_STDIN_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_vm *pVm = (jx9_vm *)pUserData;
+       void *pResource;
+       pResource = jx9ExportStdin(pVm);
+       jx9_value_resource(pVal, pResource);
+}
+/*
+ * STDOUT
+ *   Expand the STDOUT handle as a resource.
+ */
+static void JX9_STDOUT_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_vm *pVm = (jx9_vm *)pUserData;
+       void *pResource;
+       pResource = jx9ExportStdout(pVm);
+       jx9_value_resource(pVal, pResource);
+}
+/*
+ * STDERR
+ *  Expand the STDERR handle as a resource.
+ */
+static void JX9_STDERR_Const(jx9_value *pVal, void *pUserData)
+{
+       jx9_vm *pVm = (jx9_vm *)pUserData;
+       void *pResource;
+       pResource = jx9ExportStderr(pVm);
+       jx9_value_resource(pVal, pResource);
+}
+/*
+ * INI_SCANNER_NORMAL
+ *   Expand 1
+ */
+static void JX9_INI_SCANNER_NORMAL_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 1);
+}
+/*
+ * INI_SCANNER_RAW
+ *   Expand 2
+ */
+static void JX9_INI_SCANNER_RAW_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 2);
+}
+/*
+ * EXTR_OVERWRITE
+ *   Expand 0x01 (Must be a power of two)
+ */
+static void JX9_EXTR_OVERWRITE_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x1);
+}
+/*
+ * EXTR_SKIP
+ *   Expand 0x02 (Must be a power of two)
+ */
+static void JX9_EXTR_SKIP_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x2);
+}
+/*
+ * EXTR_PREFIX_SAME
+ *   Expand 0x04 (Must be a power of two)
+ */
+static void JX9_EXTR_PREFIX_SAME_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x4);
+}
+/*
+ * EXTR_PREFIX_ALL
+ *   Expand 0x08 (Must be a power of two)
+ */
+static void JX9_EXTR_PREFIX_ALL_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x8);
+}
+/*
+ * EXTR_PREFIX_INVALID
+ *   Expand 0x10 (Must be a power of two)
+ */
+static void JX9_EXTR_PREFIX_INVALID_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x10);
+}
+/*
+ * EXTR_IF_EXISTS
+ *   Expand 0x20 (Must be a power of two)
+ */
+static void JX9_EXTR_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x20);
+}
+/*
+ * EXTR_PREFIX_IF_EXISTS
+ *   Expand 0x40 (Must be a power of two)
+ */
+static void JX9_EXTR_PREFIX_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
+{
+       SXUNUSED(pUserData); /* cc warning */
+       jx9_value_int(pVal, 0x40);
+}
+/*
+ * Table of built-in constants.
+ */
+static const jx9_builtin_constant aBuiltIn[] = {
+       {"JX9_VERSION",          JX9_VER_Const      }, 
+       {"JX9_ENGINE",           JX9_VER_Const      }, 
+       {"__JX9__",              JX9_VER_Const      }, 
+       {"JX9_OS",               JX9_OS_Const       }, 
+       {"__OS__",               JX9_OS_Const       }, 
+       {"JX9_EOL",              JX9_EOL_Const      }, 
+       {"JX9_INT_MAX",          JX9_INTMAX_Const   }, 
+       {"MAXINT",               JX9_INTMAX_Const   }, 
+       {"JX9_INT_SIZE",         JX9_INTSIZE_Const  }, 
+       {"PATH_SEPARATOR",       JX9_PATHSEP_Const  }, 
+       {"DIRECTORY_SEPARATOR",  JX9_DIRSEP_Const   }, 
+       {"DIR_SEP",              JX9_DIRSEP_Const   }, 
+       {"__TIME__",             JX9_TIME_Const     }, 
+       {"__DATE__",             JX9_DATE_Const     }, 
+       {"__FILE__",             JX9_FILE_Const     }, 
+       {"__DIR__",              JX9_DIR_Const      }, 
+       {"E_ERROR",              JX9_E_ERROR_Const  }, 
+       {"E_WARNING",            JX9_E_WARNING_Const}, 
+       {"E_PARSE",              JX9_E_PARSE_Const  }, 
+       {"E_NOTICE",             JX9_E_NOTICE_Const }, 
+       {"CASE_LOWER",           JX9_CASE_LOWER_Const   }, 
+       {"CASE_UPPER",           JX9_CASE_UPPER_Const   }, 
+       {"STR_PAD_LEFT",         JX9_STR_PAD_LEFT_Const }, 
+       {"STR_PAD_RIGHT",        JX9_STR_PAD_RIGHT_Const}, 
+       {"STR_PAD_BOTH",         JX9_STR_PAD_BOTH_Const }, 
+       {"COUNT_NORMAL",         JX9_COUNT_NORMAL_Const }, 
+       {"COUNT_RECURSIVE",      JX9_COUNT_RECURSIVE_Const }, 
+       {"SORT_ASC",             JX9_SORT_ASC_Const     }, 
+       {"SORT_DESC",            JX9_SORT_DESC_Const    }, 
+       {"SORT_REGULAR",         JX9_SORT_REG_Const     }, 
+       {"SORT_NUMERIC",         JX9_SORT_NUMERIC_Const }, 
+       {"SORT_STRING",          JX9_SORT_STRING_Const  }, 
+       {"JX9_ROUND_HALF_DOWN",  JX9_JX9_ROUND_HALF_DOWN_Const }, 
+       {"JX9_ROUND_HALF_EVEN",  JX9_JX9_ROUND_HALF_EVEN_Const }, 
+       {"JX9_ROUND_HALF_UP",    JX9_JX9_ROUND_HALF_UP_Const   }, 
+       {"JX9_ROUND_HALF_ODD",   JX9_JX9_ROUND_HALF_ODD_Const  }, 
+#ifdef JX9_ENABLE_MATH_FUNC 
+       {"PI",                 JX9_M_PI_Const         }, 
+       {"M_E",                  JX9_M_E_Const          }, 
+       {"M_LOG2E",              JX9_M_LOG2E_Const      }, 
+       {"M_LOG10E",             JX9_M_LOG10E_Const     }, 
+       {"M_LN2",                JX9_M_LN2_Const        }, 
+       {"M_LN10",               JX9_M_LN10_Const       }, 
+       {"M_PI_2",               JX9_M_PI_2_Const       }, 
+       {"M_PI_4",               JX9_M_PI_4_Const       }, 
+       {"M_1_PI",               JX9_M_1_PI_Const       }, 
+       {"M_2_PI",               JX9_M_2_PI_Const       }, 
+       {"M_SQRTPI",             JX9_M_SQRTPI_Const     }, 
+       {"M_2_SQRTPI",           JX9_M_2_SQRTPI_Const   }, 
+       {"M_SQRT2",              JX9_M_SQRT2_Const      }, 
+       {"M_SQRT3",              JX9_M_SQRT3_Const      }, 
+       {"M_SQRT1_2",            JX9_M_SQRT1_2_Const    }, 
+       {"M_LNPI",               JX9_M_LNPI_Const       }, 
+       {"M_EULER",              JX9_M_EULER_Const      }, 
+#endif /* JX9_ENABLE_MATH_FUNC */
+       {"DATE_ATOM",            JX9_DATE_ATOM_Const    }, 
+       {"DATE_COOKIE",          JX9_DATE_COOKIE_Const  }, 
+       {"DATE_ISO8601",         JX9_DATE_ISO8601_Const }, 
+       {"DATE_RFC822",          JX9_DATE_RFC822_Const  }, 
+       {"DATE_RFC850",          JX9_DATE_RFC850_Const  }, 
+       {"DATE_RFC1036",         JX9_DATE_RFC1036_Const }, 
+       {"DATE_RFC1123",         JX9_DATE_RFC1123_Const }, 
+       {"DATE_RFC2822",         JX9_DATE_RFC2822_Const }, 
+       {"DATE_RFC3339",         JX9_DATE_ATOM_Const    }, 
+       {"DATE_RSS",             JX9_DATE_RSS_Const     }, 
+       {"DATE_W3C",             JX9_DATE_W3C_Const     }, 
+       {"ENT_COMPAT",           JX9_ENT_COMPAT_Const   }, 
+       {"ENT_QUOTES",           JX9_ENT_QUOTES_Const   }, 
+       {"ENT_NOQUOTES",         JX9_ENT_NOQUOTES_Const }, 
+       {"ENT_IGNORE",           JX9_ENT_IGNORE_Const   }, 
+       {"ENT_SUBSTITUTE",       JX9_ENT_SUBSTITUTE_Const}, 
+       {"ENT_DISALLOWED",       JX9_ENT_DISALLOWED_Const}, 
+       {"ENT_HTML401",          JX9_ENT_HTML401_Const  }, 
+       {"ENT_XML1",             JX9_ENT_XML1_Const     }, 
+       {"ENT_XHTML",            JX9_ENT_XHTML_Const    }, 
+       {"ENT_HTML5",            JX9_ENT_HTML5_Const    }, 
+       {"ISO-8859-1",           JX9_ISO88591_Const     }, 
+       {"ISO_8859_1",           JX9_ISO88591_Const     }, 
+       {"UTF-8",                JX9_UTF8_Const         }, 
+       {"UTF8",                 JX9_UTF8_Const         }, 
+       {"HTML_ENTITIES",        JX9_HTML_ENTITIES_Const}, 
+       {"HTML_SPECIALCHARS",    JX9_HTML_SPECIALCHARS_Const }, 
+       {"JX9_URL_SCHEME",       JX9_JX9_URL_SCHEME_Const}, 
+       {"JX9_URL_HOST",         JX9_JX9_URL_HOST_Const}, 
+       {"JX9_URL_PORT",         JX9_JX9_URL_PORT_Const}, 
+       {"JX9_URL_USER",         JX9_JX9_URL_USER_Const}, 
+       {"JX9_URL_PASS",         JX9_JX9_URL_PASS_Const}, 
+       {"JX9_URL_PATH",         JX9_JX9_URL_PATH_Const}, 
+       {"JX9_URL_QUERY",        JX9_JX9_URL_QUERY_Const}, 
+       {"JX9_URL_FRAGMENT",     JX9_JX9_URL_FRAGMENT_Const}, 
+       {"JX9_QUERY_RFC1738",    JX9_JX9_QUERY_RFC1738_Const}, 
+       {"JX9_QUERY_RFC3986",    JX9_JX9_QUERY_RFC3986_Const}, 
+       {"FNM_NOESCAPE",         JX9_FNM_NOESCAPE_Const }, 
+       {"FNM_PATHNAME",         JX9_FNM_PATHNAME_Const }, 
+       {"FNM_PERIOD",           JX9_FNM_PERIOD_Const   }, 
+       {"FNM_CASEFOLD",         JX9_FNM_CASEFOLD_Const }, 
+       {"PATHINFO_DIRNAME",     JX9_PATHINFO_DIRNAME_Const  }, 
+       {"PATHINFO_BASENAME",    JX9_PATHINFO_BASENAME_Const }, 
+       {"PATHINFO_EXTENSION",   JX9_PATHINFO_EXTENSION_Const}, 
+       {"PATHINFO_FILENAME",    JX9_PATHINFO_FILENAME_Const }, 
+       {"ASSERT_ACTIVE",        JX9_ASSERT_ACTIVE_Const     }, 
+       {"ASSERT_WARNING",       JX9_ASSERT_WARNING_Const    }, 
+       {"ASSERT_BAIL",          JX9_ASSERT_BAIL_Const       }, 
+       {"ASSERT_QUIET_EVAL",    JX9_ASSERT_QUIET_EVAL_Const }, 
+       {"ASSERT_CALLBACK",      JX9_ASSERT_CALLBACK_Const   }, 
+       {"SEEK_SET",             JX9_SEEK_SET_Const      }, 
+       {"SEEK_CUR",             JX9_SEEK_CUR_Const      }, 
+       {"SEEK_END",             JX9_SEEK_END_Const      }, 
+       {"LOCK_EX",              JX9_LOCK_EX_Const      }, 
+       {"LOCK_SH",              JX9_LOCK_SH_Const      }, 
+       {"LOCK_NB",              JX9_LOCK_NB_Const      }, 
+       {"LOCK_UN",              JX9_LOCK_UN_Const      }, 
+       {"FILE_USE_INC_PATH",    JX9_FILE_USE_INCLUDE_PATH_Const}, 
+       {"FILE_IGN_NL",          JX9_FILE_IGNORE_NEW_LINES_Const}, 
+       {"FILE_SKIP_EL",         JX9_FILE_SKIP_EMPTY_LINES_Const}, 
+       {"FILE_APPEND",          JX9_FILE_APPEND_Const }, 
+       {"SCANDIR_SORT_ASC",     JX9_SCANDIR_SORT_ASCENDING_Const  }, 
+       {"SCANDIR_SORT_DESC",    JX9_SCANDIR_SORT_DESCENDING_Const }, 
+       {"SCANDIR_SORT_NONE",    JX9_SCANDIR_SORT_NONE_Const }, 
+       {"GLOB_MARK",            JX9_GLOB_MARK_Const    }, 
+       {"GLOB_NOSORT",          JX9_GLOB_NOSORT_Const  }, 
+       {"GLOB_NOCHECK",         JX9_GLOB_NOCHECK_Const }, 
+       {"GLOB_NOESCAPE",        JX9_GLOB_NOESCAPE_Const}, 
+       {"GLOB_BRACE",           JX9_GLOB_BRACE_Const   }, 
+       {"GLOB_ONLYDIR",         JX9_GLOB_ONLYDIR_Const }, 
+       {"GLOB_ERR",             JX9_GLOB_ERR_Const     }, 
+       {"STDIN",                JX9_STDIN_Const        }, 
+       {"stdin",                JX9_STDIN_Const        }, 
+       {"STDOUT",               JX9_STDOUT_Const       }, 
+       {"stdout",               JX9_STDOUT_Const       }, 
+       {"STDERR",               JX9_STDERR_Const       }, 
+       {"stderr",               JX9_STDERR_Const       }, 
+       {"INI_SCANNER_NORMAL",   JX9_INI_SCANNER_NORMAL_Const }, 
+       {"INI_SCANNER_RAW",      JX9_INI_SCANNER_RAW_Const    }, 
+       {"EXTR_OVERWRITE",       JX9_EXTR_OVERWRITE_Const     }, 
+       {"EXTR_SKIP",            JX9_EXTR_SKIP_Const        }, 
+       {"EXTR_PREFIX_SAME",     JX9_EXTR_PREFIX_SAME_Const }, 
+       {"EXTR_PREFIX_ALL",      JX9_EXTR_PREFIX_ALL_Const  }, 
+       {"EXTR_PREFIX_INVALID",  JX9_EXTR_PREFIX_INVALID_Const }, 
+       {"EXTR_IF_EXISTS",       JX9_EXTR_IF_EXISTS_Const   }, 
+       {"EXTR_PREFIX_IF_EXISTS", JX9_EXTR_PREFIX_IF_EXISTS_Const}
+};
+/*
+ * Register the built-in constants defined above.
+ */
+JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm)
+{
+       sxu32 n;
+       /* 
+        * Note that all built-in constants have access to the jx9 virtual machine
+        * that trigger the constant invocation as their private data.
+        */
+       for( n = 0 ; n < SX_ARRAYSIZE(aBuiltIn) ; ++n ){
+               jx9_create_constant(&(*pVm), aBuiltIn[n].zName, aBuiltIn[n].xExpand, &(*pVm));
+       }
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_hashmap.c
+ * MD5: 4e93d15cd37e6093e25d8ede3064e210
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: hashmap.c v2.6 Win7 2012-12-11 00:50 stable <chm@symisc.net> $ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+/* This file implement generic hashmaps used to represent JSON arrays and objects */
+/* Allowed node types */
+#define HASHMAP_INT_NODE   1  /* Node with an int [i.e: 64-bit integer] key */
+#define HASHMAP_BLOB_NODE  2  /* Node with a string/BLOB key */
+/*
+ * Default hash function for int [i.e; 64-bit integer] keys.
+ */
+static sxu32 IntHash(sxi64 iKey)
+{
+       return (sxu32)(iKey ^ (iKey << 8) ^ (iKey >> 8));
+}
+/*
+ * Default hash function for string/BLOB keys.
+ */
+static sxu32 BinHash(const void *pSrc, sxu32 nLen)
+{
+       register unsigned char *zIn = (unsigned char *)pSrc;
+       unsigned char *zEnd;
+       sxu32 nH = 5381;
+       zEnd = &zIn[nLen];
+       for(;;){
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+       }       
+       return nH;
+}
+/*
+ * Return the total number of entries in a given hashmap.
+ * If bRecurisve is set to TRUE then recurse on hashmap entries.
+ * If the nesting limit is reached, this function abort immediately. 
+ */
+static sxi64 HashmapCount(jx9_hashmap *pMap, int bRecursive, int iRecCount)
+{
+       sxi64 iCount = 0;
+       if( !bRecursive ){
+               iCount = pMap->nEntry;
+       }else{
+               /* Recursive hashmap walk */
+               jx9_hashmap_node *pEntry = pMap->pLast;
+               jx9_value *pElem;
+               sxu32 n = 0;
+               for(;;){
+                       if( n >= pMap->nEntry ){
+                               break;
+                       }
+                       /* Point to the element value */
+                       pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pEntry->nValIdx);
+                       if( pElem ){
+                               if( pElem->iFlags & MEMOBJ_HASHMAP ){
+                                       if( iRecCount > 31 ){
+                                               /* Nesting limit reached */
+                                               return iCount;
+                                       }
+                                       /* Recurse */
+                                       iRecCount++;
+                                       iCount += HashmapCount((jx9_hashmap *)pElem->x.pOther, TRUE, iRecCount);
+                                       iRecCount--;
+                               }
+                       }
+                       /* Point to the next entry */
+                       pEntry = pEntry->pNext;
+                       ++n;
+               }
+               /* Update count */
+               iCount += pMap->nEntry;
+       }
+       return iCount;
+}
+/*
+ * Allocate a new hashmap node with a 64-bit integer key.
+ * If something goes wrong [i.e: out of memory], this function return NULL.
+ * Otherwise a fresh [jx9_hashmap_node] instance is returned.
+ */
+static jx9_hashmap_node * HashmapNewIntNode(jx9_hashmap *pMap, sxi64 iKey, sxu32 nHash, sxu32 nValIdx)
+{
+       jx9_hashmap_node *pNode;
+       /* Allocate a new node */
+       pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
+       if( pNode == 0 ){
+               return 0;
+       }
+       /* Zero the stucture */
+       SyZero(pNode, sizeof(jx9_hashmap_node));
+       /* Fill in the structure */
+       pNode->pMap  = &(*pMap);
+       pNode->iType = HASHMAP_INT_NODE;
+       pNode->nHash = nHash;
+       pNode->xKey.iKey = iKey;
+       pNode->nValIdx  = nValIdx;
+       return pNode;
+}
+/*
+ * Allocate a new hashmap node with a BLOB key.
+ * If something goes wrong [i.e: out of memory], this function return NULL.
+ * Otherwise a fresh [jx9_hashmap_node] instance is returned.
+ */
+static jx9_hashmap_node * HashmapNewBlobNode(jx9_hashmap *pMap, const void *pKey, sxu32 nKeyLen, sxu32 nHash, sxu32 nValIdx)
+{
+       jx9_hashmap_node *pNode;
+       /* Allocate a new node */
+       pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
+       if( pNode == 0 ){
+               return 0;
+       }
+       /* Zero the stucture */
+       SyZero(pNode, sizeof(jx9_hashmap_node));
+       /* Fill in the structure */
+       pNode->pMap  = &(*pMap);
+       pNode->iType = HASHMAP_BLOB_NODE;
+       pNode->nHash = nHash;
+       SyBlobInit(&pNode->xKey.sKey, &pMap->pVm->sAllocator);
+       SyBlobAppend(&pNode->xKey.sKey, pKey, nKeyLen);
+       pNode->nValIdx = nValIdx;
+       return pNode;
+}
+/*
+ * link a hashmap node to the given bucket index (last argument to this function).
+ */
+static void HashmapNodeLink(jx9_hashmap *pMap, jx9_hashmap_node *pNode, sxu32 nBucketIdx)
+{
+       /* Link */
+       if( pMap->apBucket[nBucketIdx] != 0 ){
+               pNode->pNextCollide = pMap->apBucket[nBucketIdx];
+               pMap->apBucket[nBucketIdx]->pPrevCollide = pNode;
+       }
+       pMap->apBucket[nBucketIdx] = pNode;
+       /* Link to the map list */
+       if( pMap->pFirst == 0 ){
+               pMap->pFirst = pMap->pLast = pNode;
+               /* Point to the first inserted node */
+               pMap->pCur = pNode;
+       }else{
+               MACRO_LD_PUSH(pMap->pLast, pNode);
+       }
+       ++pMap->nEntry;
+}
+/*
+ * Unlink a node from the hashmap.
+ * If the node count reaches zero then release the whole hash-bucket.
+ */
+static void jx9HashmapUnlinkNode(jx9_hashmap_node *pNode)
+{
+       jx9_hashmap *pMap = pNode->pMap;
+       jx9_vm *pVm = pMap->pVm;
+       /* Unlink from the corresponding bucket */
+       if( pNode->pPrevCollide == 0 ){
+               pMap->apBucket[pNode->nHash & (pMap->nSize - 1)] = pNode->pNextCollide;
+       }else{
+               pNode->pPrevCollide->pNextCollide = pNode->pNextCollide;
+       }
+       if( pNode->pNextCollide ){
+               pNode->pNextCollide->pPrevCollide = pNode->pPrevCollide;
+       }
+       if( pMap->pFirst == pNode ){
+               pMap->pFirst = pNode->pPrev;
+       }
+       if( pMap->pCur == pNode ){
+               /* Advance the node cursor */
+               pMap->pCur = pMap->pCur->pPrev; /* Reverse link */
+       }
+       /* Unlink from the map list */
+       MACRO_LD_REMOVE(pMap->pLast, pNode);
+       /* Restore to the free list */
+       jx9VmUnsetMemObj(pVm, pNode->nValIdx);  
+       if( pNode->iType == HASHMAP_BLOB_NODE ){
+               SyBlobRelease(&pNode->xKey.sKey);
+       }
+       SyMemBackendPoolFree(&pVm->sAllocator, pNode);
+       pMap->nEntry--;
+       if( pMap->nEntry < 1 ){
+               /* Free the hash-bucket */
+               SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
+               pMap->apBucket = 0;
+               pMap->nSize = 0;
+               pMap->pFirst = pMap->pLast = pMap->pCur = 0;
+       }
+}
+#define HASHMAP_FILL_FACTOR 3
+/*
+ * Grow the hash-table and rehash all entries.
+ */
+static sxi32 HashmapGrowBucket(jx9_hashmap *pMap)
+{
+       if( pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR ){
+               jx9_hashmap_node **apOld = pMap->apBucket;
+               jx9_hashmap_node *pEntry, **apNew;
+               sxu32 nNew = pMap->nSize << 1;
+               sxu32 nBucket;
+               sxu32 n;
+               if( nNew < 1 ){
+                       nNew = 16;
+               }
+               /* Allocate a new bucket */
+               apNew = (jx9_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator, nNew * sizeof(jx9_hashmap_node *));
+               if( apNew == 0 ){
+                       if( pMap->nSize < 1 ){
+                               return SXERR_MEM; /* Fatal */
+                       }
+                       /* Not so fatal here, simply a performance hit */
+                       return SXRET_OK;
+               }
+               /* Zero the table */
+               SyZero((void *)apNew, nNew * sizeof(jx9_hashmap_node *));
+               /* Reflect the change */
+               pMap->apBucket = apNew;
+               pMap->nSize = nNew;
+               if( apOld == 0 ){
+                       /* First allocated table [i.e: no entry], return immediately */
+                       return SXRET_OK;
+               }
+               /* Rehash old entries */
+               pEntry = pMap->pFirst;
+               n = 0;
+               for( ;; ){
+                       if( n >= pMap->nEntry ){
+                               break;
+                       }
+                       /* Clear the old collision link */
+                       pEntry->pNextCollide = pEntry->pPrevCollide = 0;
+                       /* Link to the new bucket */
+                       nBucket = pEntry->nHash & (nNew - 1);
+                       if( pMap->apBucket[nBucket] != 0 ){
+                               pEntry->pNextCollide = pMap->apBucket[nBucket];
+                               pMap->apBucket[nBucket]->pPrevCollide = pEntry;
+                       }
+                       pMap->apBucket[nBucket] = pEntry;
+                       /* Point to the next entry */
+                       pEntry = pEntry->pPrev; /* Reverse link */
+                       n++;
+               }
+               /* Free the old table */
+               SyMemBackendFree(&pMap->pVm->sAllocator, (void *)apOld);
+       }
+       return SXRET_OK;
+}
+/*
+ * Insert a 64-bit integer key and it's associated value (if any) in the given
+ * hashmap.
+ */
+static sxi32 HashmapInsertIntKey(jx9_hashmap *pMap,sxi64 iKey,jx9_value *pValue)
+{
+       jx9_hashmap_node *pNode;
+       jx9_value *pObj;
+       sxu32 nIdx;
+       sxu32 nHash;
+       sxi32 rc;
+       /* Reserve a jx9_value for the value */
+       pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
+       if( pObj == 0 ){
+               return SXERR_MEM;
+       }
+       if( pValue ){
+               /* Duplicate the value */
+               jx9MemObjStore(pValue, pObj);
+       }       
+       /* Hash the key */
+       nHash = pMap->xIntHash(iKey);
+       /* Allocate a new int node */
+       pNode = HashmapNewIntNode(&(*pMap), iKey, nHash, nIdx);
+       if( pNode == 0 ){
+               return SXERR_MEM;
+       }
+       /* Make sure the bucket is big enough to hold the new entry */
+       rc = HashmapGrowBucket(&(*pMap));
+       if( rc != SXRET_OK ){
+               SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
+               return rc;
+       }
+       /* Perform the insertion */
+       HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
+       /* All done */
+       return SXRET_OK;
+}
+/*
+ * Insert a BLOB key and it's associated value (if any) in the given
+ * hashmap.
+ */
+static sxi32 HashmapInsertBlobKey(jx9_hashmap *pMap,const void *pKey,sxu32 nKeyLen,jx9_value *pValue)
+{
+       jx9_hashmap_node *pNode;
+       jx9_value *pObj;
+       sxu32 nHash;
+       sxu32 nIdx;
+       sxi32 rc;
+       /* Reserve a jx9_value for the value */
+       pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
+       if( pObj == 0 ){
+               return SXERR_MEM;
+       }
+       if( pValue ){
+               /* Duplicate the value */
+               jx9MemObjStore(pValue, pObj);
+       }
+       /* Hash the key */
+       nHash = pMap->xBlobHash(pKey, nKeyLen);
+       /* Allocate a new blob node */
+       pNode = HashmapNewBlobNode(&(*pMap), pKey, nKeyLen, nHash, nIdx);
+       if( pNode == 0 ){
+               return SXERR_MEM;
+       }
+       /* Make sure the bucket is big enough to hold the new entry */
+       rc = HashmapGrowBucket(&(*pMap));
+       if( rc != SXRET_OK ){
+               SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
+               return rc;
+       }
+       /* Perform the insertion */
+       HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
+       /* All done */
+       return SXRET_OK;
+}
+/*
+ * Check if a given 64-bit integer key exists in the given hashmap.
+ * Write a pointer to the target node on success. Otherwise
+ * SXERR_NOTFOUND is returned on failure.
+ */
+static sxi32 HashmapLookupIntKey(
+       jx9_hashmap *pMap,         /* Target hashmap */
+       sxi64 iKey,                /* lookup key */
+       jx9_hashmap_node **ppNode  /* OUT: target node on success */
+       )
+{
+       jx9_hashmap_node *pNode;
+       sxu32 nHash;
+       if( pMap->nEntry < 1 ){
+               /* Don't bother hashing, there is no entry anyway */
+               return SXERR_NOTFOUND;
+       }
+       /* Hash the key first */
+       nHash = pMap->xIntHash(iKey);
+       /* Point to the appropriate bucket */
+       pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
+       /* Perform the lookup */
+       for(;;){
+               if( pNode == 0 ){
+                       break;
+               }
+               if( pNode->iType == HASHMAP_INT_NODE
+                       && pNode->nHash == nHash
+                       && pNode->xKey.iKey == iKey ){
+                               /* Node found */
+                               if( ppNode ){
+                                       *ppNode = pNode;
+                               }
+                               return SXRET_OK;
+               }
+               /* Follow the collision link */
+               pNode = pNode->pNextCollide;
+       }
+       /* No such entry */
+       return SXERR_NOTFOUND;
+}
+/*
+ * Check if a given BLOB key exists in the given hashmap.
+ * Write a pointer to the target node on success. Otherwise
+ * SXERR_NOTFOUND is returned on failure.
+ */
+static sxi32 HashmapLookupBlobKey(
+       jx9_hashmap *pMap,          /* Target hashmap */
+       const void *pKey,           /* Lookup key */
+       sxu32 nKeyLen,              /* Key length in bytes */
+       jx9_hashmap_node **ppNode   /* OUT: target node on success */
+       )
+{
+       jx9_hashmap_node *pNode;
+       sxu32 nHash;
+       if( pMap->nEntry < 1 ){
+               /* Don't bother hashing, there is no entry anyway */
+               return SXERR_NOTFOUND;
+       }
+       /* Hash the key first */
+       nHash = pMap->xBlobHash(pKey, nKeyLen);
+       /* Point to the appropriate bucket */
+       pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
+       /* Perform the lookup */
+       for(;;){
+               if( pNode == 0 ){
+                       break;
+               }
+               if( pNode->iType == HASHMAP_BLOB_NODE 
+                       && pNode->nHash == nHash
+                       && SyBlobLength(&pNode->xKey.sKey) == nKeyLen 
+                       && SyMemcmp(SyBlobData(&pNode->xKey.sKey), pKey, nKeyLen) == 0 ){
+                               /* Node found */
+                               if( ppNode ){
+                                       *ppNode = pNode;
+                               }
+                               return SXRET_OK;
+               }
+               /* Follow the collision link */
+               pNode = pNode->pNextCollide;
+       }
+       /* No such entry */
+       return SXERR_NOTFOUND;
+}
+/*
+ * Check if the given BLOB key looks like a decimal number. 
+ * Retrurn TRUE on success.FALSE otherwise.
+ */
+static int HashmapIsIntKey(SyBlob *pKey)
+{
+       const char *zIn  = (const char *)SyBlobData(pKey);
+       const char *zEnd = &zIn[SyBlobLength(pKey)];
+       if( (int)(zEnd-zIn) > 1 && zIn[0] == '0' ){
+               /* Octal not decimal number */
+               return FALSE;
+       }
+       if( (zIn[0] == '-' || zIn[0] == '+') && &zIn[1] < zEnd ){
+               zIn++;
+       }
+       for(;;){
+               if( zIn >= zEnd ){
+                       return TRUE;
+               }
+               if( (unsigned char)zIn[0] >= 0xc0 /* UTF-8 stream */  || !SyisDigit(zIn[0]) ){
+                       break;
+               }
+               zIn++;
+       }
+       /* Key does not look like a decimal number */
+       return FALSE;
+}
+/*
+ * Check if a given key exists in the given hashmap.
+ * Write a pointer to the target node on success.
+ * Otherwise SXERR_NOTFOUND is returned on failure.
+ */
+static sxi32 HashmapLookup(
+       jx9_hashmap *pMap,          /* Target hashmap */
+       jx9_value *pKey,            /* Lookup key */
+       jx9_hashmap_node **ppNode   /* OUT: target node on success */
+       )
+{
+       jx9_hashmap_node *pNode = 0; /* cc -O6 warning */
+       sxi32 rc;
+       if( pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
+               if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
+                       /* Force a string cast */
+                       jx9MemObjToString(&(*pKey));
+               }
+               if( SyBlobLength(&pKey->sBlob) > 0 ){
+                       /* Perform a blob lookup */
+                       rc = HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &pNode);
+                       goto result;
+               }
+       }
+       /* Perform an int lookup */
+       if((pKey->iFlags & MEMOBJ_INT) == 0 ){
+               /* Force an integer cast */
+               jx9MemObjToInteger(pKey);
+       }
+       /* Perform an int lookup */
+       rc = HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode);
+result:
+       if( rc == SXRET_OK ){
+               /* Node found */
+               if( ppNode ){
+                       *ppNode = pNode;
+               }
+               return SXRET_OK;
+       }
+       /* No such entry */
+       return SXERR_NOTFOUND;
+}
+/*
+ * Insert a given key and it's associated value (if any) in the given
+ * hashmap.
+ * If a node with the given key already exists in the database
+ * then this function overwrite the old value.
+ */
+static sxi32 HashmapInsert(
+       jx9_hashmap *pMap, /* Target hashmap */
+       jx9_value *pKey,   /* Lookup key  */
+       jx9_value *pVal    /* Node value */
+       )
+{
+       jx9_hashmap_node *pNode = 0;
+       sxi32 rc = SXRET_OK;
+       if( pMap->nEntry < 1 && pKey && (pKey->iFlags & MEMOBJ_STRING) ){
+               pMap->iFlags |= HASHMAP_JSON_OBJECT;
+       }
+       if( pKey && (pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES)) ){
+               if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
+                       /* Force a string cast */
+                       jx9MemObjToString(&(*pKey));
+               }
+               if( SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob) ){
+                       if(SyBlobLength(&pKey->sBlob) < 1){
+                               /* Automatic index assign */
+                               pKey = 0;
+                       }
+                       goto IntKey;
+               }
+               if( SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), 
+                       SyBlobLength(&pKey->sBlob), &pNode) ){
+                               /* Overwrite the old value */
+                               jx9_value *pElem;
+                               pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
+                               if( pElem ){
+                                       if( pVal ){
+                                               jx9MemObjStore(pVal, pElem);
+                                       }else{
+                                               /* Nullify the entry */
+                                               jx9MemObjToNull(pElem);
+                                       }
+                               }
+                               return SXRET_OK;
+               }
+               /* Perform a blob-key insertion */
+               rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&(*pVal));
+               return rc;
+       }
+IntKey:
+       if( pKey ){
+               if((pKey->iFlags & MEMOBJ_INT) == 0 ){
+                       /* Force an integer cast */
+                       jx9MemObjToInteger(pKey);
+               }
+               if( SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode) ){
+                       /* Overwrite the old value */
+                       jx9_value *pElem;
+                       pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
+                       if( pElem ){
+                               if( pVal ){
+                                       jx9MemObjStore(pVal, pElem);
+                               }else{
+                                       /* Nullify the entry */
+                                       jx9MemObjToNull(pElem);
+                               }
+                       }
+                       return SXRET_OK;
+               }
+               /* Perform a 64-bit-int-key insertion */
+               rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal));
+               if( rc == SXRET_OK ){
+                       if( pKey->x.iVal >= pMap->iNextIdx ){
+                               /* Increment the automatic index */ 
+                               pMap->iNextIdx = pKey->x.iVal + 1;
+                               /* Make sure the automatic index is not reserved */
+                               while( SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0) ){
+                                       pMap->iNextIdx++;
+                               }
+                       }
+               }
+       }else{
+               /* Assign an automatic index */
+               rc = HashmapInsertIntKey(&(*pMap),pMap->iNextIdx,&(*pVal));
+               if( rc == SXRET_OK ){
+                       ++pMap->iNextIdx;
+               }
+       }
+       /* Insertion result */
+       return rc;
+}
+/*
+ * Extract node value.
+ */
+static jx9_value * HashmapExtractNodeValue(jx9_hashmap_node *pNode)
+{
+       /* Point to the desired object */
+       jx9_value *pObj;
+       pObj = (jx9_value *)SySetAt(&pNode->pMap->pVm->aMemObj, pNode->nValIdx);
+       return pObj;
+}
+/*
+ * Insert a node in the given hashmap.
+ * If a node with the given key already exists in the database
+ * then this function overwrite the old value.
+ */
+static sxi32 HashmapInsertNode(jx9_hashmap *pMap, jx9_hashmap_node *pNode, int bPreserve)
+{
+       jx9_value *pObj;
+       sxi32 rc;
+       /* Extract the node value */
+       pObj = HashmapExtractNodeValue(&(*pNode));
+       if( pObj == 0 ){
+               return SXERR_EMPTY;
+       }
+       /* Preserve key */
+       if( pNode->iType == HASHMAP_INT_NODE){
+               /* Int64 key */
+               if( !bPreserve ){
+                       /* Assign an automatic index */
+                       rc = HashmapInsert(&(*pMap), 0, pObj);
+               }else{
+                       rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj);
+               }
+       }else{
+               /* Blob key */
+               rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey), 
+                       SyBlobLength(&pNode->xKey.sKey), pObj);
+       }
+       return rc;
+}
+/*
+ * Compare two node values.
+ * Return 0 if the node values are equals, > 0 if pLeft is greater than pRight
+ * or < 0 if pRight is greater than pLeft.
+ * For a full description on jx9_values comparison, refer to the implementation
+ * of the [jx9MemObjCmp()] function defined in memobj.c or the official
+ * documenation.
+ */
+static sxi32 HashmapNodeCmp(jx9_hashmap_node *pLeft, jx9_hashmap_node *pRight, int bStrict)
+{
+       jx9_value sObj1, sObj2;
+       sxi32 rc;
+       if( pLeft == pRight ){
+               /*
+                * Same node.Refer to the sort() implementation defined
+                * below for more information on this sceanario.
+                */
+               return 0;
+       }
+       /* Do the comparison */
+       jx9MemObjInit(pLeft->pMap->pVm, &sObj1);
+       jx9MemObjInit(pLeft->pMap->pVm, &sObj2);
+       jx9HashmapExtractNodeValue(pLeft, &sObj1, FALSE);
+       jx9HashmapExtractNodeValue(pRight, &sObj2, FALSE);
+       rc = jx9MemObjCmp(&sObj1, &sObj2, bStrict, 0);
+       jx9MemObjRelease(&sObj1);
+       jx9MemObjRelease(&sObj2);
+       return rc;
+}
+/*
+ * Rehash a node with a 64-bit integer key.
+ * Refer to [merge_sort(), array_shift()] implementations for more information.
+ */
+static void HashmapRehashIntNode(jx9_hashmap_node *pEntry)
+{
+       jx9_hashmap *pMap = pEntry->pMap;
+       sxu32 nBucket;
+       /* Remove old collision links */
+       if( pEntry->pPrevCollide ){
+               pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
+       }else{
+               pMap->apBucket[pEntry->nHash & (pMap->nSize - 1)] = pEntry->pNextCollide;
+       }
+       if( pEntry->pNextCollide ){
+               pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
+       }
+       pEntry->pNextCollide = pEntry->pPrevCollide = 0;
+       /* Compute the new hash */
+       pEntry->nHash = pMap->xIntHash(pMap->iNextIdx);
+       pEntry->xKey.iKey = pMap->iNextIdx;
+       nBucket = pEntry->nHash & (pMap->nSize - 1);
+       /* Link to the new bucket */
+       pEntry->pNextCollide = pMap->apBucket[nBucket];
+       if( pMap->apBucket[nBucket] ){
+               pMap->apBucket[nBucket]->pPrevCollide = pEntry;
+       }
+       pEntry->pNextCollide = pMap->apBucket[nBucket];
+       pMap->apBucket[nBucket] = pEntry;
+       /* Increment the automatic index */
+       pMap->iNextIdx++;
+}
+/*
+ * Perform a linear search on a given hashmap.
+ * Write a pointer to the target node on success.
+ * Otherwise SXERR_NOTFOUND is returned on failure.
+ * Refer to [array_intersect(), array_diff(), in_array(), ...] implementations 
+ * for more information.
+ */
+static int HashmapFindValue(
+       jx9_hashmap *pMap,   /* Target hashmap */
+       jx9_value *pNeedle,  /* Lookup key */
+       jx9_hashmap_node **ppNode, /* OUT: target node on success  */
+       int bStrict      /* TRUE for strict comparison */
+       )
+{
+       jx9_hashmap_node *pEntry;
+       jx9_value sVal, *pVal;
+       jx9_value sNeedle;
+       sxi32 rc;
+       sxu32 n;
+       /* Perform a linear search since we cannot sort the hashmap based on values */
+       pEntry = pMap->pFirst;
+       n = pMap->nEntry;
+       jx9MemObjInit(pMap->pVm, &sVal);
+       jx9MemObjInit(pMap->pVm, &sNeedle);
+       for(;;){
+               if( n < 1 ){
+                       break;
+               }
+               /* Extract node value */
+               pVal = HashmapExtractNodeValue(pEntry);
+               if( pVal ){
+                       if( (pVal->iFlags|pNeedle->iFlags) & MEMOBJ_NULL ){
+                               sxi32 iF1 = pVal->iFlags;
+                               sxi32 iF2 = pNeedle->iFlags;
+                               if( iF1 == iF2 ){
+                                       /* NULL values are equals */
+                                       if( ppNode ){
+                                               *ppNode = pEntry;
+                                       }
+                                       return SXRET_OK;
+                               }
+                       }else{
+                               /* Duplicate value */
+                               jx9MemObjLoad(pVal, &sVal);
+                               jx9MemObjLoad(pNeedle, &sNeedle);
+                               rc = jx9MemObjCmp(&sNeedle, &sVal, bStrict, 0);
+                               jx9MemObjRelease(&sVal);
+                               jx9MemObjRelease(&sNeedle);
+                               if( rc == 0 ){
+                                       if( ppNode ){
+                                               *ppNode = pEntry;
+                                       }
+                                       /* Match found*/
+                                       return SXRET_OK;
+                               }
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+               n--;
+       }
+       /* No such entry */
+       return SXERR_NOTFOUND;
+}
+/*
+ * Compare two hashmaps.
+ * Return 0 if the hashmaps are equals.Any other value indicates inequality.
+ * Note on array comparison operators.
+ *  According to the JX9 language reference manual.
+ *  Array Operators Example    Name    Result
+ *  $a + $b    Union   Union of $a and $b.
+ *  $a == $b   Equality        TRUE if $a and $b have the same key/value pairs.
+ *  $a === $b  Identity        TRUE if $a and $b have the same key/value pairs in the same 
+ *                          order and of the same types.
+ *  $a != $b   Inequality      TRUE if $a is not equal to $b.
+ *  $a <> $b   Inequality      TRUE if $a is not equal to $b.
+ *  $a !== $b  Non-identity    TRUE if $a is not identical to $b.
+ * The + operator returns the right-hand array appended to the left-hand array;
+ * For keys that exist in both arrays, the elements from the left-hand array will be used
+ * and the matching elements from the right-hand array will be ignored.
+ * <?jx9
+ * $a = array("a" => "apple", "b" => "banana");
+ * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
+ * $c = $a + $b; // Union of $a and $b
+ * print "Union of \$a and \$b: \n";
+ * dump($c);
+ * $c = $b + $a; // Union of $b and $a
+ * print "Union of \$b and \$a: \n";
+ * dump($c);
+ * ?>
+ * When executed, this script will print the following:
+ * Union of $a and $b:
+ * array(3) {
+ *  ["a"]=>
+ *  string(5) "apple"
+ *  ["b"]=>
+ * string(6) "banana"
+ *  ["c"]=>
+ * string(6) "cherry"
+ * }
+ * Union of $b and $a:
+ * array(3) {
+ * ["a"]=>
+ * string(4) "pear"
+ * ["b"]=>
+ * string(10) "strawberry"
+ * ["c"]=>
+ * string(6) "cherry"
+ * }
+ * Elements of arrays are equal for the comparison if they have the same key and value.
+ */
+JX9_PRIVATE sxi32 jx9HashmapCmp(
+       jx9_hashmap *pLeft,  /* Left hashmap */
+       jx9_hashmap *pRight, /* Right hashmap */
+       int bStrict          /* TRUE for strict comparison */
+       )
+{
+       jx9_hashmap_node *pLe, *pRe;
+       sxi32 rc;
+       sxu32 n;
+       if( pLeft == pRight ){
+               /* Same hashmap instance. This can easily happen since hashmaps are passed by reference.
+                * Unlike the  engine.
+                */
+               return 0;
+       }
+       if( pLeft->nEntry != pRight->nEntry ){
+               /* Must have the same number of entries */
+               return pLeft->nEntry > pRight->nEntry ? 1 : -1;
+       }
+       /* Point to the first inserted entry of the left hashmap */
+       pLe = pLeft->pFirst;
+       pRe = 0; /* cc warning */
+       /* Perform the comparison */
+       n = pLeft->nEntry;
+       for(;;){
+               if( n < 1 ){
+                       break;
+               }
+               if( pLe->iType == HASHMAP_INT_NODE){
+                       /* Int key */
+                       rc = HashmapLookupIntKey(&(*pRight), pLe->xKey.iKey, &pRe);
+               }else{
+                       SyBlob *pKey = &pLe->xKey.sKey;
+                       /* Blob key */
+                       rc = HashmapLookupBlobKey(&(*pRight), SyBlobData(pKey), SyBlobLength(pKey), &pRe);
+               }
+               if( rc != SXRET_OK ){
+                       /* No such entry in the right side */
+                       return 1;
+               }
+               rc = 0;
+               if( bStrict ){
+                       /* Make sure, the keys are of the same type */
+                       if( pLe->iType != pRe->iType ){
+                               rc = 1;
+                       }
+               }
+               if( !rc ){
+                       /* Compare nodes */
+                       rc = HashmapNodeCmp(pLe, pRe, bStrict);
+               }
+               if( rc != 0 ){
+                       /* Nodes key/value differ */
+                       return rc;
+               }
+               /* Point to the next entry */
+               pLe = pLe->pPrev; /* Reverse link */
+               n--;
+       }
+       return 0; /* Hashmaps are equals */
+}
+/*
+ * Merge two hashmaps.
+ * Note on the merge process
+ * According to the JX9 language reference manual.
+ *  Merges the elements of two arrays together so that the values of one are appended
+ *  to the end of the previous one. It returns the resulting array (pDest).
+ *  If the input arrays have the same string keys, then the later value for that key
+ *  will overwrite the previous one. If, however, the arrays contain numeric keys
+ *  the later value will not overwrite the original value, but will be appended.
+ *  Values in the input array with numeric keys will be renumbered with incrementing
+ *  keys starting from zero in the result array. 
+ */
+static sxi32 HashmapMerge(jx9_hashmap *pSrc, jx9_hashmap *pDest)
+{
+       jx9_hashmap_node *pEntry;
+       jx9_value sKey, *pVal;
+       sxi32 rc;
+       sxu32 n;
+       if( pSrc == pDest ){
+               /* Same map. This can easily happen since hashmaps are passed by reference.
+                * Unlike the  engine.
+                */
+               return SXRET_OK;
+       }
+       /* Point to the first inserted entry in the source */
+       pEntry = pSrc->pFirst;
+       /* Perform the merge */
+       for( n = 0 ; n < pSrc->nEntry ; ++n ){
+               /* Extract the node value */
+               pVal = HashmapExtractNodeValue(pEntry);
+               if( pEntry->iType == HASHMAP_BLOB_NODE ){
+                       /* Blob key insertion */
+                       jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
+                       jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
+                       rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
+                       jx9MemObjRelease(&sKey);
+               }else{
+                       rc = HashmapInsert(&(*pDest), 0/* Automatic index assign */, pVal);
+               }
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       return SXRET_OK;
+}
+/*
+ * Duplicate the contents of a hashmap. Store the copy in pDest.
+ * Refer to the [array_pad(), array_copy(), ...] implementation for more information.
+ */
+JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest)
+{
+       jx9_hashmap_node *pEntry;
+       jx9_value sKey, *pVal;
+       sxi32 rc;
+       sxu32 n;
+       if( pSrc == pDest ){
+               /* Same map. This can easily happen since hashmaps are passed by reference.
+                * Unlike the  engine.
+                */
+               return SXRET_OK;
+       }
+       /* Point to the first inserted entry in the source */
+       pEntry = pSrc->pFirst;
+       /* Perform the duplication */
+       for( n = 0 ; n < pSrc->nEntry ; ++n ){
+               /* Extract the node value */
+               pVal = HashmapExtractNodeValue(pEntry);
+               if( pEntry->iType == HASHMAP_BLOB_NODE ){
+                       /* Blob key insertion */
+                       jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
+                       jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
+                       rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
+                       jx9MemObjRelease(&sKey);
+               }else{
+                       /* Int key insertion */
+                       rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal);
+               }
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       return SXRET_OK;
+}
+/*
+ * Perform the union of two hashmaps.
+ * This operation is performed only if the user uses the '+' operator
+ * with a variable holding an array as follows:
+ * <?jx9
+ * $a = array("a" => "apple", "b" => "banana");
+ * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
+ * $c = $a + $b; // Union of $a and $b
+ * print "Union of \$a and \$b: \n";
+ * dump($c);
+ * $c = $b + $a; // Union of $b and $a
+ * print "Union of \$b and \$a: \n";
+ * dump($c);
+ * ?>
+ * When executed, this script will print the following:
+ * Union of $a and $b:
+ * array(3) {
+ *  ["a"]=>
+ *  string(5) "apple"
+ *  ["b"]=>
+ * string(6) "banana"
+ *  ["c"]=>
+ * string(6) "cherry"
+ * }
+ * Union of $b and $a:
+ * array(3) {
+ * ["a"]=>
+ * string(4) "pear"
+ * ["b"]=>
+ * string(10) "strawberry"
+ * ["c"]=>
+ * string(6) "cherry"
+ * }
+ * The + operator returns the right-hand array appended to the left-hand array;
+ * For keys that exist in both arrays, the elements from the left-hand array will be used
+ * and the matching elements from the right-hand array will be ignored.
+ */
+JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight)
+{
+       jx9_hashmap_node *pEntry;
+       sxi32 rc = SXRET_OK;
+       jx9_value *pObj;
+       sxu32 n;
+       if( pLeft == pRight ){
+               /* Same map. This can easily happen since hashmaps are passed by reference.
+                * Unlike the  engine.
+                */
+               return SXRET_OK;
+       }
+       /* Perform the union */
+       pEntry = pRight->pFirst;
+       for(n = 0 ; n < pRight->nEntry ; ++n ){
+               /* Make sure the given key does not exists in the left array */
+               if( pEntry->iType == HASHMAP_BLOB_NODE ){
+                       /* BLOB key */
+                       if( SXRET_OK != 
+                               HashmapLookupBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), 0) ){
+                                       pObj = HashmapExtractNodeValue(pEntry);
+                                       if( pObj ){
+                                               /* Perform the insertion */
+                                               rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey),
+                                                       SyBlobLength(&pEntry->xKey.sKey),pObj);
+                                               if( rc != SXRET_OK ){
+                                                       return rc;
+                                               }
+                                       }
+                       }
+               }else{
+                       /* INT key */
+                       if( SXRET_OK != HashmapLookupIntKey(&(*pLeft), pEntry->xKey.iKey, 0) ){
+                               pObj = HashmapExtractNodeValue(pEntry);
+                               if( pObj ){
+                                       /* Perform the insertion */
+                                       rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj);
+                                       if( rc != SXRET_OK ){
+                                               return rc;
+                                       }
+                               }
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       return SXRET_OK;
+}
+/*
+ * Allocate a new hashmap.
+ * Return a pointer to the freshly allocated hashmap on success.NULL otherwise.
+ */
+JX9_PRIVATE jx9_hashmap * jx9NewHashmap(
+       jx9_vm *pVm,              /* VM that trigger the hashmap creation */
+       sxu32 (*xIntHash)(sxi64), /* Hash function for int keys.NULL otherwise*/
+       sxu32 (*xBlobHash)(const void *, sxu32) /* Hash function for BLOB keys.NULL otherwise */
+       )
+{
+       jx9_hashmap *pMap;
+       /* Allocate a new instance */
+       pMap = (jx9_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_hashmap));
+       if( pMap == 0 ){
+               return 0;
+       }
+       /* Zero the structure */
+       SyZero(pMap, sizeof(jx9_hashmap));
+       /* Fill in the structure */
+       pMap->pVm = &(*pVm);
+       pMap->iRef = 1;
+       /* pMap->iFlags = 0; */
+       /* Default hash functions */
+       pMap->xIntHash  = xIntHash ? xIntHash : IntHash;
+       pMap->xBlobHash = xBlobHash ? xBlobHash : BinHash;
+       return pMap;
+}
+/*
+ * Install superglobals in the given virtual machine.
+ * Note on superglobals.
+ *  According to the JX9 language reference manual.
+ *  Superglobals are built-in variables that are always available in all scopes.
+*   Description
+*   All predefined variables in JX9 are "superglobals", which means they
+*   are available in all scopes throughout a script.
+*   These variables are:
+*    $_SERVER
+*    $_GET
+*    $_POST
+*    $_FILES
+*    $_REQUEST
+*    $_ENV
+*/
+JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm)
+{
+       static const char * azSuper[] = {
+               "_SERVER",   /* $_SERVER */
+               "_GET",      /* $_GET */
+               "_POST",     /* $_POST */
+               "_FILES",    /* $_FILES */
+               "_REQUEST",  /* $_REQUEST */
+               "_COOKIE",   /* $_COOKIE */
+               "_ENV",      /* $_ENV */
+               "_HEADER",   /* $_HEADER */
+               "argv"       /* $argv */
+       };
+       SyString *pFile;
+       sxi32 rc;
+       sxu32 n;
+       /* Install globals variable now */
+       for( n =  0 ; n < SX_ARRAYSIZE(azSuper)  ; n++ ){
+               jx9_value *pSuper;
+               /* Request an empty array */
+               pSuper = jx9_new_array(&(*pVm));
+               if( pSuper == 0 ){
+                       return SXERR_MEM;
+               }
+               /* Install */
+               rc = jx9_vm_config(&(*pVm),JX9_VM_CONFIG_CREATE_VAR, azSuper[n]/* Super-global name*/, pSuper/* Super-global value */);
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+               /* Release the value now it have been installed */
+               jx9_release_value(&(*pVm), pSuper);
+       }
+       /* Set some $_SERVER entries */
+       pFile = (SyString *)SySetPeek(&pVm->aFiles);
+       /*
+        * 'SCRIPT_FILENAME'
+        * The absolute pathname of the currently executing script.
+        */
+       jx9_vm_config(pVm, JX9_VM_CONFIG_SERVER_ATTR, 
+               "SCRIPT_FILENAME", 
+               pFile ? pFile->zString : ":Memory:", 
+               pFile ? pFile->nByte : sizeof(":Memory:") - 1
+               );
+       /* All done, all global variables are installed now */
+       return SXRET_OK;
+}
+/*
+ * Release a hashmap.
+ */
+JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS)
+{
+       jx9_hashmap_node *pEntry, *pNext;
+       jx9_vm *pVm = pMap->pVm;
+       sxu32 n;
+       /* Start the release process */
+       n = 0;
+       pEntry = pMap->pFirst;
+       for(;;){
+               if( n >= pMap->nEntry ){
+                       break;
+               }
+               pNext = pEntry->pPrev; /* Reverse link */
+               /* Restore the jx9_value to the free list */
+               jx9VmUnsetMemObj(pVm, pEntry->nValIdx);
+               /* Release the node */
+               if( pEntry->iType == HASHMAP_BLOB_NODE ){
+                       SyBlobRelease(&pEntry->xKey.sKey);
+               }
+               SyMemBackendPoolFree(&pVm->sAllocator, pEntry);
+               /* Point to the next entry */
+               pEntry = pNext;
+               n++;
+       }
+       if( pMap->nEntry > 0 ){
+               /* Release the hash bucket */
+               SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
+       }
+       if( FreeDS ){
+               /* Free the whole instance */
+               SyMemBackendPoolFree(&pVm->sAllocator, pMap);
+       }else{
+               /* Keep the instance but reset it's fields */
+               pMap->apBucket = 0;
+               pMap->iNextIdx = 0;
+               pMap->nEntry = pMap->nSize = 0;
+               pMap->pFirst = pMap->pLast = pMap->pCur = 0;
+       }
+       return SXRET_OK;
+}
+/*
+ * Decrement the reference count of a given hashmap.
+ * If the count reaches zero which mean no more variables
+ * are pointing to this hashmap, then release the whole instance.
+ */
+JX9_PRIVATE void  jx9HashmapUnref(jx9_hashmap *pMap)
+{
+       pMap->iRef--;
+       if( pMap->iRef < 1 ){
+               jx9HashmapRelease(pMap, TRUE);
+       }
+}
+/*
+ * Check if a given key exists in the given hashmap.
+ * Write a pointer to the target node on success.
+ * Otherwise SXERR_NOTFOUND is returned on failure.
+ */
+JX9_PRIVATE sxi32 jx9HashmapLookup(
+       jx9_hashmap *pMap,        /* Target hashmap */
+       jx9_value *pKey,          /* Lookup key */
+       jx9_hashmap_node **ppNode /* OUT: Target node on success */
+       )
+{
+       sxi32 rc;
+       if( pMap->nEntry < 1 ){
+               /* TICKET 1433-25: Don't bother hashing, the hashmap is empty anyway.
+                */
+               return SXERR_NOTFOUND;
+       }
+       rc = HashmapLookup(&(*pMap), &(*pKey), ppNode);
+       return rc;
+}
+/*
+ * Insert a given key and it's associated value (if any) in the given
+ * hashmap.
+ * If a node with the given key already exists in the database
+ * then this function overwrite the old value.
+ */
+JX9_PRIVATE sxi32 jx9HashmapInsert(
+       jx9_hashmap *pMap, /* Target hashmap */
+       jx9_value *pKey,   /* Lookup key */
+       jx9_value *pVal    /* Node value.NULL otherwise */
+       )
+{
+       sxi32 rc;
+       rc = HashmapInsert(&(*pMap), &(*pKey), &(*pVal));
+       return rc;
+}
+/*
+ * Reset the node cursor of a given hashmap.
+ */
+JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap)
+{
+       /* Reset the loop cursor */
+       pMap->pCur = pMap->pFirst;
+}
+/*
+ * Return a pointer to the node currently pointed by the node cursor.
+ * If the cursor reaches the end of the list, then this function
+ * return NULL.
+ * Note that the node cursor is automatically advanced by this function.
+ */
+JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap)
+{
+       jx9_hashmap_node *pCur = pMap->pCur;
+       if( pCur == 0 ){
+               /* End of the list, return null */
+               return 0;
+       }
+       /* Advance the node cursor */
+       pMap->pCur = pCur->pPrev; /* Reverse link */
+       return pCur;
+}
+/*
+ * Extract a node value.
+ */
+JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode)
+{
+       jx9_value *pValue;
+       pValue = HashmapExtractNodeValue(pNode);
+       return pValue;
+}
+/*
+ * Extract a node value (Second).
+ */
+JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore)
+{
+       jx9_value *pEntry = HashmapExtractNodeValue(pNode);
+       if( pEntry ){
+               if( bStore ){
+                       jx9MemObjStore(pEntry, pValue);
+               }else{
+                       jx9MemObjLoad(pEntry, pValue);
+               }
+       }else{
+               jx9MemObjRelease(pValue);
+       }
+}
+/*
+ * Extract a node key.
+ */
+JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode,jx9_value *pKey)
+{
+       /* Fill with the current key */
+       if( pNode->iType == HASHMAP_INT_NODE ){
+               if( SyBlobLength(&pKey->sBlob) > 0 ){
+                       SyBlobRelease(&pKey->sBlob);
+               }
+               pKey->x.iVal = pNode->xKey.iKey;
+               MemObjSetType(pKey, MEMOBJ_INT);
+       }else{
+               SyBlobReset(&pKey->sBlob);
+               SyBlobAppend(&pKey->sBlob, SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey));
+               MemObjSetType(pKey, MEMOBJ_STRING);
+       }
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+/*
+ * Store the address of nodes value in the given container.
+ * Refer to the [vfprintf(), vprintf(), vsprintf()] implementations
+ * defined in 'builtin.c' for more information.
+ */
+JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut)
+{
+       jx9_hashmap_node *pEntry = pMap->pFirst;
+       jx9_value *pValue;
+       sxu32 n;
+       /* Initialize the container */
+       SySetInit(pOut, &pMap->pVm->sAllocator, sizeof(jx9_value *));
+       for(n = 0 ; n < pMap->nEntry ; n++ ){
+               /* Extract node value */
+               pValue = HashmapExtractNodeValue(pEntry);
+               if( pValue ){
+                       SySetPut(pOut, (const void *)&pValue);
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       /* Total inserted entries */
+       return (int)SySetUsed(pOut);
+}
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+/*
+ * Merge sort.
+ * The merge sort implementation is based on the one found in the SQLite3 source tree.
+ * Status: Public domain
+ */
+/* Node comparison callback signature */
+typedef sxi32 (*ProcNodeCmp)(jx9_hashmap_node *, jx9_hashmap_node *, void *);
+/*
+** Inputs:
+**   a:       A sorted, null-terminated linked list.  (May be null).
+**   b:       A sorted, null-terminated linked list.  (May be null).
+**   cmp:     A pointer to the comparison function.
+**
+** Return Value:
+**   A pointer to the head of a sorted list containing the elements
+**   of both a and b.
+**
+** Side effects:
+**   The "next", "prev" pointers for elements in the lists a and b are
+**   changed.
+*/
+static jx9_hashmap_node * HashmapNodeMerge(jx9_hashmap_node *pA, jx9_hashmap_node *pB, ProcNodeCmp xCmp, void *pCmpData)
+{
+       jx9_hashmap_node result, *pTail;
+    /* Prevent compiler warning */
+       result.pNext = result.pPrev = 0;
+       pTail = &result;
+       while( pA && pB ){
+               if( xCmp(pA, pB, pCmpData) < 0 ){
+                       pTail->pPrev = pA;
+                       pA->pNext = pTail;
+                       pTail = pA;
+                       pA = pA->pPrev;
+               }else{
+                       pTail->pPrev = pB;
+                       pB->pNext = pTail;
+                       pTail = pB;
+                       pB = pB->pPrev;
+               }
+       }
+       if( pA ){
+               pTail->pPrev = pA;
+               pA->pNext = pTail;
+       }else if( pB ){
+               pTail->pPrev = pB;
+               pB->pNext = pTail;
+       }else{
+               pTail->pPrev = pTail->pNext = 0;
+       }
+       return result.pPrev;
+}
+/*
+** Inputs:
+**   Map:       Input hashmap
+**   cmp:       A comparison function.
+**
+** Return Value:
+**   Sorted hashmap.
+**
+** Side effects:
+**   The "next" pointers for elements in list are changed.
+*/
+#define N_SORT_BUCKET  32
+static sxi32 HashmapMergeSort(jx9_hashmap *pMap, ProcNodeCmp xCmp, void *pCmpData)
+{
+       jx9_hashmap_node *a[N_SORT_BUCKET], *p, *pIn;
+       sxu32 i;
+       SyZero(a, sizeof(a));
+       /* Point to the first inserted entry */
+       pIn = pMap->pFirst;
+       while( pIn ){
+               p = pIn;
+               pIn = p->pPrev;
+               p->pPrev = 0;
+               for(i=0; i<N_SORT_BUCKET-1; i++){
+                       if( a[i]==0 ){
+                               a[i] = p;
+                               break;
+                       }else{
+                               p = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
+                               a[i] = 0;
+                       }
+               }
+               if( i==N_SORT_BUCKET-1 ){
+                       /* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
+                        * But that is impossible.
+                        */
+                       a[i] = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
+               }
+       }
+       p = a[0];
+       for(i=1; i<N_SORT_BUCKET; i++){
+               p = HashmapNodeMerge(p, a[i], xCmp, pCmpData);
+       }
+       p->pNext = 0;
+       /* Reflect the change */
+       pMap->pFirst = p;
+       /* Reset the loop cursor */
+       pMap->pCur = pMap->pFirst;
+       return SXRET_OK;
+}
+/* 
+ * Node comparison callback.
+ * used-by: [sort(), asort(), ...]
+ */
+static sxi32 HashmapCmpCallback1(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
+{
+       jx9_value sA, sB;
+       sxi32 iFlags;
+       int rc;
+       if( pCmpData == 0 ){
+               /* Perform a standard comparison */
+               rc = HashmapNodeCmp(pA, pB, FALSE);
+               return rc;
+       }
+       iFlags = SX_PTR_TO_INT(pCmpData);
+       /* Duplicate node values */
+       jx9MemObjInit(pA->pMap->pVm, &sA);
+       jx9MemObjInit(pA->pMap->pVm, &sB);
+       jx9HashmapExtractNodeValue(pA, &sA, FALSE);
+       jx9HashmapExtractNodeValue(pB, &sB, FALSE);
+       if( iFlags == 5 ){
+               /* String cast */
+               if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
+                       jx9MemObjToString(&sA);
+               }
+               if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
+                       jx9MemObjToString(&sB);
+               }
+       }else{
+               /* Numeric cast */
+               jx9MemObjToNumeric(&sA);
+               jx9MemObjToNumeric(&sB);
+       }
+       /* Perform the comparison */
+       rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
+       jx9MemObjRelease(&sA);
+       jx9MemObjRelease(&sB);
+       return rc;
+}
+/*
+ * Node comparison callback.
+ * Used by: [rsort(), arsort()];
+ */
+static sxi32 HashmapCmpCallback3(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
+{
+       jx9_value sA, sB;
+       sxi32 iFlags;
+       int rc;
+       if( pCmpData == 0 ){
+               /* Perform a standard comparison */
+               rc = HashmapNodeCmp(pA, pB, FALSE);
+               return -rc;
+       }
+       iFlags = SX_PTR_TO_INT(pCmpData);
+       /* Duplicate node values */
+       jx9MemObjInit(pA->pMap->pVm, &sA);
+       jx9MemObjInit(pA->pMap->pVm, &sB);
+       jx9HashmapExtractNodeValue(pA, &sA, FALSE);
+       jx9HashmapExtractNodeValue(pB, &sB, FALSE);
+       if( iFlags == 5 ){
+               /* String cast */
+               if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
+                       jx9MemObjToString(&sA);
+               }
+               if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
+                       jx9MemObjToString(&sB);
+               }
+       }else{
+               /* Numeric cast */
+               jx9MemObjToNumeric(&sA);
+               jx9MemObjToNumeric(&sB);
+       }
+       /* Perform the comparison */
+       rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
+       jx9MemObjRelease(&sA);
+       jx9MemObjRelease(&sB);
+       return -rc;
+}
+/*
+ * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison.
+ * used-by: [usort(), uasort()]
+ */
+static sxi32 HashmapCmpCallback4(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
+{
+       jx9_value sResult, *pCallback;
+       jx9_value *pV1, *pV2;
+       jx9_value *apArg[2];  /* Callback arguments */
+       sxi32 rc;
+       /* Point to the desired callback */
+       pCallback = (jx9_value *)pCmpData;
+       /* initialize the result value */
+       jx9MemObjInit(pA->pMap->pVm, &sResult);
+       /* Extract nodes values */
+       pV1 = HashmapExtractNodeValue(pA);
+       pV2 = HashmapExtractNodeValue(pB);
+       apArg[0] = pV1;
+       apArg[1] = pV2;
+       /* Invoke the callback */
+       rc = jx9VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult);
+       if( rc != SXRET_OK ){
+               /* An error occured while calling user defined function [i.e: not defined] */
+               rc = -1; /* Set a dummy result */
+       }else{
+               /* Extract callback result */
+               if((sResult.iFlags & MEMOBJ_INT) == 0 ){
+                       /* Perform an int cast */
+                       jx9MemObjToInteger(&sResult);
+               }
+               rc = (sxi32)sResult.x.iVal;
+       }
+       jx9MemObjRelease(&sResult);
+       /* Callback result */
+       return rc;
+}
+/*
+ * Rehash all nodes keys after a merge-sort have been applied.
+ * Used by [sort(), usort() and rsort()].
+ */
+static void HashmapSortRehash(jx9_hashmap *pMap)
+{
+       jx9_hashmap_node *p, *pLast;
+       sxu32 i;
+       /* Rehash all entries */
+       pLast = p = pMap->pFirst;
+       pMap->iNextIdx = 0; /* Reset the automatic index */
+       i = 0;
+       for( ;; ){
+               if( i >= pMap->nEntry ){
+                       pMap->pLast = pLast; /* Fix the last link broken by the merge-sort */
+                       break;
+               }
+               if( p->iType == HASHMAP_BLOB_NODE ){
+                       /* Do not maintain index association as requested by the JX9 specification */
+                       SyBlobRelease(&p->xKey.sKey);
+                       /* Change key type */
+                       p->iType = HASHMAP_INT_NODE;
+               }
+               HashmapRehashIntNode(p);
+               /* Point to the next entry */
+               i++;
+               pLast = p;
+               p = p->pPrev; /* Reverse link */
+       }
+}
+/*
+ * Array functions implementation.
+ * Authors:
+ *  Symisc Systems, devel@symisc.net.
+ *  Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *  Stable.
+ */
+/*
+ * bool sort(array &$array[, int $sort_flags = SORT_REGULAR ] )
+ * Sort an array.
+ * Parameters
+ *  $array
+ *   The input array.
+ * $sort_flags
+ *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
+ *  Sorting type flags:
+ *   SORT_REGULAR - compare items normally (don't change types)
+ *   SORT_NUMERIC - compare items numerically
+ *   SORT_STRING - compare items as strings
+ * Return
+ *  TRUE on success or FALSE on failure.
+ * 
+ */
+static int jx9_hashmap_sort(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       /* Make sure we are dealing with a valid hashmap */
+       if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
+               /* Missing/Invalid arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       if( pMap->nEntry > 1 ){
+               sxi32 iCmpFlags = 0;
+               if( nArg > 1 ){
+                       /* Extract comparison flags */
+                       iCmpFlags = jx9_value_to_int(apArg[1]);
+                       if( iCmpFlags == 3 /* SORT_REGULAR */ ){
+                               iCmpFlags = 0; /* Standard comparison */
+                       }
+               }
+               /* Do the merge sort */
+               HashmapMergeSort(pMap, HashmapCmpCallback1, SX_INT_TO_PTR(iCmpFlags));
+               /* Rehash [Do not maintain index association as requested by the JX9 specification] */
+               HashmapSortRehash(pMap);
+       }
+       /* All done, return TRUE */
+       jx9_result_bool(pCtx, 1);
+       return JX9_OK;
+}
+/*
+ * bool rsort(array &$array[, int $sort_flags = SORT_REGULAR ] )
+ * Sort an array in reverse order.
+ * Parameters
+ *  $array
+ *   The input array.
+ * $sort_flags
+ *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
+ *  Sorting type flags:
+ *   SORT_REGULAR - compare items normally (don't change types)
+ *   SORT_NUMERIC - compare items numerically
+ *   SORT_STRING - compare items as strings
+ * Return
+ *  TRUE on success or FALSE on failure.
+ */
+static int jx9_hashmap_rsort(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       /* Make sure we are dealing with a valid hashmap */
+       if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
+               /* Missing/Invalid arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       if( pMap->nEntry > 1 ){
+               sxi32 iCmpFlags = 0;
+               if( nArg > 1 ){
+                       /* Extract comparison flags */
+                       iCmpFlags = jx9_value_to_int(apArg[1]);
+                       if( iCmpFlags == 3 /* SORT_REGULAR */ ){
+                               iCmpFlags = 0; /* Standard comparison */
+                       }
+               }
+               /* Do the merge sort */
+               HashmapMergeSort(pMap, HashmapCmpCallback3, SX_INT_TO_PTR(iCmpFlags));
+               /* Rehash [Do not maintain index association as requested by the JX9 specification] */
+               HashmapSortRehash(pMap);
+       }
+       /* All done, return TRUE */
+       jx9_result_bool(pCtx, 1);
+       return JX9_OK;
+}
+/*
+ * bool usort(array &$array, callable $cmp_function)
+ *  Sort an array by values using a user-defined comparison function.
+ * Parameters
+ *  $array
+ *   The input array.
+ * $cmp_function
+ *  The comparison function must return an integer less than, equal to, or greater
+ *  than zero if the first argument is considered to be respectively less than, equal
+ *  to, or greater than the second.
+ *    int callback ( mixed $a, mixed $b )
+ * Return
+ *  TRUE on success or FALSE on failure.
+ */
+static int jx9_hashmap_usort(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       /* Make sure we are dealing with a valid hashmap */
+       if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
+               /* Missing/Invalid arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       if( pMap->nEntry > 1 ){
+               jx9_value *pCallback = 0;
+               ProcNodeCmp xCmp;
+               xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */
+               if( nArg > 1 && jx9_value_is_callable(apArg[1]) ){
+                       /* Point to the desired callback */
+                       pCallback = apArg[1];
+               }else{
+                       /* Use the default comparison function */
+                       xCmp = HashmapCmpCallback1;
+               }
+               /* Do the merge sort */
+               HashmapMergeSort(pMap, xCmp, pCallback);
+               /* Rehash [Do not maintain index association as requested by the JX9 specification] */
+               HashmapSortRehash(pMap);
+       }
+       /* All done, return TRUE */
+       jx9_result_bool(pCtx, 1);
+       return JX9_OK;
+}
+/*
+ * int count(array $var [, int $mode = COUNT_NORMAL ])
+ *   Count all elements in an array, or something in an object.
+ * Parameters
+ *  $var
+ *   The array or the object.
+ * $mode
+ *  If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count()
+ *  will recursively count the array. This is particularly useful for counting 
+ *  all the elements of a multidimensional array. count() does not detect infinite
+ *  recursion.
+ * Return
+ *  Returns the number of elements in the array.
+ */
+static int jx9_hashmap_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       int bRecursive = FALSE;
+       sxi64 iCount;
+       if( nArg < 1 ){
+               /* Missing arguments, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* TICKET 1433-19: Handle objects */
+               int res = !jx9_value_is_null(apArg[0]);
+               jx9_result_int(pCtx, res);
+               return JX9_OK;
+       }
+       if( nArg > 1 ){
+               /* Recursive count? */
+               bRecursive = jx9_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */;
+       }
+       /* Count */
+       iCount = HashmapCount((jx9_hashmap *)apArg[0]->x.pOther, bRecursive, 0);
+       jx9_result_int64(pCtx, iCount);
+       return JX9_OK;
+}
+/*
+ * bool array_key_exists(value $key, array $search)
+ *  Checks if the given key or index exists in the array.
+ * Parameters
+ * $key
+ *   Value to check.
+ * $search
+ *  An array with keys to check.
+ * Return
+ *  TRUE on success or FALSE on failure.
+ */
+static int jx9_hashmap_key_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       sxi32 rc;
+       if( nArg < 2 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[1]) ){
+               /* Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Perform the lookup */
+       rc = jx9HashmapLookup((jx9_hashmap *)apArg[1]->x.pOther, apArg[0], 0);
+       /* lookup result */
+       jx9_result_bool(pCtx, rc == SXRET_OK ? 1 : 0);
+       return JX9_OK;
+}
+/*
+ * value array_pop(array $array)
+ *   POP the last inserted element from the array.
+ * Parameter
+ *  The array to get the value from.
+ * Return
+ *  Poped value or NULL on failure.
+ */
+static int jx9_hashmap_pop(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       if( pMap->nEntry < 1 ){
+               /* Noting to pop, return NULL */
+               jx9_result_null(pCtx);
+       }else{
+               jx9_hashmap_node *pLast = pMap->pLast;
+               jx9_value *pObj;
+               pObj = HashmapExtractNodeValue(pLast);
+               if( pObj ){
+                       /* Node value */
+                       jx9_result_value(pCtx, pObj);
+                       /* Unlink the node */
+                       jx9HashmapUnlinkNode(pLast);
+               }else{
+                       jx9_result_null(pCtx);
+               }
+               /* Reset the cursor */
+               pMap->pCur = pMap->pFirst;
+       }
+       return JX9_OK;
+}
+/*
+ * int array_push($array, $var, ...)
+ *   Push one or more elements onto the end of array. (Stack insertion)
+ * Parameters
+ *  array
+ *    The input array.
+ *  var
+ *   On or more value to push.
+ * Return
+ *  New array count (including old items).
+ */
+static int jx9_hashmap_push(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       sxi32 rc;
+       int i;
+       if( nArg < 1 ){
+               /* Missing arguments, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       /* Start pushing given values */
+       for( i = 1 ; i < nArg ; ++i ){
+               rc = jx9HashmapInsert(pMap, 0, apArg[i]);
+               if( rc != SXRET_OK ){
+                       break;
+               }
+       }
+       /* Return the new count */
+       jx9_result_int64(pCtx, (sxi64)pMap->nEntry);
+       return JX9_OK;
+}
+/*
+ * value array_shift(array $array)
+ *   Shift an element off the beginning of array.
+ * Parameter
+ *  The array to get the value from.
+ * Return
+ *  Shifted value or NULL on failure.
+ */
+static int jx9_hashmap_shift(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       if( nArg < 1 ){
+               /* Missing arguments, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       if( pMap->nEntry < 1 ){
+               /* Empty hashmap, return NULL */
+               jx9_result_null(pCtx);
+       }else{
+               jx9_hashmap_node *pEntry = pMap->pFirst;
+               jx9_value *pObj;
+               sxu32 n;
+               pObj = HashmapExtractNodeValue(pEntry);
+               if( pObj ){
+                       /* Node value */
+                       jx9_result_value(pCtx, pObj);
+                       /* Unlink the first node */
+                       jx9HashmapUnlinkNode(pEntry);
+               }else{
+                       jx9_result_null(pCtx);
+               }
+               /* Rehash all int keys */
+               n = pMap->nEntry;
+               pEntry = pMap->pFirst;
+               pMap->iNextIdx = 0; /* Reset the automatic index */
+               for(;;){
+                       if( n < 1 ){
+                               break;
+                       }
+                       if( pEntry->iType == HASHMAP_INT_NODE ){
+                               HashmapRehashIntNode(pEntry);
+                       }
+                       /* Point to the next entry */
+                       pEntry = pEntry->pPrev; /* Reverse link */
+                       n--;
+               }
+               /* Reset the cursor */
+               pMap->pCur = pMap->pFirst;
+       }
+       return JX9_OK;
+}
+/*
+ * Extract the node cursor value.
+ */
+static sxi32 HashmapCurrentValue(jx9_context *pCtx, jx9_hashmap *pMap, int iDirection)
+{
+       jx9_hashmap_node *pCur = pMap->pCur;
+       jx9_value *pVal;
+       if( pCur == 0 ){
+               /* Cursor does not point to anything, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       if( iDirection != 0 ){
+               if( iDirection > 0 ){
+                       /* Point to the next entry */
+                       pMap->pCur = pCur->pPrev; /* Reverse link */
+                       pCur = pMap->pCur;
+               }else{
+                       /* Point to the previous entry */
+                       pMap->pCur = pCur->pNext; /* Reverse link */
+                       pCur = pMap->pCur;
+               }
+               if( pCur == 0 ){
+                       /* End of input reached, return FALSE */
+                       jx9_result_bool(pCtx, 0);
+                       return JX9_OK;
+               }
+       }               
+       /* Point to the desired element */
+       pVal = HashmapExtractNodeValue(pCur);
+       if( pVal ){
+               jx9_result_value(pCtx, pVal);
+       }else{
+               jx9_result_bool(pCtx, 0);
+       }
+       return JX9_OK;
+}
+/*
+ * value current(array $array)
+ *  Return the current element in an array.
+ * Parameter
+ *  $input: The input array.
+ * Return
+ *  The current() function simply returns the value of the array element that's currently
+ *  being pointed to by the internal pointer. It does not move the pointer in any way.
+ *  If the internal pointer points beyond the end of the elements list or the array 
+ *  is empty, current() returns FALSE. 
+ */
+static int jx9_hashmap_current(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 0);
+       return JX9_OK;
+}
+/*
+ * value next(array $input)
+ *  Advance the internal array pointer of an array.
+ * Parameter
+ *  $input: The input array.
+ * Return
+ *  next() behaves like current(), with one difference. It advances the internal array 
+ *  pointer one place forward before returning the element value. That means it returns 
+ *  the next array value and advances the internal array pointer by one. 
+ */
+static int jx9_hashmap_next(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 1);
+       return JX9_OK;
+}
+/* 
+ * value prev(array $input)
+ *  Rewind the internal array pointer.
+ * Parameter
+ *  $input: The input array.
+ * Return
+ *  Returns the array value in the previous place that's pointed
+ *  to by the internal array pointer, or FALSE if there are no more
+ *  elements. 
+ */
+static int jx9_hashmap_prev(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, -1);
+       return JX9_OK;
+}
+/* 
+ * value end(array $input)
+ *  Set the internal pointer of an array to its last element.
+ * Parameter
+ *  $input: The input array.
+ * Return
+ *  Returns the value of the last element or FALSE for empty array. 
+ */
+static int jx9_hashmap_end(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       /* Point to the last node */
+       pMap->pCur = pMap->pLast;
+       /* Return the last node value */
+       HashmapCurrentValue(&(*pCtx), pMap, 0);
+       return JX9_OK;
+}
+/* 
+ * value reset(array $array )
+ *  Set the internal pointer of an array to its first element.
+ * Parameter
+ *  $input: The input array.
+ * Return
+ *  Returns the value of the first array element, or FALSE if the array is empty. 
+ */
+static int jx9_hashmap_reset(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       /* Point to the first node */
+       pMap->pCur = pMap->pFirst;
+       /* Return the last node value if available */
+       HashmapCurrentValue(&(*pCtx), pMap, 0);
+       return JX9_OK;
+}
+/*
+ * value key(array $array)
+ *   Fetch a key from an array
+ * Parameter
+ *  $input
+ *   The input array.
+ * Return
+ *  The key() function simply returns the key of the array element that's currently
+ *  being pointed to by the internal pointer. It does not move the pointer in any way.
+ *  If the internal pointer points beyond the end of the elements list or the array 
+ *  is empty, key() returns NULL. 
+ */
+static int jx9_hashmap_simple_key(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap_node *pCur;
+       jx9_hashmap *pMap;
+       if( nArg < 1 ){
+               /* Missing arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       pCur = pMap->pCur;
+       if( pCur == 0 ){
+               /* Cursor does not point to anything, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       if( pCur->iType == HASHMAP_INT_NODE){
+               /* Key is integer */
+               jx9_result_int64(pCtx, pCur->xKey.iKey);
+       }else{
+               /* Key is blob */
+               jx9_result_string(pCtx, 
+                       (const char *)SyBlobData(&pCur->xKey.sKey), (int)SyBlobLength(&pCur->xKey.sKey));
+       }
+       return JX9_OK;
+}
+/*
+ * array each(array $input)
+ *  Return the current key and value pair from an array and advance the array cursor.
+ * Parameter
+ *  $input
+ *    The input array.
+ * Return
+ *  Returns the current key and value pair from the array array. This pair is returned 
+ *  in a four-element array, with the keys 0, 1, key, and value. Elements 0 and key 
+ *  contain the key name of the array element, and 1 and value contain the data.
+ *  If the internal pointer for the array points past the end of the array contents
+ *  each() returns FALSE. 
+ */
+static int jx9_hashmap_each(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap_node *pCur;
+       jx9_hashmap *pMap;
+       jx9_value *pArray;
+       jx9_value *pVal;
+       jx9_value sKey;
+       if( nArg < 1 ){
+               /* Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the internal representation that describe the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       if( pMap->pCur == 0 ){
+               /* Cursor does not point to anything, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       pCur = pMap->pCur;
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       pVal = HashmapExtractNodeValue(pCur);
+       /* Insert the current value */
+       jx9_array_add_strkey_elem(pArray, "1", pVal);
+       jx9_array_add_strkey_elem(pArray, "value", pVal);
+       /* Make the key */
+       if( pCur->iType == HASHMAP_INT_NODE ){
+               jx9MemObjInitFromInt(pMap->pVm, &sKey, pCur->xKey.iKey);
+       }else{
+               jx9MemObjInitFromString(pMap->pVm, &sKey, 0);
+               jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pCur->xKey.sKey), SyBlobLength(&pCur->xKey.sKey));
+       }
+       /* Insert the current key */
+       jx9_array_add_elem(pArray, 0, &sKey);
+       jx9_array_add_strkey_elem(pArray, "key", &sKey);
+       jx9MemObjRelease(&sKey);
+       /* Advance the cursor */
+       pMap->pCur = pCur->pPrev; /* Reverse link */
+       /* Return the current entry */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * array array_values(array $input)
+ *   Returns all the values from the input array and indexes numerically the array.
+ * Parameters
+ *   input: The input array.
+ * Return
+ *  An indexed array of values or NULL on failure.
+ */
+static int jx9_hashmap_values(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap_node *pNode;
+       jx9_hashmap *pMap;
+       jx9_value *pArray;
+       jx9_value *pObj;
+       sxu32 n;
+       if( nArg < 1 ){
+               /* Missing arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Point to the internal representation that describe the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Perform the requested operation */
+       pNode = pMap->pFirst;
+       for( n = 0 ; n < pMap->nEntry ; ++n ){
+               pObj = HashmapExtractNodeValue(pNode);
+               if( pObj ){
+                       /* perform the insertion */
+                       jx9_array_add_elem(pArray, 0/* Automatic index assign */, pObj);
+               }
+               /* Point to the next entry */
+               pNode = pNode->pPrev; /* Reverse link */
+       }
+       /* return the new array */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * bool array_same(array $arr1, array $arr2)
+ *  Return TRUE if the given arrays are the same instance.
+ *  This function is useful under JX9 since arrays and objects
+ *  are passed by reference.
+ * Parameters
+ *  $arr1
+ *   First array
+ *  $arr2
+ *   Second array
+ * Return
+ *  TRUE if the arrays are the same instance. FALSE otherwise.
+ * Note
+ *  This function is a symisc eXtension.
+ */
+static int jx9_hashmap_same(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *p1, *p2;
+       int rc;
+       if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
+               /* Missing or invalid arguments, return FALSE*/
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the hashmaps */
+       p1 = (jx9_hashmap *)apArg[0]->x.pOther;
+       p2 = (jx9_hashmap *)apArg[1]->x.pOther;
+       rc = (p1 == p2);
+       /* Same instance? */
+       jx9_result_bool(pCtx, rc);
+       return JX9_OK;
+}
+/*
+ * array array_merge(array $array1, ...)
+ *  Merge one or more arrays.
+ * Parameters
+ *  $array1
+ *    Initial array to merge.
+ *  ...
+ *   More array to merge.
+ * Return
+ *  The resulting array.
+ */
+static int jx9_hashmap_merge(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap, *pSrc;
+       jx9_value *pArray;
+       int i;
+       if( nArg < 1 ){
+               /* Missing arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the hashmap */
+       pMap = (jx9_hashmap *)pArray->x.pOther;
+       /* Start merging */
+       for( i = 0 ; i < nArg ; i++ ){
+               /* Make sure we are dealing with a valid hashmap */
+               if( !jx9_value_is_json_array(apArg[i]) ){
+                       /* Insert scalar value */
+                       jx9_array_add_elem(pArray, 0, apArg[i]);
+               }else{
+                       pSrc = (jx9_hashmap *)apArg[i]->x.pOther;
+                       /* Merge the two hashmaps */
+                       HashmapMerge(pSrc, pMap);
+               }
+       }
+       /* Return the freshly created array */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * bool in_array(value $needle, array $haystack[, bool $strict = FALSE ])
+ *  Checks if a value exists in an array.
+ * Parameters
+ *  $needle
+ *   The searched value.
+ *   Note:
+ *    If needle is a string, the comparison is done in a case-sensitive manner.
+ * $haystack
+ *  The target array.
+ * $strict
+ *  If the third parameter strict is set to TRUE then the in_array() function
+ *  will also check the types of the needle in the haystack.
+ */
+static int jx9_hashmap_in_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_value *pNeedle;
+       int bStrict;
+       int rc;
+       if( nArg < 2 ){
+               /* Missing argument, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       pNeedle = apArg[0];
+       bStrict = 0;
+       if( nArg > 2 ){
+               bStrict = jx9_value_to_bool(apArg[2]);
+       }
+       if( !jx9_value_is_json_array(apArg[1]) ){
+               /* haystack must be an array, perform a standard comparison */ 
+               rc = jx9_value_compare(pNeedle, apArg[1], bStrict);
+               /* Set the comparison result */
+               jx9_result_bool(pCtx, rc == 0);
+               return JX9_OK;
+       }
+       /* Perform the lookup */
+       rc = HashmapFindValue((jx9_hashmap *)apArg[1]->x.pOther, pNeedle, 0, bStrict);
+       /* Lookup result */
+       jx9_result_bool(pCtx, rc == SXRET_OK);
+       return JX9_OK;
+}
+/*
+ * array array_copy(array $source)
+ *  Make a blind copy of the target array.
+ * Parameters
+ *  $source
+ *   Target array
+ * Return
+ *  Copy of the target array on success. NULL otherwise.
+ * Note
+ *  This function is a symisc eXtension.
+ */
+static int jx9_hashmap_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       jx9_value *pArray;
+       if( nArg < 1 ){
+               /* Missing arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the hashmap */
+       pMap = (jx9_hashmap *)pArray->x.pOther;
+       if( jx9_value_is_json_array(apArg[0])){
+               /* Point to the internal representation of the source */
+               jx9_hashmap *pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
+               /* Perform the copy */
+               jx9HashmapDup(pSrc, pMap);
+       }else{
+               /* Simple insertion */
+               jx9HashmapInsert(pMap, 0/* Automatic index assign*/, apArg[0]); 
+       }
+       /* Return the duplicated array */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * bool array_erase(array $source)
+ *  Remove all elements from a given array.
+ * Parameters
+ *  $source
+ *   Target array
+ * Return
+ *  TRUE on success. FALSE otherwise.
+ * Note
+ *  This function is a symisc eXtension.
+ */
+static int jx9_hashmap_erase(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       if( nArg < 1 ){
+               /* Missing arguments */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Point to the target hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       /* Erase */
+       jx9HashmapRelease(pMap, FALSE);
+       return JX9_OK;
+}
+/*
+ * array array_diff(array $array1, array $array2, ...)
+ *  Computes the difference of arrays.
+ * Parameters
+ *  $array1
+ *    The array to compare from
+ *  $array2
+ *    An array to compare against 
+ *  $...
+ *   More arrays to compare against
+ * Return
+ *  Returns an array containing all the entries from array1 that
+ *  are not present in any of the other arrays.
+ */
+static int jx9_hashmap_diff(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap_node *pEntry;
+       jx9_hashmap *pSrc, *pMap;
+       jx9_value *pArray;
+       jx9_value *pVal;
+       sxi32 rc;
+       sxu32 n;
+       int i;
+       if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
+               /* Missing arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       if( nArg == 1 ){
+               /* Return the first array since we cannot perform a diff */
+               jx9_result_value(pCtx, apArg[0]);
+               return JX9_OK;
+       }
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the source hashmap */
+       pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
+       /* Perform the diff */
+       pEntry = pSrc->pFirst;
+       n = pSrc->nEntry;
+       for(;;){
+               if( n < 1 ){
+                       break;
+               }
+               /* Extract the node value */
+               pVal = HashmapExtractNodeValue(pEntry);
+               if( pVal ){
+                       for( i = 1 ; i < nArg ; i++ ){
+                               if( !jx9_value_is_json_array(apArg[i])) {
+                                       /* ignore */
+                                       continue;
+                               }
+                               /* Point to the internal representation of the hashmap */
+                               pMap = (jx9_hashmap *)apArg[i]->x.pOther;
+                               /* Perform the lookup */
+                               rc = HashmapFindValue(pMap, pVal, 0, TRUE);
+                               if( rc == SXRET_OK ){
+                                       /* Value exist */
+                                       break;
+                               }
+                       }
+                       if( i >= nArg ){
+                               /* Perform the insertion */
+                               HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+               n--;
+       }
+       /* Return the freshly created array */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * array array_intersect(array $array1 , array $array2, ...)
+ *  Computes the intersection of arrays.
+ * Parameters
+ *  $array1
+ *    The array to compare from
+ *  $array2
+ *    An array to compare against 
+ *  $...
+ *   More arrays to compare against
+ * Return
+ *  Returns an array containing all of the values in array1 whose values exist
+ *  in all of the parameters. .
+ * Note that NULL is returned on failure.
+ */
+static int jx9_hashmap_intersect(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap_node *pEntry;
+       jx9_hashmap *pSrc, *pMap;
+       jx9_value *pArray;
+       jx9_value *pVal;
+       sxi32 rc;
+       sxu32 n;
+       int i;
+       if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
+               /* Missing arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       if( nArg == 1 ){
+               /* Return the first array since we cannot perform a diff */
+               jx9_result_value(pCtx, apArg[0]);
+               return JX9_OK;
+       }
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the source hashmap */
+       pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
+       /* Perform the intersection */
+       pEntry = pSrc->pFirst;
+       n = pSrc->nEntry;
+       for(;;){
+               if( n < 1 ){
+                       break;
+               }
+               /* Extract the node value */
+               pVal = HashmapExtractNodeValue(pEntry);
+               if( pVal ){
+                       for( i = 1 ; i < nArg ; i++ ){
+                               if( !jx9_value_is_json_array(apArg[i])) {
+                                       /* ignore */
+                                       continue;
+                               }
+                               /* Point to the internal representation of the hashmap */
+                               pMap = (jx9_hashmap *)apArg[i]->x.pOther;
+                               /* Perform the lookup */
+                               rc = HashmapFindValue(pMap, pVal, 0, TRUE);
+                               if( rc != SXRET_OK ){
+                                       /* Value does not exist */
+                                       break;
+                               }
+                       }
+                       if( i >= nArg ){
+                               /* Perform the insertion */
+                               HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+               n--;
+       }
+       /* Return the freshly created array */
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * number array_sum(array $array )
+ *  Calculate the sum of values in an array.
+ * Parameters
+ *  $array: The input array.
+ * Return
+ *  Returns the sum of values as an integer or float.
+ */
+static void DoubleSum(jx9_context *pCtx, jx9_hashmap *pMap)
+{
+       jx9_hashmap_node *pEntry;
+       jx9_value *pObj;
+       double dSum = 0;
+       sxu32 n;
+       pEntry = pMap->pFirst;
+       for( n = 0 ; n < pMap->nEntry ; n++ ){
+               pObj = HashmapExtractNodeValue(pEntry);
+               if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
+                       if( pObj->iFlags & MEMOBJ_REAL ){
+                               dSum += pObj->x.rVal;
+                       }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
+                               dSum += (double)pObj->x.iVal;
+                       }else if( pObj->iFlags & MEMOBJ_STRING ){
+                               if( SyBlobLength(&pObj->sBlob) > 0 ){
+                                       double dv = 0;
+                                       SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
+                                       dSum += dv;
+                               }
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       /* Return sum */
+       jx9_result_double(pCtx, dSum);
+}
+static void Int64Sum(jx9_context *pCtx, jx9_hashmap *pMap)
+{
+       jx9_hashmap_node *pEntry;
+       jx9_value *pObj;
+       sxi64 nSum = 0;
+       sxu32 n;
+       pEntry = pMap->pFirst;
+       for( n = 0 ; n < pMap->nEntry ; n++ ){
+               pObj = HashmapExtractNodeValue(pEntry);
+               if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
+                       if( pObj->iFlags & MEMOBJ_REAL ){
+                               nSum += (sxi64)pObj->x.rVal;
+                       }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
+                               nSum += pObj->x.iVal;
+                       }else if( pObj->iFlags & MEMOBJ_STRING ){
+                               if( SyBlobLength(&pObj->sBlob) > 0 ){
+                                       sxi64 nv = 0;
+                                       SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
+                                       nSum += nv;
+                               }
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       /* Return sum */
+       jx9_result_int64(pCtx, nSum);
+}
+/* number array_sum(array $array ) 
+ * (See block-coment above)
+ */
+static int jx9_hashmap_sum(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       jx9_value *pObj;
+       if( nArg < 1 ){
+               /* Missing arguments, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       if( pMap->nEntry < 1 ){
+               /* Nothing to compute, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* If the first element is of type float, then perform floating
+        * point computaion.Otherwise switch to int64 computaion.
+        */
+       pObj = HashmapExtractNodeValue(pMap->pFirst);
+       if( pObj == 0 ){
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       if( pObj->iFlags & MEMOBJ_REAL ){
+               DoubleSum(pCtx, pMap);
+       }else{
+               Int64Sum(pCtx, pMap);
+       }
+       return JX9_OK;
+}
+/*
+ * number array_product(array $array )
+ *  Calculate the product of values in an array.
+ * Parameters
+ *  $array: The input array.
+ * Return
+ *  Returns the product of values as an integer or float.
+ */
+static void DoubleProd(jx9_context *pCtx, jx9_hashmap *pMap)
+{
+       jx9_hashmap_node *pEntry;
+       jx9_value *pObj;
+       double dProd;
+       sxu32 n;
+       pEntry = pMap->pFirst;
+       dProd = 1;
+       for( n = 0 ; n < pMap->nEntry ; n++ ){
+               pObj = HashmapExtractNodeValue(pEntry);
+               if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
+                       if( pObj->iFlags & MEMOBJ_REAL ){
+                               dProd *= pObj->x.rVal;
+                       }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
+                               dProd *= (double)pObj->x.iVal;
+                       }else if( pObj->iFlags & MEMOBJ_STRING ){
+                               if( SyBlobLength(&pObj->sBlob) > 0 ){
+                                       double dv = 0;
+                                       SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
+                                       dProd *= dv;
+                               }
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       /* Return product */
+       jx9_result_double(pCtx, dProd);
+}
+static void Int64Prod(jx9_context *pCtx, jx9_hashmap *pMap)
+{
+       jx9_hashmap_node *pEntry;
+       jx9_value *pObj;
+       sxi64 nProd;
+       sxu32 n;
+       pEntry = pMap->pFirst;
+       nProd = 1;
+       for( n = 0 ; n < pMap->nEntry ; n++ ){
+               pObj = HashmapExtractNodeValue(pEntry);
+               if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
+                       if( pObj->iFlags & MEMOBJ_REAL ){
+                               nProd *= (sxi64)pObj->x.rVal;
+                       }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
+                               nProd *= pObj->x.iVal;
+                       }else if( pObj->iFlags & MEMOBJ_STRING ){
+                               if( SyBlobLength(&pObj->sBlob) > 0 ){
+                                       sxi64 nv = 0;
+                                       SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
+                                       nProd *= nv;
+                               }
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       /* Return product */
+       jx9_result_int64(pCtx, nProd);
+}
+/* number array_product(array $array )
+ * (See block-block comment above)
+ */
+static int jx9_hashmap_product(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_hashmap *pMap;
+       jx9_value *pObj;
+       if( nArg < 1 ){
+               /* Missing arguments, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Make sure we are dealing with a valid hashmap */
+       if( !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid argument, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       if( pMap->nEntry < 1 ){
+               /* Nothing to compute, return 0 */
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       /* If the first element is of type float, then perform floating
+        * point computaion.Otherwise switch to int64 computaion.
+        */
+       pObj = HashmapExtractNodeValue(pMap->pFirst);
+       if( pObj == 0 ){
+               jx9_result_int(pCtx, 0);
+               return JX9_OK;
+       }
+       if( pObj->iFlags & MEMOBJ_REAL ){
+               DoubleProd(pCtx, pMap);
+       }else{
+               Int64Prod(pCtx, pMap);
+       }
+       return JX9_OK;
+}
+/*
+ * array array_map(callback $callback, array $arr1)
+ *  Applies the callback to the elements of the given arrays.
+ * Parameters
+ *  $callback
+ *   Callback function to run for each element in each array.
+ * $arr1
+ *   An array to run through the callback function.
+ * Return
+ *  Returns an array containing all the elements of arr1 after applying
+ *  the callback function to each one. 
+ * NOTE:
+ *  array_map() passes only a single value to the callback. 
+ */
+static int jx9_hashmap_map(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_value *pArray, *pValue, sKey, sResult;
+       jx9_hashmap_node *pEntry;
+       jx9_hashmap *pMap;
+       sxu32 n;
+       if( nArg < 2 || !jx9_value_is_json_array(apArg[1]) ){
+               /* Invalid arguments, return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Create a new array */
+       pArray = jx9_context_new_array(pCtx);
+       if( pArray == 0 ){
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Point to the internal representation of the input hashmap */
+       pMap = (jx9_hashmap *)apArg[1]->x.pOther;
+       jx9MemObjInit(pMap->pVm, &sResult);
+       jx9MemObjInit(pMap->pVm, &sKey);
+       /* Perform the requested operation */
+       pEntry = pMap->pFirst;
+       for( n = 0 ; n < pMap->nEntry ; n++ ){
+               /* Extrcat the node value */
+               pValue = HashmapExtractNodeValue(pEntry);
+               if( pValue ){
+                       sxi32 rc;
+                       /* Invoke the supplied callback */
+                       rc = jx9VmCallUserFunction(pMap->pVm, apArg[0], 1, &pValue, &sResult);
+                       /* Extract the node key */
+                       jx9HashmapExtractNodeKey(pEntry, &sKey);
+                       if( rc != SXRET_OK ){
+                               /* An error occured while invoking the supplied callback [i.e: not defined] */
+                               jx9_array_add_elem(pArray, &sKey, pValue); /* Keep the same value */
+                       }else{
+                               /* Insert the callback return value */
+                               jx9_array_add_elem(pArray, &sKey, &sResult);
+                       }
+                       jx9MemObjRelease(&sKey);
+                       jx9MemObjRelease(&sResult);
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       jx9_result_value(pCtx, pArray);
+       return JX9_OK;
+}
+/*
+ * bool array_walk(array &$array, callback $funcname [, value $userdata ] )
+ *  Apply a user function to every member of an array.
+ * Parameters
+ *  $array
+ *   The input array.
+ * $funcname
+ *  Typically, funcname takes on two parameters.The array parameter's value being
+ *  the first, and the key/index second.
+ * Note:
+ *  If funcname needs to be working with the actual values of the array, specify the first
+ *  parameter of funcname as a reference. Then, any changes made to those elements will 
+ *  be made in the original array itself.
+ * $userdata
+ *  If the optional userdata parameter is supplied, it will be passed as the third parameter
+ *  to the callback funcname.
+ * Return
+ *  Returns TRUE on success or FALSE on failure.
+ */
+static int jx9_hashmap_walk(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       jx9_value *pValue, *pUserData, sKey;
+       jx9_hashmap_node *pEntry;
+       jx9_hashmap *pMap;
+       sxi32 rc;
+       sxu32 n;
+       if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) ){
+               /* Invalid/Missing arguments, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       pUserData = nArg > 2 ? apArg[2] : 0;
+       /* Point to the internal representation of the input hashmap */
+       pMap = (jx9_hashmap *)apArg[0]->x.pOther;
+       jx9MemObjInit(pMap->pVm, &sKey);
+       /* Perform the desired operation */
+       pEntry = pMap->pFirst;
+       for( n = 0 ; n < pMap->nEntry ; n++ ){
+               /* Extract the node value */
+               pValue = HashmapExtractNodeValue(pEntry);
+               if( pValue ){
+                       /* Extract the entry key */
+                       jx9HashmapExtractNodeKey(pEntry, &sKey);
+                       /* Invoke the supplied callback */
+                       rc = jx9VmCallUserFunctionAp(pMap->pVm, apArg[1], 0, pValue, &sKey, pUserData, 0);
+                       jx9MemObjRelease(&sKey);
+                       if( rc != SXRET_OK ){
+                               /* An error occured while invoking the supplied callback [i.e: not defined] */
+                               jx9_result_bool(pCtx, 0); /* return FALSE */
+                               return JX9_OK;
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+       }
+       /* All done, return TRUE */
+       jx9_result_bool(pCtx, 1);
+       return JX9_OK;
+}
+/*
+ * Table of built-in hashmap functions.
+ */
+static const jx9_builtin_func aHashmapFunc[] = {
+       {"count",             jx9_hashmap_count }, 
+       {"sizeof",            jx9_hashmap_count }, 
+       {"array_key_exists",  jx9_hashmap_key_exists }, 
+       {"array_pop",         jx9_hashmap_pop     }, 
+       {"array_push",        jx9_hashmap_push    }, 
+       {"array_shift",       jx9_hashmap_shift   }, 
+       {"array_product",     jx9_hashmap_product }, 
+       {"array_sum",         jx9_hashmap_sum     }, 
+       {"array_values",      jx9_hashmap_values  }, 
+       {"array_same",        jx9_hashmap_same    },
+       {"array_merge",       jx9_hashmap_merge   }, 
+       {"array_diff",        jx9_hashmap_diff    }, 
+       {"array_intersect",   jx9_hashmap_intersect}, 
+       {"in_array",          jx9_hashmap_in_array },
+       {"array_copy",        jx9_hashmap_copy    }, 
+       {"array_erase",       jx9_hashmap_erase   }, 
+       {"array_map",         jx9_hashmap_map     }, 
+       {"array_walk",        jx9_hashmap_walk    }, 
+       {"sort",              jx9_hashmap_sort    }, 
+       {"rsort",             jx9_hashmap_rsort   }, 
+       {"usort",             jx9_hashmap_usort   }, 
+       {"current",           jx9_hashmap_current }, 
+       {"each",              jx9_hashmap_each    }, 
+       {"pos",               jx9_hashmap_current }, 
+       {"next",              jx9_hashmap_next    }, 
+       {"prev",              jx9_hashmap_prev    }, 
+       {"end",               jx9_hashmap_end     }, 
+       {"reset",             jx9_hashmap_reset   }, 
+       {"key",               jx9_hashmap_simple_key }
+};
+/*
+ * Register the built-in hashmap functions defined above.
+ */
+JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm)
+{
+       sxu32 n;
+       for( n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++ ){
+               jx9_create_function(&(*pVm), aHashmapFunc[n].zName, aHashmapFunc[n].xFunc, 0);
+       }
+}
+/*
+ * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each 
+ * retrieved entry.
+ * Note that argument are passed to the callback by copy. That is, any modification to 
+ * the entry value in the callback body will not alter the real value.
+ * If the callback wishes to abort processing [i.e: it's invocation] it must return
+ * a value different from JX9_OK.
+ * Refer to [jx9_array_walk()] for more information.
+ */
+JX9_PRIVATE sxi32 jx9HashmapWalk(
+       jx9_hashmap *pMap, /* Target hashmap */
+       int (*xWalk)(jx9_value *, jx9_value *, void *), /* Walker callback */
+       void *pUserData /* Last argument to xWalk() */
+       )
+{
+       jx9_hashmap_node *pEntry;
+       jx9_value sKey, sValue;
+       sxi32 rc;
+       sxu32 n;
+       /* Initialize walker parameter */
+       rc = SXRET_OK;
+       jx9MemObjInit(pMap->pVm, &sKey);
+       jx9MemObjInit(pMap->pVm, &sValue);
+       n = pMap->nEntry;
+       pEntry = pMap->pFirst;
+       /* Start the iteration process */
+       for(;;){
+               if( n < 1 ){
+                       break;
+               }
+               /* Extract a copy of the key and a copy the current value */
+               jx9HashmapExtractNodeKey(pEntry, &sKey);
+               jx9HashmapExtractNodeValue(pEntry, &sValue, FALSE);
+               /* Invoke the user callback */
+               rc = xWalk(&sKey, &sValue, pUserData);
+               /* Release the copy of the key and the value */
+               jx9MemObjRelease(&sKey);
+               jx9MemObjRelease(&sValue);
+               if( rc != JX9_OK ){
+                       /* Callback request an operation abort */
+                       return SXERR_ABORT;
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pPrev; /* Reverse link */
+               n--;
+       }
+       /* All done */
+       return SXRET_OK;
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_json.c
+ * MD5: 31a27f8797418de511c669feed763341
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: json.c v1.0 FreeBSD 2012-12-16 00:28 stable <chm@symisc.net> $ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+/* This file deals with JSON serialization, decoding and stuff like that. */
+/*
+ * Section: 
+ *  JSON encoding/decoding routines.
+ * Authors:
+ *  Symisc Systems, devel@symisc.net.
+ *  Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Devel.
+ */
+/* Forward reference */
+static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
+static int VmJsonObjectEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
+/* 
+ * JSON encoder state is stored in an instance 
+ * of the following structure.
+ */
+typedef struct json_private_data json_private_data;
+struct json_private_data
+{
+       SyBlob *pOut;      /* Output consumer buffer */
+       int isFirst;       /* True if first encoded entry */
+       int iFlags;        /* JSON encoding flags */
+       int nRecCount;     /* Recursion count */
+};
+/*
+ * Returns the JSON representation of a value.In other word perform a JSON encoding operation.
+ * According to wikipedia
+ * JSON's basic types are:
+ *   Number (double precision floating-point format in JavaScript, generally depends on implementation)
+ *   String (double-quoted Unicode, with backslash escaping)
+ *   Boolean (true or false)
+ *   Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
+ *    do not need to be of the same type)
+ *   Object (an unordered collection of key:value pairs with the ':' character separating the key 
+ *     and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
+ *     be distinct from each other)
+ *   null (empty)
+ * Non-significant white space may be added freely around the "structural characters"
+ * (i.e. the brackets "[{]}", colon ":" and comma ",").
+ */
+static sxi32 VmJsonEncode(
+       jx9_value *pIn,          /* Encode this value */
+       json_private_data *pData /* Context data */
+       ){
+               SyBlob *pOut = pData->pOut;
+               int nByte;
+               if( jx9_value_is_null(pIn) || jx9_value_is_resource(pIn)){
+                       /* null */
+                       SyBlobAppend(pOut, "null", sizeof("null")-1);
+               }else if( jx9_value_is_bool(pIn) ){
+                       int iBool = jx9_value_to_bool(pIn);
+                       sxu32 iLen;
+                       /* true/false */
+                       iLen = iBool ? sizeof("true") : sizeof("false");
+                       SyBlobAppend(pOut, iBool ? "true" : "false", iLen-1);
+               }else if(  jx9_value_is_numeric(pIn) && !jx9_value_is_string(pIn) ){
+                       const char *zNum;
+                       /* Get a string representation of the number */
+                       zNum = jx9_value_to_string(pIn, &nByte);
+                       SyBlobAppend(pOut,zNum,nByte);
+               }else if( jx9_value_is_string(pIn) ){
+                               const char *zIn, *zEnd;
+                               int c;
+                               /* Encode the string */
+                               zIn = jx9_value_to_string(pIn, &nByte);
+                               zEnd = &zIn[nByte];
+                               /* Append the double quote */
+                               SyBlobAppend(pOut,"\"", sizeof(char));
+                               for(;;){
+                                       if( zIn >= zEnd ){
+                                               /* No more input to process */
+                                               break;
+                                       }
+                                       c = zIn[0];
+                                       /* Advance the stream cursor */
+                                       zIn++;
+                                       if( c == '"' || c == '\\' ){
+                                               /* Unescape the character */
+                                               SyBlobAppend(pOut,"\\", sizeof(char));
+                                       }
+                                       /* Append character verbatim */
+                                       SyBlobAppend(pOut,(const char *)&c,sizeof(char));
+                               }
+                               /* Append the double quote */
+                               SyBlobAppend(pOut,"\"",sizeof(char));
+               }else if( jx9_value_is_json_array(pIn) ){
+                       /* Encode the array/object */
+                       pData->isFirst = 1;
+                       if( jx9_value_is_json_object(pIn) ){
+                               /* Encode the object instance */
+                               pData->isFirst = 1;
+                               /* Append the curly braces */
+                               SyBlobAppend(pOut, "{",sizeof(char));
+                               /* Iterate throw object attribute */
+                               jx9_array_walk(pIn,VmJsonObjectEncode, pData);
+                               /* Append the closing curly braces  */
+                               SyBlobAppend(pOut, "}",sizeof(char));
+                       }else{
+                               /* Append the square bracket or curly braces */
+                               SyBlobAppend(pOut,"[",sizeof(char));
+                               /* Iterate throw array entries */
+                               jx9_array_walk(pIn, VmJsonArrayEncode, pData);
+                               /* Append the closing square bracket or curly braces */
+                               SyBlobAppend(pOut,"]",sizeof(char));
+                       }
+               }else{
+                       /* Can't happen */
+                       SyBlobAppend(pOut,"null",sizeof("null")-1);
+               }
+               /* All done */
+               return JX9_OK;
+}
+/*
+ * The following walker callback is invoked each time we need
+ * to encode an array to JSON.
+ */
+static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData)
+{
+       json_private_data *pJson = (json_private_data *)pUserData;
+       if( pJson->nRecCount > 31 ){
+               /* Recursion limit reached, return immediately */
+               SXUNUSED(pKey); /* cc warning */
+               return JX9_OK;
+       }
+       if( !pJson->isFirst ){
+               /* Append the colon first */
+               SyBlobAppend(pJson->pOut,",",(int)sizeof(char));
+       }
+       /* Encode the value */
+       pJson->nRecCount++;
+       VmJsonEncode(pValue, pJson);
+       pJson->nRecCount--;
+       pJson->isFirst = 0;
+       return JX9_OK;
+}
+/*
+ * The following walker callback is invoked each time we need to encode
+ * a object instance [i.e: Object in the JX9 jargon] to JSON.
+ */
+static int VmJsonObjectEncode(jx9_value *pKey,jx9_value *pValue,void *pUserData)
+{
+       json_private_data *pJson = (json_private_data *)pUserData;
+       const char *zKey;
+       int nByte;
+       if( pJson->nRecCount > 31 ){
+               /* Recursion limit reached, return immediately */
+               return JX9_OK;
+       }
+       if( !pJson->isFirst ){
+               /* Append the colon first */
+               SyBlobAppend(pJson->pOut,",",sizeof(char));
+       }
+       /* Extract a string representation of the key */
+       zKey = jx9_value_to_string(pKey, &nByte);
+       /* Append the key and the double colon */
+       if( nByte > 0 ){
+               SyBlobAppend(pJson->pOut,"\"",sizeof(char));
+               SyBlobAppend(pJson->pOut,zKey,(sxu32)nByte);
+               SyBlobAppend(pJson->pOut,"\"",sizeof(char));
+       }else{
+               /* Can't happen */
+               SyBlobAppend(pJson->pOut,"null",sizeof("null")-1);
+       }
+       SyBlobAppend(pJson->pOut,":",sizeof(char));
+       /* Encode the value */
+       pJson->nRecCount++;
+       VmJsonEncode(pValue, pJson);
+       pJson->nRecCount--;
+       pJson->isFirst = 0;
+       return JX9_OK;
+}
+/*
+ *  Returns a string containing the JSON representation of value. 
+ *  In other words, perform the serialization of the given JSON object.
+ */
+JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut)
+{
+       json_private_data sJson;
+       /* Prepare the JSON data */
+       sJson.nRecCount = 0;
+       sJson.pOut = pOut;
+       sJson.isFirst = 1;
+       sJson.iFlags = 0;
+       /* Perform the encoding operation */
+       VmJsonEncode(pValue, &sJson);
+       /* All done */
+       return JX9_OK;
+}
+/* Possible tokens from the JSON tokenization process */
+#define JSON_TK_TRUE    0x001 /* Boolean true */
+#define JSON_TK_FALSE   0x002 /* Boolean false */
+#define JSON_TK_STR     0x004 /* String enclosed in double quotes */
+#define JSON_TK_NULL    0x008 /* null */
+#define JSON_TK_NUM     0x010 /* Numeric */
+#define JSON_TK_OCB     0x020 /* Open curly braces '{' */
+#define JSON_TK_CCB     0x040 /* Closing curly braces '}' */
+#define JSON_TK_OSB     0x080 /* Open square bracke '[' */
+#define JSON_TK_CSB     0x100 /* Closing square bracket ']' */
+#define JSON_TK_COLON   0x200 /* Single colon ':' */
+#define JSON_TK_COMMA   0x400 /* Single comma ',' */
+#define JSON_TK_ID      0x800 /* ID */
+#define JSON_TK_INVALID 0x1000 /* Unexpected token */
+/* 
+ * Tokenize an entire JSON input.
+ * Get a single low-level token from the input file.
+ * Update the stream pointer so that it points to the first
+ * character beyond the extracted token.
+ */
+static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData)
+{
+       int *pJsonErr = (int *)pUserData;
+       SyString *pStr;
+       int c;
+       /* Ignore leading white spaces */
+       while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
+               /* Advance the stream cursor */
+               if( pStream->zText[0] == '\n' ){
+                       /* Update line counter */
+                       pStream->nLine++;
+               }
+               pStream->zText++;
+       }
+       if( pStream->zText >= pStream->zEnd ){
+               /* End of input reached */
+               SXUNUSED(pCtxData); /* cc warning */
+               return SXERR_EOF;
+       }
+       /* Record token starting position and line */
+       pToken->nLine = pStream->nLine;
+       pToken->pUserData = 0;
+       pStr = &pToken->sData;
+       SyStringInitFromBuf(pStr, pStream->zText, 0);
+       if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
+               /* The following code fragment is taken verbatim from the xPP source tree.
+                * xPP is a modern embeddable macro processor with advanced features useful for
+                * application seeking for a production quality, ready to use macro processor.
+                * xPP is a widely used library developed and maintened by Symisc Systems.
+                * You can reach the xPP home page by following this link:
+                * http://xpp.symisc.net/
+                */
+               const unsigned char *zIn;
+               /* Isolate UTF-8 or alphanumeric stream */
+               if( pStream->zText[0] < 0xc0 ){
+                       pStream->zText++;
+               }
+               for(;;){
+                       zIn = pStream->zText;
+                       if( zIn[0] >= 0xc0 ){
+                               zIn++;
+                               /* UTF-8 stream */
+                               while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
+                                       zIn++;
+                               }
+                       }
+                       /* Skip alphanumeric stream */
+                       while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
+                               zIn++;
+                       }
+                       if( zIn == pStream->zText ){
+                               /* Not an UTF-8 or alphanumeric stream */
+                               break;
+                       }
+                       /* Synchronize pointers */
+                       pStream->zText = zIn;
+               }
+               /* Record token length */
+               pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
+               /* A simple identifier */
+               pToken->nType = JSON_TK_ID;
+               if( pStr->nByte == sizeof("true") -1 && SyStrnicmp(pStr->zString, "true", sizeof("true")-1) == 0 ){
+                       /* boolean true */
+                       pToken->nType = JSON_TK_TRUE;
+               }else if( pStr->nByte == sizeof("false") -1 && SyStrnicmp(pStr->zString,"false", sizeof("false")-1) == 0 ){
+                       /* boolean false */
+                       pToken->nType = JSON_TK_FALSE;
+               }else if( pStr->nByte == sizeof("null") -1 && SyStrnicmp(pStr->zString,"null", sizeof("null")-1) == 0 ){
+                       /* NULL */
+                       pToken->nType = JSON_TK_NULL;
+               }
+               return SXRET_OK;
+       }
+       if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']'
+               || pStream->zText[0] == ':' || pStream->zText[0] == ',' ){
+                       /* Single character */
+                       c = pStream->zText[0];
+                       /* Set token type */
+                       switch(c){
+                       case '[': pToken->nType = JSON_TK_OSB;   break;
+                       case '{': pToken->nType = JSON_TK_OCB;   break;
+                       case '}': pToken->nType = JSON_TK_CCB;   break;
+                       case ']': pToken->nType = JSON_TK_CSB;   break;
+                       case ':': pToken->nType = JSON_TK_COLON; break;
+                       case ',': pToken->nType = JSON_TK_COMMA; break;
+                       default:
+                               break;
+                       }
+                       /* Advance the stream cursor */
+                       pStream->zText++;
+       }else if( pStream->zText[0] == '"') {
+               /* JSON string */
+               pStream->zText++;
+               pStr->zString++;
+               /* Delimit the string */
+               while( pStream->zText < pStream->zEnd ){
+                       if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){
+                               break;
+                       }
+                       if( pStream->zText[0] == '\n' ){
+                               /* Update line counter */
+                               pStream->nLine++;
+                       }
+                       pStream->zText++;
+               }
+               if( pStream->zText >= pStream->zEnd ){
+                       /* Missing closing '"' */
+                       pToken->nType = JSON_TK_INVALID;
+                       *pJsonErr = SXERR_SYNTAX;
+               }else{
+                       pToken->nType = JSON_TK_STR;
+                       pStream->zText++; /* Jump the closing double quotes */
+               }
+       }else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+               /* Number */
+               pStream->zText++;
+               pToken->nType = JSON_TK_NUM;
+               while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+                       pStream->zText++;
+               }
+               if( pStream->zText < pStream->zEnd ){
+                       c = pStream->zText[0];
+                       if( c == '.' ){
+                                       /* Real number */
+                                       pStream->zText++;
+                                       while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+                                               pStream->zText++;
+                                       }
+                                       if( pStream->zText < pStream->zEnd ){
+                                               c = pStream->zText[0];
+                                               if( c=='e' || c=='E' ){
+                                                       pStream->zText++;
+                                                       if( pStream->zText < pStream->zEnd ){
+                                                               c = pStream->zText[0];
+                                                               if( c =='+' || c=='-' ){
+                                                                       pStream->zText++;
+                                                               }
+                                                               while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+                                                                       pStream->zText++;
+                                                               }
+                                                       }
+                                               }
+                                       }                                       
+                               }else if( c=='e' || c=='E' ){
+                                       /* Real number */
+                                       pStream->zText++;
+                                       if( pStream->zText < pStream->zEnd ){
+                                               c = pStream->zText[0];
+                                               if( c =='+' || c=='-' ){
+                                                       pStream->zText++;
+                                               }
+                                               while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+                                                       pStream->zText++;
+                                               }
+                                       }                                       
+                               }
+                       }
+       }else{
+               /* Unexpected token */
+               pToken->nType = JSON_TK_INVALID;
+               /* Advance the stream cursor */
+               pStream->zText++;
+               *pJsonErr = SXERR_SYNTAX;
+               /* Abort processing immediatley */
+               return SXERR_ABORT;
+       }
+       /* record token length */
+       pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
+       if( pToken->nType == JSON_TK_STR ){
+               pStr->nByte--;
+       }
+       /* Return to the lexer */
+       return SXRET_OK;
+}
+/*
+ * JSON decoded input consumer callback signature.
+ */
+typedef int (*ProcJSONConsumer)(jx9_context *, jx9_value *, jx9_value *, void *); 
+/* 
+ * JSON decoder state is kept in the following structure.
+ */
+typedef struct json_decoder json_decoder;
+struct json_decoder
+{
+       jx9_context *pCtx; /* Call context */
+       ProcJSONConsumer xConsumer; /* Consumer callback */ 
+       void *pUserData;   /* Last argument to xConsumer() */
+       int iFlags;        /* Configuration flags */
+       SyToken *pIn;      /* Token stream */
+       SyToken *pEnd;     /* End of the token stream */
+       int rec_count;     /* Current nesting level */
+       int *pErr;         /* JSON decoding error if any */
+};
+/* Forward declaration */
+static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData);
+/*
+ * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store
+ * the result in the given jx9_value.
+ */
+static void VmJsonDequoteString(const SyString *pStr, jx9_value *pWorker)
+{
+       const char *zIn = pStr->zString;
+       const char *zEnd = &pStr->zString[pStr->nByte];
+       const char *zCur;
+       int c;
+       /* Mark the value as a string */
+       jx9_value_string(pWorker, "", 0); /* Empty string */
+       for(;;){
+               zCur = zIn;
+               while( zIn < zEnd && zIn[0] != '\\' ){
+                       zIn++;
+               }
+               if( zIn > zCur ){
+                       /* Append chunk verbatim */
+                       jx9_value_string(pWorker, zCur, (int)(zIn-zCur));
+               }
+               zIn++;
+               if( zIn >= zEnd ){
+                       /* End of the input reached */
+                       break;
+               }
+               c = zIn[0];
+               /* Unescape the character */
+               switch(c){
+               case '"':  jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
+               case '\\': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
+               case 'n':  jx9_value_string(pWorker, "\n", (int)sizeof(char)); break;
+               case 'r':  jx9_value_string(pWorker, "\r", (int)sizeof(char)); break;
+               case 't':  jx9_value_string(pWorker, "\t", (int)sizeof(char)); break;
+               case 'f':  jx9_value_string(pWorker, "\f", (int)sizeof(char)); break;
+               default:
+                       jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char));
+                       break;
+               }
+               /* Advance the stream cursor */
+               zIn++;
+       }
+}
+/*
+ * Returns a jx9_value holding the image of a JSON string. In other word perform a JSON decoding operation.
+ * According to wikipedia
+ * JSON's basic types are:
+ *   Number (double precision floating-point format in JavaScript, generally depends on implementation)
+ *   String (double-quoted Unicode, with backslash escaping)
+ *   Boolean (true or false)
+ *   Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
+ *    do not need to be of the same type)
+ *   Object (an unordered collection of key:value pairs with the ':' character separating the key 
+ *     and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
+ *     be distinct from each other)
+ *   null (empty)
+ * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ", ").
+ */
+static sxi32 VmJsonDecode(
+       json_decoder *pDecoder, /* JSON decoder */      
+       jx9_value *pArrayKey    /* Key for the decoded array */
+       ){
+       jx9_value *pWorker; /* Worker variable */
+       sxi32 rc;
+       /* Check if we do not nest to much */
+       if( pDecoder->rec_count > 31 ){
+               /* Nesting limit reached, abort decoding immediately */
+               return SXERR_ABORT;
+       }
+       if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_ID|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){
+               /* Scalar value */
+               pWorker = jx9_context_new_scalar(pDecoder->pCtx);
+               if( pWorker == 0 ){
+                       jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
+                       /* Abort the decoding operation immediately */
+                       return SXERR_ABORT;
+               }
+               /* Reflect the JSON image */
+               if( pDecoder->pIn->nType & JSON_TK_NULL ){
+                       /* Nullify the value.*/
+                       jx9_value_null(pWorker);
+               }else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){
+                       /* Boolean value */
+                       jx9_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 );
+               }else if( pDecoder->pIn->nType & JSON_TK_NUM ){
+                       SyString *pStr = &pDecoder->pIn->sData;
+                       /* 
+                        * Numeric value.
+                        * Get a string representation first then try to get a numeric
+                        * value.
+                        */
+                       jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
+                       /* Obtain a numeric representation */
+                       jx9MemObjToNumeric(pWorker);
+               }else if( pDecoder->pIn->nType & JSON_TK_ID ){
+                       SyString *pStr = &pDecoder->pIn->sData;
+                       jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
+               }else{
+                       /* Dequote the string */
+                       VmJsonDequoteString(&pDecoder->pIn->sData, pWorker);
+               }
+               /* Invoke the consumer callback */
+               rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData);
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }
+               /* All done, advance the stream cursor */
+               pDecoder->pIn++;
+       }else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) {
+               ProcJSONConsumer xOld;
+               void *pOld;
+               /* Array representation*/
+               pDecoder->pIn++;
+               /* Create a working array */
+               pWorker = jx9_context_new_array(pDecoder->pCtx);
+               if( pWorker == 0 ){
+                       jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
+                       /* Abort the decoding operation immediately */
+                       return SXERR_ABORT;
+               }
+               /* Save the old consumer */
+               xOld = pDecoder->xConsumer;
+               pOld = pDecoder->pUserData;
+               /* Set the new consumer */
+               pDecoder->xConsumer = VmJsonArrayDecoder;
+               pDecoder->pUserData = pWorker;
+               /* Decode the array */
+               for(;;){
+                       /* Jump trailing comma. Note that the standard JX9 engine will not let you
+                        * do this.
+                        */
+                       while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
+                               pDecoder->pIn++;
+                       }
+                       if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){
+                               if( pDecoder->pIn < pDecoder->pEnd ){
+                                       pDecoder->pIn++; /* Jump the trailing ']' */
+                               }
+                               break;
+                       }
+                       /* Recurse and decode the entry */
+                       pDecoder->rec_count++;
+                       rc = VmJsonDecode(pDecoder, 0);
+                       pDecoder->rec_count--;
+                       if( rc == SXERR_ABORT ){
+                               /* Abort processing immediately */
+                               return SXERR_ABORT;
+                       }
+                       /*The cursor is automatically advanced by the VmJsonDecode() function */
+                       if( (pDecoder->pIn < pDecoder->pEnd) &&
+                               ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){
+                                       /* Unexpected token, abort immediatley */
+                                       *pDecoder->pErr = SXERR_SYNTAX;
+                                       return SXERR_ABORT;
+                       }
+               }
+               /* Restore the old consumer */
+               pDecoder->xConsumer = xOld;
+               pDecoder->pUserData = pOld;
+               /* Invoke the old consumer on the decoded array */
+               xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
+       }else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) {
+               ProcJSONConsumer xOld;
+               jx9_value *pKey;
+               void *pOld;
+               /* Object representation*/
+               pDecoder->pIn++;
+               /* Create a working array */
+               pWorker = jx9_context_new_array(pDecoder->pCtx);
+               pKey = jx9_context_new_scalar(pDecoder->pCtx);
+               if( pWorker == 0 || pKey == 0){
+                       jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
+                       /* Abort the decoding operation immediately */
+                       return SXERR_ABORT;
+               }
+               /* Save the old consumer */
+               xOld = pDecoder->xConsumer;
+               pOld = pDecoder->pUserData;
+               /* Set the new consumer */
+               pDecoder->xConsumer = VmJsonArrayDecoder;
+               pDecoder->pUserData = pWorker;
+               /* Decode the object */
+               for(;;){
+                       /* Jump trailing comma. Note that the standard JX9 engine will not let you
+                        * do this.
+                        */
+                       while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
+                               pDecoder->pIn++;
+                       }
+                       if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){
+                               if( pDecoder->pIn < pDecoder->pEnd ){
+                                       pDecoder->pIn++; /* Jump the trailing ']' */
+                               }
+                               break;
+                       }
+                       if( (pDecoder->pIn->nType & (JSON_TK_ID|JSON_TK_STR)) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd
+                               || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){
+                                       /* Syntax error, return immediately */
+                                       *pDecoder->pErr = SXERR_SYNTAX;
+                                       return SXERR_ABORT;
+                       }
+                       if( pDecoder->pIn->nType & JSON_TK_ID ){
+                               SyString *pStr = &pDecoder->pIn->sData;
+                               jx9_value_string(pKey, pStr->zString, (int)pStr->nByte);
+                       }else{
+                               /* Dequote the key */
+                               VmJsonDequoteString(&pDecoder->pIn->sData, pKey);
+                       }
+                       /* Jump the key and the colon */
+                       pDecoder->pIn += 2; 
+                       /* Recurse and decode the value */
+                       pDecoder->rec_count++;
+                       rc = VmJsonDecode(pDecoder, pKey);
+                       pDecoder->rec_count--;
+                       if( rc == SXERR_ABORT ){
+                               /* Abort processing immediately */
+                               return SXERR_ABORT;
+                       }
+                       /* Reset the internal buffer of the key */
+                       jx9_value_reset_string_cursor(pKey);
+                       /*The cursor is automatically advanced by the VmJsonDecode() function */
+               }
+               /* Restore the old consumer */
+               pDecoder->xConsumer = xOld;
+               pDecoder->pUserData = pOld;
+               /* Invoke the old consumer on the decoded object*/
+               xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
+               /* Release the key */
+               jx9_context_release_value(pDecoder->pCtx, pKey);
+       }else{
+               /* Unexpected token */
+               return SXERR_ABORT; /* Abort immediately */
+       }
+       /* Release the worker variable */
+       jx9_context_release_value(pDecoder->pCtx, pWorker);
+       return SXRET_OK;
+}
+/*
+ * The following JSON decoder callback is invoked each time
+ * a JSON array representation [i.e: [15, "hello", FALSE] ]
+ * is being decoded.
+ */
+static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
+{
+       jx9_value *pArray = (jx9_value *)pUserData;
+       /* Insert the entry */
+       jx9_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */
+       SXUNUSED(pCtx); /* cc warning */
+       /* All done */
+       return SXRET_OK;
+}
+/*
+ * Standard JSON decoder callback.
+ */
+static int VmJsonDefaultDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
+{
+       /* Return the value directly */
+       jx9_result_value(pCtx, pWorker); /* Will make it's own copy */
+       SXUNUSED(pKey); /* cc warning */
+       SXUNUSED(pUserData);
+       /* All done */
+       return SXRET_OK;
+}
+/*
+ * Exported JSON decoding interface
+ */
+JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte)
+{
+       jx9_vm *pVm = pCtx->pVm;
+       json_decoder sDecoder;
+       SySet sToken;
+       SyLex sLex;
+       sxi32 rc;
+       /* Tokenize the input */
+       SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken));
+       rc = SXRET_OK;
+       SyLexInit(&sLex, &sToken, VmJsonTokenize, &rc);
+       SyLexTokenizeInput(&sLex,zJSON,(sxu32)nByte, 0, 0, 0);
+       if( rc != SXRET_OK ){
+               /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */
+               SyLexRelease(&sLex);
+               SySetRelease(&sToken);
+               /* return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Fill the decoder */
+       sDecoder.pCtx = pCtx;
+       sDecoder.pErr = &rc;
+       sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken);
+       sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)];
+       sDecoder.iFlags = 0;
+       sDecoder.rec_count = 0;
+       /* Set a default consumer */
+       sDecoder.xConsumer = VmJsonDefaultDecoder;
+       sDecoder.pUserData = 0;
+       /* Decode the raw JSON input */
+       rc = VmJsonDecode(&sDecoder, 0);
+       if( rc == SXERR_ABORT ){
+               /* 
+                * Something goes wrong while decoding JSON input.Return NULL.
+                */
+               jx9_result_null(pCtx);
+       }
+       /* Clean-up the mess left behind */
+       SyLexRelease(&sLex);
+       SySetRelease(&sToken);
+       /* All done */
+       return JX9_OK;
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_lex.c
+ * MD5: a79518c0635dbaf5dcfaca62efa2faf8
+ * ----------------------------------------------------------
+ */
+/*
+ * 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
+/* This file implements a thread-safe and full reentrant lexical analyzer for the Jx9 programming language */
+/* Forward declarations */
+static sxu32 keywordCode(const char *z,int n);
+static sxi32 LexExtractNowdoc(SyStream *pStream,SyToken *pToken);
+/*
+ * Tokenize a raw jx9 input.
+ * Get a single low-level token from the input file. Update the stream pointer so that
+ * it points to the first character beyond the extracted token.
+ */
+static sxi32 jx9TokenizeInput(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData)
+{
+       SyString *pStr;
+       sxi32 rc;
+       /* Ignore leading white spaces */
+       while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
+               /* Advance the stream cursor */
+               if( pStream->zText[0] == '\n' ){
+                       /* Update line counter */
+                       pStream->nLine++;
+               }
+               pStream->zText++;
+       }
+       if( pStream->zText >= pStream->zEnd ){
+               /* End of input reached */
+               return SXERR_EOF;
+       }
+       /* Record token starting position and line */
+       pToken->nLine = pStream->nLine;
+       pToken->pUserData = 0;
+       pStr = &pToken->sData;
+       SyStringInitFromBuf(pStr, pStream->zText, 0);
+       if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
+               /* The following code fragment is taken verbatim from the xPP source tree.
+                * xPP is a modern embeddable macro processor with advanced features useful for
+                * application seeking for a production quality, ready to use macro processor.
+                * xPP is a widely used library developed and maintened by Symisc Systems.
+                * You can reach the xPP home page by following this link:
+                * http://xpp.symisc.net/
+                */
+               const unsigned char *zIn;
+               sxu32 nKeyword;
+               /* Isolate UTF-8 or alphanumeric stream */
+               if( pStream->zText[0] < 0xc0 ){
+                       pStream->zText++;
+               }
+               for(;;){
+                       zIn = pStream->zText;
+                       if( zIn[0] >= 0xc0 ){
+                               zIn++;
+                               /* UTF-8 stream */
+                               while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
+                                       zIn++;
+                               }
+                       }
+                       /* Skip alphanumeric stream */
+                       while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
+                               zIn++;
+                       }
+                       if( zIn == pStream->zText ){
+                               /* Not an UTF-8 or alphanumeric stream */
+                               break;
+                       }
+                       /* Synchronize pointers */
+                       pStream->zText = zIn;
+               }
+               /* Record token length */
+               pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
+               nKeyword = keywordCode(pStr->zString, (int)pStr->nByte);
+               if( nKeyword != JX9_TK_ID ){
+                       /* We are dealing with a keyword [i.e: if, function, CREATE, ...], save the keyword ID */
+                       pToken->nType = JX9_TK_KEYWORD;
+                       pToken->pUserData = SX_INT_TO_PTR(nKeyword);
+               }else{
+                       /* A simple identifier */
+                       pToken->nType = JX9_TK_ID;
+               }
+       }else{
+               sxi32 c;
+               /* Non-alpha stream */
+               if( pStream->zText[0] == '#' || 
+                       ( pStream->zText[0] == '/' &&  &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '/') ){
+                               pStream->zText++;
+                               /* Inline comments */
+                               while( pStream->zText < pStream->zEnd && pStream->zText[0] != '\n' ){
+                                       pStream->zText++;
+                               }
+                               /* Tell the upper-layer to ignore this token */ 
+                               return SXERR_CONTINUE;
+               }else if( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '*' ){
+                       pStream->zText += 2;
+                       /* Block comment */
+                       while( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '*' ){
+                                       if( &pStream->zText[1] >= pStream->zEnd || pStream->zText[1] == '/'  ){
+                                               break;
+                                       }
+                               }
+                               if( pStream->zText[0] == '\n' ){
+                                       pStream->nLine++;
+                               }
+                               pStream->zText++;
+                       }
+                       pStream->zText += 2;
+                       /* Tell the upper-layer to ignore this token */
+                       return SXERR_CONTINUE;
+               }else if( SyisDigit(pStream->zText[0]) ){
+                       pStream->zText++;
+                       /* Decimal digit stream */
+                       while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+                               pStream->zText++;
+                       }
+                       /* Mark the token as integer until we encounter a real number */
+                       pToken->nType = JX9_TK_INTEGER;
+                       if( pStream->zText < pStream->zEnd ){
+                               c = pStream->zText[0];
+                               if( c == '.' ){
+                                       /* Real number */
+                                       pStream->zText++;
+                                       while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+                                               pStream->zText++;
+                                       }
+                                       if( pStream->zText < pStream->zEnd ){
+                                               c = pStream->zText[0];
+                                               if( c=='e' || c=='E' ){
+                                                       pStream->zText++;
+                                                       if( pStream->zText < pStream->zEnd ){
+                                                               c = pStream->zText[0];
+                                                               if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd  &&
+                                                                       pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
+                                                                               pStream->zText++;
+                                                               }
+                                                               while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+                                                                       pStream->zText++;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       pToken->nType = JX9_TK_REAL;
+                               }else if( c=='e' || c=='E' ){
+                                       SXUNUSED(pUserData); /* Prevent compiler warning */
+                                       SXUNUSED(pCtxData);
+                                       pStream->zText++;
+                                       if( pStream->zText < pStream->zEnd ){
+                                               c = pStream->zText[0];
+                                               if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd  &&
+                                                       pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
+                                                               pStream->zText++;
+                                               }
+                                               while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
+                                                       pStream->zText++;
+                                               }
+                                       }
+                                       pToken->nType = JX9_TK_REAL;
+                               }else if( c == 'x' || c == 'X' ){
+                                       /* Hex digit stream */
+                                       pStream->zText++;
+                                       while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisHex(pStream->zText[0]) ){
+                                               pStream->zText++;
+                                       }
+                               }else if(c  == 'b' || c == 'B' ){
+                                       /* Binary digit stream */
+                                       pStream->zText++;
+                                       while( pStream->zText < pStream->zEnd && (pStream->zText[0] == '0' || pStream->zText[0] == '1') ){
+                                               pStream->zText++;
+                                       }
+                               }
+                       }
+                       /* Record token length */
+                       pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
+                       return SXRET_OK;
+               }
+               c = pStream->zText[0];
+               pStream->zText++; /* Advance the stream cursor */
+               /* Assume we are dealing with an operator*/
+               pToken->nType = JX9_TK_OP;
+               switch(c){
+               case '$': pToken->nType = JX9_TK_DOLLAR; break;
+               case '{': pToken->nType = JX9_TK_OCB;   break; 
+               case '}': pToken->nType = JX9_TK_CCB;    break;
+               case '(': pToken->nType = JX9_TK_LPAREN; break; 
+               case '[': pToken->nType |= JX9_TK_OSB;   break; /* Bitwise operation here, since the square bracket token '[' 
+                                                                                                                * is a potential operator [i.e: subscripting] */
+               case ']': pToken->nType = JX9_TK_CSB;    break;
+               case ')': {
+                       SySet *pTokSet = pStream->pSet;
+                       /* Assemble type cast operators [i.e: (int), (float), (bool)...] */ 
+                       if( pTokSet->nUsed >= 2 ){
+                               SyToken *pTmp;
+                               /* Peek the last recongnized token */
+                               pTmp = (SyToken *)SySetPeek(pTokSet);
+                               if( pTmp->nType & JX9_TK_KEYWORD ){
+                                       sxi32 nID = SX_PTR_TO_INT(pTmp->pUserData);
+                                       if( (sxu32)nID & (JX9_TKWRD_INT|JX9_TKWRD_FLOAT|JX9_TKWRD_STRING|JX9_TKWRD_BOOL) ){
+                                               pTmp = (SyToken *)SySetAt(pTokSet, pTokSet->nUsed - 2);
+                                               if( pTmp->nType & JX9_TK_LPAREN ){
+                                                       /* Merge the three tokens '(' 'TYPE' ')' into a single one */
+                                                       const char * zTypeCast = "(int)";
+                                                       if( nID & JX9_TKWRD_FLOAT ){
+                                                               zTypeCast = "(float)";
+                                                       }else if( nID & JX9_TKWRD_BOOL ){
+                                                               zTypeCast = "(bool)";
+                                                       }else if( nID & JX9_TKWRD_STRING ){
+                                                               zTypeCast = "(string)";
+                                                       }
+                                                       /* Reflect the change */
+                                                       pToken->nType = JX9_TK_OP;
+                                                       SyStringInitFromBuf(&pToken->sData, zTypeCast, SyStrlen(zTypeCast));
+                                                       /* Save the instance associated with the type cast operator */
+                                                       pToken->pUserData = (void *)jx9ExprExtractOperator(&pToken->sData, 0);
+                                                       /* Remove the two previous tokens */
+                                                       pTokSet->nUsed -= 2;
+                                                       return SXRET_OK;
+                                               }
+                                       }
+                               }
+                       }
+                       pToken->nType = JX9_TK_RPAREN;
+                       break;
+                                 }
+               case '\'':{
+                       /* Single quoted string */
+                       pStr->zString++;
+                       while( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '\''  ){
+                                       if( pStream->zText[-1] != '\\' ){
+                                               break;
+                                       }else{
+                                               const unsigned char *zPtr = &pStream->zText[-2];
+                                               sxi32 i = 1;
+                                               while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
+                                                       zPtr--;
+                                                       i++;
+                                               }
+                                               if((i&1)==0){
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if( pStream->zText[0] == '\n' ){
+                                       pStream->nLine++;
+                               }
+                               pStream->zText++;
+                       }
+                       /* Record token length and type */
+                       pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
+                       pToken->nType = JX9_TK_SSTR;
+                       /* Jump the trailing single quote */
+                       pStream->zText++;
+                       return SXRET_OK;
+                                 }
+               case '"':{
+                       sxi32 iNest;
+                       /* Double quoted string */
+                       pStr->zString++;
+                       while( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '{' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '$'){
+                                       iNest = 1;
+                                       pStream->zText++;
+                                       /* TICKET 1433-40: Hnadle braces'{}' in double quoted string where everything is allowed */
+                                       while(pStream->zText < pStream->zEnd ){
+                                               if( pStream->zText[0] == '{' ){
+                                                       iNest++;
+                                               }else if (pStream->zText[0] == '}' ){
+                                                       iNest--;
+                                                       if( iNest <= 0 ){
+                                                               pStream->zText++;
+                                                               break;
+                                                       }
+                                               }else if( pStream->zText[0] == '\n' ){
+                                                       pStream->nLine++;
+                                               }
+                                               pStream->zText++;
+                                       }
+                                       if( pStream->zText >= pStream->zEnd ){
+                                               break;
+                                       }
+                               }
+                               if( pStream->zText[0] == '"' ){
+                                       if( pStream->zText[-1] != '\\' ){
+                                               break;
+                                       }else{
+                                               const unsigned char *zPtr = &pStream->zText[-2];
+                                               sxi32 i = 1;
+                                               while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
+                                                       zPtr--;
+                                                       i++;
+                                               }
+                                               if((i&1)==0){
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if( pStream->zText[0] == '\n' ){
+                                       pStream->nLine++;
+                               }
+                               pStream->zText++;
+                       }
+                       /* Record token length and type */
+                       pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
+                       pToken->nType = JX9_TK_DSTR;
+                       /* Jump the trailing quote */
+                       pStream->zText++;
+                       return SXRET_OK;
+                                 }
+               case ':':
+                       pToken->nType = JX9_TK_COLON; /* Single colon */
+                       break;
+               case ',': pToken->nType |= JX9_TK_COMMA;  break; /* Comma is also an operator */
+               case ';': pToken->nType = JX9_TK_SEMI;   break;
+                       /* Handle combined operators [i.e: +=, ===, !=== ...] */
+               case '=':
+                       pToken->nType |= JX9_TK_EQUAL;
+                       if( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '=' ){
+                                       pToken->nType &= ~JX9_TK_EQUAL;
+                                       /* Current operator: == */
+                                       pStream->zText++;
+                                       if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
+                                               /* Current operator: === */
+                                               pStream->zText++;
+                                       }
+                               }
+                       }
+                       break;
+               case '!':
+                       if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
+                               /* Current operator: != */
+                               pStream->zText++;
+                               if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
+                                       /* Current operator: !== */
+                                       pStream->zText++;
+                               }
+                       }
+                       break;
+               case '&':
+                       pToken->nType |= JX9_TK_AMPER;
+                       if( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '&' ){
+                                       pToken->nType &= ~JX9_TK_AMPER;
+                                       /* Current operator: && */
+                                       pStream->zText++;
+                               }else if( pStream->zText[0] == '=' ){
+                                       pToken->nType &= ~JX9_TK_AMPER;
+                                       /* Current operator: &= */
+                                       pStream->zText++;
+                               }
+                       }
+               case '.':
+                       if( pStream->zText < pStream->zEnd && (pStream->zText[0] == '.' || pStream->zText[0] == '=') ){
+                               /* Concatenation operator: '..' or '.='  */
+                               pStream->zText++;
+                       }
+                       break;
+               case '|':
+                       if( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '|' ){
+                                       /* Current operator: || */
+                                       pStream->zText++;
+                               }else if( pStream->zText[0] == '=' ){
+                                       /* Current operator: |= */
+                                       pStream->zText++;
+                               }
+                       }
+                       break;
+               case '+':
+                       if( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '+' ){
+                                       /* Current operator: ++ */
+                                       pStream->zText++;
+                               }else if( pStream->zText[0] == '=' ){
+                                       /* Current operator: += */
+                                       pStream->zText++;
+                               }
+                       }
+                       break;
+               case '-':
+                       if( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '-' ){
+                                       /* Current operator: -- */
+                                       pStream->zText++;
+                               }else if( pStream->zText[0] == '=' ){
+                                       /* Current operator: -= */
+                                       pStream->zText++;
+                               }else if( pStream->zText[0] == '>' ){
+                                       /* Current operator: -> */
+                                       pStream->zText++;
+                               }
+                       }
+                       break;
+               case '*':
+                       if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
+                               /* Current operator: *= */
+                               pStream->zText++;
+                       }
+                       break;
+               case '/':
+                       if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
+                               /* Current operator: /= */
+                               pStream->zText++;
+                       }
+                       break;
+               case '%':
+                       if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
+                               /* Current operator: %= */
+                               pStream->zText++;
+                       }
+                       break;
+               case '^':
+                       if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
+                               /* Current operator: ^= */
+                               pStream->zText++;
+                       }
+                       break;
+               case '<':
+                       if( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '<' ){
+                                       /* Current operator: << */
+                                       pStream->zText++;
+                                       if( pStream->zText < pStream->zEnd ){
+                                               if( pStream->zText[0] == '=' ){
+                                                       /* Current operator: <<= */
+                                                       pStream->zText++;
+                                               }else if( pStream->zText[0] == '<' ){
+                                                       /* Current Token: <<<  */
+                                                       pStream->zText++;
+                                                       /* This may be the beginning of a Heredoc/Nowdoc string, try to delimit it */
+                                                       rc = LexExtractNowdoc(&(*pStream), &(*pToken));
+                                                       if( rc == SXRET_OK ){
+                                                               /* Here/Now doc successfuly extracted */
+                                                               return SXRET_OK;
+                                                       }
+                                               }
+                                       }
+                               }else if( pStream->zText[0] == '>' ){
+                                       /* Current operator: <> */
+                                       pStream->zText++;
+                               }else if( pStream->zText[0] == '=' ){
+                                       /* Current operator: <= */
+                                       pStream->zText++;
+                               }
+                       }
+                       break;
+               case '>':
+                       if( pStream->zText < pStream->zEnd ){
+                               if( pStream->zText[0] == '>' ){
+                                       /* Current operator: >> */
+                                       pStream->zText++;
+                                       if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
+                                               /* Current operator: >>= */
+                                               pStream->zText++;
+                                       }
+                               }else if( pStream->zText[0] == '=' ){
+                                       /* Current operator: >= */
+                                       pStream->zText++;
+                               }
+                       }
+                       break;
+               default:
+                       break;
+               }
+               if( pStr->nByte <= 0 ){
+                       /* Record token length */
+                       pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
+               }
+               if( pToken->nType & JX9_TK_OP ){
+                       const jx9_expr_op *pOp;
+                       /* Check if the extracted token is an operator */
+                       pOp = jx9ExprExtractOperator(pStr, (SyToken *)SySetPeek(pStream->pSet));
+                       if( pOp == 0 ){
+                               /* Not an operator */
+                               pToken->nType &= ~JX9_TK_OP;
+                               if( pToken->nType <= 0 ){
+                                       pToken->nType = JX9_TK_OTHER;
+                               }
+                       }else{
+                               /* Save the instance associated with this operator for later processing */
+                               pToken->pUserData = (void *)pOp;
+                       }
+               }
+       }
+       /* Tell the upper-layer to save the extracted token for later processing */
+       return SXRET_OK;
+}
+/***** This file contains automatically generated code ******
+**
+** The code in this file has been automatically generated by
+**
+**     $Header: /sqlite/sqlite/tool/mkkeywordhash.c,v 1.38 2011/12/21 01:00:46 <chm@symisc.net> $
+**
+** The code in this file implements a function that determines whether
+** or not a given identifier is really a JX9 keyword.  The same thing
+** might be implemented more directly using a hand-written hash table.
+** But by using this automatically generated code, the size of the code
+** is substantially reduced.  This is important for embedded applications
+** on platforms with limited memory.
+*/
+/* Hash score: 35 */
+static sxu32 keywordCode(const char *z, int n)
+{
+  /* zText[] encodes 188 bytes of keywords in 128 bytes */
+  /*   printegereturnconstaticaselseifloatincludefaultDIEXITcontinue      */
+  /*   diewhileASPRINTbooleanbreakforeachfunctionimportstringswitch       */
+  /*   uplink                                                             */
+  static const char zText[127] = {
+    'p','r','i','n','t','e','g','e','r','e','t','u','r','n','c','o','n','s',
+    't','a','t','i','c','a','s','e','l','s','e','i','f','l','o','a','t','i',
+    'n','c','l','u','d','e','f','a','u','l','t','D','I','E','X','I','T','c',
+    'o','n','t','i','n','u','e','d','i','e','w','h','i','l','e','A','S','P',
+    'R','I','N','T','b','o','o','l','e','a','n','b','r','e','a','k','f','o',
+    'r','e','a','c','h','f','u','n','c','t','i','o','n','i','m','p','o','r',
+    't','s','t','r','i','n','g','s','w','i','t','c','h','u','p','l','i','n',
+    'k',
+  };
+  static const unsigned char aHash[59] = {
+       0,   0,   0,   0,  15,   0,  30,   0,   0,   2,  19,  18,   0,
+       0,  10,   3,  12,   0,  28,  29,  23,   0,  13,  22,   0,   0,
+      14,  24,  25,  31,  11,   0,   0,   0,   0,   1,   5,   0,   0,
+      20,   0,  27,   9,   0,   0,   0,   8,   0,   0,  26,   6,   0,
+       0,  17,   0,   0,   0,   0,   0,
+  };
+  static const unsigned char aNext[31] = {
+       0,   0,   0,   0,   0,   0,   0,   0,   0,   4,   0,   0,   0,
+       0,   0,   0,   0,   0,   0,   0,   0,   0,  16,   0,  21,   7,
+       0,   0,   0,   0,   0,
+  };
+  static const unsigned char aLen[31] = {
+       5,   7,   3,   6,   5,   6,   4,   2,   6,   4,   2,   5,   7,
+       7,   3,   4,   8,   3,   5,   2,   5,   4,   7,   5,   3,   7,
+       8,   6,   6,   6,   6,
+  };
+  static const sxu16 aOffset[31] = {
+       0,   2,   2,   8,  14,  17,  22,  23,  25,  25,  29,  30,  35,
+      40,  47,  49,  53,  61,  64,  69,  71,  76,  76,  83,  88,  88,
+      95, 103, 109, 115, 121,
+  };
+  static const sxu32 aCode[31] = {
+    JX9_TKWRD_PRINT,   JX9_TKWRD_INT,      JX9_TKWRD_INT,     JX9_TKWRD_RETURN,   JX9_TKWRD_CONST, 
+    JX9_TKWRD_STATIC,  JX9_TKWRD_CASE,     JX9_TKWRD_AS,      JX9_TKWRD_ELIF,     JX9_TKWRD_ELSE,
+    JX9_TKWRD_IF,      JX9_TKWRD_FLOAT,    JX9_TKWRD_INCLUDE, JX9_TKWRD_DEFAULT,  JX9_TKWRD_DIE, 
+    JX9_TKWRD_EXIT,    JX9_TKWRD_CONTINUE, JX9_TKWRD_DIE,     JX9_TKWRD_WHILE,    JX9_TKWRD_AS,  
+    JX9_TKWRD_PRINT,   JX9_TKWRD_BOOL,     JX9_TKWRD_BOOL,    JX9_TKWRD_BREAK,    JX9_TKWRD_FOR, 
+    JX9_TKWRD_FOREACH, JX9_TKWRD_FUNCTION, JX9_TKWRD_IMPORT,  JX9_TKWRD_STRING,  JX9_TKWRD_SWITCH,  
+    JX9_TKWRD_UPLINK,  
+  };
+  int h, i;
+  if( n<2 ) return JX9_TK_ID;
+  h = (((int)z[0]*4) ^ ((int)z[n-1]*3) ^ n) % 59;
+  for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
+    if( (int)aLen[i]==n && SyMemcmp(&zText[aOffset[i]],z,n)==0 ){
+       /* JX9_TKWRD_PRINT */
+       /* JX9_TKWRD_INT */
+       /* JX9_TKWRD_INT */
+       /* JX9_TKWRD_RETURN */
+       /* JX9_TKWRD_CONST */
+       /* JX9_TKWRD_STATIC */
+       /* JX9_TKWRD_CASE */
+       /* JX9_TKWRD_AS */
+       /* JX9_TKWRD_ELIF */
+       /* JX9_TKWRD_ELSE */
+       /* JX9_TKWRD_IF */
+       /* JX9_TKWRD_FLOAT */
+       /* JX9_TKWRD_INCLUDE */
+       /* JX9_TKWRD_DEFAULT */
+       /* JX9_TKWRD_DIE */
+       /* JX9_TKWRD_EXIT */
+       /* JX9_TKWRD_CONTINUE */
+       /* JX9_TKWRD_DIE */
+       /* JX9_TKWRD_WHILE */
+       /* JX9_TKWRD_AS */
+       /* JX9_TKWRD_PRINT */
+       /* JX9_TKWRD_BOOL */
+       /* JX9_TKWRD_BOOL */
+       /* JX9_TKWRD_BREAK */
+       /* JX9_TKWRD_FOR */
+       /* JX9_TKWRD_FOREACH */
+       /* JX9_TKWRD_FUNCTION */
+       /* JX9_TKWRD_IMPORT */
+       /* JX9_TKWRD_STRING */
+       /* JX9_TKWRD_SWITCH */
+       /* JX9_TKWRD_UPLINK */
+      return aCode[i];
+    }
+  }
+  return JX9_TK_ID;
+}
+/*
+ * Extract a heredoc/nowdoc text from a raw JX9 input.
+ * According to the JX9 language reference manual:
+ *  A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
+ *  is provided, then a newline. The string itself follows, and then the same identifier again
+ *  to close the quotation.
+ *  The closing identifier must begin in the first column of the line. Also, the identifier must 
+ *  follow the same naming rules as any other label in JX9: it must contain only alphanumeric 
+ *  characters and underscores, and must start with a non-digit character or underscore. 
+ *  Heredoc text behaves just like a double-quoted string, without the double quotes.
+ *  This means that quotes in a heredoc do not need to be escaped, but the escape codes listed
+ *  above can still be used. Variables are expanded, but the same care must be taken when expressing
+ *  complex variables inside a heredoc as with strings. 
+ *  Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
+ *  A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
+ *  The construct is ideal for embedding JX9 code or other large blocks of text without the need
+ *  for escaping. It shares some features in common with the SGML <![CDATA[ ]]> construct, in that
+ *  it declares a block of text which is not for parsing.
+ *  A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier which follows
+ *  is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc identifiers also apply to nowdoc
+ *  identifiers, especially those regarding the appearance of the closing identifier.
+ */
+static sxi32 LexExtractNowdoc(SyStream *pStream, SyToken *pToken)
+{
+       const unsigned char *zIn  = pStream->zText;
+       const unsigned char *zEnd = pStream->zEnd;
+       const unsigned char *zPtr;
+       SyString sDelim;
+       SyString sStr;
+       /* Jump leading white spaces */
+       while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
+               zIn++;
+       }
+       if( zIn >= zEnd ){
+               /* A simple symbol, return immediately */
+               return SXERR_CONTINUE;
+       }
+       if( zIn[0] == '\'' || zIn[0] == '"' ){
+               zIn++;
+       }
+       if( zIn[0] < 0xc0 && !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){
+               /* Invalid delimiter, return immediately */
+               return SXERR_CONTINUE;
+       }
+       /* Isolate the identifier */
+       sDelim.zString = (const char *)zIn;
+       for(;;){
+               zPtr = zIn;
+               /* Skip alphanumeric stream */
+               while( zPtr < zEnd && zPtr[0] < 0xc0 && (SyisAlphaNum(zPtr[0]) || zPtr[0] == '_') ){
+                       zPtr++;
+               }
+               if( zPtr < zEnd && zPtr[0] >= 0xc0 ){
+                       zPtr++;
+                       /* UTF-8 stream */
+                       while( zPtr < zEnd && ((zPtr[0] & 0xc0) == 0x80) ){
+                               zPtr++;
+                       }
+               }
+               if( zPtr == zIn ){
+                       /* Not an UTF-8 or alphanumeric stream */
+                       break;
+               }
+               /* Synchronize pointers */
+               zIn = zPtr;
+       }
+       /* Get the identifier length */
+       sDelim.nByte = (sxu32)((const char *)zIn-sDelim.zString);
+       if( zIn[0] == '"' || zIn[0] == '\'' ){
+               /* Jump the trailing single quote */
+               zIn++;
+       }
+       /* Jump trailing white spaces */
+       while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
+               zIn++;
+       }
+       if( sDelim.nByte <= 0 || zIn >= zEnd || zIn[0] != '\n' ){
+               /* Invalid syntax */
+               return SXERR_CONTINUE;
+       }
+       pStream->nLine++; /* Increment line counter */
+       zIn++;
+       /* Isolate the delimited string */
+       sStr.zString = (const char *)zIn;
+       /* Go and found the closing delimiter */
+       for(;;){
+               /* Synchronize with the next line */
+               while( zIn < zEnd && zIn[0] != '\n' ){
+                       zIn++;
+               }
+               if( zIn >= zEnd ){
+                       /* End of the input reached, break immediately */
+                       pStream->zText = pStream->zEnd;
+                       break;
+               }
+               pStream->nLine++; /* Increment line counter */
+               zIn++;
+               if( (sxu32)(zEnd - zIn) >= sDelim.nByte && SyMemcmp((const void *)sDelim.zString, (const void *)zIn, sDelim.nByte) == 0 ){
+                       zPtr = &zIn[sDelim.nByte];
+                       while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
+                               zPtr++;
+                       }
+                       if( zPtr >= zEnd ){
+                               /* End of input */
+                               pStream->zText = zPtr;
+                               break;
+                       }
+                       if( zPtr[0] == ';' ){
+                               const unsigned char *zCur = zPtr;
+                               zPtr++;
+                               while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
+                                       zPtr++;
+                               }
+                               if( zPtr >= zEnd || zPtr[0] == '\n' ){
+                                       /* Closing delimiter found, break immediately */
+                                       pStream->zText = zCur; /* Keep the semi-colon */
+                                       break;
+                               }
+                       }else if( zPtr[0] == '\n' ){
+                               /* Closing delimiter found, break immediately */
+                               pStream->zText = zPtr; /* Synchronize with the stream cursor */
+                               break;
+                       }
+                       /* Synchronize pointers and continue searching */
+                       zIn = zPtr;
+               }
+       } /* For(;;) */
+       /* Get the delimited string length */
+       sStr.nByte = (sxu32)((const char *)zIn-sStr.zString);
+       /* Record token type and length */
+       pToken->nType = JX9_TK_NOWDOC;
+       SyStringDupPtr(&pToken->sData, &sStr);
+       /* Remove trailing white spaces */
+       SyStringRightTrim(&pToken->sData);
+       /* All done */
+       return SXRET_OK;
+}
+/*
+ * Tokenize a raw jx9 input.
+ * This is the public tokenizer called by most code generator routines. 
+ */
+JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput,sxu32 nLen,SySet *pOut)
+{
+       SyLex sLexer;
+       sxi32 rc;
+       /* Initialize the lexer */
+       rc = SyLexInit(&sLexer, &(*pOut),jx9TokenizeInput,0);
+       if( rc != SXRET_OK ){
+               return rc;
+       }
+       /* Tokenize input */
+       rc = SyLexTokenizeInput(&sLexer, zInput, nLen, 0, 0, 0);
+       /* Release the lexer */
+       SyLexRelease(&sLexer);
+       /* Tokenization result */
+       return rc;
+}
+
+/*
+ * ----------------------------------------------------------
+ * File: jx9_lib.c
+ * MD5: a684fb6677b1ab0110d03536f1280c50
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: lib.c v5.1 Win7 2012-08-08 04:19 stable <chm@symisc.net> $ */
+/*
+ * Symisc Run-Time API: A modern thread safe replacement of the standard libc
+ * Copyright (C) Symisc Systems 2007-2012, http://www.symisc.net/
+ *
+ * The Symisc Run-Time API is an independent project developed by symisc systems
+ * internally as a secure replacement of the standard libc.
+ * The library is re-entrant, thread-safe and platform independent.
+ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+#if defined(__WINNT__)
+#include <Windows.h>
+#else
+#include <stdlib.h>
+#endif
+#if defined(JX9_ENABLE_THREADS)
+/* SyRunTimeApi: sxmutex.c */
+#if defined(__WINNT__)
+struct SyMutex
+{
+       CRITICAL_SECTION sMutex;
+       sxu32 nType; /* Mutex type, one of SXMUTEX_TYPE_* */
+};
+/* Preallocated static mutex */
+static SyMutex aStaticMutexes[] = {
+               {{0}, SXMUTEX_TYPE_STATIC_1}, 
+               {{0}, SXMUTEX_TYPE_STATIC_2}, 
+               {{0}, SXMUTEX_TYPE_STATIC_3}, 
+               {{0}, SXMUTEX_TYPE_STATIC_4}, 
+               {{0}, SXMUTEX_TYPE_STATIC_5}, 
+               {{0}, SXMUTEX_TYPE_STATIC_6}
+};
+static BOOL winMutexInit = FALSE;
+static LONG winMutexLock = 0;
+
+static sxi32 WinMutexGlobaInit(void)
+{
+       LONG rc;
+       rc = InterlockedCompareExchange(&winMutexLock, 1, 0);
+       if ( rc == 0 ){
+               sxu32 n;
+               for( n = 0 ; n  < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
+                       InitializeCriticalSection(&aStaticMutexes[n].sMutex);
+               }
+               winMutexInit = TRUE;
+       }else{
+               /* Someone else is doing this for us */
+               while( winMutexInit == FALSE ){
+                       Sleep(1);
+               }
+       }
+       return SXRET_OK;
+}
+static void WinMutexGlobalRelease(void)
+{
+       LONG rc;
+       rc = InterlockedCompareExchange(&winMutexLock, 0, 1);
+       if( rc == 1 ){
+               /* The first to decrement to zero does the actual global release */
+               if( winMutexInit == TRUE ){
+                       sxu32 n;
+                       for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
+                               DeleteCriticalSection(&aStaticMutexes[n].sMutex);
+                       }
+                       winMutexInit = FALSE;
+               }
+       }
+}
+static SyMutex * WinMutexNew(int nType)
+{
+       SyMutex *pMutex = 0;
+       if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
+               /* Allocate a new mutex */
+               pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(), 0, sizeof(SyMutex));
+               if( pMutex == 0 ){
+                       return 0;
+               }
+               InitializeCriticalSection(&pMutex->sMutex);
+       }else{
+               /* Use a pre-allocated static mutex */
+               if( nType > SXMUTEX_TYPE_STATIC_6 ){
+                       nType = SXMUTEX_TYPE_STATIC_6;
+               }
+               pMutex = &aStaticMutexes[nType - 3];
+       }
+       pMutex->nType = nType;
+       return pMutex;
+}
+static void WinMutexRelease(SyMutex *pMutex)
+{
+       if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
+               DeleteCriticalSection(&pMutex->sMutex);
+               HeapFree(GetProcessHeap(), 0, pMutex);
+       }
+}
+static void WinMutexEnter(SyMutex *pMutex)
+{
+       EnterCriticalSection(&pMutex->sMutex);
+}
+static sxi32 WinMutexTryEnter(SyMutex *pMutex)
+{
+#ifdef _WIN32_WINNT
+       BOOL rc;
+       /* Only WindowsNT platforms */
+       rc = TryEnterCriticalSection(&pMutex->sMutex);
+       if( rc ){
+               return SXRET_OK;
+       }else{
+               return SXERR_BUSY;
+       }
+#else
+       return SXERR_NOTIMPLEMENTED;
+#endif
+}
+static void WinMutexLeave(SyMutex *pMutex)
+{
+       LeaveCriticalSection(&pMutex->sMutex);
+}
+/* Export Windows mutex interfaces */
+static const SyMutexMethods sWinMutexMethods = {
+       WinMutexGlobaInit,  /* xGlobalInit() */
+       WinMutexGlobalRelease, /* xGlobalRelease() */
+       WinMutexNew,     /* xNew() */
+       WinMutexRelease, /* xRelease() */
+       WinMutexEnter,   /* xEnter() */
+       WinMutexTryEnter, /* xTryEnter() */
+       WinMutexLeave     /* xLeave() */
+};
+JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
+{
+       return &sWinMutexMethods;
+}
+#elif defined(__UNIXES__)
+#include <pthread.h>
+struct SyMutex
+{
+       pthread_mutex_t sMutex;
+       sxu32 nType;
+};
+static SyMutex * UnixMutexNew(int nType)
+{
+       static SyMutex aStaticMutexes[] = {
+               {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1}, 
+               {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2}, 
+               {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3}, 
+               {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4}, 
+               {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5}, 
+               {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6}
+       };
+       SyMutex *pMutex;
+       
+       if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
+               pthread_mutexattr_t sRecursiveAttr;
+               /* Allocate a new mutex */
+               pMutex = (SyMutex *)malloc(sizeof(SyMutex));
+               if( pMutex == 0 ){
+                       return 0;
+               }
+               if( nType == SXMUTEX_TYPE_RECURSIVE ){
+                       pthread_mutexattr_init(&sRecursiveAttr);
+                       pthread_mutexattr_settype(&sRecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+               }
+               pthread_mutex_init(&pMutex->sMutex, nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0 );
+               if(     nType == SXMUTEX_TYPE_RECURSIVE ){
+                       pthread_mutexattr_destroy(&sRecursiveAttr);
+               }
+       }else{
+               /* Use a pre-allocated static mutex */
+               if( nType > SXMUTEX_TYPE_STATIC_6 ){
+                       nType = SXMUTEX_TYPE_STATIC_6;
+               }
+               pMutex = &aStaticMutexes[nType - 3];
+       }
+  pMutex->nType = nType;
+  
+  return pMutex;
+}
+static void UnixMutexRelease(SyMutex *pMutex)
+{
+       if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
+               pthread_mutex_destroy(&pMutex->sMutex);
+               free(pMutex);
+       }
+}
+static void UnixMutexEnter(SyMutex *pMutex)
+{
+       pthread_mutex_lock(&pMutex->sMutex);
+}
+static void UnixMutexLeave(SyMutex *pMutex)
+{
+       pthread_mutex_unlock(&pMutex->sMutex);
+}
+/* Export pthread mutex interfaces */
+static const SyMutexMethods sPthreadMutexMethods = {
+       0, /* xGlobalInit() */
+       0, /* xGlobalRelease() */
+       UnixMutexNew,      /* xNew() */
+       UnixMutexRelease,  /* xRelease() */
+       UnixMutexEnter,    /* xEnter() */
+       0,                 /* xTryEnter() */
+       UnixMutexLeave     /* xLeave() */
+};
+JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
+{
+       return &sPthreadMutexMethods;
+}
+#else
+/* Host application must register their own mutex subsystem if the target
+ * platform is not an UNIX-like or windows systems.
+ */
+struct SyMutex
+{
+       sxu32 nType;
+};
+static SyMutex * DummyMutexNew(int nType)
+{
+       static SyMutex sMutex;
+       SXUNUSED(nType);
+       return &sMutex;
+}
+static void DummyMutexRelease(SyMutex *pMutex)
+{
+       SXUNUSED(pMutex);
+}
+static void DummyMutexEnter(SyMutex *pMutex)
+{
+       SXUNUSED(pMutex);
+}
+static void DummyMutexLeave(SyMutex *pMutex)
+{
+       SXUNUSED(pMutex);
+}
+/* Export the dummy mutex interfaces */
+static const SyMutexMethods sDummyMutexMethods = {
+       0, /* xGlobalInit() */
+       0, /* xGlobalRelease() */
+       DummyMutexNew,      /* xNew() */
+       DummyMutexRelease,  /* xRelease() */
+       DummyMutexEnter,    /* xEnter() */
+       0,                  /* xTryEnter() */
+       DummyMutexLeave     /* xLeave() */
+};
+JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
+{
+       return &sDummyMutexMethods;
+}
+#endif /* __WINNT__ */
+#endif /* JX9_ENABLE_THREADS */
+static void * SyOSHeapAlloc(sxu32 nByte)
+{
+       void *pNew;
+#if defined(__WINNT__)
+       pNew = HeapAlloc(GetProcessHeap(), 0, nByte);
+#else
+       pNew = malloc((size_t)nByte);
+#endif
+       return pNew;
+}
+static void * SyOSHeapRealloc(void *pOld, sxu32 nByte)
+{
+       void *pNew;
+#if defined(__WINNT__)
+       pNew = HeapReAlloc(GetProcessHeap(), 0, pOld, nByte);
+#else
+       pNew = realloc(pOld, (size_t)nByte);
+#endif
+       return pNew;    
+}
+static void SyOSHeapFree(void *pPtr)
+{
+#if defined(__WINNT__)
+       HeapFree(GetProcessHeap(), 0, pPtr);
+#else
+       free(pPtr);
+#endif
+}
+/* SyRunTimeApi:sxstr.c */
+JX9_PRIVATE sxu32 SyStrlen(const char *zSrc)
+{
+       register const char *zIn = zSrc;
+#if defined(UNTRUST)
+       if( zIn == 0 ){
+               return 0;
+       }
+#endif
+       for(;;){
+               if( !zIn[0] ){ break; } zIn++;
+               if( !zIn[0] ){ break; } zIn++;
+               if( !zIn[0] ){ break; } zIn++;
+               if( !zIn[0] ){ break; } zIn++;  
+       }
+       return (sxu32)(zIn - zSrc);
+}
+JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
+{
+       const char *zIn = zStr;
+       const char *zEnd;
+       
+       zEnd = &zIn[nLen];
+       for(;;){
+               if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
+               if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
+               if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
+               if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
+       }
+       return SXERR_NOTFOUND;
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
+{
+       const char *zIn = zStr;
+       const char *zEnd;
+       
+       zEnd = &zIn[nLen - 1];
+       for( ;; ){
+               if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
+               if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
+               if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
+               if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos =  (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
+       }
+       return SXERR_NOTFOUND; 
+}
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos)
+{
+       const char *zIn = zSrc;
+       const char *zPtr;
+       const char *zEnd;
+       sxi32 c;
+       zEnd = &zSrc[nLen];
+       for(;;){
+               if( zIn >= zEnd ){ break; }     for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++;
+               if( zIn >= zEnd ){ break; }     for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++;
+               if( zIn >= zEnd ){ break; }     for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++;
+               if( zIn >= zEnd ){ break; }     for(zPtr = zList ; (c = zPtr[0]) != 0 ; zPtr++ ){ if( zIn[0] == c ){ if( pFirstPos ){ *pFirstPos = (sxu32)(zIn - zSrc); } return SXRET_OK; } } zIn++;
+       }       
+       return SXERR_NOTFOUND; 
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen)
+{
+       const unsigned char *zP = (const unsigned char *)zLeft;
+       const unsigned char *zQ = (const unsigned char *)zRight;
+
+       if( SX_EMPTY_STR(zP) || SX_EMPTY_STR(zQ)  ){
+                       return SX_EMPTY_STR(zP) ? (SX_EMPTY_STR(zQ) ? 0 : -1) :1;
+       }
+       if( nLen <= 0 ){
+               return 0;
+       }
+       for(;;){
+               if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
+               if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
+               if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
+               if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
+       }
+       return (sxi32)(zP[0] - zQ[0]);
+}      
+#endif
+JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen)
+{
+       register unsigned char *p = (unsigned char *)zLeft;
+       register unsigned char *q = (unsigned char *)zRight;
+       
+       if( SX_EMPTY_STR(p) || SX_EMPTY_STR(q) ){
+               return SX_EMPTY_STR(p)? SX_EMPTY_STR(q) ? 0 : -1 :1;
+       }
+       for(;;){
+               if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
+               if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
+               if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
+               if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
+               
+       }
+       return (sxi32)(SyCharToLower(p[0]) - SyCharToLower(q[0]));
+}
+JX9_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen)
+{
+       unsigned char *zBuf = (unsigned char *)zDest;
+       unsigned char *zIn = (unsigned char *)zSrc;
+       unsigned char *zEnd;
+#if defined(UNTRUST)
+       if( zSrc == (const char *)zDest ){
+                       return 0;
+       }
+#endif
+       if( nLen <= 0 ){
+               nLen = SyStrlen(zSrc);
+       }
+       zEnd = &zBuf[nDestLen - 1]; /* reserve a room for the null terminator */
+       for(;;){
+               if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
+               if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
+               if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
+               if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
+       }
+       zBuf[0] = 0;
+       return (sxu32)(zBuf-(unsigned char *)zDest);
+}
+/* SyRunTimeApi:sxmem.c */
+JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize)
+{
+       register unsigned char *zSrc = (unsigned char *)pSrc;
+       unsigned char *zEnd;
+#if defined(UNTRUST)
+       if( zSrc == 0 || nSize <= 0 ){
+               return ;
+       }
+#endif
+       zEnd = &zSrc[nSize];
+       for(;;){
+               if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
+               if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
+               if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
+               if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
+       }
+}
+JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize)
+{
+       sxi32 rc;
+       if( nSize <= 0 ){
+               return 0;
+       }
+       if( pB1 == 0 || pB2 == 0 ){
+               return pB1 != 0 ? 1 : (pB2 == 0 ? 0 : -1);
+       }
+       SX_MACRO_FAST_CMP(pB1, pB2, nSize, rc);
+       return rc;
+}
+JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen)
+{
+       if( pSrc == 0 || pDest == 0 ){
+               return 0;
+       }
+       if( pSrc == (const void *)pDest ){
+               return nLen;
+       }
+       SX_MACRO_FAST_MEMCPY(pSrc, pDest, nLen);
+       return nLen;
+}
+static void * MemOSAlloc(sxu32 nBytes)
+{
+       sxu32 *pChunk;
+       pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + sizeof(sxu32));
+       if( pChunk == 0 ){
+               return 0;
+       }
+       pChunk[0] = nBytes;
+       return (void *)&pChunk[1];
+}
+static void * MemOSRealloc(void *pOld, sxu32 nBytes)
+{
+       sxu32 *pOldChunk;
+       sxu32 *pChunk;
+       pOldChunk = (sxu32 *)(((char *)pOld)-sizeof(sxu32));
+       if( pOldChunk[0] >= nBytes ){
+               return pOld;
+       }
+       pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk, nBytes + sizeof(sxu32));
+       if( pChunk == 0 ){
+               return 0;
+       }
+       pChunk[0] = nBytes;
+       return (void *)&pChunk[1];
+}
+static void MemOSFree(void *pBlock)
+{
+       void *pChunk;
+       pChunk = (void *)(((char *)pBlock)-sizeof(sxu32));
+       SyOSHeapFree(pChunk);
+}
+static sxu32 MemOSChunkSize(void *pBlock)
+{
+       sxu32 *pChunk;
+       pChunk = (sxu32 *)(((char *)pBlock)-sizeof(sxu32));
+       return pChunk[0];
+}
+/* Export OS allocation methods */
+static const SyMemMethods sOSAllocMethods = {
+       MemOSAlloc, 
+       MemOSRealloc, 
+       MemOSFree, 
+       MemOSChunkSize, 
+       0, 
+       0, 
+       0
+};
+static void * MemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
+{
+       SyMemBlock *pBlock;
+       sxi32 nRetry = 0;
+
+       /* Append an extra block so we can tracks allocated chunks and avoid memory
+        * leaks.
+        */
+       nByte += sizeof(SyMemBlock);
+       for(;;){
+               pBlock = (SyMemBlock *)pBackend->pMethods->xAlloc(nByte);
+               if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY 
+                       || SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
+                               break;
+               }
+               nRetry++;
+       }
+       if( pBlock  == 0 ){
+               return 0;
+       }
+       pBlock->pNext = pBlock->pPrev = 0;
+       /* Link to the list of already tracked blocks */
+       MACRO_LD_PUSH(pBackend->pBlocks, pBlock);
+#if defined(UNTRUST)
+       pBlock->nGuard = SXMEM_BACKEND_MAGIC;
+#endif
+       pBackend->nBlock++;
+       return (void *)&pBlock[1];
+}
+JX9_PRIVATE void * SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
+{
+       void *pChunk;
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend) ){
+               return 0;
+       }
+#endif
+       if( pBackend->pMutexMethods ){
+               SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       pChunk = MemBackendAlloc(&(*pBackend), nByte);
+       if( pBackend->pMutexMethods ){
+               SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       return pChunk;
+}
+static void * MemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
+{
+       SyMemBlock *pBlock, *pNew, *pPrev, *pNext;
+       sxu32 nRetry = 0;
+
+       if( pOld == 0 ){
+               return MemBackendAlloc(&(*pBackend), nByte);
+       }
+       pBlock = (SyMemBlock *)(((char *)pOld) - sizeof(SyMemBlock));
+#if defined(UNTRUST)
+       if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
+               return 0;
+       }
+#endif
+       nByte += sizeof(SyMemBlock);
+       pPrev = pBlock->pPrev;
+       pNext = pBlock->pNext;
+       for(;;){
+               pNew = (SyMemBlock *)pBackend->pMethods->xRealloc(pBlock, nByte);
+               if( pNew != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY ||
+                       SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
+                               break;
+               }
+               nRetry++;
+       }
+       if( pNew == 0 ){
+               return 0;
+       }
+       if( pNew != pBlock ){
+               if( pPrev == 0 ){
+                       pBackend->pBlocks = pNew;
+               }else{
+                       pPrev->pNext = pNew;
+               }
+               if( pNext ){
+                       pNext->pPrev = pNew;
+               }
+#if defined(UNTRUST)
+               pNew->nGuard = SXMEM_BACKEND_MAGIC;
+#endif
+       }
+       return (void *)&pNew[1];
+}
+JX9_PRIVATE void * SyMemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
+{
+       void *pChunk;
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend)  ){
+               return 0;
+       }
+#endif
+       if( pBackend->pMutexMethods ){
+               SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       pChunk = MemBackendRealloc(&(*pBackend), pOld, nByte);
+       if( pBackend->pMutexMethods ){
+               SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       return pChunk;
+}
+static sxi32 MemBackendFree(SyMemBackend *pBackend, void * pChunk)
+{
+       SyMemBlock *pBlock;
+       pBlock = (SyMemBlock *)(((char *)pChunk) - sizeof(SyMemBlock));
+#if defined(UNTRUST)
+       if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       /* Unlink from the list of active blocks */
+       if( pBackend->nBlock > 0 ){
+               /* Release the block */
+#if defined(UNTRUST)
+               /* Mark as stale block */
+               pBlock->nGuard = 0x635B;
+#endif
+               MACRO_LD_REMOVE(pBackend->pBlocks, pBlock);
+               pBackend->nBlock--;
+               pBackend->pMethods->xFree(pBlock);
+       }
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void * pChunk)
+{
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend) ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       if( pChunk == 0 ){
+               return SXRET_OK;
+       }
+       if( pBackend->pMutexMethods ){
+               SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       rc = MemBackendFree(&(*pBackend), pChunk);
+       if( pBackend->pMutexMethods ){
+               SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       return rc;
+}
+#if defined(JX9_ENABLE_THREADS)
+JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods)
+{
+       SyMutex *pMutex;
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend) || pMethods == 0 || pMethods->xNew == 0){
+               return SXERR_CORRUPT;
+       }
+#endif
+       pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
+       if( pMutex == 0 ){
+               return SXERR_OS;
+       }
+       /* Attach the mutex to the memory backend */
+       pBackend->pMutex = pMutex;
+       pBackend->pMutexMethods = pMethods;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend)
+{
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend) ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       if( pBackend->pMutex == 0 ){
+               /* There is no mutex subsystem at all */
+               return SXRET_OK;
+       }
+       SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
+       pBackend->pMutexMethods = 0;
+       pBackend->pMutex = 0; 
+       return SXRET_OK;
+}
+#endif
+/*
+ * Memory pool allocator
+ */
+#define SXMEM_POOL_MAGIC               0xDEAD
+#define SXMEM_POOL_MAXALLOC            (1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR)) 
+#define SXMEM_POOL_MINALLOC            (1<<(SXMEM_POOL_INCR))
+static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend, sxu32 nBucket)
+{
+       char *zBucket, *zBucketEnd;
+       SyMemHeader *pHeader;
+       sxu32 nBucketSize;
+       
+       /* Allocate one big block first */
+       zBucket = (char *)MemBackendAlloc(&(*pBackend), SXMEM_POOL_MAXALLOC);
+       if( zBucket == 0 ){
+               return SXERR_MEM;
+       }
+       zBucketEnd = &zBucket[SXMEM_POOL_MAXALLOC];
+       /* Divide the big block into mini bucket pool */
+       nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
+       pBackend->apPool[nBucket] = pHeader = (SyMemHeader *)zBucket;
+       for(;;){
+               if( &zBucket[nBucketSize] >= zBucketEnd ){
+                       break;
+               }
+               pHeader->pNext = (SyMemHeader *)&zBucket[nBucketSize];
+               /* Advance the cursor to the next available chunk */
+               pHeader = pHeader->pNext;
+               zBucket += nBucketSize; 
+       }
+       pHeader->pNext = 0;
+       
+       return SXRET_OK;
+}
+static void * MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
+{
+       SyMemHeader *pBucket, *pNext;
+       sxu32 nBucketSize;
+       sxu32 nBucket;
+
+       if( nByte + sizeof(SyMemHeader) >= SXMEM_POOL_MAXALLOC ){
+               /* Allocate a big chunk directly */
+               pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend), nByte+sizeof(SyMemHeader));
+               if( pBucket == 0 ){
+                       return 0;
+               }
+               /* Record as big block */
+               pBucket->nBucket = (sxu32)(SXMEM_POOL_MAGIC << 16) | SXU16_HIGH;
+               return (void *)(pBucket+1);
+       }
+       /* Locate the appropriate bucket */
+       nBucket = 0;
+       nBucketSize = SXMEM_POOL_MINALLOC;
+       while( nByte + sizeof(SyMemHeader) > nBucketSize  ){
+               nBucketSize <<= 1;
+               nBucket++;
+       }
+       pBucket = pBackend->apPool[nBucket];
+       if( pBucket == 0 ){
+               sxi32 rc;
+               rc = MemPoolBucketAlloc(&(*pBackend), nBucket);
+               if( rc != SXRET_OK ){
+                       return 0;
+               }
+               pBucket = pBackend->apPool[nBucket];
+       }
+       /* Remove from the free list */
+       pNext = pBucket->pNext;
+       pBackend->apPool[nBucket] = pNext;
+       /* Record bucket&magic number */
+       pBucket->nBucket = (SXMEM_POOL_MAGIC << 16) | nBucket;
+       return (void *)&pBucket[1];
+}
+JX9_PRIVATE void * SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
+{
+       void *pChunk;
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend) ){
+               return 0;
+       }
+#endif
+       if( pBackend->pMutexMethods ){
+               SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       pChunk = MemBackendPoolAlloc(&(*pBackend), nByte);
+       if( pBackend->pMutexMethods ){
+               SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       return pChunk;
+}
+static sxi32 MemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
+{
+       SyMemHeader *pHeader;
+       sxu32 nBucket;
+       /* Get the corresponding bucket */
+       pHeader = (SyMemHeader *)(((char *)pChunk) - sizeof(SyMemHeader));
+       /* Sanity check to avoid misuse */
+       if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
+               return SXERR_CORRUPT;
+       }
+       nBucket = pHeader->nBucket & 0xFFFF;
+       if( nBucket == SXU16_HIGH ){
+               /* Free the big block */
+               MemBackendFree(&(*pBackend), pHeader);
+       }else{
+               /* Return to the free list */
+               pHeader->pNext = pBackend->apPool[nBucket & 0x0f];
+               pBackend->apPool[nBucket & 0x0f] = pHeader;
+       }
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
+{
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend) || pChunk == 0 ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       if( pBackend->pMutexMethods ){
+               SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       rc = MemBackendPoolFree(&(*pBackend), pChunk);
+       if( pBackend->pMutexMethods ){
+               SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       return rc;
+}
+#if 0
+static void * MemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
+{
+       sxu32 nBucket, nBucketSize;
+       SyMemHeader *pHeader;
+       void * pNew;
+
+       if( pOld == 0 ){
+               /* Allocate a new pool */
+               pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
+               return pNew;
+       }
+       /* Get the corresponding bucket */
+       pHeader = (SyMemHeader *)(((char *)pOld) - sizeof(SyMemHeader));
+       /* Sanity check to avoid misuse */
+       if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
+               return 0;
+       }
+       nBucket = pHeader->nBucket & 0xFFFF;
+       if( nBucket == SXU16_HIGH ){
+               /* Big block */
+               return MemBackendRealloc(&(*pBackend), pHeader, nByte);
+       }
+       nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
+       if( nBucketSize >= nByte + sizeof(SyMemHeader) ){
+               /* The old bucket can honor the requested size */
+               return pOld;
+       }
+       /* Allocate a new pool */
+       pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
+       if( pNew == 0 ){
+               return 0;
+       }
+       /* Copy the old data into the new block */
+       SyMemcpy(pOld, pNew, nBucketSize);
+       /* Free the stale block */
+       MemBackendPoolFree(&(*pBackend), pOld);
+       return pNew;
+}
+JX9_PRIVATE void * SyMemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
+{
+       void *pChunk;
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend) ){
+               return 0;
+       }
+#endif
+       if( pBackend->pMutexMethods ){
+               SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       pChunk = MemBackendPoolRealloc(&(*pBackend), pOld, nByte);
+       if( pBackend->pMutexMethods ){
+               SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       return pChunk;
+}
+#endif
+JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void * pUserData)
+{
+#if defined(UNTRUST)
+       if( pBackend == 0 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       /* Zero the allocator first */
+       SyZero(&(*pBackend), sizeof(SyMemBackend));
+       pBackend->xMemError = xMemErr;
+       pBackend->pUserData = pUserData;
+       /* Switch to the OS memory allocator */
+       pBackend->pMethods = &sOSAllocMethods;
+       if( pBackend->pMethods->xInit ){
+               /* Initialize the backend  */
+               if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
+                       return SXERR_ABORT;
+               }
+       }
+#if defined(UNTRUST)
+       pBackend->nMagic = SXMEM_BACKEND_MAGIC;
+#endif
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void * pUserData)
+{
+#if defined(UNTRUST)
+       if( pBackend == 0 || pMethods == 0){
+               return SXERR_EMPTY;
+       }
+#endif
+       if( pMethods->xAlloc == 0 || pMethods->xRealloc == 0 || pMethods->xFree == 0 || pMethods->xChunkSize == 0 ){
+               /* mandatory methods are missing */
+               return SXERR_INVALID;
+       }
+       /* Zero the allocator first */
+       SyZero(&(*pBackend), sizeof(SyMemBackend));
+       pBackend->xMemError = xMemErr;
+       pBackend->pUserData = pUserData;
+       /* Switch to the host application memory allocator */
+       pBackend->pMethods = pMethods;
+       if( pBackend->pMethods->xInit ){
+               /* Initialize the backend  */
+               if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
+                       return SXERR_ABORT;
+               }
+       }
+#if defined(UNTRUST)
+       pBackend->nMagic = SXMEM_BACKEND_MAGIC;
+#endif
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend,const SyMemBackend *pParent)
+{
+       sxu8 bInheritMutex;
+#if defined(UNTRUST)
+       if( pBackend == 0 || SXMEM_BACKEND_CORRUPT(pParent) ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       /* Zero the allocator first */
+       SyZero(&(*pBackend), sizeof(SyMemBackend));
+       pBackend->pMethods  = pParent->pMethods;
+       pBackend->xMemError = pParent->xMemError;
+       pBackend->pUserData = pParent->pUserData;
+       bInheritMutex = pParent->pMutexMethods ? TRUE : FALSE;
+       if( bInheritMutex ){
+               pBackend->pMutexMethods = pParent->pMutexMethods;
+               /* Create a private mutex */
+               pBackend->pMutex = pBackend->pMutexMethods->xNew(SXMUTEX_TYPE_FAST);
+               if( pBackend->pMutex ==  0){
+                       return SXERR_OS;
+               }
+       }
+#if defined(UNTRUST)
+       pBackend->nMagic = SXMEM_BACKEND_MAGIC;
+#endif
+       return SXRET_OK;
+}
+static sxi32 MemBackendRelease(SyMemBackend *pBackend)
+{
+       SyMemBlock *pBlock, *pNext;
+
+       pBlock = pBackend->pBlocks;
+       for(;;){
+               if( pBackend->nBlock == 0 ){
+                       break;
+               }
+               pNext  = pBlock->pNext;
+               pBackend->pMethods->xFree(pBlock);
+               pBlock = pNext;
+               pBackend->nBlock--;
+               /* LOOP ONE */
+               if( pBackend->nBlock == 0 ){
+                       break;
+               }
+               pNext  = pBlock->pNext;
+               pBackend->pMethods->xFree(pBlock);
+               pBlock = pNext;
+               pBackend->nBlock--;
+               /* LOOP TWO */
+               if( pBackend->nBlock == 0 ){
+                       break;
+               }
+               pNext  = pBlock->pNext;
+               pBackend->pMethods->xFree(pBlock);
+               pBlock = pNext;
+               pBackend->nBlock--;
+               /* LOOP THREE */
+               if( pBackend->nBlock == 0 ){
+                       break;
+               }
+               pNext  = pBlock->pNext;
+               pBackend->pMethods->xFree(pBlock);
+               pBlock = pNext;
+               pBackend->nBlock--;
+               /* LOOP FOUR */
+       }
+       if( pBackend->pMethods->xRelease ){
+               pBackend->pMethods->xRelease(pBackend->pMethods->pUserData);
+       }
+       pBackend->pMethods = 0;
+       pBackend->pBlocks  = 0;
+#if defined(UNTRUST)
+       pBackend->nMagic = 0x2626;
+#endif
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend)
+{
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( SXMEM_BACKEND_CORRUPT(pBackend) ){
+               return SXERR_INVALID;
+       }
+#endif
+       if( pBackend->pMutexMethods ){
+               SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       rc = MemBackendRelease(&(*pBackend));
+       if( pBackend->pMutexMethods ){
+               SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
+               SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
+       }
+       return rc;
+}
+JX9_PRIVATE void * SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize)
+{
+       void *pNew;
+#if defined(UNTRUST)
+       if( pSrc == 0 || nSize <= 0 ){
+               return 0;
+       }
+#endif
+       pNew = SyMemBackendAlloc(&(*pBackend), nSize);
+       if( pNew ){
+               SyMemcpy(pSrc, pNew, nSize);
+       }
+       return pNew;
+}
+JX9_PRIVATE char * SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize)
+{
+       char *zDest;
+       zDest = (char *)SyMemBackendAlloc(&(*pBackend), nSize + 1);
+       if( zDest ){
+               Systrcpy(zDest, nSize+1, zSrc, nSize);
+       }
+       return zDest;
+}
+JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize)
+{
+#if defined(UNTRUST)
+       if( pBlob == 0 || pBuffer == 0 || nSize < 1 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       pBlob->pBlob = pBuffer;
+       pBlob->mByte = nSize;
+       pBlob->nByte = 0;
+       pBlob->pAllocator = 0;
+       pBlob->nFlags = SXBLOB_LOCKED|SXBLOB_STATIC;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator)
+{
+#if defined(UNTRUST)
+       if( pBlob == 0  ){
+               return SXERR_EMPTY;
+       }
+#endif
+       pBlob->pBlob = 0;
+       pBlob->mByte = pBlob->nByte     = 0;
+       pBlob->pAllocator = &(*pAllocator);
+       pBlob->nFlags = 0;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte)
+{
+#if defined(UNTRUST)
+       if( pBlob == 0  ){
+               return SXERR_EMPTY;
+       }
+#endif
+       pBlob->pBlob = (void *)pData;
+       pBlob->nByte = nByte;
+       pBlob->mByte = 0;
+       pBlob->nFlags |= SXBLOB_RDONLY;
+       return SXRET_OK;
+}
+#ifndef SXBLOB_MIN_GROWTH
+#define SXBLOB_MIN_GROWTH 16
+#endif
+static sxi32 BlobPrepareGrow(SyBlob *pBlob, sxu32 *pByte)
+{
+       sxu32 nByte;
+       void *pNew;
+       nByte = *pByte;
+       if( pBlob->nFlags & (SXBLOB_LOCKED|SXBLOB_STATIC) ){
+               if ( SyBlobFreeSpace(pBlob) < nByte ){
+                       *pByte = SyBlobFreeSpace(pBlob);
+                       if( (*pByte) == 0 ){
+                               return SXERR_SHORT;
+                       }
+               }
+               return SXRET_OK;
+       }
+       if( pBlob->nFlags & SXBLOB_RDONLY ){
+               /* Make a copy of the read-only item */
+               if( pBlob->nByte > 0 ){
+                       pNew = SyMemBackendDup(pBlob->pAllocator, pBlob->pBlob, pBlob->nByte);
+                       if( pNew == 0 ){
+                               return SXERR_MEM;
+                       }
+                       pBlob->pBlob = pNew;
+                       pBlob->mByte = pBlob->nByte;
+               }else{
+                       pBlob->pBlob = 0;
+                       pBlob->mByte = 0;
+               }
+               /* Remove the read-only flag */
+               pBlob->nFlags &= ~SXBLOB_RDONLY;
+       }
+       if( SyBlobFreeSpace(pBlob) >= nByte ){
+               return SXRET_OK;
+       }
+       if( pBlob->mByte > 0 ){
+               nByte = nByte + pBlob->mByte * 2 + SXBLOB_MIN_GROWTH;
+       }else if ( nByte < SXBLOB_MIN_GROWTH ){
+               nByte = SXBLOB_MIN_GROWTH;
+       }
+       pNew = SyMemBackendRealloc(pBlob->pAllocator, pBlob->pBlob, nByte);
+       if( pNew == 0 ){
+               return SXERR_MEM;
+       }
+       pBlob->pBlob = pNew;
+       pBlob->mByte = nByte;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize)
+{
+       sxu8 *zBlob;
+       sxi32 rc;
+       if( nSize < 1 ){
+               return SXRET_OK;
+       }
+       rc = BlobPrepareGrow(&(*pBlob), &nSize);
+       if( SXRET_OK != rc ){
+               return rc;
+       }
+       if( pData ){
+               zBlob = (sxu8 *)pBlob->pBlob ;
+               zBlob = &zBlob[pBlob->nByte];
+               pBlob->nByte += nSize;
+               SX_MACRO_FAST_MEMCPY(pData, zBlob, nSize);
+       }
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob)
+{
+       sxi32 rc;
+       sxu32 n;
+       n = pBlob->nByte;
+       rc = SyBlobAppend(&(*pBlob), (const void *)"\0", sizeof(char));
+       if (rc == SXRET_OK ){
+               pBlob->nByte = n;
+       }
+       return rc;
+}
+JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest)
+{
+       sxi32 rc = SXRET_OK;
+       if( pSrc->nByte > 0 ){
+               rc = SyBlobAppend(&(*pDest), pSrc->pBlob, pSrc->nByte);
+       }
+       return rc;
+}
+JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob)
+{
+       pBlob->nByte = 0;
+       if( pBlob->nFlags & SXBLOB_RDONLY ){
+               /* Read-only (Not malloced chunk) */
+               pBlob->pBlob = 0;
+               pBlob->mByte = 0;
+               pBlob->nFlags &= ~SXBLOB_RDONLY;
+       }
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyBlobTruncate(SyBlob *pBlob,sxu32 nNewLen)
+{
+       if( nNewLen < pBlob->nByte ){
+               pBlob->nByte = nNewLen;
+       }
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob)
+{
+       if( (pBlob->nFlags & (SXBLOB_STATIC|SXBLOB_RDONLY)) == 0 && pBlob->mByte > 0 ){
+               SyMemBackendFree(pBlob->pAllocator, pBlob->pBlob);
+       }
+       pBlob->pBlob = 0;
+       pBlob->nByte = pBlob->mByte = 0;
+       pBlob->nFlags = 0;
+       return SXRET_OK;
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft)
+{
+       const char *zIn = (const char *)pBlob;
+       const char *zEnd;
+       sxi32 rc;
+       if( pLen > nLen ){
+               return SXERR_NOTFOUND;
+       }
+       zEnd = &zIn[nLen-pLen];
+       for(;;){
+               if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++;
+               if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++;
+               if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++;
+               if( zIn > zEnd ){break;} SX_MACRO_FAST_CMP(zIn, pPattern, pLen, rc); if( rc == 0 ){ if( pOfft ){ *pOfft = (sxu32)(zIn - (const char *)pBlob);} return SXRET_OK; } zIn++;
+       }
+       return SXERR_NOTFOUND;
+}
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+/* SyRunTimeApi:sxds.c */
+JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize)
+{
+       pSet->nSize = 0 ;
+       pSet->nUsed = 0;
+       pSet->nCursor = 0;
+       pSet->eSize = ElemSize;
+       pSet->pAllocator = pAllocator;
+       pSet->pBase =  0;
+       pSet->pUserData = 0;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem)
+{
+       unsigned char *zbase;
+       if( pSet->nUsed >= pSet->nSize ){
+               void *pNew;
+               if( pSet->pAllocator == 0 ){
+                       return  SXERR_LOCKED;
+               }
+               if( pSet->nSize <= 0 ){
+                       pSet->nSize = 4;
+               }
+               pNew = SyMemBackendRealloc(pSet->pAllocator, pSet->pBase, pSet->eSize * pSet->nSize * 2);
+               if( pNew == 0 ){
+                       return SXERR_MEM;
+               }
+               pSet->pBase = pNew;
+               pSet->nSize <<= 1;
+       }
+       zbase = (unsigned char *)pSet->pBase;
+       SX_MACRO_FAST_MEMCPY(pItem, &zbase[pSet->nUsed * pSet->eSize], pSet->eSize);
+       pSet->nUsed++;  
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem)
+{
+       if( pSet->nSize > 0 ){
+               return SXERR_LOCKED;
+       }
+       if( nItem < 8 ){
+               nItem = 8;
+       }
+       pSet->pBase = SyMemBackendAlloc(pSet->pAllocator, pSet->eSize * nItem);
+       if( pSet->pBase == 0 ){
+               return SXERR_MEM;
+       }
+       pSet->nSize = nItem;
+       return SXRET_OK;
+} 
+JX9_PRIVATE sxi32 SySetReset(SySet *pSet)
+{
+       pSet->nUsed   = 0;
+       pSet->nCursor = 0;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet)
+{
+       pSet->nCursor = 0;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry)
+{
+       register unsigned char *zSrc;
+       if( pSet->nCursor >= pSet->nUsed ){
+               /* Reset cursor */
+               pSet->nCursor = 0;
+               return SXERR_EOF;
+       }
+       zSrc = (unsigned char *)SySetBasePtr(pSet);
+       if( ppEntry ){
+               *ppEntry = (void *)&zSrc[pSet->nCursor * pSet->eSize];
+       }
+       pSet->nCursor++;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SySetRelease(SySet *pSet)
+{
+       sxi32 rc = SXRET_OK;
+       if( pSet->pAllocator && pSet->pBase ){
+               rc = SyMemBackendFree(pSet->pAllocator, pSet->pBase);
+       }
+       pSet->pBase = 0;
+       pSet->nUsed = 0;
+       pSet->nCursor = 0;
+       return rc;
+}
+JX9_PRIVATE void * SySetPeek(SySet *pSet)
+{
+       const char *zBase;
+       if( pSet->nUsed <= 0 ){
+               return 0;
+       }
+       zBase = (const char *)pSet->pBase;
+       return (void *)&zBase[(pSet->nUsed - 1) * pSet->eSize]; 
+}
+JX9_PRIVATE void * SySetPop(SySet *pSet)
+{
+       const char *zBase;
+       void *pData;
+       if( pSet->nUsed <= 0 ){
+               return 0;
+       }
+       zBase = (const char *)pSet->pBase;
+       pSet->nUsed--;
+       pData =  (void *)&zBase[pSet->nUsed * pSet->eSize]; 
+       return pData;
+}
+JX9_PRIVATE void * SySetAt(SySet *pSet, sxu32 nIdx)
+{
+       const char *zBase;
+       if( nIdx >= pSet->nUsed ){
+               /* Out of range */
+               return 0;
+       }
+       zBase = (const char *)pSet->pBase;
+       return (void *)&zBase[nIdx * pSet->eSize]; 
+}
+/* Private hash entry */
+struct SyHashEntry_Pr
+{
+       const void *pKey; /* Hash key */
+       sxu32 nKeyLen;    /* Key length */
+       void *pUserData;  /* User private data */
+       /* Private fields */
+       sxu32 nHash;
+       SyHash *pHash;
+       SyHashEntry_Pr *pNext, *pPrev; /* Next and previous entry in the list */
+       SyHashEntry_Pr *pNextCollide, *pPrevCollide; /* Collision list */
+};
+#define INVALID_HASH(H) ((H)->apBucket == 0)
+JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp)
+{
+       SyHashEntry_Pr **apNew;
+#if defined(UNTRUST)
+       if( pHash == 0 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       /* Allocate a new table */
+       apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(&(*pAllocator), sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
+       if( apNew == 0 ){
+               return SXERR_MEM;
+       }
+       SyZero((void *)apNew, sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
+       pHash->pAllocator = &(*pAllocator);
+       pHash->xHash = xHash ? xHash : SyBinHash;
+       pHash->xCmp = xCmp ? xCmp : SyMemcmp;
+       pHash->pCurrent = pHash->pList = 0;
+       pHash->nEntry = 0;
+       pHash->apBucket = apNew;
+       pHash->nBucketSize = SXHASH_BUCKET_SIZE;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash)
+{
+       SyHashEntry_Pr *pEntry, *pNext;
+#if defined(UNTRUST)
+       if( INVALID_HASH(pHash)  ){
+               return SXERR_EMPTY;
+       }
+#endif
+       pEntry = pHash->pList;
+       for(;;){
+               if( pHash->nEntry == 0 ){
+                       break;
+               }
+               pNext = pEntry->pNext;
+               SyMemBackendPoolFree(pHash->pAllocator, pEntry);
+               pEntry = pNext;
+               pHash->nEntry--;
+       }
+       if( pHash->apBucket ){
+               SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
+       }
+       pHash->apBucket = 0;
+       pHash->nBucketSize = 0;
+       pHash->pAllocator = 0;
+       return SXRET_OK;
+}
+static SyHashEntry_Pr * HashGetEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
+{
+       SyHashEntry_Pr *pEntry;
+       sxu32 nHash;
+
+       nHash = pHash->xHash(pKey, nKeyLen);
+       pEntry = pHash->apBucket[nHash & (pHash->nBucketSize - 1)];
+       for(;;){
+               if( pEntry == 0 ){
+                       break;
+               }
+               if( pEntry->nHash == nHash && pEntry->nKeyLen == nKeyLen && 
+                       pHash->xCmp(pEntry->pKey, pKey, nKeyLen) == 0 ){
+                               return pEntry;
+               }
+               pEntry = pEntry->pNextCollide;
+       }
+       /* Entry not found */
+       return 0;
+}
+JX9_PRIVATE SyHashEntry * SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
+{
+       SyHashEntry_Pr *pEntry;
+#if defined(UNTRUST)
+       if( INVALID_HASH(pHash) ){
+               return 0;
+       }
+#endif
+       if( pHash->nEntry < 1 || nKeyLen < 1 ){
+               /* Don't bother hashing, return immediately */
+               return 0;
+       }
+       pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
+       if( pEntry == 0 ){
+               return 0;
+       }
+       return (SyHashEntry *)pEntry;
+}
+static sxi32 HashDeleteEntry(SyHash *pHash, SyHashEntry_Pr *pEntry, void **ppUserData)
+{
+       sxi32 rc;
+       if( pEntry->pPrevCollide == 0 ){
+               pHash->apBucket[pEntry->nHash & (pHash->nBucketSize - 1)] = pEntry->pNextCollide;
+       }else{
+               pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
+       }
+       if( pEntry->pNextCollide ){
+               pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
+       }
+       MACRO_LD_REMOVE(pHash->pList, pEntry);
+       pHash->nEntry--;
+       if( ppUserData ){
+               /* Write a pointer to the user data */
+               *ppUserData = pEntry->pUserData;
+       }
+       /* Release the entry */
+       rc = SyMemBackendPoolFree(pHash->pAllocator, pEntry);
+       return rc;
+}
+JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData)
+{
+       SyHashEntry_Pr *pEntry;
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( INVALID_HASH(pHash) ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
+       if( pEntry == 0 ){
+               return SXERR_NOTFOUND;
+       }
+       rc = HashDeleteEntry(&(*pHash), pEntry, ppUserData);
+       return rc;
+}
+JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32 (*xStep)(SyHashEntry *, void *), void *pUserData)
+{
+       SyHashEntry_Pr *pEntry;
+       sxi32 rc;
+       sxu32 n;
+#if defined(UNTRUST)
+       if( INVALID_HASH(pHash) || xStep == 0){
+               return 0;
+       }
+#endif
+       pEntry = pHash->pList;
+       for( n = 0 ; n < pHash->nEntry ; n++ ){
+               /* Invoke the callback */
+               rc = xStep((SyHashEntry *)pEntry, pUserData);
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pNext;
+       }
+       return SXRET_OK;
+}
+static sxi32 HashGrowTable(SyHash *pHash)
+{
+       sxu32 nNewSize = pHash->nBucketSize * 2;
+       SyHashEntry_Pr *pEntry;
+       SyHashEntry_Pr **apNew;
+       sxu32 n, iBucket;
+
+       /* Allocate a new larger table */
+       apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(pHash->pAllocator, nNewSize * sizeof(SyHashEntry_Pr *));
+       if( apNew == 0 ){
+               /* Not so fatal, simply a performance hit */
+               return SXRET_OK;
+       }
+       /* Zero the new table */
+       SyZero((void *)apNew, nNewSize * sizeof(SyHashEntry_Pr *));
+       /* Rehash all entries */
+       for( n = 0, pEntry = pHash->pList; n < pHash->nEntry ; n++  ){
+               pEntry->pNextCollide = pEntry->pPrevCollide = 0;
+               /* Install in the new bucket */
+               iBucket = pEntry->nHash & (nNewSize - 1);
+               pEntry->pNextCollide = apNew[iBucket];
+               if( apNew[iBucket] != 0 ){
+                       apNew[iBucket]->pPrevCollide = pEntry;
+               }
+               apNew[iBucket] = pEntry;
+               /* Point to the next entry */
+               pEntry = pEntry->pNext;
+       }
+       /* Release the old table and reflect the change */
+       SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
+       pHash->apBucket = apNew;
+       pHash->nBucketSize = nNewSize;
+       return SXRET_OK;
+}
+static sxi32 HashInsert(SyHash *pHash, SyHashEntry_Pr *pEntry)
+{
+       sxu32 iBucket = pEntry->nHash & (pHash->nBucketSize - 1);
+       /* Insert the entry in its corresponding bcuket */
+       pEntry->pNextCollide = pHash->apBucket[iBucket];
+       if( pHash->apBucket[iBucket] != 0 ){
+               pHash->apBucket[iBucket]->pPrevCollide = pEntry;
+       }
+       pHash->apBucket[iBucket] = pEntry;
+       /* Link to the entry list */
+       MACRO_LD_PUSH(pHash->pList, pEntry);
+       if( pHash->nEntry == 0 ){
+               pHash->pCurrent = pHash->pList;
+       }
+       pHash->nEntry++;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData)
+{
+       SyHashEntry_Pr *pEntry;
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( INVALID_HASH(pHash) || pKey == 0 ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       if( pHash->nEntry >= pHash->nBucketSize * SXHASH_FILL_FACTOR ){
+               rc = HashGrowTable(&(*pHash));
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+       }
+       /* Allocate a new hash entry */
+       pEntry = (SyHashEntry_Pr *)SyMemBackendPoolAlloc(pHash->pAllocator, sizeof(SyHashEntry_Pr));
+       if( pEntry == 0 ){
+               return SXERR_MEM;
+       }
+       /* Zero the entry */
+       SyZero(pEntry, sizeof(SyHashEntry_Pr));
+       pEntry->pHash = pHash;
+       pEntry->pKey = pKey;
+       pEntry->nKeyLen = nKeyLen;
+       pEntry->pUserData = pUserData;
+       pEntry->nHash = pHash->xHash(pEntry->pKey, pEntry->nKeyLen);
+       /* Finally insert the entry in its corresponding bucket */
+       rc = HashInsert(&(*pHash), pEntry);
+       return rc;
+}
+/* SyRunTimeApi:sxutils.c */
+JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char  **pzTail)
+{
+       const char *zCur, *zEnd;
+#ifdef UNTRUST
+       if( SX_EMPTY_STR(zSrc) ){
+               return SXERR_EMPTY;
+       }
+#endif
+       zEnd = &zSrc[nLen];
+       /* Jump leading white spaces */
+       while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0  && SyisSpace(zSrc[0]) ){
+               zSrc++;
+       }
+       if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
+               zSrc++;
+       }
+       zCur = zSrc;
+       if( pReal ){
+               *pReal = FALSE;
+       }
+       for(;;){
+               if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
+               if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
+               if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
+               if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
+       };
+       if( zSrc < zEnd && zSrc > zCur ){
+               int c = zSrc[0];
+               if( c == '.' ){
+                       zSrc++;
+                       if( pReal ){
+                               *pReal = TRUE;
+                       }
+                       if( pzTail ){
+                               while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
+                                       zSrc++;
+                               }
+                               if( zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E') ){
+                                       zSrc++;
+                                       if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
+                                               zSrc++;
+                                       }
+                                       while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
+                                               zSrc++;
+                                       }
+                               }
+                       }
+               }else if( c == 'e' || c == 'E' ){
+                       zSrc++;
+                       if( pReal ){
+                               *pReal = TRUE;
+                       }
+                       if( pzTail ){
+                               if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
+                                       zSrc++;
+                               }
+                               while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
+                                       zSrc++;
+                               }
+                       }
+               }
+       }
+       if( pzTail ){
+               /* Point to the non numeric part */
+               *pzTail = zSrc;
+       }
+       return zSrc > zCur ? SXRET_OK /* String prefix is numeric */ : SXERR_INVALID /* Not a digit stream */;
+}
+#define SXINT32_MIN_STR                "2147483648"
+#define SXINT32_MAX_STR                "2147483647"
+#define SXINT64_MIN_STR                "9223372036854775808"
+#define SXINT64_MAX_STR                "9223372036854775807"
+JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
+{
+       int isNeg = FALSE;
+       const char *zEnd;
+       sxi32 nVal = 0;
+       sxi16 i;
+#if defined(UNTRUST)
+       if( SX_EMPTY_STR(zSrc) ){
+               if( pOutVal ){
+                       *(sxi32 *)pOutVal = 0;
+               }
+               return SXERR_EMPTY;
+       }
+#endif
+       zEnd = &zSrc[nLen];
+       while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
+               zSrc++;
+       }
+       if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
+               isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
+               zSrc++;
+       }
+       /* Skip leading zero */
+       while(zSrc < zEnd && zSrc[0] == '0' ){
+               zSrc++; 
+       }
+       i = 10;
+       if( (sxu32)(zEnd-zSrc) >= 10 ){
+               /* Handle overflow */
+               i = SyMemcmp(zSrc, (isNeg == TRUE) ? SXINT32_MIN_STR : SXINT32_MAX_STR, nLen) <= 0 ? 10 : 9; 
+       }
+       for(;;){
+               if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
+               if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
+               if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
+               if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
+       }
+       /* Skip trailing spaces */
+       while(zSrc < zEnd && SyisSpace(zSrc[0])){
+               zSrc++;
+       }
+       if( zRest ){
+               *zRest = (char *)zSrc;
+       }       
+       if( pOutVal ){
+               if( isNeg == TRUE && nVal != 0 ){
+                       nVal = -nVal;
+               }
+               *(sxi32 *)pOutVal = nVal;
+       }
+       return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
+}
+JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
+{
+       int isNeg = FALSE;
+       const char *zEnd;
+       sxi64 nVal;
+       sxi16 i;
+#if defined(UNTRUST)
+       if( SX_EMPTY_STR(zSrc) ){
+               if( pOutVal ){
+                       *(sxi32 *)pOutVal = 0;
+               }
+               return SXERR_EMPTY;
+       }
+#endif
+       zEnd = &zSrc[nLen];
+       while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
+               zSrc++;
+       }
+       if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
+               isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
+               zSrc++;
+       }
+       /* Skip leading zero */
+       while(zSrc < zEnd && zSrc[0] == '0' ){
+               zSrc++;
+       }
+       i = 19;
+       if( (sxu32)(zEnd-zSrc) >= 19 ){
+               i = SyMemcmp(zSrc, isNeg ? SXINT64_MIN_STR : SXINT64_MAX_STR, 19) <= 0 ? 19 : 18 ;
+       }
+       nVal = 0;
+       for(;;){
+               if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
+               if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
+               if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
+               if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
+       }
+       /* Skip trailing spaces */
+       while(zSrc < zEnd && SyisSpace(zSrc[0])){
+               zSrc++;
+       }
+       if( zRest ){
+               *zRest = (char *)zSrc;
+       }       
+       if( pOutVal ){
+               if( isNeg == TRUE && nVal != 0 ){
+                       nVal = -nVal;
+               }
+               *(sxi64 *)pOutVal = nVal;
+       }
+       return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
+}
+JX9_PRIVATE sxi32 SyHexToint(sxi32 c)
+{
+       switch(c){
+       case '0': return 0;
+       case '1': return 1;
+       case '2': return 2;
+       case '3': return 3;
+       case '4': return 4;
+       case '5': return 5;
+       case '6': return 6;
+       case '7': return 7;
+       case '8': return 8;
+       case '9': return 9;
+       case 'A': case 'a': return 10;
+       case 'B': case 'b': return 11;
+       case 'C': case 'c': return 12;
+       case 'D': case 'd': return 13;
+       case 'E': case 'e': return 14;
+       case 'F': case 'f': return 15;
+       }
+       return -1;      
+}
+JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
+{
+       const char *zIn, *zEnd;
+       int isNeg = FALSE;
+       sxi64 nVal = 0;
+#if defined(UNTRUST)
+       if( SX_EMPTY_STR(zSrc) ){
+               if( pOutVal ){
+                       *(sxi32 *)pOutVal = 0;
+               }
+               return SXERR_EMPTY;
+       }
+#endif
+       zEnd = &zSrc[nLen];
+       while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
+               zSrc++;
+       }
+       if( zSrc < zEnd && ( *zSrc == '-' || *zSrc == '+' ) ){
+               isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
+               zSrc++;
+       }
+       if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'x' || zSrc[1] == 'X') ){
+               /* Bypass hex prefix */
+               zSrc += sizeof(char) * 2;
+       }       
+       /* Skip leading zero */
+       while(zSrc < zEnd && zSrc[0] == '0' ){
+               zSrc++;
+       }
+       zIn = zSrc;
+       for(;;){
+               if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
+               if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
+               if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
+               if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]);  zSrc++ ;
+       }
+       while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
+               zSrc++;
+       }       
+       if( zRest ){
+               *zRest = zSrc;
+       }
+       if( pOutVal ){
+               if( isNeg == TRUE && nVal != 0 ){
+                       nVal = -nVal;
+               }
+               *(sxi64 *)pOutVal = nVal;
+       }
+       return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
+}
+JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
+{
+       const char *zIn, *zEnd;
+       int isNeg = FALSE;
+       sxi64 nVal = 0;
+       int c;
+#if defined(UNTRUST)
+       if( SX_EMPTY_STR(zSrc) ){
+               if( pOutVal ){
+                       *(sxi32 *)pOutVal = 0;
+               }
+               return SXERR_EMPTY;
+       }
+#endif
+       zEnd = &zSrc[nLen];
+       while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
+               zSrc++;
+       }
+       if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
+               isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
+               zSrc++;
+       }
+       /* Skip leading zero */
+       while(zSrc < zEnd && zSrc[0] == '0' ){
+               zSrc++; 
+       }
+       zIn = zSrc;
+       for(;;){
+               if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
+               if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
+               if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
+               if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 +  c; zSrc++;
+       }
+       /* Skip trailing spaces */
+       while(zSrc < zEnd && SyisSpace(zSrc[0])){
+               zSrc++;
+       }
+       if( zRest ){
+               *zRest = zSrc;
+       }       
+       if( pOutVal ){
+               if( isNeg == TRUE && nVal != 0 ){
+                       nVal = -nVal;
+               }
+               *(sxi64 *)pOutVal = nVal;
+       }
+       return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
+}
+JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
+{
+       const char *zIn, *zEnd;
+       int isNeg = FALSE;
+       sxi64 nVal = 0;
+       int c;
+#if defined(UNTRUST)
+       if( SX_EMPTY_STR(zSrc) ){
+               if( pOutVal ){
+                       *(sxi32 *)pOutVal = 0;
+               }
+               return SXERR_EMPTY;
+       }
+#endif
+       zEnd = &zSrc[nLen];
+       while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
+               zSrc++;
+       }
+       if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
+               isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
+               zSrc++;
+       }
+       if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'b' || zSrc[1] == 'B') ){
+               /* Bypass binary prefix */
+               zSrc += sizeof(char) * 2;
+       }
+       /* Skip leading zero */
+       while(zSrc < zEnd && zSrc[0] == '0' ){
+               zSrc++; 
+       }
+       zIn = zSrc;
+       for(;;){
+               if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
+               if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
+               if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
+               if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
+       }
+       /* Skip trailing spaces */
+       while(zSrc < zEnd && SyisSpace(zSrc[0])){
+               zSrc++;
+       }
+       if( zRest ){
+               *zRest = zSrc;
+       }       
+       if( pOutVal ){
+               if( isNeg == TRUE && nVal != 0 ){
+                       nVal = -nVal;
+               }
+               *(sxi64 *)pOutVal = nVal;
+       }
+       return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
+}
+JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
+{
+#define SXDBL_DIG        15
+#define SXDBL_MAX_EXP    308
+#define SXDBL_MIN_EXP_PLUS     307
+       static const sxreal aTab[] = {
+       10, 
+       1.0e2, 
+       1.0e4, 
+       1.0e8, 
+       1.0e16, 
+       1.0e32, 
+       1.0e64, 
+       1.0e128, 
+       1.0e256
+       };
+       sxu8 neg = FALSE;
+       sxreal Val = 0.0;
+       const char *zEnd;
+       sxi32 Lim, exp;
+       sxreal *p = 0;
+#ifdef UNTRUST
+       if( SX_EMPTY_STR(zSrc)  ){
+               if( pOutVal ){
+                       *(sxreal *)pOutVal = 0.0;
+               }
+               return SXERR_EMPTY;
+       }
+#endif
+       zEnd = &zSrc[nLen];
+       while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
+               zSrc++; 
+       }
+       if( zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+' ) ){
+               neg =  zSrc[0] == '-' ? TRUE : FALSE ;
+               zSrc++;
+       }
+       Lim = SXDBL_DIG ;
+       for(;;){
+               if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
+               if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
+               if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
+               if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
+       }
+       if( zSrc < zEnd && ( zSrc[0] == '.' || zSrc[0] == ',' ) ){
+               sxreal dec = 1.0;
+               zSrc++;
+               for(;;){
+                       if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
+                       if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
+                       if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
+                       if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
+               }
+               Val /= dec;
+       }
+       if( neg == TRUE && Val != 0.0 ) {
+               Val = -Val ; 
+       }
+       if( Lim <= 0 ){
+               /* jump overflow digit */
+               while( zSrc < zEnd ){
+                       if( zSrc[0] == 'e' || zSrc[0] == 'E' ){
+                               break;  
+                       }
+                       zSrc++;
+               }
+       }
+       neg = FALSE;
+       if( zSrc < zEnd && ( zSrc[0] == 'e' || zSrc[0] == 'E' ) ){
+               zSrc++;
+               if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+') ){
+                       neg = zSrc[0] == '-' ? TRUE : FALSE ;
+                       zSrc++;
+               }
+               exp = 0;
+               while( zSrc < zEnd && SyisDigit(zSrc[0]) && exp < SXDBL_MAX_EXP ){
+                       exp = exp * 10 + (zSrc[0] - '0');
+                       zSrc++;
+               }
+               if( neg  ){
+                       if( exp > SXDBL_MIN_EXP_PLUS ) exp = SXDBL_MIN_EXP_PLUS ;
+               }else if ( exp > SXDBL_MAX_EXP ){
+                       exp = SXDBL_MAX_EXP; 
+               }               
+               for( p = (sxreal *)aTab ; exp ; exp >>= 1 , p++ ){
+                       if( exp & 01 ){
+                               if( neg ){
+                                       Val /= *p ;
+                               }else{
+                                       Val *= *p;
+                               }
+                       }
+               }
+       }
+       while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
+               zSrc++;
+       }
+       if( zRest ){
+               *zRest = zSrc; 
+       }
+       if( pOutVal ){
+               *(sxreal *)pOutVal = Val;
+       }
+       return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
+}
+/* SyRunTimeApi:sxlib.c  */
+JX9_PRIVATE sxu32 SyBinHash(const void *pSrc, sxu32 nLen)
+{
+       register unsigned char *zIn = (unsigned char *)pSrc;
+       unsigned char *zEnd;
+       sxu32 nH = 5381;
+       zEnd = &zIn[nLen];
+       for(;;){
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+       }       
+       return nH;
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
+{
+       static const unsigned char zBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+       unsigned char *zIn = (unsigned char *)zSrc;
+       unsigned char z64[4];
+       sxu32 i;
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( SX_EMPTY_STR(zSrc) || xConsumer == 0){
+               return SXERR_EMPTY;
+       }
+#endif
+       for(i = 0; i + 2 < nLen; i += 3){
+               z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
+               z64[1] = zBase64[( ((zIn[i] & 0x03) << 4)   | (zIn[i+1] >> 4)) & 0x3F]; 
+               z64[2] = zBase64[( ((zIn[i+1] & 0x0F) << 2) | (zIn[i + 2] >> 6) ) & 0x3F];
+               z64[3] = zBase64[ zIn[i + 2] & 0x3F];
+               
+               rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
+               if( rc != SXRET_OK ){return SXERR_ABORT;}
+
+       }       
+       if ( i+1 < nLen ){
+               z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
+               z64[1] = zBase64[( ((zIn[i] & 0x03) << 4)   | (zIn[i+1] >> 4)) & 0x3F]; 
+               z64[2] = zBase64[(zIn[i+1] & 0x0F) << 2 ];
+               z64[3] = '=';
+               
+               rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
+               if( rc != SXRET_OK ){return SXERR_ABORT;}
+
+       }else if( i < nLen ){
+               z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
+               z64[1]   = zBase64[(zIn[i] & 0x03) << 4];
+               z64[2] = '=';
+               z64[3] = '=';
+               
+               rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
+               if( rc != SXRET_OK ){return SXERR_ABORT;}
+       }
+
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
+{
+       static const sxu32 aBase64Trans[] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 
+       5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 
+       28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 
+       0, 0, 0
+       };
+       sxu32 n, w, x, y, z;
+       sxi32 rc;
+       unsigned char zOut[10];
+#if defined(UNTRUST)
+       if( SX_EMPTY_STR(zB64) || xConsumer == 0 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       while(nLen > 0 && zB64[nLen - 1] == '=' ){
+               nLen--;
+       }
+       for( n = 0 ; n+3<nLen ; n += 4){
+               w = aBase64Trans[zB64[n] & 0x7F];
+               x = aBase64Trans[zB64[n+1] & 0x7F];
+               y = aBase64Trans[zB64[n+2] & 0x7F];
+               z = aBase64Trans[zB64[n+3] & 0x7F];
+               zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
+               zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
+               zOut[2] = ((y<<6) & 0xC0) | (z & 0x3F);
+
+               rc = xConsumer((const void *)zOut, sizeof(unsigned char)*3, pUserData);
+               if( rc != SXRET_OK ){ return SXERR_ABORT;}
+       }
+       if( n+2 < nLen ){
+               w = aBase64Trans[zB64[n] & 0x7F];
+               x = aBase64Trans[zB64[n+1] & 0x7F];
+               y = aBase64Trans[zB64[n+2] & 0x7F];
+
+               zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
+               zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
+
+               rc = xConsumer((const void *)zOut, sizeof(unsigned char)*2, pUserData);
+               if( rc != SXRET_OK ){ return SXERR_ABORT;}
+       }else if( n+1 < nLen ){
+               w = aBase64Trans[zB64[n] & 0x7F];
+               x = aBase64Trans[zB64[n+1] & 0x7F];
+
+               zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
+
+               rc = xConsumer((const void *)zOut, sizeof(unsigned char)*1, pUserData);
+               if( rc != SXRET_OK ){ return SXERR_ABORT;}
+       }
+       return SXRET_OK;
+}
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+#define INVALID_LEXER(LEX)     (  LEX == 0  || LEX->xTokenizer == 0 )
+JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData)
+{
+       SyStream *pStream;
+#if defined (UNTRUST)
+       if ( pLex == 0 || xTokenizer == 0 ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       pLex->pTokenSet = 0;
+       /* Initialize lexer fields */
+       if( pSet ){
+               if ( SySetElemSize(pSet) != sizeof(SyToken) ){
+                       return SXERR_INVALID;
+               }
+               pLex->pTokenSet = pSet;
+       }
+       pStream = &pLex->sStream;
+       pLex->xTokenizer = xTokenizer;
+       pLex->pUserData = pUserData;
+       
+       pStream->nLine = 1;
+       pStream->nIgn  = 0;
+       pStream->zText = pStream->zEnd = 0;
+       pStream->pSet  = pSet;
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp)
+{
+       const unsigned char *zCur;
+       SyStream *pStream;
+       SyToken sToken;
+       sxi32 rc;
+#if defined (UNTRUST)
+       if ( INVALID_LEXER(pLex) || zInput == 0 ){
+               return SXERR_CORRUPT;
+       }
+#endif
+       pStream = &pLex->sStream;
+       /* Point to the head of the input */
+       pStream->zText = pStream->zInput = (const unsigned char *)zInput;
+       /* Point to the end of the input */
+       pStream->zEnd = &pStream->zInput[nLen];
+       for(;;){
+               if( pStream->zText >= pStream->zEnd ){
+                       /* End of the input reached */
+                       break;
+               }
+               zCur = pStream->zText;
+               /* Call the tokenizer callback */
+               rc = pLex->xTokenizer(pStream, &sToken, pLex->pUserData, pCtxData);
+               if( rc != SXRET_OK && rc != SXERR_CONTINUE ){
+                       /* Tokenizer callback request an operation abort */
+                       if( rc == SXERR_ABORT ){
+                               return SXERR_ABORT;
+                       }
+                       break;
+               }
+               if( rc == SXERR_CONTINUE ){
+                       /* Request to ignore this token */
+                       pStream->nIgn++;
+               }else if( pLex->pTokenSet  ){
+                       /* Put the token in the set */
+                       rc = SySetPut(pLex->pTokenSet, (const void *)&sToken);
+                       if( rc != SXRET_OK ){
+                               break;
+                       }
+               }
+               if( zCur >= pStream->zText ){
+                       /* Automatic advance of the stream cursor */
+                       pStream->zText = &zCur[1];
+               }
+       }
+       if( xSort &&  pLex->pTokenSet ){
+               SyToken *aToken = (SyToken *)SySetBasePtr(pLex->pTokenSet);
+               /* Sort the extrated tokens */
+               if( xCmp == 0 ){
+                       /* Use a default comparison function */
+                       xCmp = SyMemcmp;
+               }
+               xSort(aToken, SySetUsed(pLex->pTokenSet), sizeof(SyToken), xCmp);
+       }
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex)
+{
+       sxi32 rc = SXRET_OK;
+#if defined (UNTRUST)
+       if ( INVALID_LEXER(pLex) ){
+               return SXERR_CORRUPT;
+       }
+#else
+       SXUNUSED(pLex); /* Prevent compiler warning */
+#endif
+       return rc;
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#define SAFE_HTTP(C)   (SyisAlphaNum(c) || c == '_' || c == '-' || c == '$' || c == '.' )
+JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
+{
+       unsigned char *zIn = (unsigned char *)zSrc;
+       unsigned char zHex[3] = { '%', 0, 0 };
+       unsigned char zOut[2];
+       unsigned char *zCur, *zEnd;
+       sxi32 c;
+       sxi32 rc;
+#ifdef UNTRUST
+       if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       rc = SXRET_OK;
+       zEnd = &zIn[nLen]; zCur = zIn;
+       for(;;){
+               if( zCur >= zEnd ){
+                       if( zCur != zIn ){
+                               rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData);
+                       }
+                       break;
+               }
+               c = zCur[0];
+               if( SAFE_HTTP(c) ){
+                       zCur++; continue;
+               }
+               if( zCur != zIn && SXRET_OK != (rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData))){
+                       break;
+               }               
+               if( c == ' ' ){
+                       zOut[0] = '+';
+                       rc = xConsumer((const void *)zOut, sizeof(unsigned char), pUserData);
+               }else{
+                       zHex[1] = "0123456789ABCDEF"[(c >> 4) & 0x0F];
+                       zHex[2] = "0123456789ABCDEF"[c & 0x0F];
+                       rc = xConsumer(zHex, sizeof(zHex), pUserData);
+               }
+               if( SXRET_OK != rc ){
+                       break;
+               }                               
+               zIn = &zCur[1]; zCur = zIn ;
+       }
+       return rc == SXRET_OK ? SXRET_OK : SXERR_ABORT;
+}
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+static sxi32 SyAsciiToHex(sxi32 c)
+{
+       if( c >= 'a' && c <= 'f' ){
+               c += 10 - 'a';
+               return c;
+       }
+       if( c >= '0' && c <= '9' ){
+               c -= '0';
+               return c;
+       }
+       if( c >= 'A' && c <= 'F') {
+               c += 10 - 'A';
+               return c;
+       }               
+       return 0; 
+}
+JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8)
+{
+       static const sxu8 Utf8Trans[] = {
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
+               0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
+               0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
+               0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
+               0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
+               0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00
+       };
+       const char *zIn = zSrc;
+       const char *zEnd;
+       const char *zCur;
+       sxu8 *zOutPtr;
+       sxu8 zOut[10];
+       sxi32 c, d;
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       rc = SXRET_OK;
+       zEnd = &zSrc[nLen];
+       zCur = zIn;
+       for(;;){
+               while(zCur < zEnd && zCur[0] != '%' && zCur[0] != '+' ){
+                       zCur++;
+               }
+               if( zCur != zIn ){
+                       /* Consume input */
+                       rc = xConsumer(zIn, (unsigned int)(zCur-zIn), pUserData);
+                       if( rc != SXRET_OK ){
+                               /* User consumer routine request an operation abort */
+                               break;
+                       }
+               }
+               if( zCur >= zEnd ){
+                       rc = SXRET_OK;
+                       break;
+               }
+               /* Decode unsafe HTTP characters */
+               zOutPtr = zOut;
+               if( zCur[0] == '+' ){
+                       *zOutPtr++ = ' ';
+                       zCur++;
+               }else{
+                       if( &zCur[2] >= zEnd ){
+                               rc = SXERR_OVERFLOW;
+                               break;
+                       }
+                       c = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
+                       zCur += 3;
+                       if( c < 0x000C0 ){
+                               *zOutPtr++ = (sxu8)c;
+                       }else{
+                               c = Utf8Trans[c-0xC0];
+                               while( zCur[0] == '%' ){
+                                       d = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
+                                       if( (d&0xC0) != 0x80 ){
+                                               break;
+                                       }
+                                       c = (c<<6) + (0x3f & d);
+                                       zCur += 3;
+                               }
+                               if( bUTF8 == FALSE ){
+                                       *zOutPtr++ = (sxu8)c;
+                               }else{
+                                       SX_WRITE_UTF8(zOutPtr, c);
+                               }
+                       }
+                       
+               }
+               /* Consume the decoded characters */
+               rc = xConsumer((const void *)zOut, (unsigned int)(zOutPtr-zOut), pUserData);
+               if( rc != SXRET_OK ){
+                       break;
+               }
+               /* Synchronize pointers */
+               zIn = zCur;
+       }
+       return rc;
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+static const char *zEngDay[] = { 
+       "Sunday", "Monday", "Tuesday", "Wednesday", 
+       "Thursday", "Friday", "Saturday"
+};
+static const char *zEngMonth[] = {
+       "January", "February", "March", "April", 
+       "May", "June", "July", "August", 
+       "September", "October", "November", "December"
+};
+static const char * GetDay(sxi32 i)
+{
+       return zEngDay[ i % 7 ];
+}
+static const char * GetMonth(sxi32 i)
+{
+       return zEngMonth[ i % 12 ];
+}
+JX9_PRIVATE const char * SyTimeGetDay(sxi32 iDay)
+{
+       return GetDay(iDay);
+}
+JX9_PRIVATE const char * SyTimeGetMonth(sxi32 iMonth)
+{
+       return GetMonth(iMonth);
+}
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+/* SyRunTimeApi: sxfmt.c */
+#define SXFMT_BUFSIZ 1024 /* Conversion buffer size */
+/*
+** Conversion types fall into various categories as defined by the
+** following enumeration.
+*/
+#define SXFMT_RADIX       1 /* Integer types.%d, %x, %o, and so forth */
+#define SXFMT_FLOAT       2 /* Floating point.%f */
+#define SXFMT_EXP         3 /* Exponentional notation.%e and %E */
+#define SXFMT_GENERIC     4 /* Floating or exponential, depending on exponent.%g */
+#define SXFMT_SIZE        5 /* Total number of characters processed so far.%n */
+#define SXFMT_STRING      6 /* Strings.%s */
+#define SXFMT_PERCENT     7 /* Percent symbol.%% */
+#define SXFMT_CHARX       8 /* Characters.%c */
+#define SXFMT_ERROR       9 /* Used to indicate no such conversion type */
+/* Extension by Symisc Systems */
+#define SXFMT_RAWSTR     13 /* %z Pointer to raw string (SyString *) */
+#define SXFMT_UNUSED     15 
+/*
+** Allowed values for SyFmtInfo.flags
+*/
+#define SXFLAG_SIGNED  0x01
+#define SXFLAG_UNSIGNED 0x02
+/* Allowed values for SyFmtConsumer.nType */
+#define SXFMT_CONS_PROC                1       /* Consumer is a procedure */
+#define SXFMT_CONS_STR         2       /* Consumer is a managed string */
+#define SXFMT_CONS_FILE                5       /* Consumer is an open File */
+#define SXFMT_CONS_BLOB                6       /* Consumer is a BLOB */
+/*
+** Each builtin conversion character (ex: the 'd' in "%d") is described
+** by an instance of the following structure
+*/
+typedef struct SyFmtInfo SyFmtInfo;
+struct SyFmtInfo
+{
+  char fmttype;  /* The format field code letter [i.e: 'd', 's', 'x'] */
+  sxu8 base;     /* The base for radix conversion */
+  int flags;    /* One or more of SXFLAG_ constants below */
+  sxu8 type;     /* Conversion paradigm */
+  char *charset; /* The character set for conversion */
+  char *prefix;  /* Prefix on non-zero values in alt format */
+};
+typedef struct SyFmtConsumer SyFmtConsumer;
+struct SyFmtConsumer
+{
+       sxu32 nLen; /* Total output length */
+       sxi32 nType; /* Type of the consumer see below */
+       sxi32 rc;       /* Consumer return value;Abort processing if rc != SXRET_OK */
+ union{
+       struct{ 
+       ProcConsumer xUserConsumer;
+       void *pUserData;
+       }sFunc;  
+       SyBlob *pBlob;
+ }uConsumer;   
+}; 
+#ifndef SX_OMIT_FLOATINGPOINT
+static int getdigit(sxlongreal *val, int *cnt)
+{
+  sxlongreal d;
+  int digit;
+
+  if( (*cnt)++ >= 16 ){
+         return '0';
+  }
+  digit = (int)*val;
+  d = digit;
+   *val = (*val - d)*10.0;
+  return digit + '0' ;
+}
+#endif /* SX_OMIT_FLOATINGPOINT */
+/*
+ * The following routine was taken from the SQLITE2 source tree and was
+ * extended by Symisc Systems to fit its need.
+ * Status: Public Domain
+ */
+static sxi32 InternFormat(ProcConsumer xConsumer, void *pUserData, const char *zFormat, va_list ap)
+{
+       /*
+        * The following table is searched linearly, so it is good to put the most frequently
+        * used conversion types first.
+        */
+static const SyFmtInfo aFmt[] = {
+  {  'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0    }, 
+  {  's',  0, 0, SXFMT_STRING,     0,                  0    }, 
+  {  'c',  0, 0, SXFMT_CHARX,      0,                  0    }, 
+  {  'x', 16, 0, SXFMT_RADIX,      "0123456789abcdef", "x0" }, 
+  {  'X', 16, 0, SXFMT_RADIX,      "0123456789ABCDEF", "X0" }, 
+         /* -- Extensions by Symisc Systems -- */
+  {  'z',  0, 0, SXFMT_RAWSTR,     0,                   0   }, /* Pointer to a raw string (SyString *) */
+  {  'B',  2, 0, SXFMT_RADIX,      "01",                "b0"}, 
+         /* -- End of Extensions -- */
+  {  'o',  8, 0, SXFMT_RADIX,      "01234567",         "0"  }, 
+  {  'u', 10, 0, SXFMT_RADIX,      "0123456789",       0    }, 
+#ifndef SX_OMIT_FLOATINGPOINT
+  {  'f',  0, SXFLAG_SIGNED, SXFMT_FLOAT,       0,     0    }, 
+  {  'e',  0, SXFLAG_SIGNED, SXFMT_EXP,        "e",    0    }, 
+  {  'E',  0, SXFLAG_SIGNED, SXFMT_EXP,        "E",    0    }, 
+  {  'g',  0, SXFLAG_SIGNED, SXFMT_GENERIC,    "e",    0    }, 
+  {  'G',  0, SXFLAG_SIGNED, SXFMT_GENERIC,    "E",    0    }, 
+#endif
+  {  'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0    }, 
+  {  'n',  0, 0, SXFMT_SIZE,       0,                  0    }, 
+  {  '%',  0, 0, SXFMT_PERCENT,    0,                  0    }, 
+  {  'p', 10, 0, SXFMT_RADIX,      "0123456789",       0    }
+};
+  int c;                     /* Next character in the format string */
+  char *bufpt;               /* Pointer to the conversion buffer */
+  int precision;             /* Precision of the current field */
+  int length;                /* Length of the field */
+  int idx;                   /* A general purpose loop counter */
+  int width;                 /* Width of the current field */
+  sxu8 flag_leftjustify;   /* True if "-" flag is present */
+  sxu8 flag_plussign;      /* True if "+" flag is present */
+  sxu8 flag_blanksign;     /* True if " " flag is present */
+  sxu8 flag_alternateform; /* True if "#" flag is present */
+  sxu8 flag_zeropad;       /* True if field width constant starts with zero */
+  sxu8 flag_long;          /* True if "l" flag is present */
+  sxi64 longvalue;         /* Value for integer types */
+  const SyFmtInfo *infop;  /* Pointer to the appropriate info structure */
+  char buf[SXFMT_BUFSIZ];  /* Conversion buffer */
+  char prefix;             /* Prefix character."+" or "-" or " " or '\0'.*/
+  sxu8 errorflag = 0;      /* True if an error is encountered */
+  sxu8 xtype;              /* Conversion paradigm */
+  char *zExtra;    
+  static char spaces[] = "                                                  ";
+#define etSPACESIZE ((int)sizeof(spaces)-1)
+#ifndef SX_OMIT_FLOATINGPOINT
+  sxlongreal realvalue;    /* Value for real types */
+  int  exp;                /* exponent of real numbers */
+  double rounder;          /* Used for rounding floating point values */
+  sxu8 flag_dp;            /* True if decimal point should be shown */
+  sxu8 flag_rtz;           /* True if trailing zeros should be removed */
+  sxu8 flag_exp;           /* True to force display of the exponent */
+  int nsd;                 /* Number of significant digits returned */
+#endif
+  int rc;
+
+  length = 0;
+  bufpt = 0;
+  for(; (c=(*zFormat))!=0; ++zFormat){
+    if( c!='%' ){
+      unsigned int amt;
+      bufpt = (char *)zFormat;
+      amt = 1;
+      while( (c=(*++zFormat))!='%' && c!=0 ) amt++;
+         rc = xConsumer((const void *)bufpt, amt, pUserData);
+         if( rc != SXRET_OK ){
+                 return SXERR_ABORT; /* Consumer routine request an operation abort */
+         }
+      if( c==0 ){
+                 return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
+         }
+    }
+    if( (c=(*++zFormat))==0 ){
+      errorflag = 1;
+         rc = xConsumer("%", sizeof("%")-1, pUserData);
+         if( rc != SXRET_OK ){
+                 return SXERR_ABORT; /* Consumer routine request an operation abort */
+         }
+      return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
+    }
+    /* Find out what flags are present */
+    flag_leftjustify = flag_plussign = flag_blanksign = 
+     flag_alternateform = flag_zeropad = 0;
+    do{
+      switch( c ){
+        case '-':   flag_leftjustify = 1;     c = 0;   break;
+        case '+':   flag_plussign = 1;        c = 0;   break;
+        case ' ':   flag_blanksign = 1;       c = 0;   break;
+        case '#':   flag_alternateform = 1;   c = 0;   break;
+        case '0':   flag_zeropad = 1;         c = 0;   break;
+        default:                                       break;
+      }
+    }while( c==0 && (c=(*++zFormat))!=0 );
+    /* Get the field width */
+    width = 0;
+    if( c=='*' ){
+      width = va_arg(ap, int);
+      if( width<0 ){
+        flag_leftjustify = 1;
+        width = -width;
+      }
+      c = *++zFormat;
+    }else{
+      while( c>='0' && c<='9' ){
+        width = width*10 + c - '0';
+        c = *++zFormat;
+      }
+    }
+    if( width > SXFMT_BUFSIZ-10 ){
+      width = SXFMT_BUFSIZ-10;
+    }
+    /* Get the precision */
+       precision = -1;
+    if( c=='.' ){
+      precision = 0;
+      c = *++zFormat;
+      if( c=='*' ){
+        precision = va_arg(ap, int);
+        if( precision<0 ) precision = -precision;
+        c = *++zFormat;
+      }else{
+        while( c>='0' && c<='9' ){
+          precision = precision*10 + c - '0';
+          c = *++zFormat;
+        }
+      }
+    }
+    /* Get the conversion type modifier */
+       flag_long = 0;
+    if( c=='l' || c == 'q' /* BSD quad (expect a 64-bit integer) */ ){
+      flag_long = (c == 'q') ? 2 : 1;
+      c = *++zFormat;
+         if( c == 'l' ){
+                 /* Standard printf emulation 'lld' (expect a 64bit integer) */
+                 flag_long = 2;
+         }
+    }
+    /* Fetch the info entry for the field */
+    infop = 0;
+    xtype = SXFMT_ERROR;
+       for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
+      if( c==aFmt[idx].fmttype ){
+        infop = &aFmt[idx];
+               xtype = infop->type;
+        break;
+      }
+    }
+    zExtra = 0;
+
+    /*
+    ** At this point, variables are initialized as follows:
+    **
+    **   flag_alternateform          TRUE if a '#' is present.
+    **   flag_plussign               TRUE if a '+' is present.
+    **   flag_leftjustify            TRUE if a '-' is present or if the
+    **                               field width was negative.
+    **   flag_zeropad                TRUE if the width began with 0.
+    **   flag_long                   TRUE if the letter 'l' (ell) or 'q'(BSD quad) prefixed
+    **                               the conversion character.
+    **   flag_blanksign              TRUE if a ' ' is present.
+    **   width                       The specified field width.This is
+    **                               always non-negative.Zero is the default.
+    **   precision                   The specified precision.The default
+    **                               is -1.
+    **   xtype                       The object of the conversion.
+    **   infop                       Pointer to the appropriate info struct.
+    */
+    switch( xtype ){
+      case SXFMT_RADIX:
+        if( flag_long > 0 ){
+                       if( flag_long > 1 ){
+                               /* BSD quad: expect a 64-bit integer */
+                               longvalue = va_arg(ap, sxi64);
+                       }else{
+                               longvalue = va_arg(ap, sxlong);
+                       }
+               }else{
+                       if( infop->flags & SXFLAG_SIGNED ){
+                               longvalue = va_arg(ap, sxi32);
+                       }else{
+                               longvalue = va_arg(ap, sxu32);
+                       }
+               }
+               /* Limit the precision to prevent overflowing buf[] during conversion */
+      if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
+#if 1
+        /* For the format %#x, the value zero is printed "0" not "0x0".
+        ** I think this is stupid.*/
+        if( longvalue==0 ) flag_alternateform = 0;
+#else
+        /* More sensible: turn off the prefix for octal (to prevent "00"), 
+        ** but leave the prefix for hex.*/
+        if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
+#endif
+        if( infop->flags & SXFLAG_SIGNED ){
+          if( longvalue<0 ){ 
+            longvalue = -longvalue;
+                       /* Ticket 1433-003 */
+                       if( longvalue < 0 ){
+                               /* Overflow */
+                               longvalue= 0x7FFFFFFFFFFFFFFF;
+                       }
+            prefix = '-';
+          }else if( flag_plussign )  prefix = '+';
+          else if( flag_blanksign )  prefix = ' ';
+          else                       prefix = 0;
+        }else{
+                       if( longvalue<0 ){
+                               longvalue = -longvalue;
+                               /* Ticket 1433-003 */
+                               if( longvalue < 0 ){
+                                       /* Overflow */
+                                       longvalue= 0x7FFFFFFFFFFFFFFF;
+                               }
+                       }
+                       prefix = 0;
+               }
+        if( flag_zeropad && precision<width-(prefix!=0) ){
+          precision = width-(prefix!=0);
+        }
+        bufpt = &buf[SXFMT_BUFSIZ-1];
+        {
+          register char *cset;      /* Use registers for speed */
+          register int base;
+          cset = infop->charset;
+          base = infop->base;
+          do{                                           /* Convert to ascii */
+            *(--bufpt) = cset[longvalue%base];
+            longvalue = longvalue/base;
+          }while( longvalue>0 );
+        }
+        length = &buf[SXFMT_BUFSIZ-1]-bufpt;
+        for(idx=precision-length; idx>0; idx--){
+          *(--bufpt) = '0';                             /* Zero pad */
+        }
+        if( prefix ) *(--bufpt) = prefix;               /* Add sign */
+        if( flag_alternateform && infop->prefix ){      /* Add "0" or "0x" */
+          char *pre, x;
+          pre = infop->prefix;
+          if( *bufpt!=pre[0] ){
+            for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
+          }
+        }
+        length = &buf[SXFMT_BUFSIZ-1]-bufpt;
+        break;
+      case SXFMT_FLOAT:
+      case SXFMT_EXP:
+      case SXFMT_GENERIC:
+#ifndef SX_OMIT_FLOATINGPOINT
+               realvalue = va_arg(ap, double);
+        if( precision<0 ) precision = 6;         /* Set default precision */
+        if( precision>SXFMT_BUFSIZ-40) precision = SXFMT_BUFSIZ-40;
+        if( realvalue<0.0 ){
+          realvalue = -realvalue;
+          prefix = '-';
+        }else{
+          if( flag_plussign )          prefix = '+';
+          else if( flag_blanksign )    prefix = ' ';
+          else                         prefix = 0;
+        }
+        if( infop->type==SXFMT_GENERIC && precision>0 ) precision--;
+        rounder = 0.0;
+#if 0
+        /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
+        for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
+#else
+        /* It makes more sense to use 0.5 */
+        for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
+#endif
+        if( infop->type==SXFMT_FLOAT ) realvalue += rounder;
+        /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
+        exp = 0;
+        if( realvalue>0.0 ){
+          while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
+          while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+          while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
+          while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
+          if( exp>350 || exp<-350 ){
+            bufpt = "NaN";
+            length = 3;
+            break;
+          }
+        }
+        bufpt = buf;
+        /*
+        ** If the field type is etGENERIC, then convert to either etEXP
+        ** or etFLOAT, as appropriate.
+        */
+        flag_exp = xtype==SXFMT_EXP;
+        if( xtype!=SXFMT_FLOAT ){
+          realvalue += rounder;
+          if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
+        }
+        if( xtype==SXFMT_GENERIC ){
+          flag_rtz = !flag_alternateform;
+          if( exp<-4 || exp>precision ){
+            xtype = SXFMT_EXP;
+          }else{
+            precision = precision - exp;
+            xtype = SXFMT_FLOAT;
+          }
+        }else{
+          flag_rtz = 0;
+        }
+        /*
+        ** The "exp+precision" test causes output to be of type etEXP if
+        ** the precision is too large to fit in buf[].
+        */
+        nsd = 0;
+        if( xtype==SXFMT_FLOAT && exp+precision<SXFMT_BUFSIZ-30 ){
+          flag_dp = (precision>0 || flag_alternateform);
+          if( prefix ) *(bufpt++) = prefix;         /* Sign */
+          if( exp<0 )  *(bufpt++) = '0';            /* Digits before "." */
+          else for(; exp>=0; exp--) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
+          if( flag_dp ) *(bufpt++) = '.';           /* The decimal point */
+          for(exp++; exp<0 && precision>0; precision--, exp++){
+            *(bufpt++) = '0';
+          }
+          while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
+          *(bufpt--) = 0;                           /* Null terminate */
+          if( flag_rtz && flag_dp ){     /* Remove trailing zeros and "." */
+            while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
+            if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
+          }
+          bufpt++;                            /* point to next free slot */
+        }else{    /* etEXP or etGENERIC */
+          flag_dp = (precision>0 || flag_alternateform);
+          if( prefix ) *(bufpt++) = prefix;   /* Sign */
+          *(bufpt++) = (char)getdigit(&realvalue, &nsd);  /* First digit */
+          if( flag_dp ) *(bufpt++) = '.';     /* Decimal point */
+          while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
+          bufpt--;                            /* point to last digit */
+          if( flag_rtz && flag_dp ){          /* Remove tail zeros */
+            while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
+            if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
+          }
+          bufpt++;                            /* point to next free slot */
+          if( exp || flag_exp ){
+            *(bufpt++) = infop->charset[0];
+            if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
+            else       { *(bufpt++) = '+'; }
+            if( exp>=100 ){
+              *(bufpt++) = (char)((exp/100)+'0');                /* 100's digit */
+              exp %= 100;
+            }
+            *(bufpt++) = (char)(exp/10+'0');                     /* 10's digit */
+            *(bufpt++) = (char)(exp%10+'0');                     /* 1's digit */
+          }
+        }
+        /* The converted number is in buf[] and zero terminated.Output it.
+        ** Note that the number is in the usual order, not reversed as with
+        ** integer conversions.*/
+        length = bufpt-buf;
+        bufpt = buf;
+
+        /* Special case:  Add leading zeros if the flag_zeropad flag is
+        ** set and we are not left justified */
+        if( flag_zeropad && !flag_leftjustify && length < width){
+          int i;
+          int nPad = width - length;
+          for(i=width; i>=nPad; i--){
+            bufpt[i] = bufpt[i-nPad];
+          }
+          i = prefix!=0;
+          while( nPad-- ) bufpt[i++] = '0';
+          length = width;
+        }
+#else
+         bufpt = " ";
+                length = (int)sizeof(" ") - 1;
+#endif /* SX_OMIT_FLOATINGPOINT */
+        break;
+      case SXFMT_SIZE:{
+                int *pSize = va_arg(ap, int *);
+                *pSize = ((SyFmtConsumer *)pUserData)->nLen;
+                length = width = 0;
+                                         }
+        break;
+      case SXFMT_PERCENT:
+        buf[0] = '%';
+        bufpt = buf;
+        length = 1;
+        break;
+      case SXFMT_CHARX:
+        c = va_arg(ap, int);
+               buf[0] = (char)c;
+               /* Limit the precision to prevent overflowing buf[] during conversion */
+               if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
+        if( precision>=0 ){
+          for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
+          length = precision;
+        }else{
+          length =1;
+        }
+        bufpt = buf;
+        break;
+      case SXFMT_STRING:
+        bufpt = va_arg(ap, char*);
+        if( bufpt==0 ){
+          bufpt = " ";
+                 length = (int)sizeof(" ")-1;
+                 break;
+        }
+               length = precision;
+               if( precision < 0 ){
+                       /* Symisc extension */
+                       length = (int)SyStrlen(bufpt);
+               }
+        if( precision>=0 && precision<length ) length = precision;
+        break;
+       case SXFMT_RAWSTR:{
+               /* Symisc extension */
+               SyString *pStr = va_arg(ap, SyString *);
+               if( pStr == 0 || pStr->zString == 0 ){
+                        bufpt = " ";
+                    length = (int)sizeof(char);
+                    break;
+               }
+               bufpt = (char *)pStr->zString;
+               length = (int)pStr->nByte;
+               break;
+                                         }
+      case SXFMT_ERROR:
+        buf[0] = '?';
+        bufpt = buf;
+               length = (int)sizeof(char);
+        if( c==0 ) zFormat--;
+        break;
+    }/* End switch over the format type */
+    /*
+    ** The text of the conversion is pointed to by "bufpt" and is
+    ** "length" characters long.The field width is "width".Do
+    ** the output.
+    */
+    if( !flag_leftjustify ){
+      register int nspace;
+      nspace = width-length;
+      if( nspace>0 ){
+        while( nspace>=etSPACESIZE ){
+                       rc = xConsumer(spaces, etSPACESIZE, pUserData);
+                       if( rc != SXRET_OK ){
+                               return SXERR_ABORT; /* Consumer routine request an operation abort */
+                       }
+                       nspace -= etSPACESIZE;
+        }
+        if( nspace>0 ){
+                       rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
+                       if( rc != SXRET_OK ){
+                               return SXERR_ABORT; /* Consumer routine request an operation abort */
+                       }
+               }
+      }
+    }
+    if( length>0 ){
+               rc = xConsumer(bufpt, (unsigned int)length, pUserData);
+               if( rc != SXRET_OK ){
+                 return SXERR_ABORT; /* Consumer routine request an operation abort */
+               }
+    }
+    if( flag_leftjustify ){
+      register int nspace;
+      nspace = width-length;
+      if( nspace>0 ){
+        while( nspace>=etSPACESIZE ){
+                       rc = xConsumer(spaces, etSPACESIZE, pUserData);
+                       if( rc != SXRET_OK ){
+                               return SXERR_ABORT; /* Consumer routine request an operation abort */
+                       }
+                       nspace -= etSPACESIZE;
+        }
+        if( nspace>0 ){
+                       rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
+                       if( rc != SXRET_OK ){
+                               return SXERR_ABORT; /* Consumer routine request an operation abort */
+                       }
+               }
+      }
+    }
+  }/* End for loop over the format string */
+  return errorflag ? SXERR_FORMAT : SXRET_OK;
+} 
+static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData)
+{
+       SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData;
+       sxi32 rc = SXERR_ABORT;
+       switch(pConsumer->nType){
+       case SXFMT_CONS_PROC:
+                       /* User callback */
+                       rc = pConsumer->uConsumer.sFunc.xUserConsumer(pSrc, nLen, pConsumer->uConsumer.sFunc.pUserData);
+                       break;
+       case SXFMT_CONS_BLOB:
+                       /* Blob consumer */
+                       rc = SyBlobAppend(pConsumer->uConsumer.pBlob, pSrc, (sxu32)nLen);
+                       break;
+               default: 
+                       /* Unknown consumer */
+                       break;
+       }
+       /* Update total number of bytes consumed so far */
+       pConsumer->nLen += nLen;
+       pConsumer->rc = rc;
+       return rc;      
+}
+static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, void *pUserData, sxu32 *pOutLen, const char *zFormat, va_list ap)
+{
+       SyFmtConsumer sCons;
+       sCons.nType = nType;
+       sCons.rc = SXRET_OK;
+       sCons.nLen = 0;
+       if( pOutLen ){
+               *pOutLen = 0;
+       }
+       switch(nType){
+       case SXFMT_CONS_PROC:
+#if defined(UNTRUST)
+                       if( xUserCons == 0 ){
+                               return SXERR_EMPTY;
+                       }
+#endif
+                       sCons.uConsumer.sFunc.xUserConsumer = xUserCons;
+                       sCons.uConsumer.sFunc.pUserData     = pUserData;
+               break;
+               case SXFMT_CONS_BLOB:
+                       sCons.uConsumer.pBlob = (SyBlob *)pConsumer;
+                       break;
+               default: 
+                       return SXERR_UNKNOWN;
+       }
+       InternFormat(FormatConsumer, &sCons, zFormat, ap); 
+       if( pOutLen ){
+               *pOutLen = sCons.nLen;
+       }
+       return sCons.rc;
+}
+JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...)
+{
+       va_list ap;
+       sxi32 rc;
+#if defined(UNTRUST)   
+       if( SX_EMPTY_STR(zFormat) ){
+               return SXERR_EMPTY;
+       }
+#endif
+       va_start(ap, zFormat);
+       rc = FormatMount(SXFMT_CONS_PROC, 0, xConsumer, pData, 0, zFormat, ap);
+       va_end(ap);
+       return rc;
+}
+JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...)
+{
+       va_list ap;
+       sxu32 n;
+#if defined(UNTRUST)   
+       if( SX_EMPTY_STR(zFormat) ){
+               return 0;
+       }
+#endif                 
+       va_start(ap, zFormat);
+       FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
+       va_end(ap);
+       return n;
+}
+JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap)
+{
+       sxu32 n = 0; /* cc warning */
+#if defined(UNTRUST)   
+       if( SX_EMPTY_STR(zFormat) ){
+               return 0;
+       }
+#endif 
+       FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
+       return n;
+}
+JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...)
+{
+       SyBlob sBlob;
+       va_list ap;
+       sxu32 n;
+#if defined(UNTRUST)   
+       if( SX_EMPTY_STR(zFormat) ){
+               return 0;
+       }
+#endif 
+       if( SXRET_OK != SyBlobInitFromBuf(&sBlob, zBuf, nLen - 1) ){
+               return 0;
+       }               
+       va_start(ap, zFormat);
+       FormatMount(SXFMT_CONS_BLOB, &sBlob, 0, 0, 0, zFormat, ap);
+       va_end(ap);
+       n = SyBlobLength(&sBlob);
+       /* Append the null terminator */
+       sBlob.mByte++;
+       SyBlobAppend(&sBlob, "\0", sizeof(char));
+       return n;
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+/*
+ * Zip File Format:
+ *
+ * Byte order: Little-endian
+ * 
+ * [Local file header + Compressed data [+ Extended local header]?]*
+ * [Central directory]*
+ * [End of central directory record]
+ * 
+ * Local file header:*
+ * Offset   Length   Contents
+ *  0      4 bytes  Local file header signature (0x04034b50)
+ *  4      2 bytes  Version needed to extract
+ *  6      2 bytes  General purpose bit flag
+ *  8      2 bytes  Compression method
+ * 10      2 bytes  Last mod file time
+ * 12      2 bytes  Last mod file date
+ * 14      4 bytes  CRC-32
+ * 18      4 bytes  Compressed size (n)
+ * 22      4 bytes  Uncompressed size
+ * 26      2 bytes  Filename length (f)
+ * 28      2 bytes  Extra field length (e)
+ * 30     (f)bytes  Filename
+ *        (e)bytes  Extra field
+ *        (n)bytes  Compressed data
+ *
+ * Extended local header:*
+ * Offset   Length   Contents
+ *  0      4 bytes  Extended Local file header signature (0x08074b50)
+ *  4      4 bytes  CRC-32
+ *  8      4 bytes  Compressed size
+ * 12      4 bytes  Uncompressed size
+ *
+ * Extra field:?(if any)
+ * Offset      Length          Contents
+ * 0           2 bytes         Header ID (0x001 until 0xfb4a) see extended appnote from Info-zip
+ * 2           2 bytes         Data size (g)
+ *                     (g) bytes       (g) bytes of extra field
+ * 
+ * Central directory:*
+ * Offset   Length   Contents
+ *  0      4 bytes  Central file header signature (0x02014b50)
+ *  4      2 bytes  Version made by
+ *  6      2 bytes  Version needed to extract
+ *  8      2 bytes  General purpose bit flag
+ * 10      2 bytes  Compression method
+ * 12      2 bytes  Last mod file time
+ * 14      2 bytes  Last mod file date
+ * 16      4 bytes  CRC-32
+ * 20      4 bytes  Compressed size
+ * 24      4 bytes  Uncompressed size
+ * 28      2 bytes  Filename length (f)
+ * 30      2 bytes  Extra field length (e)
+ * 32      2 bytes  File comment length (c)
+ * 34      2 bytes  Disk number start
+ * 36      2 bytes  Internal file attributes
+ * 38      4 bytes  External file attributes
+ * 42      4 bytes  Relative offset of local header
+ * 46     (f)bytes  Filename
+ *        (e)bytes  Extra field
+ *        (c)bytes  File comment
+ *
+ * End of central directory record:
+ * Offset   Length   Contents
+ *  0      4 bytes  End of central dir signature (0x06054b50)
+ *  4      2 bytes  Number of this disk
+ *  6      2 bytes  Number of the disk with the start of the central directory
+ *  8      2 bytes  Total number of entries in the central dir on this disk
+ * 10      2 bytes  Total number of entries in the central dir
+ * 12      4 bytes  Size of the central directory
+ * 16      4 bytes  Offset of start of central directory with respect to the starting disk number
+ * 20      2 bytes  zipfile comment length (c)
+ * 22     (c)bytes  zipfile comment
+ *
+ * compression method: (2 bytes)
+ *          0 - The file is stored (no compression)
+ *          1 - The file is Shrunk
+ *          2 - The file is Reduced with compression factor 1
+ *          3 - The file is Reduced with compression factor 2
+ *          4 - The file is Reduced with compression factor 3
+ *          5 - The file is Reduced with compression factor 4
+ *          6 - The file is Imploded
+ *          7 - Reserved for Tokenizing compression algorithm
+ *          8 - The file is Deflated
+ */ 
+
+#define SXMAKE_ZIP_WORKBUF     (SXU16_HIGH/2)  /* 32KB Initial working buffer size */
+#define SXMAKE_ZIP_EXTRACT_VER 0x000a  /* Version needed to extract */
+#define SXMAKE_ZIP_VER 0x003   /* Version made by */
+
+#define SXZIP_CENTRAL_MAGIC                    0x02014b50
+#define SXZIP_END_CENTRAL_MAGIC                0x06054b50
+#define SXZIP_LOCAL_MAGIC                      0x04034b50
+/*#define SXZIP_CRC32_START                    0xdebb20e3*/
+
+#define SXZIP_LOCAL_HDRSZ              30      /* Local header size */
+#define SXZIP_LOCAL_EXT_HDRZ   16      /* Extended local header(footer) size */
+#define SXZIP_CENTRAL_HDRSZ            46      /* Central directory header size */
+#define SXZIP_END_CENTRAL_HDRSZ        22      /* End of central directory header size */
+        
+#define SXARCHIVE_HASH_SIZE    64 /* Starting hash table size(MUST BE POWER OF 2)*/
+static sxi32 SyLittleEndianUnpack32(sxu32 *uNB, const unsigned char *buf, sxu32 Len)
+{
+       if( Len < sizeof(sxu32) ){ 
+               return SXERR_SHORT;
+       }
+       *uNB =  buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
+       return SXRET_OK;
+}
+static sxi32 SyLittleEndianUnpack16(sxu16 *pOut, const unsigned char *zBuf, sxu32 nLen)
+{
+       if( nLen < sizeof(sxu16) ){
+               return SXERR_SHORT;
+       }
+       *pOut = zBuf[0] + (zBuf[1] <<8);
+       
+       return SXRET_OK;
+}
+/*
+ * Archive hashtable manager
+ */
+static sxi32 ArchiveHashGetEntry(SyArchive *pArch, const char *zName, sxu32 nLen, SyArchiveEntry **ppEntry)
+{
+       SyArchiveEntry *pBucketEntry;
+       SyString sEntry;
+       sxu32 nHash;
+
+       nHash = pArch->xHash(zName, nLen);
+       pBucketEntry = pArch->apHash[nHash & (pArch->nSize - 1)];
+
+       SyStringInitFromBuf(&sEntry, zName, nLen);
+
+       for(;;){
+               if( pBucketEntry == 0 ){
+                       break;
+               }
+               if( nHash == pBucketEntry->nHash && pArch->xCmp(&sEntry, &pBucketEntry->sFileName) == 0 ){
+                       if( ppEntry ){
+                               *ppEntry = pBucketEntry;
+                       }
+                       return SXRET_OK;
+               }
+               pBucketEntry = pBucketEntry->pNextHash;
+       }
+       return SXERR_NOTFOUND;
+}
+static void ArchiveHashBucketInstall(SyArchiveEntry **apTable, sxu32 nBucket, SyArchiveEntry *pEntry)
+{
+       pEntry->pNextHash = apTable[nBucket];
+       if( apTable[nBucket] != 0 ){
+               apTable[nBucket]->pPrevHash = pEntry;
+       }
+       apTable[nBucket] = pEntry;
+}
+static sxi32 ArchiveHashGrowTable(SyArchive *pArch)
+{
+       sxu32 nNewSize = pArch->nSize * 2;
+       SyArchiveEntry **apNew;
+       SyArchiveEntry *pEntry;
+       sxu32 n;
+
+       /* Allocate a new table */
+       apNew = (SyArchiveEntry **)SyMemBackendAlloc(pArch->pAllocator, nNewSize * sizeof(SyArchiveEntry *));
+       if( apNew == 0 ){
+               return SXRET_OK; /* Not so fatal, simply a performance hit */
+       }
+       SyZero(apNew, nNewSize * sizeof(SyArchiveEntry *));
+       /* Rehash old entries */
+       for( n = 0 , pEntry = pArch->pList ; n < pArch->nLoaded ; n++ , pEntry = pEntry->pNext ){
+               pEntry->pNextHash = pEntry->pPrevHash = 0;
+               ArchiveHashBucketInstall(apNew, pEntry->nHash & (nNewSize - 1), pEntry);
+       }
+       /* Release the old table */
+       SyMemBackendFree(pArch->pAllocator, pArch->apHash);
+       pArch->apHash = apNew;
+       pArch->nSize = nNewSize;
+
+       return SXRET_OK;
+}
+static sxi32 ArchiveHashInstallEntry(SyArchive *pArch, SyArchiveEntry *pEntry)
+{
+       if( pArch->nLoaded > pArch->nSize * 3 ){
+               ArchiveHashGrowTable(&(*pArch));
+       }
+       pEntry->nHash = pArch->xHash(SyStringData(&pEntry->sFileName), SyStringLength(&pEntry->sFileName));
+       /* Install the entry in its bucket */
+       ArchiveHashBucketInstall(pArch->apHash, pEntry->nHash & (pArch->nSize - 1), pEntry);
+       MACRO_LD_PUSH(pArch->pList, pEntry);
+       pArch->nLoaded++;
+
+       return SXRET_OK;
+}
+ /*
+  * Parse the End of central directory and report status
+  */ 
+ static sxi32 ParseEndOfCentralDirectory(SyArchive *pArch, const unsigned char *zBuf)
+ {
+       sxu32 nMagic = 0; /* cc -O6 warning */
+       sxi32 rc;
+       
+       /* Sanity check */
+       rc = SyLittleEndianUnpack32(&nMagic, zBuf, sizeof(sxu32));
+       if( /* rc != SXRET_OK || */nMagic != SXZIP_END_CENTRAL_MAGIC ){
+               return SXERR_CORRUPT;
+       }
+       /* # of entries */
+       rc = SyLittleEndianUnpack16((sxu16 *)&pArch->nEntry, &zBuf[8], sizeof(sxu16));
+       if( /* rc != SXRET_OK || */ pArch->nEntry > SXI16_HIGH /* SXU16_HIGH */ ){
+               return SXERR_CORRUPT;
+       }
+       /* Size of central directory */
+       rc = SyLittleEndianUnpack32(&pArch->nCentralSize, &zBuf[12], sizeof(sxu32));
+       if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
+               return SXERR_CORRUPT;
+       }
+       /* Starting offset of central directory */
+       rc = SyLittleEndianUnpack32(&pArch->nCentralOfft, &zBuf[16], sizeof(sxu32));
+       if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
+               return SXERR_CORRUPT;
+       }
+       
+       return SXRET_OK;
+ }
+ /*
+  * Fill the zip entry with the appropriate information from the central directory
+  */
+static sxi32 GetCentralDirectoryEntry(SyArchive *pArch, SyArchiveEntry *pEntry, const unsigned char *zCentral, sxu32 *pNextOffset)
+ { 
+       SyString *pName = &pEntry->sFileName; /* File name */
+       sxu16 nDosDate, nDosTime;
+       sxu16 nComment = 0 ;
+       sxu32 nMagic = 0; /* cc -O6 warning */
+       sxi32 rc;       
+       nDosDate = nDosTime = 0; /* cc -O6 warning */
+       SXUNUSED(pArch);
+       /* Sanity check */
+       rc = SyLittleEndianUnpack32(&nMagic, zCentral, sizeof(sxu32));
+       if( /* rc != SXRET_OK || */ nMagic != SXZIP_CENTRAL_MAGIC ){
+               rc = SXERR_CORRUPT;             
+               /*
+                * Try to recover by examing the next central directory record.
+                * Dont worry here, there is no risk of an infinite loop since
+                * the buffer size is delimited.
+                */
+
+               /* pName->nByte = 0; nComment = 0; pName->nExtra = 0 */
+               goto update;
+       }
+       /*
+        * entry name length
+        */
+       SyLittleEndianUnpack16((sxu16 *)&pName->nByte, &zCentral[28], sizeof(sxu16));
+       if( pName->nByte > SXI16_HIGH /* SXU16_HIGH */){
+                rc = SXERR_BIG;
+                goto update;
+       }
+       /* Extra information */
+       SyLittleEndianUnpack16(&pEntry->nExtra, &zCentral[30], sizeof(sxu16));
+       /* Comment length  */
+       SyLittleEndianUnpack16(&nComment, &zCentral[32], sizeof(sxu16));        
+       /* Compression method 0 == stored / 8 == deflated */
+       rc = SyLittleEndianUnpack16(&pEntry->nComprMeth, &zCentral[10], sizeof(sxu16));
+       /* DOS Timestamp */
+       SyLittleEndianUnpack16(&nDosTime, &zCentral[12], sizeof(sxu16));
+       SyLittleEndianUnpack16(&nDosDate, &zCentral[14], sizeof(sxu16));
+       SyDosTimeFormat((nDosDate << 16 | nDosTime), &pEntry->sFmt);
+       /* Little hack to fix month index  */
+       pEntry->sFmt.tm_mon--;
+       /* CRC32 */
+       rc = SyLittleEndianUnpack32(&pEntry->nCrc, &zCentral[16], sizeof(sxu32));
+       /* Content size before compression */
+       rc = SyLittleEndianUnpack32(&pEntry->nByte, &zCentral[24], sizeof(sxu32));
+       if(  pEntry->nByte > SXI32_HIGH ){
+               rc = SXERR_BIG;
+               goto update; 
+       }       
+       /*
+        * Content size after compression.
+        * Note that if the file is stored pEntry->nByte should be equal to pEntry->nByteCompr
+        */ 
+       rc = SyLittleEndianUnpack32(&pEntry->nByteCompr, &zCentral[20], sizeof(sxu32));
+       if( pEntry->nByteCompr > SXI32_HIGH ){
+               rc = SXERR_BIG;
+               goto update; 
+       }               
+       /* Finally grab the contents offset */
+       SyLittleEndianUnpack32(&pEntry->nOfft, &zCentral[42], sizeof(sxu32));
+       if( pEntry->nOfft > SXI32_HIGH ){
+               rc = SXERR_BIG;
+               goto update;
+       }       
+        rc = SXRET_OK;
+update:          
+       /* Update the offset to point to the next central directory record */
+       *pNextOffset =  SXZIP_CENTRAL_HDRSZ + pName->nByte + pEntry->nExtra + nComment;
+       return rc; /* Report failure or success */
+}
+static sxi32 ZipFixOffset(SyArchiveEntry *pEntry, void *pSrc)
+{      
+       sxu16 nExtra, nNameLen;
+       unsigned char *zHdr;
+       nExtra = nNameLen = 0;
+       zHdr = (unsigned char *)pSrc;
+       zHdr = &zHdr[pEntry->nOfft];
+       if( SyMemcmp(zHdr, "PK\003\004", sizeof(sxu32)) != 0 ){
+               return SXERR_CORRUPT;
+       }
+       SyLittleEndianUnpack16(&nNameLen, &zHdr[26], sizeof(sxu16));
+       SyLittleEndianUnpack16(&nExtra, &zHdr[28], sizeof(sxu16));
+       /* Fix contents offset */
+       pEntry->nOfft += SXZIP_LOCAL_HDRSZ + nExtra + nNameLen;
+       return SXRET_OK;
+}
+/*
+ * Extract all valid entries from the central directory 
+ */     
+static sxi32 ZipExtract(SyArchive *pArch, const unsigned char *zCentral, sxu32 nLen, void *pSrc)
+{
+       SyArchiveEntry *pEntry, *pDup;
+       const unsigned char *zEnd ; /* End of central directory */
+       sxu32 nIncr, nOfft;          /* Central Offset */
+       SyString *pName;                /* Entry name */
+       char *zName;
+       sxi32 rc;
+       
+       nOfft = nIncr = 0;
+       zEnd = &zCentral[nLen];
+       
+       for(;;){
+               if( &zCentral[nOfft] >= zEnd ){
+                       break;
+               }
+               /* Add a new entry */
+               pEntry = (SyArchiveEntry *)SyMemBackendPoolAlloc(pArch->pAllocator, sizeof(SyArchiveEntry));
+               if( pEntry == 0 ){
+                       break;
+               }
+               SyZero(pEntry, sizeof(SyArchiveEntry)); 
+               pEntry->nMagic = SXARCH_MAGIC;
+               nIncr = 0;
+               rc = GetCentralDirectoryEntry(&(*pArch), pEntry, &zCentral[nOfft], &nIncr);
+               if( rc == SXRET_OK ){
+                       /* Fix the starting record offset so we can access entry contents correctly */
+                       rc = ZipFixOffset(pEntry, pSrc);
+               }
+               if(rc != SXRET_OK ){
+                       sxu32 nJmp = 0;
+                       SyMemBackendPoolFree(pArch->pAllocator, pEntry);
+                       /* Try to recover by brute-forcing for a valid central directory record */
+                       if( SXRET_OK == SyBlobSearch((const void *)&zCentral[nOfft + nIncr], (sxu32)(zEnd - &zCentral[nOfft + nIncr]), 
+                               (const void *)"PK\001\002", sizeof(sxu32), &nJmp)){
+                                       nOfft += nIncr + nJmp; /* Check next entry */
+                                       continue;
+                       }
+                       break; /* Giving up, archive is hopelessly corrupted */
+               }
+               pName = &pEntry->sFileName;
+               pName->zString = (const char *)&zCentral[nOfft + SXZIP_CENTRAL_HDRSZ];
+               if( pName->nByte <= 0 || ( pEntry->nByte <= 0 && pName->zString[pName->nByte - 1] != '/') ){
+                       /* Ignore zero length records (except folders) and records without names */
+                       SyMemBackendPoolFree(pArch->pAllocator, pEntry); 
+                       nOfft += nIncr; /* Check next entry */
+                       continue;
+               }
+               zName = SyMemBackendStrDup(pArch->pAllocator, pName->zString, pName->nByte);
+               if( zName == 0 ){
+                        SyMemBackendPoolFree(pArch->pAllocator, pEntry); 
+                        nOfft += nIncr; /* Check next entry */
+                       continue;
+               }
+               pName->zString = (const char *)zName;
+               /* Check for duplicates */
+               rc = ArchiveHashGetEntry(&(*pArch), pName->zString, pName->nByte, &pDup);
+               if( rc == SXRET_OK ){
+                       /* Another entry with the same name exists ; link them together */
+                       pEntry->pNextName = pDup->pNextName;
+                       pDup->pNextName = pEntry;
+                       pDup->nDup++;
+               }else{
+                       /* Insert in hashtable */
+                       ArchiveHashInstallEntry(pArch, pEntry);
+               }       
+               nOfft += nIncr; /* Check next record */
+       }
+       pArch->pCursor = pArch->pList;
+       
+       return pArch->nLoaded > 0 ? SXRET_OK : SXERR_EMPTY;
+}                                              
+JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen)
+ {
+       const unsigned char *zCentral, *zEnd;
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( SXARCH_INVALID(pArch) || zBuf == 0 ){
+               return SXERR_INVALID;
+       }
+#endif         
+       /* The miminal size of a zip archive:
+        * LOCAL_HDR_SZ + CENTRAL_HDR_SZ + END_OF_CENTRAL_HDR_SZ
+        *              30                              46                              22
+        */
+        if( nLen < SXZIP_LOCAL_HDRSZ + SXZIP_CENTRAL_HDRSZ + SXZIP_END_CENTRAL_HDRSZ ){
+               return SXERR_CORRUPT; /* Don't bother processing return immediately */
+        }
+                       
+       zEnd = (unsigned char *)&zBuf[nLen - SXZIP_END_CENTRAL_HDRSZ];
+       /* Find the end of central directory */
+       while( ((sxu32)((unsigned char *)&zBuf[nLen] - zEnd) < (SXZIP_END_CENTRAL_HDRSZ + SXI16_HIGH)) &&
+               zEnd > (unsigned char *)zBuf && SyMemcmp(zEnd, "PK\005\006", sizeof(sxu32)) != 0 ){
+               zEnd--;
+       }       
+       /* Parse the end of central directory */
+       rc = ParseEndOfCentralDirectory(&(*pArch), zEnd);
+       if( rc != SXRET_OK ){
+               return rc;
+       }       
+       
+       /* Find the starting offset of the central directory */
+       zCentral = &zEnd[-(sxi32)pArch->nCentralSize];
+       if( zCentral <= (unsigned char *)zBuf || SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
+               if( pArch->nCentralOfft >= nLen ){
+                       /* Corrupted central directory offset */
+                       return SXERR_CORRUPT;
+               }
+               zCentral = (unsigned char *)&zBuf[pArch->nCentralOfft];
+               if( SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
+                       /* Corrupted zip archive */
+                       return SXERR_CORRUPT;
+               }
+               /* Fall thru and extract all valid entries from the central directory */
+       }
+       rc = ZipExtract(&(*pArch), zCentral, (sxu32)(zEnd - zCentral), (void *)zBuf);
+       return rc;
+ }
+/*
+  * Default comparison function.
+  */
+ static sxi32 ArchiveHashCmp(const SyString *pStr1, const SyString *pStr2)
+ {
+        sxi32 rc;
+        rc = SyStringCmp(pStr1, pStr2, SyMemcmp);
+        return rc;
+ }
+JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp)
+ {
+       SyArchiveEntry **apHash;
+#if defined(UNTRUST)
+       if( pArch == 0 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       SyZero(pArch, sizeof(SyArchive));
+       /* Allocate a new hashtable */  
+       apHash = (SyArchiveEntry **)SyMemBackendAlloc(&(*pAllocator), SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
+       if( apHash == 0){
+               return SXERR_MEM;
+       }
+       SyZero(apHash, SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
+       pArch->apHash = apHash;
+       pArch->xHash  = xHash ? xHash : SyBinHash;
+       pArch->xCmp   = xCmp ? xCmp : ArchiveHashCmp;
+       pArch->nSize  = SXARCHIVE_HASH_SIZE;
+       pArch->pAllocator = &(*pAllocator);
+       pArch->nMagic = SXARCH_MAGIC;
+       return SXRET_OK;
+ }
+ static sxi32 ArchiveReleaseEntry(SyMemBackend *pAllocator, SyArchiveEntry *pEntry)
+ {
+       SyArchiveEntry *pDup = pEntry->pNextName;
+       SyArchiveEntry *pNextDup;
+       
+       /* Release duplicates first since there are not stored in the hashtable */
+       for(;;){
+               if( pEntry->nDup == 0 ){
+                       break;
+               }
+               pNextDup = pDup->pNextName;
+               pDup->nMagic = 0x2661;
+               SyMemBackendFree(pAllocator, (void *)SyStringData(&pDup->sFileName));
+               SyMemBackendPoolFree(pAllocator, pDup);                 
+               pDup = pNextDup;
+               pEntry->nDup--;
+       }               
+       pEntry->nMagic = 0x2661;
+       SyMemBackendFree(pAllocator, (void *)SyStringData(&pEntry->sFileName));
+       SyMemBackendPoolFree(pAllocator, pEntry);
+       return SXRET_OK;
+ }     
+JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch)
+ {
+       SyArchiveEntry *pEntry, *pNext;
+       pEntry = pArch->pList;          
+       for(;;){
+               if( pArch->nLoaded < 1 ){
+                       break;
+               }
+               pNext = pEntry->pNext;
+               MACRO_LD_REMOVE(pArch->pList, pEntry);
+               ArchiveReleaseEntry(pArch->pAllocator, pEntry);
+               pEntry = pNext;
+               pArch->nLoaded--;
+       }
+       SyMemBackendFree(pArch->pAllocator, pArch->apHash);
+       pArch->pCursor = 0;
+       pArch->nMagic = 0x2626;
+       return SXRET_OK;
+ }
+ JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch)
+ {
+       pArch->pCursor = pArch->pList;
+       return SXRET_OK;
+ }
+ JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry)
+ {
+       SyArchiveEntry *pNext;
+       if( pArch->pCursor == 0 ){
+               /* Rewind the cursor */
+               pArch->pCursor = pArch->pList;
+               return SXERR_EOF;
+       }
+       *ppEntry = pArch->pCursor;
+        pNext = pArch->pCursor->pNext;
+        /* Advance the cursor to the next entry */
+        pArch->pCursor = pNext;
+        return SXRET_OK;
+  }
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+/*
+ * Psuedo Random Number Generator (PRNG)
+ * @authors: SQLite authors <http://www.sqlite.org/>
+ * @status: Public Domain
+ * NOTE:
+ *  Nothing in this file or anywhere else in the library does any kind of
+ *  encryption.The RC4 algorithm is being used as a PRNG (pseudo-random
+ *  number generator) not as an encryption device.
+ */
+#define SXPRNG_MAGIC   0x13C4
+#ifdef __UNIXES__
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#endif
+static sxi32 SyOSUtilRandomSeed(void *pBuf, sxu32 nLen, void *pUnused)
+{
+       char *zBuf = (char *)pBuf;
+#ifdef __WINNT__
+       DWORD nProcessID; /* Yes, keep it uninitialized when compiling using the MinGW32 builds tools */
+#elif defined(__UNIXES__)
+       pid_t pid;
+       int fd;
+#else
+       char zGarbage[128]; /* Yes, keep this buffer uninitialized */
+#endif
+       SXUNUSED(pUnused);
+#ifdef __WINNT__
+#ifndef __MINGW32__
+       nProcessID = GetProcessId(GetCurrentProcess());
+#endif
+       SyMemcpy((const void *)&nProcessID, zBuf, SXMIN(nLen, sizeof(DWORD)));
+       if( (sxu32)(&zBuf[nLen] - &zBuf[sizeof(DWORD)]) >= sizeof(SYSTEMTIME)  ){
+               GetSystemTime((LPSYSTEMTIME)&zBuf[sizeof(DWORD)]);
+       }
+#elif defined(__UNIXES__)
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd >= 0 ){
+               if( read(fd, zBuf, nLen) > 0 ){
+                       close(fd);
+                       return SXRET_OK;
+               }
+               /* FALL THRU */
+               close(fd);
+       }
+       pid = getpid();
+       SyMemcpy((const void *)&pid, zBuf, SXMIN(nLen, sizeof(pid_t)));
+       if( &zBuf[nLen] - &zBuf[sizeof(pid_t)] >= (int)sizeof(struct timeval)  ){
+               gettimeofday((struct timeval *)&zBuf[sizeof(pid_t)], 0);
+       }
+#else
+       /* Fill with uninitialized data */
+       SyMemcpy(zGarbage, zBuf, SXMIN(nLen, sizeof(zGarbage)));
+#endif
+       return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void * pUserData)
+{
+       char zSeed[256];
+       sxu8 t;
+       sxi32 rc;
+       sxu32 i;
+       if( pCtx->nMagic == SXPRNG_MAGIC ){
+               return SXRET_OK; /* Already initialized */
+       }
+ /* Initialize the state of the random number generator once, 
+  ** the first time this routine is called.The seed value does
+  ** not need to contain a lot of randomness since we are not
+  ** trying to do secure encryption or anything like that...
+  */   
+       if( xSeed == 0 ){
+               xSeed = SyOSUtilRandomSeed;
+       }
+       rc = xSeed(zSeed, sizeof(zSeed), pUserData);
+       if( rc != SXRET_OK ){
+               return rc;
+       }
+       pCtx->i = pCtx->j = 0;
+       for(i=0; i < SX_ARRAYSIZE(pCtx->s) ; i++){
+               pCtx->s[i] = (unsigned char)i;
+    }
+    for(i=0; i < sizeof(zSeed) ; i++){
+      pCtx->j += pCtx->s[i] + zSeed[i];
+      t = pCtx->s[pCtx->j];
+      pCtx->s[pCtx->j] = pCtx->s[i];
+      pCtx->s[i] = t;
+    }
+       pCtx->nMagic = SXPRNG_MAGIC;
+       
+       return SXRET_OK;
+}
+/*
+ * Get a single 8-bit random value using the RC4 PRNG.
+ */
+static sxu8 randomByte(SyPRNGCtx *pCtx)
+{
+  sxu8 t;
+  
+  /* Generate and return single random byte */
+  pCtx->i++;
+  t = pCtx->s[pCtx->i];
+  pCtx->j += t;
+  pCtx->s[pCtx->i] = pCtx->s[pCtx->j];
+  pCtx->s[pCtx->j] = t;
+  t += pCtx->s[pCtx->i];
+  return pCtx->s[t];
+}
+JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen)
+{
+       unsigned char *zBuf = (unsigned char *)pBuf;
+       unsigned char *zEnd = &zBuf[nLen];
+#if defined(UNTRUST)
+       if( pCtx == 0 || pBuf == 0 || nLen <= 0 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       if(pCtx->nMagic != SXPRNG_MAGIC ){
+               return SXERR_CORRUPT;
+       }
+       for(;;){
+               if( zBuf >= zEnd ){break;}      zBuf[0] = randomByte(pCtx);     zBuf++; 
+               if( zBuf >= zEnd ){break;}      zBuf[0] = randomByte(pCtx);     zBuf++; 
+               if( zBuf >= zEnd ){break;}      zBuf[0] = randomByte(pCtx);     zBuf++; 
+               if( zBuf >= zEnd ){break;}      zBuf[0] = randomByte(pCtx);     zBuf++; 
+       }
+       return SXRET_OK;  
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifndef JX9_DISABLE_HASH_FUNC
+/* SyRunTimeApi: sxhash.c */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent, 
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#define SX_MD5_BINSZ   16
+#define SX_MD5_HEXSZ   32
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse (unsigned char *buf, unsigned longs)
+{
+       sxu32 t;
+        do {
+                t = (sxu32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
+                            ((unsigned)buf[1]<<8 | buf[0]);
+                *(sxu32*)buf = t;
+                buf += 4;
+        } while (--longs);
+}
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#ifdef F1
+#undef F1
+#endif
+#ifdef F2
+#undef F2
+#endif
+#ifdef F3
+#undef F3
+#endif
+#ifdef F4
+#undef F4
+#endif
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm.*/
+#define SX_MD5STEP(f, w, x, y, z, data, s) \
+        ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(sxu32 buf[4], const sxu32 in[16])
+{
+       register sxu32 a, b, c, d;
+
+        a = buf[0];
+        b = buf[1];
+        c = buf[2];
+        d = buf[3];
+
+        SX_MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
+        SX_MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+        SX_MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+        SX_MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+        SX_MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
+        SX_MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+        SX_MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+        SX_MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+        SX_MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
+        SX_MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+        SX_MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+        SX_MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+        SX_MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
+        SX_MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+        SX_MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+        SX_MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+        SX_MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
+        SX_MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
+        SX_MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+        SX_MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+        SX_MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
+        SX_MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
+        SX_MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+        SX_MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+        SX_MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
+        SX_MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
+        SX_MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+        SX_MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+        SX_MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
+        SX_MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
+        SX_MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+        SX_MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+        SX_MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
+        SX_MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+        SX_MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+        SX_MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+        SX_MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
+        SX_MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+        SX_MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+        SX_MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+        SX_MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
+        SX_MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+        SX_MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+        SX_MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+        SX_MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
+        SX_MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+        SX_MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+        SX_MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+        SX_MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
+        SX_MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+        SX_MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+        SX_MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+        SX_MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
+        SX_MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+        SX_MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+        SX_MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+        SX_MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
+        SX_MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+        SX_MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+        SX_MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+        SX_MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
+        SX_MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+        SX_MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+        SX_MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+        buf[0] += a;
+        buf[1] += b;
+        buf[2] += c;
+        buf[3] += d;
+}
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len)
+{
+       sxu32 t;
+
+        /* Update bitcount */
+        t = ctx->bits[0];
+        if ((ctx->bits[0] = t + ((sxu32)len << 3)) < t)
+                ctx->bits[1]++; /* Carry from low to high */
+        ctx->bits[1] += len >> 29;
+        t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
+        /* Handle any leading odd-sized chunks */
+        if ( t ) {
+                unsigned char *p = (unsigned char *)ctx->in + t;
+
+                t = 64-t;
+                if (len < t) {
+                        SyMemcpy(buf, p, len);
+                        return;
+                }
+                SyMemcpy(buf, p, t);
+                byteReverse(ctx->in, 16);
+                MD5Transform(ctx->buf, (sxu32*)ctx->in);
+                buf += t;
+                len -= t;
+        }
+        /* Process data in 64-byte chunks */
+        while (len >= 64) {
+                SyMemcpy(buf, ctx->in, 64);
+                byteReverse(ctx->in, 16);
+                MD5Transform(ctx->buf, (sxu32*)ctx->in);
+                buf += 64;
+                len -= 64;
+        }
+        /* Handle any remaining bytes of data.*/
+        SyMemcpy(buf, ctx->in, len);
+}
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx){
+        unsigned count;
+        unsigned char *p;
+
+        /* Compute number of bytes mod 64 */
+        count = (ctx->bits[0] >> 3) & 0x3F;
+
+        /* Set the first char of padding to 0x80.This is safe since there is
+           always at least one byte free */
+        p = ctx->in + count;
+        *p++ = 0x80;
+
+        /* Bytes of padding needed to make 64 bytes */
+        count = 64 - 1 - count;
+
+        /* Pad out to 56 mod 64 */
+        if (count < 8) {
+                /* Two lots of padding:  Pad the first block to 64 bytes */
+               SyZero(p, count);
+                byteReverse(ctx->in, 16);
+                MD5Transform(ctx->buf, (sxu32*)ctx->in);
+
+                /* Now fill the next block with 56 bytes */
+                SyZero(ctx->in, 56);
+        } else {
+                /* Pad block to 56 bytes */
+                SyZero(p, count-8);
+        }
+        byteReverse(ctx->in, 14);
+
+        /* Append length in bits and transform */
+        ((sxu32*)ctx->in)[ 14 ] = ctx->bits[0];
+        ((sxu32*)ctx->in)[ 15 ] = ctx->bits[1];
+
+        MD5Transform(ctx->buf, (sxu32*)ctx->in);
+        byteReverse((unsigned char *)ctx->buf, 4);
+        SyMemcpy(ctx->buf, digest, 0x10);
+        SyZero(ctx, sizeof(ctx));    /* In case it's sensitive */
+}
+#undef F1
+#undef F2
+#undef F3
+#undef F4
+JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx)
+{      
+       pCtx->buf[0] = 0x67452301;
+    pCtx->buf[1] = 0xefcdab89;
+    pCtx->buf[2] = 0x98badcfe;
+    pCtx->buf[3] = 0x10325476;
+    pCtx->bits[0] = 0;
+    pCtx->bits[1] = 0;
+   
+   return SXRET_OK;
+}
+JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16])
+{
+       MD5Context sCtx;
+       MD5Init(&sCtx);
+       MD5Update(&sCtx, (const unsigned char *)pIn, nLen);
+       MD5Final(zDigest, &sCtx);       
+       return SXRET_OK;
+}
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * Status: Public Domain
+ */
+/*
+ * blk0() and blk() perform the initial expand.
+ * I got the idea of expanding during the round function from SSLeay
+ *
+ * blk0le() for little-endian and blk0be() for big-endian.
+ */
+#if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
+/*
+ * GCC by itself only generates left rotates.  Use right rotates if
+ * possible to be kinder to dinky implementations with iterative rotate
+ * instructions.
+ */
+#define SHA_ROT(op, x, k) \
+        ({ unsigned int y; asm(op " %1, %0" : "=r" (y) : "I" (k), "0" (x)); y; })
+#define rol(x, k) SHA_ROT("roll", x, k)
+#define ror(x, k) SHA_ROT("rorl", x, k)
+
+#else
+/* Generic C equivalent */
+#define SHA_ROT(x, l, r) ((x) << (l) | (x) >> (r))
+#define rol(x, k) SHA_ROT(x, k, 32-(k))
+#define ror(x, k) SHA_ROT(x, 32-(k), k)
+#endif
+
+#define blk0le(i) (block[i] = (ror(block[i], 8)&0xFF00FF00) \
+    |(rol(block[i], 8)&0x00FF00FF))
+#define blk0be(i) block[i]
+#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
+    ^block[(i+2)&15]^block[i&15], 1))
+
+/*
+ * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
+ *
+ * Rl0() for little-endian and Rb0() for big-endian.  Endianness is 
+ * determined at run-time.
+ */
+#define Rl0(v, w, x, y, z, i) \
+    z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
+#define Rb0(v, w, x, y, z, i) \
+    z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
+#define R1(v, w, x, y, z, i) \
+    z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
+#define R2(v, w, x, y, z, i) \
+    z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v, 5);w=ror(w, 2);
+#define R3(v, w, x, y, z, i) \
+    z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v, 5);w=ror(w, 2);
+#define R4(v, w, x, y, z, i) \
+    z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v, 5);w=ror(w, 2);
+
+/*
+ * Hash a single 512-bit block. This is the core of the algorithm.
+ */
+#define a qq[0]
+#define b qq[1]
+#define c qq[2]
+#define d qq[3]
+#define e qq[4]
+
+static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
+{
+  unsigned int qq[5]; /* a, b, c, d, e; */
+  static int one = 1;
+  unsigned int block[16];
+  SyMemcpy(buffer, (void *)block, 64);
+  SyMemcpy(state, qq, 5*sizeof(unsigned int));
+
+  /* Copy context->state[] to working vars */
+  /*
+  a = state[0];
+  b = state[1];
+  c = state[2];
+  d = state[3];
+  e = state[4];
+  */
+
+  /* 4 rounds of 20 operations each. Loop unrolled. */
+  if( 1 == *(unsigned char*)&one ){
+    Rl0(a, b, c, d, e, 0); Rl0(e, a, b, c, d, 1); Rl0(d, e, a, b, c, 2); Rl0(c, d, e, a, b, 3);
+    Rl0(b, c, d, e, a, 4); Rl0(a, b, c, d, e, 5); Rl0(e, a, b, c, d, 6); Rl0(d, e, a, b, c, 7);
+    Rl0(c, d, e, a, b, 8); Rl0(b, c, d, e, a, 9); Rl0(a, b, c, d, e, 10); Rl0(e, a, b, c, d, 11);
+    Rl0(d, e, a, b, c, 12); Rl0(c, d, e, a, b, 13); Rl0(b, c, d, e, a, 14); Rl0(a, b, c, d, e, 15);
+  }else{
+    Rb0(a, b, c, d, e, 0); Rb0(e, a, b, c, d, 1); Rb0(d, e, a, b, c, 2); Rb0(c, d, e, a, b, 3);
+    Rb0(b, c, d, e, a, 4); Rb0(a, b, c, d, e, 5); Rb0(e, a, b, c, d, 6); Rb0(d, e, a, b, c, 7);
+    Rb0(c, d, e, a, b, 8); Rb0(b, c, d, e, a, 9); Rb0(a, b, c, d, e, 10); Rb0(e, a, b, c, d, 11);
+    Rb0(d, e, a, b, c, 12); Rb0(c, d, e, a, b, 13); Rb0(b, c, d, e, a, 14); Rb0(a, b, c, d, e, 15);
+  }
+  R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19);
+  R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23);
+  R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27);
+  R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31);
+  R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35);
+  R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39);
+  R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43);
+  R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47);
+  R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51);
+  R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55);
+  R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59);
+  R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63);
+  R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67);
+  R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71);
+  R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75);
+  R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79);
+
+  /* Add the working vars back into context.state[] */
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+  state[4] += e;
+}
+#undef a
+#undef b
+#undef c
+#undef d
+#undef e
+/*
+ * SHA1Init - Initialize new context
+ */
+JX9_PRIVATE void SHA1Init(SHA1Context *context){
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+/*
+ * Run your data through this.
+ */
+JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len){
+    unsigned int i, j;
+
+    j = context->count[0];
+    if ((context->count[0] += len << 3) < j)
+       context->count[1] += (len>>29)+1;
+    j = (j >> 3) & 63;
+    if ((j + len) > 63) {
+               (void)SyMemcpy(data, &context->buffer[j],  (i = 64-j));
+       SHA1Transform(context->state, context->buffer);
+       for ( ; i + 63 < len; i += 64)
+           SHA1Transform(context->state, &data[i]);
+       j = 0;
+    } else {
+       i = 0;
+    }
+       (void)SyMemcpy(&data[i], &context->buffer[j], len - i);
+}
+/*
+ * Add padding and return the message digest.
+ */
+JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]){
+    unsigned int i;
+    unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+       finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+        >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1Update(context, (const unsigned char *)"\200", 1);
+    while ((context->count[0] & 504) != 448)
+       SHA1Update(context, (const unsigned char *)"\0", 1);
+    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+
+    if (digest) {
+       for (i = 0; i < 20; i++)
+           digest[i] = (unsigned char)
+               ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+}
+#undef Rl0
+#undef Rb0
+#undef R1
+#undef R2
+#undef R3
+#undef R4
+
+JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20])
+{
+       SHA1Context sCtx;
+       SHA1Init(&sCtx);
+       SHA1Update(&sCtx, (const unsigned char *)pIn, nLen);
+       SHA1Final(&sCtx, zDigest);
+       return SXRET_OK;
+}
+static const sxu32 crc32_table[] = {
+       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 
+       0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 
+       0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 
+       0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 
+       0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 
+       0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 
+       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 
+       0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 
+       0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 
+       0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 
+       0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 
+       0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 
+       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 
+       0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 
+       0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 
+       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 
+       0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 
+       0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 
+       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 
+       0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 
+       0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 
+       0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 
+       0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 
+       0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 
+       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 
+       0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 
+       0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 
+       0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 
+       0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 
+       0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 
+       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 
+       0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 
+       0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 
+       0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 
+       0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 
+       0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 
+       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 
+       0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 
+       0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 
+       0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 
+       0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 
+       0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 
+       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 
+       0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 
+       0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 
+       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 
+       0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 
+       0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 
+       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 
+       0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 
+       0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 
+       0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 
+       0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 
+       0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 
+       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 
+       0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 
+       0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 
+       0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 
+       0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 
+       0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 
+       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 
+       0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 
+       0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 
+       0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, 
+};
+#define CRC32C(c, d) (c = ( crc32_table[(c ^ (d)) & 0xFF] ^ (c>>8) ) )
+static sxu32 SyCrc32Update(sxu32 crc32, const void *pSrc, sxu32 nLen)
+{
+       register unsigned char *zIn = (unsigned char *)pSrc;
+       unsigned char *zEnd;
+       if( zIn == 0 ){
+               return crc32;
+       }
+       zEnd = &zIn[nLen];
+       for(;;){
+               if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
+               if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
+               if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
+               if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
+       }
+               
+       return crc32;
+}
+JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen)
+{
+       return SyCrc32Update(SXU32_HIGH, pSrc, nLen);
+}
+#endif /* JX9_DISABLE_HASH_FUNC */
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData)
+{
+       static const unsigned char zHexTab[] = "0123456789abcdef";
+       const unsigned char *zIn, *zEnd;
+       unsigned char zOut[3];
+       sxi32 rc;
+#if defined(UNTRUST)
+       if( pIn == 0 || xConsumer == 0 ){
+               return SXERR_EMPTY;
+       }
+#endif
+       zIn   = (const unsigned char *)pIn;
+       zEnd  = &zIn[nLen];
+       for(;;){
+               if( zIn >= zEnd  ){
+                       break;
+               }
+               zOut[0] = zHexTab[zIn[0] >> 4];  zOut[1] = zHexTab[zIn[0] & 0x0F];
+               rc = xConsumer((const void *)zOut, sizeof(char)*2, pConsumerData);
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+               zIn++; 
+       }
+       return SXRET_OK;
+}
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+JX9_PRIVATE void SyBigEndianPack32(unsigned char *buf,sxu32 nb)
+{
+       buf[3] = nb & 0xFF ; nb >>=8;
+       buf[2] = nb & 0xFF ; nb >>=8;
+       buf[1] = nb & 0xFF ; nb >>=8;
+       buf[0] = (unsigned char)nb ;
+}
+JX9_PRIVATE void SyBigEndianUnpack32(const unsigned char *buf,sxu32 *uNB)
+{
+       *uNB = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24);
+}
+JX9_PRIVATE void SyBigEndianPack16(unsigned char *buf,sxu16 nb)
+{
+       buf[1] = nb & 0xFF ; nb >>=8;
+       buf[0] = (unsigned char)nb ;
+}
+JX9_PRIVATE void SyBigEndianUnpack16(const unsigned char *buf,sxu16 *uNB)
+{
+       *uNB = buf[1] + (buf[0] << 8);
+}
+JX9_PRIVATE void SyBigEndianPack64(unsigned char *buf,sxu64 n64)
+{
+       buf[7] = n64 & 0xFF; n64 >>=8;
+       buf[6] = n64 & 0xFF; n64 >>=8;
+       buf[5] = n64 & 0xFF; n64 >>=8;
+       buf[4] = n64 & 0xFF; n64 >>=8;
+       buf[3] = n64 & 0xFF; n64 >>=8;
+       buf[2] = n64 & 0xFF; n64 >>=8;
+       buf[1] = n64 & 0xFF; n64 >>=8;
+       buf[0] = (sxu8)n64 ; 
+}
+JX9_PRIVATE void SyBigEndianUnpack64(const unsigned char *buf,sxu64 *n64)
+{
+       sxu32 u1,u2;
+       u1 = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24);
+       u2 = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24);
+       *n64 = (((sxu64)u2) << 32) | u1;
+}
+JX9_PRIVATE sxi32 SyBlobAppendBig64(SyBlob *pBlob,sxu64 n64)
+{
+       unsigned char zBuf[8];
+       sxi32 rc;
+       SyBigEndianPack64(zBuf,n64);
+       rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
+       return rc;
+}
+JX9_PRIVATE sxi32 SyBlobAppendBig32(SyBlob *pBlob,sxu32 n32)
+{
+       unsigned char zBuf[4];
+       sxi32 rc;
+       SyBigEndianPack32(zBuf,n32);
+       rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
+       return rc;
+}
+JX9_PRIVATE sxi32 SyBlobAppendBig16(SyBlob *pBlob,sxu16 n16)
+{
+       unsigned char zBuf[2];
+       sxi32 rc;
+       SyBigEndianPack16(zBuf,n16);
+       rc = SyBlobAppend(pBlob,(const void *)zBuf,sizeof(zBuf));
+       return rc;
+}
+JX9_PRIVATE void SyTimeFormatToDos(Sytm *pFmt,sxu32 *pOut)
+{
+       sxi32 nDate,nTime;
+       nDate = ((pFmt->tm_year - 1980) << 9) + (pFmt->tm_mon << 5) + pFmt->tm_mday;
+       nTime = (pFmt->tm_hour << 11) + (pFmt->tm_min << 5)+ (pFmt->tm_sec >> 1);
+       *pOut = (nDate << 16) | nTime;
+}
+JX9_PRIVATE void SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut)
+{
+       sxu16 nDate;
+       sxu16 nTime;
+       nDate = nDosDate >> 16;
+       nTime = nDosDate & 0xFFFF;
+       pOut->tm_isdst  = 0;
+       pOut->tm_year   = 1980 + (nDate >> 9);
+       pOut->tm_mon    = (nDate % (1<<9))>>5;
+       pOut->tm_mday   = (nDate % (1<<9))&0x1F;
+       pOut->tm_hour   = nTime >> 11;
+       pOut->tm_min    = (nTime % (1<<11)) >> 5;
+       pOut->tm_sec    = ((nTime % (1<<11))& 0x1F )<<1;
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_memobj.c
+ * MD5: 8692d7f4cb297c0946066b4a9034c637
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable <chm@symisc.net> $ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+/* This file manage low-level stuff related to indexed memory objects [i.e: jx9_value] */
+/*
+ * Notes on memory objects [i.e: jx9_value].
+ * 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.
+ */
+/*
+ * Convert a 64-bit IEEE double into a 64-bit signed integer.
+ * If the double is too large, return 0x8000000000000000.
+ *
+ * Most systems appear to do this simply by assigning ariables and without
+ * the extra range tests.
+ * But there are reports that windows throws an expection if the floating 
+ * point value is out of range.
+ */
+static sxi64 MemObjRealToInt(jx9_value *pObj)
+{
+#ifdef JX9_OMIT_FLOATING_POINT
+       /* Real and 64bit integer are the same when floating point arithmetic
+        * is omitted from the build.
+        */
+       return pObj->x.rVal;
+#else
+ /*
+  ** Many compilers we encounter do not define constants for the
+  ** minimum and maximum 64-bit integers, or they define them
+  ** inconsistently.  And many do not understand the "LL" notation.
+  ** So we define our own static constants here using nothing
+  ** larger than a 32-bit integer constant.
+  */
+  static const sxi64 maxInt = LARGEST_INT64;
+  static const sxi64 minInt = SMALLEST_INT64;
+  jx9_real r = pObj->x.rVal;
+  if( r<(jx9_real)minInt ){
+    return minInt;
+  }else if( r>(jx9_real)maxInt ){
+    /* minInt is correct here - not maxInt.  It turns out that assigning
+    ** a very large positive number to an integer results in a very large
+    ** negative integer.  This makes no sense, but it is what x86 hardware
+    ** does so for compatibility we will do the same in software. */
+    return minInt;
+  }else{
+    return (sxi64)r;
+  }
+#endif
+}
+/*
+ * Convert a raw token value typically a stream of digit [i.e: hex, octal, binary or decimal] 
+ * to a 64-bit integer.
+ */
+JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pVal)
+{
+       sxi64 iVal = 0;
+       if( pVal->nByte <= 0 ){
+               return 0;
+       }
+       if( pVal->zString[0] == '0' ){
+               sxi32 c;
+               if( pVal->nByte == sizeof(char) ){
+                       return 0;
+               }
+               c = pVal->zString[1];
+               if( c  == 'x' || c == 'X' ){
+                       /* Hex digit stream */
+                       SyHexStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
+               }else if( c == 'b' || c == 'B' ){
+                       /* Binary digit stream */
+                       SyBinaryStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
+               }else{
+                       /* Octal digit stream */
+                       SyOctalStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
+               }
+       }else{
+               /* Decimal digit stream */
+               SyStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
+       }
+       return iVal;
+}
+/*
+ * Return some kind of 64-bit integer value which is the best we can
+ * do at representing the value that pObj describes as a string
+ * representation.
+ */
+static sxi64 MemObjStringToInt(jx9_value *pObj)
+{
+       SyString sVal;
+       SyStringInitFromBuf(&sVal, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
+       return jx9TokenValueToInt64(&sVal);     
+}
+/*
+ * Return some kind of integer value which is the best we can
+ * do at representing the value that pObj describes as an integer.
+ * If pObj is an integer, then the value is exact. If pObj is
+ * a floating-point then  the value returned is the integer part.
+ * If pObj is a string, then we make an attempt to convert it into
+ * a integer and return that. 
+ * If pObj represents a NULL value, return 0.
+ */
+static sxi64 MemObjIntValue(jx9_value *pObj)
+{
+       sxi32 iFlags;
+       iFlags = pObj->iFlags;
+       if (iFlags & MEMOBJ_REAL ){
+               return MemObjRealToInt(&(*pObj));
+       }else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
+               return pObj->x.iVal;
+       }else if (iFlags & MEMOBJ_STRING) {
+               return MemObjStringToInt(&(*pObj));
+       }else if( iFlags & MEMOBJ_NULL ){
+               return 0;
+       }else if( iFlags & MEMOBJ_HASHMAP ){
+               jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
+               sxu32 n = pMap->nEntry;
+               jx9HashmapUnref(pMap);
+               /* Return total number of entries in the hashmap */
+               return n; 
+       }else if(iFlags & MEMOBJ_RES ){
+               return pObj->x.pOther != 0;
+       }
+       /* CANT HAPPEN */
+       return 0;
+}
+/*
+ * Return some kind of real value which is the best we can
+ * do at representing the value that pObj describes as a real.
+ * If pObj is a real, then the value is exact.If pObj is an
+ * integer then the integer  is promoted to real and that value
+ * is returned.
+ * If pObj is a string, then we make an attempt to convert it
+ * into a real and return that. 
+ * If pObj represents a NULL value, return 0.0
+ */
+static jx9_real MemObjRealValue(jx9_value *pObj)
+{
+       sxi32 iFlags;
+       iFlags = pObj->iFlags;
+       if( iFlags & MEMOBJ_REAL ){
+               return pObj->x.rVal;
+       }else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
+               return (jx9_real)pObj->x.iVal;
+       }else if (iFlags & MEMOBJ_STRING){
+               SyString sString;
+#ifdef JX9_OMIT_FLOATING_POINT
+               jx9_real rVal = 0;
+#else
+               jx9_real rVal = 0.0;
+#endif
+               SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
+               if( SyBlobLength(&pObj->sBlob) > 0 ){
+                       /* Convert as much as we can */
+#ifdef JX9_OMIT_FLOATING_POINT
+                       rVal = MemObjStringToInt(&(*pObj));
+#else
+                       SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0);
+#endif
+               }
+               return rVal;
+       }else if( iFlags & MEMOBJ_NULL ){
+#ifdef JX9_OMIT_FLOATING_POINT
+               return 0;
+#else
+               return 0.0;
+#endif
+       }else if( iFlags & MEMOBJ_HASHMAP ){
+               /* Return the total number of entries in the hashmap */
+               jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
+               jx9_real n = (jx9_real)pMap->nEntry;
+               jx9HashmapUnref(pMap);
+               return n;
+       }else if(iFlags & MEMOBJ_RES ){
+               return (jx9_real)(pObj->x.pOther != 0);
+       }
+       /* NOT REACHED  */
+       return 0;
+}
+/* 
+ * Return the string representation of a given jx9_value.
+ * This function never fail and always return SXRET_OK.
+ */
+static sxi32 MemObjStringValue(SyBlob *pOut,jx9_value *pObj)
+{
+       if( pObj->iFlags & MEMOBJ_REAL ){
+               SyBlobFormat(&(*pOut), "%.15g", pObj->x.rVal);
+       }else if( pObj->iFlags & MEMOBJ_INT ){
+               SyBlobFormat(&(*pOut), "%qd", pObj->x.iVal);
+               /* %qd (BSD quad) is equivalent to %lld in the libc printf */
+       }else if( pObj->iFlags & MEMOBJ_BOOL ){
+               if( pObj->x.iVal ){
+                       SyBlobAppend(&(*pOut),"true", sizeof("true")-1);
+               }else{
+                       SyBlobAppend(&(*pOut),"false", sizeof("false")-1);
+               }
+       }else if( pObj->iFlags & MEMOBJ_HASHMAP ){
+               /* Serialize JSON object or array */
+               jx9JsonSerialize(pObj,pOut);
+               jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
+       }else if(pObj->iFlags & MEMOBJ_RES ){
+               SyBlobFormat(&(*pOut), "ResourceID_%#x", pObj->x.pOther);
+       }
+       return SXRET_OK;
+}
+/*
+ * Return some kind of boolean value which is the best we can do
+ * at representing the value that pObj describes as a boolean.
+ * When converting to boolean, the following values are considered FALSE:
+ * NULL
+ * the boolean FALSE itself.
+ * the integer 0 (zero).
+ * the real 0.0 (zero).
+ * the empty string, a stream of zero [i.e: "0", "00", "000", ...] and the string
+ * "false".
+ * an array with zero elements. 
+ */
+static sxi32 MemObjBooleanValue(jx9_value *pObj)
+{
+       sxi32 iFlags;   
+       iFlags = pObj->iFlags;
+       if (iFlags & MEMOBJ_REAL ){
+#ifdef JX9_OMIT_FLOATING_POINT
+               return pObj->x.rVal ? 1 : 0;
+#else
+               return pObj->x.rVal != 0.0 ? 1 : 0;
+#endif
+       }else if( iFlags & MEMOBJ_INT ){
+               return pObj->x.iVal ? 1 : 0;
+       }else if (iFlags & MEMOBJ_STRING) {
+               SyString sString;
+               SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
+               if( sString.nByte == 0 ){
+                       /* Empty string */
+                       return 0;
+               }else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString, "true", sizeof("true")-1) == 0) ||
+                       (sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString, "on", sizeof("on")-1) == 0) ||
+                       (sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString, "yes", sizeof("yes")-1) == 0) ){
+                               return 1;
+               }else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString, "false", sizeof("false")-1) == 0 ){
+                       return 0;
+               }else{
+                       const char *zIn, *zEnd;
+                       zIn = sString.zString;
+                       zEnd = &zIn[sString.nByte];
+                       while( zIn < zEnd && zIn[0] == '0' ){
+                               zIn++;
+                       }
+                       return zIn >= zEnd ? 0 : 1;
+               }
+       }else if( iFlags & MEMOBJ_NULL ){
+               return 0;
+       }else if( iFlags & MEMOBJ_HASHMAP ){
+               jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
+               sxu32 n = pMap->nEntry;
+               jx9HashmapUnref(pMap);
+               return n > 0 ? TRUE : FALSE;
+       }else if(iFlags & MEMOBJ_RES ){
+               return pObj->x.pOther != 0;
+       }
+       /* NOT REACHED */
+       return 0;
+}
+/*
+ * If the jx9_value is of type real, try to make it an integer also.
+ */
+static sxi32 MemObjTryIntger(jx9_value *pObj)
+{
+       sxi64 iVal = MemObjRealToInt(&(*pObj));
+  /* Only mark the value as an integer if
+  **
+  **    (1) the round-trip conversion real->int->real is a no-op, and
+  **    (2) The integer is neither the largest nor the smallest
+  **        possible integer
+  **
+  ** The second and third terms in the following conditional enforces
+  ** the second condition under the assumption that addition overflow causes
+  ** values to wrap around.  On x86 hardware, the third term is always
+  ** true and could be omitted.  But we leave it in because other
+  ** architectures might behave differently.
+  */
+       if( pObj->x.rVal ==(jx9_real)iVal && iVal>SMALLEST_INT64 && iVal<LARGEST_INT64 ){
+               pObj->x.iVal = iVal; 
+               pObj->iFlags = MEMOBJ_INT;
+       }
+       return SXRET_OK;
+}
+/*
+ * Convert a jx9_value to type integer.Invalidate any prior representations.
+ */
+JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj)
+{
+       if( (pObj->iFlags & MEMOBJ_INT) == 0 ){
+               /* Preform the conversion */
+               pObj->x.iVal = MemObjIntValue(&(*pObj));
+               /* Invalidate any prior representations */
+               SyBlobRelease(&pObj->sBlob);
+               MemObjSetType(pObj, MEMOBJ_INT);
+       }
+       return SXRET_OK;
+}
+/*
+ * Convert a jx9_value to type real (Try to get an integer representation also).
+ * Invalidate any prior representations
+ */
+JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj)
+{
+       if((pObj->iFlags & MEMOBJ_REAL) == 0 ){
+               /* Preform the conversion */
+               pObj->x.rVal = MemObjRealValue(&(*pObj));
+               /* Invalidate any prior representations */
+               SyBlobRelease(&pObj->sBlob);
+               MemObjSetType(pObj, MEMOBJ_REAL);
+       }
+       return SXRET_OK;
+}
+/*
+ * Convert a jx9_value to type boolean.Invalidate any prior representations.
+ */
+JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj)
+{
+       if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){
+               /* Preform the conversion */
+               pObj->x.iVal = MemObjBooleanValue(&(*pObj));
+               /* Invalidate any prior representations */
+               SyBlobRelease(&pObj->sBlob);
+               MemObjSetType(pObj, MEMOBJ_BOOL);
+       }
+       return SXRET_OK;
+}
+/*
+ * Convert a jx9_value to type string.Prior representations are NOT invalidated.
+ */
+JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj)
+{
+       sxi32 rc = SXRET_OK;
+       if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
+               /* Perform the conversion */
+               SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */
+               rc = MemObjStringValue(&pObj->sBlob, &(*pObj));
+               MemObjSetType(pObj, MEMOBJ_STRING);
+       }
+       return rc;
+}
+/*
+ * Nullify a jx9_value.In other words invalidate any prior
+ * representation.
+ */
+JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj)
+{
+       return jx9MemObjRelease(pObj);
+}
+/*
+ * Convert a jx9_value to type array.Invalidate any prior representations.
+  * According to the JX9 language reference manual.
+  *   For any of the types: integer, float, string, boolean converting a value
+  *   to an array results in an array with a single element with index zero 
+  *   and the value of the scalar which was converted.
+  */
+JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj)
+{
+       if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
+               jx9_hashmap *pMap;
+               /* Allocate a new hashmap instance */
+               pMap = jx9NewHashmap(pObj->pVm, 0, 0);
+               if( pMap == 0 ){
+                       return SXERR_MEM;
+               }
+               if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){
+                       /* 
+                        * According to the JX9 language reference manual.
+                        *   For any of the types: integer, float, string, boolean converting a value
+                        *   to an array results in an array with a single element with index zero 
+                        *   and the value of the scalar which was converted.
+                        */
+                       /* Insert a single element */
+                       jx9HashmapInsert(pMap, 0/* Automatic index assign */, &(*pObj));
+                       SyBlobRelease(&pObj->sBlob);
+               }
+               /* Invalidate any prior representation */
+               MemObjSetType(pObj, MEMOBJ_HASHMAP);
+               pObj->x.pOther = pMap;
+       }
+       return SXRET_OK;
+}
+/*
+ * Return a pointer to the appropriate convertion method associated 
+ * with the given type. 
+ * Note on type juggling.
+ * Accoding to the JX9 language reference manual
+ *  JX9 does not require (or support) explicit type definition in variable
+ *  declaration; a variable's type is determined by the context in which
+ *  the variable is used. That is to say, if a string value is assigned 
+ *  to variable $var, $var becomes a string. If an integer value is then
+ *  assigned to $var, it becomes an integer. 
+ */
+JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags)
+{
+       if( iFlags & MEMOBJ_STRING ){
+               return jx9MemObjToString;
+       }else if( iFlags & MEMOBJ_INT ){
+               return jx9MemObjToInteger;
+       }else if( iFlags & MEMOBJ_REAL ){
+               return jx9MemObjToReal;
+       }else if( iFlags & MEMOBJ_BOOL ){
+               return jx9MemObjToBool;
+       }else if( iFlags & MEMOBJ_HASHMAP ){
+               return jx9MemObjToHashmap;
+       }
+       /* NULL cast */
+       return jx9MemObjToNull;
+}
+/*
+ * Check whether the jx9_value is numeric [i.e: int/float/bool] or looks
+ * like a numeric number [i.e: if the jx9_value is of type string.].
+ * Return TRUE if numeric.FALSE otherwise.
+ */
+JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj)
+{
+       if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){
+               return TRUE;
+       }else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
+               return FALSE;
+       }else if( pObj->iFlags & MEMOBJ_STRING ){
+               SyString sStr;
+               sxi32 rc;
+               SyStringInitFromBuf(&sStr, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
+               if( sStr.nByte <= 0 ){
+                       /* Empty string */
+                       return FALSE;
+               }
+               /* Check if the string representation looks like a numeric number */
+               rc = SyStrIsNumeric(sStr.zString, sStr.nByte, 0, 0);
+               return rc == SXRET_OK ? TRUE : FALSE;
+       }
+       /* NOT REACHED */
+       return FALSE;
+}
+/*
+ * Check whether the jx9_value is empty.Return TRUE if empty.
+ * FALSE otherwise.
+ * An jx9_value is considered empty if the following are true:
+ * NULL value.
+ * Boolean FALSE.
+ * Integer/Float with a 0 (zero) value.
+ * An empty string or a stream of 0 (zero) [i.e: "0", "00", "000", ...].
+ * An empty array.
+ * NOTE
+ *  OBJECT VALUE MUST NOT BE MODIFIED.
+ */
+JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj)
+{
+       if( pObj->iFlags & MEMOBJ_NULL ){
+               return TRUE;
+       }else if( pObj->iFlags & MEMOBJ_INT ){
+               return pObj->x.iVal == 0 ? TRUE : FALSE;
+       }else if( pObj->iFlags & MEMOBJ_REAL ){
+               return pObj->x.rVal == (jx9_real)0 ? TRUE : FALSE;
+       }else if( pObj->iFlags & MEMOBJ_BOOL ){
+               return !pObj->x.iVal;
+       }else if( pObj->iFlags & MEMOBJ_STRING ){
+               if( SyBlobLength(&pObj->sBlob) <= 0 ){
+                       return TRUE;
+               }else{
+                       const char *zIn, *zEnd;
+                       zIn = (const char *)SyBlobData(&pObj->sBlob);
+                       zEnd = &zIn[SyBlobLength(&pObj->sBlob)];
+                       while( zIn < zEnd ){
+                               if( zIn[0] != '0' ){
+                                       break;
+                               }
+                               zIn++;
+                       }
+                       return zIn >= zEnd ? TRUE : FALSE;
+               }
+       }else if( pObj->iFlags & MEMOBJ_HASHMAP ){
+               jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
+               return pMap->nEntry == 0 ? TRUE : FALSE;
+       }else if ( pObj->iFlags & (MEMOBJ_RES) ){
+               return FALSE;
+       }
+       /* Assume empty by default */
+       return TRUE;
+}
+/*
+ * Convert a jx9_value so that it has types MEMOBJ_REAL or MEMOBJ_INT
+ * or both.
+ * Invalidate any prior representations. Every effort is made to force
+ * the conversion, even if the input is a string that does not look 
+ * completely like a number.Convert as much of the string as we can
+ * and ignore the rest.
+ */
+JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj)
+{
+       if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){
+               if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){
+                       if( pObj->iFlags & MEMOBJ_NULL ){
+                               pObj->x.iVal = 0;
+                       }
+                       MemObjSetType(pObj, MEMOBJ_INT);
+               }
+               /* Already numeric */
+               return  SXRET_OK;
+       }
+       if( pObj->iFlags & MEMOBJ_STRING ){
+               sxi32 rc = SXERR_INVALID;
+               sxu8 bReal = FALSE;
+               SyString sString;
+               SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
+               /* Check if the given string looks like a numeric number */
+               if( sString.nByte > 0 ){
+                       rc = SyStrIsNumeric(sString.zString, sString.nByte, &bReal, 0);
+               }
+               if( bReal ){
+                       jx9MemObjToReal(&(*pObj));
+               }else{
+                       if( rc != SXRET_OK ){
+                               /* The input does not look at all like a number, set the value to 0 */
+                               pObj->x.iVal = 0;
+                       }else{
+                               /* Convert as much as we can */
+                               pObj->x.iVal = MemObjStringToInt(&(*pObj));
+                       }
+                       MemObjSetType(pObj, MEMOBJ_INT);
+                       SyBlobRelease(&pObj->sBlob);
+               }
+       }else if(pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)){
+               jx9MemObjToInteger(pObj);
+       }else{
+               /* Perform a blind cast */
+               jx9MemObjToReal(&(*pObj));
+       }
+       return SXRET_OK;
+}
+/*
+ * Try a get an integer representation of the given jx9_value.
+ * If the jx9_value is not of type real, this function is a no-op.
+ */
+JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj)
+{
+       if( pObj->iFlags & MEMOBJ_REAL ){
+               /* Work only with reals */
+               MemObjTryIntger(&(*pObj));
+       }
+       return SXRET_OK;
+}
+/*
+ * Initialize a jx9_value to the null type.
+ */
+JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj)
+{
+       /* Zero the structure */
+       SyZero(pObj, sizeof(jx9_value));
+       /* Initialize fields */
+       pObj->pVm = pVm;
+       SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
+       /* Set the NULL type */
+       pObj->iFlags = MEMOBJ_NULL;
+       return SXRET_OK;
+}
+/*
+ * Initialize a jx9_value to the integer type.
+ */
+JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal)
+{
+       /* Zero the structure */
+       SyZero(pObj, sizeof(jx9_value));
+       /* Initialize fields */
+       pObj->pVm = pVm;
+       SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
+       /* Set the desired type */
+       pObj->x.iVal = iVal;
+       pObj->iFlags = MEMOBJ_INT;
+       return SXRET_OK;
+}
+/*
+ * Initialize a jx9_value to the boolean type.
+ */
+JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal)
+{
+       /* Zero the structure */
+       SyZero(pObj, sizeof(jx9_value));
+       /* Initialize fields */
+       pObj->pVm = pVm;
+       SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
+       /* Set the desired type */
+       pObj->x.iVal = iVal ? 1 : 0;
+       pObj->iFlags = MEMOBJ_BOOL;
+       return SXRET_OK;
+}
+#if 0
+/*
+ * Initialize a jx9_value to the real type.
+ */
+JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal)
+{
+       /* Zero the structure */
+       SyZero(pObj, sizeof(jx9_value));
+       /* Initialize fields */
+       pObj->pVm = pVm;
+       SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
+       /* Set the desired type */
+       pObj->x.rVal = rVal;
+       pObj->iFlags = MEMOBJ_REAL;
+       return SXRET_OK;
+}
+#endif
+/*
+ * Initialize a jx9_value to the array type.
+ */
+JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray)
+{
+       /* Zero the structure */
+       SyZero(pObj, sizeof(jx9_value));
+       /* Initialize fields */
+       pObj->pVm = pVm;
+       SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
+       /* Set the desired type */
+       pObj->iFlags = MEMOBJ_HASHMAP;
+       pObj->x.pOther = pArray;
+       return SXRET_OK;
+}
+/*
+ * Initialize a jx9_value to the string type.
+ */
+JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal)
+{
+       /* Zero the structure */
+       SyZero(pObj, sizeof(jx9_value));
+       /* Initialize fields */
+       pObj->pVm = pVm;
+       SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
+       if( pVal ){
+               /* Append contents */
+               SyBlobAppend(&pObj->sBlob, (const void *)pVal->zString, pVal->nByte);
+       }
+       /* Set the desired type */
+       pObj->iFlags = MEMOBJ_STRING;
+       return SXRET_OK;
+}
+/*
+ * Append some contents to the internal buffer of a given jx9_value.
+ * If the given jx9_value is not of type string, this function
+ * invalidate any prior representation and set the string type.
+ * Then a simple append operation is performed.
+ */
+JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen)
+{
+       sxi32 rc;
+       if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
+               /* Invalidate any prior representation */
+               jx9MemObjRelease(pObj);
+               MemObjSetType(pObj, MEMOBJ_STRING);
+       }
+       /* Append contents */
+       rc = SyBlobAppend(&pObj->sBlob, zData, nLen);
+       return rc;
+}
+#if 0
+/*
+ * Format and append some contents to the internal buffer of a given jx9_value.
+ * If the given jx9_value is not of type string, this function invalidate
+ * any prior representation and set the string type.
+ * Then a simple format and append operation is performed.
+ */
+JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap)
+{
+       sxi32 rc;
+       if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
+               /* Invalidate any prior representation */
+               jx9MemObjRelease(pObj);
+               MemObjSetType(pObj, MEMOBJ_STRING);
+       }
+       /* Format and append contents */
+       rc = SyBlobFormatAp(&pObj->sBlob, zFormat, ap);
+       return rc;
+}
+#endif
+/*
+ * Duplicate the contents of a jx9_value.
+ */
+JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest)
+{
+       jx9_hashmap *pMap = 0;
+       sxi32 rc;
+       if( pSrc->iFlags & MEMOBJ_HASHMAP ){
+               /* Increment reference count */
+               ((jx9_hashmap *)pSrc->x.pOther)->iRef++;
+       }
+       if( pDest->iFlags & MEMOBJ_HASHMAP ){
+               pMap = (jx9_hashmap *)pDest->x.pOther;
+       }
+       SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
+       rc = SXRET_OK;
+       if( SyBlobLength(&pSrc->sBlob) > 0 ){
+               SyBlobReset(&pDest->sBlob);
+               rc = SyBlobDup(&pSrc->sBlob, &pDest->sBlob);
+       }else{
+               if( SyBlobLength(&pDest->sBlob) > 0 ){
+                       SyBlobRelease(&pDest->sBlob);
+               }
+       }
+       if( pMap ){
+               jx9HashmapUnref(pMap);
+       }
+       return rc;
+}
+/*
+ * Duplicate the contents of a jx9_value but do not copy internal
+ * buffer contents, simply point to it.
+ */
+JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest)
+{
+       SyMemcpy((const void *)&(*pSrc), &(*pDest), 
+               sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
+       if( pSrc->iFlags & MEMOBJ_HASHMAP ){
+               /* Increment reference count */
+               ((jx9_hashmap *)pSrc->x.pOther)->iRef++;
+       }
+       if( SyBlobLength(&pDest->sBlob) > 0 ){
+               SyBlobRelease(&pDest->sBlob);
+       }
+       if( SyBlobLength(&pSrc->sBlob) > 0 ){
+               SyBlobReadOnly(&pDest->sBlob, SyBlobData(&pSrc->sBlob), SyBlobLength(&pSrc->sBlob));
+       }
+       return SXRET_OK;
+}
+/*
+ * Invalidate any prior representation of a given jx9_value.
+ */
+JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj)
+{
+       if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){
+               if( pObj->iFlags & MEMOBJ_HASHMAP ){
+                       jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
+               }
+               /* Release the internal buffer */
+               SyBlobRelease(&pObj->sBlob);
+               /* Invalidate any prior representation */
+               pObj->iFlags = MEMOBJ_NULL;
+       }
+       return SXRET_OK;
+}
+/*
+ * Compare two jx9_values.
+ * Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2
+ * or < 0 if pObj2 is greater than pObj1.
+ * Type comparison table taken from the JX9 language reference manual.
+ * Comparisons of $x with JX9 functions Expression
+ *              gettype()      empty()         is_null()       isset()         boolean : if($x)
+ * $x = "";    string      TRUE        FALSE   TRUE    FALSE
+ * $x = null   NULL        TRUE        TRUE    FALSE   FALSE
+ * var $x;         NULL        TRUE    TRUE    FALSE   FALSE
+ * $x is undefined     NULL    TRUE    TRUE    FALSE   FALSE
+ *  $x = array();      array   TRUE    FALSE   TRUE    FALSE
+ * $x = false;         boolean         TRUE    FALSE   TRUE    FALSE
+ * $x = true;  boolean         FALSE   FALSE   TRUE    TRUE
+ * $x = 1;         integer     FALSE   FALSE   TRUE    TRUE
+ * $x = 42;    integer         FALSE   FALSE   TRUE    TRUE
+ * $x = 0;         integer     TRUE    FALSE   TRUE    FALSE
+ * $x = -1;    integer         FALSE   FALSE   TRUE    TRUE
+ * $x = "1";   string  FALSE   FALSE   TRUE    TRUE
+ * $x = "0";   string  TRUE    FALSE   TRUE    FALSE
+ * $x = "-1";  string  FALSE   FALSE   TRUE    TRUE
+ * $x = "jx9";         string  FALSE   FALSE   TRUE    TRUE
+ * $x = "true"; string         FALSE   FALSE   TRUE    TRUE
+ * $x = "false"; string        FALSE   FALSE   TRUE    TRUE
+ *      Loose comparisons with == 
+ * TRUE        FALSE   1       0       -1      "1"     "0"     "-1"    NULL    array()         "jx9"   ""
+ * TRUE        TRUE    FALSE   TRUE    FALSE   TRUE    TRUE    FALSE   TRUE    FALSE   FALSE   TRUE    FALSE
+ * FALSE       FALSE   TRUE    FALSE   TRUE    FALSE   FALSE   TRUE    FALSE   TRUE    TRUE    FALSE   TRUE
+ * 1   TRUE    FALSE   TRUE    FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE
+ * 0   FALSE   TRUE    FALSE   TRUE    FALSE   FALSE   TRUE    FALSE   TRUE    FALSE   TRUE    TRUE
+ * -1  TRUE    FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE
+ * "1"         TRUE    FALSE   TRUE    FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE
+ * "0"         FALSE   TRUE    FALSE   TRUE    FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE
+ * "-1"        TRUE    FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE
+ * NULL        FALSE   TRUE    FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   TRUE    TRUE    FALSE   TRUE
+ * array()     FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   TRUE    TRUE    FALSE   FALSE
+ * "jx9"       TRUE    FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   TRUE    FALSE
+ * ""  FALSE   TRUE    FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   TRUE
+ *    Strict comparisons with === 
+ * TRUE        FALSE   1       0       -1      "1"     "0"     "-1"    NULL    array()         "jx9"   ""
+ * TRUE        TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE
+ * FALSE       FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE
+ * 1   FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE
+ * 0   FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE
+ * -1  FALSE   FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE
+ * "1"         FALSE   FALSE   FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE   FALSE
+ * "0"         FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE   FALSE
+ * "-1"        FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   FALSE   FALSE 
+ * NULL        FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   TRUE    FALSE   FALSE   FALSE
+ * array()     FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   TRUE    FALSE   FALSE
+ * "jx9"       FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   TRUE    FALSE
+ * ""      FALSE       FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   FALSE   TRUE
+ */
+JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest)
+{
+       sxi32 iComb; 
+       sxi32 rc;
+       if( bStrict ){
+               sxi32 iF1, iF2;
+               /* Strict comparisons with === */
+               iF1 = pObj1->iFlags;
+               iF2 = pObj2->iFlags;
+               if( iF1 != iF2 ){
+                       /* Not of the same type */
+                       return 1;
+               }
+       }
+       /* Combine flag together */
+       iComb = pObj1->iFlags|pObj2->iFlags;
+       if( iComb & (MEMOBJ_RES|MEMOBJ_BOOL) ){
+               /* Convert to boolean: Keep in mind FALSE < TRUE */
+               if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){
+                       jx9MemObjToBool(pObj1);
+               }
+               if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){
+                       jx9MemObjToBool(pObj2);
+               }
+               return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0));
+       }else if( iComb & MEMOBJ_NULL ){
+               if( (pObj1->iFlags & MEMOBJ_NULL) == 0 ){
+                       return 1;
+               }
+               if( (pObj2->iFlags & MEMOBJ_NULL) == 0 ){
+                       return -1;
+               }
+       }else if ( iComb & MEMOBJ_HASHMAP ){
+               /* Hashmap aka 'array' comparison */
+               if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
+                       /* Array is always greater */
+                       return -1;
+               }
+               if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){
+                       /* Array is always greater */
+                       return 1;
+               }
+               /* Perform the comparison */
+               rc = jx9HashmapCmp((jx9_hashmap *)pObj1->x.pOther, (jx9_hashmap *)pObj2->x.pOther, bStrict);
+               return rc;
+       }else if ( iComb & MEMOBJ_STRING ){
+               SyString s1, s2;
+               /* Perform a strict string comparison.*/
+               if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){
+                       jx9MemObjToString(pObj1);
+               }
+               if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){
+                       jx9MemObjToString(pObj2);
+               }
+               SyStringInitFromBuf(&s1, SyBlobData(&pObj1->sBlob), SyBlobLength(&pObj1->sBlob));
+               SyStringInitFromBuf(&s2, SyBlobData(&pObj2->sBlob), SyBlobLength(&pObj2->sBlob));
+               /*
+                * Strings are compared using memcmp(). If one value is an exact prefix of the
+                * other, then the shorter value is less than the longer value.
+                */
+               rc = SyMemcmp((const void *)s1.zString, (const void *)s2.zString, SXMIN(s1.nByte, s2.nByte));
+               if( rc == 0 ){
+                       if( s1.nByte != s2.nByte ){
+                               rc = s1.nByte < s2.nByte ? -1 : 1;
+                       }
+               }
+               return rc;
+       }else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){
+               /* Perform a numeric comparison if one of the operand is numeric(integer or real) */
+               if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
+                       jx9MemObjToNumeric(pObj1);
+               }
+               if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
+                       jx9MemObjToNumeric(pObj2);
+               }
+               if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) {
+                       jx9_real r1, r2;
+                       /* Compare as reals */
+                       if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
+                               jx9MemObjToReal(pObj1);
+                       }
+                       r1 = pObj1->x.rVal;     
+                       if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
+                               jx9MemObjToReal(pObj2);
+                       }
+                       r2 = pObj2->x.rVal;
+                       if( r1 > r2 ){
+                               return 1;
+                       }else if( r1 < r2 ){
+                               return -1;
+                       }
+                       return 0;
+               }else{
+                       /* Integer comparison */
+                       if( pObj1->x.iVal > pObj2->x.iVal ){
+                               return 1;
+                       }else if( pObj1->x.iVal < pObj2->x.iVal ){
+                               return -1;
+                       }
+                       return 0;
+               }
+       }
+       /* NOT REACHED */
+       SXUNUSED(iNest);
+       return 0;
+}
+/*
+ * Perform an addition operation of two jx9_values.
+ * The reason this function is implemented here rather than 'vm.c'
+ * is that the '+' operator is overloaded.
+ * That is, the '+' operator is used for arithmetic operation and also 
+ * used for operation on arrays [i.e: union]. When used with an array 
+ * The + operator returns the right-hand array appended to the left-hand array.
+ * For keys that exist in both arrays, the elements from the left-hand array
+ * will be used, and the matching elements from the right-hand array will
+ * be ignored.
+ * This function take care of handling all the scenarios.
+ */
+JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore)
+{
+       if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){
+                       /* Arithemtic operation */
+                       jx9MemObjToNumeric(pObj1);
+                       jx9MemObjToNumeric(pObj2);
+                       if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){
+                               /* Floating point arithmetic */
+                               jx9_real a, b;
+                               if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
+                                       jx9MemObjToReal(pObj1);
+                               }
+                               if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
+                                       jx9MemObjToReal(pObj2);
+                               }
+                               a = pObj1->x.rVal;
+                               b = pObj2->x.rVal;
+                               pObj1->x.rVal = a+b;
+                               MemObjSetType(pObj1, MEMOBJ_REAL);
+                               /* Try to get an integer representation also */
+                               MemObjTryIntger(&(*pObj1));
+                       }else{
+                               /* Integer arithmetic */
+                               sxi64 a, b;
+                               a = pObj1->x.iVal;
+                               b = pObj2->x.iVal;
+                               pObj1->x.iVal = a+b;
+                               MemObjSetType(pObj1, MEMOBJ_INT);
+                       }
+       }else{
+               if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){
+                       jx9_hashmap *pMap;
+                       sxi32 rc;
+                       if( bAddStore ){
+                               /* Do not duplicate the hashmap, use the left one since its an add&store operation.
+                                */
+                               if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){                            
+                                       /* Force a hashmap cast */
+                                       rc = jx9MemObjToHashmap(pObj1);
+                                       if( rc != SXRET_OK ){
+                                               jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
+                                               return rc;
+                                       }
+                               }
+                               /* Point to the structure that describe the hashmap */
+                               pMap = (jx9_hashmap *)pObj1->x.pOther;
+                       }else{
+                               /* Create a new hashmap */
+                               pMap = jx9NewHashmap(pObj1->pVm, 0, 0);
+                               if( pMap == 0){
+                                       jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
+                                       return SXERR_MEM;
+                               }
+                       }
+                       if( !bAddStore ){
+                               if(pObj1->iFlags & MEMOBJ_HASHMAP ){
+                                       /* Perform a hashmap duplication */
+                                       jx9HashmapDup((jx9_hashmap *)pObj1->x.pOther, pMap);
+                               }else{
+                                       if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){
+                                               /* Simple insertion */
+                                               jx9HashmapInsert(pMap, 0, pObj1);
+                                       }
+                               }
+                       }
+                       /* Perform the union */
+                       if(pObj2->iFlags & MEMOBJ_HASHMAP ){
+                               jx9HashmapUnion(pMap, (jx9_hashmap *)pObj2->x.pOther);
+                       }else{
+                               if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){
+                                       /* Simple insertion */
+                                       jx9HashmapInsert(pMap, 0, pObj2);
+                               }
+                       }
+                       /* Reflect the change */
+                       if( pObj1->iFlags & MEMOBJ_STRING ){
+                               SyBlobRelease(&pObj1->sBlob);
+                       }
+                       pObj1->x.pOther = pMap;
+                       MemObjSetType(pObj1, MEMOBJ_HASHMAP);
+               }
+       }
+       return SXRET_OK;
+}
+/*
+ * Return a printable representation of the type of a given 
+ * jx9_value.
+ */
+JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal)
+{
+       const char *zType = "";
+       if( pVal->iFlags & MEMOBJ_NULL ){
+               zType = "null";
+       }else if( pVal->iFlags & MEMOBJ_INT ){
+               zType = "int";
+       }else if( pVal->iFlags & MEMOBJ_REAL ){
+               zType = "float";
+       }else if( pVal->iFlags & MEMOBJ_STRING ){
+               zType = "string";
+       }else if( pVal->iFlags & MEMOBJ_BOOL ){
+               zType = "bool";
+       }else if( pVal->iFlags & MEMOBJ_HASHMAP ){
+               jx9_hashmap *pMap = (jx9_hashmap *)pVal->x.pOther;
+               if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
+                       zType = "JSON Object";
+               }else{
+                       zType = "JSON Array";
+               }
+       }else if( pVal->iFlags & MEMOBJ_RES ){
+               zType = "resource";
+       }
+       return zType;
+}
+/*
+ * Dump a jx9_value [i.e: get a printable representation of it's type and contents.].
+ * Store the dump in the given blob.
+ */
+JX9_PRIVATE sxi32 jx9MemObjDump(
+       SyBlob *pOut,      /* Store the dump here */
+       jx9_value *pObj   /* Dump this */
+       )
+{
+       sxi32 rc = SXRET_OK;
+       const char *zType;
+       /* Get value type first */
+       zType = jx9MemObjTypeDump(pObj);
+       SyBlobAppend(&(*pOut), zType, SyStrlen(zType));
+       if((pObj->iFlags & MEMOBJ_NULL) == 0 ){
+               SyBlobAppend(&(*pOut), "(", sizeof(char));
+               if( pObj->iFlags & MEMOBJ_HASHMAP ){
+                       jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
+                       SyBlobFormat(pOut,"%u ",pMap->nEntry);
+                       /* Dump hashmap entries */
+                       rc = jx9JsonSerialize(pObj,pOut);
+               }else{
+                       SyBlob *pContents = &pObj->sBlob;
+                       /* Get a printable representation of the contents */
+                       if((pObj->iFlags & MEMOBJ_STRING) == 0 ){
+                               MemObjStringValue(&(*pOut), &(*pObj));
+                       }else{
+                               /* Append length first */
+                               SyBlobFormat(&(*pOut), "%u '", SyBlobLength(&pObj->sBlob));
+                               if( SyBlobLength(pContents) > 0 ){
+                                       SyBlobAppend(&(*pOut), SyBlobData(pContents), SyBlobLength(pContents));
+                               }
+                               SyBlobAppend(&(*pOut), "'", sizeof(char));
+                       }
+               }
+               SyBlobAppend(&(*pOut), ")", sizeof(char));      
+       }
+#ifdef __WINNT__
+       SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n")-1);
+#else
+       SyBlobAppend(&(*pOut), "\n", sizeof(char));
+#endif
+       return rc;
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_parse.c
+ * MD5: d8fcac4c6cd7672f0103c0bf4a4b61fc
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: parse.c v1.2 FreeBSD 2012-12-11 00:46 stable <chm@symisc.net> $ */
+#ifndef JX9_AMALGAMATION
+#include "jx9Int.h"
+#endif
+/* Expression parser for the Jx9 programming language */
+/* Operators associativity */
+#define EXPR_OP_ASSOC_LEFT   0x01 /* Left associative operator */
+#define EXPR_OP_ASSOC_RIGHT  0x02 /* Right associative operator */
+#define EXPR_OP_NON_ASSOC    0x04 /* Non-associative operator */
+/* 
+ * Operators table
+ * This table is sorted by operators priority (highest to lowest) according
+ * the JX9 language reference manual.
+ * JX9 implements all the 60 JX9 operators and have introduced the eq and ne operators.
+ * The operators precedence table have been improved dramatically so that you can do same
+ * amazing things now such as array dereferencing, on the fly function call, anonymous function
+ * as array values, object member access on instantiation and so on.
+ * Refer to the following page for a full discussion on these improvements:
+ * http://jx9.symisc.net/features.html
+ */
+static const jx9_expr_op aOpTable[] = {
+                                     /* Postfix operators */
+       /* Precedence 2(Highest), left-associative */
+       { {".", sizeof(char)}, EXPR_OP_DOT,     2, EXPR_OP_ASSOC_LEFT ,   JX9_OP_MEMBER },
+       { {"[", sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_LOAD_IDX}, 
+       /* Precedence 3, non-associative  */
+       { {"++", sizeof(char)*2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC , JX9_OP_INCR}, 
+       { {"--", sizeof(char)*2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC , JX9_OP_DECR}, 
+                                     /* Unary operators */
+       /* Precedence 4, right-associative  */
+       { {"-", sizeof(char)},                 EXPR_OP_UMINUS,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UMINUS }, 
+       { {"+", sizeof(char)},                 EXPR_OP_UPLUS,     4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UPLUS }, 
+       { {"~", sizeof(char)},                 EXPR_OP_BITNOT,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_BITNOT }, 
+       { {"!", sizeof(char)},                 EXPR_OP_LOGNOT,    4, EXPR_OP_ASSOC_RIGHT, JX9_OP_LNOT }, 
+                                    /* Cast operators */
+       { {"(int)",    sizeof("(int)")-1   }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_INT  }, 
+       { {"(bool)",   sizeof("(bool)")-1  }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_BOOL }, 
+       { {"(string)", sizeof("(string)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_STR  }, 
+       { {"(float)",  sizeof("(float)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_REAL },
+       { {"(array)",  sizeof("(array)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY },   /* Not used, but reserved for future use */
+       { {"(object)",  sizeof("(object)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */
+                                  /* Binary operators */
+       /* Precedence 7, left-associative */ 
+       { {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MUL}, 
+       { {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_DIV}, 
+       { {"%", sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MOD}, 
+       /* Precedence 8, left-associative */
+       { {"+", sizeof(char)}, EXPR_OP_ADD, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_ADD}, 
+       { {"-", sizeof(char)}, EXPR_OP_SUB, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_SUB}, 
+       { {"..", sizeof(char)*2},EXPR_OP_DDOT, 8,  EXPR_OP_ASSOC_LEFT, JX9_OP_CAT}, 
+       /* Precedence 9, left-associative */
+       { {"<<", sizeof(char)*2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHL}, 
+       { {">>", sizeof(char)*2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHR}, 
+       /* Precedence 10, non-associative */
+       { {"<", sizeof(char)},    EXPR_OP_LT,  10, EXPR_OP_NON_ASSOC, JX9_OP_LT}, 
+       { {">", sizeof(char)},    EXPR_OP_GT,  10, EXPR_OP_NON_ASSOC, JX9_OP_GT}, 
+       { {"<=", sizeof(char)*2}, EXPR_OP_LE,  10, EXPR_OP_NON_ASSOC, JX9_OP_LE}, 
+       { {">=", sizeof(char)*2}, EXPR_OP_GE,  10, EXPR_OP_NON_ASSOC, JX9_OP_GE}, 
+       { {"<>", sizeof(char)*2}, EXPR_OP_NE,  10, EXPR_OP_NON_ASSOC, JX9_OP_NEQ}, 
+       /* Precedence 11, non-associative */
+       { {"==", sizeof(char)*2},  EXPR_OP_EQ,  11, EXPR_OP_NON_ASSOC, JX9_OP_EQ}, 
+       { {"!=", sizeof(char)*2},  EXPR_OP_NE,  11, EXPR_OP_NON_ASSOC, JX9_OP_NEQ}, 
+       { {"===", sizeof(char)*3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_TEQ}, 
+       { {"!==", sizeof(char)*3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, JX9_OP_TNE}, 
+               /* Precedence 12, left-associative */
+       { {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT,   JX9_OP_BAND}, 
+                                /* Binary operators */
+       /* Precedence 13, left-associative */
+       { {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, JX9_OP_BXOR}, 
+       /* Precedence 14, left-associative */
+       { {"|", sizeof(char)}, EXPR_OP_BOR, 14, EXPR_OP_ASSOC_LEFT, JX9_OP_BOR}, 
+       /* Precedence 15, left-associative */
+       { {"&&", sizeof(char)*2}, EXPR_OP_LAND, 15, EXPR_OP_ASSOC_LEFT, JX9_OP_LAND}, 
+       /* Precedence 16, left-associative */
+       { {"||", sizeof(char)*2}, EXPR_OP_LOR, 16, EXPR_OP_ASSOC_LEFT, JX9_OP_LOR}, 
+                             /* Ternary operator */
+       /* Precedence 17, left-associative */
+    { {"?", sizeof(char)}, EXPR_OP_QUESTY, 17, EXPR_OP_ASSOC_LEFT, 0}, 
+                            /* Combined binary operators */
+       /* Precedence 18, right-associative */
+       { {"=", sizeof(char)},     EXPR_OP_ASSIGN,     18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_STORE}, 
+       { {"+=", sizeof(char)*2},  EXPR_OP_ADD_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_ADD_STORE }, 
+       { {"-=", sizeof(char)*2},  EXPR_OP_SUB_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SUB_STORE }, 
+       { {".=", sizeof(char)*2},  EXPR_OP_DOT_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_CAT_STORE }, 
+       { {"*=", sizeof(char)*2},  EXPR_OP_MUL_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_MUL_STORE }, 
+       { {"/=", sizeof(char)*2},  EXPR_OP_DIV_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_DIV_STORE }, 
+       { {"%=", sizeof(char)*2},  EXPR_OP_MOD_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_MOD_STORE }, 
+       { {"&=", sizeof(char)*2},  EXPR_OP_AND_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BAND_STORE }, 
+       { {"|=", sizeof(char)*2},  EXPR_OP_OR_ASSIGN,  18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BOR_STORE  }, 
+       { {"^=", sizeof(char)*2},  EXPR_OP_XOR_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_BXOR_STORE }, 
+       { {"<<=", sizeof(char)*3}, EXPR_OP_SHL_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SHL_STORE }, 
+       { {">>=", sizeof(char)*3}, EXPR_OP_SHR_ASSIGN, 18,  EXPR_OP_ASSOC_RIGHT, JX9_OP_SHR_STORE },
+               /* Precedence 22, left-associative [Lowest operator] */
+       { {",", sizeof(char)},  EXPR_OP_COMMA, 22, EXPR_OP_ASSOC_LEFT, 0}, /* IMP-0139-COMMA: Symisc eXtension */
+};
+/* Function call operator need special handling */
+static const jx9_expr_op sFCallOp = {{"(", sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_CALL};
+/*
+ * Check if the given token is a potential operator or not.
+ * This function is called by the lexer each time it extract a token that may 
+ * look like an operator.
+ * Return a structure [i.e: jx9_expr_op instnace ] that describe the operator on success.
+ * Otherwise NULL.
+ * Note that the function take care of handling ambiguity [i.e: whether we are dealing with
+ * a binary minus or unary minus.]
+ */
+JX9_PRIVATE const jx9_expr_op *  jx9ExprExtractOperator(SyString *pStr, SyToken *pLast)
+{
+       sxu32 n = 0;
+       sxi32 rc;
+       /* Do a linear lookup on the operators table */
+       for(;;){
+               if( n >= SX_ARRAYSIZE(aOpTable) ){
+                       break;
+               }
+               rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyMemcmp);             
+               if( rc == 0 ){
+                       if( aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0 ){
+                               if( aOpTable[n].iOp == EXPR_OP_SUBSCRIPT && (pLast == 0 || (pLast->nType & (JX9_TK_ID|JX9_TK_CSB/*]*/|JX9_TK_RPAREN/*)*/)) == 0) ){
+                                       /* JSON Array not subscripting, return NULL  */
+                                       return 0;
+                               }
+                               /* There is no ambiguity here, simply return the first operator seen */
+                               return &aOpTable[n];
+                       }
+                       /* Handle ambiguity */
+                       if( pLast->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_COLON/*:*/|JX9_TK_COMMA/*, '*/) ){
+                               /* Unary opertors have prcedence here over binary operators */
+                               return &aOpTable[n];
+                       }
+                       if( pLast->nType & JX9_TK_OP ){
+                               const jx9_expr_op *pOp = (const jx9_expr_op *)pLast->pUserData;
+                               /* Ticket 1433-31: Handle the '++', '--' operators case */
+                               if( pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR ){
+                                       /* Unary opertors have prcedence here over binary operators */
+                                       return &aOpTable[n];
+                               }
+                       
+                       }
+               }
+               ++n; /* Next operator in the table */
+       }
+       /* No such operator */
+       return 0;
+}
+/*
+ * Delimit a set of token stream.
+ * This function take care of handling the nesting level and stops when it hit
+ * the end of the input or the ending token is found and the nesting level is zero.
+ */
+JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd)
+{
+       SyToken *pCur = pIn;
+       sxi32 iNest = 1;
+       for(;;){
+               if( pCur >= pEnd ){
+                       break;
+               }
+               if( pCur->nType & nTokStart ){
+                       /* Increment nesting level */
+                       iNest++;
+               }else if( pCur->nType & nTokEnd ){
+                       /* Decrement nesting level */
+                       iNest--;
+                       if( iNest <= 0 ){
+                               break;
+                       }
+               }
+               /* Advance cursor */
+               pCur++;
+       }
+       /* Point to the end of the chunk */
+       *ppEnd = pCur;
+}
+/*
+ * Retrun TRUE if the given ID represent a language construct [i.e: print, print..]. FALSE otherwise.
+ * Note on reserved keywords.
+ *  According to the JX9 language reference manual:
+ *   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.
+ */
+JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID)
+{
+       if( nKeyID == JX9_TKWRD_PRINT || nKeyID == JX9_TKWRD_EXIT || nKeyID == JX9_TKWRD_DIE
+               || nKeyID == JX9_TKWRD_INCLUDE|| nKeyID == JX9_TKWRD_IMPORT ){
+                       return TRUE;
+       }
+       /* Not a language construct */
+       return FALSE; 
+}
+/*
+ * Point to the next expression that should be evaluated shortly.
+ * The cursor stops when it hit a comma ', ' or a semi-colon and the nesting
+ * level is zero.
+ */
+JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext)
+{
+       SyToken *pCur = pStart;
+       sxi32 iNest = 0;
+       if( pCur >= pEnd || (pCur->nType & JX9_TK_SEMI/*';'*/) ){
+               /* Last expression */
+               return SXERR_EOF;
+       }
+       while( pCur < pEnd ){
+               if( (pCur->nType & (JX9_TK_COMMA/*','*/|JX9_TK_SEMI/*';'*/)) && iNest <= 0){
+                       break;
+               }
+               if( pCur->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OSB/*'['*/|JX9_TK_OCB/*'{'*/) ){
+                       iNest++;
+               }else if( pCur->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*']'*/|JX9_TK_CCB/*'}*/) ){
+                       iNest--;
+               }
+               pCur++;
+       }
+       *ppNext = pCur;
+       return SXRET_OK;
+}
+/*
+ * Collect and assemble tokens holding annonymous functions/closure body.
+ * When errors, JX9 take care of generating the appropriate error message.
+ * Note on annonymous functions.
+ *  According to the JX9 language reference manual:
+ *  Anonymous functions, also known as closures, allow the creation of functions
+ *  which have no specified name. They are most useful as the value of callback
+ *  parameters, but they have many other uses. 
+ *  Closures may also inherit variables from the parent scope. Any such variables
+ *  must be declared in the function header. Inheriting variables from the parent
+ *  scope is not the same as using global variables. Global variables exist in the global scope
+ *  which is the same no matter what function is executing. The parent scope of a closure is the 
+ *  function in which the closure was declared (not necessarily the function it was called from).
+ *
+ * Some example:
+ *  $greet = function($name)
+ * {
+ *   printf("Hello %s\r\n", $name);
+ * };
+ *  $greet('World');
+ *  $greet('JX9');
+ *
+ * $double = function($a) {
+ *   return $a * 2;
+ * };
+ * // This is our range of numbers
+ * $numbers = range(1, 5);
+ * // Use the Annonymous function as a callback here to 
+ * // double the size of each element in our 
+ * // range
+ * $new_numbers = array_map($double, $numbers);
+ * print implode(' ', $new_numbers);
+ */
+static sxi32 ExprAssembleAnnon(jx9_gen_state *pGen,SyToken **ppCur, SyToken *pEnd)
+{
+       SyToken *pIn = *ppCur;
+       sxu32 nLine;
+       sxi32 rc;
+       /* Jump the 'function' keyword */
+       nLine = pIn->nLine;
+       pIn++;
+       if( pIn < pEnd && (pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) ){
+               pIn++;
+       }
+       if( pIn >= pEnd || (pIn->nType & JX9_TK_LPAREN) == 0 ){
+               /* Syntax error */
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing opening parenthesis '(' while declaring annonymous function");
+               if( rc != SXERR_ABORT ){
+                       rc = SXERR_SYNTAX;
+               }
+               goto Synchronize;
+       }
+       pIn++; /* Jump the leading parenthesis '(' */
+       jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_LPAREN/*'('*/, JX9_TK_RPAREN/*')'*/, &pIn);
+       if( pIn >= pEnd || &pIn[1] >= pEnd ){
+               /* Syntax error */
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function");
+               if( rc != SXERR_ABORT ){
+                       rc = SXERR_SYNTAX;
+               }
+               goto Synchronize;
+       }
+       pIn++; /* Jump the trailing parenthesis */
+       if( pIn->nType & JX9_TK_OCB /*'{'*/ ){
+               pIn++; /* Jump the leading curly '{' */
+               jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_OCB/*'{'*/, JX9_TK_CCB/*'}'*/, &pIn);
+               if( pIn < pEnd ){
+                       pIn++;
+               }
+       }else{
+               /* Syntax error */
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function, missing '{'");
+               if( rc == SXERR_ABORT ){
+                       return SXERR_ABORT;
+               }                       
+       }
+       rc = SXRET_OK;
+Synchronize:
+       /* Synchronize pointers */
+       *ppCur = pIn;
+       return rc;
+}
+/*
+ * Make sure we are dealing with a valid expression tree.
+ * This function check for balanced parenthesis, braces, brackets and so on.
+ * When errors, JX9 take care of generating the appropriate error message.
+ * Return SXRET_OK on success. Any other return value indicates syntax error.
+ */
+static sxi32 ExprVerifyNodes(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nNode)
+{
+       sxi32 iParen, iSquare, iBraces;
+       sxi32 i, rc;
+
+       if( nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB) ){
+               /* Fix and mark as an unary not binary plus/minus operator */
+               apNode[0]->pOp = jx9ExprExtractOperator(&apNode[0]->pStart->sData, 0);
+               apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp;
+       }
+       iParen = iSquare = iBraces = 0;
+       for( i = 0 ; i < nNode ; ++i ){
+               if( apNode[i]->pStart->nType & JX9_TK_LPAREN /*'('*/){
+                       if( i > 0 && ( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ||
+                               (apNode[i - 1]->pStart->nType & (JX9_TK_ID|JX9_TK_KEYWORD|JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*]*/))) ){
+                                       /* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or, xor] operators followed by an opening parenthesis */
+                                       if( (apNode[i - 1]->pStart->nType & JX9_TK_OP) == 0 ){
+                                               /* We are dealing with a postfix [i.e: function call]  operator
+                                                * not a simple left parenthesis. Mark the node.
+                                                */
+                                               apNode[i]->pStart->nType |= JX9_TK_OP;
+                                               apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */
+                                               apNode[i]->pOp = &sFCallOp;
+                                       }
+                       }
+                       iParen++;
+               }else if( apNode[i]->pStart->nType & JX9_TK_RPAREN/*')*/){
+                       if( iParen <= 0 ){
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ')'");
+                               if( rc != SXERR_ABORT ){
+                                       rc = SXERR_SYNTAX;
+                               }
+                               return rc;
+                       }
+                       iParen--;
+               }else if( apNode[i]->pStart->nType & JX9_TK_OSB /*'['*/ && apNode[i]->xCode == 0 ){
+                       iSquare++;
+               }else if (apNode[i]->pStart->nType & JX9_TK_CSB /*']'*/){
+                       if( iSquare <= 0 ){
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ']'");
+                               if( rc != SXERR_ABORT ){
+                                       rc = SXERR_SYNTAX;
+                               }
+                               return rc;
+                       }
+                       iSquare--;
+               }else if( apNode[i]->pStart->nType & JX9_TK_OCB /*'{'*/ && apNode[i]->xCode == 0 ){
+                       iBraces++;
+               }else if (apNode[i]->pStart->nType & JX9_TK_CCB /*'}'*/){
+                       if( iBraces <= 0 ){
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token '}'");
+                               if( rc != SXERR_ABORT ){
+                                       rc = SXERR_SYNTAX;
+                               }
+                               return rc;
+                       }
+                       iBraces--;
+               }else if( apNode[i]->pStart->nType & JX9_TK_OP ){
+                       const jx9_expr_op *pOp = (const jx9_expr_op *)apNode[i]->pOp;
+                       if( i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)){
+                               if( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ){
+                                       sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */
+                                       sxu32 n = 0;
+                                       if( pOp->iOp == EXPR_OP_UPLUS ){
+                                               iExprOp = EXPR_OP_ADD; /* Binary plus */
+                                       }
+                                       /*
+                                        * TICKET 1433-013: This is a fix around an obscure bug when the user uses
+                                        * a variable name which is an alpha-stream operator [i.e: $and, $xor, $eq..].
+                                        */
+                                       while( n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp ){
+                                               ++n;
+                                       }
+                                       pOp = &aOpTable[n];
+                                       /* Mark as binary '+' or '-', not an unary */
+                                       apNode[i]->pOp = pOp;
+                                       apNode[i]->pStart->pUserData = (void *)pOp;
+                               }
+                       }
+               }
+       }
+       if( iParen != 0 || iSquare != 0 || iBraces != 0){
+               rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error, mismatched '(', '[' or '{'");
+               if( rc != SXERR_ABORT ){
+                       rc = SXERR_SYNTAX;
+               }
+               return rc;
+       }
+       return SXRET_OK;
+}
+/*
+ * Extract a single expression node from the input.
+ * On success store the freshly extractd node in ppNode.
+ * When errors, JX9 take care of generating the appropriate error message.
+ * An expression node can be a variable [i.e: $var], an operator [i.e: ++] 
+ * an annonymous function [i.e: function(){ return "Hello"; }, a double/single
+ * quoted string, a heredoc/nowdoc, a literal [i.e: JX9_EOL], a namespace path
+ * [i.e: namespaces\path\to..], a array/list [i.e: array(4, 5, 6)] and so on.
+ */
+static sxi32 ExprExtractNode(jx9_gen_state *pGen, jx9_expr_node **ppNode)
+{
+       jx9_expr_node *pNode;
+       SyToken *pCur;
+       sxi32 rc;
+       /* Allocate a new node */
+       pNode = (jx9_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_expr_node));
+       if( pNode == 0 ){
+               /* If the supplied memory subsystem is so sick that we are unable to allocate
+                * a tiny chunk of memory, there is no much we can do here.
+                */
+               return SXERR_MEM;
+       }
+       /* Zero the structure */
+       SyZero(pNode, sizeof(jx9_expr_node));
+       SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(jx9_expr_node **));
+       /* Point to the head of the token stream */
+       pCur = pNode->pStart = pGen->pIn;
+       /* Start collecting tokens */
+       if( pCur->nType & JX9_TK_OP ){
+               /* Point to the instance that describe this operator */
+               pNode->pOp = (const jx9_expr_op *)pCur->pUserData;
+               /* Advance the stream cursor */
+               pCur++;
+       }else if( pCur->nType & JX9_TK_DOLLAR ){
+               /* Isolate variable */
+               pCur++; /* Jump the dollar sign */
+               if( pCur >= pGen->pEnd ){
+                       /* Syntax error */
+                       rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"Invalid variable name");
+                       if( rc != SXERR_ABORT ){
+                               rc = SXERR_SYNTAX;
+                       }
+                       SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
+                       return rc;
+               }
+               pCur++; /* Jump the variable name */
+               pNode->xCode = jx9CompileVariable;
+       }else if( pCur->nType & JX9_TK_OCB /* '{' */ ){
+               /* JSON Object, assemble tokens */
+               pCur++;
+               jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OCB /* '[' */, JX9_TK_CCB /* ']' */, &pCur);
+               if( pCur < pGen->pEnd ){
+                       pCur++;
+               }else{
+                       /* Syntax error */
+                       rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Object: Missing closing braces '}'");
+                       if( rc != SXERR_ABORT ){
+                               rc = SXERR_SYNTAX;
+                       }
+                       SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
+                       return rc;
+               }
+               pNode->xCode = jx9CompileJsonObject;
+       }else if( pCur->nType & JX9_TK_OSB /* '[' */ && !(pCur->nType & JX9_TK_OP) ){
+               /* JSON Array, assemble tokens */
+               pCur++;
+               jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OSB /* '[' */, JX9_TK_CSB /* ']' */, &pCur);
+               if( pCur < pGen->pEnd ){
+                       pCur++;
+               }else{
+                       /* Syntax error */
+                       rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Array: Missing closing square bracket ']'");
+                       if( rc != SXERR_ABORT ){
+                               rc = SXERR_SYNTAX;
+                       }
+                       SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
+                       return rc;
+               }
+               pNode->xCode = jx9CompileJsonArray;
+       }else if( pCur->nType & JX9_TK_KEYWORD ){
+                int nKeyword = SX_PTR_TO_INT(pCur->pUserData);
+                if( nKeyword == JX9_TKWRD_FUNCTION ){
+                        /* Annonymous function */
+                         if( &pCur[1] >= pGen->pEnd ){
+                                /* Assume a literal */
+                               pCur++;
+                               pNode->xCode = jx9CompileLiteral;
+                        }else{
+                                /* Assemble annonymous functions body */
+                                rc = ExprAssembleAnnon(&(*pGen), &pCur, pGen->pEnd);
+                                if( rc != SXRET_OK ){
+                                        SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
+                                        return rc; 
+                                }
+                                pNode->xCode = jx9CompileAnnonFunc;
+                         }
+                }else if( jx9IsLangConstruct(nKeyword) && &pCur[1] < pGen->pEnd ){
+                        /* Language constructs [i.e: print,die...] require special handling */
+                        jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_LPAREN|JX9_TK_OCB|JX9_TK_OSB, JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB, &pCur);
+                        pNode->xCode = jx9CompileLangConstruct;
+                }else{
+                        /* Assume a literal */
+                        pCur++;
+                        pNode->xCode = jx9CompileLiteral;
+                }
+        }else if( pCur->nType & (JX9_TK_ID) ){
+                /* Constants, function name, namespace path, object name... */
+                pCur++;
+                pNode->xCode = jx9CompileLiteral;
+        }else{
+                if( (pCur->nType & (JX9_TK_LPAREN|JX9_TK_RPAREN|JX9_TK_COMMA|JX9_TK_CSB|JX9_TK_OCB|JX9_TK_CCB|JX9_TK_COLON)) == 0 ){
+                        /* Point to the code generator routine */
+                        pNode->xCode = jx9GetNodeHandler(pCur->nType);
+                        if( pNode->xCode == 0 ){
+                                rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData);
+                                if( rc != SXERR_ABORT ){
+                                        rc = SXERR_SYNTAX;
+                                }
+                                SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
+                                return rc;
+                        }
+                }
+               /* Advance the stream cursor */
+               pCur++;
+        }
+       /* Point to the end of the token stream */
+       pNode->pEnd = pCur;
+       /* Save the node for later processing */
+       *ppNode = pNode;
+       /* Synchronize cursors */
+       pGen->pIn = pCur;
+       return SXRET_OK;
+}
+/*
+ * Free an expression tree.
+ */
+static void ExprFreeTree(jx9_gen_state *pGen, jx9_expr_node *pNode)
+{
+       if( pNode->pLeft ){
+               /* Release the left tree */
+               ExprFreeTree(&(*pGen), pNode->pLeft);
+       }
+       if( pNode->pRight ){
+               /* Release the right tree */
+               ExprFreeTree(&(*pGen), pNode->pRight);
+       }
+       if( pNode->pCond ){
+               /* Release the conditional tree used by the ternary operator */
+               ExprFreeTree(&(*pGen), pNode->pCond);
+       }
+       if( SySetUsed(&pNode->aNodeArgs) > 0 ){
+               jx9_expr_node **apArg;
+               sxu32 n;
+               /* Release node arguments */
+               apArg = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
+               for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){
+                       ExprFreeTree(&(*pGen), apArg[n]);
+               }
+               SySetRelease(&pNode->aNodeArgs);
+       }
+       /* Finally, release this node */
+       SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
+}
+/*
+ * Free an expression tree.
+ * This function is a wrapper around ExprFreeTree() defined above.
+ */
+JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet)
+{
+       jx9_expr_node **apNode;
+       sxu32 n;
+       apNode = (jx9_expr_node **)SySetBasePtr(pNodeSet);
+       for( n = 0  ; n < SySetUsed(pNodeSet) ; ++n ){
+               if( apNode[n] ){
+                       ExprFreeTree(&(*pGen), apNode[n]);
+               }
+       }
+       return SXRET_OK;
+}
+/*
+ * Check if the given node is a modifialbe l/r-value.
+ * Return TRUE if modifiable.FALSE otherwise.
+ */
+static int ExprIsModifiableValue(jx9_expr_node *pNode)
+{
+       sxi32 iExprOp;
+       if( pNode->pOp == 0 ){
+               return pNode->xCode == jx9CompileVariable ? TRUE : FALSE;
+       }
+       iExprOp = pNode->pOp->iOp;
+       if( iExprOp == EXPR_OP_DOT /*'.' */  ){
+                       return TRUE;
+       }
+       if( iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/ ){
+               if( pNode->pLeft->pOp ) {
+                       if( pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_DOT /*'.'*/){
+                               return FALSE;
+                       }
+               }else if( pNode->pLeft->xCode != jx9CompileVariable ){
+                       return FALSE;
+               }
+               return TRUE;
+       }
+       /* Not a modifiable l or r-value */
+       return FALSE;
+}
+/* Forward declaration */
+static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken);
+/* Macro to check if the given node is a terminal */
+#define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft ))
+/*
+ * Buid an expression tree for each given function argument.
+ * When errors, JX9 take care of generating the appropriate error message.
+ */
+static sxi32 ExprProcessFuncArguments(jx9_gen_state *pGen, jx9_expr_node *pOp, jx9_expr_node **apNode, sxi32 nToken)
+{
+       sxi32 iNest, iCur, iNode;
+       sxi32 rc;
+       /* Process function arguments from left to right */
+       iCur = 0;
+       for(;;){
+               if( iCur >= nToken ){
+                       /* No more arguments to process */
+                       break;
+               }
+               iNode = iCur;
+               iNest = 0;
+               while( iCur < nToken ){
+                       if( apNode[iCur] ){
+                               if( (apNode[iCur]->pStart->nType & JX9_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0 ){
+                                       break;
+                               }else if( apNode[iCur]->pStart->nType & (JX9_TK_LPAREN|JX9_TK_OSB|JX9_TK_OCB) ){
+                                       iNest++;
+                               }else if( apNode[iCur]->pStart->nType & (JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB) ){
+                                       iNest--;
+                               }
+                       }
+                       iCur++;
+               }
+               if( iCur > iNode ){
+                       ExprMakeTree(&(*pGen), &apNode[iNode], iCur-iNode);
+                       if( apNode[iNode] ){
+                               /* Put a pointer to the root of the tree in the arguments set */
+                               SySetPut(&pOp->aNodeArgs, (const void *)&apNode[iNode]);
+                       }else{
+                               /* Empty function argument */
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Empty function argument");
+                               if( rc != SXERR_ABORT ){
+                                       rc = SXERR_SYNTAX;
+                               }
+                               return rc;
+                       }
+               }else{
+                       rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
+                       if( rc != SXERR_ABORT ){
+                               rc = SXERR_SYNTAX;
+                       }
+                       return rc;
+               }
+               /* Jump trailing comma */
+               if( iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & JX9_TK_COMMA) ){
+                       iCur++;
+                       if( iCur >= nToken ){
+                               /* missing function argument */
+                               rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
+                               if( rc != SXERR_ABORT ){
+                                       rc = SXERR_SYNTAX;
+                               }
+                               return rc;
+                       }
+               }
+       }
+       return SXRET_OK;
+}
+/*
+  * Create an expression tree from an array of tokens.
+  * If successful, the root of the tree is stored in apNode[0].
+  * When errors, JX9 take care of generating the appropriate error message.
+  */
+ static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken)
+ {
+        sxi32 i, iLeft, iRight;
+        jx9_expr_node *pNode;
+        sxi32 iCur;
+        sxi32 rc;
+        if( nToken <= 0 || (nToken == 1 && apNode[0]->xCode) ){
+                /* TICKET 1433-17: self evaluating node */
+                return SXRET_OK;
+        }
+        /* Process expressions enclosed in parenthesis first */
+        for( iCur =  0 ; iCur < nToken ; ++iCur ){
+                sxi32 iNest;
+                /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator
+                 * since the LPAREN token can also be an operator [i.e: Function call].
+                 */
+                if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != JX9_TK_LPAREN ){
+                        continue;
+                }
+                iNest = 1;
+                iLeft = iCur;
+                /* Find the closing parenthesis */
+                iCur++;
+                while( iCur < nToken ){
+                        if( apNode[iCur] ){
+                                if( apNode[iCur]->pStart->nType & JX9_TK_RPAREN /* ')' */){
+                                        /* Decrement nesting level */
+                                        iNest--;
+                                        if( iNest <= 0 ){
+                                                break;
+                                        }
+                                }else if( apNode[iCur]->pStart->nType & JX9_TK_LPAREN /* '(' */ ){
+                                        /* Increment nesting level */
+                                        iNest++;
+                                }
+                        }
+                        iCur++;
+                }
+                if( iCur - iLeft > 1 ){
+                        /* Recurse and process this expression */
+                        rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1);
+                        if( rc != SXRET_OK ){
+                                return rc;
+                        }
+                }
+                /* Free the left and right nodes */
+                ExprFreeTree(&(*pGen), apNode[iLeft]);
+                ExprFreeTree(&(*pGen), apNode[iCur]);
+                apNode[iLeft] = 0;
+                apNode[iCur] = 0;
+        }
+        /* Handle postfix [i.e: function call, member access] operators with precedence 2 */
+        iLeft = -1;
+        for( iCur = 0 ; iCur < nToken ; ++iCur ){
+                if( apNode[iCur] == 0 ){
+                        continue;
+                }
+                pNode = apNode[iCur];
+                if( pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0  ){
+                        if( pNode->pOp->iOp == EXPR_OP_FUNC_CALL ){
+                                /* Collect function arguments */
+                                sxi32 iPtr = 0;
+                                sxi32 nFuncTok = 0;
+                                while( nFuncTok + iCur < nToken ){
+                                        if( apNode[nFuncTok+iCur] ){
+                                                if( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_LPAREN /*'('*/ ){
+                                                        iPtr++;
+                                                }else if ( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_RPAREN /*')'*/){
+                                                        iPtr--;
+                                                        if( iPtr <= 0 ){
+                                                                break;
+                                                        }
+                                                }
+                                        }
+                                        nFuncTok++;
+                                }
+                                if( nFuncTok + iCur >= nToken ){
+                                        /* Syntax error */
+                                        rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Missing right parenthesis ')'");
+                                        if( rc != SXERR_ABORT ){
+                                                rc = SXERR_SYNTAX;
+                                        }
+                                        return rc; 
+                                }
+                                if(  iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/ ){
+                                        /* Syntax error */
+                                        rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid function name");
+                                        if( rc != SXERR_ABORT ){
+                                                rc = SXERR_SYNTAX;
+                                        }
+                                        return rc;
+                                }
+                                if( nFuncTok > 1 ){
+                                        /* Process function arguments */
+                                        rc = ExprProcessFuncArguments(&(*pGen), pNode, &apNode[iCur+1], nFuncTok-1);
+                                        if( rc != SXRET_OK ){
+                                                return rc;
+                                        }
+                                }
+                                /* Link the node to the tree */
+                                pNode->pLeft = apNode[iLeft];
+                                apNode[iLeft] = 0;
+                                for( iPtr = 1; iPtr <= nFuncTok ; iPtr++ ){
+                                        apNode[iCur+iPtr] = 0;
+                                }
+                        }else if (pNode->pOp->iOp == EXPR_OP_SUBSCRIPT ){
+                                /* Subscripting */
+                                sxi32 iArrTok = iCur + 1;
+                                sxi32 iNest = 1;
+                                if(  iLeft >= 0 && (apNode[iLeft]->xCode == jx9CompileVariable || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* postfix */) ) ){
+                                        /* Collect index tokens */
+                                   while( iArrTok < nToken ){
+                                        if( apNode[iArrTok] ){
+                                                if( apNode[iArrTok]->pStart->nType & JX9_TK_OSB /*'['*/){
+                                                        /* Increment nesting level */
+                                                        iNest++;
+                                                }else if( apNode[iArrTok]->pStart->nType & JX9_TK_CSB /*']'*/){
+                                                        /* Decrement nesting level */
+                                                        iNest--;
+                                                        if( iNest <= 0 ){
+                                                                break;
+                                                        }
+                                                }
+                                        }
+                                        ++iArrTok;
+                                  }
+                                  if( iArrTok > iCur + 1 ){
+                                        /* Recurse and process this expression */
+                                        rc = ExprMakeTree(&(*pGen), &apNode[iCur+1], iArrTok - iCur - 1);
+                                        if( rc != SXRET_OK ){
+                                                return rc;
+                                        }
+                                        /* Link the node to it's index */
+                                        SySetPut(&pNode->aNodeArgs, (const void *)&apNode[iCur+1]);
+                                  }
+                                  /* Link the node to the tree */
+                                  pNode->pLeft = apNode[iLeft];
+                                  pNode->pRight = 0;
+                                  apNode[iLeft] = 0;
+                                  for( iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest ){
+                                        apNode[iNest] = 0;
+                                 }
+                                }
+                        }else{
+                                /* Member access operators [i.e: '.' ] */
+                                iRight = iCur + 1;
+                                while( iRight < nToken && apNode[iRight] == 0 ){
+                                        iRight++;
+                                }
+                                if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
+                                        /* Syntax error */
+                                        rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid member name", &pNode->pOp->sOp);
+                                        if( rc != SXERR_ABORT ){
+                                                rc = SXERR_SYNTAX;
+                                        }
+                                        return rc;
+                                }
+                                /* Link the node to the tree */
+                                pNode->pLeft = apNode[iLeft];
+                                if( pNode->pLeft->pOp == 0 && pNode->pLeft->xCode != jx9CompileVariable ){
+                                        /* Syntax error */
+                                        rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, 
+                                                "'%z': Expecting a variable as left operand", &pNode->pOp->sOp);
+                                        if( rc != SXERR_ABORT ){
+                                                rc = SXERR_SYNTAX;
+                                        }
+                                        return rc;
+                                }
+                                pNode->pRight = apNode[iRight];
+                                apNode[iLeft] = apNode[iRight] = 0;
+                        }
+                }
+                iLeft = iCur;
+        }
+         /* Handle post/pre icrement/decrement [i.e: ++/--] operators with precedence 3 */
+        iLeft = -1;
+        for( iCur = 0 ; iCur < nToken ; ++iCur ){
+                if( apNode[iCur] == 0 ){
+                        continue;
+                }
+                pNode = apNode[iCur];
+                if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
+                        if( iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */)
+                                || apNode[iLeft]->xCode == jx9CompileVariable) ){
+                                        /* Link the node to the tree */
+                                        pNode->pLeft = apNode[iLeft];
+                                        apNode[iLeft] = 0; 
+                        }
+                 }
+                iLeft = iCur;
+         }
+        iLeft = -1;
+        for( iCur = nToken -  1 ; iCur >= 0 ; iCur-- ){
+                if( apNode[iCur] == 0 ){
+                        continue;
+                }
+                pNode = apNode[iCur];
+                if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
+                        if( iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != jx9CompileVariable)
+                                || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */) ){
+                                        /* Syntax error */
+                                        rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z' operator needs l-value", &pNode->pOp->sOp);
+                                        if( rc != SXERR_ABORT ){
+                                                rc = SXERR_SYNTAX;
+                                        }
+                                        return rc;
+                        }
+                        /* Link the node to the tree */
+                        pNode->pLeft = apNode[iLeft];
+                        apNode[iLeft] = 0;
+                        /* Mark as pre-increment/decrement node */
+                        pNode->iFlags |= EXPR_NODE_PRE_INCR;
+                 }
+                iLeft = iCur;
+        }
+        /* Handle right associative unary and cast operators [i.e: !, (string), ~...]  with precedence 4 */
+         iLeft = 0;
+         for( iCur = nToken -  1 ; iCur >= 0 ; iCur-- ){
+                 if( apNode[iCur] ){
+                         pNode = apNode[iCur];
+                         if( pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0){
+                                 if( iLeft > 0 ){
+                                         /* Link the node to the tree */
+                                         pNode->pLeft = apNode[iLeft];
+                                         apNode[iLeft] = 0;
+                                         if( pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4 ){
+                                                 if( pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0 ){
+                                                          /* Syntax error */
+                                                         rc = jx9GenCompileError(pGen, E_ERROR, pNode->pLeft->pStart->nLine, "'%z': Missing operand", &pNode->pLeft->pOp->sOp);
+                                                         if( rc != SXERR_ABORT ){
+                                                                 rc = SXERR_SYNTAX;
+                                                         }
+                                                         return rc;
+                                                 }
+                                         }
+                                 }else{
+                                         /* Syntax error */
+                                         rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing operand", &pNode->pOp->sOp);
+                                         if( rc != SXERR_ABORT ){
+                                                 rc = SXERR_SYNTAX;
+                                         }
+                                         return rc;
+                                 }
+                         }
+                         /* Save terminal position */
+                         iLeft = iCur;
+                 }
+         }      
+        /* Process left and non-associative binary operators [i.e: *, /, &&, ||...]*/
+        for( i = 7 ; i < 17 ; i++ ){
+                iLeft = -1;
+                for( iCur = 0 ; iCur < nToken ; ++iCur ){
+                        if( apNode[iCur] == 0 ){
+                                continue;
+                        }
+                        pNode = apNode[iCur];
+                        if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){
+                                /* Get the right node */
+                                iRight = iCur + 1;
+                                while( iRight < nToken && apNode[iRight] == 0 ){
+                                        iRight++;
+                                }
+                                if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
+                                        /* Syntax error */
+                                        rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
+                                        if( rc != SXERR_ABORT ){
+                                                rc = SXERR_SYNTAX;
+                                        }
+                                        return rc; 
+                                }
+                                /* Link the node to the tree */
+                                pNode->pLeft = apNode[iLeft];
+                                pNode->pRight = apNode[iRight];
+                                apNode[iLeft] = apNode[iRight] = 0;
+                        }
+                        iLeft = iCur;
+                }
+        }
+        /* Handle the ternary operator. (expr1) ? (expr2) : (expr3) 
+         * Note that we do not need a precedence loop here since
+         * we are dealing with a single operator.
+         */
+         iLeft = -1;
+         for( iCur = 0 ; iCur < nToken ; ++iCur ){
+                 if( apNode[iCur] == 0 ){
+                         continue;
+                 }
+                 pNode = apNode[iCur];
+                 if( pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0 ){
+                         sxi32 iNest = 1;
+                         if( iLeft < 0 || !NODE_ISTERM(iLeft) ){
+                                 /* Missing condition */
+                                 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Syntax error", &pNode->pOp->sOp);
+                                 if( rc != SXERR_ABORT ){
+                                         rc = SXERR_SYNTAX;
+                                 }
+                                 return rc;
+                         }
+                         /* Get the right node */
+                         iRight = iCur + 1;
+                         while( iRight < nToken  ){
+                                 if( apNode[iRight] ){
+                                         if( apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0){
+                                                 /* Increment nesting level */
+                                                 ++iNest;
+                                         }else if( apNode[iRight]->pStart->nType & JX9_TK_COLON /*:*/ ){
+                                                 /* Decrement nesting level */
+                                                 --iNest;
+                                                 if( iNest <= 0 ){
+                                                         break;
+                                                 }
+                                         }
+                                 }
+                                 iRight++;
+                         }
+                         if( iRight > iCur + 1 ){
+                                 /* Recurse and process the then expression */
+                                 rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iRight - iCur - 1);
+                                 if( rc != SXRET_OK ){
+                                         return rc;
+                                 }
+                                 /* Link the node to the tree */
+                                 pNode->pLeft = apNode[iCur + 1];
+                         }else{
+                                 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'then' expression", &pNode->pOp->sOp);
+                                 if( rc != SXERR_ABORT ){
+                                        rc = SXERR_SYNTAX;
+                                }
+                                return rc;
+                         }
+                         apNode[iCur + 1] = 0;
+                         if( iRight + 1 < nToken ){
+                                 /* Recurse and process the else expression */
+                                 rc = ExprMakeTree(&(*pGen), &apNode[iRight + 1], nToken - iRight - 1);
+                                 if( rc != SXRET_OK ){
+                                         return rc;
+                                 }
+                                 /* Link the node to the tree */
+                                 pNode->pRight = apNode[iRight + 1];
+                                 apNode[iRight + 1] =  apNode[iRight] = 0;
+                         }else{
+                                 rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'else' expression", &pNode->pOp->sOp);
+                                 if( rc != SXERR_ABORT ){
+                                        rc = SXERR_SYNTAX;
+                                }
+                                return rc;
+                         }
+                         /* Point to the condition */
+                         pNode->pCond  = apNode[iLeft];
+                         apNode[iLeft] = 0;
+                         break;
+                 }
+                 iLeft = iCur;
+         }
+        /* Process right associative binary operators [i.e: '=', '+=', '/='] 
+         * Note: All right associative binary operators have precedence 18
+         * so there is no need for a precedence loop here.
+         */
+        iRight = -1;
+        for( iCur = nToken -  1 ; iCur >= 0 ; iCur--){
+                if( apNode[iCur] == 0 ){
+                        continue;
+                }
+                pNode = apNode[iCur];
+                if( pNode->pOp && pNode->pOp->iPrec == 18 && pNode->pLeft == 0 ){
+                        /* Get the left node */
+                        iLeft = iCur - 1;
+                        while( iLeft >= 0 && apNode[iLeft] == 0 ){
+                                iLeft--;
+                        }
+                        if( iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
+                                /* Syntax error */
+                                rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
+                                if( rc != SXERR_ABORT ){
+                                        rc = SXERR_SYNTAX;
+                                }
+                                return rc;
+                        }
+                        if( ExprIsModifiableValue(apNode[iLeft]) == FALSE ){
+                                if( pNode->pOp->iVmOp != JX9_OP_STORE  ){
+                                        /* Left operand must be a modifiable l-value */
+                                        rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, 
+                                                "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp);
+                                        if( rc != SXERR_ABORT ){
+                                                rc = SXERR_SYNTAX;
+                                        }
+                                        return rc; 
+                                }
+                        }
+                        /* Link the node to the tree (Reverse) */
+                        pNode->pLeft = apNode[iRight];
+                        pNode->pRight = apNode[iLeft];
+                        apNode[iLeft] = apNode[iRight] = 0;
+                }
+                iRight = iCur;
+        }
+        /* Process the lowest precedence operator (22, comma) */
+        iLeft = -1;
+        for( iCur = 0 ; iCur < nToken ; ++iCur ){
+                if( apNode[iCur] == 0 ){
+                        continue;
+                }
+                pNode = apNode[iCur];
+                if( pNode->pOp && pNode->pOp->iPrec == 22 /* ',' */ && pNode->pLeft == 0 ){
+                        /* Get the right node */
+                        iRight = iCur + 1;
+                        while( iRight < nToken && apNode[iRight] == 0 ){
+                                iRight++;
+                        }
+                        if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
+                                /* Syntax error */
+                                rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
+                                if( rc != SXERR_ABORT ){
+                                        rc = SXERR_SYNTAX;
+                                }
+                                return rc;
+                        }
+                        /* Link the node to the tree */
+                        pNode->pLeft = apNode[iLeft];
+                        pNode->pRight = apNode[iRight];
+                        apNode[iLeft] = apNode[iRight] = 0;
+                }
+                iLeft = iCur;
+        }
+        /* Point to the root of the expression tree */
+        for( iCur = 1 ; iCur < nToken ; ++iCur ){
+                if( apNode[iCur] ){
+                        if( (apNode[iCur]->pOp || apNode[iCur]->xCode ) && apNode[0] != 0){
+                                rc = jx9GenCompileError(pGen, E_ERROR, apNode[iCur]->pStart->nLine, "Unexpected token '%z'", &apNode[iCur]->pStart->sData);
+                                 if( rc != SXERR_ABORT ){
+                                         rc = SXERR_SYNTAX;
+                                 }
+                                 return rc;  
+                        }
+                        apNode[0] = apNode[iCur];
+                        apNode[iCur] = 0;
+                }
+        }
+        return SXRET_OK;
+ }
+ /*
+  * Build an expression tree from the freshly extracted raw tokens.
+  * If successful, the root of the tree is stored in ppRoot.
+  * When errors, JX9 take care of generating the appropriate error message.
+  * This is the public interface used by the most code generator routines.
+  */
+JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot)
+{
+       jx9_expr_node **apNode;
+       jx9_expr_node *pNode;
+       sxi32 rc;
+       /* Reset node container */
+       SySetReset(pExprNode);
+       pNode = 0; /* Prevent compiler warning */
+       /* Extract nodes one after one until we hit the end of the input */
+       while( pGen->pIn < pGen->pEnd ){
+               rc = ExprExtractNode(&(*pGen), &pNode);
+               if( rc != SXRET_OK ){
+                       return rc;
+               }
+               /* Save the extracted node */
+               SySetPut(pExprNode, (const void *)&pNode);
+       }
+       if( SySetUsed(pExprNode) < 1 ){
+               /* Empty expression [i.e: A semi-colon;] */
+               *ppRoot = 0;
+               return SXRET_OK;
+       }
+       apNode = (jx9_expr_node **)SySetBasePtr(pExprNode);
+       /* Make sure we are dealing with valid nodes */
+       rc = ExprVerifyNodes(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
+       if( rc != SXRET_OK ){
+               /* Don't worry about freeing memory, upper layer will
+                * cleanup the mess left behind.
+                */
+               *ppRoot = 0;
+               return rc;
+       }
+       /* Build the tree */
+       rc = ExprMakeTree(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
+       if( rc != SXRET_OK ){
+               /* Something goes wrong [i.e: Syntax error] */
+               *ppRoot = 0;
+               return rc;
+       }
+       /* Point to the root of the tree */
+       *ppRoot = apNode[0];
+       return SXRET_OK;
+}
+/*
+ * ----------------------------------------------------------
+ * File: jx9_vfs.c
+ * MD5: 8b73046a366acaf6aa7227c2133e16c0
+ * ----------------------------------------------------------
+ */
+/*
+ * 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( SXARCH_ENTRY_INVALID(pEntry) ){
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* All done. Actually this function is a no-op, return TRUE */
+       jx9_result_bool(pCtx, 1);
+       return JX9_OK;
+}
+/*
+  * bool zip_entry_close(resource $zip_entry)
+  *  Close a directory entry.
+  * Parameters
+  *  $zip_entry
+  *   A directory entry returned by zip_read().
+  * Return
+  *  Returns TRUE on success or FALSE on failure.
+  */
+static int jx9Builtin_zip_entry_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SyArchiveEntry *pEntry;
+       if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
+               /* Missing/Invalid arguments */
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* 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[0]);
+       if( SXARCH_ENTRY_INVALID(pEntry) ){
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Reset the read cursor */
+       pEntry->nReadCount = 0;
+       /*All done. Actually this function is a no-op, return TRUE */
+       jx9_result_bool(pCtx, 1);
+       return JX9_OK;
+}
+/*
+  * string zip_entry_name(resource $zip_entry)
+  *  Retrieve the name of a directory entry.
+  * Parameters
+  *  $zip_entry
+  *   A directory entry returned by zip_read().
+  * Return
+  *  The name of the directory entry.
+  */
+static int jx9Builtin_zip_entry_name(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SyArchiveEntry *pEntry;
+       SyString *pName;
+       if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
+               /* Missing/Invalid arguments */
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* 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[0]);
+       if( SXARCH_ENTRY_INVALID(pEntry) ){
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Return entry name */
+       pName = &pEntry->sFileName;
+       jx9_result_string(pCtx, pName->zString, (int)pName->nByte);
+       return JX9_OK;
+}
+/*
+  * int64 zip_entry_filesize(resource $zip_entry)
+  *  Retrieve the actual file size of a directory entry.
+  * Parameters
+  *  $zip_entry
+  *   A directory entry returned by zip_read().
+  * Return
+  *  The size of the directory entry.
+  */
+static int jx9Builtin_zip_entry_filesize(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SyArchiveEntry *pEntry;
+       if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
+               /* Missing/Invalid arguments */
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* 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[0]);
+       if( SXARCH_ENTRY_INVALID(pEntry) ){
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Return entry size */
+       jx9_result_int64(pCtx, (jx9_int64)pEntry->nByte);
+       return JX9_OK;
+}
+/*
+  * int64 zip_entry_compressedsize(resource $zip_entry)
+  *  Retrieve the compressed size of a directory entry.
+  * Parameters
+  *  $zip_entry
+  *   A directory entry returned by zip_read().
+  * Return
+  *  The compressed size.
+  */
+static int jx9Builtin_zip_entry_compressedsize(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SyArchiveEntry *pEntry;
+       if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
+               /* Missing/Invalid arguments */
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* 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[0]);
+       if( SXARCH_ENTRY_INVALID(pEntry) ){
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Return entry compressed size */
+       jx9_result_int64(pCtx, (jx9_int64)pEntry->nByteCompr);
+       return JX9_OK;
+}
+/*
+  * string zip_entry_read(resource $zip_entry[, int $length])
+  *  Reads from an open directory entry.
+  * Parameters
+  *  $zip_entry
+  *   A directory entry returned by zip_read().
+  *  $length
+  *   The number of bytes to return. If not specified, this function
+  *   will attempt to read 1024 bytes.
+  * Return
+  *  Returns the data read, or FALSE if the end of the file is reached.
+  */
+static int jx9Builtin_zip_entry_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SyArchiveEntry *pEntry;
+       zip_raw_data *pRaw;
+       const char *zData;
+       int iLength;
+       if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
+               /* Missing/Invalid arguments */
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* 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[0]);
+       if( SXARCH_ENTRY_INVALID(pEntry) ){
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       zData = 0;
+       if( pEntry->nReadCount >= pEntry->nByteCompr ){
+               /* No more data to read, return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Set a default read length */
+       iLength = 1024;
+       if( nArg > 1 ){
+               iLength = jx9_value_to_int(apArg[1]);
+               if( iLength < 1 ){
+                       iLength = 1024;
+               }
+       }
+       if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){
+               iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount);
+       }
+       /* Return the entry contents */
+       pRaw = (zip_raw_data *)pEntry->pUserData;
+       if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
+               zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob, (pEntry->nOfft+pEntry->nReadCount));
+       }else{
+               const char *zMap = (const char *)pRaw->raw.mmap.pMap;
+               /* Memory mmaped chunk */
+               zData = &zMap[pEntry->nOfft+pEntry->nReadCount];
+       }
+       /* Increment the read counter */
+       pEntry->nReadCount += iLength;
+       /* Return the raw data */
+       jx9_result_string(pCtx, zData, iLength);
+       return JX9_OK;
+}
+/*
+  * bool zip_entry_reset_cursor(resource $zip_entry)
+  *  Reset the read cursor of an open directory entry.
+  * Parameters
+  *  $zip_entry
+  *   A directory entry returned by zip_read().
+  * Return
+  *  TRUE on success, FALSE on failure.
+  */
+static int jx9Builtin_zip_entry_reset_cursor(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SyArchiveEntry *pEntry;
+       if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
+               /* Missing/Invalid arguments */
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* 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[0]);
+       if( SXARCH_ENTRY_INVALID(pEntry) ){
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       /* Reset the cursor */
+       pEntry->nReadCount = 0;
+       /* Return TRUE */
+       jx9_result_bool(pCtx, 1);
+       return JX9_OK;
+}
+/*
+  * string zip_entry_compressionmethod(resource $zip_entry)
+  *  Retrieve the compression method of a directory entry.
+  * Parameters
+  *  $zip_entry
+  *   A directory entry returned by zip_read().
+  * Return
+  *  The compression method on success or FALSE on failure.
+  */
+static int jx9Builtin_zip_entry_compressionmethod(jx9_context *pCtx, int nArg, jx9_value **apArg)
+{
+       SyArchiveEntry *pEntry;
+       if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
+               /* Missing/Invalid arguments */
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* 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[0]);
+       if( SXARCH_ENTRY_INVALID(pEntry) ){
+               jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
+               /* return FALSE */
+               jx9_result_bool(pCtx, 0);
+               return JX9_OK;
+       }
+       switch(pEntry->nComprMeth){
+       case 0:
+               /* No compression;entry is stored */
+               jx9_result_string(pCtx, "stored", (int)sizeof("stored")-1);
+               break;
+       case 8:
+               /* Entry is deflated (Default compression algorithm)  */
+               jx9_result_string(pCtx, "deflate", (int)sizeof("deflate")-1);
+               break;
+               /* Exotic compression algorithms */ 
+       case 1:
+               jx9_result_string(pCtx, "shrunk", (int)sizeof("shrunk")-1);
+               break;
+       case 2:
+       case 3:
+       case 4:
+       case 5:
+               /* Entry is reduced */
+               jx9_result_string(pCtx, "reduced", (int)sizeof("reduced")-1);
+               break;
+       case 6:
+               /* Entry is imploded */
+               jx9_result_string(pCtx, "implode", (int)sizeof("implode")-1);
+               break;
+       default:
+               jx9_result_string(pCtx, "unknown", (int)sizeof("unknown")-1);
+               break;
+       }
+       return JX9_OK;
+}
+#endif /* #ifndef JX9_DISABLE_BUILTIN_FUNC*/
+/* NULL VFS [i.e: a no-op VFS]*/
+static const jx9_vfs null_vfs = {
+       "null_vfs", 
+       JX9_VFS_VERSION, 
+       0, /* int (*xChdir)(const char *) */
+       0, /* int (*xChroot)(const char *); */
+       0, /* int (*xGetcwd)(jx9_context *) */
+       0, /* int (*xMkdir)(const char *, int, int) */
+       0, /* int (*xRmdir)(const char *) */ 
+       0, /* int (*xIsdir)(const char *) */
+       0, /* int (*xRename)(const char *, const char *) */
+       0, /*int (*xRealpath)(const char *, jx9_context *)*/
+       0, /* int (*xSleep)(unsigned int) */
+       0, /* int (*xUnlink)(const char *) */
+       0, /* int (*xFileExists)(const char *) */
+       0, /*int (*xChmod)(const char *, int)*/
+       0, /*int (*xChown)(const char *, const char *)*/
+       0, /*int (*xChgrp)(const char *, const char *)*/
+       0, /* jx9_int64 (*xFreeSpace)(const char *) */
+       0, /* jx9_int64 (*xTotalSpace)(const char *) */
+       0, /* jx9_int64 (*xFileSize)(const char *) */
+       0, /* jx9_int64 (*xFileAtime)(const char *) */
+       0, /* jx9_int64 (*xFileMtime)(const char *) */
+       0, /* jx9_int64 (*xFileCtime)(const char *) */
+       0, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
+       0, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
+       0, /* int (*xIsfile)(const char *) */
+       0, /* int (*xIslink)(const char *) */
+       0, /* int (*xReadable)(const char *) */
+       0, /* int (*xWritable)(const char *) */
+       0, /* int (*xExecutable)(const char *) */
+       0, /* int (*xFiletype)(const char *, jx9_context *) */
+       0, /* int (*xGetenv)(const char *, jx9_context *) */
+       0, /* int (*xSetenv)(const char *, const char *) */ 
+       0, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
+       0, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
+       0, /* void (*xUnmap)(void *, jx9_int64);  */
+       0, /* int (*xLink)(const char *, const char *, int) */
+       0, /* int (*xUmask)(int) */
+       0, /* void (*xTempDir)(jx9_context *) */
+       0, /* unsigned int (*xProcessId)(void) */
+       0, /* int (*xUid)(void) */
+       0, /* int (*xGid)(void) */
+       0, /* void (*xUsername)(jx9_context *) */
+       0  /* int (*xExec)(const char *, jx9_context *) */
+};
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifndef JX9_DISABLE_DISK_IO
+#ifdef __WINNT__
+/*
+ * Windows VFS implementation for the JX9 engine.
+ * Authors:
+ *    Symisc Systems, devel@symisc.net.
+ *    Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Stable.
+ */
+/* What follows here is code that is specific to windows systems. */
+#include <Windows.h>
+/*
+** Convert a UTF-8 string to microsoft unicode (UTF-16?).
+**
+** Space to hold the returned string is obtained from HeapAlloc().
+** Taken from the sqlite3 source tree
+** status: Public Domain
+*/
+static WCHAR *jx9utf8ToUnicode(const char *zFilename){
+  int nChar;
+  WCHAR *zWideFilename;
+
+  nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
+  zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, nChar*sizeof(zWideFilename[0]));
+  if( zWideFilename == 0 ){
+       return 0;
+  } 
+  nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
+  if( nChar==0 ){
+    HeapFree(GetProcessHeap(), 0, zWideFilename);
+    return 0;
+  }
+  return zWideFilename;
+}
+/*
+** Convert a UTF-8 filename into whatever form the underlying
+** operating system wants filenames in.Space to hold the result
+** is obtained from HeapAlloc() and must be freed by the calling
+** function.
+** Taken from the sqlite3 source tree
+** status: Public Domain
+*/
+static void *jx9convertUtf8Filename(const char *zFilename){
+  void *zConverted;
+  zConverted = jx9utf8ToUnicode(zFilename);
+  return zConverted;
+}
+/*
+** Convert microsoft unicode to UTF-8.  Space to hold the returned string is
+** obtained from HeapAlloc().
+** Taken from the sqlite3 source tree
+** status: Public Domain
+*/
+static char *jx9unicodeToUtf8(const WCHAR *zWideFilename){
+  char *zFilename;
+  int nByte;
+
+  nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
+  zFilename = (char *)HeapAlloc(GetProcessHeap(), 0, nByte);
+  if( zFilename == 0 ){
+       return 0;
+  }
+  nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0);
+  if( nByte == 0 ){
+    HeapFree(GetProcessHeap(), 0, zFilename);
+    return 0;
+  }
+  return zFilename;
+}
+/* int (*xchdir)(const char *) */
+static int WinVfs_chdir(const char *zPath)
+{
+       void * pConverted;
+       BOOL rc;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       rc = SetCurrentDirectoryW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       return rc ? JX9_OK : -1;
+}
+/* int (*xGetcwd)(jx9_context *) */
+static int WinVfs_getcwd(jx9_context *pCtx)
+{
+       WCHAR zDir[2048];
+       char *zConverted;
+       DWORD rc;
+       /* Get the current directory */
+       rc = GetCurrentDirectoryW(sizeof(zDir), zDir);
+       if( rc < 1 ){
+               return -1;
+       }
+       zConverted = jx9unicodeToUtf8(zDir);
+       if( zConverted == 0 ){
+               return -1;
+       }
+       jx9_result_string(pCtx, zConverted, -1/*Compute length automatically*/); /* Will make it's own copy */
+       HeapFree(GetProcessHeap(), 0, zConverted);
+       return JX9_OK;
+}
+/* int (*xMkdir)(const char *, int, int) */
+static int WinVfs_mkdir(const char *zPath, int mode, int recursive)
+{
+       void * pConverted;
+       BOOL rc;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       mode= 0; /* MSVC warning */
+       recursive = 0;
+       rc = CreateDirectoryW((LPCWSTR)pConverted, 0);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       return rc ? JX9_OK : -1;
+}
+/* int (*xRmdir)(const char *) */
+static int WinVfs_rmdir(const char *zPath)
+{
+       void * pConverted;
+       BOOL rc;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       rc = RemoveDirectoryW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       return rc ? JX9_OK : -1;
+}
+/* int (*xIsdir)(const char *) */
+static int WinVfs_isdir(const char *zPath)
+{
+       void * pConverted;
+       DWORD dwAttr;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( dwAttr == INVALID_FILE_ATTRIBUTES ){
+               return -1;
+       }
+       return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? JX9_OK : -1;
+}
+/* int (*xRename)(const char *, const char *) */
+static int WinVfs_Rename(const char *zOld, const char *zNew)
+{
+       void *pOld, *pNew;
+       BOOL rc = 0;
+       pOld = jx9convertUtf8Filename(zOld);
+       if( pOld == 0 ){
+               return -1;
+       }
+       pNew = jx9convertUtf8Filename(zNew);
+       if( pNew  ){
+               rc = MoveFileW((LPCWSTR)pOld, (LPCWSTR)pNew);
+       }
+       HeapFree(GetProcessHeap(), 0, pOld);
+       if( pNew ){
+               HeapFree(GetProcessHeap(), 0, pNew);
+       }
+       return rc ? JX9_OK : - 1;
+}
+/* int (*xRealpath)(const char *, jx9_context *) */
+static int WinVfs_Realpath(const char *zPath, jx9_context *pCtx)
+{
+       WCHAR zTemp[2048];
+       void *pPath;
+       char *zReal;
+       DWORD n;
+       pPath = jx9convertUtf8Filename(zPath);
+       if( pPath == 0 ){
+               return -1;
+       }
+       n = GetFullPathNameW((LPCWSTR)pPath, 0, 0, 0);
+       if( n > 0 ){
+               if( n >= sizeof(zTemp) ){
+                       n = sizeof(zTemp) - 1;
+               }
+               GetFullPathNameW((LPCWSTR)pPath, n, zTemp, 0);
+       }
+       HeapFree(GetProcessHeap(), 0, pPath);
+       if( !n ){
+               return -1;
+       }
+       zReal = jx9unicodeToUtf8(zTemp);
+       if( zReal == 0 ){
+               return -1;
+       }
+       jx9_result_string(pCtx, zReal, -1); /* Will make it's own copy */
+       HeapFree(GetProcessHeap(), 0, zReal);
+       return JX9_OK;
+}
+/* int (*xSleep)(unsigned int) */
+static int WinVfs_Sleep(unsigned int uSec)
+{
+       Sleep(uSec/1000/*uSec per Millisec */);
+       return JX9_OK;
+}
+/* int (*xUnlink)(const char *) */
+static int WinVfs_unlink(const char *zPath)
+{
+       void * pConverted;
+       BOOL rc;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       rc = DeleteFileW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       return rc ? JX9_OK : - 1;
+}
+/* jx9_int64 (*xFreeSpace)(const char *) */
+static jx9_int64 WinVfs_DiskFreeSpace(const char *zPath)
+{
+#ifdef _WIN32_WCE
+       /* GetDiskFreeSpace is not supported under WINCE */
+       SXUNUSED(zPath);
+       return 0;
+#else
+       DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
+       void * pConverted;
+       WCHAR *p;
+       BOOL rc;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return 0;
+       }
+       p = (WCHAR *)pConverted;
+       for(;*p;p++){
+               if( *p == '\\' || *p == '/'){
+                       *p = '\0';
+                       break;
+               }
+       }
+       rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
+       if( !rc ){
+               return 0;
+       }
+       return (jx9_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect;
+#endif
+}
+/* jx9_int64 (*xTotalSpace)(const char *) */
+static jx9_int64 WinVfs_DiskTotalSpace(const char *zPath)
+{
+#ifdef _WIN32_WCE
+       /* GetDiskFreeSpace is not supported under WINCE */
+       SXUNUSED(zPath);
+       return 0;
+#else
+       DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
+       void * pConverted;
+       WCHAR *p;
+       BOOL rc;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return 0;
+       }
+       p = (WCHAR *)pConverted;
+       for(;*p;p++){
+               if( *p == '\\' || *p == '/'){
+                       *p = '\0';
+                       break;
+               }
+       }
+       rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
+       if( !rc ){
+               return 0;
+       }
+       return (jx9_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect;
+#endif
+}
+/* int (*xFileExists)(const char *) */
+static int WinVfs_FileExists(const char *zPath)
+{
+       void * pConverted;
+       DWORD dwAttr;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( dwAttr == INVALID_FILE_ATTRIBUTES ){
+               return -1;
+       }
+       return JX9_OK;
+}
+/* Open a file in a read-only mode */
+static HANDLE OpenReadOnly(LPCWSTR pPath)
+{
+       DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
+       DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
+       DWORD dwAccess = GENERIC_READ;
+       DWORD dwCreate = OPEN_EXISTING; 
+       HANDLE pHandle;
+       pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0);
+       if( pHandle == INVALID_HANDLE_VALUE){
+               return 0;
+       }
+       return pHandle;
+}
+/* jx9_int64 (*xFileSize)(const char *) */
+static jx9_int64 WinVfs_FileSize(const char *zPath)
+{
+       DWORD dwLow, dwHigh;
+       void * pConverted;
+       jx9_int64 nSize;
+       HANDLE pHandle;
+       
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       /* Open the file in read-only mode */
+       pHandle = OpenReadOnly((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( pHandle ){
+               dwLow = GetFileSize(pHandle, &dwHigh);
+               nSize = dwHigh;
+               nSize <<= 32;
+               nSize += dwLow;
+               CloseHandle(pHandle);
+       }else{
+               nSize = -1;
+       }
+       return nSize;
+}
+#define TICKS_PER_SECOND 10000000
+#define EPOCH_DIFFERENCE 11644473600LL
+/* Convert Windows timestamp to UNIX timestamp */
+static jx9_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime)
+{
+    jx9_int64 input, temp;
+       input = pTime->dwHighDateTime;
+       input <<= 32;
+       input += pTime->dwLowDateTime;
+    temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/
+    temp = temp - EPOCH_DIFFERENCE;  /*subtract number of seconds between epochs*/
+    return temp;
+}
+/* Convert UNIX timestamp to Windows timestamp */
+static void convertUnixTimeToWindowsTime(jx9_int64 nUnixtime, LPFILETIME pOut)
+{
+  jx9_int64 result = EPOCH_DIFFERENCE;
+  result += nUnixtime;
+  result *= 10000000LL;
+  pOut->dwHighDateTime = (DWORD)(nUnixtime>>32);
+  pOut->dwLowDateTime = (DWORD)nUnixtime;
+}
+/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
+static int WinVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
+{
+       FILETIME sTouch, sAccess;
+       void *pConverted;
+       void *pHandle;
+       BOOL rc = 0;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       pHandle = OpenReadOnly((LPCWSTR)pConverted);
+       if( pHandle ){
+               if( touch_time < 0 ){
+                       GetSystemTimeAsFileTime(&sTouch);
+               }else{
+                       convertUnixTimeToWindowsTime(touch_time, &sTouch);
+               }
+               if( access_time < 0 ){
+                       /* Use the touch time */
+                       sAccess = sTouch; /* Structure assignment */
+               }else{
+                       convertUnixTimeToWindowsTime(access_time, &sAccess);
+               }
+               rc = SetFileTime(pHandle, &sTouch, &sAccess, 0);
+               /* Close the handle */
+               CloseHandle(pHandle);
+       }
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       return rc ? JX9_OK : -1;
+}
+/* jx9_int64 (*xFileAtime)(const char *) */
+static jx9_int64 WinVfs_FileAtime(const char *zPath)
+{
+       BY_HANDLE_FILE_INFORMATION sInfo;
+       void * pConverted;
+       jx9_int64 atime;
+       HANDLE pHandle;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       /* Open the file in read-only mode */
+       pHandle = OpenReadOnly((LPCWSTR)pConverted);
+       if( pHandle ){
+               BOOL rc;
+               rc = GetFileInformationByHandle(pHandle, &sInfo);
+               if( rc ){
+                       atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime);
+               }else{
+                       atime = -1;
+               }
+               CloseHandle(pHandle);
+       }else{
+               atime = -1;
+       }
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       return atime;
+}
+/* jx9_int64 (*xFileMtime)(const char *) */
+static jx9_int64 WinVfs_FileMtime(const char *zPath)
+{
+       BY_HANDLE_FILE_INFORMATION sInfo;
+       void * pConverted;
+       jx9_int64 mtime;
+       HANDLE pHandle;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       /* Open the file in read-only mode */
+       pHandle = OpenReadOnly((LPCWSTR)pConverted);
+       if( pHandle ){
+               BOOL rc;
+               rc = GetFileInformationByHandle(pHandle, &sInfo);
+               if( rc ){
+                       mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime);
+               }else{
+                       mtime = -1;
+               }
+               CloseHandle(pHandle);
+       }else{
+               mtime = -1;
+       }
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       return mtime;
+}
+/* jx9_int64 (*xFileCtime)(const char *) */
+static jx9_int64 WinVfs_FileCtime(const char *zPath)
+{
+       BY_HANDLE_FILE_INFORMATION sInfo;
+       void * pConverted;
+       jx9_int64 ctime;
+       HANDLE pHandle;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       /* Open the file in read-only mode */
+       pHandle = OpenReadOnly((LPCWSTR)pConverted);
+       if( pHandle ){
+               BOOL rc;
+               rc = GetFileInformationByHandle(pHandle, &sInfo);
+               if( rc ){
+                       ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime);
+               }else{
+                       ctime = -1;
+               }
+               CloseHandle(pHandle);
+       }else{
+               ctime = -1;
+       }
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       return ctime;
+}
+/* int (*xStat)(const char *, jx9_value *, jx9_value *) */
+/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
+static int WinVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
+{
+       BY_HANDLE_FILE_INFORMATION sInfo;
+       void *pConverted;
+       HANDLE pHandle;
+       BOOL rc;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       /* Open the file in read-only mode */
+       pHandle = OpenReadOnly((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( pHandle == 0 ){
+               return -1;
+       }
+       rc = GetFileInformationByHandle(pHandle, &sInfo);
+       CloseHandle(pHandle);
+       if( !rc ){
+               return -1;
+       }
+       /* dev */
+       jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
+       jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
+       /* ino */
+       jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
+       jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
+       /* mode */
+       jx9_value_int(pWorker, 0);
+       jx9_array_add_strkey_elem(pArray, "mode", pWorker);
+       /* nlink */
+       jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
+       jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
+       /* uid, gid, rdev */
+       jx9_value_int(pWorker, 0);
+       jx9_array_add_strkey_elem(pArray, "uid", pWorker);
+       jx9_array_add_strkey_elem(pArray, "gid", pWorker);
+       jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
+       /* size */
+       jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
+       jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
+       /* atime */
+       jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
+       jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
+       /* mtime */
+       jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
+       jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
+       /* ctime */
+       jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
+       jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
+       /* blksize, blocks */
+       jx9_value_int(pWorker, 0);              
+       jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
+       jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
+       return JX9_OK;
+}
+/* int (*xIsfile)(const char *) */
+static int WinVfs_isfile(const char *zPath)
+{
+       void * pConverted;
+       DWORD dwAttr;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( dwAttr == INVALID_FILE_ATTRIBUTES ){
+               return -1;
+       }
+       return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? JX9_OK : -1;
+}
+/* int (*xIslink)(const char *) */
+static int WinVfs_islink(const char *zPath)
+{
+       void * pConverted;
+       DWORD dwAttr;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( dwAttr == INVALID_FILE_ATTRIBUTES ){
+               return -1;
+       }
+       return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? JX9_OK : -1;
+}
+/* int (*xWritable)(const char *) */
+static int WinVfs_iswritable(const char *zPath)
+{
+       void * pConverted;
+       DWORD dwAttr;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( dwAttr == INVALID_FILE_ATTRIBUTES ){
+               return -1;
+       }
+       if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){
+               /* Not a regular file */
+               return -1;
+       }
+       if( dwAttr & FILE_ATTRIBUTE_READONLY ){
+               /* Read-only file */
+               return -1;
+       }
+       /* File is writable */
+       return JX9_OK;
+}
+/* int (*xExecutable)(const char *) */
+static int WinVfs_isexecutable(const char *zPath)
+{
+       void * pConverted;
+       DWORD dwAttr;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( dwAttr == INVALID_FILE_ATTRIBUTES ){
+               return -1;
+       }
+       if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){
+               /* Not a regular file */
+               return -1;
+       }
+       /* File is executable */
+       return JX9_OK;
+}
+/* int (*xFiletype)(const char *, jx9_context *) */
+static int WinVfs_Filetype(const char *zPath, jx9_context *pCtx)
+{
+       void * pConverted;
+       DWORD dwAttr;
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               /* Expand 'unknown' */
+               jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
+               return -1;
+       }
+       dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( dwAttr == INVALID_FILE_ATTRIBUTES ){
+               /* Expand 'unknown' */
+               jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
+               return -1;
+       }
+       if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){
+               jx9_result_string(pCtx, "file", sizeof("file")-1);
+       }else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){
+               jx9_result_string(pCtx, "dir", sizeof("dir")-1);
+       }else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){
+               jx9_result_string(pCtx, "link", sizeof("link")-1);
+       }else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){
+               jx9_result_string(pCtx, "block", sizeof("block")-1);
+       }else{
+               jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
+       }
+       return JX9_OK;
+}
+/* int (*xGetenv)(const char *, jx9_context *) */
+static int WinVfs_Getenv(const char *zVar, jx9_context *pCtx)
+{
+       char zValue[1024];
+       DWORD n;
+       /*
+        * According to MSDN
+        * If lpBuffer is not large enough to hold the data, the return 
+        * value is the buffer size, in characters, required to hold the 
+        * string and its terminating null character and the contents 
+        * of lpBuffer are undefined.
+        */
+       n = sizeof(zValue);
+       SyMemcpy("Undefined", zValue, sizeof("Undefined")-1);
+       /* Extract the environment value */
+       n = GetEnvironmentVariableA(zVar, zValue, sizeof(zValue));
+       if( !n ){
+               /* No such variable*/
+               return -1;
+       }
+       jx9_result_string(pCtx, zValue, (int)n);
+       return JX9_OK;
+}
+/* int (*xSetenv)(const char *, const char *) */
+static int WinVfs_Setenv(const char *zName, const char *zValue)
+{
+       BOOL rc;
+       rc = SetEnvironmentVariableA(zName, zValue);
+       return rc ? JX9_OK : -1;
+}
+/* int (*xMmap)(const char *, void **, jx9_int64 *) */
+static int WinVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
+{
+       DWORD dwSizeLow, dwSizeHigh;
+       HANDLE pHandle, pMapHandle;
+       void *pConverted, *pView;
+
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       pHandle = OpenReadOnly((LPCWSTR)pConverted);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( pHandle == 0 ){
+               return -1;
+       }
+       /* Get the file size */
+       dwSizeLow = GetFileSize(pHandle, &dwSizeHigh);
+       /* Create the mapping */
+       pMapHandle = CreateFileMappingW(pHandle, 0, PAGE_READONLY, dwSizeHigh, dwSizeLow, 0);
+       if( pMapHandle == 0 ){
+               CloseHandle(pHandle);
+               return -1;
+       }
+       *pSize = ((jx9_int64)dwSizeHigh << 32) | dwSizeLow;
+       /* Obtain the view */
+       pView = MapViewOfFile(pMapHandle, FILE_MAP_READ, 0, 0, (SIZE_T)(*pSize));
+       if( pView ){
+               /* Let the upper layer point to the view */
+               *ppMap = pView;
+       }
+       /* Close the handle
+        * According to MSDN it's OK the close the HANDLES.
+        */
+       CloseHandle(pMapHandle);
+       CloseHandle(pHandle);
+       return pView ? JX9_OK : -1;
+}
+/* void (*xUnmap)(void *, jx9_int64)  */
+static void WinVfs_Unmap(void *pView, jx9_int64 nSize)
+{
+       nSize = 0; /* Compiler warning */
+       UnmapViewOfFile(pView);
+}
+/* void (*xTempDir)(jx9_context *) */
+static void WinVfs_TempDir(jx9_context *pCtx)
+{
+       CHAR zTemp[1024];
+       DWORD n;
+       n = GetTempPathA(sizeof(zTemp), zTemp);
+       if( n < 1 ){
+               /* Assume the default windows temp directory */
+               jx9_result_string(pCtx, "C:\\Windows\\Temp", -1/*Compute length automatically*/);
+       }else{
+               jx9_result_string(pCtx, zTemp, (int)n);
+       }
+}
+/* unsigned int (*xProcessId)(void) */
+static unsigned int WinVfs_ProcessId(void)
+{
+       DWORD nID = 0;
+#ifndef __MINGW32__
+       nID = GetProcessId(GetCurrentProcess());
+#endif /* __MINGW32__ */
+       return (unsigned int)nID;
+}
+
+/* Export the windows vfs */
+static const jx9_vfs sWinVfs = {
+       "Windows_vfs", 
+       JX9_VFS_VERSION, 
+       WinVfs_chdir,    /* int (*xChdir)(const char *) */
+       0,               /* int (*xChroot)(const char *); */
+       WinVfs_getcwd,   /* int (*xGetcwd)(jx9_context *) */
+       WinVfs_mkdir,    /* int (*xMkdir)(const char *, int, int) */
+       WinVfs_rmdir,    /* int (*xRmdir)(const char *) */ 
+       WinVfs_isdir,    /* int (*xIsdir)(const char *) */
+       WinVfs_Rename,   /* int (*xRename)(const char *, const char *) */
+       WinVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
+       WinVfs_Sleep,               /* int (*xSleep)(unsigned int) */
+       WinVfs_unlink,   /* int (*xUnlink)(const char *) */
+       WinVfs_FileExists, /* int (*xFileExists)(const char *) */
+       0, /*int (*xChmod)(const char *, int)*/
+       0, /*int (*xChown)(const char *, const char *)*/
+       0, /*int (*xChgrp)(const char *, const char *)*/
+       WinVfs_DiskFreeSpace, /* jx9_int64 (*xFreeSpace)(const char *) */
+       WinVfs_DiskTotalSpace, /* jx9_int64 (*xTotalSpace)(const char *) */
+       WinVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
+       WinVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
+       WinVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
+       WinVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
+       WinVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
+       WinVfs_Stat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
+       WinVfs_isfile,     /* int (*xIsfile)(const char *) */
+       WinVfs_islink,     /* int (*xIslink)(const char *) */
+       WinVfs_isfile,     /* int (*xReadable)(const char *) */
+       WinVfs_iswritable, /* int (*xWritable)(const char *) */
+       WinVfs_isexecutable, /* int (*xExecutable)(const char *) */
+       WinVfs_Filetype,   /* int (*xFiletype)(const char *, jx9_context *) */
+       WinVfs_Getenv,     /* int (*xGetenv)(const char *, jx9_context *) */
+       WinVfs_Setenv,     /* int (*xSetenv)(const char *, const char *) */ 
+       WinVfs_Touch,      /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
+       WinVfs_Mmap,       /* int (*xMmap)(const char *, void **, jx9_int64 *) */
+       WinVfs_Unmap,      /* void (*xUnmap)(void *, jx9_int64);  */
+       0,                 /* int (*xLink)(const char *, const char *, int) */
+       0,                 /* int (*xUmask)(int) */
+       WinVfs_TempDir,    /* void (*xTempDir)(jx9_context *) */
+       WinVfs_ProcessId,  /* unsigned int (*xProcessId)(void) */
+       0, /* int (*xUid)(void) */
+       0, /* int (*xGid)(void) */
+       0, /* void (*xUsername)(jx9_context *) */
+       0  /* int (*xExec)(const char *, jx9_context *) */
+};
+/* Windows file IO */
+#ifndef INVALID_SET_FILE_POINTER
+# define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+/* int (*xOpen)(const char *, int, jx9_value *, void **) */
+static int WinFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
+{
+       DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
+       DWORD dwAccess = GENERIC_READ;
+       DWORD dwShare, dwCreate;        
+       void *pConverted;
+       HANDLE pHandle;
+
+       pConverted = jx9convertUtf8Filename(zPath);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       /* Set the desired flags according to the open mode */
+       if( iOpenMode & JX9_IO_OPEN_CREATE ){
+               /* Open existing file, or create if it doesn't exist */
+               dwCreate = OPEN_ALWAYS;
+               if( iOpenMode & JX9_IO_OPEN_TRUNC ){
+                       /* If the specified file exists and is writable, the function overwrites the file */
+                       dwCreate = CREATE_ALWAYS;
+               }
+       }else if( iOpenMode & JX9_IO_OPEN_EXCL ){
+               /* Creates a new file, only if it does not already exist.
+               * If the file exists, it fails.
+               */
+               dwCreate = CREATE_NEW;
+       }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
+               /* Opens a file and truncates it so that its size is zero bytes
+                * The file must exist.
+                */
+               dwCreate = TRUNCATE_EXISTING;
+       }else{
+               /* Opens a file, only if it exists. */
+               dwCreate = OPEN_EXISTING;
+       }
+       if( iOpenMode & JX9_IO_OPEN_RDWR ){
+               /* Read+Write access */
+               dwAccess |= GENERIC_WRITE;
+       }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
+               /* Write only access */
+               dwAccess = GENERIC_WRITE;
+       }
+       if( iOpenMode & JX9_IO_OPEN_APPEND ){
+               /* Append mode */
+               dwAccess = FILE_APPEND_DATA;
+       }
+       if( iOpenMode & JX9_IO_OPEN_TEMP ){
+               /* File is temporary */
+               dwType = FILE_ATTRIBUTE_TEMPORARY;
+       }
+       dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
+       pHandle = CreateFileW((LPCWSTR)pConverted, dwAccess, dwShare, 0, dwCreate, dwType, 0);
+       HeapFree(GetProcessHeap(), 0, pConverted);
+       if( pHandle == INVALID_HANDLE_VALUE){
+               SXUNUSED(pResource); /* MSVC warning */
+               return -1;
+       }
+       /* Make the handle accessible to the upper layer */
+       *ppHandle = (void *)pHandle;
+       return JX9_OK;
+}
+/* An instance of the following structure is used to record state information 
+ * while iterating throw directory entries.
+ */
+typedef struct WinDir_Info WinDir_Info;
+struct WinDir_Info
+{
+       HANDLE pDirHandle;
+       void *pPath;
+       WIN32_FIND_DATAW sInfo;
+       int rc;
+};
+/* int (*xOpenDir)(const char *, jx9_value *, void **) */
+static int WinDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
+{
+       WinDir_Info *pDirInfo;
+       void *pConverted;
+       char *zPrep;
+       sxu32 n;
+       /* Prepare the path */
+       n = SyStrlen(zPath);
+       zPrep = (char *)HeapAlloc(GetProcessHeap(), 0, n+sizeof("\\*")+4);
+       if( zPrep == 0 ){
+               return -1;
+       }
+       SyMemcpy((const void *)zPath, zPrep, n);
+       zPrep[n]   = '\\';
+       zPrep[n+1] =  '*';
+       zPrep[n+2] = 0;
+       pConverted = jx9convertUtf8Filename(zPrep);
+       HeapFree(GetProcessHeap(), 0, zPrep);
+       if( pConverted == 0 ){
+               return -1;
+       }
+       /* Allocate a new instance */
+       pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(), 0, sizeof(WinDir_Info));
+       if( pDirInfo == 0 ){
+               pResource = 0; /* Compiler warning */
+               return -1;
+       }
+       pDirInfo->rc = SXRET_OK;
+       pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted, &pDirInfo->sInfo);
+       if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
+               /* Cannot open directory */
+               HeapFree(GetProcessHeap(), 0, pConverted);
+               HeapFree(GetProcessHeap(), 0, pDirInfo);
+               return -1;
+       }
+       /* Save the path */
+       pDirInfo->pPath = pConverted;
+       /* Save our structure */
+       *ppHandle = pDirInfo;
+       return JX9_OK;
+}
+/* void (*xCloseDir)(void *) */
+static void WinDir_Close(void *pUserData)
+{
+       WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
+       if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){
+               FindClose(pDirInfo->pDirHandle);
+       }
+       HeapFree(GetProcessHeap(), 0, pDirInfo->pPath);
+       HeapFree(GetProcessHeap(), 0, pDirInfo);
+}
+/* void (*xClose)(void *); */
+static void WinFile_Close(void *pUserData)
+{
+       HANDLE pHandle = (HANDLE)pUserData;
+       CloseHandle(pHandle);
+}
+/* int (*xReadDir)(void *, jx9_context *) */
+static int WinDir_Read(void *pUserData, jx9_context *pCtx)
+{
+       WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
+       LPWIN32_FIND_DATAW pData;
+       char *zName;
+       BOOL rc;
+       sxu32 n;
+       if( pDirInfo->rc != SXRET_OK ){
+               /* No more entry to process */
+               return -1;
+       }
+       pData = &pDirInfo->sInfo;
+       for(;;){
+               zName = jx9unicodeToUtf8(pData->cFileName);
+               if( zName == 0 ){
+                       /* Out of memory */
+                       return -1;
+               }
+               n = SyStrlen(zName);
+               /* Ignore '.' && '..' */
+               if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
+                       break;
+               }
+               HeapFree(GetProcessHeap(), 0, zName);
+               rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
+               if( !rc ){
+                       return -1;
+               }
+       }
+       /* Return the current file name */
+       jx9_result_string(pCtx, zName, -1);
+       HeapFree(GetProcessHeap(), 0, zName);
+       /* Point to the next entry */
+       rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
+       if( !rc ){
+               pDirInfo->rc = SXERR_EOF;
+       }
+       return JX9_OK;
+}
+/* void (*xRewindDir)(void *) */
+static void WinDir_RewindDir(void *pUserData)
+{
+       WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
+       FindClose(pDirInfo->pDirHandle);
+       pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath, &pDirInfo->sInfo);
+       if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
+               pDirInfo->rc = SXERR_EOF;
+       }else{
+               pDirInfo->rc = SXRET_OK;
+       }
+}
+/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
+static jx9_int64 WinFile_Read(void *pOS, void *pBuffer, jx9_int64 nDatatoRead)
+{
+       HANDLE pHandle = (HANDLE)pOS;
+       DWORD nRd;
+       BOOL rc;
+       rc = ReadFile(pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
+       if( !rc ){
+               /* EOF or IO error */
+               return -1;
+       }
+       return (jx9_int64)nRd;
+}
+/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
+static jx9_int64 WinFile_Write(void *pOS, const void *pBuffer, jx9_int64 nWrite)
+{
+       const char *zData = (const char *)pBuffer;
+       HANDLE pHandle = (HANDLE)pOS;
+       jx9_int64 nCount;
+       DWORD nWr;
+       BOOL rc;
+       nWr = 0;
+       nCount = 0;
+       for(;;){
+               if( nWrite < 1 ){
+                       break;
+               }
+               rc = WriteFile(pHandle, zData, (DWORD)nWrite, &nWr, 0);
+               if( !rc ){
+                       /* IO error */
+                       break;
+               }
+               nWrite -= nWr;
+               nCount += nWr;
+               zData += nWr;
+       }
+       if( nWrite > 0 ){
+               return -1;
+       }
+       return nCount;
+}
+/* int (*xSeek)(void *, jx9_int64, int) */
+static int WinFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
+{
+       HANDLE pHandle = (HANDLE)pUserData;
+       DWORD dwMove, dwNew;
+       LONG nHighOfft;
+       switch(whence){
+       case 1:/*SEEK_CUR*/
+               dwMove = FILE_CURRENT;
+               break;
+       case 2: /* SEEK_END */
+               dwMove = FILE_END;
+               break;
+       case 0: /* SEEK_SET */
+       default:
+               dwMove = FILE_BEGIN;
+               break;
+       }
+       nHighOfft = (LONG)(iOfft >> 32);
+       dwNew = SetFilePointer(pHandle, (LONG)iOfft, &nHighOfft, dwMove);
+       if( dwNew == INVALID_SET_FILE_POINTER ){
+               return -1;
+       }
+       return JX9_OK;
+}
+/* int (*xLock)(void *, int) */
+static int WinFile_Lock(void *pUserData, int lock_type)
+{
+       HANDLE pHandle = (HANDLE)pUserData;
+       static DWORD dwLo = 0, dwHi = 0; /* xx: MT-SAFE */
+       OVERLAPPED sDummy;
+       BOOL rc;
+       SyZero(&sDummy, sizeof(sDummy));
+       /* Get the file size */
+       if( lock_type < 1 ){
+               /* Unlock the file */
+               rc = UnlockFileEx(pHandle, 0, dwLo, dwHi, &sDummy);
+       }else{
+               DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/
+               /* Lock the file */
+               if( lock_type == 1 /* LOCK_EXCL */ ){
+                       dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
+               }
+               dwLo = GetFileSize(pHandle, &dwHi);
+               rc = LockFileEx(pHandle, dwFlags, 0, dwLo, dwHi, &sDummy);
+       }
+       return rc ? JX9_OK : -1 /* Lock error */;
+}
+/* jx9_int64 (*xTell)(void *) */
+static jx9_int64 WinFile_Tell(void *pUserData)
+{
+       HANDLE pHandle = (HANDLE)pUserData;
+       DWORD dwNew;
+       dwNew = SetFilePointer(pHandle, 0, 0, FILE_CURRENT/* SEEK_CUR */);
+       if( dwNew == INVALID_SET_FILE_POINTER ){
+               return -1;
+       }
+       return (jx9_int64)dwNew;
+}
+/* int (*xTrunc)(void *, jx9_int64) */
+static int WinFile_Trunc(void *pUserData, jx9_int64 nOfft)
+{
+       HANDLE pHandle = (HANDLE)pUserData;
+       LONG HighOfft;
+       DWORD dwNew;
+       BOOL rc;
+       HighOfft = (LONG)(nOfft >> 32);
+       dwNew = SetFilePointer(pHandle, (LONG)nOfft, &HighOfft, FILE_BEGIN);
+       if( dwNew == INVALID_SET_FILE_POINTER ){
+               return -1;
+       }
+       rc = SetEndOfFile(pHandle);
+       return rc ? JX9_OK : -1;
+}
+/* int (*xSync)(void *); */
+static int WinFile_Sync(void *pUserData)
+{
+       HANDLE pHandle = (HANDLE)pUserData;
+       BOOL rc;
+       rc = FlushFileBuffers(pHandle);
+       return rc ? JX9_OK : - 1;
+}
+/* int (*xStat)(void *, jx9_value *, jx9_value *) */
+static int WinFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
+{
+       BY_HANDLE_FILE_INFORMATION sInfo;
+       HANDLE pHandle = (HANDLE)pUserData;
+       BOOL rc;
+       rc = GetFileInformationByHandle(pHandle, &sInfo);
+       if( !rc ){
+               return -1;
+       }
+       /* dev */
+       jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
+       jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
+       /* ino */
+       jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
+       jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
+       /* mode */
+       jx9_value_int(pWorker, 0);
+       jx9_array_add_strkey_elem(pArray, "mode", pWorker);
+       /* nlink */
+       jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
+       jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
+       /* uid, gid, rdev */
+       jx9_value_int(pWorker, 0);
+       jx9_array_add_strkey_elem(pArray, "uid", pWorker);
+       jx9_array_add_strkey_elem(pArray, "gid", pWorker);
+       jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
+       /* size */
+       jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
+       jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
+       /* atime */
+       jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
+       jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
+       /* mtime */
+       jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
+       jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
+       /* ctime */
+       jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
+       jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
+       /* blksize, blocks */
+       jx9_value_int(pWorker, 0);              
+       jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
+       jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
+       return JX9_OK;
+}
+/* Export the file:// stream */
+static const jx9_io_stream sWinFileStream = {
+       "file", /* Stream name */
+       JX9_IO_STREAM_VERSION, 
+       WinFile_Open,  /* xOpen */
+       WinDir_Open,   /* xOpenDir */
+       WinFile_Close, /* xClose */
+       WinDir_Close,  /* xCloseDir */
+       WinFile_Read,  /* xRead */
+       WinDir_Read,   /* xReadDir */
+       WinFile_Write, /* xWrite */
+       WinFile_Seek,  /* xSeek */
+       WinFile_Lock,  /* xLock */
+       WinDir_RewindDir, /* xRewindDir */
+       WinFile_Tell,  /* xTell */
+       WinFile_Trunc, /* xTrunc */
+       WinFile_Sync,  /* xSeek */
+       WinFile_Stat   /* xStat */
+};
+#elif defined(__UNIXES__)
+/*
+ * UNIX VFS implementation for the JX9 engine.
+ * Authors:
+ *    Symisc Systems, devel@symisc.net.
+ *    Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *    Stable.
+ */
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/file.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <utime.h>
+#include <stdio.h>
+#include <stdlib.h>
+/* int (*xchdir)(const char *) */
+static int UnixVfs_chdir(const char *zPath)
+{
+  int rc;
+  rc = chdir(zPath);
+  return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xGetcwd)(jx9_context *) */
+static int UnixVfs_getcwd(jx9_context *pCtx)
+{
+       char zBuf[4096];
+       char *zDir;
+       /* Get the current directory */
+       zDir = getcwd(zBuf, sizeof(zBuf));
+       if( zDir == 0 ){
+         return -1;
+    }
+       jx9_result_string(pCtx, zDir, -1/*Compute length automatically*/);
+       return JX9_OK;
+}
+/* int (*xMkdir)(const char *, int, int) */
+static int UnixVfs_mkdir(const char *zPath, int mode, int recursive)
+{
+       int rc;
+        rc = mkdir(zPath, mode);
+       recursive = 0; /* cc warning */
+       return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xRmdir)(const char *) */
+static int UnixVfs_rmdir(const char *zPath)
+{
+       int rc;
+       rc = rmdir(zPath);
+       return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xIsdir)(const char *) */
+static int UnixVfs_isdir(const char *zPath)
+{
+       struct stat st;
+       int rc;
+       rc = stat(zPath, &st);
+       if( rc != 0 ){
+        return -1;
+       }
+       rc = S_ISDIR(st.st_mode);
+       return rc ? JX9_OK : -1 ;
+}
+/* int (*xRename)(const char *, const char *) */
+static int UnixVfs_Rename(const char *zOld, const char *zNew)
+{
+       int rc;
+       rc = rename(zOld, zNew);
+       return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xRealpath)(const char *, jx9_context *) */
+static int UnixVfs_Realpath(const char *zPath, jx9_context *pCtx)
+{
+#ifndef JX9_UNIX_OLD_LIBC
+       char *zReal;
+       zReal = realpath(zPath, 0);
+       if( zReal == 0 ){
+         return -1;
+       }
+       jx9_result_string(pCtx, zReal, -1/*Compute length automatically*/);
+        /* Release the allocated buffer */
+       free(zReal);
+       return JX9_OK;
+#else
+    zPath = 0; /* cc warning */
+    pCtx = 0;
+    return -1;
+#endif
+}
+/* int (*xSleep)(unsigned int) */
+static int UnixVfs_Sleep(unsigned int uSec)
+{
+       usleep(uSec);
+       return JX9_OK;
+}
+/* int (*xUnlink)(const char *) */
+static int UnixVfs_unlink(const char *zPath)
+{
+       int rc;
+       rc = unlink(zPath);
+       return rc == 0 ? JX9_OK : -1 ;
+}
+/* int (*xFileExists)(const char *) */
+static int UnixVfs_FileExists(const char *zPath)
+{
+       int rc;
+       rc = access(zPath, F_OK);
+       return rc == 0 ? JX9_OK : -1;
+}
+/* jx9_int64 (*xFileSize)(const char *) */
+static jx9_int64 UnixVfs_FileSize(const char *zPath)
+{
+       struct stat st;
+       int rc;
+       rc = stat(zPath, &st);
+       if( rc != 0 ){ 
+        return -1;
+       }
+       return (jx9_int64)st.st_size;
+}
+/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
+static int UnixVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
+{
+       struct utimbuf ut;
+       int rc;
+       ut.actime  = (time_t)access_time;
+       ut.modtime = (time_t)touch_time;
+       rc = utime(zPath, &ut);
+       if( rc != 0 ){
+        return -1;
+       }
+       return JX9_OK;
+}
+/* jx9_int64 (*xFileAtime)(const char *) */
+static jx9_int64 UnixVfs_FileAtime(const char *zPath)
+{
+       struct stat st;
+       int rc;
+       rc = stat(zPath, &st);
+       if( rc != 0 ){ 
+        return -1;
+       }
+       return (jx9_int64)st.st_atime;
+}
+/* jx9_int64 (*xFileMtime)(const char *) */
+static jx9_int64 UnixVfs_FileMtime(const char *zPath)
+{
+       struct stat st;
+       int rc;
+       rc = stat(zPath, &st);
+       if( rc != 0 ){ 
+        return -1;
+       }
+       return (jx9_int64)st.st_mtime;
+}
+/* jx9_int64 (*xFileCtime)(const char *) */
+static jx9_int64 UnixVfs_FileCtime(const char *zPath)
+{
+       struct stat st;
+       int rc;
+       rc = stat(zPath, &st);
+       if( rc != 0 ){ 
+        return -1;
+       }
+       return (jx9_int64)st.st_ctime;
+}
+/* int (*xStat)(const char *, jx9_value *, jx9_value *) */
+static int UnixVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
+{
+       struct stat st;
+       int rc;
+       rc = stat(zPath, &st);
+       if( rc != 0 ){ 
+        return -1;
+       }
+       /* dev */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
+       jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
+       /* ino */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
+       jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
+       /* mode */
+       jx9_value_int(pWorker, (int)st.st_mode);
+       jx9_array_add_strkey_elem(pArray, "mode", pWorker);
+       /* nlink */
+       jx9_value_int(pWorker, (int)st.st_nlink);
+       jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
+       /* uid, gid, rdev */
+       jx9_value_int(pWorker, (int)st.st_uid);
+       jx9_array_add_strkey_elem(pArray, "uid", pWorker);
+       jx9_value_int(pWorker, (int)st.st_gid);
+       jx9_array_add_strkey_elem(pArray, "gid", pWorker);
+       jx9_value_int(pWorker, (int)st.st_rdev);
+       jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
+       /* size */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_size);
+       jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
+       /* atime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
+       jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
+       /* mtime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
+       jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
+       /* ctime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
+       jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
+       /* blksize, blocks */
+       jx9_value_int(pWorker, (int)st.st_blksize);             
+       jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
+       jx9_value_int(pWorker, (int)st.st_blocks);
+       jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
+       return JX9_OK;
+}
+/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
+static int UnixVfs_lStat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
+{
+       struct stat st;
+       int rc;
+       rc = lstat(zPath, &st);
+       if( rc != 0 ){ 
+        return -1;
+       }
+       /* dev */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
+       jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
+       /* ino */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
+       jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
+       /* mode */
+       jx9_value_int(pWorker, (int)st.st_mode);
+       jx9_array_add_strkey_elem(pArray, "mode", pWorker);
+       /* nlink */
+       jx9_value_int(pWorker, (int)st.st_nlink);
+       jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
+       /* uid, gid, rdev */
+       jx9_value_int(pWorker, (int)st.st_uid);
+       jx9_array_add_strkey_elem(pArray, "uid", pWorker);
+       jx9_value_int(pWorker, (int)st.st_gid);
+       jx9_array_add_strkey_elem(pArray, "gid", pWorker);
+       jx9_value_int(pWorker, (int)st.st_rdev);
+       jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
+       /* size */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_size);
+       jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
+       /* atime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
+       jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
+       /* mtime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
+       jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
+       /* ctime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
+       jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
+       /* blksize, blocks */
+       jx9_value_int(pWorker, (int)st.st_blksize);             
+       jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
+       jx9_value_int(pWorker, (int)st.st_blocks);
+       jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
+       return JX9_OK;
+}
+/* int (*xChmod)(const char *, int) */
+static int UnixVfs_Chmod(const char *zPath, int mode)
+{
+    int rc;
+    rc = chmod(zPath, (mode_t)mode);
+    return rc == 0 ? JX9_OK : - 1;
+}
+/* int (*xChown)(const char *, const char *) */
+static int UnixVfs_Chown(const char *zPath, const char *zUser)
+{
+#ifndef JX9_UNIX_STATIC_BUILD
+  struct passwd *pwd;
+  uid_t uid;
+  int rc;
+  pwd = getpwnam(zUser);   /* Try getting UID for username */
+  if (pwd == 0) {
+    return -1;
+  }
+  uid = pwd->pw_uid;
+  rc = chown(zPath, uid, -1);
+  return rc == 0 ? JX9_OK : -1;
+#else
+       SXUNUSED(zPath);
+       SXUNUSED(zUser);
+       return -1;
+#endif /* JX9_UNIX_STATIC_BUILD */
+}
+/* int (*xChgrp)(const char *, const char *) */
+static int UnixVfs_Chgrp(const char *zPath, const char *zGroup)
+{
+#ifndef JX9_UNIX_STATIC_BUILD
+  struct group *group;
+  gid_t gid;
+  int rc;
+  group = getgrnam(zGroup);
+  if (group == 0) {
+    return -1;
+  }
+  gid = group->gr_gid;
+  rc = chown(zPath, -1, gid);
+  return rc == 0 ? JX9_OK : -1;
+#else
+       SXUNUSED(zPath);
+       SXUNUSED(zGroup);
+       return -1;
+#endif /* JX9_UNIX_STATIC_BUILD */
+}
+/* int (*xIsfile)(const char *) */
+static int UnixVfs_isfile(const char *zPath)
+{
+       struct stat st;
+       int rc;
+       rc = stat(zPath, &st);
+       if( rc != 0 ){
+        return -1;
+       }
+       rc = S_ISREG(st.st_mode);
+       return rc ? JX9_OK : -1 ;
+}
+/* int (*xIslink)(const char *) */
+static int UnixVfs_islink(const char *zPath)
+{
+       struct stat st;
+       int rc;
+       rc = stat(zPath, &st);
+       if( rc != 0 ){
+        return -1;
+       }
+       rc = S_ISLNK(st.st_mode);
+       return rc ? JX9_OK : -1 ;
+}
+/* int (*xReadable)(const char *) */
+static int UnixVfs_isreadable(const char *zPath)
+{
+       int rc;
+       rc = access(zPath, R_OK);
+       return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xWritable)(const char *) */
+static int UnixVfs_iswritable(const char *zPath)
+{
+       int rc;
+       rc = access(zPath, W_OK);
+       return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xExecutable)(const char *) */
+static int UnixVfs_isexecutable(const char *zPath)
+{
+       int rc;
+       rc = access(zPath, X_OK);
+       return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xFiletype)(const char *, jx9_context *) */
+static int UnixVfs_Filetype(const char *zPath, jx9_context *pCtx)
+{
+       struct stat st;
+       int rc;
+    rc = stat(zPath, &st);
+       if( rc != 0 ){
+         /* Expand 'unknown' */
+         jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
+         return -1;
+       }
+       if(S_ISREG(st.st_mode) ){
+               jx9_result_string(pCtx, "file", sizeof("file")-1);
+       }else if(S_ISDIR(st.st_mode)){
+               jx9_result_string(pCtx, "dir", sizeof("dir")-1);
+       }else if(S_ISLNK(st.st_mode)){
+               jx9_result_string(pCtx, "link", sizeof("link")-1);
+       }else if(S_ISBLK(st.st_mode)){
+               jx9_result_string(pCtx, "block", sizeof("block")-1);
+    }else if(S_ISSOCK(st.st_mode)){
+               jx9_result_string(pCtx, "socket", sizeof("socket")-1);
+       }else if(S_ISFIFO(st.st_mode)){
+       jx9_result_string(pCtx, "fifo", sizeof("fifo")-1);
+       }else{
+               jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
+       }
+       return JX9_OK;
+}
+/* int (*xGetenv)(const char *, jx9_context *) */
+static int UnixVfs_Getenv(const char *zVar, jx9_context *pCtx)
+{
+       char *zEnv;
+       zEnv = getenv(zVar);
+       if( zEnv == 0 ){
+         return -1;
+       }
+       jx9_result_string(pCtx, zEnv, -1/*Compute length automatically*/);
+       return JX9_OK;
+}
+/* int (*xSetenv)(const char *, const char *) */
+static int UnixVfs_Setenv(const char *zName, const char *zValue)
+{
+   int rc;
+   rc = setenv(zName, zValue, 1);
+   return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xMmap)(const char *, void **, jx9_int64 *) */
+static int UnixVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
+{
+       struct stat st;
+       void *pMap;
+       int fd;
+       int rc;
+       /* Open the file in a read-only mode */
+       fd = open(zPath, O_RDONLY);
+       if( fd < 0 ){
+               return -1;
+       }
+       /* stat the handle */
+       fstat(fd, &st);
+       /* Obtain a memory view of the whole file */
+       pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0);
+       rc = JX9_OK;
+       if( pMap == MAP_FAILED ){
+               rc = -1;
+       }else{
+               /* Point to the memory view */
+               *ppMap = pMap;
+               *pSize = (jx9_int64)st.st_size;
+       }
+       close(fd);
+       return rc;
+}
+/* void (*xUnmap)(void *, jx9_int64)  */
+static void UnixVfs_Unmap(void *pView, jx9_int64 nSize)
+{
+       munmap(pView, (size_t)nSize);
+}
+/* void (*xTempDir)(jx9_context *) */
+static void UnixVfs_TempDir(jx9_context *pCtx)
+{
+       static const char *azDirs[] = {
+     "/var/tmp", 
+     "/usr/tmp", 
+        "/usr/local/tmp"
+  };
+  unsigned int i;
+  struct stat buf;
+  const char *zDir;
+  zDir = getenv("TMPDIR");
+  if( zDir && zDir[0] != 0 && !access(zDir, 07) ){
+         jx9_result_string(pCtx, zDir, -1);
+         return;
+  }
+  for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
+       zDir=azDirs[i];
+    if( zDir==0 ) continue;
+    if( stat(zDir, &buf) ) continue;
+    if( !S_ISDIR(buf.st_mode) ) continue;
+    if( access(zDir, 07) ) continue;
+    /* Got one */
+       jx9_result_string(pCtx, zDir, -1);
+       return;
+  }
+  /* Default temp dir */
+  jx9_result_string(pCtx, "/tmp", (int)sizeof("/tmp")-1);
+}
+/* unsigned int (*xProcessId)(void) */
+static unsigned int UnixVfs_ProcessId(void)
+{
+       return (unsigned int)getpid();
+}
+/* int (*xUid)(void) */
+static int UnixVfs_uid(void)
+{
+       return (int)getuid();
+}
+/* int (*xGid)(void) */
+static int UnixVfs_gid(void)
+{
+       return (int)getgid();
+}
+/* int (*xUmask)(int) */
+static int UnixVfs_Umask(int new_mask)
+{
+       int old_mask;
+       old_mask = umask(new_mask);
+       return old_mask;
+}
+/* void (*xUsername)(jx9_context *) */
+static void UnixVfs_Username(jx9_context *pCtx)
+{
+#ifndef JX9_UNIX_STATIC_BUILD
+  struct passwd *pwd;
+  uid_t uid;
+  uid = getuid();
+  pwd = getpwuid(uid);   /* Try getting UID for username */
+  if (pwd == 0) {
+    return;
+  }
+  /* Return the username */
+  jx9_result_string(pCtx, pwd->pw_name, -1);
+#else
+  jx9_result_string(pCtx, "Unknown", -1);
+#endif /* JX9_UNIX_STATIC_BUILD */
+  return;
+}
+/* int (*xLink)(const char *, const char *, int) */
+static int UnixVfs_link(const char *zSrc, const char *zTarget, int is_sym)
+{
+       int rc;
+       if( is_sym ){
+               /* Symbolic link */
+               rc = symlink(zSrc, zTarget);
+       }else{
+               /* Hard link */
+               rc = link(zSrc, zTarget);
+       }
+       return rc == 0 ? JX9_OK : -1;
+}
+/* int (*xChroot)(const char *) */
+static int UnixVfs_chroot(const char *zRootDir)
+{
+       int rc;
+       rc = chroot(zRootDir);
+       return rc == 0 ? JX9_OK : -1;
+}
+/* Export the UNIX vfs */
+static const jx9_vfs sUnixVfs = {
+       "Unix_vfs", 
+       JX9_VFS_VERSION, 
+       UnixVfs_chdir,    /* int (*xChdir)(const char *) */
+       UnixVfs_chroot,   /* int (*xChroot)(const char *); */
+       UnixVfs_getcwd,   /* int (*xGetcwd)(jx9_context *) */
+       UnixVfs_mkdir,    /* int (*xMkdir)(const char *, int, int) */
+       UnixVfs_rmdir,    /* int (*xRmdir)(const char *) */ 
+       UnixVfs_isdir,    /* int (*xIsdir)(const char *) */
+       UnixVfs_Rename,   /* int (*xRename)(const char *, const char *) */
+       UnixVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
+       UnixVfs_Sleep,    /* int (*xSleep)(unsigned int) */
+       UnixVfs_unlink,   /* int (*xUnlink)(const char *) */
+       UnixVfs_FileExists, /* int (*xFileExists)(const char *) */
+       UnixVfs_Chmod, /*int (*xChmod)(const char *, int)*/
+       UnixVfs_Chown, /*int (*xChown)(const char *, const char *)*/
+       UnixVfs_Chgrp, /*int (*xChgrp)(const char *, const char *)*/
+       0,             /* jx9_int64 (*xFreeSpace)(const char *) */
+       0,             /* jx9_int64 (*xTotalSpace)(const char *) */
+       UnixVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
+       UnixVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
+       UnixVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
+       UnixVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
+       UnixVfs_Stat,  /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
+       UnixVfs_lStat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
+       UnixVfs_isfile,     /* int (*xIsfile)(const char *) */
+       UnixVfs_islink,     /* int (*xIslink)(const char *) */
+       UnixVfs_isreadable, /* int (*xReadable)(const char *) */
+       UnixVfs_iswritable, /* int (*xWritable)(const char *) */
+       UnixVfs_isexecutable, /* int (*xExecutable)(const char *) */
+       UnixVfs_Filetype,   /* int (*xFiletype)(const char *, jx9_context *) */
+       UnixVfs_Getenv,     /* int (*xGetenv)(const char *, jx9_context *) */
+       UnixVfs_Setenv,     /* int (*xSetenv)(const char *, const char *) */ 
+       UnixVfs_Touch,      /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
+       UnixVfs_Mmap,       /* int (*xMmap)(const char *, void **, jx9_int64 *) */
+       UnixVfs_Unmap,      /* void (*xUnmap)(void *, jx9_int64);  */
+       UnixVfs_link,       /* int (*xLink)(const char *, const char *, int) */
+       UnixVfs_Umask,      /* int (*xUmask)(int) */
+       UnixVfs_TempDir,    /* void (*xTempDir)(jx9_context *) */
+       UnixVfs_ProcessId,  /* unsigned int (*xProcessId)(void) */
+       UnixVfs_uid, /* int (*xUid)(void) */
+       UnixVfs_gid, /* int (*xGid)(void) */
+       UnixVfs_Username,    /* void (*xUsername)(jx9_context *) */
+       0 /* int (*xExec)(const char *, jx9_context *) */
+};
+/* UNIX File IO */
+#define JX9_UNIX_OPEN_MODE     0640 /* Default open mode */
+/* int (*xOpen)(const char *, int, jx9_value *, void **) */
+static int UnixFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
+{
+       int iOpen = O_RDONLY;
+       int fd;
+       /* Set the desired flags according to the open mode */
+       if( iOpenMode & JX9_IO_OPEN_CREATE ){
+               /* Open existing file, or create if it doesn't exist */
+               iOpen = O_CREAT;
+               if( iOpenMode & JX9_IO_OPEN_TRUNC ){
+                       /* If the specified file exists and is writable, the function overwrites the file */
+                       iOpen |= O_TRUNC;
+                       SXUNUSED(pResource); /* cc warning */
+               }
+       }else if( iOpenMode & JX9_IO_OPEN_EXCL ){
+               /* Creates a new file, only if it does not already exist.
+               * If the file exists, it fails.
+               */
+               iOpen = O_CREAT|O_EXCL;
+       }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
+               /* Opens a file and truncates it so that its size is zero bytes
+                * The file must exist.
+                */
+               iOpen = O_RDWR|O_TRUNC;
+       }
+       if( iOpenMode & JX9_IO_OPEN_RDWR ){
+               /* Read+Write access */
+               iOpen &= ~O_RDONLY;
+               iOpen |= O_RDWR;
+       }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
+               /* Write only access */
+               iOpen &= ~O_RDONLY;
+               iOpen |= O_WRONLY;
+       }
+       if( iOpenMode & JX9_IO_OPEN_APPEND ){
+               /* Append mode */
+               iOpen |= O_APPEND;
+       }
+#ifdef O_TEMP
+       if( iOpenMode & JX9_IO_OPEN_TEMP ){
+               /* File is temporary */
+               iOpen |= O_TEMP;
+       }
+#endif
+       /* Open the file now */
+       fd = open(zPath, iOpen, JX9_UNIX_OPEN_MODE);
+       if( fd < 0 ){
+               /* IO error */
+               return -1;
+       }
+       /* Save the handle */
+       *ppHandle = SX_INT_TO_PTR(fd);
+       return JX9_OK;
+}
+/* int (*xOpenDir)(const char *, jx9_value *, void **) */
+static int UnixDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
+{
+       DIR *pDir;
+       /* Open the target directory */
+       pDir = opendir(zPath);
+       if( pDir == 0 ){
+               pResource = 0; /* Compiler warning */
+               return -1;
+       }
+       /* Save our structure */
+       *ppHandle = pDir;
+       return JX9_OK;
+}
+/* void (*xCloseDir)(void *) */
+static void UnixDir_Close(void *pUserData)
+{
+       closedir((DIR *)pUserData);
+}
+/* void (*xClose)(void *); */
+static void UnixFile_Close(void *pUserData)
+{
+       close(SX_PTR_TO_INT(pUserData));
+}
+/* int (*xReadDir)(void *, jx9_context *) */
+static int UnixDir_Read(void *pUserData, jx9_context *pCtx)
+{
+       DIR *pDir = (DIR *)pUserData;
+       struct dirent *pEntry;
+       char *zName = 0; /* cc warning */
+       sxu32 n = 0;
+       for(;;){
+               pEntry = readdir(pDir);
+               if( pEntry == 0 ){
+                       /* No more entries to process */
+                       return -1;
+               }
+               zName = pEntry->d_name; 
+               n = SyStrlen(zName);
+               /* Ignore '.' && '..' */
+               if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
+                       break;
+               }
+               /* Next entry */
+       }
+       /* Return the current file name */
+       jx9_result_string(pCtx, zName, (int)n);
+       return JX9_OK;
+}
+/* void (*xRewindDir)(void *) */
+static void UnixDir_Rewind(void *pUserData)
+{
+       rewinddir((DIR *)pUserData);
+}
+/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
+static jx9_int64 UnixFile_Read(void *pUserData, void *pBuffer, jx9_int64 nDatatoRead)
+{
+       ssize_t nRd;
+       nRd = read(SX_PTR_TO_INT(pUserData), pBuffer, (size_t)nDatatoRead);
+       if( nRd < 1 ){
+               /* EOF or IO error */
+               return -1;
+       }
+       return (jx9_int64)nRd;
+}
+/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
+static jx9_int64 UnixFile_Write(void *pUserData, const void *pBuffer, jx9_int64 nWrite)
+{
+       const char *zData = (const char *)pBuffer;
+       int fd = SX_PTR_TO_INT(pUserData);
+       jx9_int64 nCount;
+       ssize_t nWr;
+       nCount = 0;
+       for(;;){
+               if( nWrite < 1 ){
+                       break;
+               }
+               nWr = write(fd, zData, (size_t)nWrite);
+               if( nWr < 1 ){
+                       /* IO error */
+                       break;
+               }
+               nWrite -= nWr;
+               nCount += nWr;
+               zData += nWr;
+       }
+       if( nWrite > 0 ){
+               return -1;
+       }
+       return nCount;
+}
+/* int (*xSeek)(void *, jx9_int64, int) */
+static int UnixFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
+{
+       off_t iNew;
+       switch(whence){
+       case 1:/*SEEK_CUR*/
+               whence = SEEK_CUR;
+               break;
+       case 2: /* SEEK_END */
+               whence = SEEK_END;
+               break;
+       case 0: /* SEEK_SET */
+       default:
+               whence = SEEK_SET;
+               break;
+       }
+       iNew = lseek(SX_PTR_TO_INT(pUserData), (off_t)iOfft, whence);
+       if( iNew < 0 ){
+               return -1;
+       }
+       return JX9_OK;
+}
+/* int (*xLock)(void *, int) */
+static int UnixFile_Lock(void *pUserData, int lock_type)
+{
+       int fd = SX_PTR_TO_INT(pUserData);
+       int rc = JX9_OK; /* cc warning */
+       if( lock_type < 0 ){
+               /* Unlock the file */
+               rc = flock(fd, LOCK_UN);
+       }else{
+               if( lock_type == 1 ){
+                       /* Exculsive lock */
+                       rc = flock(fd, LOCK_EX);
+               }else{
+                       /* Shared lock */
+                       rc = flock(fd, LOCK_SH);
+               }
+       }
+       return !rc ? JX9_OK : -1;
+}
+/* jx9_int64 (*xTell)(void *) */
+static jx9_int64 UnixFile_Tell(void *pUserData)
+{
+       off_t iNew;
+       iNew = lseek(SX_PTR_TO_INT(pUserData), 0, SEEK_CUR);
+       return (jx9_int64)iNew;
+}
+/* int (*xTrunc)(void *, jx9_int64) */
+static int UnixFile_Trunc(void *pUserData, jx9_int64 nOfft)
+{
+       int rc;
+       rc = ftruncate(SX_PTR_TO_INT(pUserData), (off_t)nOfft);
+       if( rc != 0 ){
+               return -1;
+       }
+       return JX9_OK;
+}
+/* int (*xSync)(void *); */
+static int UnixFile_Sync(void *pUserData)
+{
+       int rc; 
+       rc = fsync(SX_PTR_TO_INT(pUserData));
+       return rc == 0 ? JX9_OK : - 1;
+}
+/* int (*xStat)(void *, jx9_value *, jx9_value *) */
+static int UnixFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
+{
+       struct stat st;
+       int rc;
+       rc = fstat(SX_PTR_TO_INT(pUserData), &st);
+       if( rc != 0 ){ 
+        return -1;
+       }
+       /* dev */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
+       jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
+       /* ino */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
+       jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
+       /* mode */
+       jx9_value_int(pWorker, (int)st.st_mode);
+       jx9_array_add_strkey_elem(pArray, "mode", pWorker);
+       /* nlink */
+       jx9_value_int(pWorker, (int)st.st_nlink);
+       jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
+       /* uid, gid, rdev */
+       jx9_value_int(pWorker, (int)st.st_uid);
+       jx9_array_add_strkey_elem(pArray, "uid", pWorker);
+       jx9_value_int(pWorker, (int)st.st_gid);
+       jx9_array_add_strkey_elem(pArray, "gid", pWorker);
+       jx9_value_int(pWorker, (int)st.st_rdev);
+       jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
+       /* size */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_size);
+       jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
+       /* atime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
+       jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
+       /* mtime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
+       jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
+       /* ctime */
+       jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
+       jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
+       /* blksize, blocks */
+       jx9_value_int(pWorker, (int)st.st_blksize);             
+       jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
+       jx9_value_int(pWorker, (int)st.st_blocks);
+       jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
+       return JX9_OK;
+}
+/* Export the file:// stream */
+static const jx9_io_stream sUnixFileStream = {
+       "file", /* Stream name */
+       JX9_IO_STREAM_VERSION, 
+       UnixFile_Open,  /* xOpen */
+       UnixDir_Open,   /* xOpenDir */
+       UnixFile_Close, /* xClose */
+       UnixDir_Close,  /* xCloseDir */
+       UnixFile_Read,  /* xRead */
+       UnixDir_Read,   /* xReadDir */
+       UnixFile_Write, /* xWrite */
+       UnixFile_Seek,  /* xSeek */
+       UnixFile_Lock,  /* xLock */
+       UnixDir_Rewind, /* xRewindDir */
+       UnixFile_Tell,  /* xTell */
+       UnixFile_Trunc, /* xTrunc */
+       UnixFile_Sync,  /* xSeek */
+       UnixFile_Stat   /* xStat */
+};
+#endif /* __WINNT__/__UNIXES__ */
+#endif /* JX9_DISABLE_DISK_IO */
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+/* 
+ * Export the builtin vfs.
+ * Return a pointer to the builtin vfs if available.
+ * Otherwise return the null_vfs [i.e: a no-op vfs] instead.
+ * Note:
+ *  The built-in vfs is always available for Windows/UNIX systems.
+ * Note:
+ *  If the engine is compiled with the JX9_DISABLE_DISK_IO/JX9_DISABLE_BUILTIN_FUNC
+ *  directives defined then this function return the null_vfs instead.
+ */
+JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void)
+{
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifdef JX9_DISABLE_DISK_IO
+       return &null_vfs;
+#else
+#ifdef __WINNT__
+       return &sWinVfs;
+#elif defined(__UNIXES__)
+       return &sUnixVfs;
+#else
+       return &null_vfs;
+#endif /* __WINNT__/__UNIXES__ */
+#endif /*JX9_DISABLE_DISK_IO*/
+#else
+       return &null_vfs;
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+}
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifndef JX9_DISABLE_DISK_IO
+/*
+ * The following defines are mostly used by the UNIX built and have
+ * no particular meaning on windows.
+ */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO   0
+#endif
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO  1
+#endif
+#ifndef STDERR_FILENO
+#define STDERR_FILENO  2
+#endif
+/*
+ * jx9:// Accessing various I/O streams
+ * According to the JX9 langage reference manual
+ * JX9 provides a number of miscellaneous I/O streams that allow access to JX9's own input
+ * and output streams, the standard input, output and error file descriptors.
+ * jx9://stdin, jx9://stdout and jx9://stderr:
+ *  Allow direct access to the corresponding input or output stream of the JX9 process.
+ *  The stream references a duplicate file descriptor, so if you open jx9://stdin and later
+ *  close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected.
+ *  jx9://stdin is read-only, whereas jx9://stdout and jx9://stderr are write-only.
+ * jx9://output
+ *  jx9://output is a write-only stream that allows you to write to the output buffer 
+ *  mechanism in the same way as print and print. 
+ */
+typedef struct jx9_stream_data jx9_stream_data;
+/* Supported IO streams */
+#define JX9_IO_STREAM_STDIN  1 /* jx9://stdin */
+#define JX9_IO_STREAM_STDOUT 2 /* jx9://stdout */
+#define JX9_IO_STREAM_STDERR 3 /* jx9://stderr */
+#define JX9_IO_STREAM_OUTPUT 4 /* jx9://output */
+ /* The following structure is the private data associated with the jx9:// stream */
+struct jx9_stream_data
+{
+       jx9_vm *pVm; /* VM that own this instance */
+       int iType;   /* Stream type */
+       union{
+               void *pHandle; /* Stream handle */
+               jx9_output_consumer sConsumer; /* VM output consumer */
+       }x;
+};
+/*
+ * Allocate a new instance of the jx9_stream_data structure.
+ */
+static jx9_stream_data * JX9StreamDataInit(jx9_vm *pVm, int iType)
+{
+       jx9_stream_data *pData;
+       if( pVm == 0 ){
+               return 0;
+       }
+       /* Allocate a new instance */
+       pData = (jx9_stream_data *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(jx9_stream_data));
+       if( pData == 0 ){
+               return 0;
+       }
+       /* Zero the structure */
+       SyZero(pData, sizeof(jx9_stream_data));
+       /* Initialize fields */
+       pData->iType = iType;
+       if( iType == JX9_IO_STREAM_OUTPUT ){
+               /* Point to the default VM consumer routine. */
+               pData->x.sConsumer = pVm->sVmConsumer; 
+       }else{
+#ifdef __WINNT__
+               DWORD nChannel;
+               switch(iType){
+               case JX9_IO_STREAM_STDOUT:      nChannel = STD_OUTPUT_HANDLE; break;
+               case JX9_IO_STREAM_STDERR:  nChannel = STD_ERROR_HANDLE; break;
+               default:
+                       nChannel = STD_INPUT_HANDLE; 
+                       break;
+               }
+               pData->x.pHandle = GetStdHandle(nChannel);
+#else
+               /* Assume an UNIX system */
+               int ifd = STDIN_FILENO;
+               switch(iType){
+               case JX9_IO_STREAM_STDOUT:  ifd = STDOUT_FILENO; break;
+               case JX9_IO_STREAM_STDERR:  ifd = STDERR_FILENO; break;
+               default:
+                       break;
+               }
+               pData->x.pHandle = SX_INT_TO_PTR(ifd);
+#endif
+       }
+       pData->pVm = pVm;
+       return pData;
+}
+/* 
+ * Implementation of the jx9:// IO streams routines
+ * Authors:
+ *  Symisc Systems, devel@symisc.net.
+ *  Copyright (C) Symisc Systems, http://jx9.symisc.net
+ * Status:
+ *   Stable.
+ */
+/* int (*xOpen)(const char *, int, jx9_value *, void **) */
+static int JX9StreamData_Open(const char *zName, int iMode, jx9_value *pResource, void ** ppHandle)
+{
+       jx9_stream_data *pData;
+       SyString sStream;
+       SyStringInitFromBuf(&sStream, zName, SyStrlen(zName));
+       /* Trim leading and trailing white spaces */
+       SyStringFullTrim(&sStream);
+       /* Stream to open */
+       if( SyStrnicmp(sStream.zString, "stdin", sizeof("stdin")-1) == 0 ){
+               iMode = JX9_IO_STREAM_STDIN;
+       }else if( SyStrnicmp(sStream.zString, "output", sizeof("output")-1) == 0 ){
+               iMode = JX9_IO_STREAM_OUTPUT;
+       }else if( SyStrnicmp(sStream.zString, "stdout", sizeof("stdout")-1) == 0 ){
+               iMode = JX9_IO_STREAM_STDOUT;
+       }else if( SyStrnicmp(sStream.zString, "stderr", sizeof("stderr")-1) == 0 ){
+               iMode = JX9_IO_STREAM_STDERR;
+       }else{
+               /* unknown stream name */
+               return -1;
+       }
+       /* Create our handle */
+       pData = JX9StreamDataInit(pResource?pResource->pVm:0, iMode);
+       if( pData == 0 ){
+               return -1;
+       }
+       /* Make the handle public */
+       *ppHandle = (void *)pData;
+       return JX9_OK;
+}
+/* jx9_int64 (*xRead)(void *, void *, jx9_int64) */
+static jx9_int64 JX9StreamData_Read(void *pHandle, void *pBuffer, jx9_int64 nDatatoRead)
+{
+       jx9_stream_data *pData = (jx9_stream_data *)pHandle;
+       if( pData == 0 ){
+               return -1;
+       }
+       if( pData->iType != JX9_IO_STREAM_STDIN ){
+               /* Forbidden */
+               return -1;
+       }
+#ifdef __WINNT__
+       {
+               DWORD nRd;
+               BOOL rc;
+               rc = ReadFile(pData->x.pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
+               if( !rc ){
+                       /* IO error */
+                       return -1;
+               }
+               return (jx9_int64)nRd;
+       }
+#elif defined(__UNIXES__)
+       {
+               ssize_t nRd;
+               int fd;
+               fd = SX_PTR_TO_INT(pData->x.pHandle);
+               nRd = read(fd, pBuffer, (size_t)nDatatoRead);
+               if( nRd < 1 ){
+                       return -1;
+               }
+               return (jx9_int64)nRd;
+       }
+#else
+       return -1;
+#endif
+}
+/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64) */
+static jx9_int64 JX9StreamData_Write(void *pHandle, const void *pBuf, jx9_int64 nWrite)
+{
+       jx9_stream_data *pData = (jx9_stream_data *)pHandle;
+       if( pData == 0 ){
+               return -1;
+       }
+       if( pData->iType == JX9_IO_STREAM_STDIN ){
+               /* Forbidden */
+               return -1;
+       }else if( pData->iType == JX9_IO_STREAM_OUTPUT ){
+               jx9_output_consumer *pCons = &pData->x.sConsumer;
+               int rc;
+               /* Call the vm output consumer */
+               rc = pCons->xConsumer(pBuf, (unsigned int)nWrite, pCons->pUserData);
+               if( rc == JX9_ABORT ){
+                       return -1;
+               }
+               return nWrite;
+       }
+#ifdef __WINNT__
+       {
+               DWORD nWr;
+               BOOL rc;
+               rc = WriteFile(pData->x.pHandle, pBuf, (DWORD)nWrite, &nWr, 0);
+               if( !rc ){
+                       /* IO error */
+                       return -1;
+               }
+               return (jx9_int64)nWr;
+       }
+#elif defined(__UNIXES__)
+       {
+               ssize_t nWr;
+               int fd;
+               fd = SX_PTR_TO_INT(pData->x.pHandle);
+               nWr = write(fd, pBuf, (size_t)nWrite);
+               if( nWr < 1 ){
+                       return -1;
+               }
+               return (jx9_int64)nWr;
+       }
+#else
+       return -1;
+#endif
+}
+/* void (*xClose)(void *) */
+static void JX9StreamData_Close(void *pHandle)
+{
+       jx9_stream_data *pData = (jx9_stream_data *)pHandle;
+       jx9_vm *pVm;
+       if( pData == 0 ){
+               return;
+       }
+       pVm = pData->pVm;
+       /* Free the instance */
+       SyMemBackendFree(&pVm->sAllocator, pData);
+}
+/* Export the jx9:// stream */
+static const jx9_io_stream sjx9Stream = {
+       "jx9", 
+       JX9_IO_STREAM_VERSION, 
+       JX9StreamData_Open,  /* xOpen */
+       0,   /* xOpenDir */
+       JX9StreamData_Close, /* xClose */
+       0,  /* xCloseDir */
+       JX9StreamData_Read,  /* xRead */
+       0,  /* xReadDir */
+       JX9StreamData_Write, /* xWrite */
+       0,  /* xSeek */
+       0,  /* xLock */
+       0,  /* xRewindDir */
+       0,  /* xTell */
+       0,  /* xTrunc */
+       0,  /* xSeek */
+       0   /* xStat */
+};
+#endif /* JX9_DISABLE_DISK_IO */
+/*
+ * Return TRUE if we are dealing with the jx9:// stream.
+ * FALSE otherwise.
+ */
+static int is_jx9_stream(const jx9_io_stream *pStream)
+{
+#ifndef JX9_DISABLE_DISK_IO
+       return pStream == &sjx9Stream;
+#else
+       SXUNUSED(pStream); /* cc warning */
+       return 0;
+#endif /* JX9_DISABLE_DISK_IO */
+}
+
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+/*
+ * Export the IO routines defined above and the built-in IO streams
+ * [i.e: file://, jx9://].
+ * Note:
+ *  If the engine is compiled with the JX9_DISABLE_BUILTIN_FUNC directive
+ *  defined then this function is a no-op.
+ */
+JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm)
+{
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+             /* VFS functions */
+       static const jx9_builtin_func aVfsFunc[] = {
+               {"chdir",   jx9Vfs_chdir   }, 
+               {"chroot",  jx9Vfs_chroot  }, 
+               {"getcwd",  jx9Vfs_getcwd  }, 
+               {"rmdir",   jx9Vfs_rmdir   }, 
+               {"is_dir",  jx9Vfs_is_dir  }, 
+               {"mkdir",   jx9Vfs_mkdir   }, 
+               {"rename",  jx9Vfs_rename  }, 
+               {"realpath", jx9Vfs_realpath}, 
+               {"sleep",   jx9Vfs_sleep   }, 
+               {"usleep",  jx9Vfs_usleep  }, 
+               {"unlink",  jx9Vfs_unlink  }, 
+               {"delete",  jx9Vfs_unlink  }, 
+               {"chmod",   jx9Vfs_chmod   }, 
+               {"chown",   jx9Vfs_chown   }, 
+               {"chgrp",   jx9Vfs_chgrp   }, 
+               {"disk_free_space", jx9Vfs_disk_free_space  }, 
+               {"disk_total_space", jx9Vfs_disk_total_space}, 
+               {"file_exists", jx9Vfs_file_exists }, 
+               {"filesize",    jx9Vfs_file_size   }, 
+               {"fileatime",   jx9Vfs_file_atime  }, 
+               {"filemtime",   jx9Vfs_file_mtime  }, 
+               {"filectime",   jx9Vfs_file_ctime  }, 
+               {"is_file",     jx9Vfs_is_file  }, 
+               {"is_link",     jx9Vfs_is_link  }, 
+               {"is_readable", jx9Vfs_is_readable   }, 
+               {"is_writable", jx9Vfs_is_writable   }, 
+               {"is_executable", jx9Vfs_is_executable}, 
+               {"filetype",    jx9Vfs_filetype }, 
+               {"stat",        jx9Vfs_stat     }, 
+               {"lstat",       jx9Vfs_lstat    }, 
+               {"getenv",      jx9Vfs_getenv   }, 
+               {"setenv",      jx9Vfs_putenv   }, 
+               {"putenv",      jx9Vfs_putenv   }, 
+               {"touch",       jx9Vfs_touch    }, 
+               {"link",        jx9Vfs_link     }, 
+               {"symlink",     jx9Vfs_symlink  }, 
+               {"umask",       jx9Vfs_umask    }, 
+               {"sys_get_temp_dir", jx9Vfs_sys_get_temp_dir }, 
+               {"get_current_user", jx9Vfs_get_current_user }, 
+               {"getpid",      jx9Vfs_getmypid }, 
+               {"getuid",      jx9Vfs_getmyuid }, 
+               {"getgid",      jx9Vfs_getmygid }, 
+               {"uname",       jx9Vfs_uname}, 
+                    /* Path processing */ 
+               {"dirname",     jx9Builtin_dirname  }, 
+               {"basename",    jx9Builtin_basename }, 
+               {"pathinfo",    jx9Builtin_pathinfo }, 
+               {"strglob",     jx9Builtin_strglob  }, 
+               {"fnmatch",     jx9Builtin_fnmatch  }, 
+                    /* ZIP processing */
+               {"zip_open",    jx9Builtin_zip_open }, 
+               {"zip_close",   jx9Builtin_zip_close}, 
+               {"zip_read",    jx9Builtin_zip_read }, 
+               {"zip_entry_open", jx9Builtin_zip_entry_open }, 
+               {"zip_entry_close", jx9Builtin_zip_entry_close}, 
+               {"zip_entry_name", jx9Builtin_zip_entry_name }, 
+               {"zip_entry_filesize",      jx9Builtin_zip_entry_filesize       }, 
+               {"zip_entry_compressedsize", jx9Builtin_zip_entry_compressedsize }, 
+               {"zip_entry_read", jx9Builtin_zip_entry_read }, 
+               {"zip_entry_reset_cursor", jx9Builtin_zip_entry_reset_cursor}, 
+               {"zip_entry_compressionmethod", jx9Builtin_zip_entry_compressionmethod}
+       };
+           /* IO stream functions */
+       static const jx9_builtin_func aIOFunc[] = {
+               {"ftruncate", jx9Builtin_ftruncate }, 
+               {"fseek",     jx9Builtin_fseek  }, 
+               {"ftell",     jx9Builtin_ftell  }, 
+               {"rewind",    jx9Builtin_rewind }, 
+               {"fflush",    jx9Builtin_fflush }, 
+               {"feof",      jx9Builtin_feof   }, 
+               {"fgetc",     jx9Builtin_fgetc  }, 
+               {"fgets",     jx9Builtin_fgets  }, 
+               {"fread",     jx9Builtin_fread  }, 
+               {"fgetcsv",   jx9Builtin_fgetcsv}, 
+               {"fgetss",    jx9Builtin_fgetss }, 
+               {"readdir",   jx9Builtin_readdir}, 
+               {"rewinddir", jx9Builtin_rewinddir }, 
+               {"closedir",  jx9Builtin_closedir}, 
+               {"opendir",   jx9Builtin_opendir }, 
+               {"readfile",  jx9Builtin_readfile}, 
+               {"file_get_contents", jx9Builtin_file_get_contents}, 
+               {"file_put_contents", jx9Builtin_file_put_contents}, 
+               {"file",      jx9Builtin_file   }, 
+               {"copy",      jx9Builtin_copy   }, 
+               {"fstat",     jx9Builtin_fstat  }, 
+               {"fwrite",    jx9Builtin_fwrite }, 
+               {"fputs",     jx9Builtin_fwrite }, 
+               {"flock",     jx9Builtin_flock  }, 
+               {"fclose",    jx9Builtin_fclose }, 
+               {"fopen",     jx9Builtin_fopen  }, 
+               {"fpassthru", jx9Builtin_fpassthru }, 
+               {"fputcsv",   jx9Builtin_fputcsv }, 
+               {"fprintf",   jx9Builtin_fprintf }, 
+#if !defined(JX9_DISABLE_HASH_FUNC)
+               {"md5_file",  jx9Builtin_md5_file}, 
+               {"sha1_file", jx9Builtin_sha1_file}, 
+#endif /* JX9_DISABLE_HASH_FUNC */
+               {"parse_ini_file", jx9Builtin_parse_ini_file}, 
+               {"vfprintf",  jx9Builtin_vfprintf}
+       };
+       const jx9_io_stream *pFileStream = 0;
+       sxu32 n = 0;
+       /* Register the functions defined above */
+       for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){
+               jx9_create_function(&(*pVm), aVfsFunc[n].zName, aVfsFunc[n].xFunc, (void *)pVm->pEngine->pVfs);
+       }
+       for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){
+               jx9_create_function(&(*pVm), aIOFunc[n].zName, aIOFunc[n].xFunc, pVm);
+       }
+#ifndef JX9_DISABLE_DISK_IO
+       /* Register the file stream if available */
+#ifdef __WINNT__
+       pFileStream = &sWinFileStream;
+#elif defined(__UNIXES__)
+       pFileStream = &sUnixFileStream;
+#endif
+       /* Install the jx9:// stream */
+       jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, &sjx9Stream);
+#endif /* JX9_DISABLE_DISK_IO */
+       if( pFileStream ){
+               /* Install the file:// stream */
+               jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, pFileStream);
+       }
+#else
+   SXUNUSED(pVm); /* cc warning */
+#endif /* JX9_DISABLE_BUILTIN_FUNC */
+       return SXRET_OK;
+}
+/*
+ * Export the STDIN handle.
+ */
+JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm)
+{
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifndef JX9_DISABLE_DISK_IO
+       if( pVm->pStdin == 0  ){
+               io_private *pIn;
+               /* Allocate an IO private instance */
+               pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
+               if( pIn == 0 ){
+                       return 0;
+               }
+               InitIOPrivate(pVm, &sjx9Stream, pIn);
+               /* Initialize the handle */
+               pIn->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDIN);
+               /* Install the STDIN stream */
+               pVm->pStdin = pIn;
+               return pIn;
+       }else{
+               /* NULL or STDIN */
+               return pVm->pStdin;
+       }
+#else
+       return 0;
+#endif
+#else
+       SXUNUSED(pVm); /* cc warning */
+       return 0;
+#endif
+}
+/*
+ * Export the STDOUT handle.
+ */
+JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm)
+{
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifndef JX9_DISABLE_DISK_IO
+       if( pVm->pStdout == 0  ){
+               io_private *pOut;
+               /* Allocate an IO private instance */
+               pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
+               if( pOut == 0 ){
+                       return 0;
+               }
+               InitIOPrivate(pVm, &sjx9Stream, pOut);
+               /* Initialize the handle */
+               pOut->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDOUT);
+               /* Install the STDOUT stream */
+               pVm->pStdout = pOut;
+               return pOut;
+       }else{
+               /* NULL or STDOUT */
+               return pVm->pStdout;
+       }
+#else
+       return 0;
+#endif
+#else
+       SXUNUSED(pVm); /* cc warning */
+       return 0;
+#endif
+}
+/*
+ * Export the STDERR handle.
+ */
+JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm)
+{
+#ifndef JX9_DISABLE_BUILTIN_FUNC
+#ifndef JX9_DISABLE_DISK_IO
+       if( pVm->pStderr == 0  ){
+               io_private *pErr;
+               /* Allocate an IO private instance */
+               pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
+               if( pErr == 0 ){
+                       return 0;
+               }
+               InitIOPrivate(pVm, &sjx9Stream, pErr);
+               /* Initialize the handle */
+               pErr->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDERR);
+               /* Install the STDERR stream */
+               pVm->pStderr = pErr;
+               return pErr;
+       }else{
+               /* NULL or STDERR */
+               return pVm->pStderr;
+       }
+#else
+       return 0;
+#endif
+#else
+       SXUNUSED(pVm); /* cc warning */
+       return 0;
+#endif
+}
+
+/*
+ * ----------------------------------------------------------
+ * File: jx9_vm.c
+ * MD5: beca4be65a9a49c932c356d7680034c9
+ * ----------------------------------------------------------
+ */
+/*
+ * 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: jx9_vm.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: lhash_kv.c
+ * MD5: 581b07ce2984fd95740677285d8a11d3
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: lhash_kv.c v1.7 Solaris 2013-01-14 12:56 stable <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* 
+ * This file implements disk based hashtable using the linear hashing algorithm.
+ * This implementation is the one decribed in the paper:
+ *  LINEAR HASHING : A NEW TOOL FOR FILE AND TABLE ADDRESSING. Witold Litwin. I. N. Ft. I. A.. 78 150 Le Chesnay, France.
+ * Plus a smart extension called Virtual Bucket Table. (contact devel@symisc.net for additional information).
+ */
+/* Magic number identifying a valid storage image */
+#define L_HASH_MAGIC 0xFA782DCB
+/*
+ * Magic word to hash to identify a valid hash function.
+ */
+#define L_HASH_WORD "chm@symisc"
+/*
+ * Cell size on disk. 
+ */
+#define L_HASH_CELL_SZ (4/*Hash*/+4/*Key*/+8/*Data*/+2/* Offset of the next cell */+8/*Overflow*/)
+/*
+ * Primary page (not overflow pages) header size on disk.
+ */
+#define L_HASH_PAGE_HDR_SZ (2/* Cell offset*/+2/* Free block offset*/+8/*Slave page number*/)
+/*
+ * The maximum amount of payload (in bytes) that can be stored locally for
+ * a database entry.  If the entry contains more data than this, the
+ * extra goes onto overflow pages.
+*/
+#define L_HASH_MX_PAYLOAD(PageSize)  (PageSize-(L_HASH_PAGE_HDR_SZ+L_HASH_CELL_SZ))
+/*
+ * Maxium free space on a single page.
+ */
+#define L_HASH_MX_FREE_SPACE(PageSize) (PageSize - (L_HASH_PAGE_HDR_SZ))
+/*
+** The maximum number of bytes of payload allowed on a single overflow page.
+*/
+#define L_HASH_OVERFLOW_SIZE(PageSize) (PageSize-8)
+/* Forward declaration */
+typedef struct lhash_kv_engine lhash_kv_engine;
+typedef struct lhpage lhpage;
+/*
+ * Each record in the database is identified either in-memory or in
+ * disk by an instance of the following structure.
+ */
+typedef struct lhcell lhcell;
+struct lhcell
+{
+       /* Disk-data (Big-Endian) */
+       sxu32 nHash;   /* Hash of the key: 4 bytes */
+       sxu32 nKey;    /* Key length: 4 bytes */
+       sxu64 nData;   /* Data length: 8 bytes */
+       sxu16 iNext;   /* Offset of the next cell: 2 bytes */
+       pgno iOvfl;    /* Overflow page number if any: 8 bytes */
+       /* In-memory data only */
+       lhpage *pPage;     /* Page this cell belongs */
+       sxu16 iStart;      /* Offset of this cell */
+       pgno iDataPage;    /* Data page number when overflow */
+       sxu16 iDataOfft;   /* Offset of the data in iDataPage */
+       SyBlob sKey;       /* Record key for fast lookup (Kept in-memory if < 256KB ) */
+       lhcell *pNext,*pPrev;         /* Linked list of the loaded memory cells */
+       lhcell *pNextCol,*pPrevCol;   /* Collison chain  */
+};
+/*
+** Each database page has a header that is an instance of this
+** structure.
+*/
+typedef struct lhphdr lhphdr;
+struct lhphdr 
+{
+  sxu16 iOfft; /* Offset of the first cell */
+  sxu16 iFree; /* Offset of the first free block*/
+  pgno iSlave; /* Slave page number */
+};
+/*
+ * Each loaded primary disk page is represented in-memory using
+ * an instance of the following structure.
+ */
+struct lhpage
+{
+       lhash_kv_engine *pHash;  /* KV Storage engine that own this page */
+       unqlite_page *pRaw;      /* Raw page contents */
+       lhphdr sHdr;             /* Processed page header */
+       lhcell **apCell;         /* Cell buckets */
+       lhcell *pList,*pFirst;   /* Linked list of cells */
+       sxu32 nCell;             /* Total number of cells */
+       sxu32 nCellSize;         /* apCell[] size */
+       lhpage *pMaster;         /* Master page in case we are dealing with a slave page */
+       lhpage *pSlave;          /* List of slave pages */
+       lhpage *pNextSlave;      /* Next slave page on the list */
+       sxi32 iSlave;            /* Total number of slave pages */
+       sxu16 nFree;             /* Amount of free space available in the page */
+};
+/*
+ * A Bucket map record which is used to map logical bucket number to real
+ * bucket number is represented by an instance of the following structure.
+ */
+typedef struct lhash_bmap_rec lhash_bmap_rec;
+struct lhash_bmap_rec
+{
+       pgno iLogic;                   /* Logical bucket number */
+       pgno iReal;                    /* Real bucket number */
+       lhash_bmap_rec *pNext,*pPrev;  /* Link to other bucket map */     
+       lhash_bmap_rec *pNextCol,*pPrevCol; /* Collision links */
+};
+typedef struct lhash_bmap_page lhash_bmap_page;
+struct lhash_bmap_page
+{
+       pgno iNum;   /* Page number where this entry is stored */
+       sxu16 iPtr;  /* Offset to start reading/writing from */
+       sxu32 nRec;  /* Total number of records in this page */
+       pgno iNext;  /* Next map page */
+};
+/*
+ * An in memory linear hash implemenation is represented by in an isntance
+ * of the following structure.
+ */
+struct lhash_kv_engine
+{
+       const unqlite_kv_io *pIo;     /* IO methods: Must be first */
+       /* Private fields */
+       SyMemBackend sAllocator;      /* Private memory backend */
+       ProcHash xHash;               /* Default hash function */
+       ProcCmp xCmp;                 /* Default comparison function */
+       unqlite_page *pHeader;        /* Page one to identify a valid implementation */
+       lhash_bmap_rec **apMap;       /* Buckets map records */
+       sxu32 nBuckRec;               /* Total number of bucket map records */
+       sxu32 nBuckSize;              /* apMap[] size  */
+       lhash_bmap_rec *pList;        /* List of bucket map records */
+       lhash_bmap_rec *pFirst;       /* First record*/
+       lhash_bmap_page sPageMap;     /* Primary bucket map */
+       int iPageSize;                /* Page size */
+       pgno nFreeList;               /* List of free pages */
+       pgno split_bucket;            /* Current split bucket: MUST BE A POWER OF TWO */
+       pgno max_split_bucket;        /* Maximum split bucket: MUST BE A POWER OF TWO */
+       pgno nmax_split_nucket;       /* Next maximum split bucket (1 << nMsb): In-memory only */
+       sxu32 nMagic;                 /* Magic number to identify a valid linear hash disk database */
+};
+/*
+ * Given a logical bucket number, return the record associated with it.
+ */
+static lhash_bmap_rec * lhMapFindBucket(lhash_kv_engine *pEngine,pgno iLogic)
+{
+       lhash_bmap_rec *pRec;
+       if( pEngine->nBuckRec < 1 ){
+               /* Don't bother */
+               return 0;
+       }
+       pRec = pEngine->apMap[iLogic & (pEngine->nBuckSize - 1)];
+       for(;;){
+               if( pRec == 0 ){
+                       break;
+               }
+               if( pRec->iLogic == iLogic ){
+                       return pRec;
+               }
+               /* Point to the next entry */
+               pRec = pRec->pNextCol;
+       }
+       /* No such record */
+       return 0;
+}
+/*
+ * Install a new bucket map record.
+ */
+static int lhMapInstallBucket(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal)
+{
+       lhash_bmap_rec *pRec;
+       sxu32 iBucket;
+       /* Allocate a new instance */
+       pRec = (lhash_bmap_rec *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhash_bmap_rec));
+       if( pRec == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pRec,sizeof(lhash_bmap_rec));
+       /* Fill in the structure */
+       pRec->iLogic = iLogic;
+       pRec->iReal = iReal;
+       iBucket = iLogic & (pEngine->nBuckSize - 1);
+       pRec->pNextCol = pEngine->apMap[iBucket];
+       if( pEngine->apMap[iBucket] ){
+               pEngine->apMap[iBucket]->pPrevCol = pRec;
+       }
+       pEngine->apMap[iBucket] = pRec;
+       /* Link */
+       if( pEngine->pFirst == 0 ){
+               pEngine->pFirst = pEngine->pList = pRec;
+       }else{
+               MACRO_LD_PUSH(pEngine->pList,pRec);
+       }
+       pEngine->nBuckRec++;
+       if( (pEngine->nBuckRec >= pEngine->nBuckSize * 3) && pEngine->nBuckRec < 100000 ){
+               /* Allocate a new larger table */
+               sxu32 nNewSize = pEngine->nBuckSize << 1;
+               lhash_bmap_rec *pEntry;
+               lhash_bmap_rec **apNew;
+               sxu32 n;
+               
+               apNew = (lhash_bmap_rec **)SyMemBackendAlloc(&pEngine->sAllocator, nNewSize * sizeof(lhash_bmap_rec *));
+               if( apNew ){
+                       /* Zero the new table */
+                       SyZero((void *)apNew, nNewSize * sizeof(lhash_bmap_rec *));
+                       /* Rehash all entries */
+                       n = 0;
+                       pEntry = pEngine->pList;
+                       for(;;){
+                               /* Loop one */
+                               if( n >= pEngine->nBuckRec ){
+                                       break;
+                               }
+                               pEntry->pNextCol = pEntry->pPrevCol = 0;
+                               /* Install in the new bucket */
+                               iBucket = pEntry->iLogic & (nNewSize - 1);
+                               pEntry->pNextCol = apNew[iBucket];
+                               if( apNew[iBucket] ){
+                                       apNew[iBucket]->pPrevCol = pEntry;
+                               }
+                               apNew[iBucket] = pEntry;
+                               /* Point to the next entry */
+                               pEntry = pEntry->pNext;
+                               n++;
+                       }
+                       /* Release the old table and reflect the change */
+                       SyMemBackendFree(&pEngine->sAllocator,(void *)pEngine->apMap);
+                       pEngine->apMap = apNew;
+                       pEngine->nBuckSize  = nNewSize;
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Process a raw bucket map record.
+ */
+static int lhMapLoadPage(lhash_kv_engine *pEngine,lhash_bmap_page *pMap,const unsigned char *zRaw)
+{
+       const unsigned char *zEnd = &zRaw[pEngine->iPageSize];
+       const unsigned char *zPtr = zRaw;
+       pgno iLogic,iReal;
+       sxu32 n;
+       int rc;
+       if( pMap->iPtr == 0 ){
+               /* Read the map header */
+               SyBigEndianUnpack64(zRaw,&pMap->iNext);
+               zRaw += 8;
+               SyBigEndianUnpack32(zRaw,&pMap->nRec);
+               zRaw += 4;
+       }else{
+               /* Mostly page one of the database */
+               zRaw += pMap->iPtr;
+       }
+       /* Start processing */
+       for( n = 0; n < pMap->nRec ; ++n ){
+               if( zRaw >= zEnd ){
+                       break;
+               }
+               /* Extract the logical and real bucket number */
+               SyBigEndianUnpack64(zRaw,&iLogic);
+               zRaw += 8;
+               SyBigEndianUnpack64(zRaw,&iReal);
+               zRaw += 8;
+               /* Install the record in the map */
+               rc = lhMapInstallBucket(pEngine,iLogic,iReal);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+       }
+       pMap->iPtr = (sxu16)(zRaw-zPtr);
+       /* All done */
+       return UNQLITE_OK;
+}
+/* 
+ * Allocate a new cell instance.
+ */
+static lhcell * lhNewCell(lhash_kv_engine *pEngine,lhpage *pPage)
+{
+       lhcell *pCell;
+       pCell = (lhcell *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhcell));
+       if( pCell == 0 ){
+               return 0;
+       }
+       /* Zero the structure */
+       SyZero(pCell,sizeof(lhcell));
+       /* Fill in the structure */
+       SyBlobInit(&pCell->sKey,&pEngine->sAllocator);
+       pCell->pPage = pPage;
+       return pCell;
+}
+/*
+ * Discard a cell from the page table.
+ */
+static void lhCellDiscard(lhcell *pCell)
+{
+       lhpage *pPage = pCell->pPage->pMaster;  
+       
+       if( pCell->pPrevCol ){
+               pCell->pPrevCol->pNextCol = pCell->pNextCol;
+       }else{
+               pPage->apCell[pCell->nHash & (pPage->nCellSize - 1)] = pCell->pNextCol;
+       }
+       if( pCell->pNextCol ){
+               pCell->pNextCol->pPrevCol = pCell->pPrevCol;
+       }
+       MACRO_LD_REMOVE(pPage->pList,pCell);
+       if( pCell == pPage->pFirst ){
+               pPage->pFirst = pCell->pPrev;
+       }
+       pPage->nCell--;
+       /* Release the cell */
+       SyBlobRelease(&pCell->sKey);
+       SyMemBackendPoolFree(&pPage->pHash->sAllocator,pCell);
+}
+/*
+ * Install a cell in the page table.
+ */
+static int lhInstallCell(lhcell *pCell)
+{
+       lhpage *pPage = pCell->pPage->pMaster;
+       sxu32 iBucket;
+       if( pPage->nCell < 1 ){
+               sxu32 nTableSize = 32; /* Must be a power of two */
+               lhcell **apTable;
+               /* Allocate a new cell table */
+               apTable = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nTableSize * sizeof(lhcell *));
+               if( apTable == 0 ){
+                       return UNQLITE_NOMEM;
+               }
+               /* Zero the new table */
+               SyZero((void *)apTable, nTableSize * sizeof(lhcell *));
+               /* Install it */
+               pPage->apCell = apTable;
+               pPage->nCellSize = nTableSize;
+       }
+       iBucket = pCell->nHash & (pPage->nCellSize - 1);
+       pCell->pNextCol = pPage->apCell[iBucket];
+       if( pPage->apCell[iBucket] ){
+               pPage->apCell[iBucket]->pPrevCol = pCell;
+       }
+       pPage->apCell[iBucket] = pCell;
+       if( pPage->pFirst == 0 ){
+               pPage->pFirst = pPage->pList = pCell;
+       }else{
+               MACRO_LD_PUSH(pPage->pList,pCell);
+       }
+       pPage->nCell++;
+       if( (pPage->nCell >= pPage->nCellSize * 3) && pPage->nCell < 100000 ){
+               /* Allocate a new larger table */
+               sxu32 nNewSize = pPage->nCellSize << 1;
+               lhcell *pEntry;
+               lhcell **apNew;
+               sxu32 n;
+               
+               apNew = (lhcell **)SyMemBackendAlloc(&pPage->pHash->sAllocator, nNewSize * sizeof(lhcell *));
+               if( apNew ){
+                       /* Zero the new table */
+                       SyZero((void *)apNew, nNewSize * sizeof(lhcell *));
+                       /* Rehash all entries */
+                       n = 0;
+                       pEntry = pPage->pList;
+                       for(;;){
+                               /* Loop one */
+                               if( n >= pPage->nCell ){
+                                       break;
+                               }
+                               pEntry->pNextCol = pEntry->pPrevCol = 0;
+                               /* Install in the new bucket */
+                               iBucket = pEntry->nHash & (nNewSize - 1);
+                               pEntry->pNextCol = apNew[iBucket];
+                               if( apNew[iBucket]  ){
+                                       apNew[iBucket]->pPrevCol = pEntry;
+                               }
+                               apNew[iBucket] = pEntry;
+                               /* Point to the next entry */
+                               pEntry = pEntry->pNext;
+                               n++;
+                       }
+                       /* Release the old table and reflect the change */
+                       SyMemBackendFree(&pPage->pHash->sAllocator,(void *)pPage->apCell);
+                       pPage->apCell = apNew;
+                       pPage->nCellSize  = nNewSize;
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Private data of lhKeyCmp().
+ */
+struct lhash_key_cmp
+{
+       const char *zIn;  /* Start of the stream */
+       const char *zEnd; /* End of the stream */
+       ProcCmp xCmp;     /* Comparison function */
+};
+/*
+ * Comparsion callback for large key > 256 KB
+ */
+static int lhKeyCmp(const void *pData,sxu32 nLen,void *pUserData)
+{
+       struct lhash_key_cmp *pCmp = (struct lhash_key_cmp *)pUserData;
+       int rc;
+       if( pCmp->zIn >= pCmp->zEnd ){
+               if( nLen > 0 ){
+                       return UNQLITE_ABORT;
+               }
+               return UNQLITE_OK;
+       }
+       /* Perform the comparison */
+       rc = pCmp->xCmp((const void *)pCmp->zIn,pData,nLen);
+       if( rc != 0 ){
+               /* Abort comparison */
+               return UNQLITE_ABORT;
+       }
+       /* Advance the cursor */
+       pCmp->zIn += nLen;
+       return UNQLITE_OK;
+}
+/* Forward declaration */
+static int lhConsumeCellkey(lhcell *pCell,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData,int offt_only);
+/*
+ * given a key, return the cell associated with it on success. NULL otherwise.
+ */
+static lhcell * lhFindCell(
+       lhpage *pPage,    /* Target page */
+       const void *pKey, /* Lookup key */
+       sxu32 nByte,      /* Key length */
+       sxu32 nHash       /* Hash of the key */
+       )
+{
+       lhcell *pEntry;
+       if( pPage->nCell < 1 ){
+               /* Don't bother hashing */
+               return 0;
+       }
+       /* Point to the corresponding bucket */
+       pEntry = pPage->apCell[nHash & (pPage->nCellSize - 1)];
+       for(;;){
+               if( pEntry == 0 ){
+                       break;
+               }
+               if( pEntry->nHash == nHash && pEntry->nKey == nByte ){
+                       if( SyBlobLength(&pEntry->sKey) < 1 ){
+                               /* Large key (> 256 KB) are not kept in-memory */
+                               struct lhash_key_cmp sCmp;
+                               int rc;
+                               /* Fill-in the structure */
+                               sCmp.zIn = (const char *)pKey;
+                               sCmp.zEnd = &sCmp.zIn[nByte];
+                               sCmp.xCmp = pPage->pHash->xCmp;
+                               /* Fetch the key from disk and perform the comparison */
+                               rc = lhConsumeCellkey(pEntry,lhKeyCmp,&sCmp,0);
+                               if( rc == UNQLITE_OK ){
+                                       /* Cell found */
+                                       return pEntry;
+                               }
+                       }else if ( pPage->pHash->xCmp(pKey,SyBlobData(&pEntry->sKey),nByte) == 0 ){
+                               /* Cell found */
+                               return pEntry;
+                       }
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pNextCol;
+       }
+       /* No such entry */
+       return 0;
+}
+/*
+ * Parse a raw cell fetched from disk.
+ */
+static int lhParseOneCell(lhpage *pPage,const unsigned char *zRaw,const unsigned char *zEnd,lhcell **ppOut)
+{
+       sxu16 iNext,iOfft;
+       sxu32 iHash,nKey;
+       lhcell *pCell;
+       sxu64 nData;
+       int rc;
+       /* Offset this cell is stored */
+       iOfft = (sxu16)(zRaw - (const unsigned char *)pPage->pRaw->zData);
+       /* 4 byte hash number */
+       SyBigEndianUnpack32(zRaw,&iHash);
+       zRaw += 4;      
+       /* 4 byte key length  */
+       SyBigEndianUnpack32(zRaw,&nKey);
+       zRaw += 4;      
+       /* 8 byte data length */
+       SyBigEndianUnpack64(zRaw,&nData);
+       zRaw += 8;
+       /* 2 byte offset of the next cell */
+       SyBigEndianUnpack16(zRaw,&iNext);
+       /* Perform a sanity check */
+       if( iNext > 0 && &pPage->pRaw->zData[iNext] >= zEnd ){
+               return UNQLITE_CORRUPT;
+       }
+       zRaw += 2;
+       pCell = lhNewCell(pPage->pHash,pPage);
+       if( pCell == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Fill in the structure */
+       pCell->iNext = iNext;
+       pCell->nKey  = nKey;
+       pCell->nData = nData;
+       pCell->nHash = iHash;
+       /* Overflow page if any */
+       SyBigEndianUnpack64(zRaw,&pCell->iOvfl);
+       zRaw += 8;
+       /* Cell offset */
+       pCell->iStart = iOfft;
+       /* Consume the key */
+       rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,pCell->nKey > 262144 /* 256 KB */? 1 : 0);
+       if( rc != UNQLITE_OK ){
+               /* TICKET: 14-32-chm@symisc.net: Key too large for memory */
+               SyBlobRelease(&pCell->sKey);
+       }
+       /* Finally install the cell */
+       rc = lhInstallCell(pCell);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( ppOut ){
+               *ppOut = pCell;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Compute the total number of free space on a given page.
+ */
+static int lhPageFreeSpace(lhpage *pPage)
+{
+       const unsigned char *zEnd,*zRaw = pPage->pRaw->zData;
+       lhphdr *pHdr = &pPage->sHdr;
+       sxu16 iNext,iAmount;
+       sxu16 nFree = 0;
+       if( pHdr->iFree < 1 ){
+               /* Don't bother processing, the page is full */
+               pPage->nFree = 0;
+               return UNQLITE_OK;
+       }
+       /* Point to first free block */
+       zEnd = &zRaw[pPage->pHash->iPageSize];
+       zRaw += pHdr->iFree;
+       for(;;){
+               /* Offset of the next free block */
+               SyBigEndianUnpack16(zRaw,&iNext);
+               zRaw += 2;
+               /* Available space on this block */
+               SyBigEndianUnpack16(zRaw,&iAmount);
+               nFree += iAmount;
+               if( iNext < 1 ){
+                       /* No more free blocks */
+                       break;
+               }
+               /* Point to the next free block*/
+               zRaw = &pPage->pRaw->zData[iNext];
+               if( zRaw >= zEnd ){
+                       /* Corrupt page */
+                       return UNQLITE_CORRUPT;
+               }
+       }
+       /* Save the amount of free space */
+       pPage->nFree = nFree;
+       return UNQLITE_OK;
+}
+/*
+ * Given a primary page, load all its cell.
+ */
+static int lhLoadCells(lhpage *pPage)
+{
+       const unsigned char *zEnd,*zRaw = pPage->pRaw->zData;
+       lhphdr *pHdr = &pPage->sHdr;
+       lhcell *pCell = 0; /* cc warning */
+       int rc;
+       /* Calculate the amount of free space available first */
+       rc = lhPageFreeSpace(pPage);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( pHdr->iOfft < 1 ){
+               /* Don't bother processing, the page is empty */
+               return UNQLITE_OK;
+       }
+       /* Point to first cell */
+       zRaw += pHdr->iOfft;
+       zEnd = &zRaw[pPage->pHash->iPageSize];
+       for(;;){
+               /* Parse a single cell */
+               rc = lhParseOneCell(pPage,zRaw,zEnd,&pCell);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               if( pCell->iNext < 1 ){
+                       /* No more cells */
+                       break;
+               }
+               /* Point to the next cell */
+               zRaw = &pPage->pRaw->zData[pCell->iNext];
+               if( zRaw >= zEnd ){
+                       /* Corrupt page */
+                       return UNQLITE_CORRUPT;
+               }
+       }
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Given a page, parse its raw headers.
+ */
+static int lhParsePageHeader(lhpage *pPage)
+{
+       const unsigned char *zRaw = pPage->pRaw->zData;
+       lhphdr *pHdr = &pPage->sHdr;
+       /* Offset of the first cell */
+       SyBigEndianUnpack16(zRaw,&pHdr->iOfft);
+       zRaw += 2;
+       /* Offset of the first free block */
+       SyBigEndianUnpack16(zRaw,&pHdr->iFree);
+       zRaw += 2;
+       /* Slave page number */
+       SyBigEndianUnpack64(zRaw,&pHdr->iSlave);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Allocate a new page instance.
+ */
+static lhpage * lhNewPage(
+       lhash_kv_engine *pEngine, /* KV store which own this instance */
+       unqlite_page *pRaw,       /* Raw page contents */
+       lhpage *pMaster           /* Master page in case we are dealing with a slave page */
+       )
+{
+       lhpage *pPage;
+       /* Allocate a new instance */
+       pPage = (lhpage *)SyMemBackendPoolAlloc(&pEngine->sAllocator,sizeof(lhpage));
+       if( pPage == 0 ){
+               return 0;
+       }
+       /* Zero the structure */
+       SyZero(pPage,sizeof(lhpage));
+       /* Fill-in the structure */
+       pPage->pHash = pEngine;
+       pPage->pRaw = pRaw;
+       pPage->pMaster = pMaster ? pMaster /* Slave page */ : pPage /* Master page */ ;
+       if( pPage->pMaster != pPage ){
+               /* Slave page, attach it to its master */
+               pPage->pNextSlave = pMaster->pSlave;
+               pMaster->pSlave = pPage;
+               pMaster->iSlave++;
+       }
+       /* Save this instance for future fast lookup */
+       pRaw->pUserData = pPage;
+       /* All done */
+       return pPage;
+}
+/*
+ * Load a primary and its associated slave pages from disk.
+ */
+static int lhLoadPage(lhash_kv_engine *pEngine,pgno pnum,lhpage *pMaster,lhpage **ppOut,int iNest)
+{
+       unqlite_page *pRaw;
+       lhpage *pPage = 0; /* cc warning */
+       int rc;
+       /* Aquire the page from the pager first */
+       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pnum,&pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( pRaw->pUserData ){
+               /* The page is already parsed and loaded in memory. Point to it */
+               pPage = (lhpage *)pRaw->pUserData;
+       }else{
+               /* Allocate a new page */
+               pPage = lhNewPage(pEngine,pRaw,pMaster);
+               if( pPage == 0 ){
+                       return UNQLITE_NOMEM;
+               }
+               /* Process the page */
+               rc = lhParsePageHeader(pPage);
+               if( rc == UNQLITE_OK ){
+                       /* Load cells */
+                       rc = lhLoadCells(pPage);
+               }
+               if( rc != UNQLITE_OK ){
+                       pEngine->pIo->xPageUnref(pPage->pRaw); /* pPage will be released inside this call */
+                       return rc;
+               }
+               if( pPage->sHdr.iSlave > 0 && iNest < 128 ){
+                       if( pMaster == 0 ){
+                               pMaster = pPage;
+                       }
+                       /* Slave page. Not a fatal error if something goes wrong here */
+                       lhLoadPage(pEngine,pPage->sHdr.iSlave,pMaster,0,iNest++);
+               }
+       }
+       if( ppOut ){
+               *ppOut = pPage;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Given a cell, Consume its key by invoking the given callback for each extracted chunk.
+ */
+static int lhConsumeCellkey(
+       lhcell *pCell, /* Target cell */
+       int (*xConsumer)(const void *,unsigned int,void *), /* Consumer callback */
+       void *pUserData, /* Last argument to xConsumer() */
+       int offt_only
+       )
+{
+       lhpage *pPage = pCell->pPage;
+       const unsigned char *zRaw = pPage->pRaw->zData;
+       const unsigned char *zPayload;
+       int rc;
+       /* Point to the payload area */
+       zPayload = &zRaw[pCell->iStart];
+       if( pCell->iOvfl == 0 ){
+               /* Best scenario, consume the key directly without any overflow page */
+               zPayload += L_HASH_CELL_SZ;
+               rc = xConsumer((const void *)zPayload,pCell->nKey,pUserData);
+               if( rc != UNQLITE_OK ){
+                       rc = UNQLITE_ABORT;
+               }
+       }else{
+               lhash_kv_engine *pEngine = pPage->pHash;
+               sxu32 nByte,nData = pCell->nKey;
+               unqlite_page *pOvfl;
+               int data_offset = 0;
+               pgno iOvfl;
+               /* Overflow page */
+               iOvfl = pCell->iOvfl;
+               /* Total usable bytes in an overflow page */
+               nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize);
+               for(;;){
+                       if( iOvfl == 0 || nData < 1 ){
+                               /* no more overflow page */
+                               break;
+                       }
+                       /* Point to the overflow page */
+                       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       zPayload = &pOvfl->zData[8];
+                       /* Point to the raw content */
+                       if( !data_offset ){
+                               /* Get the data page and offset */
+                               SyBigEndianUnpack64(zPayload,&pCell->iDataPage);
+                               zPayload += 8;
+                               SyBigEndianUnpack16(zPayload,&pCell->iDataOfft);
+                               zPayload += 2;
+                               if( offt_only ){
+                                       /* Key too large, grab the data offset and return */
+                                       pEngine->pIo->xPageUnref(pOvfl);
+                                       return UNQLITE_OK;
+                               }
+                               data_offset = 1;
+                       }
+                       /* Consume the key */
+                       if( nData <= nByte ){
+                               rc = xConsumer((const void *)zPayload,nData,pUserData);
+                               if( rc != UNQLITE_OK ){
+                                       pEngine->pIo->xPageUnref(pOvfl);
+                                       return UNQLITE_ABORT;
+                               }
+                               nData = 0;
+                       }else{
+                               rc = xConsumer((const void *)zPayload,nByte,pUserData);
+                               if( rc != UNQLITE_OK ){
+                                       pEngine->pIo->xPageUnref(pOvfl);
+                                       return UNQLITE_ABORT;
+                               }
+                               nData -= nByte;
+                       }
+                       /* Next overflow page in the chain */
+                       SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
+                       /* Unref the page */
+                       pEngine->pIo->xPageUnref(pOvfl);
+               }
+               rc = UNQLITE_OK;
+       }
+       return rc;
+}
+/*
+ * Given a cell, Consume its data by invoking the given callback for each extracted chunk.
+ */
+static int lhConsumeCellData(
+       lhcell *pCell, /* Target cell */
+       int (*xConsumer)(const void *,unsigned int,void *), /* Data consumer callback */
+       void *pUserData /* Last argument to xConsumer() */
+       )
+{
+       lhpage *pPage = pCell->pPage;
+       const unsigned char *zRaw = pPage->pRaw->zData;
+       const unsigned char *zPayload;
+       int rc;
+       /* Point to the payload area */
+       zPayload = &zRaw[pCell->iStart];
+       if( pCell->iOvfl == 0 ){
+               /* Best scenario, consume the data directly without any overflow page */
+               zPayload += L_HASH_CELL_SZ + pCell->nKey;
+               rc = xConsumer((const void *)zPayload,(sxu32)pCell->nData,pUserData);
+               if( rc != UNQLITE_OK ){
+                       rc = UNQLITE_ABORT;
+               }
+       }else{
+               lhash_kv_engine *pEngine = pPage->pHash;
+               sxu64 nData = pCell->nData;
+               unqlite_page *pOvfl;
+               int fix_offset = 0;
+               sxu32 nByte;
+               pgno iOvfl;
+               /* Overflow page where data is stored */
+               iOvfl = pCell->iDataPage;
+               for(;;){
+                       if( iOvfl == 0 || nData < 1 ){
+                               /* no more overflow page */
+                               break;
+                       }
+                       /* Point to the overflow page */
+                       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOvfl);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Point to the raw content */
+                       zPayload = pOvfl->zData;
+                       if( !fix_offset ){
+                               /* Point to the data */
+                               zPayload += pCell->iDataOfft;
+                               nByte = pEngine->iPageSize - pCell->iDataOfft;
+                               fix_offset = 1;
+                       }else{
+                               zPayload += 8;
+                               /* Total usable bytes in an overflow page */
+                               nByte = L_HASH_OVERFLOW_SIZE(pEngine->iPageSize);
+                       }
+                       /* Consume the data */
+                       if( nData <= (sxu64)nByte ){
+                               rc = xConsumer((const void *)zPayload,(unsigned int)nData,pUserData);
+                               if( rc != UNQLITE_OK ){
+                                       pEngine->pIo->xPageUnref(pOvfl);
+                                       return UNQLITE_ABORT;
+                               }
+                               nData = 0;
+                       }else{
+                               if( nByte > 0 ){
+                                       rc = xConsumer((const void *)zPayload,nByte,pUserData);
+                                       if( rc != UNQLITE_OK ){
+                                               pEngine->pIo->xPageUnref(pOvfl);
+                                               return UNQLITE_ABORT;
+                                       }
+                                       nData -= nByte;
+                               }
+                       }
+                       /* Next overflow page in the chain */
+                       SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
+                       /* Unref the page */
+                       pEngine->pIo->xPageUnref(pOvfl);
+               }
+               rc = UNQLITE_OK;
+       }
+       return rc;
+}
+/*
+ * Read the linear hash header (Page one of the database).
+ */
+static int lhash_read_header(lhash_kv_engine *pEngine,unqlite_page *pHeader)
+{
+       const unsigned char *zRaw = pHeader->zData;
+       lhash_bmap_page *pMap;
+       sxu32 nHash;
+       int rc;
+       pEngine->pHeader = pHeader;
+       /* 4 byte magic number */
+       SyBigEndianUnpack32(zRaw,&pEngine->nMagic);
+       zRaw += 4;
+       if( pEngine->nMagic != L_HASH_MAGIC ){
+               /* Corrupt implementation */
+               return UNQLITE_CORRUPT;
+       }
+       /* 4 byte hash value to identify a valid hash function */
+       SyBigEndianUnpack32(zRaw,&nHash);
+       zRaw += 4;
+       /* Sanity check */
+       if( pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1) != nHash ){
+               /* Different hash function */
+               pEngine->pIo->xErr(pEngine->pIo->pHandle,"Invalid hash function");
+               return UNQLITE_INVALID;
+       }
+       /* List of free pages */
+       SyBigEndianUnpack64(zRaw,&pEngine->nFreeList);
+       zRaw += 8;
+       /* Current split bucket */
+       SyBigEndianUnpack64(zRaw,&pEngine->split_bucket);
+       zRaw += 8;
+       /* Maximum split bucket */
+       SyBigEndianUnpack64(zRaw,&pEngine->max_split_bucket);
+       zRaw += 8;
+       /* Next generation */
+       pEngine->nmax_split_nucket = pEngine->max_split_bucket << 1;
+       /* Initialiaze the bucket map */
+       pMap = &pEngine->sPageMap;
+       /* Fill in the structure */
+       pMap->iNum = pHeader->pgno;
+       /* Next page in the bucket map */
+       SyBigEndianUnpack64(zRaw,&pMap->iNext);
+       zRaw += 8;
+       /* Total number of records in the bucket map (This page only) */
+       SyBigEndianUnpack32(zRaw,&pMap->nRec);
+       zRaw += 4;
+       pMap->iPtr = (sxu16)(zRaw - pHeader->zData);
+       /* Load the map in memory */
+       rc = lhMapLoadPage(pEngine,pMap,pHeader->zData);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Load the bucket map chain if any */
+       for(;;){
+               pgno iNext = pMap->iNext;
+               unqlite_page *pPage;
+               if( iNext == 0 ){
+                       /* No more map pages */
+                       break;
+               }
+               /* Point to the target page */
+               rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pPage);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Fill in the structure */
+               pMap->iNum = iNext;
+               pMap->iPtr = 0;
+               /* Load the map in memory */
+               rc = lhMapLoadPage(pEngine,pMap,pPage->zData);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+       }
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Perform a record lookup.
+ */
+static int lhRecordLookup(
+       lhash_kv_engine *pEngine, /* KV storage engine */
+       const void *pKey,         /* Lookup key */
+       sxu32 nByte,              /* Key length */
+       lhcell **ppCell           /* OUT: Target cell on success */
+       )
+{
+       lhash_bmap_rec *pRec;
+       lhpage *pPage;
+       lhcell *pCell;
+       pgno iBucket;
+       sxu32 nHash;
+       int rc;
+       /* Acquire the first page (hash Header) so that everything gets loaded autmatically */
+       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Compute the hash of the key first */
+       nHash = pEngine->xHash(pKey,nByte);
+       /* Extract the logical (i.e. not real) page number */
+       iBucket = nHash & (pEngine->nmax_split_nucket - 1);
+       if( iBucket >= (pEngine->split_bucket + pEngine->max_split_bucket) ){
+               /* Low mask */
+               iBucket = nHash & (pEngine->max_split_bucket - 1);
+       }
+       /* Map the logical bucket number to real page number */
+       pRec = lhMapFindBucket(pEngine,iBucket);
+       if( pRec == 0 ){
+               /* No such entry */
+               return UNQLITE_NOTFOUND;
+       }
+       /* Load the master page and it's slave page in-memory  */
+       rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0);
+       if( rc != UNQLITE_OK ){
+               /* IO error, unlikely scenario */
+               return rc;
+       }
+       /* Lookup for the cell */
+       pCell = lhFindCell(pPage,pKey,nByte,nHash);
+       if( pCell == 0 ){
+               /* No such entry */
+               return UNQLITE_NOTFOUND;
+       }
+       if( ppCell ){
+               *ppCell = pCell;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Acquire a new page either from the free list or ask the pager
+ * for a new one.
+ */
+static int lhAcquirePage(lhash_kv_engine *pEngine,unqlite_page **ppOut)
+{
+       unqlite_page *pPage;
+       int rc;
+       if( pEngine->nFreeList != 0 ){
+               /* Acquire one from the free list */
+               rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pEngine->nFreeList,&pPage);
+               if( rc == UNQLITE_OK ){
+                       /* Point to the next free page */
+                       SyBigEndianUnpack64(pPage->zData,&pEngine->nFreeList);
+                       /* Update the database header */
+                       rc = pEngine->pIo->xWrite(pEngine->pHeader);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList);
+                       /* Tell the pager do not journal this page */
+                       pEngine->pIo->xDontJournal(pPage);
+                       /* Return to the caller */
+                       *ppOut = pPage;
+                       /* All done */
+                       return UNQLITE_OK;
+               }
+       }
+       /* Acquire a new page */
+       rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pPage);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Point to the target page */
+       *ppOut = pPage;
+       return UNQLITE_OK;
+}
+/*
+ * Write a bucket map record to disk.
+ */
+static int lhMapWriteRecord(lhash_kv_engine *pEngine,pgno iLogic,pgno iReal)
+{
+       lhash_bmap_page *pMap = &pEngine->sPageMap;
+       unqlite_page *pPage = 0;
+       int rc;
+       if( pMap->iPtr > (pEngine->iPageSize - 16) /* 8 byte logical bucket number + 8 byte real bucket number */ ){
+               unqlite_page *pOld;
+               /* Point to the old page */
+               rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pOld);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Acquire a new page */
+               rc = lhAcquirePage(pEngine,&pPage);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Reflect the change  */
+               pMap->iNext = 0;
+               pMap->iNum = pPage->pgno;
+               pMap->nRec = 0;
+               pMap->iPtr = 8/* Next page number */+4/* Total records in the map*/;
+               /* Link this page */
+               rc = pEngine->pIo->xWrite(pOld);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               if( pOld->pgno == pEngine->pHeader->pgno ){
+                       /* First page (Hash header) */
+                       SyBigEndianPack64(&pOld->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/],pPage->pgno);
+               }else{
+                       /* Link the new page */
+                       SyBigEndianPack64(pOld->zData,pPage->pgno);
+                       /* Unref */
+                       pEngine->pIo->xPageUnref(pOld);
+               }
+               /* Assume the last bucket map page */
+               rc = pEngine->pIo->xWrite(pPage);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               SyBigEndianPack64(pPage->zData,0); /* Next bucket map page on the list */
+       }
+       if( pPage == 0){
+               /* Point to the current map page */
+               rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pMap->iNum,&pPage);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+       }
+       /* Make page writable */
+       rc = pEngine->pIo->xWrite(pPage);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Write the data */
+       SyBigEndianPack64(&pPage->zData[pMap->iPtr],iLogic);
+       pMap->iPtr += 8;
+       SyBigEndianPack64(&pPage->zData[pMap->iPtr],iReal);
+       pMap->iPtr += 8;
+       /* Install the bucket map */
+       rc = lhMapInstallBucket(pEngine,iLogic,iReal);
+       if( rc == UNQLITE_OK ){
+               /* Total number of records */
+               pMap->nRec++;
+               if( pPage->pgno == pEngine->pHeader->pgno ){
+                       /* Page one: Always writable */
+                       SyBigEndianPack32(
+                               &pPage->zData[4/*magic*/+4/*hash*/+8/* Free page */+8/*current split bucket*/+8/*Maximum split bucket*/+8/*Next map page*/],
+                               pMap->nRec);
+               }else{
+                       /* Make page writable */
+                       rc = pEngine->pIo->xWrite(pPage);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       SyBigEndianPack32(&pPage->zData[8],pMap->nRec);
+               }
+       }
+       return rc;
+}
+/*
+ * Defragment a page.
+ */
+static int lhPageDefragment(lhpage *pPage)
+{
+       lhash_kv_engine *pEngine = pPage->pHash;
+       unsigned char *zTmp,*zPtr,*zEnd,*zPayload;
+       lhcell *pCell;
+       /* Get a temporary page from the pager. This opertaion never fail */
+       zTmp = pEngine->pIo->xTmpPage(pEngine->pIo->pHandle);
+       /* Move the target cells to the begining */
+       pCell = pPage->pList;
+       /* Write the slave page number */
+       SyBigEndianPack64(&zTmp[2/*Offset of the first cell */+2/*Offset of the first free block */],pPage->sHdr.iSlave);
+       zPtr = &zTmp[L_HASH_PAGE_HDR_SZ]; /* Offset to start writing from */
+       zEnd = &zTmp[pEngine->iPageSize];
+       pPage->sHdr.iOfft = 0; /* Offset of the first cell */
+       for(;;){
+               if( pCell == 0 ){
+                       /* No more cells */
+                       break;
+               }
+               if( pCell->pPage->pRaw->pgno == pPage->pRaw->pgno ){
+                       /* Cell payload if locally stored */
+                       zPayload = 0;
+                       if( pCell->iOvfl == 0 ){
+                               zPayload = &pCell->pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ];
+                       }
+                       /* Move the cell */
+                       pCell->iNext = pPage->sHdr.iOfft;
+                       pCell->iStart = (sxu16)(zPtr - zTmp); /* Offset where this cell start */
+                       pPage->sHdr.iOfft = pCell->iStart;
+                       /* Write the cell header */
+                       /* 4 byte hash number */
+                       SyBigEndianPack32(zPtr,pCell->nHash);
+                       zPtr += 4;
+                       /* 4 byte ley length */
+                       SyBigEndianPack32(zPtr,pCell->nKey);
+                       zPtr += 4;
+                       /* 8 byte data length */
+                       SyBigEndianPack64(zPtr,pCell->nData);
+                       zPtr += 8;
+                       /* 2 byte offset of the next cell */
+                       SyBigEndianPack16(zPtr,pCell->iNext);
+                       zPtr += 2;
+                       /* 8 byte overflow page number */
+                       SyBigEndianPack64(zPtr,pCell->iOvfl);
+                       zPtr += 8;
+                       if( zPayload ){
+                               /* Local payload */
+                               SyMemcpy((const void *)zPayload,zPtr,(sxu32)(pCell->nKey + pCell->nData));
+                               zPtr += pCell->nKey + pCell->nData;
+                       }
+                       if( zPtr >= zEnd ){
+                               /* Can't happen */
+                               break;
+                       }
+               }
+               /* Point to the next page */
+               pCell = pCell->pNext;
+       }
+       /* Mark the free block */
+       pPage->nFree = (sxu16)(zEnd - zPtr); /* Block length */
+       if( pPage->nFree > 3 ){
+               pPage->sHdr.iFree = (sxu16)(zPtr - zTmp); /* Offset of the free block */
+               /* Mark the block */
+               SyBigEndianPack16(zPtr,0); /* Offset of the next free block */
+               SyBigEndianPack16(&zPtr[2],pPage->nFree); /* Block length */
+       }else{
+               /* Block of length less than 4 bytes are simply discarded */
+               pPage->nFree = 0;
+               pPage->sHdr.iFree = 0;
+       }
+       /* Reflect the change */
+       SyBigEndianPack16(zTmp,pPage->sHdr.iOfft);     /* Offset of the first cell */
+       SyBigEndianPack16(&zTmp[2],pPage->sHdr.iFree); /* Offset of the first free block */
+       SyMemcpy((const void *)zTmp,pPage->pRaw->zData,pEngine->iPageSize);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+** Allocate nByte bytes of space on a page.
+**
+** Return the index into pPage->pRaw->zData[] of the first byte of
+** the new allocation. Or return 0 if there is not enough free
+** space on the page to satisfy the allocation request.
+**
+** If the page contains nBytes of free space but does not contain
+** nBytes of contiguous free space, then this routine automatically
+** calls defragementPage() to consolidate all free space before 
+** allocating the new chunk.
+*/
+static int lhAllocateSpace(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft)
+{
+       const unsigned char *zEnd,*zPtr;
+       sxu16 iNext,iBlksz,nByte;
+       unsigned char *zPrev;
+       int rc;
+       if( (sxu64)pPage->nFree < nAmount ){
+               /* Don't bother looking for a free chunk */
+               return UNQLITE_FULL;
+       }
+       if( pPage->nCell < 10 && ((int)nAmount >= (pPage->pHash->iPageSize / 2)) ){
+               /* Big chunk need an overflow page for its data */
+               return UNQLITE_FULL;
+       }
+       zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree];
+       zEnd = &pPage->pRaw->zData[pPage->pHash->iPageSize];
+       nByte = (sxu16)nAmount;
+       zPrev = 0;
+       iBlksz = 0; /* cc warning */
+       /* Perform the lookup */
+       for(;;){
+               if( zPtr >= zEnd ){
+                       return UNQLITE_FULL;
+               }
+               /* Offset of the next free block */
+               SyBigEndianUnpack16(zPtr,&iNext);
+               /* Block size */
+               SyBigEndianUnpack16(&zPtr[2],&iBlksz);
+               if( iBlksz >= nByte ){
+                       /* Got one */
+                       break;
+               }
+               zPrev = (unsigned char *)zPtr;
+               if( iNext == 0 ){
+                       /* No more free blocks, defragment the page */
+                       rc = lhPageDefragment(pPage);
+                       if( rc == UNQLITE_OK && pPage->nFree >= nByte) {
+                               /* Free blocks are merged together */
+                               iNext = 0;
+                               zPtr = &pPage->pRaw->zData[pPage->sHdr.iFree];
+                               iBlksz = pPage->nFree;
+                               zPrev = 0;
+                               break;
+                       }else{
+                               return UNQLITE_FULL;
+                       }
+               }
+               /* Point to the next free block */
+               zPtr = &pPage->pRaw->zData[iNext];
+       }
+       /* Acquire writer lock on this page */
+       rc = pPage->pHash->pIo->xWrite(pPage->pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Save block offset */
+       *pOfft = (sxu16)(zPtr - pPage->pRaw->zData);
+       /* Fix pointers */
+       if( iBlksz >= nByte && (iBlksz - nByte) > 3 ){
+               unsigned char *zBlock = &pPage->pRaw->zData[(*pOfft) + nByte];
+               /* Create a new block */
+               zPtr = zBlock;
+               SyBigEndianPack16(zBlock,iNext); /* Offset of the next block */
+               SyBigEndianPack16(&zBlock[2],iBlksz-nByte); /* Block size*/
+               /* Offset of the new block */
+               iNext = (sxu16)(zPtr - pPage->pRaw->zData);
+               iBlksz = nByte;
+       }
+       /* Fix offsets */
+       if( zPrev ){
+               SyBigEndianPack16(zPrev,iNext);
+       }else{
+               /* First block */
+               pPage->sHdr.iFree = iNext;
+               /* Reflect on the page header */
+               SyBigEndianPack16(&pPage->pRaw->zData[2/* Offset of the first cell1*/],iNext);
+       }
+       /* All done */
+       pPage->nFree -= iBlksz;
+       return UNQLITE_OK;
+}
+/*
+ * Write the cell header into the corresponding offset.
+ */
+static int lhCellWriteHeader(lhcell *pCell)
+{
+       lhpage *pPage = pCell->pPage;
+       unsigned char *zRaw = pPage->pRaw->zData;
+       /* Seek to the desired location */
+       zRaw += pCell->iStart;
+       /* 4 byte hash number */
+       SyBigEndianPack32(zRaw,pCell->nHash);
+       zRaw += 4;
+       /* 4 byte key length */
+       SyBigEndianPack32(zRaw,pCell->nKey);
+       zRaw += 4;
+       /* 8 byte data length */
+       SyBigEndianPack64(zRaw,pCell->nData);
+       zRaw += 8;
+       /* 2 byte offset of the next cell */
+       pCell->iNext = pPage->sHdr.iOfft;
+       SyBigEndianPack16(zRaw,pCell->iNext);
+       zRaw += 2;
+       /* 8 byte overflow page number */
+       SyBigEndianPack64(zRaw,pCell->iOvfl);
+       /* Update the page header */
+       pPage->sHdr.iOfft = pCell->iStart;
+       /* pEngine->pIo->xWrite() has been successfully called on this page */
+       SyBigEndianPack16(pPage->pRaw->zData,pCell->iStart);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Write local payload.
+ */
+static int lhCellWriteLocalPayload(lhcell *pCell,
+       const void *pKey,sxu32 nKeylen,
+       const void *pData,unqlite_int64 nDatalen
+       )
+{
+       /* A writer lock have been acquired on this page */
+       lhpage *pPage = pCell->pPage;
+       unsigned char *zRaw = pPage->pRaw->zData;
+       /* Seek to the desired location */
+       zRaw += pCell->iStart + L_HASH_CELL_SZ;
+       /* Write the key */
+       SyMemcpy(pKey,(void *)zRaw,nKeylen);
+       zRaw += nKeylen;
+       if( nDatalen > 0 ){
+               /* Write the Data */
+               SyMemcpy(pData,(void *)zRaw,(sxu32)nDatalen);
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Allocate as much overflow page we need to store the cell payload.
+ */
+static int lhCellWriteOvflPayload(lhcell *pCell,const void *pKey,sxu32 nKeylen,...)
+{
+       lhpage *pPage = pCell->pPage;
+       lhash_kv_engine *pEngine = pPage->pHash;
+       unqlite_page *pOvfl,*pFirst,*pNew;
+       const unsigned char *zPtr,*zEnd;
+       unsigned char *zRaw,*zRawEnd;
+       sxu32 nAvail;
+       va_list ap;
+       int rc;
+       /* Acquire a new overflow page */
+       rc = lhAcquirePage(pEngine,&pOvfl);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Acquire a writer lock */
+       rc = pEngine->pIo->xWrite(pOvfl);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       pFirst = pOvfl;
+       /* Link */
+       pCell->iOvfl = pOvfl->pgno;
+       /* Update the cell header */
+       SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4/*Hash*/ + 4/*Key*/ + 8/*Data*/ + 2 /*Next cell*/],pCell->iOvfl);
+       /* Start the write process */
+       zPtr = (const unsigned char *)pKey;
+       zEnd = &zPtr[nKeylen];
+       SyBigEndianPack64(pOvfl->zData,0); /* Next overflow page on the chain */
+       zRaw = &pOvfl->zData[8/* Next ovfl page*/ + 8 /* Data page */ + 2 /* Data offset*/];
+       zRawEnd = &pOvfl->zData[pEngine->iPageSize];
+       pNew = pOvfl;
+       /* Write the key */
+       for(;;){
+               if( zPtr >= zEnd ){
+                       break;
+               }
+               if( zRaw >= zRawEnd ){
+                       /* Acquire a new page */
+                       rc = lhAcquirePage(pEngine,&pNew);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       rc = pEngine->pIo->xWrite(pNew);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Link */
+                       SyBigEndianPack64(pOvfl->zData,pNew->pgno);
+                       pEngine->pIo->xPageUnref(pOvfl);
+                       SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
+                       pOvfl = pNew;
+                       zRaw = &pNew->zData[8];
+                       zRawEnd = &pNew->zData[pEngine->iPageSize];
+               }
+               nAvail = (sxu32)(zRawEnd-zRaw);
+               nKeylen = (sxu32)(zEnd-zPtr);
+               if( nKeylen > nAvail ){
+                       nKeylen = nAvail;
+               }
+               SyMemcpy((const void *)zPtr,(void *)zRaw,nKeylen);
+               /* Synchronize pointers */
+               zPtr += nKeylen;
+               zRaw += nKeylen;
+       }
+       rc = UNQLITE_OK;
+       va_start(ap,nKeylen);
+       pCell->iDataPage = pNew->pgno;
+       pCell->iDataOfft = (sxu16)(zRaw-pNew->zData);
+       /* Write the data page and its offset */
+       SyBigEndianPack64(&pFirst->zData[8/*Next ovfl*/],pCell->iDataPage);
+       SyBigEndianPack16(&pFirst->zData[8/*Next ovfl*/+8/*Data page*/],pCell->iDataOfft);
+       /* Write data */
+       for(;;){
+               const void *pData;
+               sxu32 nDatalen;
+               sxu64 nData;
+               pData = va_arg(ap,const void *);
+               nData = va_arg(ap,sxu64);
+               if( pData == 0 ){
+                       /* No more chunks */
+                       break;
+               }
+               /* Write this chunk */
+               zPtr = (const unsigned char *)pData;
+               zEnd = &zPtr[nData];
+               for(;;){
+                       if( zPtr >= zEnd ){
+                               break;
+                       }
+                       if( zRaw >= zRawEnd ){
+                               /* Acquire a new page */
+                               rc = lhAcquirePage(pEngine,&pNew);
+                               if( rc != UNQLITE_OK ){
+                                       va_end(ap);
+                                       return rc;
+                               }
+                               rc = pEngine->pIo->xWrite(pNew);
+                               if( rc != UNQLITE_OK ){
+                                       va_end(ap);
+                                       return rc;
+                               }
+                               /* Link */
+                               SyBigEndianPack64(pOvfl->zData,pNew->pgno);
+                               pEngine->pIo->xPageUnref(pOvfl);
+                               SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
+                               pOvfl = pNew;
+                               zRaw = &pNew->zData[8];
+                               zRawEnd = &pNew->zData[pEngine->iPageSize];
+                       }
+                       nAvail = (sxu32)(zRawEnd-zRaw);
+                       nDatalen = (sxu32)(zEnd-zPtr);
+                       if( nDatalen > nAvail ){
+                               nDatalen = nAvail;
+                       }
+                       SyMemcpy((const void *)zPtr,(void *)zRaw,nDatalen);
+                       /* Synchronize pointers */
+                       zPtr += nDatalen;
+                       zRaw += nDatalen;
+               }
+       }
+       /* Unref the overflow page */
+       pEngine->pIo->xPageUnref(pOvfl);
+       va_end(ap);
+       return UNQLITE_OK;
+}
+/*
+ * Restore a page to the free list.
+ */
+static int lhRestorePage(lhash_kv_engine *pEngine,unqlite_page *pPage)
+{
+       int rc;
+       rc = pEngine->pIo->xWrite(pEngine->pHeader);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       rc = pEngine->pIo->xWrite(pPage);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Link to the list of free page */
+       SyBigEndianPack64(pPage->zData,pEngine->nFreeList);
+       pEngine->nFreeList = pPage->pgno;
+       SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/],pEngine->nFreeList);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Restore cell space and mark it as a free block.
+ */
+static int lhRestoreSpace(lhpage *pPage,sxu16 iOfft,sxu16 nByte)
+{
+       unsigned char *zRaw;
+       if( nByte < 4 ){
+               /* At least 4 bytes of freespace must form a valid block */
+               return UNQLITE_OK;
+       }
+       /* pEngine->pIo->xWrite() has been successfully called on this page */
+       zRaw = &pPage->pRaw->zData[iOfft];
+       /* Mark as a free block */
+       SyBigEndianPack16(zRaw,pPage->sHdr.iFree); /* Offset of the next free block */
+       zRaw += 2;
+       SyBigEndianPack16(zRaw,nByte);
+       /* Link */
+       SyBigEndianPack16(&pPage->pRaw->zData[2/* offset of the first cell */],iOfft);
+       pPage->sHdr.iFree = iOfft;
+       pPage->nFree += nByte;
+       return UNQLITE_OK;
+}
+/* Forward declaration */
+static lhcell * lhFindSibeling(lhcell *pCell);
+/*
+ * Unlink a cell.
+ */
+static int lhUnlinkCell(lhcell *pCell)
+{
+       lhash_kv_engine *pEngine = pCell->pPage->pHash;
+       lhpage *pPage = pCell->pPage;
+       sxu16 nByte = L_HASH_CELL_SZ;
+       lhcell *pPrev;
+       int rc;
+       rc = pEngine->pIo->xWrite(pPage->pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Bring the link */
+       pPrev = lhFindSibeling(pCell);
+       if( pPrev ){
+               pPrev->iNext = pCell->iNext;
+               /* Fix offsets in the page header */
+               SyBigEndianPack16(&pPage->pRaw->zData[pPrev->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext);
+       }else{
+               /* First entry on this page (either master or slave) */
+               pPage->sHdr.iOfft = pCell->iNext;
+               /* Update the page header */
+               SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext);
+       }
+       /* Restore cell space */
+       if( pCell->iOvfl == 0 ){
+               nByte += (sxu16)(pCell->nData + pCell->nKey);
+       }
+       lhRestoreSpace(pPage,pCell->iStart,nByte);
+       /* Discard the cell from the in-memory hashtable */
+       lhCellDiscard(pCell);
+       return UNQLITE_OK;
+}
+/*
+ * Remove a cell and its paylod (key + data).
+ */
+static int lhRecordRemove(lhcell *pCell)
+{
+       lhash_kv_engine *pEngine = pCell->pPage->pHash;
+       int rc;
+       if( pCell->iOvfl > 0){
+               /* Discard overflow pages */
+               unqlite_page *pOvfl;
+               pgno iNext = pCell->iOvfl;
+               for(;;){
+                       /* Point to the overflow page */
+                       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iNext,&pOvfl);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Next page on the chain */
+                       SyBigEndianUnpack64(pOvfl->zData,&iNext);
+                       /* Restore the page to the free list */
+                       rc = lhRestorePage(pEngine,pOvfl);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Unref */
+                       pEngine->pIo->xPageUnref(pOvfl);
+                       if( iNext == 0 ){
+                               break;
+                       }
+               }
+       }
+       /* Unlink the cell */
+       rc = lhUnlinkCell(pCell);
+       return rc;
+}
+/*
+ * Find cell sibeling.
+ */
+static lhcell * lhFindSibeling(lhcell *pCell)
+{
+       lhpage *pPage = pCell->pPage->pMaster;
+       lhcell *pEntry;
+       pEntry = pPage->pFirst; 
+       while( pEntry ){
+               if( pEntry->pPage == pCell->pPage && pEntry->iNext == pCell->iStart ){
+                       /* Sibeling found */
+                       return pEntry;
+               }
+               /* Point to the previous entry */
+               pEntry = pEntry->pPrev; 
+       }
+       /* Last inserted cell */
+       return 0;
+}
+/*
+ * Move a cell to a new location with its new data.
+ */
+static int lhMoveLocalCell(
+       lhcell *pCell,
+       sxu16 iOfft,
+       const void *pData,
+       unqlite_int64 nData
+       )
+{
+       sxu16 iKeyOfft = pCell->iStart + L_HASH_CELL_SZ;
+       lhpage *pPage = pCell->pPage;
+       lhcell *pSibeling;
+       pSibeling = lhFindSibeling(pCell);
+       if( pSibeling ){
+               /* Fix link */
+               SyBigEndianPack16(&pPage->pRaw->zData[pSibeling->iStart + 4/*Hash*/+4/*Key*/+8/*Data*/],pCell->iNext);
+               pSibeling->iNext = pCell->iNext;
+       }else{
+               /* First cell, update page header only */
+               SyBigEndianPack16(pPage->pRaw->zData,pCell->iNext);
+               pPage->sHdr.iOfft = pCell->iNext;
+       }
+       /* Set the new offset */
+       pCell->iStart = iOfft;
+       pCell->nData = (sxu64)nData;
+       /* Write the cell payload */
+       lhCellWriteLocalPayload(pCell,(const void *)&pPage->pRaw->zData[iKeyOfft],pCell->nKey,pData,nData);
+       /* Finally write the cell header */
+       lhCellWriteHeader(pCell);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Overwrite an existing record.
+ */
+static int lhRecordOverwrite(
+       lhcell *pCell,
+       const void *pData,unqlite_int64 nByte
+       )
+{
+       lhash_kv_engine *pEngine = pCell->pPage->pHash;
+       unsigned char *zRaw,*zRawEnd,*zPayload;
+       const unsigned char *zPtr,*zEnd;
+       unqlite_page *pOvfl,*pOld,*pNew;
+       lhpage *pPage = pCell->pPage;
+       sxu32 nAvail;
+       pgno iOvfl;
+       int rc;
+       /* Acquire a writer lock on this page */
+       rc = pEngine->pIo->xWrite(pPage->pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( pCell->iOvfl == 0 ){
+               /* Local payload, try to deal with the free space issues */
+               zPayload = &pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey];
+               if( pCell->nData == (sxu64)nByte ){
+                       /* Best scenario, simply a memcpy operation */
+                       SyMemcpy(pData,(void *)zPayload,(sxu32)nByte);
+               }else if( (sxu64)nByte < pCell->nData ){
+                       /* Shorter data, not so ugly */
+                       SyMemcpy(pData,(void *)zPayload,(sxu32)nByte);
+                       /* Update the cell header */
+                       SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],nByte);
+                       /* Restore freespace */
+                       lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ + pCell->nKey + nByte),(sxu16)(pCell->nData - nByte));
+                       /* New data size */
+                       pCell->nData = (sxu64)nByte;
+               }else{
+                       sxu16 iOfft = 0; /* cc warning */
+                       /* Check if another chunk is available for this cell */
+                       rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + nByte,&iOfft);
+                       if( rc != UNQLITE_OK ){
+                               /* Transfer the payload to an overflow page */
+                               rc = lhCellWriteOvflPayload(pCell,&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,pData,nByte,(const void *)0);
+                               if( rc != UNQLITE_OK ){
+                                       return rc;
+                               }
+                               /* Update the cell header */
+                               SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],(sxu64)nByte);
+                               /* Restore freespace */
+                               lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData));
+                               /* New data size */
+                               pCell->nData = (sxu64)nByte;
+                       }else{
+                               sxu16 iOldOfft = pCell->iStart;
+                               sxu32 iOld = (sxu32)pCell->nData;
+                               /* Space is available, transfer the cell */
+                               lhMoveLocalCell(pCell,iOfft,pData,nByte);
+                               /* Restore cell space */
+                               lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld));
+                       }
+               }
+               return UNQLITE_OK;
+       }
+       /* Point to the overflow page */
+       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Relase all old overflow pages first */
+       SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
+       pOld = pOvfl;
+       for(;;){
+               if( iOvfl == 0 ){
+                       /* No more overflow pages on the chain */
+                       break;
+               }
+               /* Point to the target page */
+               if( UNQLITE_OK != pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pOld) ){
+                       /* Not so fatal if something goes wrong here */
+                       break;
+               }
+               /* Next overflow page to be released */
+               SyBigEndianUnpack64(pOld->zData,&iOvfl);
+               if( pOld != pOvfl ){ /* xx: chm is maniac */
+                       /* Restore the page to the free list */
+                       lhRestorePage(pEngine,pOld);
+                       /* Unref */
+                       pEngine->pIo->xPageUnref(pOld);
+               }
+       }
+       /* Point to the data offset */
+       zRaw = &pOvfl->zData[pCell->iDataOfft];
+       zRawEnd = &pOvfl->zData[pEngine->iPageSize];
+       /* The data to be stored */
+       zPtr = (const unsigned char *)pData;
+       zEnd = &zPtr[nByte];
+       /* Start the overwrite process */
+       /* Acquire a writer lock */
+       rc = pEngine->pIo->xWrite(pOvfl);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       SyBigEndianPack64(pOvfl->zData,0);
+       for(;;){
+               sxu32 nLen;
+               if( zPtr >= zEnd ){
+                       break;
+               }
+               if( zRaw >= zRawEnd ){
+                       /* Acquire a new page */
+                       rc = lhAcquirePage(pEngine,&pNew);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       rc = pEngine->pIo->xWrite(pNew);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Link */
+                       SyBigEndianPack64(pOvfl->zData,pNew->pgno);
+                       pEngine->pIo->xPageUnref(pOvfl);
+                       SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
+                       pOvfl = pNew;
+                       zRaw = &pNew->zData[8];
+                       zRawEnd = &pNew->zData[pEngine->iPageSize];
+               }
+               nAvail = (sxu32)(zRawEnd-zRaw);
+               nLen = (sxu32)(zEnd-zPtr);
+               if( nLen > nAvail ){
+                       nLen = nAvail;
+               }
+               SyMemcpy((const void *)zPtr,(void *)zRaw,nLen);
+               /* Synchronize pointers */
+               zPtr += nLen;
+               zRaw += nLen;
+       }
+       /* Unref the last overflow page */
+       pEngine->pIo->xPageUnref(pOvfl);
+       /* Finally, update the cell header */
+       pCell->nData = (sxu64)nByte;
+       SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Append data to an existing record.
+ */
+static int lhRecordAppend(
+       lhcell *pCell,
+       const void *pData,unqlite_int64 nByte
+       )
+{
+       lhash_kv_engine *pEngine = pCell->pPage->pHash;
+       const unsigned char *zPtr,*zEnd;
+       lhpage *pPage = pCell->pPage;
+       unsigned char *zRaw,*zRawEnd;
+       unqlite_page *pOvfl,*pNew;
+       sxu64 nDatalen;
+       sxu32 nAvail;
+       pgno iOvfl;
+       int rc;
+       if( pCell->nData + nByte < pCell->nData ){
+               /* Overflow */
+               pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");
+               return UNQLITE_LIMIT;
+       }
+       /* Acquire a writer lock on this page */
+       rc = pEngine->pIo->xWrite(pPage->pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( pCell->iOvfl == 0 ){
+               sxu16 iOfft = 0; /* cc warning */
+               /* Local payload, check for a bigger place */
+               rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ + pCell->nKey + pCell->nData + nByte,&iOfft);
+               if( rc != UNQLITE_OK ){
+                       /* Transfer the payload to an overflow page */
+                       rc = lhCellWriteOvflPayload(pCell,
+                               &pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ],pCell->nKey,
+                               (const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],pCell->nData,
+                               pData,nByte,
+                               (const void *)0);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Update the cell header */
+                       SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData + nByte);
+                       /* Restore freespace */
+                       lhRestoreSpace(pPage,(sxu16)(pCell->iStart + L_HASH_CELL_SZ),(sxu16)(pCell->nKey + pCell->nData));
+                       /* New data size */
+                       pCell->nData += nByte;
+               }else{
+                       sxu16 iOldOfft = pCell->iStart;
+                       sxu32 iOld = (sxu32)pCell->nData;
+                       SyBlob sWorker;
+                       SyBlobInit(&sWorker,&pEngine->sAllocator);
+                       /* Copy the old data */
+                       rc = SyBlobAppend(&sWorker,(const void *)&pPage->pRaw->zData[pCell->iStart + L_HASH_CELL_SZ + pCell->nKey],(sxu32)pCell->nData);
+                       if( rc == SXRET_OK ){
+                               /* Append the new data */
+                               rc = SyBlobAppend(&sWorker,pData,(sxu32)nByte);
+                       }
+                       if( rc != UNQLITE_OK ){
+                               SyBlobRelease(&sWorker);
+                               return rc;
+                       }
+                       /* Space is available, transfer the cell */
+                       lhMoveLocalCell(pCell,iOfft,SyBlobData(&sWorker),(unqlite_int64)SyBlobLength(&sWorker));
+                       /* Restore cell space */
+                       lhRestoreSpace(pPage,iOldOfft,(sxu16)(L_HASH_CELL_SZ + pCell->nKey + iOld));
+                       /* All done */
+                       SyBlobRelease(&sWorker);
+               }
+               return UNQLITE_OK;
+       }
+       /* Point to the overflow page which hold the data */
+       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,pCell->iDataPage,&pOvfl);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Next overflow page in the chain */
+       SyBigEndianUnpack64(pOvfl->zData,&iOvfl);
+       /* Point to the end of the chunk */
+       zRaw = &pOvfl->zData[pCell->iDataOfft];
+       zRawEnd = &pOvfl->zData[pEngine->iPageSize];
+       nDatalen = pCell->nData;
+       nAvail = (sxu32)(zRawEnd - zRaw);
+       for(;;){
+               if( zRaw >= zRawEnd ){
+                       if( iOvfl == 0 ){
+                               /* Cant happen */
+                               pEngine->pIo->xErr(pEngine->pIo->pHandle,"Corrupt overflow page");
+                               return UNQLITE_CORRUPT;
+                       }
+                       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,iOvfl,&pNew);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Next overflow page on the chain */
+                       SyBigEndianUnpack64(pNew->zData,&iOvfl);
+                       /* Unref the previous overflow page */
+                       pEngine->pIo->xPageUnref(pOvfl);
+                       /* Point to the new chunk */
+                       zRaw = &pNew->zData[8];
+                       zRawEnd = &pNew->zData[pCell->pPage->pHash->iPageSize];
+                       nAvail = L_HASH_OVERFLOW_SIZE(pCell->pPage->pHash->iPageSize);
+                       pOvfl = pNew;
+               }
+               if( (sxu64)nAvail > nDatalen ){
+                       zRaw += nDatalen;
+                       break;
+               }else{
+                       nDatalen -= nAvail;
+               }
+               zRaw += nAvail;
+       }
+       /* Start the append process */
+       zPtr = (const unsigned char *)pData;
+       zEnd = &zPtr[nByte];
+       /* Acquire a writer lock */
+       rc = pEngine->pIo->xWrite(pOvfl);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       for(;;){
+               sxu32 nLen;
+               if( zPtr >= zEnd ){
+                       break;
+               }
+               if( zRaw >= zRawEnd ){
+                       /* Acquire a new page */
+                       rc = lhAcquirePage(pEngine,&pNew);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       rc = pEngine->pIo->xWrite(pNew);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Link */
+                       SyBigEndianPack64(pOvfl->zData,pNew->pgno);
+                       pEngine->pIo->xPageUnref(pOvfl);
+                       SyBigEndianPack64(pNew->zData,0); /* Next overflow page on the chain */
+                       pOvfl = pNew;
+                       zRaw = &pNew->zData[8];
+                       zRawEnd = &pNew->zData[pEngine->iPageSize];
+               }
+               nAvail = (sxu32)(zRawEnd-zRaw);
+               nLen = (sxu32)(zEnd-zPtr);
+               if( nLen > nAvail ){
+                       nLen = nAvail;
+               }
+               SyMemcpy((const void *)zPtr,(void *)zRaw,nLen);
+               /* Synchronize pointers */
+               zPtr += nLen;
+               zRaw += nLen;
+       }
+       /* Unref the last overflow page */
+       pEngine->pIo->xPageUnref(pOvfl);
+       /* Finally, update the cell header */
+       pCell->nData += nByte;
+       SyBigEndianPack64(&pPage->pRaw->zData[pCell->iStart + 4 /* Hash */ + 4 /* Key */],pCell->nData);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * A write privilege have been acquired on this page.
+ * Mark it as an empty page (No cells).
+ */
+static int lhSetEmptyPage(lhpage *pPage)
+{
+       unsigned char *zRaw = pPage->pRaw->zData;
+       lhphdr *pHeader = &pPage->sHdr;
+       sxu16 nByte;
+       int rc;
+       /* Acquire a writer lock */
+       rc = pPage->pHash->pIo->xWrite(pPage->pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Offset of the first cell */
+       SyBigEndianPack16(zRaw,0);
+       zRaw += 2;
+       /* Offset of the first free block */
+       pHeader->iFree = L_HASH_PAGE_HDR_SZ;
+       SyBigEndianPack16(zRaw,L_HASH_PAGE_HDR_SZ);
+       zRaw += 2;
+       /* Slave page number */
+       SyBigEndianPack64(zRaw,0);
+       zRaw += 8;
+       /* Fill the free block */
+       SyBigEndianPack16(zRaw,0); /* Offset of the next free block */
+       zRaw += 2;
+       nByte = (sxu16)L_HASH_MX_FREE_SPACE(pPage->pHash->iPageSize);
+       SyBigEndianPack16(zRaw,nByte);
+       pPage->nFree = nByte;
+       /* Do not add this page to the hot dirty list */
+       pPage->pHash->pIo->xDontMkHot(pPage->pRaw);
+       return UNQLITE_OK;
+}
+/* Forward declaration */
+static int lhSlaveStore(
+       lhpage *pPage,
+       const void *pKey,sxu32 nKeyLen,
+       const void *pData,unqlite_int64 nDataLen,
+       sxu32 nHash
+       );
+/*
+ * Store a cell and its payload in a given page.
+ */
+static int lhStoreCell(
+       lhpage *pPage, /* Target page */
+       const void *pKey,sxu32 nKeyLen, /* Payload: Key */
+       const void *pData,unqlite_int64 nDataLen, /* Payload: Data */
+       sxu32 nHash, /* Hash of the key */
+       int auto_append /* Auto append a slave page if full */
+       )
+{
+       lhash_kv_engine *pEngine = pPage->pHash;
+       int iNeedOvfl = 0; /* Need overflow page for this cell and its payload*/
+       lhcell *pCell;
+       sxu16 nOfft;
+       int rc;
+       /* Acquire a writer lock on this page first */
+       rc = pEngine->pIo->xWrite(pPage->pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Check for a free block  */
+       rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ+nKeyLen+nDataLen,&nOfft);
+       if( rc != UNQLITE_OK ){
+               /* Check for a free block to hold a single cell only (without payload) */
+               rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft);
+               if( rc != UNQLITE_OK ){
+                       if( !auto_append ){
+                               /* A split must be done */
+                               return UNQLITE_FULL;
+                       }else{
+                               /* Store this record in a slave page */
+                               rc = lhSlaveStore(pPage,pKey,nKeyLen,pData,nDataLen,nHash);
+                               return rc;
+                       }
+               }
+               iNeedOvfl = 1;
+       }
+       /* Allocate a new cell instance */
+       pCell = lhNewCell(pEngine,pPage);
+       if( pCell == 0 ){
+               pEngine->pIo->xErr(pEngine->pIo->pHandle,"KV store is running out of memory");
+               return UNQLITE_NOMEM;
+       }
+       /* Fill-in the structure */
+       pCell->iStart = nOfft;
+       pCell->nKey = nKeyLen;
+       pCell->nData = (sxu64)nDataLen;
+       pCell->nHash = nHash;
+       if( nKeyLen < 262144 /* 256 KB */ ){
+               /* Keep the key in-memory for fast lookup */
+               SyBlobAppend(&pCell->sKey,pKey,nKeyLen);
+       }
+       /* Link the cell */
+       rc = lhInstallCell(pCell);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Write the payload */
+       if( iNeedOvfl ){
+               rc = lhCellWriteOvflPayload(pCell,pKey,nKeyLen,pData,nDataLen,(const void *)0);
+               if( rc != UNQLITE_OK ){
+                       lhCellDiscard(pCell);
+                       return rc;
+               }
+       }else{
+               lhCellWriteLocalPayload(pCell,pKey,nKeyLen,pData,nDataLen);
+       }
+       /* Finally, Write the cell header */
+       lhCellWriteHeader(pCell);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Find a slave page capable of hosting the given amount.
+ */
+static int lhFindSlavePage(lhpage *pPage,sxu64 nAmount,sxu16 *pOfft,lhpage **ppSlave)
+{
+       lhash_kv_engine *pEngine = pPage->pHash;
+       lhpage *pMaster = pPage->pMaster;
+       lhpage *pSlave = pMaster->pSlave;
+       unqlite_page *pRaw;
+       lhpage *pNew;
+       sxu16 iOfft;
+       sxi32 i;
+       int rc;
+       /* Look for an already attached slave page */
+       for( i = 0 ; i < pMaster->iSlave ; ++i ){
+               /* Find a free chunk big enough */
+               sxu16 size = (sxu16)(L_HASH_CELL_SZ + nAmount);
+               rc = lhAllocateSpace(pSlave,size,&iOfft);
+               if( rc != UNQLITE_OK ){
+                       /* A space for cell header only */
+                       size = L_HASH_CELL_SZ;
+                       rc = lhAllocateSpace(pSlave,size,&iOfft);
+               }
+               if( rc == UNQLITE_OK ){
+                       /* All done */
+                       if( pOfft ){
+                               *pOfft = iOfft;
+                       }else{
+                               rc = lhRestoreSpace(pSlave, iOfft, size);
+                       }
+                       *ppSlave = pSlave;
+                       return rc;
+               }
+               /* Point to the next slave page */
+               pSlave = pSlave->pNextSlave;
+       }
+       /* Acquire a new slave page */
+       rc = lhAcquirePage(pEngine,&pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Last slave page */
+       pSlave = pMaster->pSlave;
+       if( pSlave == 0 ){
+               /* First slave page */
+               pSlave = pMaster;
+       }
+       /* Initialize the page */
+       pNew = lhNewPage(pEngine,pRaw,pMaster);
+       if( pNew == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Mark as an empty page */
+       rc = lhSetEmptyPage(pNew);
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       if( pOfft ){
+               /* Look for a free block */
+               if( UNQLITE_OK != lhAllocateSpace(pNew,L_HASH_CELL_SZ+nAmount,&iOfft) ){
+                       /* Cell header only */
+                       lhAllocateSpace(pNew,L_HASH_CELL_SZ,&iOfft); /* Never fail */
+               }       
+               *pOfft = iOfft;
+       }
+       /* Link this page to the previous slave page */
+       rc = pEngine->pIo->xWrite(pSlave->pRaw);
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       /* Reflect in the page header */
+       SyBigEndianPack64(&pSlave->pRaw->zData[2/*Cell offset*/+2/*Free block offset*/],pRaw->pgno);
+       pSlave->sHdr.iSlave = pRaw->pgno;
+       /* All done */
+       *ppSlave = pNew;
+       return UNQLITE_OK;
+fail:
+       pEngine->pIo->xPageUnref(pNew->pRaw); /* pNew will be released in this call */
+       return rc;
+
+}
+/*
+ * Perform a store operation in a slave page.
+ */
+static int lhSlaveStore(
+       lhpage *pPage, /* Master page */
+       const void *pKey,sxu32 nKeyLen, /* Payload: key */
+       const void *pData,unqlite_int64 nDataLen, /* Payload: data */
+       sxu32 nHash /* Hash of the key */
+       )
+{
+       lhpage *pSlave;
+       int rc;
+       /* Find a slave page */
+       rc = lhFindSlavePage(pPage,nKeyLen + nDataLen,0,&pSlave);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Perform the insertion in the slave page */
+       rc = lhStoreCell(pSlave,pKey,nKeyLen,pData,nDataLen,nHash,1);
+       return rc;
+}
+/*
+ * Transfer a cell to a new page (either a master or slave).
+ */
+static int lhTransferCell(lhcell *pTarget,lhpage *pPage)
+{
+       lhcell *pCell;
+       sxu16 nOfft;
+       int rc;
+       /* Check for a free block to hold a single cell only */
+       rc = lhAllocateSpace(pPage,L_HASH_CELL_SZ,&nOfft);
+       if( rc != UNQLITE_OK ){
+               /* Store in a slave page */
+               rc = lhFindSlavePage(pPage,L_HASH_CELL_SZ,&nOfft,&pPage);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+       }
+       /* Allocate a new cell instance */
+       pCell = lhNewCell(pPage->pHash,pPage);
+       if( pCell == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Fill-in the structure */
+       pCell->iStart = nOfft;
+       pCell->nData  = pTarget->nData;
+       pCell->nKey   = pTarget->nKey;
+       pCell->iOvfl  = pTarget->iOvfl;
+       pCell->iDataOfft = pTarget->iDataOfft;
+       pCell->iDataPage = pTarget->iDataPage;
+       pCell->nHash = pTarget->nHash;
+       SyBlobDup(&pTarget->sKey,&pCell->sKey);
+       /* Link the cell */
+       rc = lhInstallCell(pCell);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Finally, Write the cell header */
+       lhCellWriteHeader(pCell);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Perform a page split.
+ */
+static int lhPageSplit(
+       lhpage *pOld,      /* Page to be split */
+       lhpage *pNew,      /* New page */
+       pgno split_bucket, /* Current split bucket */
+       pgno high_mask     /* High mask (Max split bucket - 1) */
+       )
+{
+       lhcell *pCell,*pNext;
+       SyBlob sWorker;
+       pgno iBucket;
+       int rc; 
+       SyBlobInit(&sWorker,&pOld->pHash->sAllocator);
+       /* Perform the split */
+       pCell = pOld->pList;
+       for( ;; ){
+               if( pCell == 0 ){
+                       /* No more cells */
+                       break;
+               }
+               /* Obtain the new logical bucket */
+               iBucket = pCell->nHash & high_mask;
+               pNext =  pCell->pNext;
+               if( iBucket != split_bucket){
+                       rc = UNQLITE_OK;
+                       if( pCell->iOvfl ){
+                               /* Transfer the cell only */
+                               rc = lhTransferCell(pCell,pNew);
+                       }else{
+                               /* Transfer the cell and its payload */
+                               SyBlobReset(&sWorker);
+                               if( SyBlobLength(&pCell->sKey) < 1 ){
+                                       /* Consume the key */
+                                       rc = lhConsumeCellkey(pCell,unqliteDataConsumer,&pCell->sKey,0);
+                                       if( rc != UNQLITE_OK ){
+                                               goto fail;
+                                       }
+                               }
+                               /* Consume the data (Very small data < 65k) */
+                               rc = lhConsumeCellData(pCell,unqliteDataConsumer,&sWorker);
+                               if( rc != UNQLITE_OK ){
+                                       goto fail;
+                               }
+                               /* Perform the transfer */
+                               rc = lhStoreCell(
+                                       pNew,
+                                       SyBlobData(&pCell->sKey),(int)SyBlobLength(&pCell->sKey),
+                                       SyBlobData(&sWorker),SyBlobLength(&sWorker),
+                                       pCell->nHash,
+                                       1
+                                       );
+                       }
+                       if( rc != UNQLITE_OK ){
+                               goto fail;
+                       }
+                       /* Discard the cell from the old page */
+                       lhUnlinkCell(pCell);
+               }
+               /* Point to the next cell */
+               pCell = pNext;
+       }
+       /* All done */
+       rc = UNQLITE_OK;
+fail:
+       SyBlobRelease(&sWorker);
+       return rc;
+}
+/*
+ * Perform the infamous linear hash split operation.
+ */
+static int lhSplit(lhpage *pTarget,int *pRetry)
+{
+       lhash_kv_engine *pEngine = pTarget->pHash;
+       lhash_bmap_rec *pRec;
+       lhpage *pOld,*pNew;
+       unqlite_page *pRaw;
+       int rc;
+       /* Get the real page number of the bucket to split */
+       pRec = lhMapFindBucket(pEngine,pEngine->split_bucket);
+       if( pRec == 0 ){
+               /* Can't happen */
+               return UNQLITE_CORRUPT;
+       }
+       /* Load the page to be split */
+       rc = lhLoadPage(pEngine,pRec->iReal,0,&pOld,0);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Request a new page */
+       rc = lhAcquirePage(pEngine,&pRaw);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Initialize the page */
+       pNew = lhNewPage(pEngine,pRaw,0);
+       if( pNew == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Mark as an empty page */
+       rc = lhSetEmptyPage(pNew);
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       /* Install and write the logical map record */
+       rc = lhMapWriteRecord(pEngine,
+               pEngine->split_bucket + pEngine->max_split_bucket,
+               pRaw->pgno
+               );
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       if( pTarget->pRaw->pgno == pOld->pRaw->pgno ){
+               *pRetry = 1;
+       }
+       /* Perform the split */
+       rc = lhPageSplit(pOld,pNew,pEngine->split_bucket,pEngine->nmax_split_nucket - 1);
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       /* Update the database header */
+       pEngine->split_bucket++;
+       /* Acquire a writer lock on the first page */
+       rc = pEngine->pIo->xWrite(pEngine->pHeader);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( pEngine->split_bucket >= pEngine->max_split_bucket ){
+               /* Increment the generation number */
+               pEngine->split_bucket = 0;
+               pEngine->max_split_bucket = pEngine->nmax_split_nucket;
+               pEngine->nmax_split_nucket <<= 1;
+               if( !pEngine->nmax_split_nucket ){
+                       /* If this happen to your installation, please tell us <chm@symisc.net> */
+                       pEngine->pIo->xErr(pEngine->pIo->pHandle,"Database page (64-bit integer) limit reached");
+                       return UNQLITE_LIMIT;
+               }
+               /* Reflect in the page header */
+               SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket);
+               SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/+8/*Split bucket*/],pEngine->max_split_bucket);
+       }else{
+               /* Modify only the split bucket */
+               SyBigEndianPack64(&pEngine->pHeader->zData[4/*Magic*/+4/*Hash*/+8/*Free list*/],pEngine->split_bucket);
+       }
+       /* All done */
+       return UNQLITE_OK;
+fail:
+       pEngine->pIo->xPageUnref(pNew->pRaw);
+       return rc;
+}
+/*
+ * Store a record in the target page.
+ */
+static int lhRecordInstall(
+         lhpage *pPage, /* Target page */
+         sxu32 nHash,   /* Hash of the key */
+         const void *pKey,sxu32 nKeyLen,          /* Payload: Key */
+         const void *pData,unqlite_int64 nDataLen /* Payload: Data */
+         )
+{
+       int rc;
+       rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,0);
+       if( rc == UNQLITE_FULL ){
+               int do_retry = 0;
+               /* Split */
+               rc = lhSplit(pPage,&do_retry);
+               if( rc == UNQLITE_OK ){
+                       if( do_retry ){
+                               /* Re-calculate logical bucket number */
+                               return SXERR_RETRY;
+                       }
+                       /* Perform the store */
+                       rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1);
+               }
+       }
+       return rc;
+}
+/*
+ * Insert a record (Either overwrite or append operation) in our database.
+ */
+static int lh_record_insert(
+         unqlite_kv_engine *pKv,         /* KV store */
+         const void *pKey,sxu32 nKeyLen, /* Payload: Key */
+         const void *pData,unqlite_int64 nDataLen, /* Payload: data */
+         int is_append /* True for an append operation */
+         )
+{
+       lhash_kv_engine *pEngine = (lhash_kv_engine *)pKv;
+       lhash_bmap_rec *pRec;
+       unqlite_page *pRaw;
+       lhpage *pPage;
+       lhcell *pCell;
+       pgno iBucket;
+       sxu32 nHash;
+       int iCnt;
+       int rc;
+
+       /* Acquire the first page (DB hash Header) so that everything gets loaded autmatically */
+       rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       iCnt = 0;
+       /* Compute the hash of the key first */
+       nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
+retry:
+       /* Extract the logical bucket number */
+       iBucket = nHash & (pEngine->nmax_split_nucket - 1);
+       if( iBucket >= pEngine->split_bucket + pEngine->max_split_bucket ){
+               /* Low mask */
+               iBucket = nHash & (pEngine->max_split_bucket - 1);
+       }
+       /* Map the logical bucket number to real page number */
+       pRec = lhMapFindBucket(pEngine,iBucket);
+       if( pRec == 0 ){
+               /* Request a new page */
+               rc = lhAcquirePage(pEngine,&pRaw);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Initialize the page */
+               pPage = lhNewPage(pEngine,pRaw,0);
+               if( pPage == 0 ){
+                       return UNQLITE_NOMEM;
+               }
+               /* Mark as an empty page */
+               rc = lhSetEmptyPage(pPage);
+               if( rc != UNQLITE_OK ){
+                       pEngine->pIo->xPageUnref(pRaw); /* pPage will be released during this call */
+                       return rc;
+               }
+               /* Store the cell */
+               rc = lhStoreCell(pPage,pKey,nKeyLen,pData,nDataLen,nHash,1);
+               if( rc == UNQLITE_OK ){
+                       /* Install and write the logical map record */
+                       rc = lhMapWriteRecord(pEngine,iBucket,pRaw->pgno);
+               }
+               pEngine->pIo->xPageUnref(pRaw);
+               return rc;
+       }else{
+               /* Load the page */
+               rc = lhLoadPage(pEngine,pRec->iReal,0,&pPage,0);
+               if( rc != UNQLITE_OK ){
+                       /* IO error, unlikely scenario */
+                       return rc;
+               }
+               /* Do not add this page to the hot dirty list */
+               pEngine->pIo->xDontMkHot(pPage->pRaw);
+               /* Lookup for the cell */
+               pCell = lhFindCell(pPage,pKey,(sxu32)nKeyLen,nHash);
+               if( pCell == 0 ){
+                       /* Create the record */
+                       rc = lhRecordInstall(pPage,nHash,pKey,nKeyLen,pData,nDataLen);
+                       if( rc == SXERR_RETRY && iCnt++ < 2 ){
+                               rc = UNQLITE_OK;
+                               goto retry;
+                       }
+               }else{
+                       if( is_append ){
+                               /* Append operation */
+                               rc = lhRecordAppend(pCell,pData,nDataLen);
+                       }else{
+                               /* Overwrite old value */
+                               rc = lhRecordOverwrite(pCell,pData,nDataLen);
+                       }
+               }
+               pEngine->pIo->xPageUnref(pPage->pRaw);
+       }
+       return rc;
+}
+/*
+ * Replace method.
+ */
+static int lhash_kv_replace(
+         unqlite_kv_engine *pKv,
+         const void *pKey,int nKeyLen,
+         const void *pData,unqlite_int64 nDataLen
+         )
+{
+       int rc;
+       rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,0);
+       return rc;
+}
+/*
+ * Append method.
+ */
+static int lhash_kv_append(
+         unqlite_kv_engine *pKv,
+         const void *pKey,int nKeyLen,
+         const void *pData,unqlite_int64 nDataLen
+         )
+{
+       int rc;
+       rc = lh_record_insert(pKv,pKey,(sxu32)nKeyLen,pData,nDataLen,1);
+       return rc;
+}
+/*
+ * Write the hash header (Page one).
+ */
+static int lhash_write_header(lhash_kv_engine *pEngine,unqlite_page *pHeader)
+{
+       unsigned char *zRaw = pHeader->zData;
+       lhash_bmap_page *pMap;
+
+       pEngine->pHeader = pHeader;
+       /* 4 byte magic number */
+       SyBigEndianPack32(zRaw,pEngine->nMagic);
+       zRaw += 4;
+       /* 4 byte hash value to identify a valid hash function */
+       SyBigEndianPack32(zRaw,pEngine->xHash(L_HASH_WORD,sizeof(L_HASH_WORD)-1));
+       zRaw += 4;
+       /* List of free pages: Empty */
+       SyBigEndianPack64(zRaw,0);
+       zRaw += 8;
+       /* Current split bucket */
+       SyBigEndianPack64(zRaw,pEngine->split_bucket);
+       zRaw += 8;
+       /* Maximum split bucket */
+       SyBigEndianPack64(zRaw,pEngine->max_split_bucket);
+       zRaw += 8;
+       /* Initialiaze the bucket map */
+       pMap = &pEngine->sPageMap;
+       /* Fill in the structure */
+       pMap->iNum = pHeader->pgno;
+       /* Next page in the bucket map */
+       SyBigEndianPack64(zRaw,0);
+       zRaw += 8;
+       /* Total number of records in the bucket map */
+       SyBigEndianPack32(zRaw,0);
+       zRaw += 4;
+       pMap->iPtr = (sxu16)(zRaw - pHeader->zData);
+       /* All done */
+       return UNQLITE_OK;
+ }
+/*
+ * Exported: xOpen() method.
+ */
+static int lhash_kv_open(unqlite_kv_engine *pEngine,pgno dbSize)
+{
+       lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
+       unqlite_page *pHeader;
+       int rc;
+       if( dbSize < 1 ){
+               /* A new database, create the header */
+               rc = pEngine->pIo->xNew(pEngine->pIo->pHandle,&pHeader);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Acquire a writer lock */
+               rc = pEngine->pIo->xWrite(pHeader);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Write the hash header */
+               rc = lhash_write_header(pHash,pHeader);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+       }else{
+               /* Acquire the page one of the database */
+               rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,&pHeader);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Read the database header */
+               rc = lhash_read_header(pHash,pHeader);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Release a master or slave page. (xUnpin callback).
+ */
+static void lhash_page_release(void *pUserData)
+{
+       lhpage *pPage = (lhpage *)pUserData;
+       lhash_kv_engine *pEngine = pPage->pHash;
+       lhcell *pNext,*pCell = pPage->pList;
+       unqlite_page *pRaw = pPage->pRaw;
+       sxu32 n;
+       /* Drop in-memory cells */
+       for( n = 0 ; n < pPage->nCell ; ++n ){
+               pNext = pCell->pNext;
+               SyBlobRelease(&pCell->sKey);
+               /* Release the cell instance */
+               SyMemBackendPoolFree(&pEngine->sAllocator,(void *)pCell);
+               /* Point to the next entry */
+               pCell = pNext;
+       }
+       if( pPage->apCell ){
+               /* Release the cell table */
+               SyMemBackendFree(&pEngine->sAllocator,(void *)pPage->apCell);
+       }
+       /* Finally, release the whole page */
+       SyMemBackendPoolFree(&pEngine->sAllocator,pPage);
+       pRaw->pUserData = 0;
+}
+/*
+ * Default hash function (DJB).
+ */
+static sxu32 lhash_bin_hash(const void *pSrc,sxu32 nLen)
+{
+       register unsigned char *zIn = (unsigned char *)pSrc;
+       unsigned char *zEnd;
+       sxu32 nH = 5381;
+       if( nLen > 2048 /* 2K */ ){
+               nLen = 2048;
+       }
+       zEnd = &zIn[nLen];
+       for(;;){
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+       }       
+       return nH;
+}
+/*
+ * Exported: xInit() method.
+ * Initialize the Key value storage engine.
+ */
+static int lhash_kv_init(unqlite_kv_engine *pEngine,int iPageSize)
+{
+       lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
+       int rc;
+
+       /* This structure is always zeroed, go to the initialization directly */
+       SyMemBackendInitFromParent(&pHash->sAllocator,unqliteExportMemBackend());
+//#if defined(UNQLITE_ENABLE_THREADS)
+//     /* Already protected by the upper layers */
+//     SyMemBackendDisbaleMutexing(&pHash->sAllocator);
+//#endif
+       pHash->iPageSize = iPageSize;
+       /* Default hash function */
+       pHash->xHash = lhash_bin_hash;
+       /* Default comparison function */
+       pHash->xCmp = SyMemcmp;
+       /* Allocate a new record map */
+       pHash->nBuckSize = 32;
+       pHash->apMap = (lhash_bmap_rec **)SyMemBackendAlloc(&pHash->sAllocator,pHash->nBuckSize *sizeof(lhash_bmap_rec *));
+       if( pHash->apMap == 0 ){
+               rc = UNQLITE_NOMEM;
+               goto err;
+       }
+       /* Zero the table */
+       SyZero(pHash->apMap,pHash->nBuckSize * sizeof(lhash_bmap_rec *));
+       /* Linear hashing components */
+       pHash->split_bucket = 0; /* Logical not real bucket number */
+       pHash->max_split_bucket = 1;
+       pHash->nmax_split_nucket = 2;
+       pHash->nMagic = L_HASH_MAGIC;
+       /* Install the cache unpin and reload callbacks */
+       pHash->pIo->xSetUnpin(pHash->pIo->pHandle,lhash_page_release);
+       pHash->pIo->xSetReload(pHash->pIo->pHandle,lhash_page_release);
+       return UNQLITE_OK;
+err:
+       SyMemBackendRelease(&pHash->sAllocator);
+       return rc;
+}
+/*
+ * Exported: xRelease() method.
+ * Release the Key value storage engine.
+ */
+static void lhash_kv_release(unqlite_kv_engine *pEngine)
+{
+       lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
+       /* Release the private memory backend */
+       SyMemBackendRelease(&pHash->sAllocator);
+}
+/*
+ *  Exported: xConfig() method.
+ *  Configure the linear hash KV store.
+ */
+static int lhash_kv_config(unqlite_kv_engine *pEngine,int op,va_list ap)
+{
+       lhash_kv_engine *pHash = (lhash_kv_engine *)pEngine;
+       int rc = UNQLITE_OK;
+       switch(op){
+       case UNQLITE_KV_CONFIG_HASH_FUNC: {
+               /* Default hash function */
+               if( pHash->nBuckRec > 0 ){
+                       /* Locked operation */
+                       rc = UNQLITE_LOCKED;
+               }else{
+                       ProcHash xHash = va_arg(ap,ProcHash);
+                       if( xHash ){
+                               pHash->xHash = xHash;
+                       }
+               }
+               break;
+                                                                         }
+       case UNQLITE_KV_CONFIG_CMP_FUNC: {
+               /* Default comparison function */
+               ProcCmp xCmp = va_arg(ap,ProcCmp);
+               if( xCmp ){
+                       pHash->xCmp  = xCmp;
+               }
+               break;
+                                                                        }
+       default:
+               /* Unknown OP */
+               rc = UNQLITE_UNKNOWN;
+               break;
+       }
+       return rc;
+}
+/*
+ * Each public cursor is identified by an instance of this structure.
+ */
+typedef struct lhash_kv_cursor lhash_kv_cursor;
+struct lhash_kv_cursor
+{
+       unqlite_kv_engine *pStore; /* Must be first */
+       /* Private fields */
+       int iState;           /* Current state of the cursor */
+       int is_first;         /* True to read the database header */
+       lhcell *pCell;        /* Current cell we are processing */
+       unqlite_page *pRaw;   /* Raw disk page */
+       lhash_bmap_rec *pRec; /* Logical to real bucket map */
+};
+/* 
+ * Possible state of the cursor
+ */
+#define L_HASH_CURSOR_STATE_NEXT_PAGE 1 /* Next page in the list */
+#define L_HASH_CURSOR_STATE_CELL      2 /* Processing Cell */
+#define L_HASH_CURSOR_STATE_DONE      3 /* Cursor does not point to anything */
+/*
+ * Initialize the cursor.
+ */
+static void lhInitCursor(unqlite_kv_cursor *pPtr)
+{
+        lhash_kv_engine *pEngine = (lhash_kv_engine *)pPtr->pStore;
+        lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
+        /* Init */
+        pCur->iState = L_HASH_CURSOR_STATE_NEXT_PAGE;
+        pCur->pCell = 0;
+        pCur->pRec = pEngine->pFirst;
+        pCur->pRaw = 0;
+        pCur->is_first = 1;
+}
+/*
+ * Point to the next page on the database.
+ */
+static int lhCursorNextPage(lhash_kv_cursor *pPtr)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
+       lhash_bmap_rec *pRec;
+       lhpage *pPage;
+       int rc;
+       for(;;){
+               pRec = pCur->pRec;
+               if( pRec == 0 ){
+                       pCur->iState = L_HASH_CURSOR_STATE_DONE;
+                       return UNQLITE_DONE;
+               }
+               if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){
+                       /* Unref this page */
+                       pCur->pStore->pIo->xPageUnref(pPtr->pRaw);
+                       pPtr->pRaw = 0;
+               }
+               /* Advance the map cursor */
+               pCur->pRec = pRec->pPrev; /* Not a bug, reverse link */
+               /* Load the next page on the list */
+               rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               if( pPage->pList ){
+                       /* Reflect the change */
+                       pCur->pCell = pPage->pList;
+                       pCur->iState = L_HASH_CURSOR_STATE_CELL;
+                       pCur->pRaw = pPage->pRaw;
+                       break;
+               }
+               /* Empty page, discard this page and continue */
+               pPage->pHash->pIo->xPageUnref(pPage->pRaw);
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Point to the previous page on the database.
+ */
+static int lhCursorPrevPage(lhash_kv_cursor *pPtr)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
+       lhash_bmap_rec *pRec;
+       lhpage *pPage;
+       int rc;
+       for(;;){
+               pRec = pCur->pRec;
+               if( pRec == 0 ){
+                       pCur->iState = L_HASH_CURSOR_STATE_DONE;
+                       return UNQLITE_DONE;
+               }
+               if( pPtr->iState == L_HASH_CURSOR_STATE_CELL && pPtr->pRaw ){
+                       /* Unref this page */
+                       pCur->pStore->pIo->xPageUnref(pPtr->pRaw);
+                       pPtr->pRaw = 0;
+               }
+               /* Advance the map cursor */
+               pCur->pRec = pRec->pNext; /* Not a bug, reverse link */
+               /* Load the previous page on the list */
+               rc = lhLoadPage((lhash_kv_engine *)pCur->pStore,pRec->iReal,0,&pPage,0);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               if( pPage->pFirst ){
+                       /* Reflect the change */
+                       pCur->pCell = pPage->pFirst;
+                       pCur->iState = L_HASH_CURSOR_STATE_CELL;
+                       pCur->pRaw = pPage->pRaw;
+                       break;
+               }
+               /* Discard this page and continue */
+               pPage->pHash->pIo->xPageUnref(pPage->pRaw);
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Is a valid cursor.
+ */
+static int lhCursorValid(unqlite_kv_cursor *pPtr)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pPtr;
+       return (pCur->iState == L_HASH_CURSOR_STATE_CELL) && pCur->pCell;
+}
+/*
+ * Point to the first record.
+ */
+static int lhCursorFirst(unqlite_kv_cursor *pCursor)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore;
+       int rc;
+       if( pCur->is_first ){
+               /* Read the database header first */
+               rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               pCur->is_first = 0;
+       }
+       /* Point to the first map record */
+       pCur->pRec = pEngine->pFirst;
+       /* Load the cells */
+       rc = lhCursorNextPage(pCur);
+       return rc;
+}
+/*
+ * Point to the last record.
+ */
+static int lhCursorLast(unqlite_kv_cursor *pCursor)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhash_kv_engine *pEngine = (lhash_kv_engine *)pCursor->pStore;
+       int rc;
+       if( pCur->is_first ){
+               /* Read the database header first */
+               rc = pEngine->pIo->xGet(pEngine->pIo->pHandle,1,0);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               pCur->is_first = 0;
+       }
+       /* Point to the last map record */
+       pCur->pRec = pEngine->pList;
+       /* Load the cells */
+       rc = lhCursorPrevPage(pCur);
+       return rc;
+}
+/*
+ * Reset the cursor.
+ */
+static void lhCursorReset(unqlite_kv_cursor *pCursor)
+{
+       lhCursorFirst(pCursor);
+}
+/*
+ * Point to the next record.
+ */
+static int lhCursorNext(unqlite_kv_cursor *pCursor)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhcell *pCell;
+       int rc;
+       if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
+               /* Load the cells of the next page  */
+               rc = lhCursorNextPage(pCur);
+               return rc;
+       }
+       pCell = pCur->pCell;
+       pCur->pCell = pCell->pNext;
+       if( pCur->pCell == 0 ){
+               /* Load the cells of the next page  */
+               rc = lhCursorNextPage(pCur);
+               return rc;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Point to the previous record.
+ */
+static int lhCursorPrev(unqlite_kv_cursor *pCursor)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhcell *pCell;
+       int rc;
+       if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
+               /* Load the cells of the previous page  */
+               rc = lhCursorPrevPage(pCur);
+               return rc;
+       }
+       pCell = pCur->pCell;
+       pCur->pCell = pCell->pPrev;
+       if( pCur->pCell == 0 ){
+               /* Load the cells of the previous page  */
+               rc = lhCursorPrevPage(pCur);
+               return rc;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Return key length.
+ */
+static int lhCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhcell *pCell;
+       
+       if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
+               /* Invalid state */
+               return UNQLITE_INVALID;
+       }
+       /* Point to the target cell */
+       pCell = pCur->pCell;
+       /* Return key length */
+       *pLen = (int)pCell->nKey;
+       return UNQLITE_OK;
+}
+/*
+ * Return data length.
+ */
+static int lhCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhcell *pCell;
+       
+       if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
+               /* Invalid state */
+               return UNQLITE_INVALID;
+       }
+       /* Point to the target cell */
+       pCell = pCur->pCell;
+       /* Return data length */
+       *pLen = (unqlite_int64)pCell->nData;
+       return UNQLITE_OK;
+}
+/*
+ * Consume the key.
+ */
+static int lhCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhcell *pCell;
+       int rc;
+       if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
+               /* Invalid state */
+               return UNQLITE_INVALID;
+       }
+       /* Point to the target cell */
+       pCell = pCur->pCell;
+       if( SyBlobLength(&pCell->sKey) > 0 ){
+               /* Consume the key directly */
+               rc = xConsumer(SyBlobData(&pCell->sKey),SyBlobLength(&pCell->sKey),pUserData);
+       }else{
+               /* Very large key */
+               rc = lhConsumeCellkey(pCell,xConsumer,pUserData,0);
+       }
+       return rc;
+}
+/*
+ * Consume the data.
+ */
+static int lhCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhcell *pCell;
+       int rc;
+       if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
+               /* Invalid state */
+               return UNQLITE_INVALID;
+       }
+       /* Point to the target cell */
+       pCell = pCur->pCell;
+       /* Consume the data */
+       rc = lhConsumeCellData(pCell,xConsumer,pUserData);
+       return rc;
+}
+/*
+ * Find a partiuclar record.
+ */
+static int lhCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       int rc;
+       /* Perform a lookup */
+       rc = lhRecordLookup((lhash_kv_engine *)pCur->pStore,pKey,nByte,&pCur->pCell);
+       if( rc != UNQLITE_OK ){
+               SXUNUSED(iPos);
+               pCur->pCell = 0;
+               pCur->iState = L_HASH_CURSOR_STATE_DONE;
+               return rc;
+       }
+       pCur->iState = L_HASH_CURSOR_STATE_CELL;
+       return UNQLITE_OK;
+}
+/*
+ * Remove a particular record.
+ */
+static int lhCursorDelete(unqlite_kv_cursor *pCursor)
+{
+       lhash_kv_cursor *pCur = (lhash_kv_cursor *)pCursor;
+       lhcell *pCell;
+       int rc;
+       if( pCur->iState != L_HASH_CURSOR_STATE_CELL || pCur->pCell == 0 ){
+               /* Invalid state */
+               return UNQLITE_INVALID;
+       }
+       /* Point to the target cell  */
+       pCell = pCur->pCell;
+       /* Point to the next entry */
+       pCur->pCell = pCell->pNext;
+       /* Perform the deletion */
+       rc = lhRecordRemove(pCell);
+       return rc;
+}
+/*
+ * Export the linear-hash storage engine.
+ */
+UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportDiskKvStorage(void)
+{
+       static const unqlite_kv_methods sDiskStore = {
+               "hash",                     /* zName */
+               sizeof(lhash_kv_engine),    /* szKv */
+               sizeof(lhash_kv_cursor),    /* szCursor */
+               1,                          /* iVersion */
+               lhash_kv_init,              /* xInit */
+               lhash_kv_release,           /* xRelease */
+               lhash_kv_config,            /* xConfig */
+               lhash_kv_open,              /* xOpen */
+               lhash_kv_replace,           /* xReplace */
+               lhash_kv_append,            /* xAppend */
+               lhInitCursor,               /* xCursorInit */
+               lhCursorSeek,               /* xSeek */
+               lhCursorFirst,              /* xFirst */
+               lhCursorLast,               /* xLast */
+               lhCursorValid,              /* xValid */
+               lhCursorNext,               /* xNext */
+               lhCursorPrev,               /* xPrev */
+               lhCursorDelete,             /* xDelete */
+               lhCursorKeyLength,          /* xKeyLength */
+               lhCursorKey,                /* xKey */
+               lhCursorDataLength,         /* xDataLength */
+               lhCursorData,               /* xData */
+               lhCursorReset,              /* xReset */
+               0                           /* xRelease */                        
+       };
+       return &sDiskStore;
+}
+/*
+ * ----------------------------------------------------------
+ * File: mem_kv.c
+ * MD5: 32e2610c95f53038114d9566f0d0489e
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: mem_kv.c v1.7 Win7 2012-11-28 01:41 stable <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* 
+ * This file implements an in-memory key value storage engine for unQLite.
+ * Note that this storage engine does not support transactions.
+ *
+ * Normaly, I (chm@symisc.net) planned to implement a red-black tree
+ * which is suitable for this kind of operation, but due to the lack
+ * of time, I decided to implement a tunned hashtable which everybody
+ * know works very well for this kind of operation.
+ * Again, I insist on a red-black tree implementation for future version
+ * of Unqlite.
+ */
+/* Forward declaration */
+typedef struct mem_hash_kv_engine mem_hash_kv_engine;
+/*
+ * Each record is storead in an instance of the following structure.
+ */
+typedef struct mem_hash_record mem_hash_record;
+struct mem_hash_record
+{
+       mem_hash_kv_engine *pEngine;    /* Storage engine */
+       sxu32 nHash;                    /* Hash of the key */
+       const void *pKey;               /* Key */
+       sxu32 nKeyLen;                  /* Key size (Max 1GB) */
+       const void *pData;              /* Data */
+       sxu32 nDataLen;                 /* Data length (Max 4GB) */
+       mem_hash_record *pNext,*pPrev;  /* Link to other records */
+       mem_hash_record *pNextHash,*pPrevHash; /* Collision link */
+};
+/*
+ * Each in-memory KV engine is represented by an instance
+ * of the following structure.
+ */
+struct mem_hash_kv_engine
+{
+       const unqlite_kv_io *pIo; /* IO methods: MUST be first */
+       /* Private data */
+       SyMemBackend sAlloc;        /* Private memory allocator */
+       ProcHash    xHash;          /* Default hash function */
+       ProcCmp     xCmp;           /* Default comparison function */
+       sxu32 nRecord;              /* Total number of records  */
+       sxu32 nBucket;              /* Bucket size: Must be a power of two */
+       mem_hash_record **apBucket; /* Hash bucket */
+       mem_hash_record *pFirst;    /* First inserted entry */
+       mem_hash_record *pLast;     /* Last inserted entry */
+};
+/*
+ * Allocate a new hash record.
+ */
+static mem_hash_record * MemHashNewRecord(
+       mem_hash_kv_engine *pEngine,
+       const void *pKey,int nKey,
+       const void *pData,unqlite_int64 nData,
+       sxu32 nHash
+       )
+{
+       SyMemBackend *pAlloc = &pEngine->sAlloc;
+       mem_hash_record *pRecord;
+       void *pDupData;
+       sxu32 nByte;
+       char *zPtr;
+       
+       /* Total number of bytes to alloc */
+       nByte = sizeof(mem_hash_record) + nKey;
+       /* Allocate a new instance */
+       pRecord = (mem_hash_record *)SyMemBackendAlloc(pAlloc,nByte);
+       if( pRecord == 0 ){
+               return 0;
+       }
+       pDupData = (void *)SyMemBackendAlloc(pAlloc,(sxu32)nData);
+       if( pDupData == 0 ){
+               SyMemBackendFree(pAlloc,pRecord);
+               return 0;
+       }
+       zPtr = (char *)pRecord;
+       zPtr += sizeof(mem_hash_record);
+       /* Zero the structure */
+       SyZero(pRecord,sizeof(mem_hash_record));
+       /* Fill in the structure */
+       pRecord->pEngine = pEngine;
+       pRecord->nDataLen = (sxu32)nData;
+       pRecord->nKeyLen = (sxu32)nKey;
+       pRecord->nHash = nHash;
+       SyMemcpy(pKey,zPtr,pRecord->nKeyLen);
+       pRecord->pKey = (const void *)zPtr;
+       SyMemcpy(pData,pDupData,pRecord->nDataLen);
+       pRecord->pData = pDupData;
+       /* All done */
+       return pRecord;
+}
+/*
+ * Install a given record in the hashtable.
+ */
+static void MemHashLinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pRecord)
+{
+       sxu32 nBucket = pRecord->nHash & (pEngine->nBucket - 1);
+       pRecord->pNextHash = pEngine->apBucket[nBucket];
+       if( pEngine->apBucket[nBucket] ){
+               pEngine->apBucket[nBucket]->pPrevHash = pRecord;
+       }
+       pEngine->apBucket[nBucket] = pRecord;
+       if( pEngine->pFirst == 0 ){
+               pEngine->pFirst = pEngine->pLast = pRecord;
+       }else{
+               MACRO_LD_PUSH(pEngine->pLast,pRecord);
+       }
+       pEngine->nRecord++;
+}
+/*
+ * Unlink a given record from the hashtable.
+ */
+static void MemHashUnlinkRecord(mem_hash_kv_engine *pEngine,mem_hash_record *pEntry)
+{
+       sxu32 nBucket = pEntry->nHash & (pEngine->nBucket - 1);
+       SyMemBackend *pAlloc = &pEngine->sAlloc;
+       if( pEntry->pPrevHash == 0 ){
+               pEngine->apBucket[nBucket] = pEntry->pNextHash;
+       }else{
+               pEntry->pPrevHash->pNextHash = pEntry->pNextHash;
+       }
+       if( pEntry->pNextHash ){
+               pEntry->pNextHash->pPrevHash = pEntry->pPrevHash;
+       }
+       MACRO_LD_REMOVE(pEngine->pLast,pEntry);
+       if( pEntry == pEngine->pFirst ){
+               pEngine->pFirst = pEntry->pPrev;
+       }
+       pEngine->nRecord--;
+       /* Release the entry */
+       SyMemBackendFree(pAlloc,(void *)pEntry->pData);
+       SyMemBackendFree(pAlloc,pEntry); /* Key is also stored here */
+}
+/*
+ * Perform a lookup for a given entry.
+ */
+static mem_hash_record * MemHashGetEntry(
+       mem_hash_kv_engine *pEngine,
+       const void *pKey,int nKeyLen
+       )
+{
+       mem_hash_record *pEntry;
+       sxu32 nHash,nBucket;
+       /* Hash the entry */
+       nHash = pEngine->xHash(pKey,(sxu32)nKeyLen);
+       nBucket = nHash & (pEngine->nBucket - 1);
+       pEntry = pEngine->apBucket[nBucket];
+       for(;;){
+               if( pEntry == 0 ){
+                       break;
+               }
+               if( pEntry->nHash == nHash && pEntry->nKeyLen == (sxu32)nKeyLen && 
+                       pEngine->xCmp(pEntry->pKey,pKey,pEntry->nKeyLen) == 0 ){
+                               return pEntry;
+               }
+               pEntry = pEntry->pNextHash;
+       }
+       /* No such entry */
+       return 0;
+}
+/*
+ * Rehash all the entries in the given table.
+ */
+static int MemHashGrowTable(mem_hash_kv_engine *pEngine)
+{
+       sxu32 nNewSize = pEngine->nBucket << 1;
+       mem_hash_record *pEntry;
+       mem_hash_record **apNew;
+       sxu32 n,iBucket;
+       /* Allocate a new larger table */
+       apNew = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc, nNewSize * sizeof(mem_hash_record *));
+       if( apNew == 0 ){
+               /* Not so fatal, simply a performance hit */
+               return UNQLITE_OK;
+       }
+       /* Zero the new table */
+       SyZero((void *)apNew, nNewSize * sizeof(mem_hash_record *));
+       /* Rehash all entries */
+       n = 0;
+       pEntry = pEngine->pLast;
+       for(;;){
+               
+               /* Loop one */
+               if( n >= pEngine->nRecord ){
+                       break;
+               }
+               pEntry->pNextHash = pEntry->pPrevHash = 0;
+               /* Install in the new bucket */
+               iBucket = pEntry->nHash & (nNewSize - 1);
+               pEntry->pNextHash = apNew[iBucket];
+               if( apNew[iBucket] ){
+                       apNew[iBucket]->pPrevHash = pEntry;
+               }
+               apNew[iBucket] = pEntry;
+               /* Point to the next entry */
+               pEntry = pEntry->pNext;
+               n++;
+
+               /* Loop two */
+               if( n >= pEngine->nRecord ){
+                       break;
+               }
+               pEntry->pNextHash = pEntry->pPrevHash = 0;
+               /* Install in the new bucket */
+               iBucket = pEntry->nHash & (nNewSize - 1);
+               pEntry->pNextHash = apNew[iBucket];
+               if( apNew[iBucket] ){
+                       apNew[iBucket]->pPrevHash = pEntry;
+               }
+               apNew[iBucket] = pEntry;
+               /* Point to the next entry */
+               pEntry = pEntry->pNext;
+               n++;
+
+               /* Loop three */
+               if( n >= pEngine->nRecord ){
+                       break;
+               }
+               pEntry->pNextHash = pEntry->pPrevHash = 0;
+               /* Install in the new bucket */
+               iBucket = pEntry->nHash & (nNewSize - 1);
+               pEntry->pNextHash = apNew[iBucket];
+               if( apNew[iBucket] ){
+                       apNew[iBucket]->pPrevHash = pEntry;
+               }
+               apNew[iBucket] = pEntry;
+               /* Point to the next entry */
+               pEntry = pEntry->pNext;
+               n++;
+
+               /* Loop four */
+               if( n >= pEngine->nRecord ){
+                       break;
+               }
+               pEntry->pNextHash = pEntry->pPrevHash = 0;
+               /* Install in the new bucket */
+               iBucket = pEntry->nHash & (nNewSize - 1);
+               pEntry->pNextHash = apNew[iBucket];
+               if( apNew[iBucket] ){
+                       apNew[iBucket]->pPrevHash = pEntry;
+               }
+               apNew[iBucket] = pEntry;
+               /* Point to the next entry */
+               pEntry = pEntry->pNext;
+               n++;
+       }
+       /* Release the old table and reflect the change */
+       SyMemBackendFree(&pEngine->sAlloc,(void *)pEngine->apBucket);
+       pEngine->apBucket = apNew;
+       pEngine->nBucket  = nNewSize;
+       return UNQLITE_OK;
+}
+/*
+ * Exported Interfaces.
+ */
+/*
+ * Each public cursor is identified by an instance of this structure.
+ */
+typedef struct mem_hash_cursor mem_hash_cursor;
+struct mem_hash_cursor
+{
+       unqlite_kv_engine *pStore; /* Must be first */
+       /* Private fields */
+       mem_hash_record *pCur;     /* Current hash record */
+};
+/*
+ * Initialize the cursor.
+ */
+static void MemHashInitCursor(unqlite_kv_cursor *pCursor)
+{
+        mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
+        mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+        /* Point to the first inserted entry */
+        pMem->pCur = pEngine->pFirst;
+}
+/*
+ * Point to the first entry.
+ */
+static int MemHashCursorFirst(unqlite_kv_cursor *pCursor)
+{
+        mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
+        mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+        pMem->pCur = pEngine->pFirst;
+        return UNQLITE_OK;
+}
+/*
+ * Point to the last entry.
+ */
+static int MemHashCursorLast(unqlite_kv_cursor *pCursor)
+{
+        mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
+        mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+        pMem->pCur = pEngine->pLast;
+        return UNQLITE_OK;
+}
+/*
+ * is a Valid Cursor.
+ */
+static int MemHashCursorValid(unqlite_kv_cursor *pCursor)
+{
+        mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+        return pMem->pCur != 0 ? 1 : 0;
+}
+/*
+ * Point to the next entry.
+ */
+static int MemHashCursorNext(unqlite_kv_cursor *pCursor)
+{
+        mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+        if( pMem->pCur == 0){
+                return UNQLITE_EOF;
+        }
+        pMem->pCur = pMem->pCur->pPrev; /* Reverse link: Not a Bug */
+        return UNQLITE_OK;
+}
+/*
+ * Point to the previous entry.
+ */
+static int MemHashCursorPrev(unqlite_kv_cursor *pCursor)
+{
+        mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+        if( pMem->pCur == 0){
+                return UNQLITE_EOF;
+        }
+        pMem->pCur = pMem->pCur->pNext; /* Reverse link: Not a Bug */
+        return UNQLITE_OK;
+}
+/*
+ * Return key length.
+ */
+static int MemHashCursorKeyLength(unqlite_kv_cursor *pCursor,int *pLen)
+{
+       mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+       if( pMem->pCur == 0){
+                return UNQLITE_EOF;
+       }
+       *pLen = (int)pMem->pCur->nKeyLen;
+       return UNQLITE_OK;
+}
+/*
+ * Return data length.
+ */
+static int MemHashCursorDataLength(unqlite_kv_cursor *pCursor,unqlite_int64 *pLen)
+{
+       mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+       if( pMem->pCur == 0 ){
+                return UNQLITE_EOF;
+       }
+       *pLen = pMem->pCur->nDataLen;
+       return UNQLITE_OK;
+}
+/*
+ * Consume the key.
+ */
+static int MemHashCursorKey(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
+{
+       mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+       int rc;
+       if( pMem->pCur == 0){
+                return UNQLITE_EOF;
+       }
+       /* Invoke the callback */
+       rc = xConsumer(pMem->pCur->pKey,pMem->pCur->nKeyLen,pUserData);
+       /* Callback result */
+       return rc;
+}
+/*
+ * Consume the data.
+ */
+static int MemHashCursorData(unqlite_kv_cursor *pCursor,int (*xConsumer)(const void *,unsigned int,void *),void *pUserData)
+{
+       mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+       int rc;
+       if( pMem->pCur == 0){
+                return UNQLITE_EOF;
+       }
+       /* Invoke the callback */
+       rc = xConsumer(pMem->pCur->pData,pMem->pCur->nDataLen,pUserData);
+       /* Callback result */
+       return rc;
+}
+/*
+ * Reset the cursor.
+ */
+static void MemHashCursorReset(unqlite_kv_cursor *pCursor)
+{
+       mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+       pMem->pCur = ((mem_hash_kv_engine *)pCursor->pStore)->pFirst;
+}
+/*
+ * Remove a particular record.
+ */
+static int MemHashCursorDelete(unqlite_kv_cursor *pCursor)
+{
+       mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+       mem_hash_record *pNext;
+       if( pMem->pCur == 0 ){
+               /* Cursor does not point to anything */
+               return UNQLITE_NOTFOUND;
+       }
+       pNext = pMem->pCur->pPrev;
+       /* Perform the deletion */
+       MemHashUnlinkRecord(pMem->pCur->pEngine,pMem->pCur);
+       /* Point to the next entry */
+       pMem->pCur = pNext;
+       return UNQLITE_OK;
+}
+/*
+ * Find a particular record.
+ */
+static int MemHashCursorSeek(unqlite_kv_cursor *pCursor,const void *pKey,int nByte,int iPos)
+{
+       mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pCursor->pStore;
+       mem_hash_cursor *pMem = (mem_hash_cursor *)pCursor;
+       /* Perform the lookup */
+       pMem->pCur = MemHashGetEntry(pEngine,pKey,nByte);
+       if( pMem->pCur == 0 ){
+               if( iPos != UNQLITE_CURSOR_MATCH_EXACT ){
+                       /* noop; */
+               }
+               /* No such record */
+               return UNQLITE_NOTFOUND;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Builtin hash function.
+ */
+static sxu32 MemHashFunc(const void *pSrc,sxu32 nLen)
+{
+       register unsigned char *zIn = (unsigned char *)pSrc;
+       unsigned char *zEnd;
+       sxu32 nH = 5381;
+       zEnd = &zIn[nLen];
+       for(;;){
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+               if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
+       }       
+       return nH;
+}
+/* Default bucket size */
+#define MEM_HASH_BUCKET_SIZE 64
+/* Default fill factor */
+#define MEM_HASH_FILL_FACTOR 4 /* or 3 */
+/*
+ * Initialize the in-memory storage engine.
+ */
+static int MemHashInit(unqlite_kv_engine *pKvEngine,int iPageSize)
+{
+       mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
+       /* Note that this instance is already zeroed */ 
+       /* Memory backend */
+       SyMemBackendInitFromParent(&pEngine->sAlloc,unqliteExportMemBackend());
+//#if defined(UNQLITE_ENABLE_THREADS)
+//     /* Already protected by the upper layers */
+//     SyMemBackendDisbaleMutexing(&pEngine->sAlloc);
+//#endif
+       /* Default hash & comparison function */
+       pEngine->xHash = MemHashFunc;
+       pEngine->xCmp = SyMemcmp;
+       /* Allocate a new bucket */
+       pEngine->apBucket = (mem_hash_record **)SyMemBackendAlloc(&pEngine->sAlloc,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
+       if( pEngine->apBucket == 0 ){
+               SXUNUSED(iPageSize); /* cc warning */
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the bucket */
+       SyZero(pEngine->apBucket,MEM_HASH_BUCKET_SIZE * sizeof(mem_hash_record *));
+       pEngine->nRecord = 0;
+       pEngine->nBucket = MEM_HASH_BUCKET_SIZE;
+       return UNQLITE_OK;
+}
+/*
+ * Release the in-memory storage engine.
+ */
+static void MemHashRelease(unqlite_kv_engine *pKvEngine)
+{
+       mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
+       /* Release the private memory backend */
+       SyMemBackendRelease(&pEngine->sAlloc);
+}
+/*
+ * Configure the in-memory storage engine.
+ */
+static int MemHashConfigure(unqlite_kv_engine *pKvEngine,int iOp,va_list ap)
+{
+       mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKvEngine;
+       int rc = UNQLITE_OK;
+       switch(iOp){
+       case UNQLITE_KV_CONFIG_HASH_FUNC:{
+               /* Use a default hash function */
+               if( pEngine->nRecord > 0 ){
+                       rc = UNQLITE_LOCKED;
+               }else{
+                       ProcHash xHash = va_arg(ap,ProcHash);
+                       if( xHash ){
+                               pEngine->xHash = xHash;
+                       }
+               }
+               break;
+                                                                        }
+       case UNQLITE_KV_CONFIG_CMP_FUNC: {
+               /* Default comparison function */
+               ProcCmp xCmp = va_arg(ap,ProcCmp);
+               if( xCmp ){
+                       pEngine->xCmp = xCmp;
+               }
+               break;
+                                                                        }
+       default:
+               /* Unknown configuration option */
+               rc = UNQLITE_UNKNOWN;
+       }
+       return rc;
+}
+/*
+ * Replace method.
+ */
+static int MemHashReplace(
+         unqlite_kv_engine *pKv,
+         const void *pKey,int nKeyLen,
+         const void *pData,unqlite_int64 nDataLen
+         )
+{
+       mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
+       mem_hash_record *pRecord;
+       if( nDataLen > SXU32_HIGH ){
+               /* Database limit */
+               pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
+               return UNQLITE_LIMIT;
+       }
+       /* Fetch the record first */
+       pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
+       if( pRecord == 0 ){
+               /* Allocate a new record */
+               pRecord = MemHashNewRecord(pEngine,
+                       pKey,nKeyLen,
+                       pData,nDataLen,
+                       pEngine->xHash(pKey,nKeyLen)
+                       );
+               if( pRecord == 0 ){
+                       return UNQLITE_NOMEM;
+               }
+               /* Link the entry */
+               MemHashLinkRecord(pEngine,pRecord);
+               if( (pEngine->nRecord >= pEngine->nBucket * MEM_HASH_FILL_FACTOR) && pEngine->nRecord < 100000 ){
+                       /* Rehash the table */
+                       MemHashGrowTable(pEngine);
+               }
+       }else{
+               sxu32 nData = (sxu32)nDataLen;
+               void *pNew;
+               /* Replace an existing record */
+               if( nData == pRecord->nDataLen ){
+                       /* No need to free the old chunk */
+                       pNew = (void *)pRecord->pData;
+               }else{
+                       pNew = SyMemBackendAlloc(&pEngine->sAlloc,nData);
+                       if( pNew == 0 ){
+                               return UNQLITE_NOMEM;
+                       }
+                       /* Release the old data */
+                       SyMemBackendFree(&pEngine->sAlloc,(void *)pRecord->pData);
+               }
+               /* Reflect the change */
+               pRecord->nDataLen = nData;
+               SyMemcpy(pData,pNew,nData);
+               pRecord->pData = pNew;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Append method.
+ */
+static int MemHashAppend(
+         unqlite_kv_engine *pKv,
+         const void *pKey,int nKeyLen,
+         const void *pData,unqlite_int64 nDataLen
+         )
+{
+       mem_hash_kv_engine *pEngine = (mem_hash_kv_engine *)pKv;
+       mem_hash_record *pRecord;
+       if( nDataLen > SXU32_HIGH ){
+               /* Database limit */
+               pEngine->pIo->xErr(pEngine->pIo->pHandle,"Record size limit reached");
+               return UNQLITE_LIMIT;
+       }
+       /* Fetch the record first */
+       pRecord = MemHashGetEntry(pEngine,pKey,nKeyLen);
+       if( pRecord == 0 ){
+               /* Allocate a new record */
+               pRecord = MemHashNewRecord(pEngine,
+                       pKey,nKeyLen,
+                       pData,nDataLen,
+                       pEngine->xHash(pKey,nKeyLen)
+                       );
+               if( pRecord == 0 ){
+                       return UNQLITE_NOMEM;
+               }
+               /* Link the entry */
+               MemHashLinkRecord(pEngine,pRecord);
+               if( pEngine->nRecord * MEM_HASH_FILL_FACTOR >= pEngine->nBucket && pEngine->nRecord < 100000 ){
+                       /* Rehash the table */
+                       MemHashGrowTable(pEngine);
+               }
+       }else{
+               unqlite_int64 nNew = pRecord->nDataLen + nDataLen;
+               void *pOld = (void *)pRecord->pData;
+               sxu32 nData;
+               char *zNew;
+               /* Append data to the existing record */
+               if( nNew > SXU32_HIGH ){
+                       /* Overflow */
+                       pEngine->pIo->xErr(pEngine->pIo->pHandle,"Append operation will cause data overflow");  
+                       return UNQLITE_LIMIT;
+               }
+               nData = (sxu32)nNew;
+               /* Allocate bigger chunk */
+               zNew = (char *)SyMemBackendRealloc(&pEngine->sAlloc,pOld,nData);
+               if( zNew == 0 ){
+                       return UNQLITE_NOMEM;
+               }
+               /* Reflect the change */
+               SyMemcpy(pData,&zNew[pRecord->nDataLen],(sxu32)nDataLen);
+               pRecord->pData = (const void *)zNew;
+               pRecord->nDataLen = nData;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Export the in-memory storage engine.
+ */
+UNQLITE_PRIVATE const unqlite_kv_methods * unqliteExportMemKvStorage(void)
+{
+       static const unqlite_kv_methods sMemStore = {
+               "mem",                      /* zName */
+               sizeof(mem_hash_kv_engine), /* szKv */
+               sizeof(mem_hash_cursor),    /* szCursor */
+               1,                          /* iVersion */
+               MemHashInit,                /* xInit */
+               MemHashRelease,             /* xRelease */
+               MemHashConfigure,           /* xConfig */
+               0,                          /* xOpen */
+               MemHashReplace,             /* xReplace */
+               MemHashAppend,              /* xAppend */
+               MemHashInitCursor,          /* xCursorInit */
+               MemHashCursorSeek,          /* xSeek */
+               MemHashCursorFirst,         /* xFirst */
+               MemHashCursorLast,          /* xLast */
+               MemHashCursorValid,         /* xValid */
+               MemHashCursorNext,          /* xNext */
+               MemHashCursorPrev,          /* xPrev */
+               MemHashCursorDelete,        /* xDelete */
+               MemHashCursorKeyLength,     /* xKeyLength */
+               MemHashCursorKey,           /* xKey */
+               MemHashCursorDataLength,    /* xDataLength */
+               MemHashCursorData,          /* xData */
+               MemHashCursorReset,         /* xReset */
+               0        /* xRelease */                        
+       };
+       return &sMemStore;
+}
+/*
+ * ----------------------------------------------------------
+ * File: os.c
+ * MD5: e7ad243c3cd9e6aac5fba406eedb7766
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: os.c v1.0 FreeBSD 2012-11-12 21:27 devel <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* OS interfaces abstraction layers: Mostly SQLite3 source tree */
+/*
+** The following routines are convenience wrappers around methods
+** of the unqlite_file object.  This is mostly just syntactic sugar. All
+** of this would be completely automatic if UnQLite were coded using
+** C++ instead of plain old C.
+*/
+UNQLITE_PRIVATE int unqliteOsRead(unqlite_file *id, void *pBuf, unqlite_int64 amt, unqlite_int64 offset)
+{
+  return id->pMethods->xRead(id, pBuf, amt, offset);
+}
+UNQLITE_PRIVATE int unqliteOsWrite(unqlite_file *id, const void *pBuf, unqlite_int64 amt, unqlite_int64 offset)
+{
+  return id->pMethods->xWrite(id, pBuf, amt, offset);
+}
+UNQLITE_PRIVATE int unqliteOsTruncate(unqlite_file *id, unqlite_int64 size)
+{
+  return id->pMethods->xTruncate(id, size);
+}
+UNQLITE_PRIVATE int unqliteOsSync(unqlite_file *id, int flags)
+{
+  return id->pMethods->xSync(id, flags);
+}
+UNQLITE_PRIVATE int unqliteOsFileSize(unqlite_file *id, unqlite_int64 *pSize)
+{
+  return id->pMethods->xFileSize(id, pSize);
+}
+UNQLITE_PRIVATE int unqliteOsLock(unqlite_file *id, int lockType)
+{
+  return id->pMethods->xLock(id, lockType);
+}
+UNQLITE_PRIVATE int unqliteOsUnlock(unqlite_file *id, int lockType)
+{
+  return id->pMethods->xUnlock(id, lockType);
+}
+UNQLITE_PRIVATE int unqliteOsCheckReservedLock(unqlite_file *id, int *pResOut)
+{
+  return id->pMethods->xCheckReservedLock(id, pResOut);
+}
+UNQLITE_PRIVATE int unqliteOsSectorSize(unqlite_file *id)
+{
+  if( id->pMethods->xSectorSize ){
+         return id->pMethods->xSectorSize(id);
+  }
+  return  UNQLITE_DEFAULT_SECTOR_SIZE;
+}
+/*
+** The next group of routines are convenience wrappers around the
+** VFS methods.
+*/
+UNQLITE_PRIVATE int unqliteOsOpen(
+  unqlite_vfs *pVfs,
+  SyMemBackend *pAlloc,
+  const char *zPath, 
+  unqlite_file **ppOut, 
+  unsigned int flags 
+)
+{
+       unqlite_file *pFile;
+       int rc;
+       *ppOut = 0;
+       if( zPath == 0 ){
+               /* May happen if dealing with an in-memory database */
+               return SXERR_EMPTY;
+       }
+       /* Allocate a new instance */
+       pFile = (unqlite_file *)SyMemBackendAlloc(pAlloc,sizeof(unqlite_file)+pVfs->szOsFile);
+       if( pFile == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pFile,sizeof(unqlite_file)+pVfs->szOsFile);
+       /* Invoke the xOpen method of the underlying VFS */
+       rc = pVfs->xOpen(pVfs, zPath, pFile, flags);
+       if( rc != UNQLITE_OK ){
+               SyMemBackendFree(pAlloc,pFile);
+               pFile = 0;
+       }
+       *ppOut = pFile;
+       return rc;
+}
+UNQLITE_PRIVATE int unqliteOsCloseFree(SyMemBackend *pAlloc,unqlite_file *pId)
+{
+       int rc = UNQLITE_OK;
+       if( pId ){
+               rc = pId->pMethods->xClose(pId);
+               SyMemBackendFree(pAlloc,pId);
+       }
+       return rc;
+}
+UNQLITE_PRIVATE int unqliteOsDelete(unqlite_vfs *pVfs, const char *zPath, int dirSync){
+  return pVfs->xDelete(pVfs, zPath, dirSync);
+}
+UNQLITE_PRIVATE int unqliteOsAccess(
+  unqlite_vfs *pVfs, 
+  const char *zPath, 
+  int flags, 
+  int *pResOut
+){
+  return pVfs->xAccess(pVfs, zPath, flags, pResOut);
+}
+/*
+ * ----------------------------------------------------------
+ * File: os_unix.c
+ * MD5: 5efd57d03f8fb988d081c5bcf5cc2998
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: os_unix.c v1.3 FreeBSD 2013-04-05 01:10 devel <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* 
+ * Omit the whole layer from the build if compiling for platforms other than Unix (Linux, BSD, Solaris, OS X, etc.).
+ * Note: Mostly SQLite3 source tree.
+ */
+#if defined(__UNIXES__)
+/** This file contains the VFS implementation for unix-like operating systems
+** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others.
+**
+** There are actually several different VFS implementations in this file.
+** The differences are in the way that file locking is done.  The default
+** implementation uses Posix Advisory Locks.  Alternative implementations
+** use flock(), dot-files, various proprietary locking schemas, or simply
+** skip locking all together.
+**
+** This source file is organized into divisions where the logic for various
+** subfunctions is contained within the appropriate division.  PLEASE
+** KEEP THE STRUCTURE OF THIS FILE INTACT.  New code should be placed
+** in the correct division and should be clearly labeled.
+**
+*/
+/*
+** standard include files.
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+#if defined(__APPLE__) 
+# include <sys/mount.h>
+#endif
+/*
+** Allowed values of unixFile.fsFlags
+*/
+#define UNQLITE_FSFLAGS_IS_MSDOS     0x1
+
+/*
+** Default permissions when creating a new file
+*/
+#ifndef UNQLITE_DEFAULT_FILE_PERMISSIONS
+# define UNQLITE_DEFAULT_FILE_PERMISSIONS 0644
+#endif
+/*
+ ** Default permissions when creating auto proxy dir
+ */
+#ifndef UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS
+# define UNQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
+#endif
+/*
+** Maximum supported path-length.
+*/
+#define MAX_PATHNAME 512
+/*
+** Only set the lastErrno if the error code is a real error and not 
+** a normal expected return code of UNQLITE_BUSY or UNQLITE_OK
+*/
+#define IS_LOCK_ERROR(x)  ((x != UNQLITE_OK) && (x != UNQLITE_BUSY))
+/* Forward references */
+typedef struct unixInodeInfo unixInodeInfo;   /* An i-node */
+typedef struct UnixUnusedFd UnixUnusedFd;     /* An unused file descriptor */
+/*
+** Sometimes, after a file handle is closed by SQLite, the file descriptor
+** cannot be closed immediately. In these cases, instances of the following
+** structure are used to store the file descriptor while waiting for an
+** opportunity to either close or reuse it.
+*/
+struct UnixUnusedFd {
+  int fd;                   /* File descriptor to close */
+  int flags;                /* Flags this file descriptor was opened with */
+  UnixUnusedFd *pNext;      /* Next unused file descriptor on same file */
+};
+/*
+** The unixFile structure is subclass of unqlite3_file specific to the unix
+** VFS implementations.
+*/
+typedef struct unixFile unixFile;
+struct unixFile {
+  const unqlite_io_methods *pMethod;  /* Always the first entry */
+  unixInodeInfo *pInode;              /* Info about locks on this inode */
+  int h;                              /* The file descriptor */
+  int dirfd;                          /* File descriptor for the directory */
+  unsigned char eFileLock;            /* The type of lock held on this fd */
+  int lastErrno;                      /* The unix errno from last I/O error */
+  void *lockingContext;               /* Locking style specific state */
+  UnixUnusedFd *pUnused;              /* Pre-allocated UnixUnusedFd */
+  int fileFlags;                      /* Miscellanous flags */
+  const char *zPath;                  /* Name of the file */
+  unsigned fsFlags;                   /* cached details from statfs() */
+};
+/*
+** The following macros define bits in unixFile.fileFlags
+*/
+#define UNQLITE_WHOLE_FILE_LOCKING  0x0001   /* Use whole-file locking */
+/*
+** Define various macros that are missing from some systems.
+*/
+#ifndef O_LARGEFILE
+# define O_LARGEFILE 0
+#endif
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+/*
+** Helper functions to obtain and relinquish the global mutex. The
+** global mutex is used to protect the unixInodeInfo and
+** vxworksFileId objects used by this file, all of which may be 
+** shared by multiple threads.
+**
+** Function unixMutexHeld() is used to assert() that the global mutex 
+** is held when required. This function is only used as part of assert() 
+** statements. e.g.
+**
+**   unixEnterMutex()
+**     assert( unixMutexHeld() );
+**   unixEnterLeave()
+*/
+static void unixEnterMutex(void){
+#ifdef UNQLITE_ENABLE_THREADS
+       const SyMutexMethods *pMutexMethods = SyMutexExportMethods();
+       if( pMutexMethods ){
+               SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */
+               SyMutexEnter(pMutexMethods,pMutex);
+       }
+#endif /* UNQLITE_ENABLE_THREADS */
+}
+static void unixLeaveMutex(void){
+#ifdef UNQLITE_ENABLE_THREADS
+  const SyMutexMethods *pMutexMethods = SyMutexExportMethods();
+  if( pMutexMethods ){
+        SyMutex *pMutex = pMutexMethods->xNew(SXMUTEX_TYPE_STATIC_2); /* pre-allocated, never fail */
+        SyMutexLeave(pMutexMethods,pMutex);
+  }
+#endif /* UNQLITE_ENABLE_THREADS */
+}
+/*
+** This routine translates a standard POSIX errno code into something
+** useful to the clients of the unqlite3 functions.  Specifically, it is
+** intended to translate a variety of "try again" errors into UNQLITE_BUSY
+** and a variety of "please close the file descriptor NOW" errors into 
+** UNQLITE_IOERR
+** 
+** Errors during initialization of locks, or file system support for locks,
+** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
+*/
+static int unqliteErrorFromPosixError(int posixError, int unqliteIOErr) {
+  switch (posixError) {
+  case 0: 
+    return UNQLITE_OK;
+    
+  case EAGAIN:
+  case ETIMEDOUT:
+  case EBUSY:
+  case EINTR:
+  case ENOLCK:  
+    /* random NFS retry error, unless during file system support 
+     * introspection, in which it actually means what it says */
+    return UNQLITE_BUSY;
+  case EACCES: 
+    /* EACCES is like EAGAIN during locking operations, but not any other time*/
+      return UNQLITE_BUSY;
+    
+  case EPERM: 
+    return UNQLITE_PERM;
+    
+  case EDEADLK:
+    return UNQLITE_IOERR;
+    
+#if EOPNOTSUPP!=ENOTSUP
+  case EOPNOTSUPP: 
+    /* something went terribly awry, unless during file system support 
+     * introspection, in which it actually means what it says */
+#endif
+#ifdef ENOTSUP
+  case ENOTSUP: 
+    /* invalid fd, unless during file system support introspection, in which 
+     * it actually means what it says */
+#endif
+  case EIO:
+  case EBADF:
+  case EINVAL:
+  case ENOTCONN:
+  case ENODEV:
+  case ENXIO:
+  case ENOENT:
+  case ESTALE:
+  case ENOSYS:
+    /* these should force the client to close the file and reconnect */
+    
+  default: 
+    return unqliteIOErr;
+  }
+}
+/******************************************************************************
+*************************** Posix Advisory Locking ****************************
+**
+** POSIX advisory locks are broken by design.  ANSI STD 1003.1 (1996)
+** section 6.5.2.2 lines 483 through 490 specify that when a process
+** sets or clears a lock, that operation overrides any prior locks set
+** by the same process.  It does not explicitly say so, but this implies
+** that it overrides locks set by the same process using a different
+** file descriptor.  Consider this test case:
+**
+**       int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);
+**       int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);
+**
+** Suppose ./file1 and ./file2 are really the same file (because
+** one is a hard or symbolic link to the other) then if you set
+** an exclusive lock on fd1, then try to get an exclusive lock
+** on fd2, it works.  I would have expected the second lock to
+** fail since there was already a lock on the file due to fd1.
+** But not so.  Since both locks came from the same process, the
+** second overrides the first, even though they were on different
+** file descriptors opened on different file names.
+**
+** This means that we cannot use POSIX locks to synchronize file access
+** among competing threads of the same process.  POSIX locks will work fine
+** to synchronize access for threads in separate processes, but not
+** threads within the same process.
+**
+** To work around the problem, SQLite has to manage file locks internally
+** on its own.  Whenever a new database is opened, we have to find the
+** specific inode of the database file (the inode is determined by the
+** st_dev and st_ino fields of the stat structure that fstat() fills in)
+** and check for locks already existing on that inode.  When locks are
+** created or removed, we have to look at our own internal record of the
+** locks to see if another thread has previously set a lock on that same
+** inode.
+**
+** (Aside: The use of inode numbers as unique IDs does not work on VxWorks.
+** For VxWorks, we have to use the alternative unique ID system based on
+** canonical filename and implemented in the previous division.)
+**
+** There is one locking structure
+** per inode, so if the same inode is opened twice, both unixFile structures
+** point to the same locking structure.  The locking structure keeps
+** a reference count (so we will know when to delete it) and a "cnt"
+** field that tells us its internal lock status.  cnt==0 means the
+** file is unlocked.  cnt==-1 means the file has an exclusive lock.
+** cnt>0 means there are cnt shared locks on the file.
+**
+** Any attempt to lock or unlock a file first checks the locking
+** structure.  The fcntl() system call is only invoked to set a 
+** POSIX lock if the internal lock structure transitions between
+** a locked and an unlocked state.
+**
+** But wait:  there are yet more problems with POSIX advisory locks.
+**
+** If you close a file descriptor that points to a file that has locks,
+** all locks on that file that are owned by the current process are
+** released.  To work around this problem, each unixInodeInfo object
+** maintains a count of the number of pending locks on that inode.
+** When an attempt is made to close an unixFile, if there are
+** other unixFile open on the same inode that are holding locks, the call
+** to close() the file descriptor is deferred until all of the locks clear.
+** The unixInodeInfo structure keeps a list of file descriptors that need to
+** be closed and that list is walked (and cleared) when the last lock
+** clears.
+**
+** Yet another problem:  LinuxThreads do not play well with posix locks.
+**
+** Many older versions of linux use the LinuxThreads library which is
+** not posix compliant.  Under LinuxThreads, a lock created by thread
+** A cannot be modified or overridden by a different thread B.
+** Only thread A can modify the lock.  Locking behavior is correct
+** if the appliation uses the newer Native Posix Thread Library (NPTL)
+** on linux - with NPTL a lock created by thread A can override locks
+** in thread B.  But there is no way to know at compile-time which
+** threading library is being used.  So there is no way to know at
+** compile-time whether or not thread A can override locks on thread B.
+** One has to do a run-time check to discover the behavior of the
+** current process.
+**
+*/
+
+/*
+** An instance of the following structure serves as the key used
+** to locate a particular unixInodeInfo object.
+*/
+struct unixFileId {
+  dev_t dev;                  /* Device number */
+  ino_t ino;                  /* Inode number */
+};
+/*
+** An instance of the following structure is allocated for each open
+** inode.  Or, on LinuxThreads, there is one of these structures for
+** each inode opened by each thread.
+**
+** A single inode can have multiple file descriptors, so each unixFile
+** structure contains a pointer to an instance of this object and this
+** object keeps a count of the number of unixFile pointing to it.
+*/
+struct unixInodeInfo {
+  struct unixFileId fileId;       /* The lookup key */
+  int nShared;                    /* Number of SHARED locks held */
+  int eFileLock;                  /* One of SHARED_LOCK, RESERVED_LOCK etc. */
+  int nRef;                       /* Number of pointers to this structure */
+  int nLock;                      /* Number of outstanding file locks */
+  UnixUnusedFd *pUnused;          /* Unused file descriptors to close */
+  unixInodeInfo *pNext;           /* List of all unixInodeInfo objects */
+  unixInodeInfo *pPrev;           /*    .... doubly linked */
+};
+
+static unixInodeInfo *inodeList = 0;
+/*
+ * Local memory allocation stuff.
+ */
+void * unqlite_malloc(unsigned int nByte)
+{
+       SyMemBackend *pAlloc;
+       void *p;
+       pAlloc = (SyMemBackend *)unqliteExportMemBackend();
+       p = SyMemBackendAlloc(pAlloc,nByte);
+       return p;
+}
+void unqlite_free(void *p)
+{
+       SyMemBackend *pAlloc;
+       pAlloc = (SyMemBackend *)unqliteExportMemBackend();
+       SyMemBackendFree(pAlloc,p);
+}
+/*
+** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
+** If all such file descriptors are closed without error, the list is
+** cleared and UNQLITE_OK returned.
+**
+** Otherwise, if an error occurs, then successfully closed file descriptor
+** entries are removed from the list, and UNQLITE_IOERR_CLOSE returned. 
+** not deleted and UNQLITE_IOERR_CLOSE returned.
+*/ 
+static int closePendingFds(unixFile *pFile){
+  int rc = UNQLITE_OK;
+  unixInodeInfo *pInode = pFile->pInode;
+  UnixUnusedFd *pError = 0;
+  UnixUnusedFd *p;
+  UnixUnusedFd *pNext;
+  for(p=pInode->pUnused; p; p=pNext){
+    pNext = p->pNext;
+    if( close(p->fd) ){
+      pFile->lastErrno = errno;
+         rc = UNQLITE_IOERR;
+      p->pNext = pError;
+      pError = p;
+    }else{
+      unqlite_free(p);
+    }
+  }
+  pInode->pUnused = pError;
+  return rc;
+}
+/*
+** Release a unixInodeInfo structure previously allocated by findInodeInfo().
+**
+** The mutex entered using the unixEnterMutex() function must be held
+** when this function is called.
+*/
+static void releaseInodeInfo(unixFile *pFile){
+  unixInodeInfo *pInode = pFile->pInode;
+  if( pInode ){
+    pInode->nRef--;
+    if( pInode->nRef==0 ){
+      closePendingFds(pFile);
+      if( pInode->pPrev ){
+        pInode->pPrev->pNext = pInode->pNext;
+      }else{
+        inodeList = pInode->pNext;
+      }
+      if( pInode->pNext ){
+        pInode->pNext->pPrev = pInode->pPrev;
+      }
+      unqlite_free(pInode);
+    }
+  }
+}
+/*
+** Given a file descriptor, locate the unixInodeInfo object that
+** describes that file descriptor.  Create a new one if necessary.  The
+** return value might be uninitialized if an error occurs.
+**
+** The mutex entered using the unixEnterMutex() function must be held
+** when this function is called.
+**
+** Return an appropriate error code.
+*/
+static int findInodeInfo(
+  unixFile *pFile,               /* Unix file with file desc used in the key */
+  unixInodeInfo **ppInode        /* Return the unixInodeInfo object here */
+){
+  int rc;                        /* System call return code */
+  int fd;                        /* The file descriptor for pFile */
+  struct unixFileId fileId;      /* Lookup key for the unixInodeInfo */
+  struct stat statbuf;           /* Low-level file information */
+  unixInodeInfo *pInode = 0;     /* Candidate unixInodeInfo object */
+
+  /* Get low-level information about the file that we can used to
+  ** create a unique name for the file.
+  */
+  fd = pFile->h;
+  rc = fstat(fd, &statbuf);
+  if( rc!=0 ){
+    pFile->lastErrno = errno;
+#ifdef EOVERFLOW
+       if( pFile->lastErrno==EOVERFLOW ) return UNQLITE_NOTIMPLEMENTED;
+#endif
+    return UNQLITE_IOERR;
+  }
+
+#ifdef __APPLE__
+  /* On OS X on an msdos filesystem, the inode number is reported
+  ** incorrectly for zero-size files.  See ticket #3260.  To work
+  ** around this problem (we consider it a bug in OS X, not SQLite)
+  ** we always increase the file size to 1 by writing a single byte
+  ** prior to accessing the inode number.  The one byte written is
+  ** an ASCII 'S' character which also happens to be the first byte
+  ** in the header of every SQLite database.  In this way, if there
+  ** is a race condition such that another thread has already populated
+  ** the first page of the database, no damage is done.
+  */
+  if( statbuf.st_size==0 && (pFile->fsFlags & UNQLITE_FSFLAGS_IS_MSDOS)!=0 ){
+    rc = write(fd, "S", 1);
+    if( rc!=1 ){
+      pFile->lastErrno = errno;
+      return UNQLITE_IOERR;
+    }
+    rc = fstat(fd, &statbuf);
+    if( rc!=0 ){
+      pFile->lastErrno = errno;
+      return UNQLITE_IOERR;
+    }
+  }
+#endif
+  SyZero(&fileId,sizeof(fileId));
+  fileId.dev = statbuf.st_dev;
+  fileId.ino = statbuf.st_ino;
+  pInode = inodeList;
+  while( pInode && SyMemcmp((const void *)&fileId,(const void *)&pInode->fileId, sizeof(fileId)) ){
+    pInode = pInode->pNext;
+  }
+  if( pInode==0 ){
+    pInode = (unixInodeInfo *)unqlite_malloc( sizeof(*pInode) );
+    if( pInode==0 ){
+      return UNQLITE_NOMEM;
+    }
+    SyZero(pInode,sizeof(*pInode));
+       SyMemcpy((const void *)&fileId,(void *)&pInode->fileId,sizeof(fileId));
+    pInode->nRef = 1;
+    pInode->pNext = inodeList;
+    pInode->pPrev = 0;
+    if( inodeList ) inodeList->pPrev = pInode;
+    inodeList = pInode;
+  }else{
+    pInode->nRef++;
+  }
+  *ppInode = pInode;
+  return UNQLITE_OK;
+}
+/*
+** This routine checks if there is a RESERVED lock held on the specified
+** file by this or any other process. If such a lock is held, set *pResOut
+** to a non-zero value otherwise *pResOut is set to zero.  The return value
+** is set to UNQLITE_OK unless an I/O error occurs during lock checking.
+*/
+static int unixCheckReservedLock(unqlite_file *id, int *pResOut){
+  int rc = UNQLITE_OK;
+  int reserved = 0;
+  unixFile *pFile = (unixFile*)id;
+
+  unixEnterMutex(); /* Because pFile->pInode is shared across threads */
+
+  /* Check if a thread in this process holds such a lock */
+  if( pFile->pInode->eFileLock>SHARED_LOCK ){
+    reserved = 1;
+  }
+
+  /* Otherwise see if some other process holds it.
+  */
+  if( !reserved ){
+    struct flock lock;
+    lock.l_whence = SEEK_SET;
+    lock.l_start = RESERVED_BYTE;
+    lock.l_len = 1;
+    lock.l_type = F_WRLCK;
+    if (-1 == fcntl(pFile->h, F_GETLK, &lock)) {
+      int tErrno = errno;
+         rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+      pFile->lastErrno = tErrno;
+    } else if( lock.l_type!=F_UNLCK ){
+      reserved = 1;
+    }
+  }
+  
+  unixLeaveMutex();
+  *pResOut = reserved;
+  return rc;
+}
+/*
+** Lock the file with the lock specified by parameter eFileLock - one
+** of the following:
+**
+**     (1) SHARED_LOCK
+**     (2) RESERVED_LOCK
+**     (3) PENDING_LOCK
+**     (4) EXCLUSIVE_LOCK
+**
+** Sometimes when requesting one lock state, additional lock states
+** are inserted in between.  The locking might fail on one of the later
+** transitions leaving the lock state different from what it started but
+** still short of its goal.  The following chart shows the allowed
+** transitions and the inserted intermediate states:
+**
+**    UNLOCKED -> SHARED
+**    SHARED -> RESERVED
+**    SHARED -> (PENDING) -> EXCLUSIVE
+**    RESERVED -> (PENDING) -> EXCLUSIVE
+**    PENDING -> EXCLUSIVE
+**
+** This routine will only increase a lock.  Use the unqliteOsUnlock()
+** routine to lower a locking level.
+*/
+static int unixLock(unqlite_file *id, int eFileLock){
+  /* The following describes the implementation of the various locks and
+  ** lock transitions in terms of the POSIX advisory shared and exclusive
+  ** lock primitives (called read-locks and write-locks below, to avoid
+  ** confusion with SQLite lock names). The algorithms are complicated
+  ** slightly in order to be compatible with unixdows systems simultaneously
+  ** accessing the same database file, in case that is ever required.
+  **
+  ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
+  ** byte', each single bytes at well known offsets, and the 'shared byte
+  ** range', a range of 510 bytes at a well known offset.
+  **
+  ** To obtain a SHARED lock, a read-lock is obtained on the 'pending
+  ** byte'.  If this is successful, a random byte from the 'shared byte
+  ** range' is read-locked and the lock on the 'pending byte' released.
+  **
+  ** A process may only obtain a RESERVED lock after it has a SHARED lock.
+  ** A RESERVED lock is implemented by grabbing a write-lock on the
+  ** 'reserved byte'. 
+  **
+  ** A process may only obtain a PENDING lock after it has obtained a
+  ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock
+  ** on the 'pending byte'. This ensures that no new SHARED locks can be
+  ** obtained, but existing SHARED locks are allowed to persist. A process
+  ** does not have to obtain a RESERVED lock on the way to a PENDING lock.
+  ** This property is used by the algorithm for rolling back a journal file
+  ** after a crash.
+  **
+  ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is
+  ** implemented by obtaining a write-lock on the entire 'shared byte
+  ** range'. Since all other locks require a read-lock on one of the bytes
+  ** within this range, this ensures that no other locks are held on the
+  ** database. 
+  **
+  ** The reason a single byte cannot be used instead of the 'shared byte
+  ** range' is that some versions of unixdows do not support read-locks. By
+  ** locking a random byte from a range, concurrent SHARED locks may exist
+  ** even if the locking primitive used is always a write-lock.
+  */
+  int rc = UNQLITE_OK;
+  unixFile *pFile = (unixFile*)id;
+  unixInodeInfo *pInode = pFile->pInode;
+  struct flock lock;
+  int s = 0;
+  int tErrno = 0;
+
+  /* If there is already a lock of this type or more restrictive on the
+  ** unixFile, do nothing. Don't use the end_lock: exit path, as
+  ** unixEnterMutex() hasn't been called yet.
+  */
+  if( pFile->eFileLock>=eFileLock ){
+    return UNQLITE_OK;
+  }
+  /* This mutex is needed because pFile->pInode is shared across threads
+  */
+  unixEnterMutex();
+  pInode = pFile->pInode;
+
+  /* If some thread using this PID has a lock via a different unixFile*
+  ** handle that precludes the requested lock, return BUSY.
+  */
+  if( (pFile->eFileLock!=pInode->eFileLock && 
+          (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
+  ){
+    rc = UNQLITE_BUSY;
+    goto end_lock;
+  }
+
+  /* If a SHARED lock is requested, and some thread using this PID already
+  ** has a SHARED or RESERVED lock, then increment reference counts and
+  ** return UNQLITE_OK.
+  */
+  if( eFileLock==SHARED_LOCK && 
+      (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
+    pFile->eFileLock = SHARED_LOCK;
+    pInode->nShared++;
+    pInode->nLock++;
+    goto end_lock;
+  }
+  /* A PENDING lock is needed before acquiring a SHARED lock and before
+  ** acquiring an EXCLUSIVE lock.  For the SHARED lock, the PENDING will
+  ** be released.
+  */
+  lock.l_len = 1L;
+  lock.l_whence = SEEK_SET;
+  if( eFileLock==SHARED_LOCK 
+      || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
+  ){
+    lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
+    lock.l_start = PENDING_BYTE;
+    s = fcntl(pFile->h, F_SETLK, &lock);
+    if( s==(-1) ){
+      tErrno = errno;
+      rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+      if( IS_LOCK_ERROR(rc) ){
+        pFile->lastErrno = tErrno;
+      }
+      goto end_lock;
+    }
+  }
+  /* If control gets to this point, then actually go ahead and make
+  ** operating system calls for the specified lock.
+  */
+  if( eFileLock==SHARED_LOCK ){
+    /* Now get the read-lock */
+    lock.l_start = SHARED_FIRST;
+    lock.l_len = SHARED_SIZE;
+    if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){
+      tErrno = errno;
+    }
+    /* Drop the temporary PENDING lock */
+    lock.l_start = PENDING_BYTE;
+    lock.l_len = 1L;
+    lock.l_type = F_UNLCK;
+    if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){
+      if( s != -1 ){
+        /* This could happen with a network mount */
+        tErrno = errno; 
+        rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR); 
+        if( IS_LOCK_ERROR(rc) ){
+          pFile->lastErrno = tErrno;
+        }
+        goto end_lock;
+      }
+    }
+    if( s==(-1) ){
+               rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+      if( IS_LOCK_ERROR(rc) ){
+        pFile->lastErrno = tErrno;
+      }
+    }else{
+      pFile->eFileLock = SHARED_LOCK;
+      pInode->nLock++;
+      pInode->nShared = 1;
+    }
+  }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
+    /* We are trying for an exclusive lock but another thread in this
+    ** same process is still holding a shared lock. */
+    rc = UNQLITE_BUSY;
+  }else{
+    /* The request was for a RESERVED or EXCLUSIVE lock.  It is
+    ** assumed that there is a SHARED or greater lock on the file
+    ** already.
+    */
+    lock.l_type = F_WRLCK;
+    switch( eFileLock ){
+      case RESERVED_LOCK:
+        lock.l_start = RESERVED_BYTE;
+        break;
+      case EXCLUSIVE_LOCK:
+        lock.l_start = SHARED_FIRST;
+        lock.l_len = SHARED_SIZE;
+        break;
+      default:
+                 /* Can't happen */
+        break;
+    }
+    s = fcntl(pFile->h, F_SETLK, &lock);
+    if( s==(-1) ){
+      tErrno = errno;
+      rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+      if( IS_LOCK_ERROR(rc) ){
+        pFile->lastErrno = tErrno;
+      }
+    }
+  }
+  if( rc==UNQLITE_OK ){
+    pFile->eFileLock = eFileLock;
+    pInode->eFileLock = eFileLock;
+  }else if( eFileLock==EXCLUSIVE_LOCK ){
+    pFile->eFileLock = PENDING_LOCK;
+    pInode->eFileLock = PENDING_LOCK;
+  }
+end_lock:
+  unixLeaveMutex();
+  return rc;
+}
+/*
+** Add the file descriptor used by file handle pFile to the corresponding
+** pUnused list.
+*/
+static void setPendingFd(unixFile *pFile){
+  unixInodeInfo *pInode = pFile->pInode;
+  UnixUnusedFd *p = pFile->pUnused;
+  p->pNext = pInode->pUnused;
+  pInode->pUnused = p;
+  pFile->h = -1;
+  pFile->pUnused = 0;
+}
+/*
+** Lower the locking level on file descriptor pFile to eFileLock.  eFileLock
+** must be either NO_LOCK or SHARED_LOCK.
+**
+** If the locking level of the file descriptor is already at or below
+** the requested locking level, this routine is a no-op.
+** 
+** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED
+** the byte range is divided into 2 parts and the first part is unlocked then
+** set to a read lock, then the other part is simply unlocked.  This works 
+** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to 
+** remove the write lock on a region when a read lock is set.
+*/
+static int _posixUnlock(unqlite_file *id, int eFileLock, int handleNFSUnlock){
+  unixFile *pFile = (unixFile*)id;
+  unixInodeInfo *pInode;
+  struct flock lock;
+  int rc = UNQLITE_OK;
+  int h;
+  int tErrno;                      /* Error code from system call errors */
+
+   if( pFile->eFileLock<=eFileLock ){
+    return UNQLITE_OK;
+  }
+  unixEnterMutex();
+  
+  h = pFile->h;
+  pInode = pFile->pInode;
+  
+  if( pFile->eFileLock>SHARED_LOCK ){
+    /* downgrading to a shared lock on NFS involves clearing the write lock
+    ** before establishing the readlock - to avoid a race condition we downgrade
+    ** the lock in 2 blocks, so that part of the range will be covered by a 
+    ** write lock until the rest is covered by a read lock:
+    **  1:   [WWWWW]
+    **  2:   [....W]
+    **  3:   [RRRRW]
+    **  4:   [RRRR.]
+    */
+    if( eFileLock==SHARED_LOCK ){
+      if( handleNFSUnlock ){
+        off_t divSize = SHARED_SIZE - 1;
+        
+        lock.l_type = F_UNLCK;
+        lock.l_whence = SEEK_SET;
+        lock.l_start = SHARED_FIRST;
+        lock.l_len = divSize;
+        if( fcntl(h, F_SETLK, &lock)==(-1) ){
+          tErrno = errno;
+                 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+          if( IS_LOCK_ERROR(rc) ){
+            pFile->lastErrno = tErrno;
+          }
+          goto end_unlock;
+        }
+        lock.l_type = F_RDLCK;
+        lock.l_whence = SEEK_SET;
+        lock.l_start = SHARED_FIRST;
+        lock.l_len = divSize;
+        if( fcntl(h, F_SETLK, &lock)==(-1) ){
+          tErrno = errno;
+                 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+          if( IS_LOCK_ERROR(rc) ){
+            pFile->lastErrno = tErrno;
+          }
+          goto end_unlock;
+        }
+        lock.l_type = F_UNLCK;
+        lock.l_whence = SEEK_SET;
+        lock.l_start = SHARED_FIRST+divSize;
+        lock.l_len = SHARED_SIZE-divSize;
+        if( fcntl(h, F_SETLK, &lock)==(-1) ){
+          tErrno = errno;
+                 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+          if( IS_LOCK_ERROR(rc) ){
+            pFile->lastErrno = tErrno;
+          }
+          goto end_unlock;
+        }
+      }else{
+        lock.l_type = F_RDLCK;
+        lock.l_whence = SEEK_SET;
+        lock.l_start = SHARED_FIRST;
+        lock.l_len = SHARED_SIZE;
+        if( fcntl(h, F_SETLK, &lock)==(-1) ){
+          tErrno = errno;
+                 rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+          if( IS_LOCK_ERROR(rc) ){
+            pFile->lastErrno = tErrno;
+          }
+          goto end_unlock;
+        }
+      }
+    }
+    lock.l_type = F_UNLCK;
+    lock.l_whence = SEEK_SET;
+    lock.l_start = PENDING_BYTE;
+    lock.l_len = 2L;
+    if( fcntl(h, F_SETLK, &lock)!=(-1) ){
+      pInode->eFileLock = SHARED_LOCK;
+    }else{
+      tErrno = errno;
+         rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+      if( IS_LOCK_ERROR(rc) ){
+        pFile->lastErrno = tErrno;
+      }
+      goto end_unlock;
+    }
+  }
+  if( eFileLock==NO_LOCK ){
+    /* Decrement the shared lock counter.  Release the lock using an
+    ** OS call only when all threads in this same process have released
+    ** the lock.
+    */
+    pInode->nShared--;
+    if( pInode->nShared==0 ){
+      lock.l_type = F_UNLCK;
+      lock.l_whence = SEEK_SET;
+      lock.l_start = lock.l_len = 0L;
+      
+      if( fcntl(h, F_SETLK, &lock)!=(-1) ){
+        pInode->eFileLock = NO_LOCK;
+      }else{
+        tErrno = errno;
+               rc = unqliteErrorFromPosixError(tErrno, UNQLITE_LOCKERR);
+        if( IS_LOCK_ERROR(rc) ){
+          pFile->lastErrno = tErrno;
+        }
+        pInode->eFileLock = NO_LOCK;
+        pFile->eFileLock = NO_LOCK;
+      }
+    }
+
+    /* Decrement the count of locks against this same file.  When the
+    ** count reaches zero, close any other file descriptors whose close
+    ** was deferred because of outstanding locks.
+    */
+    pInode->nLock--;
+    if( pInode->nLock==0 ){
+      int rc2 = closePendingFds(pFile);
+      if( rc==UNQLITE_OK ){
+        rc = rc2;
+      }
+    }
+  }
+       
+end_unlock:
+
+  unixLeaveMutex();
+  
+  if( rc==UNQLITE_OK ) pFile->eFileLock = eFileLock;
+  return rc;
+}
+/*
+** Lower the locking level on file descriptor pFile to eFileLock.  eFileLock
+** must be either NO_LOCK or SHARED_LOCK.
+**
+** If the locking level of the file descriptor is already at or below
+** the requested locking level, this routine is a no-op.
+*/
+static int unixUnlock(unqlite_file *id, int eFileLock){
+  return _posixUnlock(id, eFileLock, 0);
+}
+/*
+** This function performs the parts of the "close file" operation 
+** common to all locking schemes. It closes the directory and file
+** handles, if they are valid, and sets all fields of the unixFile
+** structure to 0.
+**
+*/
+static int closeUnixFile(unqlite_file *id){
+  unixFile *pFile = (unixFile*)id;
+  if( pFile ){
+    if( pFile->dirfd>=0 ){
+      int err = close(pFile->dirfd);
+      if( err ){
+        pFile->lastErrno = errno;
+        return UNQLITE_IOERR;
+      }else{
+        pFile->dirfd=-1;
+      }
+    }
+    if( pFile->h>=0 ){
+      int err = close(pFile->h);
+      if( err ){
+        pFile->lastErrno = errno;
+        return UNQLITE_IOERR;
+      }
+    }
+    unqlite_free(pFile->pUnused);
+    SyZero(pFile,sizeof(unixFile));
+  }
+  return UNQLITE_OK;
+}
+/*
+** Close a file.
+*/
+static int unixClose(unqlite_file *id){
+  int rc = UNQLITE_OK;
+  if( id ){
+    unixFile *pFile = (unixFile *)id;
+    unixUnlock(id, NO_LOCK);
+    unixEnterMutex();
+    if( pFile->pInode && pFile->pInode->nLock ){
+      /* If there are outstanding locks, do not actually close the file just
+      ** yet because that would clear those locks.  Instead, add the file
+      ** descriptor to pInode->pUnused list.  It will be automatically closed 
+      ** when the last lock is cleared.
+      */
+      setPendingFd(pFile);
+    }
+    releaseInodeInfo(pFile);
+    rc = closeUnixFile(id);
+    unixLeaveMutex();
+  }
+  return rc;
+}
+/************** End of the posix advisory lock implementation *****************
+******************************************************************************/
+/*
+**
+** The next division contains implementations for all methods of the 
+** unqlite_file object other than the locking methods.  The locking
+** methods were defined in divisions above (one locking method per
+** division).  Those methods that are common to all locking modes
+** are gather together into this division.
+*/
+/*
+** Seek to the offset passed as the second argument, then read cnt 
+** bytes into pBuf. Return the number of bytes actually read.
+**
+** NB:  If you define USE_PREAD or USE_PREAD64, then it might also
+** be necessary to define _XOPEN_SOURCE to be 500.  This varies from
+** one system to another.  Since SQLite does not define USE_PREAD
+** any form by default, we will not attempt to define _XOPEN_SOURCE.
+** See tickets #2741 and #2681.
+**
+** To avoid stomping the errno value on a failed read the lastErrno value
+** is set before returning.
+*/
+static int seekAndRead(unixFile *id, unqlite_int64 offset, void *pBuf, int cnt){
+  int got;
+#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
+  unqlite_int64 newOffset;
+#endif
+#if defined(USE_PREAD)
+  got = pread(id->h, pBuf, cnt, offset);
+#elif defined(USE_PREAD64)
+  got = pread64(id->h, pBuf, cnt, offset);
+#else
+  newOffset = lseek(id->h, offset, SEEK_SET);
+  
+  if( newOffset!=offset ){
+    if( newOffset == -1 ){
+      ((unixFile*)id)->lastErrno = errno;
+    }else{
+      ((unixFile*)id)->lastErrno = 0;                  
+    }
+    return -1;
+  }
+  got = read(id->h, pBuf, cnt);
+#endif
+  if( got<0 ){
+    ((unixFile*)id)->lastErrno = errno;
+  }
+  return got;
+}
+/*
+** Read data from a file into a buffer.  Return UNQLITE_OK if all
+** bytes were read successfully and UNQLITE_IOERR if anything goes
+** wrong.
+*/
+static int unixRead(
+  unqlite_file *id, 
+  void *pBuf, 
+  unqlite_int64 amt,
+  unqlite_int64 offset
+){
+  unixFile *pFile = (unixFile *)id;
+  int got;
+  
+  got = seekAndRead(pFile, offset, pBuf, (int)amt);
+  if( got==(int)amt ){
+    return UNQLITE_OK;
+  }else if( got<0 ){
+    /* lastErrno set by seekAndRead */
+    return UNQLITE_IOERR;
+  }else{
+    pFile->lastErrno = 0; /* not a system error */
+    /* Unread parts of the buffer must be zero-filled */
+    SyZero(&((char*)pBuf)[got],(sxu32)amt-got);
+    return UNQLITE_IOERR;
+  }
+}
+/*
+** Seek to the offset in id->offset then read cnt bytes into pBuf.
+** Return the number of bytes actually read.  Update the offset.
+**
+** To avoid stomping the errno value on a failed write the lastErrno value
+** is set before returning.
+*/
+static int seekAndWrite(unixFile *id, unqlite_int64 offset, const void *pBuf, unqlite_int64 cnt){
+  int got;
+#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
+  unqlite_int64 newOffset;
+#endif
+  
+#if defined(USE_PREAD)
+  got = pwrite(id->h, pBuf, cnt, offset);
+#elif defined(USE_PREAD64)
+  got = pwrite64(id->h, pBuf, cnt, offset);
+#else
+  newOffset = lseek(id->h, offset, SEEK_SET);
+  if( newOffset!=offset ){
+    if( newOffset == -1 ){
+      ((unixFile*)id)->lastErrno = errno;
+    }else{
+      ((unixFile*)id)->lastErrno = 0;                  
+    }
+    return -1;
+  }
+  got = write(id->h, pBuf, cnt);
+#endif
+  if( got<0 ){
+    ((unixFile*)id)->lastErrno = errno;
+  }
+  return got;
+}
+/*
+** Write data from a buffer into a file.  Return UNQLITE_OK on success
+** or some other error code on failure.
+*/
+static int unixWrite(
+  unqlite_file *id, 
+  const void *pBuf, 
+  unqlite_int64 amt,
+  unqlite_int64 offset 
+){
+  unixFile *pFile = (unixFile*)id;
+  int wrote = 0;
+
+  while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
+    amt -= wrote;
+    offset += wrote;
+    pBuf = &((char*)pBuf)[wrote];
+  }
+  
+  if( amt>0 ){
+    if( wrote<0 ){
+      /* lastErrno set by seekAndWrite */
+      return UNQLITE_IOERR;
+    }else{
+      pFile->lastErrno = 0; /* not a system error */
+      return UNQLITE_FULL;
+    }
+  }
+  return UNQLITE_OK;
+}
+/*
+** We do not trust systems to provide a working fdatasync().  Some do.
+** Others do no.  To be safe, we will stick with the (slower) fsync().
+** If you know that your system does support fdatasync() correctly,
+** then simply compile with -Dfdatasync=fdatasync
+*/
+#if !defined(fdatasync) && !defined(__linux__)
+# define fdatasync fsync
+#endif
+
+/*
+** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not
+** the F_FULLFSYNC macro is defined.  F_FULLFSYNC is currently
+** only available on Mac OS X.  But that could change.
+*/
+#ifdef F_FULLFSYNC
+# define HAVE_FULLFSYNC 1
+#else
+# define HAVE_FULLFSYNC 0
+#endif
+/*
+** The fsync() system call does not work as advertised on many
+** unix systems.  The following procedure is an attempt to make
+** it work better.
+**
+**
+** SQLite sets the dataOnly flag if the size of the file is unchanged.
+** The idea behind dataOnly is that it should only write the file content
+** to disk, not the inode.  We only set dataOnly if the file size is 
+** unchanged since the file size is part of the inode.  However, 
+** Ted Ts'o tells us that fdatasync() will also write the inode if the
+** file size has changed.  The only real difference between fdatasync()
+** and fsync(), Ted tells us, is that fdatasync() will not flush the
+** inode if the mtime or owner or other inode attributes have changed.
+** We only care about the file size, not the other file attributes, so
+** as far as SQLite is concerned, an fdatasync() is always adequate.
+** So, we always use fdatasync() if it is available, regardless of
+** the value of the dataOnly flag.
+*/
+static int full_fsync(int fd, int fullSync, int dataOnly){
+  int rc;
+#if HAVE_FULLFSYNC
+  SXUNUSED(dataOnly);
+#else
+  SXUNUSED(fullSync);
+  SXUNUSED(dataOnly);
+#endif
+
+  /* If we compiled with the UNQLITE_NO_SYNC flag, then syncing is a
+  ** no-op
+  */
+#if HAVE_FULLFSYNC
+  if( fullSync ){
+    rc = fcntl(fd, F_FULLFSYNC, 0);
+  }else{
+    rc = 1;
+  }
+  /* If the FULLFSYNC failed, fall back to attempting an fsync().
+  ** It shouldn't be possible for fullfsync to fail on the local 
+  ** file system (on OSX), so failure indicates that FULLFSYNC
+  ** isn't supported for this file system. So, attempt an fsync 
+  ** and (for now) ignore the overhead of a superfluous fcntl call.  
+  ** It'd be better to detect fullfsync support once and avoid 
+  ** the fcntl call every time sync is called.
+  */
+  if( rc ) rc = fsync(fd);
+
+#elif defined(__APPLE__)
+  /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly
+  ** so currently we default to the macro that redefines fdatasync to fsync
+  */
+  rc = fsync(fd);
+#else 
+  rc = fdatasync(fd);
+#endif /* ifdef UNQLITE_NO_SYNC elif HAVE_FULLFSYNC */
+  if( rc!= -1 ){
+    rc = 0;
+  }
+  return rc;
+}
+/*
+** Make sure all writes to a particular file are committed to disk.
+**
+** If dataOnly==0 then both the file itself and its metadata (file
+** size, access time, etc) are synced.  If dataOnly!=0 then only the
+** file data is synced.
+**
+** Under Unix, also make sure that the directory entry for the file
+** has been created by fsync-ing the directory that contains the file.
+** If we do not do this and we encounter a power failure, the directory
+** entry for the journal might not exist after we reboot.  The next
+** SQLite to access the file will not know that the journal exists (because
+** the directory entry for the journal was never created) and the transaction
+** will not roll back - possibly leading to database corruption.
+*/
+static int unixSync(unqlite_file *id, int flags){
+  int rc;
+  unixFile *pFile = (unixFile*)id;
+
+  int isDataOnly = (flags&UNQLITE_SYNC_DATAONLY);
+  int isFullsync = (flags&0x0F)==UNQLITE_SYNC_FULL;
+
+  rc = full_fsync(pFile->h, isFullsync, isDataOnly);
+
+  if( rc ){
+    pFile->lastErrno = errno;
+    return UNQLITE_IOERR;
+  }
+  if( pFile->dirfd>=0 ){
+    int err;
+#ifndef UNQLITE_DISABLE_DIRSYNC
+    /* The directory sync is only attempted if full_fsync is
+    ** turned off or unavailable.  If a full_fsync occurred above,
+    ** then the directory sync is superfluous.
+    */
+    if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){
+       /*
+       ** We have received multiple reports of fsync() returning
+       ** errors when applied to directories on certain file systems.
+       ** A failed directory sync is not a big deal.  So it seems
+       ** better to ignore the error.  Ticket #1657
+       */
+       /* pFile->lastErrno = errno; */
+       /* return UNQLITE_IOERR; */
+    }
+#endif
+    err = close(pFile->dirfd); /* Only need to sync once, so close the */
+    if( err==0 ){              /* directory when we are done */
+      pFile->dirfd = -1;
+    }else{
+      pFile->lastErrno = errno;
+      rc = UNQLITE_IOERR;
+    }
+  }
+  return rc;
+}
+/*
+** Truncate an open file to a specified size
+*/
+static int unixTruncate(unqlite_file *id, sxi64 nByte){
+  unixFile *pFile = (unixFile *)id;
+  int rc;
+
+  rc = ftruncate(pFile->h, (off_t)nByte);
+  if( rc ){
+    pFile->lastErrno = errno;
+    return UNQLITE_IOERR;
+  }else{
+    return UNQLITE_OK;
+  }
+}
+/*
+** Determine the current size of a file in bytes
+*/
+static int unixFileSize(unqlite_file *id,sxi64 *pSize){
+  int rc;
+  struct stat buf;
+  
+  rc = fstat(((unixFile*)id)->h, &buf);
+  
+  if( rc!=0 ){
+    ((unixFile*)id)->lastErrno = errno;
+    return UNQLITE_IOERR;
+  }
+  *pSize = buf.st_size;
+
+  /* When opening a zero-size database, the findInodeInfo() procedure
+  ** writes a single byte into that file in order to work around a bug
+  ** in the OS-X msdos filesystem.  In order to avoid problems with upper
+  ** layers, we need to report this file size as zero even though it is
+  ** really 1.   Ticket #3260.
+  */
+  if( *pSize==1 ) *pSize = 0;
+
+  return UNQLITE_OK;
+}
+/*
+** Return the sector size in bytes of the underlying block device for
+** the specified file. This is almost always 512 bytes, but may be
+** larger for some devices.
+**
+** SQLite code assumes this function cannot fail. It also assumes that
+** if two files are created in the same file-system directory (i.e.
+** a database and its journal file) that the sector size will be the
+** same for both.
+*/
+static int unixSectorSize(unqlite_file *NotUsed){
+  SXUNUSED(NotUsed);
+  return UNQLITE_DEFAULT_SECTOR_SIZE;
+}
+/*
+** This vector defines all the methods that can operate on an
+** unqlite_file for Windows systems.
+*/
+static const unqlite_io_methods unixIoMethod = {
+  1,                              /* iVersion */
+  unixClose,                       /* xClose */
+  unixRead,                        /* xRead */
+  unixWrite,                       /* xWrite */
+  unixTruncate,                    /* xTruncate */
+  unixSync,                        /* xSync */
+  unixFileSize,                    /* xFileSize */
+  unixLock,                        /* xLock */
+  unixUnlock,                      /* xUnlock */
+  unixCheckReservedLock,           /* xCheckReservedLock */
+  unixSectorSize,                  /* xSectorSize */
+};
+/****************************************************************************
+**************************** unqlite_vfs methods ****************************
+**
+** This division contains the implementation of methods on the
+** unqlite_vfs object.
+*/
+/*
+** Initialize the contents of the unixFile structure pointed to by pId.
+*/
+static int fillInUnixFile(
+  unqlite_vfs *pVfs,      /* Pointer to vfs object */
+  int h,                  /* Open file descriptor of file being opened */
+  int dirfd,              /* Directory file descriptor */
+  unqlite_file *pId,      /* Write to the unixFile structure here */
+  const char *zFilename,  /* Name of the file being opened */
+  int noLock,             /* Omit locking if true */
+  int isDelete            /* Delete on close if true */
+){
+  const unqlite_io_methods *pLockingStyle = &unixIoMethod;
+  unixFile *pNew = (unixFile *)pId;
+  int rc = UNQLITE_OK;
+
+  /* Parameter isDelete is only used on vxworks. Express this explicitly 
+  ** here to prevent compiler warnings about unused parameters.
+  */
+  SXUNUSED(isDelete);
+  SXUNUSED(noLock);
+  SXUNUSED(pVfs);
+
+  pNew->h = h;
+  pNew->dirfd = dirfd;
+  pNew->fileFlags = 0;
+  pNew->zPath = zFilename;
+  
+  unixEnterMutex();
+  rc = findInodeInfo(pNew, &pNew->pInode);
+  if( rc!=UNQLITE_OK ){
+      /* If an error occured in findInodeInfo(), close the file descriptor
+      ** immediately, before releasing the mutex. findInodeInfo() may fail
+      ** in two scenarios:
+      **
+      **   (a) A call to fstat() failed.
+      **   (b) A malloc failed.
+      **
+      ** Scenario (b) may only occur if the process is holding no other
+      ** file descriptors open on the same file. If there were other file
+      ** descriptors on this file, then no malloc would be required by
+      ** findInodeInfo(). If this is the case, it is quite safe to close
+      ** handle h - as it is guaranteed that no posix locks will be released
+      ** by doing so.
+      **
+      ** If scenario (a) caused the error then things are not so safe. The
+      ** implicit assumption here is that if fstat() fails, things are in
+      ** such bad shape that dropping a lock or two doesn't matter much.
+      */
+      close(h);
+      h = -1;
+  }
+  unixLeaveMutex();
+  
+  pNew->lastErrno = 0;
+  if( rc!=UNQLITE_OK ){
+    if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */
+    if( h>=0 ) close(h);
+  }else{
+    pNew->pMethod = pLockingStyle;
+  }
+  return rc;
+}
+/*
+** Open a file descriptor to the directory containing file zFilename.
+** If successful, *pFd is set to the opened file descriptor and
+** UNQLITE_OK is returned. If an error occurs, either UNQLITE_NOMEM
+** or UNQLITE_CANTOPEN is returned and *pFd is set to an undefined
+** value.
+**
+** If UNQLITE_OK is returned, the caller is responsible for closing
+** the file descriptor *pFd using close().
+*/
+static int openDirectory(const char *zFilename, int *pFd){
+  sxu32 ii;
+  int fd = -1;
+  char zDirname[MAX_PATHNAME+1];
+  sxu32 n;
+  n = Systrcpy(zDirname,sizeof(zDirname),zFilename,0);
+  for(ii=n; ii>1 && zDirname[ii]!='/'; ii--);
+  if( ii>0 ){
+    zDirname[ii] = '\0';
+    fd = open(zDirname, O_RDONLY|O_BINARY, 0);
+    if( fd>=0 ){
+#ifdef FD_CLOEXEC
+      fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+#endif
+    }
+  }
+  *pFd = fd;
+  return (fd>=0?UNQLITE_OK: UNQLITE_IOERR );
+}
+/*
+** Search for an unused file descriptor that was opened on the database 
+** file (not a journal or master-journal file) identified by pathname
+** zPath with UNQLITE_OPEN_XXX flags matching those passed as the second
+** argument to this function.
+**
+** Such a file descriptor may exist if a database connection was closed
+** but the associated file descriptor could not be closed because some
+** other file descriptor open on the same file is holding a file-lock.
+** Refer to comments in the unixClose() function and the lengthy comment
+** describing "Posix Advisory Locking" at the start of this file for 
+** further details. Also, ticket #4018.
+**
+** If a suitable file descriptor is found, then it is returned. If no
+** such file descriptor is located, -1 is returned.
+*/
+static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
+  UnixUnusedFd *pUnused = 0;
+  struct stat sStat;                   /* Results of stat() call */
+  /* A stat() call may fail for various reasons. If this happens, it is
+  ** almost certain that an open() call on the same path will also fail.
+  ** For this reason, if an error occurs in the stat() call here, it is
+  ** ignored and -1 is returned. The caller will try to open a new file
+  ** descriptor on the same path, fail, and return an error to SQLite.
+  **
+  ** Even if a subsequent open() call does succeed, the consequences of
+  ** not searching for a resusable file descriptor are not dire.  */
+  if( 0==stat(zPath, &sStat) ){
+    unixInodeInfo *pInode;
+
+    unixEnterMutex();
+    pInode = inodeList;
+    while( pInode && (pInode->fileId.dev!=sStat.st_dev
+                     || pInode->fileId.ino!=sStat.st_ino) ){
+       pInode = pInode->pNext;
+    }
+    if( pInode ){
+      UnixUnusedFd **pp;
+      for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
+      pUnused = *pp;
+      if( pUnused ){
+        *pp = pUnused->pNext;
+      }
+    }
+    unixLeaveMutex();
+  }
+  return pUnused;
+}
+/*
+** This function is called by unixOpen() to determine the unix permissions
+** to create new files with. If no error occurs, then UNQLITE_OK is returned
+** and a value suitable for passing as the third argument to open(2) is
+** written to *pMode. If an IO error occurs, an SQLite error code is 
+** returned and the value of *pMode is not modified.
+**
+** If the file being opened is a temporary file, it is always created with
+** the octal permissions 0600 (read/writable by owner only). If the file
+** is a database or master journal file, it is created with the permissions 
+** mask UNQLITE_DEFAULT_FILE_PERMISSIONS.
+**
+** Finally, if the file being opened is a WAL or regular journal file, then 
+** this function queries the file-system for the permissions on the 
+** corresponding database file and sets *pMode to this value. Whenever 
+** possible, WAL and journal files are created using the same permissions 
+** as the associated database file.
+*/
+static int findCreateFileMode(
+  const char *zPath,              /* Path of file (possibly) being created */
+  int flags,                      /* Flags passed as 4th argument to xOpen() */
+  mode_t *pMode                   /* OUT: Permissions to open file with */
+){
+  int rc = UNQLITE_OK;             /* Return Code */
+  if( flags & UNQLITE_OPEN_TEMP_DB ){
+    *pMode = 0600;
+     SXUNUSED(zPath);
+  }else{
+    *pMode = UNQLITE_DEFAULT_FILE_PERMISSIONS;
+  }
+  return rc;
+}
+/*
+** Open the file zPath.
+** 
+** Previously, the SQLite OS layer used three functions in place of this
+** one:
+**
+**     unqliteOsOpenReadWrite();
+**     unqliteOsOpenReadOnly();
+**     unqliteOsOpenExclusive();
+**
+** These calls correspond to the following combinations of flags:
+**
+**     ReadWrite() ->     (READWRITE | CREATE)
+**     ReadOnly()  ->     (READONLY) 
+**     OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE)
+**
+** The old OpenExclusive() accepted a boolean argument - "delFlag". If
+** true, the file was configured to be automatically deleted when the
+** file handle closed. To achieve the same effect using this new 
+** interface, add the DELETEONCLOSE flag to those specified above for 
+** OpenExclusive().
+*/
+static int unixOpen(
+  unqlite_vfs *pVfs,           /* The VFS for which this is the xOpen method */
+  const char *zPath,           /* Pathname of file to be opened */
+  unqlite_file *pFile,         /* The file descriptor to be filled in */
+  unsigned int flags           /* Input flags to control the opening */
+){
+  unixFile *p = (unixFile *)pFile;
+  int fd = -1;                   /* File descriptor returned by open() */
+  int dirfd = -1;                /* Directory file descriptor */
+  int openFlags = 0;             /* Flags to pass to open() */
+  int noLock;                    /* True to omit locking primitives */
+  int rc = UNQLITE_OK;            /* Function Return Code */
+  UnixUnusedFd *pUnused;
+  int isExclusive  = (flags & UNQLITE_OPEN_EXCLUSIVE);
+  int isDelete     = (flags & UNQLITE_OPEN_TEMP_DB);
+  int isCreate     = (flags & UNQLITE_OPEN_CREATE);
+  int isReadonly   = (flags & UNQLITE_OPEN_READONLY);
+  int isReadWrite  = (flags & UNQLITE_OPEN_READWRITE);
+  /* If creating a master or main-file journal, this function will open
+  ** a file-descriptor on the directory too. The first time unixSync()
+  ** is called the directory file descriptor will be fsync()ed and close()d.
+  */
+  int isOpenDirectory = isCreate;
+  const char *zName = zPath;
+
+  SyZero(p,sizeof(unixFile));
+  
+  pUnused = findReusableFd(zName, flags);
+  if( pUnused ){
+         fd = pUnused->fd;
+  }else{
+         pUnused = unqlite_malloc(sizeof(*pUnused));
+      if( !pUnused ){
+        return UNQLITE_NOMEM;
+      }
+  }
+  p->pUnused = pUnused;
+  
+  /* Determine the value of the flags parameter passed to POSIX function
+  ** open(). These must be calculated even if open() is not called, as
+  ** they may be stored as part of the file handle and used by the 
+  ** 'conch file' locking functions later on.  */
+  if( isReadonly )  openFlags |= O_RDONLY;
+  if( isReadWrite ) openFlags |= O_RDWR;
+  if( isCreate )    openFlags |= O_CREAT;
+  if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW);
+  openFlags |= (O_LARGEFILE|O_BINARY);
+
+  if( fd<0 ){
+    mode_t openMode;              /* Permissions to create file with */
+    rc = findCreateFileMode(zName, flags, &openMode);
+    if( rc!=UNQLITE_OK ){
+      return rc;
+    }
+    fd = open(zName, openFlags, openMode);
+    if( fd<0 ){
+         rc = UNQLITE_IOERR;
+      goto open_finished;
+    }
+  }
+  
+  if( p->pUnused ){
+    p->pUnused->fd = fd;
+    p->pUnused->flags = flags;
+  }
+
+  if( isDelete ){
+    unlink(zName);
+  }
+
+  if( isOpenDirectory ){
+    rc = openDirectory(zPath, &dirfd);
+    if( rc!=UNQLITE_OK ){
+      /* It is safe to close fd at this point, because it is guaranteed not
+      ** to be open on a database file. If it were open on a database file,
+      ** it would not be safe to close as this would release any locks held
+      ** on the file by this process.  */
+      close(fd);             /* silently leak if fail, already in error */
+      goto open_finished;
+    }
+  }
+
+#ifdef FD_CLOEXEC
+  fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+#endif
+
+  noLock = 0;
+
+#if defined(__APPLE__) 
+  struct statfs fsInfo;
+  if( fstatfs(fd, &fsInfo) == -1 ){
+    ((unixFile*)pFile)->lastErrno = errno;
+    if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */
+    close(fd); /* silently leak if fail, in error */
+    return UNQLITE_IOERR;
+  }
+  if (0 == SyStrncmp("msdos", fsInfo.f_fstypename, 5)) {
+    ((unixFile*)pFile)->fsFlags |= UNQLITE_FSFLAGS_IS_MSDOS;
+  }
+#endif
+  
+  rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete);
+open_finished:
+  if( rc!=UNQLITE_OK ){
+    unqlite_free(p->pUnused);
+  }
+  return rc;
+}
+/*
+** Delete the file at zPath. If the dirSync argument is true, fsync()
+** the directory after deleting the file.
+*/
+static int unixDelete(
+  unqlite_vfs *NotUsed,     /* VFS containing this as the xDelete method */
+  const char *zPath,        /* Name of file to be deleted */
+  int dirSync               /* If true, fsync() directory after deleting file */
+){
+  int rc = UNQLITE_OK;
+  SXUNUSED(NotUsed);
+  
+  if( unlink(zPath)==(-1) && errno!=ENOENT ){
+         return UNQLITE_IOERR;
+  }
+#ifndef UNQLITE_DISABLE_DIRSYNC
+  if( dirSync ){
+    int fd;
+    rc = openDirectory(zPath, &fd);
+    if( rc==UNQLITE_OK ){
+      if( fsync(fd) )
+      {
+        rc = UNQLITE_IOERR;
+      }
+      if( close(fd) && !rc ){
+        rc = UNQLITE_IOERR;
+      }
+    }
+  }
+#endif
+  return rc;
+}
+/*
+** Sleep for a little while.  Return the amount of time slept.
+** The argument is the number of microseconds we want to sleep.
+** The return value is the number of microseconds of sleep actually
+** requested from the underlying operating system, a number which
+** might be greater than or equal to the argument, but not less
+** than the argument.
+*/
+static int unixSleep(unqlite_vfs *NotUsed, int microseconds)
+{
+#if defined(HAVE_USLEEP) && HAVE_USLEEP
+  usleep(microseconds);
+  SXUNUSED(NotUsed);
+  return microseconds;
+#else
+  int seconds = (microseconds+999999)/1000000;
+  SXUNUSED(NotUsed);
+  sleep(seconds);
+  return seconds*1000000;
+#endif
+}
+/*
+ * Export the current system time.
+ */
+static int unixCurrentTime(unqlite_vfs *pVfs,Sytm *pOut)
+{
+       struct tm *pTm;
+       time_t tt;
+       SXUNUSED(pVfs);
+       time(&tt);
+       pTm = gmtime(&tt);
+       if( pTm ){ /* Yes, it can fail */
+               STRUCT_TM_TO_SYTM(pTm,pOut);
+       }
+       return UNQLITE_OK;
+}
+/*
+** Test the existance of or access permissions of file zPath. The
+** test performed depends on the value of flags:
+**
+**     UNQLITE_ACCESS_EXISTS: Return 1 if the file exists
+**     UNQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable.
+**     UNQLITE_ACCESS_READONLY: Return 1 if the file is readable.
+**
+** Otherwise return 0.
+*/
+static int unixAccess(
+  unqlite_vfs *NotUsed,   /* The VFS containing this xAccess method */
+  const char *zPath,      /* Path of the file to examine */
+  int flags,              /* What do we want to learn about the zPath file? */
+  int *pResOut            /* Write result boolean here */
+){
+  int amode = 0;
+  SXUNUSED(NotUsed);
+  switch( flags ){
+    case UNQLITE_ACCESS_EXISTS:
+      amode = F_OK;
+      break;
+    case UNQLITE_ACCESS_READWRITE:
+      amode = W_OK|R_OK;
+      break;
+    case UNQLITE_ACCESS_READ:
+      amode = R_OK;
+      break;
+    default:
+               /* Can't happen */
+      break;
+  }
+  *pResOut = (access(zPath, amode)==0);
+  if( flags==UNQLITE_ACCESS_EXISTS && *pResOut ){
+    struct stat buf;
+    if( 0==stat(zPath, &buf) && buf.st_size==0 ){
+      *pResOut = 0;
+    }
+  }
+  return UNQLITE_OK;
+}
+/*
+** Turn a relative pathname into a full pathname. The relative path
+** is stored as a nul-terminated string in the buffer pointed to by
+** zPath. 
+**
+** zOut points to a buffer of at least unqlite_vfs.mxPathname bytes 
+** (in this case, MAX_PATHNAME bytes). The full-path is written to
+** this buffer before returning.
+*/
+static int unixFullPathname(
+  unqlite_vfs *pVfs,            /* Pointer to vfs object */
+  const char *zPath,            /* Possibly relative input path */
+  int nOut,                     /* Size of output buffer in bytes */
+  char *zOut                    /* Output buffer */
+){
+  if( zPath[0]=='/' ){
+         Systrcpy(zOut,(sxu32)nOut,zPath,0);
+         SXUNUSED(pVfs);
+  }else{
+    sxu32 nCwd;
+       zOut[nOut-1] = '\0';
+    if( getcwd(zOut, nOut-1)==0 ){
+               return UNQLITE_IOERR;
+    }
+    nCwd = SyStrlen(zOut);
+    SyBufferFormat(&zOut[nCwd],(sxu32)nOut-nCwd,"/%s",zPath);
+  }
+  return UNQLITE_OK;
+}
+/*
+ * Export the Unix Vfs.
+ */
+UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void)
+{
+       static const unqlite_vfs sUnixvfs = {
+               "Unix",              /* Vfs name */
+               1,                   /* Vfs structure version */
+               sizeof(unixFile),    /* szOsFile */
+               MAX_PATHNAME,        /* mxPathName */
+               unixOpen,            /* xOpen */
+               unixDelete,          /* xDelete */
+               unixAccess,          /* xAccess */
+               unixFullPathname,    /* xFullPathname */
+               0,                   /* xTmp */
+               unixSleep,           /* xSleep */
+               unixCurrentTime,     /* xCurrentTime */
+               0,                   /* xGetLastError */
+       };
+       return &sUnixvfs;
+}
+
+#endif /* __UNIXES__ */
+
+/*
+ * ----------------------------------------------------------
+ * File: os_win.c
+ * MD5: ab70fb386c21b39a08b0eb776a8391ab
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: os_win.c v1.2 Win7 2012-11-10 12:10 devel <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* Omit the whole layer from the build if compiling for platforms other than Windows */
+#ifdef __WINNT__
+/* This file contains code that is specific to windows. (Mostly SQLite3 source tree) */
+#include <Windows.h>
+/*
+** Some microsoft compilers lack this definition.
+*/
+#ifndef INVALID_FILE_ATTRIBUTES
+# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) 
+#endif
+/*
+** WinCE lacks native support for file locking so we have to fake it
+** with some code of our own.
+*/
+#ifdef __WIN_CE__
+typedef struct winceLock {
+  int nReaders;       /* Number of reader locks obtained */
+  BOOL bPending;      /* Indicates a pending lock has been obtained */
+  BOOL bReserved;     /* Indicates a reserved lock has been obtained */
+  BOOL bExclusive;    /* Indicates an exclusive lock has been obtained */
+} winceLock;
+#define AreFileApisANSI() 1
+#define FormatMessageW(a,b,c,d,e,f,g) 0
+#endif
+
+/*
+** The winFile structure is a subclass of unqlite_file* specific to the win32
+** portability layer.
+*/
+typedef struct winFile winFile;
+struct winFile {
+  const unqlite_io_methods *pMethod; /*** Must be first ***/
+  unqlite_vfs *pVfs;      /* The VFS used to open this file */
+  HANDLE h;               /* Handle for accessing the file */
+  sxu8 locktype;          /* Type of lock currently held on this file */
+  short sharedLockByte;   /* Randomly chosen byte used as a shared lock */
+  DWORD lastErrno;        /* The Windows errno from the last I/O error */
+  DWORD sectorSize;       /* Sector size of the device file is on */
+  int szChunk;            /* Chunk size */
+#ifdef __WIN_CE__
+  WCHAR *zDeleteOnClose;  /* Name of file to delete when closing */
+  HANDLE hMutex;          /* Mutex used to control access to shared lock */  
+  HANDLE hShared;         /* Shared memory segment used for locking */
+  winceLock local;        /* Locks obtained by this instance of winFile */
+  winceLock *shared;      /* Global shared lock memory for the file  */
+#endif
+};
+/*
+** Convert a UTF-8 string to microsoft unicode (UTF-16?). 
+**
+** Space to hold the returned string is obtained from HeapAlloc().
+*/
+static WCHAR *utf8ToUnicode(const char *zFilename){
+  int nChar;
+  WCHAR *zWideFilename;
+
+  nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
+  zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nChar*sizeof(zWideFilename[0]) );
+  if( zWideFilename==0 ){
+    return 0;
+  }
+  nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
+  if( nChar==0 ){
+    HeapFree(GetProcessHeap(),0,zWideFilename);
+    zWideFilename = 0;
+  }
+  return zWideFilename;
+}
+
+/*
+** Convert microsoft unicode to UTF-8.  Space to hold the returned string is
+** obtained from malloc().
+*/
+static char *unicodeToUtf8(const WCHAR *zWideFilename){
+  int nByte;
+  char *zFilename;
+
+  nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
+  zFilename = (char *)HeapAlloc(GetProcessHeap(),0,nByte );
+  if( zFilename==0 ){
+    return 0;
+  }
+  nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
+                              0, 0);
+  if( nByte == 0 ){
+    HeapFree(GetProcessHeap(),0,zFilename);
+    zFilename = 0;
+  }
+  return zFilename;
+}
+
+/*
+** Convert an ansi string to microsoft unicode, based on the
+** current codepage settings for file apis.
+** 
+** Space to hold the returned string is obtained
+** from malloc.
+*/
+static WCHAR *mbcsToUnicode(const char *zFilename){
+  int nByte;
+  WCHAR *zMbcsFilename;
+  int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+
+  nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, 0,0)*sizeof(WCHAR);
+  zMbcsFilename = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zMbcsFilename[0]) );
+  if( zMbcsFilename==0 ){
+    return 0;
+  }
+  nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
+  if( nByte==0 ){
+    HeapFree(GetProcessHeap(),0,zMbcsFilename);
+    zMbcsFilename = 0;
+  }
+  return zMbcsFilename;
+}
+/*
+** Convert multibyte character string to UTF-8.  Space to hold the
+** returned string is obtained from malloc().
+*/
+char *unqlite_win32_mbcs_to_utf8(const char *zFilename){
+  char *zFilenameUtf8;
+  WCHAR *zTmpWide;
+
+  zTmpWide = mbcsToUnicode(zFilename);
+  if( zTmpWide==0 ){
+    return 0;
+  }
+  zFilenameUtf8 = unicodeToUtf8(zTmpWide);
+  HeapFree(GetProcessHeap(),0,zTmpWide);
+  return zFilenameUtf8;
+}
+/*
+** Some microsoft compilers lack this definition.
+*/
+#ifndef INVALID_SET_FILE_POINTER
+# define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+/*
+** Move the current position of the file handle passed as the first 
+** argument to offset iOffset within the file. If successful, return 0. 
+** Otherwise, set pFile->lastErrno and return non-zero.
+*/
+static int seekWinFile(winFile *pFile, unqlite_int64 iOffset){
+  LONG upperBits;                 /* Most sig. 32 bits of new offset */
+  LONG lowerBits;                 /* Least sig. 32 bits of new offset */
+  DWORD dwRet;                    /* Value returned by SetFilePointer() */
+
+  upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
+  lowerBits = (LONG)(iOffset & 0xffffffff);
+
+  /* API oddity: If successful, SetFilePointer() returns a dword 
+  ** containing the lower 32-bits of the new file-offset. Or, if it fails,
+  ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, 
+  ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine 
+  ** whether an error has actually occured, it is also necessary to call 
+  ** GetLastError().
+  */
+  dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
+  if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
+    pFile->lastErrno = GetLastError();
+    return 1;
+  }
+  return 0;
+}
+/*
+** Close a file.
+**
+** It is reported that an attempt to close a handle might sometimes
+** fail.  This is a very unreasonable result, but windows is notorious
+** for being unreasonable so I do not doubt that it might happen.  If
+** the close fails, we pause for 100 milliseconds and try again.  As
+** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before
+** giving up and returning an error.
+*/
+#define MX_CLOSE_ATTEMPT 3
+static int winClose(unqlite_file *id)
+{
+  int rc, cnt = 0;
+  winFile *pFile = (winFile*)id;
+  do{
+    rc = CloseHandle(pFile->h);
+  }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
+
+  return rc ? UNQLITE_OK : UNQLITE_IOERR;
+}
+/*
+** Read data from a file into a buffer.  Return UNQLITE_OK if all
+** bytes were read successfully and UNQLITE_IOERR if anything goes
+** wrong.
+*/
+static int winRead(
+  unqlite_file *id,          /* File to read from */
+  void *pBuf,                /* Write content into this buffer */
+  unqlite_int64 amt,        /* Number of bytes to read */
+  unqlite_int64 offset       /* Begin reading at this offset */
+){
+  winFile *pFile = (winFile*)id;  /* file handle */
+  DWORD nRead;                    /* Number of bytes actually read from file */
+
+  if( seekWinFile(pFile, offset) ){
+    return UNQLITE_FULL;
+  }
+  if( !ReadFile(pFile->h, pBuf, (DWORD)amt, &nRead, 0) ){
+    pFile->lastErrno = GetLastError();
+    return UNQLITE_IOERR;
+  }
+  if( nRead<(DWORD)amt ){
+    /* Unread parts of the buffer must be zero-filled */
+    SyZero(&((char*)pBuf)[nRead],(sxu32)(amt-nRead));
+    return UNQLITE_IOERR;
+  }
+
+  return UNQLITE_OK;
+}
+
+/*
+** Write data from a buffer into a file.  Return UNQLITE_OK on success
+** or some other error code on failure.
+*/
+static int winWrite(
+  unqlite_file *id,               /* File to write into */
+  const void *pBuf,               /* The bytes to be written */
+  unqlite_int64 amt,                        /* Number of bytes to write */
+  unqlite_int64 offset            /* Offset into the file to begin writing at */
+){
+  int rc;                         /* True if error has occured, else false */
+  winFile *pFile = (winFile*)id;  /* File handle */
+
+  rc = seekWinFile(pFile, offset);
+  if( rc==0 ){
+    sxu8 *aRem = (sxu8 *)pBuf;        /* Data yet to be written */
+    unqlite_int64 nRem = amt;         /* Number of bytes yet to be written */
+    DWORD nWrite;                 /* Bytes written by each WriteFile() call */
+
+    while( nRem>0 && WriteFile(pFile->h, aRem, (DWORD)nRem, &nWrite, 0) && nWrite>0 ){
+      aRem += nWrite;
+      nRem -= nWrite;
+    }
+    if( nRem>0 ){
+      pFile->lastErrno = GetLastError();
+      rc = 1;
+    }
+  }
+  if( rc ){
+    if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
+      return UNQLITE_FULL;
+    }
+    return UNQLITE_IOERR;
+  }
+  return UNQLITE_OK;
+}
+
+/*
+** Truncate an open file to a specified size
+*/
+static int winTruncate(unqlite_file *id, unqlite_int64 nByte){
+  winFile *pFile = (winFile*)id;  /* File handle object */
+  int rc = UNQLITE_OK;             /* Return code for this function */
+
+
+  /* If the user has configured a chunk-size for this file, truncate the
+  ** file so that it consists of an integer number of chunks (i.e. the
+  ** actual file size after the operation may be larger than the requested
+  ** size).
+  */
+  if( pFile->szChunk ){
+    nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
+  }
+
+  /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
+  if( seekWinFile(pFile, nByte) ){
+    rc = UNQLITE_IOERR;
+  }else if( 0==SetEndOfFile(pFile->h) ){
+    pFile->lastErrno = GetLastError();
+    rc = UNQLITE_IOERR;
+  }
+  return rc;
+}
+/*
+** Make sure all writes to a particular file are committed to disk.
+*/
+static int winSync(unqlite_file *id, int flags){
+  winFile *pFile = (winFile*)id;
+  SXUNUSED(flags); /* MSVC warning */
+  if( FlushFileBuffers(pFile->h) ){
+    return UNQLITE_OK;
+  }else{
+    pFile->lastErrno = GetLastError();
+    return UNQLITE_IOERR;
+  }
+}
+/*
+** Determine the current size of a file in bytes
+*/
+static int winFileSize(unqlite_file *id, unqlite_int64 *pSize){
+  DWORD upperBits;
+  DWORD lowerBits;
+  winFile *pFile = (winFile*)id;
+  DWORD error;
+  lowerBits = GetFileSize(pFile->h, &upperBits);
+  if(   (lowerBits == INVALID_FILE_SIZE)
+     && ((error = GetLastError()) != NO_ERROR) )
+  {
+    pFile->lastErrno = error;
+    return UNQLITE_IOERR;
+  }
+  *pSize = (((unqlite_int64)upperBits)<<32) + lowerBits;
+  return UNQLITE_OK;
+}
+/*
+** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems.
+*/
+#ifndef LOCKFILE_FAIL_IMMEDIATELY
+# define LOCKFILE_FAIL_IMMEDIATELY 1
+#endif
+
+/*
+** Acquire a reader lock.
+*/
+static int getReadLock(winFile *pFile){
+  int res;
+  OVERLAPPED ovlp;
+  ovlp.Offset = SHARED_FIRST;
+  ovlp.OffsetHigh = 0;
+  ovlp.hEvent = 0;
+  res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,0, SHARED_SIZE, 0, &ovlp);
+  if( res == 0 ){
+    pFile->lastErrno = GetLastError();
+  }
+  return res;
+}
+/*
+** Undo a readlock
+*/
+static int unlockReadLock(winFile *pFile){
+  int res;
+  res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+  if( res == 0 ){
+    pFile->lastErrno = GetLastError();
+  }
+  return res;
+}
+/*
+** Lock the file with the lock specified by parameter locktype - one
+** of the following:
+**
+**     (1) SHARED_LOCK
+**     (2) RESERVED_LOCK
+**     (3) PENDING_LOCK
+**     (4) EXCLUSIVE_LOCK
+**
+** Sometimes when requesting one lock state, additional lock states
+** are inserted in between.  The locking might fail on one of the later
+** transitions leaving the lock state different from what it started but
+** still short of its goal.  The following chart shows the allowed
+** transitions and the inserted intermediate states:
+**
+**    UNLOCKED -> SHARED
+**    SHARED -> RESERVED
+**    SHARED -> (PENDING) -> EXCLUSIVE
+**    RESERVED -> (PENDING) -> EXCLUSIVE
+**    PENDING -> EXCLUSIVE
+**
+** This routine will only increase a lock.  The winUnlock() routine
+** erases all locks at once and returns us immediately to locking level 0.
+** It is not possible to lower the locking level one step at a time.  You
+** must go straight to locking level 0.
+*/
+static int winLock(unqlite_file *id, int locktype){
+  int rc = UNQLITE_OK;    /* Return code from subroutines */
+  int res = 1;           /* Result of a windows lock call */
+  int newLocktype;       /* Set pFile->locktype to this value before exiting */
+  int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
+  winFile *pFile = (winFile*)id;
+  DWORD error = NO_ERROR;
+
+  /* If there is already a lock of this type or more restrictive on the
+  ** OsFile, do nothing.
+  */
+  if( pFile->locktype>=locktype ){
+    return UNQLITE_OK;
+  }
+
+  /* Make sure the locking sequence is correct
+  assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
+  assert( locktype!=PENDING_LOCK );
+  assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
+  */
+  /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
+  ** a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of
+  ** the PENDING_LOCK byte is temporary.
+  */
+  newLocktype = pFile->locktype;
+  if(   (pFile->locktype==NO_LOCK)
+     || (   (locktype==EXCLUSIVE_LOCK)
+         && (pFile->locktype==RESERVED_LOCK))
+  ){
+    int cnt = 3;
+    while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){
+      /* Try 3 times to get the pending lock.  The pending lock might be
+      ** held by another reader process who will release it momentarily.
+         */
+      Sleep(1);
+    }
+    gotPendingLock = res;
+    if( !res ){
+      error = GetLastError();
+    }
+  }
+
+  /* Acquire a shared lock
+  */
+  if( locktype==SHARED_LOCK && res ){
+   /* assert( pFile->locktype==NO_LOCK ); */
+    res = getReadLock(pFile);
+    if( res ){
+      newLocktype = SHARED_LOCK;
+    }else{
+      error = GetLastError();
+    }
+  }
+
+  /* Acquire a RESERVED lock
+  */
+  if( locktype==RESERVED_LOCK && res ){
+    /* assert( pFile->locktype==SHARED_LOCK ); */
+    res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+    if( res ){
+      newLocktype = RESERVED_LOCK;
+    }else{
+      error = GetLastError();
+    }
+  }
+
+  /* Acquire a PENDING lock
+  */
+  if( locktype==EXCLUSIVE_LOCK && res ){
+    newLocktype = PENDING_LOCK;
+    gotPendingLock = 0;
+  }
+
+  /* Acquire an EXCLUSIVE lock
+  */
+  if( locktype==EXCLUSIVE_LOCK && res ){
+    /* assert( pFile->locktype>=SHARED_LOCK ); */
+    res = unlockReadLock(pFile);
+    res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+    if( res ){
+      newLocktype = EXCLUSIVE_LOCK;
+    }else{
+      error = GetLastError();
+      getReadLock(pFile);
+    }
+  }
+
+  /* If we are holding a PENDING lock that ought to be released, then
+  ** release it now.
+  */
+  if( gotPendingLock && locktype==SHARED_LOCK ){
+    UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
+  }
+
+  /* Update the state of the lock has held in the file descriptor then
+  ** return the appropriate result code.
+  */
+  if( res ){
+    rc = UNQLITE_OK;
+  }else{
+    pFile->lastErrno = error;
+    rc = UNQLITE_BUSY;
+  }
+  pFile->locktype = (sxu8)newLocktype;
+  return rc;
+}
+/*
+** This routine checks if there is a RESERVED lock held on the specified
+** file by this or any other process. If such a lock is held, return
+** non-zero, otherwise zero.
+*/
+static int winCheckReservedLock(unqlite_file *id, int *pResOut){
+  int rc;
+  winFile *pFile = (winFile*)id;
+  if( pFile->locktype>=RESERVED_LOCK ){
+    rc = 1;
+  }else{
+    rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+    if( rc ){
+      UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+    }
+    rc = !rc;
+  }
+  *pResOut = rc;
+  return UNQLITE_OK;
+}
+/*
+** Lower the locking level on file descriptor id to locktype.  locktype
+** must be either NO_LOCK or SHARED_LOCK.
+**
+** If the locking level of the file descriptor is already at or below
+** the requested locking level, this routine is a no-op.
+**
+** It is not possible for this routine to fail if the second argument
+** is NO_LOCK.  If the second argument is SHARED_LOCK then this routine
+** might return UNQLITE_IOERR;
+*/
+static int winUnlock(unqlite_file *id, int locktype){
+  int type;
+  winFile *pFile = (winFile*)id;
+  int rc = UNQLITE_OK;
+
+  type = pFile->locktype;
+  if( type>=EXCLUSIVE_LOCK ){
+    UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+    if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
+      /* This should never happen.  We should always be able to
+      ** reacquire the read lock */
+      rc = UNQLITE_IOERR;
+    }
+  }
+  if( type>=RESERVED_LOCK ){
+    UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+  }
+  if( locktype==NO_LOCK && type>=SHARED_LOCK ){
+    unlockReadLock(pFile);
+  }
+  if( type>=PENDING_LOCK ){
+    UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
+  }
+  pFile->locktype = (sxu8)locktype;
+  return rc;
+}
+/*
+** Return the sector size in bytes of the underlying block device for
+** the specified file. This is almost always 512 bytes, but may be
+** larger for some devices.
+**
+*/
+static int winSectorSize(unqlite_file *id){
+  return (int)(((winFile*)id)->sectorSize);
+}
+/*
+** This vector defines all the methods that can operate on an
+** unqlite_file for Windows systems.
+*/
+static const unqlite_io_methods winIoMethod = {
+  1,                              /* iVersion */
+  winClose,                       /* xClose */
+  winRead,                        /* xRead */
+  winWrite,                       /* xWrite */
+  winTruncate,                    /* xTruncate */
+  winSync,                        /* xSync */
+  winFileSize,                    /* xFileSize */
+  winLock,                        /* xLock */
+  winUnlock,                      /* xUnlock */
+  winCheckReservedLock,           /* xCheckReservedLock */
+  winSectorSize,                  /* xSectorSize */
+};
+/*
+ * Windows VFS Methods.
+ */
+/*
+** Convert a UTF-8 filename into whatever form the underlying
+** operating system wants filenames in.  Space to hold the result
+** is obtained from malloc and must be freed by the calling
+** function.
+*/
+static void *convertUtf8Filename(const char *zFilename)
+{
+  void *zConverted;
+  zConverted = utf8ToUnicode(zFilename);
+  /* caller will handle out of memory */
+  return zConverted;
+}
+/*
+** Delete the named file.
+**
+** Note that windows does not allow a file to be deleted if some other
+** process has it open.  Sometimes a virus scanner or indexing program
+** will open a journal file shortly after it is created in order to do
+** whatever it does.  While this other process is holding the
+** file open, we will be unable to delete it.  To work around this
+** problem, we delay 100 milliseconds and try to delete again.  Up
+** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
+** up and returning an error.
+*/
+#define MX_DELETION_ATTEMPTS 5
+static int winDelete(
+  unqlite_vfs *pVfs,          /* Not used on win32 */
+  const char *zFilename,      /* Name of file to delete */
+  int syncDir                 /* Not used on win32 */
+){
+  int cnt = 0;
+  DWORD rc;
+  DWORD error = 0;
+  void *zConverted;
+  zConverted = convertUtf8Filename(zFilename);
+  if( zConverted==0 ){
+          SXUNUSED(pVfs);
+          SXUNUSED(syncDir);
+    return UNQLITE_NOMEM;
+  }
+  do{
+         DeleteFileW((LPCWSTR)zConverted);
+  }while(   (   ((rc = GetFileAttributesW((LPCWSTR)zConverted)) != INVALID_FILE_ATTRIBUTES)
+         || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
+         && (++cnt < MX_DELETION_ATTEMPTS)
+         && (Sleep(100), 1)
+         );
+       HeapFree(GetProcessHeap(),0,zConverted);
+  return (   (rc == INVALID_FILE_ATTRIBUTES) 
+          && (error == ERROR_FILE_NOT_FOUND)) ? UNQLITE_OK : UNQLITE_IOERR;
+}
+/*
+** Check the existance and status of a file.
+*/
+static int winAccess(
+  unqlite_vfs *pVfs,         /* Not used  */
+  const char *zFilename,     /* Name of file to check */
+  int flags,                 /* Type of test to make on this file */
+  int *pResOut               /* OUT: Result */
+){
+  WIN32_FILE_ATTRIBUTE_DATA sAttrData;
+  DWORD attr;
+  int rc = 0;
+  void *zConverted;
+  SXUNUSED(pVfs);
+
+  zConverted = convertUtf8Filename(zFilename);
+  if( zConverted==0 ){
+    return UNQLITE_NOMEM;
+  }
+  SyZero(&sAttrData,sizeof(sAttrData));
+  if( GetFileAttributesExW((WCHAR*)zConverted,
+         GetFileExInfoStandard, 
+         &sAttrData) ){
+      /* For an UNQLITE_ACCESS_EXISTS query, treat a zero-length file
+      ** as if it does not exist.
+      */
+      if(    flags==UNQLITE_ACCESS_EXISTS
+          && sAttrData.nFileSizeHigh==0 
+          && sAttrData.nFileSizeLow==0 ){
+        attr = INVALID_FILE_ATTRIBUTES;
+      }else{
+        attr = sAttrData.dwFileAttributes;
+      }
+    }else{
+      if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
+        HeapFree(GetProcessHeap(),0,zConverted);
+        return UNQLITE_IOERR;
+      }else{
+        attr = INVALID_FILE_ATTRIBUTES;
+      }
+    }
+  HeapFree(GetProcessHeap(),0,zConverted);
+  switch( flags ){
+     case UNQLITE_ACCESS_READWRITE:
+      rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
+      break;
+    case UNQLITE_ACCESS_READ:
+    case UNQLITE_ACCESS_EXISTS:
+       default:
+      rc = attr!=INVALID_FILE_ATTRIBUTES;
+      break;
+  }
+  *pResOut = rc;
+  return UNQLITE_OK;
+}
+/*
+** Turn a relative pathname into a full pathname.  Write the full
+** pathname into zOut[].  zOut[] will be at least pVfs->mxPathname
+** bytes in size.
+*/
+static int winFullPathname(
+  unqlite_vfs *pVfs,            /* Pointer to vfs object */
+  const char *zRelative,        /* Possibly relative input path */
+  int nFull,                    /* Size of output buffer in bytes */
+  char *zFull                   /* Output buffer */
+){
+  int nByte;
+  void *zConverted;
+  WCHAR *zTemp;
+  char *zOut;
+  SXUNUSED(nFull);
+  zConverted = convertUtf8Filename(zRelative);
+  if( zConverted == 0 ){
+         return UNQLITE_NOMEM;
+  }
+  nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
+  zTemp = (WCHAR *)HeapAlloc(GetProcessHeap(),0,nByte*sizeof(zTemp[0]) );
+  if( zTemp==0 ){
+         HeapFree(GetProcessHeap(),0,zConverted);
+         return UNQLITE_NOMEM;
+  }
+  GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
+  HeapFree(GetProcessHeap(),0,zConverted);
+  zOut = unicodeToUtf8(zTemp);
+  HeapFree(GetProcessHeap(),0,zTemp);
+  if( zOut == 0 ){
+    return UNQLITE_NOMEM;
+  }
+  Systrcpy(zFull,(sxu32)pVfs->mxPathname,zOut,0);
+  HeapFree(GetProcessHeap(),0,zOut);
+  return UNQLITE_OK;
+}
+/*
+** Get the sector size of the device used to store
+** file.
+*/
+static int getSectorSize(
+    unqlite_vfs *pVfs,
+    const char *zRelative     /* UTF-8 file name */
+){
+  DWORD bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE;
+  char zFullpath[MAX_PATH+1];
+  int rc;
+  DWORD dwRet = 0;
+  DWORD dwDummy;
+  /*
+  ** We need to get the full path name of the file
+  ** to get the drive letter to look up the sector
+  ** size.
+  */
+  rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
+  if( rc == UNQLITE_OK )
+  {
+    void *zConverted = convertUtf8Filename(zFullpath);
+    if( zConverted ){
+        /* trim path to just drive reference */
+        WCHAR *p = (WCHAR *)zConverted;
+        for(;*p;p++){
+          if( *p == '\\' ){
+            *p = '\0';
+            break;
+          }
+        }
+        dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
+                                  &dwDummy,
+                                  &bytesPerSector,
+                                  &dwDummy,
+                                  &dwDummy);
+                HeapFree(GetProcessHeap(),0,zConverted);
+       }
+    if( !dwRet ){
+      bytesPerSector = UNQLITE_DEFAULT_SECTOR_SIZE;
+    }
+  }
+  return (int) bytesPerSector; 
+}
+/*
+** Sleep for a little while.  Return the amount of time slept.
+*/
+static int winSleep(unqlite_vfs *pVfs, int microsec){
+  Sleep((microsec+999)/1000);
+  SXUNUSED(pVfs);
+  return ((microsec+999)/1000)*1000;
+}
+/*
+ * Export the current system time.
+ */
+static int winCurrentTime(unqlite_vfs *pVfs,Sytm *pOut)
+{
+       SYSTEMTIME sSys;
+       SXUNUSED(pVfs);
+       GetSystemTime(&sSys);
+       SYSTEMTIME_TO_SYTM(&sSys,pOut);
+       return UNQLITE_OK;
+}
+/*
+** The idea is that this function works like a combination of
+** GetLastError() and FormatMessage() on windows (or errno and
+** strerror_r() on unix). After an error is returned by an OS
+** function, UnQLite calls this function with zBuf pointing to
+** a buffer of nBuf bytes. The OS layer should populate the
+** buffer with a nul-terminated UTF-8 encoded error message
+** describing the last IO error to have occurred within the calling
+** thread.
+**
+** If the error message is too large for the supplied buffer,
+** it should be truncated. The return value of xGetLastError
+** is zero if the error message fits in the buffer, or non-zero
+** otherwise (if the message was truncated). If non-zero is returned,
+** then it is not necessary to include the nul-terminator character
+** in the output buffer.
+*/
+static int winGetLastError(unqlite_vfs *pVfs, int nBuf, char *zBuf)
+{
+  /* FormatMessage returns 0 on failure.  Otherwise it
+  ** returns the number of TCHARs written to the output
+  ** buffer, excluding the terminating null char.
+  */
+  DWORD error = GetLastError();
+  WCHAR *zTempWide = 0;
+  DWORD dwLen;
+  char *zOut = 0;
+
+  SXUNUSED(pVfs);
+  dwLen = FormatMessageW(
+         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+         0,
+         error,
+         0,
+         (LPWSTR) &zTempWide,
+         0,
+         0
+         );
+    if( dwLen > 0 ){
+      /* allocate a buffer and convert to UTF8 */
+      zOut = unicodeToUtf8(zTempWide);
+      /* free the system buffer allocated by FormatMessage */
+      LocalFree(zTempWide);
+    }
+       if( 0 == dwLen ){
+               Systrcpy(zBuf,(sxu32)nBuf,"OS Error",sizeof("OS Error")-1);
+       }else{
+               /* copy a maximum of nBuf chars to output buffer */
+               Systrcpy(zBuf,(sxu32)nBuf,zOut,0 /* Compute input length automatically */);
+               /* free the UTF8 buffer */
+               HeapFree(GetProcessHeap(),0,zOut);
+       }
+  return 0;
+}
+/*
+** Open a file.
+*/
+static int winOpen(
+  unqlite_vfs *pVfs,        /* Not used */
+  const char *zName,        /* Name of the file (UTF-8) */
+  unqlite_file *id,         /* Write the UnQLite file handle here */
+  unsigned int flags                /* Open mode flags */
+){
+  HANDLE h;
+  DWORD dwDesiredAccess;
+  DWORD dwShareMode;
+  DWORD dwCreationDisposition;
+  DWORD dwFlagsAndAttributes = 0;
+  winFile *pFile = (winFile*)id;
+  void *zConverted;              /* Filename in OS encoding */
+  const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
+  int isExclusive  = (flags & UNQLITE_OPEN_EXCLUSIVE);
+  int isDelete     = (flags & UNQLITE_OPEN_TEMP_DB);
+  int isCreate     = (flags & UNQLITE_OPEN_CREATE);
+  int isReadWrite  = (flags & UNQLITE_OPEN_READWRITE);
+
+  pFile->h = INVALID_HANDLE_VALUE;
+  /* Convert the filename to the system encoding. */
+  zConverted = convertUtf8Filename(zUtf8Name);
+  if( zConverted==0 ){
+    return UNQLITE_NOMEM;
+  }
+  if( isReadWrite ){
+    dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
+  }else{
+    dwDesiredAccess = GENERIC_READ;
+  }
+  /* UNQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is 
+  ** created.
+  */
+  if( isExclusive ){
+    /* Creates a new file, only if it does not already exist. */
+    /* If the file exists, it fails. */
+    dwCreationDisposition = CREATE_NEW;
+  }else if( isCreate ){
+    /* Open existing file, or create if it doesn't exist */
+    dwCreationDisposition = OPEN_ALWAYS;
+  }else{
+    /* Opens a file, only if it exists. */
+    dwCreationDisposition = OPEN_EXISTING;
+  }
+
+  dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+  if( isDelete ){
+    dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
+                               | FILE_ATTRIBUTE_HIDDEN
+                               | FILE_FLAG_DELETE_ON_CLOSE;
+  }else{
+    dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+  }
+  h = CreateFileW((WCHAR*)zConverted,
+       dwDesiredAccess,
+       dwShareMode,
+       NULL,
+       dwCreationDisposition,
+       dwFlagsAndAttributes,
+       NULL
+    );
+  if( h==INVALID_HANDLE_VALUE ){
+    pFile->lastErrno = GetLastError();
+    HeapFree(GetProcessHeap(),0,zConverted);
+       return UNQLITE_IOERR;
+  }
+  SyZero(pFile,sizeof(*pFile));
+  pFile->pMethod = &winIoMethod;
+  pFile->h = h;
+  pFile->lastErrno = NO_ERROR;
+  pFile->pVfs = pVfs;
+  pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
+  HeapFree(GetProcessHeap(),0,zConverted);
+  return UNQLITE_OK;
+}
+/*
+ * Export the Windows Vfs.
+ */
+UNQLITE_PRIVATE const unqlite_vfs * unqliteExportBuiltinVfs(void)
+{
+       static const unqlite_vfs sWinvfs = {
+               "Windows",           /* Vfs name */
+               1,                   /* Vfs structure version */
+               sizeof(winFile),     /* szOsFile */
+               MAX_PATH,            /* mxPathName */
+               winOpen,             /* xOpen */
+               winDelete,           /* xDelete */
+               winAccess,           /* xAccess */
+               winFullPathname,     /* xFullPathname */
+               0,                   /* xTmp */
+               winSleep,            /* xSleep */
+               winCurrentTime,      /* xCurrentTime */
+               winGetLastError,     /* xGetLastError */
+       };
+       return &sWinvfs;
+}
+#endif /* __WINNT__ */
+/*
+ * ----------------------------------------------------------
+ * File: pager.c
+ * MD5: 57ff77347402fbf6892af589ff8a5df7
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: pager.c v1.1 Win7 2012-11-29 03:46 stable <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/*
+** This file implements the pager and the transaction manager for UnQLite (Mostly inspired from the SQLite3 Source tree).
+**
+** The Pager.eState variable stores the current 'state' of a pager. A
+** pager may be in any one of the seven states shown in the following
+** state diagram.
+**
+**                            OPEN <------+------+
+**                              |         |      |
+**                              V         |      |
+**               +---------> READER-------+      |
+**               |              |                |
+**               |              V                |
+**               |<-------WRITER_LOCKED--------->| 
+**               |              |                |  
+**               |              V                |
+**               |<------WRITER_CACHEMOD-------->|
+**               |              |                |
+**               |              V                |
+**               |<-------WRITER_DBMOD---------->|
+**               |              |                |
+**               |              V                |
+**               +<------WRITER_FINISHED-------->+
+** 
+**  OPEN:
+**
+**    The pager starts up in this state. Nothing is guaranteed in this
+**    state - the file may or may not be locked and the database size is
+**    unknown. The database may not be read or written.
+**
+**    * No read or write transaction is active.
+**    * Any lock, or no lock at all, may be held on the database file.
+**    * The dbSize and dbOrigSize variables may not be trusted.
+**
+**  READER:
+**
+**    In this state all the requirements for reading the database in 
+**    rollback mode are met. Unless the pager is (or recently
+**    was) in exclusive-locking mode, a user-level read transaction is 
+**    open. The database size is known in this state.
+** 
+**    * A read transaction may be active (but a write-transaction cannot).
+**    * A SHARED or greater lock is held on the database file.
+**    * The dbSize variable may be trusted (even if a user-level read 
+**      transaction is not active). The dbOrigSize variables
+**      may not be trusted at this point.
+**    * Even if a read-transaction is not open, it is guaranteed that 
+**      there is no hot-journal in the file-system.
+**
+**  WRITER_LOCKED:
+**
+**    The pager moves to this state from READER when a write-transaction
+**    is first opened on the database. In WRITER_LOCKED state, all locks 
+**    required to start a write-transaction are held, but no actual 
+**    modifications to the cache or database have taken place.
+**
+**    In rollback mode, a RESERVED or (if the transaction was opened with 
+**    EXCLUSIVE flag) EXCLUSIVE lock is obtained on the database file when
+**    moving to this state, but the journal file is not written to or opened 
+**    to in this state. If the transaction is committed or rolled back while 
+**    in WRITER_LOCKED state, all that is required is to unlock the database 
+**    file.
+**
+**    * A write transaction is active.
+**    * If the connection is open in rollback-mode, a RESERVED or greater 
+**      lock is held on the database file.
+**    * The dbSize and dbOrigSize variables are all valid.
+**    * The contents of the pager cache have not been modified.
+**    * The journal file may or may not be open.
+**    * Nothing (not even the first header) has been written to the journal.
+**
+**  WRITER_CACHEMOD:
+**
+**    A pager moves from WRITER_LOCKED state to this state when a page is
+**    first modified by the upper layer. In rollback mode the journal file
+**    is opened (if it is not already open) and a header written to the
+**    start of it. The database file on disk has not been modified.
+**
+**    * A write transaction is active.
+**    * A RESERVED or greater lock is held on the database file.
+**    * The journal file is open and the first header has been written 
+**      to it, but the header has not been synced to disk.
+**    * The contents of the page cache have been modified.
+**
+**  WRITER_DBMOD:
+**
+**    The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state
+**    when it modifies the contents of the database file.
+**
+**    * A write transaction is active.
+**    * An EXCLUSIVE or greater lock is held on the database file.
+**    * The journal file is open and the first header has been written 
+**      and synced to disk.
+**    * The contents of the page cache have been modified (and possibly
+**      written to disk).
+**
+**  WRITER_FINISHED:
+**
+**    A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD
+**    state after the entire transaction has been successfully written into the
+**    database file. In this state the transaction may be committed simply
+**    by finalizing the journal file. Once in WRITER_FINISHED state, it is 
+**    not possible to modify the database further. At this point, the upper 
+**    layer must either commit or rollback the transaction.
+**
+**    * A write transaction is active.
+**    * An EXCLUSIVE or greater lock is held on the database file.
+**    * All writing and syncing of journal and database data has finished.
+**      If no error occured, all that remains is to finalize the journal to
+**      commit the transaction. If an error did occur, the caller will need
+**      to rollback the transaction. 
+**  
+**
+*/
+#define PAGER_OPEN                  0
+#define PAGER_READER                1
+#define PAGER_WRITER_LOCKED         2
+#define PAGER_WRITER_CACHEMOD       3
+#define PAGER_WRITER_DBMOD          4
+#define PAGER_WRITER_FINISHED       5
+/*
+** Journal files begin with the following magic string.  The data
+** was obtained from /dev/random.  It is used only as a sanity check.
+**
+** NOTE: These values must be different from the one used by SQLite3
+** to avoid journal file collision.
+**
+*/
+static const unsigned char aJournalMagic[] = {
+  0xa6, 0xe8, 0xcd, 0x2b, 0x1c, 0x92, 0xdb, 0x9f,
+};
+/*
+** The journal header size for this pager. This is usually the same 
+** size as a single disk sector. See also setSectorSize().
+*/
+#define JOURNAL_HDR_SZ(pPager) (pPager->iSectorSize)
+/*
+ * Database page handle.
+ * Each raw disk page is represented in memory by an instance
+ * of the following structure.
+ */
+typedef struct Page Page;
+struct Page {
+  /* Must correspond to unqlite_page */
+  unsigned char *zData;           /* Content of this page */
+  void *pUserData;                /* Extra content */
+  pgno pgno;                      /* Page number for this page */
+  /**********************************************************************
+  ** Elements above are public.  All that follows is private to pcache.c
+  ** and should not be accessed by other modules.
+  */
+  Pager *pPager;                 /* The pager this page is part of */
+  int flags;                     /* Page flags defined below */
+  int nRef;                      /* Number of users of this page */
+  Page *pNext, *pPrev;    /* A list of all pages */
+  Page *pDirtyNext;             /* Next element in list of dirty pages */
+  Page *pDirtyPrev;             /* Previous element in list of dirty pages */
+  Page *pNextCollide,*pPrevCollide; /* Collission chain */
+  Page *pNextHot,*pPrevHot;    /* Hot dirty pages chain */
+};
+/* Bit values for Page.flags */
+#define PAGE_DIRTY             0x002  /* Page has changed */
+#define PAGE_NEED_SYNC         0x004  /* fsync the rollback journal before
+                                       ** writing this page to the database */
+#define PAGE_DONT_WRITE        0x008  /* Dont write page content to disk */
+#define PAGE_NEED_READ         0x010  /* Content is unread */
+#define PAGE_IN_JOURNAL        0x020  /* Page written to the journal */
+#define PAGE_HOT_DIRTY         0x040  /* Hot dirty page */
+#define PAGE_DONT_MAKE_HOT     0x080  /* Dont make this page Hot. In other words,
+                                                                          * do not link it to the hot dirty list.
+                                                                          */
+/*
+ * Each active database pager is represented by an instance of
+ * the following structure.
+ */
+struct Pager
+{
+  SyMemBackend *pAllocator;      /* Memory backend */
+  unqlite *pDb;                  /* DB handle that own this instance */
+  unqlite_kv_engine *pEngine;    /* Underlying KV storage engine */
+  char *zFilename;               /* Name of the database file */
+  char *zJournal;                /* Name of the journal file */
+  unqlite_vfs *pVfs;             /* Underlying virtual file system */
+  unqlite_file *pfd,*pjfd;       /* File descriptors for database and journal */
+  pgno dbSize;                   /* Number of pages in the file */
+  pgno dbOrigSize;               /* dbSize before the current change */
+  sxi64 dbByteSize;              /* Database size in bytes */
+  void *pMmap;                   /* Read-only Memory view (mmap) of the whole file if requested (UNQLITE_OPEN_MMAP). */
+  sxu32 nRec;                    /* Number of pages written to the journal */
+  SyPRNGCtx sPrng;               /* PRNG Context */
+  sxu32 cksumInit;               /* Quasi-random value added to every checksum */
+  sxu32 iOpenFlags;              /* Flag passed to unqlite_open() after processing */
+  sxi64 iJournalOfft;            /* Journal offset we are reading from */
+  int (*xBusyHandler)(void *);   /* Busy handler */
+  void *pBusyHandlerArg;         /* First arg to xBusyHandler() */
+  void (*xPageUnpin)(void *);    /* Page Unpin callback */
+  void (*xPageReload)(void *);   /* Page Reload callback */
+  Bitvec *pVec;                  /* Bitmap */
+  Page *pHeader;                 /* Page one of the database (Unqlite header) */
+  Sytm tmCreate;                 /* Database creation time */
+  SyString sKv;                  /* Underlying Key/Value storage engine name */
+  int iState;                    /* Pager state */
+  int iLock;                     /* Lock state */
+  sxi32 iFlags;                  /* Control flags (see below) */
+  int is_mem;                    /* True for an in-memory database */
+  int is_rdonly;                 /* True for a read-only database */
+  int no_jrnl;                   /* TRUE to omit journaling */
+  int iPageSize;                 /* Page size in bytes (default 4K) */
+  int iSectorSize;               /* Size of a single sector on disk */
+  unsigned char *zTmpPage;       /* Temporary page */
+  Page *pFirstDirty;             /* First dirty pages */
+  Page *pDirty;                  /* Transient list of dirty pages */
+  Page *pAll;                    /* List of all pages */
+  Page *pHotDirty;               /* List of hot dirty pages */
+  Page *pFirstHot;               /* First hot dirty page */
+  sxu32 nHot;                    /* Total number of hot dirty pages */
+  Page **apHash;                 /* Page table */
+  sxu32 nSize;                   /* apHash[] size: Must be a power of two  */
+  sxu32 nPage;                   /* Total number of page loaded in memory */
+  sxu32 nCacheMax;               /* Maximum page to cache*/
+};
+/* Control flags */
+#define PAGER_CTRL_COMMIT_ERR   0x001 /* Commit error */
+#define PAGER_CTRL_DIRTY_COMMIT 0x002 /* Dirty commit has been applied */ 
+/*
+** Read a 32-bit integer from the given file descriptor. 
+** All values are stored on disk as big-endian.
+*/
+static int ReadInt32(unqlite_file *pFd,sxu32 *pOut,sxi64 iOfft)
+{
+       unsigned char zBuf[4];
+       int rc;
+       rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       SyBigEndianUnpack32(zBuf,pOut);
+       return UNQLITE_OK;
+}
+/*
+** Read a 64-bit integer from the given file descriptor. 
+** All values are stored on disk as big-endian.
+*/
+static int ReadInt64(unqlite_file *pFd,sxu64 *pOut,sxi64 iOfft)
+{
+       unsigned char zBuf[8];
+       int rc;
+       rc = unqliteOsRead(pFd,zBuf,sizeof(zBuf),iOfft);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       SyBigEndianUnpack64(zBuf,pOut);
+       return UNQLITE_OK;
+}
+/*
+** Write a 32-bit integer into the given file descriptor.
+*/
+static int WriteInt32(unqlite_file *pFd,sxu32 iNum,sxi64 iOfft)
+{
+       unsigned char zBuf[4];
+       int rc;
+       SyBigEndianPack32(zBuf,iNum);
+       rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft);
+       return rc;
+}
+/*
+** Write a 64-bit integer into the given file descriptor.
+*/
+static int WriteInt64(unqlite_file *pFd,sxu64 iNum,sxi64 iOfft)
+{
+       unsigned char zBuf[8];
+       int rc;
+       SyBigEndianPack64(zBuf,iNum);
+       rc = unqliteOsWrite(pFd,zBuf,sizeof(zBuf),iOfft);
+       return rc;
+}
+/*
+** The maximum allowed sector size. 64KiB. If the xSectorsize() method 
+** returns a value larger than this, then MAX_SECTOR_SIZE is used instead.
+** This could conceivably cause corruption following a power failure on
+** such a system. This is currently an undocumented limit.
+*/
+#define MAX_SECTOR_SIZE 0x10000
+/*
+** Get the size of a single sector on disk.
+** The sector size will be used used  to determine the size
+** and alignment of journal header and within created journal files.
+**
+** The default sector size is set to 512.
+*/
+static int GetSectorSize(unqlite_file *pFd)
+{
+       int iSectorSize = UNQLITE_DEFAULT_SECTOR_SIZE;
+       if( pFd ){
+               iSectorSize = unqliteOsSectorSize(pFd);
+               if( iSectorSize < 32 ){
+                       iSectorSize = 512;
+               }
+               if( iSectorSize > MAX_SECTOR_SIZE ){
+                       iSectorSize = MAX_SECTOR_SIZE;
+               }
+       }
+       return iSectorSize;
+}
+/* Hash function for page number  */
+#define PAGE_HASH(PNUM) (PNUM)
+/*
+ * Fetch a page from the cache.
+ */
+static Page * pager_fetch_page(Pager *pPager,pgno page_num)
+{
+       Page *pEntry;
+       if( pPager->nPage < 1 ){
+               /* Don't bother hashing */
+               return 0;
+       }
+       /* Perform the lookup */
+       pEntry = pPager->apHash[PAGE_HASH(page_num) & (pPager->nSize - 1)];
+       for(;;){
+               if( pEntry == 0 ){
+                       break;
+               }
+               if( pEntry->pgno == page_num ){
+                       return pEntry;
+               }
+               /* Point to the next entry in the colission chain */
+               pEntry = pEntry->pNextCollide;
+       }
+       /* No such page */
+       return 0;
+}
+/*
+ * Allocate and initialize a new page.
+ */
+static Page * pager_alloc_page(Pager *pPager,pgno num_page)
+{
+       Page *pNew;
+       
+       pNew = (Page *)SyMemBackendPoolAlloc(pPager->pAllocator,sizeof(Page)+pPager->iPageSize);
+       if( pNew == 0 ){
+               return 0;
+       }
+       /* Zero the structure */
+       SyZero(pNew,sizeof(Page)+pPager->iPageSize);
+       /* Page data */
+       pNew->zData = (unsigned char *)&pNew[1];
+       /* Fill in the structure */
+       pNew->pPager = pPager;
+       pNew->nRef = 1;
+       pNew->pgno = num_page;
+       return pNew;
+}
+/*
+ * Increment the reference count of a given page.
+ */
+static void page_ref(Page *pPage)
+{
+       if( pPage->pPager->pAllocator->pMutexMethods ){
+               SyMutexEnter(pPage->pPager->pAllocator->pMutexMethods, pPage->pPager->pAllocator->pMutex);
+       }
+       pPage->nRef++;
+       if( pPage->pPager->pAllocator->pMutexMethods ){
+               SyMutexLeave(pPage->pPager->pAllocator->pMutexMethods, pPage->pPager->pAllocator->pMutex);
+       }
+}
+/*
+ * Release an in-memory page after its reference count reach zero.
+ */
+static int pager_release_page(Pager *pPager,Page *pPage)
+{
+       int rc = UNQLITE_OK;
+       if( !(pPage->flags & PAGE_DIRTY)){
+               /* Invoke the unpin callback if available */
+               if( pPager->xPageUnpin && pPage->pUserData ){
+                       pPager->xPageUnpin(pPage->pUserData);
+               }
+               pPage->pUserData = 0;
+               SyMemBackendPoolFree(pPager->pAllocator,pPage);
+       }else{
+               /* Dirty page, it will be released later when a dirty commit
+                * or the final commit have been applied.
+                */
+               rc = UNQLITE_LOCKED;
+       }
+       return rc;
+}
+/* Forward declaration */
+static int pager_unlink_page(Pager *pPager,Page *pPage);
+/*
+ * Decrement the reference count of a given page.
+ */
+static void page_unref(Page *pPage)
+{
+       int nRef;
+       if( pPage->pPager->pAllocator->pMutexMethods ){
+               SyMutexEnter(pPage->pPager->pAllocator->pMutexMethods, pPage->pPager->pAllocator->pMutex);
+       }
+       nRef = pPage->nRef--;
+       if( pPage->pPager->pAllocator->pMutexMethods ){
+               SyMutexLeave(pPage->pPager->pAllocator->pMutexMethods, pPage->pPager->pAllocator->pMutex);
+       }
+       if( nRef == 0){
+               Pager *pPager = pPage->pPager;
+               if( !(pPage->flags & PAGE_DIRTY)  ){
+                       pager_unlink_page(pPager,pPage);
+                       /* Release the page */
+                       pager_release_page(pPager,pPage);
+               }else{
+                       if( pPage->flags & PAGE_DONT_MAKE_HOT ){
+                               /* Do not add this page to the hot dirty list */
+                               return;
+                       }
+                       if( !(pPage->flags & PAGE_HOT_DIRTY) ){
+                               /* Add to the hot dirty list */
+                               pPage->pPrevHot = 0;
+                               if( pPager->pFirstHot == 0 ){
+                                       pPager->pFirstHot = pPager->pHotDirty = pPage;
+                               }else{
+                                       pPage->pNextHot = pPager->pHotDirty;
+                                       if( pPager->pHotDirty ){
+                                               pPager->pHotDirty->pPrevHot = pPage;
+                                       }
+                                       pPager->pHotDirty = pPage;
+                               }
+                               pPager->nHot++;
+                               pPage->flags |= PAGE_HOT_DIRTY;
+                       }
+               }
+       }
+}
+/*
+ * Link a freshly created page to the list of active page.
+ */
+static int pager_link_page(Pager *pPager,Page *pPage)
+{
+       sxu32 nBucket;
+       /* Install in the corresponding bucket */
+       nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1);
+       pPage->pNextCollide = pPager->apHash[nBucket];
+       if( pPager->apHash[nBucket] ){
+               pPager->apHash[nBucket]->pPrevCollide = pPage;
+       }
+       pPager->apHash[nBucket] = pPage;
+       /* Link to the list of active pages */
+       MACRO_LD_PUSH(pPager->pAll,pPage);
+       pPager->nPage++;
+       if( (pPager->nPage >= pPager->nSize * 4)  && pPager->nPage < 100000 ){
+               /* Grow the hashtable */
+               sxu32 nNewSize = pPager->nSize << 1;
+               Page *pEntry,**apNew;
+               sxu32 n;
+               apNew = (Page **)SyMemBackendAlloc(pPager->pAllocator, nNewSize * sizeof(Page *));
+               if( apNew ){
+                       sxu32 iBucket;
+                       /* Zero the new table */
+                       SyZero((void *)apNew, nNewSize * sizeof(Page *));
+                       /* Rehash all entries */
+                       n = 0;
+                       pEntry = pPager->pAll;
+                       for(;;){
+                               /* Loop one */
+                               if( n >= pPager->nPage ){
+                                       break;
+                               }
+                               pEntry->pNextCollide = pEntry->pPrevCollide = 0;
+                               /* Install in the new bucket */
+                               iBucket = PAGE_HASH(pEntry->pgno) & (nNewSize - 1);
+                               pEntry->pNextCollide = apNew[iBucket];
+                               if( apNew[iBucket] ){
+                                       apNew[iBucket]->pPrevCollide = pEntry;
+                               }
+                               apNew[iBucket] = pEntry;
+                               /* Point to the next entry */
+                               pEntry = pEntry->pNext;
+                               n++;
+                       }
+                       /* Release the old table and reflect the change */
+                       SyMemBackendFree(pPager->pAllocator,(void *)pPager->apHash);
+                       pPager->apHash = apNew;
+                       pPager->nSize  = nNewSize;
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Unlink a page from the list of active pages.
+ */
+static int pager_unlink_page(Pager *pPager,Page *pPage)
+{
+       if( pPage->pNextCollide ){
+               pPage->pNextCollide->pPrevCollide = pPage->pPrevCollide;
+       }
+       if( pPage->pPrevCollide ){
+               pPage->pPrevCollide->pNextCollide = pPage->pNextCollide;
+       }else{
+               sxu32 nBucket = PAGE_HASH(pPage->pgno) & (pPager->nSize - 1);
+               pPager->apHash[nBucket] = pPage->pNextCollide;
+       }
+       MACRO_LD_REMOVE(pPager->pAll,pPage);
+       pPager->nPage--;
+       return UNQLITE_OK;
+}
+/*
+ * Update the content of a cached page.
+ */
+static int pager_fill_page(Pager *pPager,pgno iNum,void *pContents)
+{
+       Page *pPage;
+       /* Fetch the page from the catch */
+       pPage = pager_fetch_page(pPager,iNum);
+       if( pPage == 0 ){
+               return SXERR_NOTFOUND;
+       }
+       /* Reflect the change */
+       SyMemcpy(pContents,pPage->zData,pPager->iPageSize);
+
+       return UNQLITE_OK;
+}
+/*
+ * Read the content of a page from disk.
+ */
+static int pager_get_page_contents(Pager *pPager,Page *pPage,int noContent)
+{
+       int rc = UNQLITE_OK;
+       if( pPager->is_mem || noContent || pPage->pgno >= pPager->dbSize ){
+               /* Do not bother reading, zero the page contents only */
+               SyZero(pPage->zData,pPager->iPageSize);
+               return UNQLITE_OK;
+       }
+       if( (pPager->iOpenFlags & UNQLITE_OPEN_MMAP) && (pPager->pMmap /* Paranoid edition */) ){
+               unsigned char *zMap = (unsigned char *)pPager->pMmap;
+               pPage->zData = &zMap[pPage->pgno * pPager->iPageSize];
+       }else{
+               /* Read content */
+               rc = unqliteOsRead(pPager->pfd,pPage->zData,pPager->iPageSize,pPage->pgno * pPager->iPageSize);
+       }
+       return rc;
+}
+/*
+ * Add a page to the dirty list.
+ */
+static void pager_page_to_dirty_list(Pager *pPager,Page *pPage)
+{
+       if( pPage->flags & PAGE_DIRTY ){
+               /* Already set */
+               return;
+       }
+       /* Mark the page as dirty */
+       pPage->flags |= PAGE_DIRTY|PAGE_NEED_SYNC|PAGE_IN_JOURNAL;
+       /* Link to the list */
+       pPage->pDirtyPrev = 0;
+       pPage->pDirtyNext = pPager->pDirty;
+       if( pPager->pDirty ){
+               pPager->pDirty->pDirtyPrev = pPage;
+       }
+       pPager->pDirty = pPage;
+       if( pPager->pFirstDirty == 0 ){
+               pPager->pFirstDirty = pPage;
+       }
+}
+/*
+ * Merge sort.
+ * The merge sort implementation is based on the one used by
+ * the PH7 Embeddable PHP Engine (http://ph7.symisc.net/).
+ */
+/*
+** Inputs:
+**   a:       A sorted, null-terminated linked list.  (May be null).
+**   b:       A sorted, null-terminated linked list.  (May be null).
+**   cmp:     A pointer to the comparison function.
+**
+** Return Value:
+**   A pointer to the head of a sorted list containing the elements
+**   of both a and b.
+**
+** Side effects:
+**   The "next", "prev" pointers for elements in the lists a and b are
+**   changed.
+*/
+static Page * page_merge_dirty(Page *pA, Page *pB)
+{
+       Page result, *pTail;
+    /* Prevent compiler warning */
+       result.pDirtyNext = result.pDirtyPrev = 0;
+       pTail = &result;
+       while( pA && pB ){
+               if( pA->pgno < pB->pgno ){
+                       pTail->pDirtyPrev = pA;
+                       pA->pDirtyNext = pTail;
+                       pTail = pA;
+                       pA = pA->pDirtyPrev;
+               }else{
+                       pTail->pDirtyPrev = pB;
+                       pB->pDirtyNext = pTail;
+                       pTail = pB;
+                       pB = pB->pDirtyPrev;
+               }
+       }
+       if( pA ){
+               pTail->pDirtyPrev = pA;
+               pA->pDirtyNext = pTail;
+       }else if( pB ){
+               pTail->pDirtyPrev = pB;
+               pB->pDirtyNext = pTail;
+       }else{
+               pTail->pDirtyPrev = pTail->pDirtyNext = 0;
+       }
+       return result.pDirtyPrev;
+}
+/*
+** Inputs:
+**   Map:       Input hashmap
+**   cmp:       A comparison function.
+**
+** Return Value:
+**   Sorted hashmap.
+**
+** Side effects:
+**   The "next" pointers for elements in list are changed.
+*/
+#define N_SORT_BUCKET  32
+static Page * pager_get_dirty_pages(Pager *pPager)
+{
+       Page *a[N_SORT_BUCKET], *p, *pIn;
+       sxu32 i;
+       if( pPager->pFirstDirty == 0 ){
+               /* Don't bother sorting, the list is already empty */
+               return 0;
+       }
+       SyZero(a, sizeof(a));
+       /* Point to the first inserted entry */
+       pIn = pPager->pFirstDirty;
+       while( pIn ){
+               p = pIn;
+               pIn = p->pDirtyPrev;
+               p->pDirtyPrev = 0;
+               for(i=0; i<N_SORT_BUCKET-1; i++){
+                       if( a[i]==0 ){
+                               a[i] = p;
+                               break;
+                       }else{
+                               p = page_merge_dirty(a[i], p);
+                               a[i] = 0;
+                       }
+               }
+               if( i==N_SORT_BUCKET-1 ){
+                       /* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
+                        * But that is impossible.
+                        */
+                       a[i] = page_merge_dirty(a[i], p);
+               }
+       }
+       p = a[0];
+       for(i=1; i<N_SORT_BUCKET; i++){
+               p = page_merge_dirty(p,a[i]);
+       }
+       p->pDirtyNext = 0;
+       return p;
+}
+/*
+ * See block comment above.
+ */
+static Page * page_merge_hot(Page *pA, Page *pB)
+{
+       Page result, *pTail;
+    /* Prevent compiler warning */
+       result.pNextHot = result.pPrevHot = 0;
+       pTail = &result;
+       while( pA && pB ){
+               if( pA->pgno < pB->pgno ){
+                       pTail->pPrevHot = pA;
+                       pA->pNextHot = pTail;
+                       pTail = pA;
+                       pA = pA->pPrevHot;
+               }else{
+                       pTail->pPrevHot = pB;
+                       pB->pNextHot = pTail;
+                       pTail = pB;
+                       pB = pB->pPrevHot;
+               }
+       }
+       if( pA ){
+               pTail->pPrevHot = pA;
+               pA->pNextHot = pTail;
+       }else if( pB ){
+               pTail->pPrevHot = pB;
+               pB->pNextHot = pTail;
+       }else{
+               pTail->pPrevHot = pTail->pNextHot = 0;
+       }
+       return result.pPrevHot;
+}
+/*
+** Inputs:
+**   Map:       Input hashmap
+**   cmp:       A comparison function.
+**
+** Return Value:
+**   Sorted hashmap.
+**
+** Side effects:
+**   The "next" pointers for elements in list are changed.
+*/
+#define N_SORT_BUCKET  32
+static Page * pager_get_hot_pages(Pager *pPager)
+{
+       Page *a[N_SORT_BUCKET], *p, *pIn;
+       sxu32 i;
+       if( pPager->pFirstHot == 0 ){
+               /* Don't bother sorting, the list is already empty */
+               return 0;
+       }
+       SyZero(a, sizeof(a));
+       /* Point to the first inserted entry */
+       pIn = pPager->pFirstHot;
+       while( pIn ){
+               p = pIn;
+               pIn = p->pPrevHot;
+               p->pPrevHot = 0;
+               for(i=0; i<N_SORT_BUCKET-1; i++){
+                       if( a[i]==0 ){
+                               a[i] = p;
+                               break;
+                       }else{
+                               p = page_merge_hot(a[i], p);
+                               a[i] = 0;
+                       }
+               }
+               if( i==N_SORT_BUCKET-1 ){
+                       /* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
+                        * But that is impossible.
+                        */
+                       a[i] = page_merge_hot(a[i], p);
+               }
+       }
+       p = a[0];
+       for(i=1; i<N_SORT_BUCKET; i++){
+               p = page_merge_hot(p,a[i]);
+       }
+       p->pNextHot = 0;
+       return p;
+}
+/*
+** The format for the journal header is as follows:
+** - 8 bytes: Magic identifying journal format.
+** - 4 bytes: Number of records in journal.
+** - 4 bytes: Random number used for page hash.
+** - 8 bytes: Initial database page count.
+** - 4 bytes: Sector size used by the process that wrote this journal.
+** - 4 bytes: Database page size.
+** 
+** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
+*/
+/*
+** Open the journal file and extract its header information.
+**
+** If the header is read successfully, *pNRec is set to the number of
+** page records following this header and *pDbSize is set to the size of the
+** database before the transaction began, in pages. Also, pPager->cksumInit
+** is set to the value read from the journal header. UNQLITE_OK is returned
+** in this case.
+**
+** If the journal header file appears to be corrupted, UNQLITE_DONE is
+** returned and *pNRec and *PDbSize are undefined.  If JOURNAL_HDR_SZ bytes
+** cannot be read from the journal file an error code is returned.
+*/
+static int pager_read_journal_header(
+  Pager *pPager,               /* Pager object */
+  sxu32 *pNRec,                /* OUT: Value read from the nRec field */
+  pgno  *pDbSize               /* OUT: Value of original database size field */
+)
+{
+       sxu32 iPageSize,iSectorSize;
+       unsigned char zMagic[8];
+       sxi64 iHdrOfft;
+       sxi64 iSize;
+       int rc;
+       /* Offset to start reading from */
+       iHdrOfft = 0;
+       /* Get the size of the journal */
+       rc = unqliteOsFileSize(pPager->pjfd,&iSize);
+       if( rc != UNQLITE_OK ){
+               return UNQLITE_DONE;
+       }
+       /* If the journal file is too small, return UNQLITE_DONE. */
+       if( 32 /* Minimum sector size */> iSize ){
+               return UNQLITE_DONE;
+       }
+       /* Make sure we are dealing with a valid journal */
+       rc = unqliteOsRead(pPager->pjfd,zMagic,sizeof(zMagic),iHdrOfft);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( SyMemcmp(zMagic,aJournalMagic,sizeof(zMagic)) != 0 ){
+               return UNQLITE_DONE;
+       }
+       iHdrOfft += sizeof(zMagic);
+        /* Read the first three 32-bit fields of the journal header: The nRec
+      ** field, the checksum-initializer and the database size at the start
+      ** of the transaction. Return an error code if anything goes wrong.
+      */
+       rc = ReadInt32(pPager->pjfd,pNRec,iHdrOfft);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       iHdrOfft += 4;
+       rc = ReadInt32(pPager->pjfd,&pPager->cksumInit,iHdrOfft);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       iHdrOfft += 4;
+       rc = ReadInt64(pPager->pjfd,pDbSize,iHdrOfft);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       iHdrOfft += 8;
+       /* Read the page-size and sector-size journal header fields. */
+       rc = ReadInt32(pPager->pjfd,&iSectorSize,iHdrOfft);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       iHdrOfft += 4;
+       rc = ReadInt32(pPager->pjfd,&iPageSize,iHdrOfft);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Check that the values read from the page-size and sector-size fields
+    ** are within range. To be 'in range', both values need to be a power
+    ** of two greater than or equal to 512 or 32, and not greater than their 
+    ** respective compile time maximum limits.
+    */
+    if( iPageSize < UNQLITE_MIN_PAGE_SIZE || iSectorSize<32
+     || iPageSize > UNQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE
+     || ((iPageSize-1)&iPageSize)!=0    || ((iSectorSize-1)&iSectorSize)!=0 
+    ){
+      /* If the either the page-size or sector-size in the journal-header is 
+      ** invalid, then the process that wrote the journal-header must have 
+      ** crashed before the header was synced. In this case stop reading 
+      ** the journal file here.
+      */
+      return UNQLITE_DONE;
+    }
+    /* Update the assumed sector-size to match the value used by 
+    ** the process that created this journal. If this journal was
+    ** created by a process other than this one, then this routine
+    ** is being called from within pager_playback(). The local value
+    ** of Pager.sectorSize is restored at the end of that routine.
+    */
+    pPager->iSectorSize = iSectorSize;
+       pPager->iPageSize = iPageSize;
+       /* Ready to rollback */
+       pPager->iJournalOfft = JOURNAL_HDR_SZ(pPager);
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Write the journal header in the given memory buffer.
+ * The given buffer is big enough to hold the whole header.
+ */
+static int pager_write_journal_header(Pager *pPager,unsigned char *zBuf)
+{
+       unsigned char *zPtr = zBuf;
+       /* 8 bytes magic number */
+       SyMemcpy(aJournalMagic,zPtr,sizeof(aJournalMagic));
+       zPtr += sizeof(aJournalMagic);
+       /* 4 bytes: Number of records in journal. */
+       SyBigEndianPack32(zPtr,0);
+       zPtr += 4;
+       /* 4 bytes: Random number used to compute page checksum. */
+       SyBigEndianPack32(zPtr,pPager->cksumInit);
+       zPtr += 4;
+       /* 8 bytes: Initial database page count. */
+       SyBigEndianPack64(zPtr,pPager->dbOrigSize);
+       zPtr += 8;
+       /* 4 bytes: Sector size used by the process that wrote this journal. */
+       SyBigEndianPack32(zPtr,(sxu32)pPager->iSectorSize);
+       zPtr += 4;
+       /* 4 bytes: Database page size. */
+       SyBigEndianPack32(zPtr,(sxu32)pPager->iPageSize);
+       return UNQLITE_OK;
+}
+/*
+** Parameter aData must point to a buffer of pPager->pageSize bytes
+** of data. Compute and return a checksum based ont the contents of the 
+** page of data and the current value of pPager->cksumInit.
+**
+** This is not a real checksum. It is really just the sum of the 
+** random initial value (pPager->cksumInit) and every 200th byte
+** of the page data, starting with byte offset (pPager->pageSize%200).
+** Each byte is interpreted as an 8-bit unsigned integer.
+**
+** Changing the formula used to compute this checksum results in an
+** incompatible journal file format.
+**
+** If journal corruption occurs due to a power failure, the most likely 
+** scenario is that one end or the other of the record will be changed. 
+** It is much less likely that the two ends of the journal record will be
+** correct and the middle be corrupt.  Thus, this "checksum" scheme,
+** though fast and simple, catches the mostly likely kind of corruption.
+*/
+static sxu32 pager_cksum(Pager *pPager,const unsigned char *zData)
+{
+  sxu32 cksum = pPager->cksumInit;         /* Checksum value to return */
+  int i = pPager->iPageSize-200;          /* Loop counter */
+  while( i>0 ){
+    cksum += zData[i];
+    i -= 200;
+  }
+  return cksum;
+}
+/*
+** Read a single page from the journal file opened on file descriptor
+** jfd. Playback this one page. Update the offset to read from.
+*/
+static int pager_play_back_one_page(Pager *pPager,sxi64 *pOfft,unsigned char *zTmp)
+{
+       unsigned char *zData = zTmp;
+       sxi64 iOfft; /* Offset to read from */
+       pgno iNum;   /* Pager number */
+       sxu32 ckSum; /* Sanity check */
+       int rc;
+       /* Offset to start reading from */
+       iOfft = *pOfft;
+       /* Database page number */
+       rc = ReadInt64(pPager->pjfd,&iNum,iOfft);
+       if( rc != UNQLITE_OK ){ return rc; }
+       iOfft += 8;
+       /* Page data */
+       rc = unqliteOsRead(pPager->pjfd,zData,pPager->iPageSize,iOfft);
+       if( rc != UNQLITE_OK ){ return rc; }
+       iOfft += pPager->iPageSize;
+       /* Page cksum */
+       rc = ReadInt32(pPager->pjfd,&ckSum,iOfft);
+       if( rc != UNQLITE_OK ){ return rc; }
+       iOfft += 4;
+       /* Synchronize pointers */
+       *pOfft = iOfft;
+       /* Make sure we are dealing with a valid page */
+       if( ckSum != pager_cksum(pPager,zData) ){
+               /* Ignore that page */
+               return SXERR_IGNORE;
+       }
+       if( iNum >= pPager->dbSize ){
+               /* Ignore that page */
+               return UNQLITE_OK;
+       }
+       /* playback */
+       rc = unqliteOsWrite(pPager->pfd,zData,pPager->iPageSize,iNum * pPager->iPageSize);
+       if( rc == UNQLITE_OK ){
+               /* Flush the cache */
+               pager_fill_page(pPager,iNum,zData);
+       }
+       return rc;
+}
+/*
+** Playback the journal and thus restore the database file to
+** the state it was in before we started making changes.  
+**
+** The journal file format is as follows: 
+**
+**  (1)  8 byte prefix.  A copy of aJournalMagic[].
+**  (2)  4 byte big-endian integer which is the number of valid page records
+**       in the journal. 
+**  (3)  4 byte big-endian integer which is the initial value for the 
+**       sanity checksum.
+**  (4)  8 byte integer which is the number of pages to truncate the
+**       database to during a rollback.
+**  (5)  4 byte big-endian integer which is the sector size.  The header
+**       is this many bytes in size.
+**  (6)  4 byte big-endian integer which is the page size.
+**  (7)  zero padding out to the next sector size.
+**  (8)  Zero or more pages instances, each as follows:
+**        +  4 byte page number.
+**        +  pPager->pageSize bytes of data.
+**        +  4 byte checksum
+**
+** When we speak of the journal header, we mean the first 7 items above.
+** Each entry in the journal is an instance of the 8th item.
+**
+** Call the value from the second bullet "nRec".  nRec is the number of
+** valid page entries in the journal.  In most cases, you can compute the
+** value of nRec from the size of the journal file.  But if a power
+** failure occurred while the journal was being written, it could be the
+** case that the size of the journal file had already been increased but
+** the extra entries had not yet made it safely to disk.  In such a case,
+** the value of nRec computed from the file size would be too large.  For
+** that reason, we always use the nRec value in the header.
+**
+** If the file opened as the journal file is not a well-formed
+** journal file then all pages up to the first corrupted page are rolled
+** back (or no pages if the journal header is corrupted). The journal file
+** is then deleted and SQLITE_OK returned, just as if no corruption had
+** been encountered.
+**
+** If an I/O or malloc() error occurs, the journal-file is not deleted
+** and an error code is returned.
+**
+*/
+static int pager_playback(Pager *pPager)
+{
+       unsigned char *zTmp = 0; /* cc warning */
+       sxu32 n,nRec;
+       sxi64 iOfft;
+       int rc;
+       /* Read the journal header*/
+       rc = pager_read_journal_header(pPager,&nRec,&pPager->dbSize);
+       if( rc != UNQLITE_OK ){
+               if( rc == UNQLITE_DONE ){
+                       goto end_playback;
+               }
+               unqliteGenErrorFormat(pPager->pDb,"IO error while reading journal file '%s' header",pPager->zJournal);
+               return rc;
+       }
+       /* Truncate the database back to its original size */
+       rc = unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize);
+       if( rc != UNQLITE_OK ){
+               unqliteGenError(pPager->pDb,"IO error while truncating database file");
+               return rc;
+       }
+       /* Allocate a temporary page */
+       zTmp = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize);
+       if( zTmp == 0 ){
+               unqliteGenOutofMem(pPager->pDb);
+               return UNQLITE_NOMEM;
+       }
+       SyZero((void *)zTmp,(sxu32)pPager->iPageSize);
+       /* Copy original pages out of the journal and back into the 
+    ** database file and/or page cache.
+    */
+       iOfft = pPager->iJournalOfft;
+       for( n = 0 ; n < nRec ; ++n ){
+               rc = pager_play_back_one_page(pPager,&iOfft,zTmp);
+               if( rc != UNQLITE_OK ){
+                       if( rc != SXERR_IGNORE ){
+                               unqliteGenError(pPager->pDb,"Page playback error");
+                               goto end_playback;
+                       }
+               }
+       }
+end_playback:
+       /* Release the temp page */
+       SyMemBackendFree(pPager->pAllocator,(void *)zTmp);
+       if( rc == UNQLITE_OK ){
+               /* Sync the database file */
+               unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL);
+       }
+       if( rc == UNQLITE_DONE ){
+               rc = UNQLITE_OK;
+       }
+       /* Return to the caller */
+       return rc;
+}
+/*
+** Unlock the database file to level eLock, which must be either NO_LOCK
+** or SHARED_LOCK. Regardless of whether or not the call to xUnlock()
+** succeeds, set the Pager.iLock variable to match the (attempted) new lock.
+**
+** Except, if Pager.iLock is set to NO_LOCK when this function is
+** called, do not modify it. See the comment above the #define of 
+** NO_LOCK for an explanation of this.
+*/
+static int pager_unlock_db(Pager *pPager, int eLock)
+{
+  int rc = UNQLITE_OK;
+  if( pPager->iLock != NO_LOCK ){
+    rc = unqliteOsUnlock(pPager->pfd,eLock);
+    pPager->iLock = eLock;
+  }
+  return rc;
+}
+/*
+** Lock the database file to level eLock, which must be either SHARED_LOCK,
+** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
+** Pager.eLock variable to the new locking state. 
+**
+** Except, if Pager.eLock is set to NO_LOCK when this function is 
+** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. 
+** See the comment above the #define of NO_LOCK for an explanation 
+** of this.
+*/
+static int pager_lock_db(Pager *pPager, int eLock){
+  int rc = UNQLITE_OK;
+  if( pPager->iLock < eLock || pPager->iLock == NO_LOCK ){
+    rc = unqliteOsLock(pPager->pfd, eLock);
+    if( rc==UNQLITE_OK ){
+      pPager->iLock = eLock;
+    }else{
+               unqliteGenError(pPager->pDb,
+                       rc == UNQLITE_BUSY ? "Another process or thread hold the requested lock" : "Error while requesting database lock"
+                       );
+       }
+  }
+  return rc;
+}
+/*
+** Try to obtain a lock of type locktype on the database file. If
+** a similar or greater lock is already held, this function is a no-op
+** (returning UNQLITE_OK immediately).
+**
+** Otherwise, attempt to obtain the lock using unqliteOsLock(). Invoke 
+** the busy callback if the lock is currently not available. Repeat 
+** until the busy callback returns false or until the attempt to 
+** obtain the lock succeeds.
+**
+** Return UNQLITE_OK on success and an error code if we cannot obtain
+** the lock. If the lock is obtained successfully, set the Pager.state 
+** variable to locktype before returning.
+*/
+static int pager_wait_on_lock(Pager *pPager, int locktype){
+  int rc;                              /* Return code */
+  do {
+    rc = pager_lock_db(pPager,locktype);
+  }while( rc==UNQLITE_BUSY && pPager->xBusyHandler && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
+  return rc;
+}
+/*
+** This function is called after transitioning from PAGER_OPEN to
+** PAGER_SHARED state. It tests if there is a hot journal present in
+** the file-system for the given pager. A hot journal is one that 
+** needs to be played back. According to this function, a hot-journal
+** file exists if the following criteria are met:
+**
+**   * The journal file exists in the file system, and
+**   * No process holds a RESERVED or greater lock on the database file, and
+**   * The database file itself is greater than 0 bytes in size, and
+**   * The first byte of the journal file exists and is not 0x00.
+**
+** If the current size of the database file is 0 but a journal file
+** exists, that is probably an old journal left over from a prior
+** database with the same name. In this case the journal file is
+** just deleted using OsDelete, *pExists is set to 0 and UNQLITE_OK
+** is returned.
+**
+** If a hot-journal file is found to exist, *pExists is set to 1 and 
+** UNQLITE_OK returned. If no hot-journal file is present, *pExists is
+** set to 0 and UNQLITE_OK returned. If an IO error occurs while trying
+** to determine whether or not a hot-journal file exists, the IO error
+** code is returned and the value of *pExists is undefined.
+*/
+static int pager_has_hot_journal(Pager *pPager, int *pExists)
+{
+  unqlite_vfs *pVfs = pPager->pVfs;
+  int rc = UNQLITE_OK;           /* Return code */
+  int exists = 1;               /* True if a journal file is present */
+
+  *pExists = 0;
+  rc = unqliteOsAccess(pVfs, pPager->zJournal, UNQLITE_ACCESS_EXISTS, &exists);
+  if( rc==UNQLITE_OK && exists ){
+    int locked = 0;             /* True if some process holds a RESERVED lock */
+
+    /* Race condition here:  Another process might have been holding the
+    ** the RESERVED lock and have a journal open at the unqliteOsAccess() 
+    ** call above, but then delete the journal and drop the lock before
+    ** we get to the following unqliteOsCheckReservedLock() call.  If that
+    ** is the case, this routine might think there is a hot journal when
+    ** in fact there is none.  This results in a false-positive which will
+    ** be dealt with by the playback routine.
+    */
+    rc = unqliteOsCheckReservedLock(pPager->pfd, &locked);
+    if( rc==UNQLITE_OK && !locked ){
+      sxi64 n = 0;                    /* Size of db file in bytes */
+      /* Check the size of the database file. If it consists of 0 pages,
+      ** then delete the journal file. See the header comment above for 
+      ** the reasoning here.  Delete the obsolete journal file under
+      ** a RESERVED lock to avoid race conditions.
+      */
+      rc = unqliteOsFileSize(pPager->pfd,&n);
+      if( rc==UNQLITE_OK ){
+        if( n < 1 ){
+          if( pager_lock_db(pPager, RESERVED_LOCK)==UNQLITE_OK ){
+            unqliteOsDelete(pVfs, pPager->zJournal, 0);
+                       pager_unlock_db(pPager, SHARED_LOCK);
+          }
+        }else{
+          /* The journal file exists and no other connection has a reserved
+          ** or greater lock on the database file. */
+                       *pExists = 1;
+        }
+      }
+    }
+  }
+  return rc;
+}
+/*
+ * Rollback a journal file. (See block-comment above).
+ */
+static int pager_journal_rollback(Pager *pPager,int check_hot)
+{
+       int rc;
+       if( check_hot ){
+               int iExists = 0; /* cc warning */
+               /* Check if the journal file exists */
+               rc = pager_has_hot_journal(pPager,&iExists);
+               if( rc != UNQLITE_OK  ){
+                       /* IO error */
+                       return rc;
+               }
+               if( !iExists ){
+                       /* Journal file does not exists */
+                       return UNQLITE_OK;
+               }
+       }
+       if( pPager->is_rdonly ){
+               unqliteGenErrorFormat(pPager->pDb,
+                       "Cannot rollback journal file '%s' due to a read-only database handle",pPager->zJournal);
+               return UNQLITE_READ_ONLY;
+       }
+       /* Get an EXCLUSIVE lock on the database file. At this point it is
+      ** important that a RESERVED lock is not obtained on the way to the
+      ** EXCLUSIVE lock. If it were, another process might open the
+      ** database file, detect the RESERVED lock, and conclude that the
+      ** database is safe to read while this process is still rolling the 
+      ** hot-journal back.
+      ** 
+      ** Because the intermediate RESERVED lock is not requested, any
+      ** other process attempting to access the database file will get to 
+      ** this point in the code and fail to obtain its own EXCLUSIVE lock 
+      ** on the database file.
+      **
+      ** Unless the pager is in locking_mode=exclusive mode, the lock is
+      ** downgraded to SHARED_LOCK before this function returns.
+      */
+       /* Open the journal file */
+       rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,&pPager->pjfd,UNQLITE_OPEN_READWRITE);
+       if( rc != UNQLITE_OK ){
+               unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: '%s'",pPager->zJournal);
+               goto fail;
+       }
+       rc = pager_lock_db(pPager,EXCLUSIVE_LOCK);
+       if( rc != UNQLITE_OK ){
+               unqliteGenError(pPager->pDb,"Cannot acquire an exclusive lock on the database while journal rollback");
+               goto fail;
+       }
+       /* Sync the journal file */
+       unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
+       /* Finally rollback the database */
+       rc = pager_playback(pPager);
+       /* Switch back to shared lock */
+       pager_unlock_db(pPager,SHARED_LOCK);
+fail:
+       /* Close the journal handle */
+       unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
+       pPager->pjfd = 0;
+       if( rc == UNQLITE_OK ){
+               /* Delete the journal file */
+               unqliteOsDelete(pPager->pVfs,pPager->zJournal,TRUE);
+       }
+       return rc;
+}
+/*
+ * Write the unqlite header (First page). (Big-Endian)
+ */
+static int pager_write_db_header(Pager *pPager)
+{
+       unsigned char *zRaw = pPager->pHeader->zData;
+       unqlite_kv_engine *pEngine = pPager->pEngine;
+       sxu32 nDos;
+       sxu16 nLen;
+       /* Database signature */
+       SyMemcpy(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1);
+       zRaw += sizeof(UNQLITE_DB_SIG)-1;
+       /* Database magic number */
+       SyBigEndianPack32(zRaw,UNQLITE_DB_MAGIC);
+       zRaw += 4; /* 4 byte magic number */
+       /* Database creation time */
+       SyZero(&pPager->tmCreate,sizeof(Sytm));
+       if( pPager->pVfs->xCurrentTime ){
+               pPager->pVfs->xCurrentTime(pPager->pVfs,&pPager->tmCreate);
+       }
+       /* DOS time format (4 bytes) */
+       SyTimeFormatToDos(&pPager->tmCreate,&nDos);
+       SyBigEndianPack32(zRaw,nDos);
+       zRaw += 4; /* 4 byte DOS time */
+       /* Sector size */
+       SyBigEndianPack32(zRaw,(sxu32)pPager->iSectorSize);
+       zRaw += 4; /* 4 byte sector size */
+       /* Page size */
+       SyBigEndianPack32(zRaw,(sxu32)pPager->iPageSize);
+       zRaw += 4; /* 4 byte page size */
+       /* Key value storage engine */
+       nLen = (sxu16)SyStrlen(pEngine->pIo->pMethods->zName);
+       SyBigEndianPack16(zRaw,nLen); /* 2 byte storage engine name */
+       zRaw += 2;
+       SyMemcpy((const void *)pEngine->pIo->pMethods->zName,(void *)zRaw,nLen);
+       zRaw += nLen;
+       /* All rest are meta-data available to the host application */
+       return UNQLITE_OK;
+}
+/*
+ * Read the unqlite header (first page). (Big-Endian)
+ */
+static int pager_extract_header(Pager *pPager,const unsigned char *zRaw,sxu32 nByte)
+{
+       const unsigned char *zEnd = &zRaw[nByte];
+       sxu32 nDos,iMagic;
+       sxu16 nLen;
+       char *zKv;
+       /* Database signature */
+       if( SyMemcmp(UNQLITE_DB_SIG,zRaw,sizeof(UNQLITE_DB_SIG)-1) != 0 ){
+               /* Corrupt database */
+               return UNQLITE_CORRUPT;
+       }
+       zRaw += sizeof(UNQLITE_DB_SIG)-1;
+       /* Database magic number */
+       SyBigEndianUnpack32(zRaw,&iMagic);
+       zRaw += 4; /* 4 byte magic number */
+       if( iMagic != UNQLITE_DB_MAGIC ){
+               /* Corrupt database */
+               return UNQLITE_CORRUPT;
+       }
+       /* Database creation time */
+       SyBigEndianUnpack32(zRaw,&nDos);
+       zRaw += 4; /* 4 byte DOS time format */
+       SyDosTimeFormat(nDos,&pPager->tmCreate);
+       /* Sector size */
+       SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iSectorSize);
+       zRaw += 4; /* 4 byte sector size */
+       /* Page size */
+       SyBigEndianUnpack32(zRaw,(sxu32 *)&pPager->iPageSize);
+       zRaw += 4; /* 4 byte page size */
+       /* Check that the values read from the page-size and sector-size fields
+    ** are within range. To be 'in range', both values need to be a power
+    ** of two greater than or equal to 512 or 32, and not greater than their 
+    ** respective compile time maximum limits.
+    */
+    if( pPager->iPageSize<UNQLITE_MIN_PAGE_SIZE || pPager->iSectorSize<32
+     || pPager->iPageSize>UNQLITE_MAX_PAGE_SIZE || pPager->iSectorSize>MAX_SECTOR_SIZE
+     || ((pPager->iPageSize<-1)&pPager->iPageSize)!=0    || ((pPager->iSectorSize-1)&pPager->iSectorSize)!=0 
+    ){
+      return UNQLITE_CORRUPT;
+       }
+       /* Key value storage engine */
+       SyBigEndianUnpack16(zRaw,&nLen); /* 2 byte storage engine length */
+       zRaw += 2;
+       if( nLen > (sxu16)(zEnd - zRaw) ){
+               nLen = (sxu16)(zEnd - zRaw);
+       }
+       zKv = (char *)SyMemBackendDup(pPager->pAllocator,(const char *)zRaw,nLen);
+       if( zKv == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       SyStringInitFromBuf(&pPager->sKv,zKv,nLen);
+       return UNQLITE_OK;
+}
+/*
+ * Read the database header.
+ */
+static int pager_read_db_header(Pager *pPager)
+{
+       unsigned char zRaw[UNQLITE_MIN_PAGE_SIZE]; /* Minimum page size */
+       sxi64 n = 0;              /* Size of db file in bytes */
+       int rc;
+       /* Get the file size first */
+       rc = unqliteOsFileSize(pPager->pfd,&n);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       pPager->dbByteSize = n;
+       if( n > 0 ){
+               unqlite_kv_methods *pMethods;
+               SyString *pKv;
+               pgno nPage;
+               if( n < UNQLITE_MIN_PAGE_SIZE ){
+                       /* A valid unqlite database must be at least 512 bytes long */
+                       unqliteGenError(pPager->pDb,"Malformed database image");
+                       return UNQLITE_CORRUPT;
+               }
+               /* Read the database header */
+               rc = unqliteOsRead(pPager->pfd,zRaw,sizeof(zRaw),0);
+               if( rc != UNQLITE_OK ){
+                       unqliteGenError(pPager->pDb,"IO error while reading database header");
+                       return rc;
+               }
+               /* Extract the header */
+               rc = pager_extract_header(pPager,zRaw,sizeof(zRaw));
+               if( rc != UNQLITE_OK ){
+                       unqliteGenError(pPager->pDb,rc == UNQLITE_NOMEM ? "Unqlite is running out of memory" : "Malformed database image");
+                       return rc;
+               }
+               /* Update pager state  */
+               nPage = (pgno)(n / pPager->iPageSize);
+               if( nPage==0 && n>0 ){
+                       nPage = 1;
+               }
+               pPager->dbSize = nPage;
+               /* Laod the target Key/Value storage engine */
+               pKv = &pPager->sKv;
+               pMethods = unqliteFindKVStore(pKv->zString,pKv->nByte);
+               if( pMethods == 0 ){
+                       unqliteGenErrorFormat(pPager->pDb,"No such Key/Value storage engine '%z'",pKv);
+                       return UNQLITE_NOTIMPLEMENTED;
+               }
+               /* Install the new KV storage engine */
+               rc = unqlitePagerRegisterKvEngine(pPager,pMethods);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+       }else{
+               /* Set a default page and sector size */
+               pPager->iSectorSize = GetSectorSize(pPager->pfd);
+               pPager->iPageSize = unqliteGetPageSize();
+               SyStringInitFromBuf(&pPager->sKv,pPager->pEngine->pIo->pMethods->zName,SyStrlen(pPager->pEngine->pIo->pMethods->zName));
+               pPager->dbSize = 0;
+       }
+       /* Allocate a temporary page size */
+       pPager->zTmpPage = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iPageSize);
+       if( pPager->zTmpPage == 0 ){
+               unqliteGenOutofMem(pPager->pDb);
+               return UNQLITE_NOMEM;
+       }
+       SyZero(pPager->zTmpPage,(sxu32)pPager->iPageSize);
+       return UNQLITE_OK;
+}
+/*
+ * Write the database header.
+ */
+static int pager_create_header(Pager *pPager)
+{
+       Page *pHeader;
+       int rc;
+       /* Allocate a new page */
+       pHeader = pager_alloc_page(pPager,0);
+       if( pHeader == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       pPager->pHeader = pHeader;
+       /* Link the page */
+       pager_link_page(pPager,pHeader);
+       /* Add to the dirty list */
+       pager_page_to_dirty_list(pPager,pHeader);
+       /* Write the database header */
+       rc = pager_write_db_header(pPager);
+       return rc;
+}
+/*
+** This function is called to obtain a shared lock on the database file.
+** It is illegal to call unqlitePagerAcquire() until after this function
+** has been successfully called. If a shared-lock is already held when
+** this function is called, it is a no-op.
+**
+** The following operations are also performed by this function.
+**
+**   1) If the pager is currently in PAGER_OPEN state (no lock held
+**      on the database file), then an attempt is made to obtain a
+**      SHARED lock on the database file. Immediately after obtaining
+**      the SHARED lock, the file-system is checked for a hot-journal,
+**      which is played back if present. 
+**
+** If everything is successful, UNQLITE_OK is returned. If an IO error 
+** occurs while locking the database, checking for a hot-journal file or 
+** rolling back a journal file, the IO error code is returned.
+*/
+static int pager_shared_lock(Pager *pPager)
+{
+       int rc = UNQLITE_OK;
+       if( pPager->iState == PAGER_OPEN ){
+               unqlite_kv_methods *pMethods;
+               /* Open the target database */
+               rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zFilename,&pPager->pfd,pPager->iOpenFlags);
+               if( rc != UNQLITE_OK ){
+                       unqliteGenErrorFormat(pPager->pDb,
+                               "IO error while opening the target database file: %s",pPager->zFilename
+                               );
+                       return rc;
+               }
+               /* Try to obtain a shared lock */
+               rc = pager_wait_on_lock(pPager,SHARED_LOCK);
+               if( rc == UNQLITE_OK ){
+                       if( pPager->iLock <= SHARED_LOCK ){
+                               /* Rollback any hot journal */
+                               rc = pager_journal_rollback(pPager,1);
+                               if( rc != UNQLITE_OK ){
+                                       return rc;
+                               }
+                       }
+                       /* Read the database header */
+                       rc = pager_read_db_header(pPager);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       if(pPager->dbSize > 0 ){
+                               if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){
+                                       const jx9_vfs *pVfs = jx9ExportBuiltinVfs();
+                                       /* Obtain a read-only memory view of the whole file */
+                                       if( pVfs && pVfs->xMmap ){
+                                               int vr;
+                                               vr = pVfs->xMmap(pPager->zFilename,&pPager->pMmap,&pPager->dbByteSize);
+                                               if( vr != JX9_OK ){
+                                                       /* Generate a warning */
+                                                       unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database");
+                                                       pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP;
+                                               }
+                                       }else{
+                                               /* Generate a warning */
+                                               unqliteGenError(pPager->pDb,"Cannot obtain a read-only memory view of the target database");
+                                               pPager->iOpenFlags &= ~UNQLITE_OPEN_MMAP;
+                                       }
+                               }
+                       }
+                       /* Update the pager state */
+                       pPager->iState = PAGER_READER;
+                       /* Invoke the xOpen methods if available */
+                       pMethods = pPager->pEngine->pIo->pMethods;
+                       if( pMethods->xOpen ){
+                               rc = pMethods->xOpen(pPager->pEngine,pPager->dbSize);
+                               if( rc != UNQLITE_OK ){
+                                       unqliteGenErrorFormat(pPager->pDb,
+                                               "xOpen() method of the underlying KV engine '%z' failed",
+                                               &pPager->sKv
+                                               );
+                                       pager_unlock_db(pPager,NO_LOCK);
+                                       pPager->iState = PAGER_OPEN;
+                                       return rc;
+                               }
+                       }
+               }else if( rc == UNQLITE_BUSY ){
+                       unqliteGenError(pPager->pDb,"Another process or thread have a reserved or exclusive lock on this database");
+               }               
+       }
+       return rc;
+}
+/*
+** Begin a write-transaction on the specified pager object. If a 
+** write-transaction has already been opened, this function is a no-op.
+*/
+UNQLITE_PRIVATE int unqlitePagerBegin(Pager *pPager)
+{
+       int rc;
+       /* Obtain a shared lock on the database first */
+       rc = pager_shared_lock(pPager);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( pPager->iState >= PAGER_WRITER_LOCKED ){
+               return UNQLITE_OK;
+       }
+       if( pPager->is_rdonly ){
+               unqliteGenError(pPager->pDb,"Read-only database");
+               /* Read only database */
+               return UNQLITE_READ_ONLY;
+       }
+       /* Obtain a reserved lock on the database */
+       rc = pager_wait_on_lock(pPager,RESERVED_LOCK);
+       if( rc == UNQLITE_OK ){
+               /* Create the bitvec */
+               pPager->pVec = unqliteBitvecCreate(pPager->pAllocator,pPager->dbSize);
+               if( pPager->pVec == 0 ){
+                       unqliteGenOutofMem(pPager->pDb);
+                       rc = UNQLITE_NOMEM;
+                       goto fail;
+               }
+               /* Change to the WRITER_LOCK state */
+               pPager->iState = PAGER_WRITER_LOCKED;
+               pPager->dbOrigSize = pPager->dbSize;
+               pPager->iJournalOfft = 0;
+               pPager->nRec = 0;
+               if( pPager->dbSize < 1 ){
+                       /* Write the  database header */
+                       rc = pager_create_header(pPager);
+                       if( rc != UNQLITE_OK ){
+                               goto fail;
+                       }
+                       pPager->dbSize = 1;
+               }
+       }else if( rc == UNQLITE_BUSY ){
+               unqliteGenError(pPager->pDb,"Another process or thread have a reserved lock on this database");
+       }
+       return rc;
+fail:
+       /* Downgrade to shared lock */
+       pager_unlock_db(pPager,SHARED_LOCK);
+       return rc;
+}
+/*
+** This function is called at the start of every write transaction.
+** There must already be a RESERVED or EXCLUSIVE lock on the database 
+** file when this routine is called.
+**
+*/
+static int unqliteOpenJournal(Pager *pPager)
+{
+       unsigned char *zHeader;
+       int rc = UNQLITE_OK;
+       if( pPager->is_mem || pPager->no_jrnl ){
+               /* Journaling is omitted for this database */
+               goto finish;
+       }
+       if( pPager->iState >= PAGER_WRITER_CACHEMOD ){
+               /* Already opened */
+               return UNQLITE_OK;
+       }
+       /* Delete any previously journal with the same name */
+       unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
+       /* Open the journal file */
+       rc = unqliteOsOpen(pPager->pVfs,pPager->pAllocator,pPager->zJournal,
+               &pPager->pjfd,UNQLITE_OPEN_CREATE|UNQLITE_OPEN_READWRITE);
+       if( rc != UNQLITE_OK ){
+               unqliteGenErrorFormat(pPager->pDb,"IO error while opening journal file: %s",pPager->zJournal);
+               return rc;
+       }
+       /* Write the journal header */
+       zHeader = (unsigned char *)SyMemBackendAlloc(pPager->pAllocator,(sxu32)pPager->iSectorSize);
+       if( zHeader == 0 ){
+               rc = UNQLITE_NOMEM;
+               goto fail;
+       }
+       pager_write_journal_header(pPager,zHeader);
+       /* Perform the disk write */
+       rc = unqliteOsWrite(pPager->pjfd,zHeader,pPager->iSectorSize,0);
+       /* Offset to start writing from */
+       pPager->iJournalOfft = pPager->iSectorSize;
+       /* All done, journal will be synced later */
+       SyMemBackendFree(pPager->pAllocator,zHeader);
+finish:
+       if( rc == UNQLITE_OK ){
+               pPager->iState = PAGER_WRITER_CACHEMOD;
+               return UNQLITE_OK;
+       }
+fail:
+       /* Unlink the journal file if something goes wrong */
+       unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
+       unqliteOsDelete(pPager->pVfs,pPager->zJournal,0);
+       pPager->pjfd = 0;
+       return rc;
+}
+/*
+** Sync the journal. In other words, make sure all the pages that have
+** been written to the journal have actually reached the surface of the
+** disk and can be restored in the event of a hot-journal rollback.
+*
+* This routine try also to obtain an exlusive lock on the database.
+*/
+static int unqliteFinalizeJournal(Pager *pPager,int *pRetry,int close_jrnl)
+{
+       int rc;
+       *pRetry = 0;
+       /* Grab the exclusive lock first */
+       rc = pager_lock_db(pPager,EXCLUSIVE_LOCK);
+       if( rc != UNQLITE_OK ){
+               /* Retry the excusive lock process */
+               *pRetry = 1;
+               rc = UNQLITE_OK;
+       }
+       if( pPager->no_jrnl ){
+               /* Journaling is omitted, return immediately */
+               return UNQLITE_OK;
+       }
+       /* Write the total number of database records */
+       rc = WriteInt32(pPager->pjfd,pPager->nRec,8 /* sizeof(aJournalRec) */);
+       if( rc != UNQLITE_OK ){
+               if( pPager->nRec > 0 ){
+                       return rc;
+               }else{
+                       /* Not so fatal */
+                       rc = UNQLITE_OK;
+               }
+       }
+       /* Sync the journal and close it */
+       rc = unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
+       if( close_jrnl ){
+               /* close the journal file */
+               if( UNQLITE_OK != unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd) ){
+                       if( rc != UNQLITE_OK /* unqliteOsSync */ ){
+                               return rc;
+                       }
+               }
+               pPager->pjfd = 0;
+       }
+       if( (*pRetry) == 1 ){
+               if( pager_lock_db(pPager,EXCLUSIVE_LOCK) == UNQLITE_OK ){
+                       /* Got exclusive lock */
+                       *pRetry = 0;
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Mark a single data page as writeable. The page is written into the 
+ * main journal as required.
+ */
+static int page_write(Pager *pPager,Page *pPage)
+{
+       int rc;
+       if( !pPager->is_mem && !pPager->no_jrnl ){
+               /* Write the page to the transaction journal */
+               if( pPage->pgno < pPager->dbOrigSize && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){
+                       sxu32 cksum;
+                       if( pPager->nRec == SXU32_HIGH ){
+                               /* Journal Limit reached */
+                               unqliteGenError(pPager->pDb,"Journal record limit reached, commit your changes");
+                               return UNQLITE_LIMIT;
+                       }
+                       /* Write the page number */
+                       rc = WriteInt64(pPager->pjfd,pPage->pgno,pPager->iJournalOfft);
+                       if( rc != UNQLITE_OK ){ return rc; }
+                       /* Write the raw page */
+                       /** CODEC */
+                       rc = unqliteOsWrite(pPager->pjfd,pPage->zData,pPager->iPageSize,pPager->iJournalOfft + 8);
+                       if( rc != UNQLITE_OK ){ return rc; }
+                       /* Compute the checksum */
+                       cksum = pager_cksum(pPager,pPage->zData);
+                       rc = WriteInt32(pPager->pjfd,cksum,pPager->iJournalOfft + 8 + pPager->iPageSize);
+                       if( rc != UNQLITE_OK ){ return rc; }
+                       /* Update the journal offset */
+                       pPager->iJournalOfft += 8 /* page num */ + pPager->iPageSize + 4 /* cksum */;
+                       pPager->nRec++;
+                       /* Mark as journalled  */
+                       unqliteBitvecSet(pPager->pVec,pPage->pgno);
+               }
+       }
+       /* Add the page to the dirty list */
+       pager_page_to_dirty_list(pPager,pPage);
+       /* Update the database size and return. */
+       if( (1 + pPage->pgno) > pPager->dbSize ){
+               pPager->dbSize = 1 + pPage->pgno;
+               if( pPager->dbSize == SXU64_HIGH ){
+                       unqliteGenError(pPager->pDb,"Database maximum page limit (64-bit) reached");
+                       return UNQLITE_LIMIT;
+               }
+       }       
+       return UNQLITE_OK;
+}
+/*
+** The argument is the first in a linked list of dirty pages connected
+** by the PgHdr.pDirty pointer. This function writes each one of the
+** in-memory pages in the list to the database file. The argument may
+** be NULL, representing an empty list. In this case this function is
+** a no-op.
+**
+** The pager must hold at least a RESERVED lock when this function
+** is called. Before writing anything to the database file, this lock
+** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
+** UNQLITE_BUSY is returned and no data is written to the database file.
+*/
+static int pager_write_dirty_pages(Pager *pPager,Page *pDirty)
+{
+       int rc = UNQLITE_OK;
+       Page *pNext;
+       for(;;){
+               if( pDirty == 0 ){
+                       break;
+               }
+               /* Point to the next dirty page */
+               pNext = pDirty->pDirtyPrev; /* Not a bug: Reverse link */
+               if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){
+                       rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize);
+                       if( rc != UNQLITE_OK ){
+                               /* A rollback should be done */
+                               break;
+                       }
+               }
+               /* Remove stale flags */
+               pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
+               if( pDirty->nRef < 1 ){
+                       /* Unlink the page now it is unused */
+                       pager_unlink_page(pPager,pDirty);
+                       /* Release the page */
+                       pager_release_page(pPager,pDirty);
+               }
+               /* Point to the next page */
+               pDirty = pNext;
+       }
+       pPager->pDirty = pPager->pFirstDirty = 0;
+       pPager->pHotDirty = pPager->pFirstHot = 0;
+       pPager->nHot = 0;
+       return rc;
+}
+/*
+** The argument is the first in a linked list of hot dirty pages connected
+** by the PgHdr.pHotDirty pointer. This function writes each one of the
+** in-memory pages in the list to the database file. The argument may
+** be NULL, representing an empty list. In this case this function is
+** a no-op.
+**
+** The pager must hold at least a RESERVED lock when this function
+** is called. Before writing anything to the database file, this lock
+** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
+** UNQLITE_BUSY is returned and no data is written to the database file.
+*/
+static int pager_write_hot_dirty_pages(Pager *pPager,Page *pDirty)
+{
+       int rc = UNQLITE_OK;
+       Page *pNext;
+       for(;;){
+               if( pDirty == 0 ){
+                       break;
+               }
+               /* Point to the next page */
+               pNext = pDirty->pPrevHot; /* Not a bug: Reverse link */
+               if( (pDirty->flags & PAGE_DONT_WRITE) == 0 ){
+                       rc = unqliteOsWrite(pPager->pfd,pDirty->zData,pPager->iPageSize,pDirty->pgno * pPager->iPageSize);
+                       if( rc != UNQLITE_OK ){
+                               break;
+                       }
+               }
+               /* Remove stale flags */
+               pDirty->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
+               /* Unlink from the list of dirty pages */
+               if( pDirty->pDirtyPrev ){
+                       pDirty->pDirtyPrev->pDirtyNext = pDirty->pDirtyNext;
+               }else{
+                       pPager->pDirty = pDirty->pDirtyNext;
+               }
+               if( pDirty->pDirtyNext ){
+                       pDirty->pDirtyNext->pDirtyPrev = pDirty->pDirtyPrev;
+               }else{
+                       pPager->pFirstDirty = pDirty->pDirtyPrev;
+               }
+               /* Discard */
+               pager_unlink_page(pPager,pDirty);
+               /* Release the page */
+               pager_release_page(pPager,pDirty);
+               /* Next hot page */
+               pDirty = pNext;
+       }
+       return rc;
+}
+/*
+ * Commit a transaction: Phase one.
+ */
+static int pager_commit_phase1(Pager *pPager)
+{
+       int get_excl = 0;
+       Page *pDirty;
+       int rc;
+       /* If no database changes have been made, return early. */
+       if( pPager->iState < PAGER_WRITER_CACHEMOD ){
+               return UNQLITE_OK;
+       }
+       if( pPager->is_mem ){
+               /* An in-memory database */
+               return UNQLITE_OK;
+       }
+       if( pPager->is_rdonly ){
+               /* Read-Only DB */
+               unqliteGenError(pPager->pDb,"Read-Only database");
+               return UNQLITE_READ_ONLY;
+       }
+       /* Finalize the journal file */
+       rc = unqliteFinalizeJournal(pPager,&get_excl,1);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Get the dirty pages */
+       pDirty = pager_get_dirty_pages(pPager);
+       if( get_excl ){
+               /* Wait one last time for the exclusive lock */
+               rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK);
+               if( rc != UNQLITE_OK ){
+                       unqliteGenError(pPager->pDb,"Cannot obtain an Exclusive lock on the target database");
+                       return rc;
+               }
+       }
+       if( pPager->iFlags & PAGER_CTRL_DIRTY_COMMIT ){
+               /* Synce the database first if a dirty commit have been applied */
+               unqliteOsSync(pPager->pfd,UNQLITE_SYNC_NORMAL);
+       }
+       /* Write the dirty pages */
+       rc = pager_write_dirty_pages(pPager,pDirty);
+       if( rc != UNQLITE_OK ){
+               /* Rollback your DB */
+               pPager->iFlags |= PAGER_CTRL_COMMIT_ERR;
+               pPager->pFirstDirty = pDirty;
+               unqliteGenError(pPager->pDb,"IO error while writing dirty pages, rollback your database");
+               return rc;
+       }
+       /* If the file on disk is not the same size as the database image,
+     * then use unqliteOsTruncate to grow or shrink the file here.
+     */
+       if( pPager->dbSize != pPager->dbOrigSize ){
+               unqliteOsTruncate(pPager->pfd,pPager->iPageSize * pPager->dbSize);
+       }
+       /* Sync the database file */
+       unqliteOsSync(pPager->pfd,UNQLITE_SYNC_FULL);
+       /* Remove stale flags */
+       pPager->iJournalOfft = 0;
+       pPager->nRec = 0;
+       return UNQLITE_OK;
+}
+/*
+ * Commit a transaction: Phase two.
+ */
+static int pager_commit_phase2(Pager *pPager)
+{
+       if( !pPager->is_mem ){
+               if( pPager->iState == PAGER_OPEN ){
+                       return UNQLITE_OK;
+               }
+               if( pPager->iState != PAGER_READER ){
+                       if( !pPager->no_jrnl ){
+                               /* Finally, unlink the journal file */
+                               unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
+                       }
+                       /* Downgrade to shraed lock */
+                       pager_unlock_db(pPager,SHARED_LOCK);
+                       pPager->iState = PAGER_READER;
+                       if( pPager->pVec ){
+                               unqliteBitvecDestroy(pPager->pVec);
+                               pPager->pVec = 0;
+                       }
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Perform a dirty commit.
+ */
+static int pager_dirty_commit(Pager *pPager)
+{
+       int get_excl = 0;
+       Page *pHot;
+       int rc;
+       /* Finalize the journal file without closing it */
+       rc = unqliteFinalizeJournal(pPager,&get_excl,0);
+       if( rc != UNQLITE_OK ){
+               /* It's not a fatal error if something goes wrong here since
+                * its not the final commit.
+                */
+               return UNQLITE_OK;
+       }
+       /* Point to the list of hot pages */
+       pHot = pager_get_hot_pages(pPager);
+       if( pHot == 0 ){
+               return UNQLITE_OK;
+       }
+       if( get_excl ){
+               /* Wait one last time for the exclusive lock */
+               rc = pager_wait_on_lock(pPager,EXCLUSIVE_LOCK);
+               if( rc != UNQLITE_OK ){
+                       /* Not so fatal, will try another time */
+                       return UNQLITE_OK;
+               }
+       }
+       /* Tell that a dirty commit happen */
+       pPager->iFlags |= PAGER_CTRL_DIRTY_COMMIT;
+       /* Write the hot pages now */
+       rc = pager_write_hot_dirty_pages(pPager,pHot);
+       if( rc != UNQLITE_OK ){
+               pPager->iFlags |= PAGER_CTRL_COMMIT_ERR;
+               unqliteGenError(pPager->pDb,"IO error while writing hot dirty pages, rollback your database");
+               return rc;
+       }
+       pPager->pFirstHot = pPager->pHotDirty = 0;
+       pPager->nHot = 0;
+       /* No need to sync the database file here, since the journal is already
+        * open here and this is not the final commit.
+        */
+       return UNQLITE_OK;
+}
+/*
+** Commit a transaction and sync the database file for the pager pPager.
+**
+** This routine ensures that:
+**
+**   * the journal is synced,
+**   * all dirty pages are written to the database file, 
+**   * the database file is truncated (if required), and
+**   * the database file synced.
+**   * the journal file is deleted.
+*/
+UNQLITE_PRIVATE int unqlitePagerCommit(Pager *pPager)
+{
+       int rc;
+       /* Commit: Phase One */
+       rc = pager_commit_phase1(pPager);
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       /* Commit: Phase Two */
+       rc = pager_commit_phase2(pPager);
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       /* Remove stale flags */
+       pPager->iFlags &= ~PAGER_CTRL_COMMIT_ERR;
+       /* All done */
+       return UNQLITE_OK;
+fail:
+       /* Disable the auto-commit flag */
+       pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
+       return rc;
+}
+/*
+ * Reset the pager to its initial state. This is caused by
+ * a rollback operation.
+ */
+static int pager_reset_state(Pager *pPager,int bResetKvEngine)
+{
+       unqlite_kv_engine *pEngine = pPager->pEngine;
+       Page *pNext,*pPtr = pPager->pAll;
+       const unqlite_kv_io *pIo;
+       int rc;
+       /* Remove stale flags */
+       pPager->iFlags &= ~(PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT);
+       pPager->iJournalOfft = 0;
+       pPager->nRec = 0;
+       /* Database original size */
+       pPager->dbSize = pPager->dbOrigSize;
+       /* Discard all in-memory pages */
+       for(;;){
+               if( pPtr == 0 ){
+                       break;
+               }
+               pNext = pPtr->pNext; /* Reverse link */
+               /* Remove stale flags */
+               pPtr->flags &= ~(PAGE_DIRTY|PAGE_DONT_WRITE|PAGE_NEED_SYNC|PAGE_IN_JOURNAL|PAGE_HOT_DIRTY);
+               /* Release the page */
+               pager_release_page(pPager,pPtr);
+               /* Point to the next page */
+               pPtr = pNext;
+       }
+       pPager->pAll = 0;
+       pPager->nPage = 0;
+       pPager->pDirty = pPager->pFirstDirty = 0;
+       pPager->pHotDirty = pPager->pFirstHot = 0;
+       pPager->nHot = 0;
+       if( pPager->apHash ){
+               /* Zero the table */
+               SyZero((void *)pPager->apHash,sizeof(Page *) * pPager->nSize);
+       }
+       if( pPager->pVec ){
+               unqliteBitvecDestroy(pPager->pVec);
+               pPager->pVec = 0;
+       }
+       /* Switch back to shared lock */
+       pager_unlock_db(pPager,SHARED_LOCK);
+       pPager->iState = PAGER_READER;
+       if( bResetKvEngine ){
+               /* Reset the underlying KV engine */
+               pIo = pEngine->pIo;
+               if( pIo->pMethods->xRelease ){
+                       /* Call the release callback */
+                       pIo->pMethods->xRelease(pEngine);
+               }
+               /* Zero the structure */
+               SyZero(pEngine,(sxu32)pIo->pMethods->szKv);
+               /* Fill in */
+               pEngine->pIo = pIo;
+               if( pIo->pMethods->xInit ){
+                       /* Call the init method */
+                       rc = pIo->pMethods->xInit(pEngine,pPager->iPageSize);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+               }
+               if( pIo->pMethods->xOpen ){
+                       /* Call the xOpen method */
+                       rc = pIo->pMethods->xOpen(pEngine,pPager->dbSize);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+               }
+       }
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+** If a write transaction is open, then all changes made within the 
+** transaction are reverted and the current write-transaction is closed.
+** The pager falls back to PAGER_READER state if successful.
+**
+** Otherwise, in rollback mode, this function performs two functions:
+**
+**   1) It rolls back the journal file, restoring all database file and 
+**      in-memory cache pages to the state they were in when the transaction
+**      was opened, and
+**
+**   2) It finalizes the journal file, so that it is not used for hot
+**      rollback at any point in the future (i.e. deletion).
+**
+** Finalization of the journal file (task 2) is only performed if the 
+** rollback is successful.
+**
+*/
+UNQLITE_PRIVATE int unqlitePagerRollback(Pager *pPager,int bResetKvEngine)
+{
+       int rc = UNQLITE_OK;
+       if( pPager->iState < PAGER_WRITER_LOCKED ){
+               /* A write transaction must be opened */
+               return UNQLITE_OK;
+       }
+       if( pPager->is_mem ){
+               /* As of this release 1.1.6: Transactions are not supported for in-memory databases */
+               return UNQLITE_OK;
+       }
+       if( pPager->is_rdonly ){
+               /* Read-Only DB */
+               unqliteGenError(pPager->pDb,"Read-Only database");
+               return UNQLITE_READ_ONLY;
+       }
+       if( pPager->iState >= PAGER_WRITER_CACHEMOD ){
+               if( !pPager->no_jrnl ){
+                       /* Close any outstanding joural file */
+                       if( pPager->pjfd ){
+                               /* Sync the journal file */
+                               unqliteOsSync(pPager->pjfd,UNQLITE_SYNC_NORMAL);
+                       }
+                       unqliteOsCloseFree(pPager->pAllocator,pPager->pjfd);
+                       pPager->pjfd = 0;
+                       if( pPager->iFlags & (PAGER_CTRL_COMMIT_ERR|PAGER_CTRL_DIRTY_COMMIT) ){
+                               /* Perform the rollback */
+                               rc = pager_journal_rollback(pPager,0);
+                               if( rc != UNQLITE_OK ){
+                                       /* Set the auto-commit flag */
+                                       pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT;
+                                       return rc;
+                               }
+                       }
+               }
+               /* Unlink the journal file */
+               unqliteOsDelete(pPager->pVfs,pPager->zJournal,1);
+               /* Reset the pager state */
+               rc = pager_reset_state(pPager,bResetKvEngine);
+               if( rc != UNQLITE_OK ){
+                       /* Mostly an unlikely scenario */
+                       pPager->pDb->iFlags |= UNQLITE_FL_DISABLE_AUTO_COMMIT; /* Set the auto-commit flag */
+                       unqliteGenError(pPager->pDb,"Error while reseting pager to its initial state");
+                       return rc;
+               }
+       }else{
+               /* Downgrade to shared lock */
+               pager_unlock_db(pPager,SHARED_LOCK);
+               pPager->iState = PAGER_READER;
+       }
+       return UNQLITE_OK;
+}
+/*
+ *  Mark a data page as non writeable.
+ */
+static int unqlitePagerDontWrite(unqlite_page *pMyPage)
+{
+       Page *pPage = (Page *)pMyPage;
+       if( pPage->pgno > 0 /* Page 0 is always writeable */ ){
+               pPage->flags |= PAGE_DONT_WRITE;
+       }
+       return UNQLITE_OK;
+}
+/*
+** Mark a data page as writeable. This routine must be called before 
+** making changes to a page. The caller must check the return value 
+** of this function and be careful not to change any page data unless 
+** this routine returns UNQLITE_OK.
+*/
+static int unqlitePageWrite(unqlite_page *pMyPage)
+{
+       Page *pPage = (Page *)pMyPage;
+       Pager *pPager = pPage->pPager;
+       int rc;
+       /* Begin the write transaction */
+       rc = unqlitePagerBegin(pPager);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( pPager->iState == PAGER_WRITER_LOCKED ){
+               /* The journal file needs to be opened. Higher level routines have already
+                ** obtained the necessary locks to begin the write-transaction, but the
+                ** rollback journal might not yet be open. Open it now if this is the case.
+                */
+               rc = unqliteOpenJournal(pPager);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+       }
+       if( pPager->nHot > 127 ){
+               /* Write hot dirty pages */
+               rc = pager_dirty_commit(pPager);
+               if( rc != UNQLITE_OK ){
+                       /* A rollback must be done */
+                       unqliteGenError(pPager->pDb,"Please perform a rollback");
+                       return rc;
+               }
+       }
+       /* Write the page to the journal file */
+       rc = page_write(pPager,pPage);
+       return rc;
+}
+/*
+** Acquire a reference to page number pgno in pager pPager (a page
+** reference has type unqlite_page*). If the requested reference is 
+** successfully obtained, it is copied to *ppPage and UNQLITE_OK returned.
+**
+** If the requested page is already in the cache, it is returned. 
+** Otherwise, a new page object is allocated and populated with data
+** read from the database file.
+*/
+static int unqlitePagerAcquire(
+  Pager *pPager,      /* The pager open on the database file */
+  pgno pgno,          /* Page number to fetch */
+  unqlite_page **ppPage,    /* OUT: Acquired page */
+  int fetchOnly,      /* Cache lookup only */
+  int noContent       /* Do not bother reading content from disk if true */
+)
+{
+       Page *pPage;
+       int rc;
+       /* Acquire a shared lock (if not yet done) on the database and rollback any hot-journal if present */
+       rc = pager_shared_lock(pPager);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Fetch the page from the cache */
+       pPage = pager_fetch_page(pPager,pgno);
+       if( fetchOnly ){
+               if( ppPage ){
+                       *ppPage = (unqlite_page *)pPage;
+               }
+               return pPage ? UNQLITE_OK : UNQLITE_NOTFOUND;
+       }
+       if( pPage == 0 ){
+               /* Allocate a new page */
+               pPage = pager_alloc_page(pPager,pgno);
+               if( pPage == 0 ){
+                       unqliteGenOutofMem(pPager->pDb);
+                       return UNQLITE_NOMEM;
+               }
+               /* Read page contents */
+               rc = pager_get_page_contents(pPager,pPage,noContent);
+               if( rc != UNQLITE_OK ){
+                       SyMemBackendPoolFree(pPager->pAllocator,pPage);
+                       return rc;
+               }
+               /* Link the page */
+               pager_link_page(pPager,pPage);
+       }else{
+               if( ppPage ){
+                       page_ref(pPage);
+               }
+       }
+       /* All done, page is loaded in memeory */
+       if( ppPage ){
+               *ppPage = (unqlite_page *)pPage;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Return true if we are dealing with an in-memory database.
+ */
+static int unqliteInMemory(const char *zFilename)
+{
+       sxu32 n;
+       if( SX_EMPTY_STR(zFilename) ){
+               /* NULL or the empty string means an in-memory database */
+               return TRUE;
+       }
+       n = SyStrlen(zFilename);
+       if( n == sizeof(":mem:") - 1 && 
+               SyStrnicmp(zFilename,":mem:",sizeof(":mem:") - 1) == 0 ){
+                       return TRUE;
+       }
+       if( n == sizeof(":memory:") - 1 && 
+               SyStrnicmp(zFilename,":memory:",sizeof(":memory:") - 1) == 0 ){
+                       return TRUE;
+       }
+       return FALSE;
+}
+/*
+ * Allocate a new KV cursor.
+ */
+UNQLITE_PRIVATE int unqliteInitCursor(unqlite *pDb,unqlite_kv_cursor **ppOut)
+{
+       unqlite_kv_methods *pMethods;
+       unqlite_kv_cursor *pCur;
+       sxu32 nByte;
+       /* Storage engine methods */
+       pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods;
+       if( pMethods->szCursor < 1 ){
+               /* Implementation does not supprt cursors */
+               unqliteGenErrorFormat(pDb,"Storage engine '%s' does not support cursors",pMethods->zName);
+               return UNQLITE_NOTIMPLEMENTED;
+       }
+       nByte = pMethods->szCursor;
+       if( nByte < sizeof(unqlite_kv_cursor) ){
+               nByte += sizeof(unqlite_kv_cursor);
+       }
+       pCur = (unqlite_kv_cursor *)SyMemBackendPoolAlloc(&pDb->sMem,nByte);
+       if( pCur == 0 ){
+               unqliteGenOutofMem(pDb);
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pCur,nByte);
+       /* Save the cursor */
+       pCur->pStore = pDb->sDB.pPager->pEngine;
+       /* Invoke the initialization callback if any */
+       if( pMethods->xCursorInit ){
+               pMethods->xCursorInit(pCur);
+       }
+       /* All done */
+       *ppOut = pCur;
+       return UNQLITE_OK;
+}
+/*
+ * Release a cursor.
+ */
+UNQLITE_PRIVATE int unqliteReleaseCursor(unqlite *pDb,unqlite_kv_cursor *pCur)
+{
+       unqlite_kv_methods *pMethods;
+       /* Storage engine methods */
+       pMethods = pDb->sDB.pPager->pEngine->pIo->pMethods;
+       /* Invoke the release callback if available */
+       if( pMethods->xCursorRelease ){
+               pMethods->xCursorRelease(pCur);
+       }
+       /* Finally, free the whole instance */
+       SyMemBackendPoolFree(&pDb->sMem,pCur);
+       return UNQLITE_OK;
+}
+/*
+ * Release the underlying KV storage engine and invoke
+ * its associated callbacks if available.
+ */
+static void pager_release_kv_engine(Pager *pPager)
+{
+       unqlite_kv_engine *pEngine = pPager->pEngine;
+       unqlite_db *pStorage = &pPager->pDb->sDB;
+       if( pStorage->pCursor ){
+               /* Release the associated cursor */
+               unqliteReleaseCursor(pPager->pDb,pStorage->pCursor);
+               pStorage->pCursor = 0;
+       }
+       if( pEngine->pIo->pMethods->xRelease ){
+               pEngine->pIo->pMethods->xRelease(pEngine);
+       }
+       /* Release the whole instance */
+       SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine->pIo);
+       SyMemBackendFree(&pPager->pDb->sMem,(void *)pEngine);
+       pPager->pEngine = 0;
+}
+/* Forward declaration */
+static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo);
+/*
+ * Allocate, initialize and register a new KV storage engine
+ * within this database instance.
+ */
+UNQLITE_PRIVATE int unqlitePagerRegisterKvEngine(Pager *pPager,unqlite_kv_methods *pMethods)
+{
+       unqlite_db *pStorage = &pPager->pDb->sDB;
+       unqlite *pDb = pPager->pDb;
+       unqlite_kv_engine *pEngine;
+       unqlite_kv_io *pIo;
+       sxu32 nByte;
+       int rc;
+       if( pPager->pEngine ){
+               if( pMethods == pPager->pEngine->pIo->pMethods ){
+                       /* Ticket 1432: Same implementation */
+                       return UNQLITE_OK;
+               }
+               /* Release the old KV engine */
+               pager_release_kv_engine(pPager);
+       }
+       /* Allocate a new KV engine instance */
+       nByte = (sxu32)pMethods->szKv;
+       pEngine = (unqlite_kv_engine *)SyMemBackendAlloc(&pDb->sMem,nByte);
+       if( pEngine == 0 ){
+               unqliteGenOutofMem(pDb);
+               return UNQLITE_NOMEM;
+       }
+       pIo = (unqlite_kv_io *)SyMemBackendAlloc(&pDb->sMem,sizeof(unqlite_kv_io));
+       if( pIo == 0 ){
+               SyMemBackendFree(&pDb->sMem,pEngine);
+               unqliteGenOutofMem(pDb);
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pIo,sizeof(unqlite_io_methods));
+       SyZero(pEngine,nByte);
+       /* Populate the IO structure */
+       pager_kv_io_init(pPager,pMethods,pIo);
+       pEngine->pIo = pIo;
+       /* Invoke the init callback if avaialble */
+       if( pMethods->xInit ){
+               rc = pMethods->xInit(pEngine,unqliteGetPageSize());
+               if( rc != UNQLITE_OK ){
+                       unqliteGenErrorFormat(pDb,
+                               "xInit() method of the underlying KV engine '%z' failed",&pPager->sKv);
+                       goto fail;
+               }
+               pEngine->pIo = pIo;
+       }
+       pPager->pEngine = pEngine;
+       /* Allocate a new cursor */
+       rc = unqliteInitCursor(pDb,&pStorage->pCursor);
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       return UNQLITE_OK;
+fail:
+       SyMemBackendFree(&pDb->sMem,pEngine);
+       SyMemBackendFree(&pDb->sMem,pIo);
+       return rc;
+}
+/*
+ * Return the underlying KV storage engine instance.
+ */
+UNQLITE_PRIVATE unqlite_kv_engine * unqlitePagerGetKvEngine(unqlite *pDb)
+{
+       return pDb->sDB.pPager->pEngine;
+}
+/*
+* Allocate and initialize a new Pager object. The pager should
+* eventually be freed by passing it to unqlitePagerClose().
+*
+* The zFilename argument is the path to the database file to open.
+* If zFilename is NULL or ":memory:" then all information is held
+* in cache. It is never written to disk.  This can be used to implement
+* an in-memory database.
+*/
+UNQLITE_PRIVATE int unqlitePagerOpen(
+  unqlite_vfs *pVfs,       /* The virtual file system to use */
+  unqlite *pDb,            /* Database handle */
+  const char *zFilename,   /* Name of the database file to open */
+  unsigned int iFlags      /* flags controlling this file */
+  )
+{
+       unqlite_kv_methods *pMethods = 0;
+       int is_mem,rd_only,no_jrnl;
+       Pager *pPager;
+       sxu32 nByte;
+       sxu32 nLen;
+       int rc;
+
+       /* Select the appropriate KV storage subsytem  */
+       if( (iFlags & UNQLITE_OPEN_IN_MEMORY) || unqliteInMemory(zFilename) ){
+               /* An in-memory database, record that  */
+               pMethods = unqliteFindKVStore("mem",sizeof("mem") - 1); /* Always available */
+               iFlags |= UNQLITE_OPEN_IN_MEMORY;
+       }else{
+               /* Install the default key value storage subsystem [i.e. Linear Hash] */
+               pMethods = unqliteFindKVStore("hash",sizeof("hash")-1);
+               if( pMethods == 0 ){
+                       /* Use the b+tree storage backend if the linear hash storage is not available */
+                       pMethods = unqliteFindKVStore("btree",sizeof("btree")-1);
+               }
+       }
+       if( pMethods == 0 ){
+               /* Can't happen */
+               unqliteGenError(pDb,"Cannot install a default Key/Value storage engine");
+               return UNQLITE_NOTIMPLEMENTED;
+       }
+       is_mem = (iFlags & UNQLITE_OPEN_IN_MEMORY) != 0;
+       rd_only = (iFlags & UNQLITE_OPEN_READONLY) != 0;
+       no_jrnl = (iFlags & UNQLITE_OPEN_OMIT_JOURNALING) != 0;
+       rc = UNQLITE_OK;
+       if( is_mem ){
+               /* Omit journaling for in-memory database */
+               no_jrnl = 1;
+       }
+       /* Total number of bytes to allocate */
+       nByte = sizeof(Pager);
+       nLen = 0;
+       if( !is_mem ){
+               nLen = SyStrlen(zFilename);
+               nByte += pVfs->mxPathname + nLen + sizeof(char) /* null termniator */;
+       }
+       /* Allocate */
+       pPager = (Pager *)SyMemBackendAlloc(&pDb->sMem,nByte);
+       if( pPager == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pPager,nByte);
+       /* Fill-in the structure */
+       pPager->pAllocator = &pDb->sMem;
+       pPager->pDb = pDb;
+       pDb->sDB.pPager = pPager;
+       /* Allocate page table */
+       pPager->nSize = 128; /* Must be a power of two */
+       nByte = pPager->nSize * sizeof(Page *);
+       pPager->apHash = (Page **)SyMemBackendAlloc(pPager->pAllocator,nByte);
+       if( pPager->apHash == 0 ){
+               rc = UNQLITE_NOMEM;
+               goto fail;
+       }
+       SyZero(pPager->apHash,nByte);
+       pPager->is_mem = is_mem;
+       pPager->no_jrnl = no_jrnl;
+       pPager->is_rdonly = rd_only;
+       pPager->iOpenFlags = iFlags;
+       pPager->pVfs = pVfs;
+       SyRandomnessInit(&pPager->sPrng,0,0);
+       SyRandomness(&pPager->sPrng,(void *)&pPager->cksumInit,sizeof(sxu32));
+       /* Unlimited cache size */
+       pPager->nCacheMax = SXU32_HIGH;
+       /* Copy filename and journal name */
+       if( !is_mem ){
+               pPager->zFilename = (char *)&pPager[1];
+               rc = UNQLITE_OK;
+               if( pVfs->xFullPathname ){
+                       rc = pVfs->xFullPathname(pVfs,zFilename,pVfs->mxPathname + nLen,pPager->zFilename);
+               }
+               if( rc != UNQLITE_OK ){
+                       /* Simple filename copy */
+                       SyMemcpy(zFilename,pPager->zFilename,nLen);
+                       pPager->zFilename[nLen] = 0;
+                       rc = UNQLITE_OK;
+               }else{
+                       nLen = SyStrlen(pPager->zFilename);
+               }
+               pPager->zJournal = (char *) SyMemBackendAlloc(pPager->pAllocator,nLen + sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) + sizeof(char));
+               if( pPager->zJournal == 0 ){
+                       rc = UNQLITE_NOMEM;
+                       goto fail;
+               }
+               /* Copy filename */
+               SyMemcpy(pPager->zFilename,pPager->zJournal,nLen);
+               /* Copy journal suffix */
+               SyMemcpy(UNQLITE_JOURNAL_FILE_SUFFIX,&pPager->zJournal[nLen],sizeof(UNQLITE_JOURNAL_FILE_SUFFIX)-1);
+               /* Append the nul terminator to the journal path */
+               pPager->zJournal[nLen + ( sizeof(UNQLITE_JOURNAL_FILE_SUFFIX) - 1)] = 0;
+       }
+       /* Finally, register the selected KV engine */
+       rc = unqlitePagerRegisterKvEngine(pPager,pMethods);
+       if( rc != UNQLITE_OK ){
+               goto fail;
+       }
+       /* Set the pager state */
+       if( pPager->is_mem ){
+               pPager->iState = PAGER_WRITER_FINISHED;
+               pPager->iLock = EXCLUSIVE_LOCK;
+       }else{
+               pPager->iState = PAGER_OPEN;
+               pPager->iLock = NO_LOCK;
+       }
+       /* All done, ready for processing */
+       return UNQLITE_OK;
+fail:
+       SyMemBackendFree(&pDb->sMem,pPager);
+       return rc;
+}
+/*
+ * Set a cache limit. Note that, this is a simple hint, the pager is not
+ * forced to honor this limit.
+ */
+UNQLITE_PRIVATE int unqlitePagerSetCachesize(Pager *pPager,int mxPage)
+{
+       if( mxPage < 256 ){
+               return UNQLITE_INVALID;
+       }
+       pPager->nCacheMax = mxPage;
+       return UNQLITE_OK;
+}
+/*
+ * Shutdown the page cache. Free all memory and close the database file.
+ */
+UNQLITE_PRIVATE int unqlitePagerClose(Pager *pPager)
+{
+       /* Release the KV engine */
+       pager_release_kv_engine(pPager);
+       if( pPager->iOpenFlags & UNQLITE_OPEN_MMAP ){
+               const jx9_vfs *pVfs = jx9ExportBuiltinVfs();
+               if( pVfs && pVfs->xUnmap && pPager->pMmap ){
+                       pVfs->xUnmap(pPager->pMmap,pPager->dbByteSize);
+               }
+       }
+       if( !pPager->is_mem && pPager->iState > PAGER_OPEN ){
+               /* Release all lock on this database handle */
+               pager_unlock_db(pPager,NO_LOCK);
+               /* Close the file  */
+               unqliteOsCloseFree(pPager->pAllocator,pPager->pfd);
+       }
+       if( pPager->pVec ){
+               unqliteBitvecDestroy(pPager->pVec);
+               pPager->pVec = 0;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Generate a random string.
+ */
+UNQLITE_PRIVATE void unqlitePagerRandomString(Pager *pPager,char *zBuf,sxu32 nLen)
+{
+       static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
+       sxu32 i;
+       /* Generate a binary string first */
+       SyRandomness(&pPager->sPrng,zBuf,nLen);
+       /* Turn the binary string into english based alphabet */
+       for( i = 0 ; i < nLen ; ++i ){
+                zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
+        }
+}
+/*
+ * Generate a random number.
+ */
+UNQLITE_PRIVATE sxu32 unqlitePagerRandomNum(Pager *pPager)
+{
+       sxu32 iNum;
+       SyRandomness(&pPager->sPrng,(void *)&iNum,sizeof(iNum));
+       return iNum;
+}
+/* Exported KV IO Methods */
+/* 
+ * Refer to [unqlitePagerAcquire()]
+ */
+static int unqliteKvIoPageGet(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage)
+{
+       int rc;
+       rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,0,0);
+       return rc;
+}
+/* 
+ * Refer to [unqlitePagerAcquire()]
+ */
+static int unqliteKvIoPageLookup(unqlite_kv_handle pHandle,pgno iNum,unqlite_page **ppPage)
+{
+       int rc;
+       rc = unqlitePagerAcquire((Pager *)pHandle,iNum,ppPage,1,0);
+       return rc;
+}
+/* 
+ * Refer to [unqlitePagerAcquire()]
+ */
+static int unqliteKvIoNewPage(unqlite_kv_handle pHandle,unqlite_page **ppPage)
+{
+       Pager *pPager = (Pager *)pHandle;
+       int rc;
+       /* 
+        * Acquire a reader-lock first so that pPager->dbSize get initialized.
+        */
+       rc = pager_shared_lock(pPager);
+       if( rc == UNQLITE_OK ){
+               rc = unqlitePagerAcquire(pPager,pPager->dbSize == 0 ? /* Page 0 is reserved */ 1 : pPager->dbSize ,ppPage,0,0);
+       }
+       return rc;
+}
+/* 
+ * Refer to [unqlitePageWrite()]
+ */
+static int unqliteKvIopageWrite(unqlite_page *pPage)
+{
+       int rc;
+       if( pPage == 0 ){
+               /* TICKET 1433-0348 */
+               return UNQLITE_OK;
+       }
+       rc = unqlitePageWrite(pPage);
+       return rc;
+}
+/* 
+ * Refer to [unqlitePagerDontWrite()]
+ */
+static int unqliteKvIoPageDontWrite(unqlite_page *pPage)
+{
+       int rc;
+       if( pPage == 0 ){
+               /* TICKET 1433-0348 */
+               return UNQLITE_OK;
+       }
+       rc = unqlitePagerDontWrite(pPage);
+       return rc;
+}
+/* 
+ * Refer to [unqliteBitvecSet()]
+ */
+static int unqliteKvIoPageDontJournal(unqlite_page *pRaw)
+{
+       Page *pPage = (Page *)pRaw;
+       Pager *pPager;
+       if( pPage == 0 ){
+               /* TICKET 1433-0348 */
+               return UNQLITE_OK;
+       }
+       pPager = pPage->pPager;
+       if( pPager->iState >= PAGER_WRITER_LOCKED ){
+               if( !pPager->no_jrnl && pPager->pVec && !unqliteBitvecTest(pPager->pVec,pPage->pgno) ){
+                       unqliteBitvecSet(pPager->pVec,pPage->pgno);
+               }
+       }
+       return UNQLITE_OK;
+}
+/* 
+ * Do not add a page to the hot dirty list.
+ */
+static int unqliteKvIoPageDontMakeHot(unqlite_page *pRaw)
+{
+       Page *pPage = (Page *)pRaw;
+       
+       if( pPage == 0 ){
+               /* TICKET 1433-0348 */
+               return UNQLITE_OK;
+       }
+       pPage->flags |= PAGE_DONT_MAKE_HOT;
+
+       /* Remove from hot dirty list if it is already there */
+       if( pPage->flags & PAGE_HOT_DIRTY ){
+               Pager *pPager = pPage->pPager;
+               if( pPage->pNextHot ){
+                       pPage->pNextHot->pPrevHot = pPage->pPrevHot;
+               }
+               if( pPage->pPrevHot ){
+                       pPage->pPrevHot->pNextHot = pPage->pNextHot;
+               }
+               if( pPager->pFirstHot == pPage ){
+                       pPager->pFirstHot = pPage->pPrevHot;
+               }
+               if( pPager->pHotDirty == pPage ){
+                       pPager->pHotDirty = pPage->pNextHot;
+               }
+               pPager->nHot--;
+               pPage->flags &= ~PAGE_HOT_DIRTY;
+       }
+
+       return UNQLITE_OK;
+}
+/* 
+ * Refer to [page_ref()]
+ */
+static int unqliteKvIopage_ref(unqlite_page *pPage)
+{
+       if( pPage ){
+               page_ref((Page *)pPage);
+       }
+       return UNQLITE_OK;
+}
+/* 
+ * Refer to [page_unref()]
+ */
+static int unqliteKvIoPageUnRef(unqlite_page *pPage)
+{
+       if( pPage ){
+               page_unref((Page *)pPage);
+       }
+       return UNQLITE_OK;
+}
+/* 
+ * Refer to the declaration of the [Pager] structure
+ */
+static int unqliteKvIoReadOnly(unqlite_kv_handle pHandle)
+{
+       return ((Pager *)pHandle)->is_rdonly;
+}
+/* 
+ * Refer to the declaration of the [Pager] structure
+ */
+static int unqliteKvIoPageSize(unqlite_kv_handle pHandle)
+{
+       return ((Pager *)pHandle)->iPageSize;
+}
+/* 
+ * Refer to the declaration of the [Pager] structure
+ */
+static unsigned char * unqliteKvIoTempPage(unqlite_kv_handle pHandle)
+{
+       return ((Pager *)pHandle)->zTmpPage;
+}
+/* 
+ * Set a page unpin callback.
+ * Refer to the declaration of the [Pager] structure
+ */
+static void unqliteKvIoPageUnpin(unqlite_kv_handle pHandle,void (*xPageUnpin)(void *))
+{
+       Pager *pPager = (Pager *)pHandle;
+       pPager->xPageUnpin = xPageUnpin;
+}
+/* 
+ * Set a page reload callback.
+ * Refer to the declaration of the [Pager] structure
+ */
+static void unqliteKvIoPageReload(unqlite_kv_handle pHandle,void (*xPageReload)(void *))
+{
+       Pager *pPager = (Pager *)pHandle;
+       pPager->xPageReload = xPageReload;
+}
+/* 
+ * Log an error.
+ * Refer to the declaration of the [Pager] structure
+ */
+static void unqliteKvIoErr(unqlite_kv_handle pHandle,const char *zErr)
+{
+       Pager *pPager = (Pager *)pHandle;
+       unqliteGenError(pPager->pDb,zErr);
+}
+/*
+ * Init an instance of the [unqlite_kv_io] structure.
+ */
+static int pager_kv_io_init(Pager *pPager,unqlite_kv_methods *pMethods,unqlite_kv_io *pIo)
+{
+       pIo->pHandle =  pPager;
+       pIo->pMethods = pMethods;
+       
+       pIo->xGet    = unqliteKvIoPageGet;
+       pIo->xLookup = unqliteKvIoPageLookup;
+       pIo->xNew    = unqliteKvIoNewPage;
+       
+       pIo->xWrite     = unqliteKvIopageWrite; 
+       pIo->xDontWrite = unqliteKvIoPageDontWrite;
+       pIo->xDontJournal = unqliteKvIoPageDontJournal;
+       pIo->xDontMkHot = unqliteKvIoPageDontMakeHot;
+
+       pIo->xPageRef   = unqliteKvIopage_ref;
+       pIo->xPageUnref = unqliteKvIoPageUnRef;
+
+       pIo->xPageSize = unqliteKvIoPageSize;
+       pIo->xReadOnly = unqliteKvIoReadOnly;
+
+       pIo->xTmpPage =  unqliteKvIoTempPage;
+
+       pIo->xSetUnpin = unqliteKvIoPageUnpin;
+       pIo->xSetReload = unqliteKvIoPageReload;
+
+       pIo->xErr = unqliteKvIoErr;
+
+       return UNQLITE_OK;
+}
+/*
+ * ----------------------------------------------------------
+ * File: unqlite_vm.c
+ * MD5: 2a0c56efb2ab87d3e52d0d7c3147c53b
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: unqlite_vm.c v1.0 Win7 2013-01-29 23:37 stable <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* This file deals with low level stuff related to the unQLite Virtual Machine */
+
+/* Record ID as a hash value */
+#define COL_RECORD_HASH(RID) (RID)
+/*
+ * Fetch a record from a given collection.
+ */
+static unqlite_col_record * CollectionCacheFetchRecord(
+       unqlite_col *pCol, /* Target collection */
+       jx9_int64 nId      /* Unique record ID */
+       )
+{
+       unqlite_col_record *pEntry;
+       if( pCol->nRec < 1 ){
+               /* Don't bother hashing */
+               return 0;
+       }
+       pEntry = pCol->apRecord[COL_RECORD_HASH(nId) & (pCol->nRecSize - 1)];
+       for(;;){
+               if( pEntry == 0 ){
+                       break;
+               }
+               if( pEntry->nId == nId ){
+                       /* Record found */
+                       return pEntry;
+               }
+               /* Point to the next entry */
+               pEntry = pEntry->pNextCol;
+
+       }
+       /* No such record */
+       return 0;
+}
+/*
+ * Install a freshly created record in a given collection. 
+ */
+static int CollectionCacheInstallRecord(
+       unqlite_col *pCol, /* Target collection */
+       jx9_int64 nId,     /* Unique record ID */
+       jx9_value *pValue  /* JSON value */
+       )
+{
+       unqlite_col_record *pRecord;
+       sxu32 iBucket;
+       /* Fetch the record first */
+       pRecord = CollectionCacheFetchRecord(pCol,nId);
+       if( pRecord ){
+               /* Record already installed, overwrite its old value  */
+               jx9MemObjStore(pValue,&pRecord->sValue);
+               return UNQLITE_OK;
+       }
+       /* Allocate a new instance */
+       pRecord = (unqlite_col_record *)SyMemBackendPoolAlloc(&pCol->pVm->sAlloc,sizeof(unqlite_col_record));
+       if( pRecord == 0 ){
+               return UNQLITE_NOMEM;
+       }
+       /* Zero the structure */
+       SyZero(pRecord,sizeof(unqlite_col_record));
+       /* Fill in the structure */
+       jx9MemObjInit(pCol->pVm->pJx9Vm,&pRecord->sValue);
+       jx9MemObjStore(pValue,&pRecord->sValue);
+       pRecord->nId = nId;
+       pRecord->pCol = pCol;
+       /* Install in the corresponding bucket */
+       iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1);
+       pRecord->pNextCol = pCol->apRecord[iBucket];
+       if( pCol->apRecord[iBucket] ){
+               pCol->apRecord[iBucket]->pPrevCol = pRecord;
+       }
+       pCol->apRecord[iBucket] = pRecord;
+       /* Link */
+       MACRO_LD_PUSH(pCol->pList,pRecord);
+       pCol->nRec++;
+       if( (pCol->nRec >= pCol->nRecSize * 3) && pCol->nRec < 100000 ){
+               /* Allocate a new larger table */
+               sxu32 nNewSize = pCol->nRecSize << 1;
+               unqlite_col_record *pEntry;
+               unqlite_col_record **apNew;
+               sxu32 n;
+               
+               apNew = (unqlite_col_record **)SyMemBackendAlloc(&pCol->pVm->sAlloc, nNewSize * sizeof(unqlite_col_record *));
+               if( apNew ){
+                       /* Zero the new table */
+                       SyZero((void *)apNew, nNewSize * sizeof(unqlite_col_record *));
+                       /* Rehash all entries */
+                       n = 0;
+                       pEntry = pCol->pList;
+                       for(;;){
+                               /* Loop one */
+                               if( n >= pCol->nRec ){
+                                       break;
+                               }
+                               pEntry->pNextCol = pEntry->pPrevCol = 0;
+                               /* Install in the new bucket */
+                               iBucket = COL_RECORD_HASH(pEntry->nId) & (nNewSize - 1);
+                               pEntry->pNextCol = apNew[iBucket];
+                               if( apNew[iBucket]  ){
+                                       apNew[iBucket]->pPrevCol = pEntry;
+                               }
+                               apNew[iBucket] = pEntry;
+                               /* Point to the next entry */
+                               pEntry = pEntry->pNext;
+                               n++;
+                       }
+                       /* Release the old table and reflect the change */
+                       SyMemBackendFree(&pCol->pVm->sAlloc,(void *)pCol->apRecord);
+                       pCol->apRecord = apNew;
+                       pCol->nRecSize = nNewSize;
+               }
+       }
+       /* All done */
+       return UNQLITE_OK;
+}
+/*
+ * Remove a record from the collection table.
+ */
+UNQLITE_PRIVATE int unqliteCollectionCacheRemoveRecord(
+       unqlite_col *pCol, /* Target collection */
+       jx9_int64 nId      /* Unique record ID */
+       )
+{
+       unqlite_col_record *pRecord;
+       /* Fetch the record first */
+       pRecord = CollectionCacheFetchRecord(pCol,nId);
+       if( pRecord == 0 ){
+               /* No such record */
+               return UNQLITE_NOTFOUND;
+       }
+       if( pRecord->pPrevCol ){
+               pRecord->pPrevCol->pNextCol = pRecord->pNextCol;
+       }else{
+               sxu32 iBucket = COL_RECORD_HASH(nId) & (pCol->nRecSize - 1);
+               pCol->apRecord[iBucket] = pRecord->pNextCol;
+       }
+       if( pRecord->pNextCol ){
+               pRecord->pNextCol->pPrevCol = pRecord->pPrevCol;
+       }
+       /* Unlink */
+       MACRO_LD_REMOVE(pCol->pList,pRecord);
+       pCol->nRec--;
+       return UNQLITE_OK;
+}
+/*
+ * Discard a collection and its records.
+ */
+static int CollectionCacheRelease(unqlite_col *pCol)
+{
+       unqlite_col_record *pNext,*pRec = pCol->pList;
+       unqlite_vm *pVm = pCol->pVm;
+       sxu32 n;
+       /* Discard all records */
+       for( n = 0 ; n < pCol->nRec ; ++n ){
+               pNext = pRec->pNext;
+               jx9MemObjRelease(&pRec->sValue);
+               SyMemBackendPoolFree(&pVm->sAlloc,(void *)pRec);
+               /* Point to the next record */
+               pRec = pNext;
+       }
+       SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord);
+       pCol->nRec = pCol->nRecSize = 0;
+       pCol->pList = 0;
+       return UNQLITE_OK;
+}
+/*
+ * Install a freshly created collection in the unqlite VM.
+ */
+static int unqliteVmInstallCollection(
+       unqlite_vm *pVm,  /* Target VM */
+       unqlite_col *pCol /* Collection to install */
+       )
+{
+       SyString *pName = &pCol->sName;
+       sxu32 iBucket;
+       /* Hash the collection name */
+       pCol->nHash = SyBinHash((const void *)pName->zString,pName->nByte);
+       /* Install it in the corresponding bucket */
+       iBucket = pCol->nHash & (pVm->iColSize - 1);
+       pCol->pNextCol = pVm->apCol[iBucket];
+       if( pVm->apCol[iBucket] ){
+               pVm->apCol[iBucket]->pPrevCol = pCol;
+       }
+       pVm->apCol[iBucket] = pCol;
+       /* Link to the list of active collections */
+       MACRO_LD_PUSH(pVm->pCol,pCol);
+       pVm->iCol++;
+       if( (pVm->iCol >= pVm->iColSize * 4) && pVm->iCol < 10000 ){
+               /* Grow the hashtable */
+               sxu32 nNewSize = pVm->iColSize << 1;
+               unqlite_col *pEntry;
+               unqlite_col **apNew;
+               sxu32 n;
+               
+               apNew = (unqlite_col **)SyMemBackendAlloc(&pVm->sAlloc, nNewSize * sizeof(unqlite_col *));
+               if( apNew ){
+                       /* Zero the new table */
+                       SyZero((void *)apNew, nNewSize * sizeof(unqlite_col *));
+                       /* Rehash all entries */
+                       n = 0;
+                       pEntry = pVm->pCol;
+                       for(;;){
+                               /* Loop one */
+                               if( n >= pVm->iCol ){
+                                       break;
+                               }
+                               pEntry->pNextCol = pEntry->pPrevCol = 0;
+                               /* Install in the new bucket */
+                               iBucket = pEntry->nHash & (nNewSize - 1);
+                               pEntry->pNextCol = apNew[iBucket];
+                               if( apNew[iBucket]  ){
+                                       apNew[iBucket]->pPrevCol = pEntry;
+                               }
+                               apNew[iBucket] = pEntry;
+                               /* Point to the next entry */
+                               pEntry = pEntry->pNext;
+                               n++;
+                       }
+                       /* Release the old table and reflect the change */
+                       SyMemBackendFree(&pVm->sAlloc,(void *)pVm->apCol);
+                       pVm->apCol = apNew;
+                       pVm->iColSize  = nNewSize;
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Fetch a collection from the target VM.
+ */
+static unqlite_col * unqliteVmFetchCollection(
+       unqlite_vm *pVm, /* Target VM */
+       SyString *pName  /* Lookup name */
+       )
+{
+       unqlite_col *pCol;
+       sxu32 nHash;
+       if( pVm->iCol < 1 ){
+               /* Don't bother hashing */
+               return 0;
+       }
+       nHash = SyBinHash((const void *)pName->zString,pName->nByte);
+       /* Perform the lookup */
+       pCol = pVm->apCol[nHash & ( pVm->iColSize - 1)];
+       for(;;){
+               if( pCol == 0 ){
+                       break;
+               }
+               if( nHash == pCol->nHash && SyStringCmp(pName,&pCol->sName,SyMemcmp) == 0 ){
+                       /* Collection found */
+                       return pCol;
+               }
+               /* Point to the next entry */
+               pCol = pCol->pNextCol;
+       }
+       /* No such collection */
+       return 0;
+}
+/*
+ * Write and/or alter collection binary header.
+ */
+static int CollectionSetHeader(
+       unqlite_kv_engine *pEngine, /* Underlying KV storage engine */
+       unqlite_col *pCol,          /* Target collection */
+       jx9_int64 iRec,             /* Last record ID */
+       jx9_int64 iTotal,           /* Total number of records in this collection */
+       jx9_value *pSchema          /* Collection schema */
+       )
+{
+       SyBlob *pHeader = &pCol->sHeader;
+       unqlite_kv_methods *pMethods;
+       int iWrite = 0;
+       int rc;
+       if( pEngine == 0 ){
+               /* Default storage engine */
+               pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
+       }
+       pMethods = pEngine->pIo->pMethods;
+       if( SyBlobLength(pHeader) < 1 ){
+               Sytm *pCreate = &pCol->sCreation; /* Creation time */
+               unqlite_vfs *pVfs;
+               sxu32 iDos;
+               /* Magic number */
+               rc = SyBlobAppendBig16(pHeader,UNQLITE_COLLECTION_MAGIC);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Initial record ID */
+               rc = SyBlobAppendBig64(pHeader,0);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Total records in the collection */
+               rc = SyBlobAppendBig64(pHeader,0);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               pVfs = (unqlite_vfs *)unqliteExportBuiltinVfs();
+               /* Creation time of the collection */
+               if( pVfs->xCurrentTime ){
+                       /* Get the creation time */
+                       pVfs->xCurrentTime(pVfs,pCreate);
+               }else{
+                       /* Zero the structure */
+                       SyZero(pCreate,sizeof(Sytm));
+               }
+               /* Convert to DOS time */
+               SyTimeFormatToDos(pCreate,&iDos);
+               rc = SyBlobAppendBig32(pHeader,iDos);
+               if( rc != UNQLITE_OK ){
+                       return rc;
+               }
+               /* Offset to start writing collection schema */
+               pCol->nSchemaOfft = SyBlobLength(pHeader);
+               iWrite = 1;
+       }else{
+               unsigned char *zBinary = (unsigned char *)SyBlobData(pHeader);
+               /* Header update */
+               if( iRec >= 0 ){
+                       /* Update record ID */
+                       SyBigEndianPack64(&zBinary[2/* Magic number*/],(sxu64)iRec);
+                       iWrite = 1;
+               }
+               if( iTotal >= 0 ){
+                       /* Total records */
+                       SyBigEndianPack64(&zBinary[2/* Magic number*/+8/* Record ID*/],(sxu64)iTotal);
+                       iWrite = 1;
+               }
+               if( pSchema ){
+                       /* Collection Schema */
+                       SyBlobTruncate(pHeader,pCol->nSchemaOfft);
+                       /* Encode the schema to FastJson */
+                       rc = FastJsonEncode(pSchema,pHeader,0);
+                       if( rc != UNQLITE_OK ){
+                               return rc;
+                       }
+                       /* Copy the collection schema */
+                       jx9MemObjStore(pSchema,&pCol->sSchema);
+                       iWrite = 1;
+               }
+       }
+       if( iWrite ){
+               SyString *pId = &pCol->sName;
+               /* Reflect the disk and/or in-memory image */
+               rc = pMethods->xReplace(pEngine,
+                       (const void *)pId->zString,pId->nByte,
+                       SyBlobData(pHeader),SyBlobLength(pHeader)
+                       );
+               if( rc != UNQLITE_OK ){
+                       unqliteGenErrorFormat(pCol->pVm->pDb,
+                               "Cannot save collection '%z' header in the underlying storage engine",
+                               pId
+                               );
+                       return rc;
+               }
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Load a binary collection from disk.
+ */
+static int CollectionLoadHeader(unqlite_col *pCol)
+{
+       SyBlob *pHeader = &pCol->sHeader;
+       unsigned char *zRaw,*zEnd;
+       sxu16 nMagic;
+       sxu32 iDos;
+       int rc;
+       SyBlobReset(pHeader);
+       /* Read the binary header */
+       rc = unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pHeader);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Perform a sanity check */
+       if( SyBlobLength(pHeader) < (2 /* magic */ + 8 /* record_id */ + 8 /* total_records */+ 4 /* DOS creation time*/) ){
+               return UNQLITE_CORRUPT;
+       }
+       zRaw = (unsigned char *)SyBlobData(pHeader);
+       zEnd = &zRaw[SyBlobLength(pHeader)];
+       /* Extract the magic number */
+       SyBigEndianUnpack16(zRaw,&nMagic);
+       if( nMagic != UNQLITE_COLLECTION_MAGIC ){
+               return UNQLITE_CORRUPT;
+       }
+       zRaw += 2; /* sizeof(sxu16) */
+       /* Extract the record ID */
+       SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nLastid);
+       zRaw += 8; /* sizeof(sxu64) */
+       /* Total records in the collection */
+       SyBigEndianUnpack64(zRaw,(sxu64 *)&pCol->nTotRec);
+       /* Extract the collection creation date (DOS) */
+       zRaw += 8; /* sizeof(sxu64) */
+       SyBigEndianUnpack32(zRaw,&iDos);
+       SyDosTimeFormat(iDos,&pCol->sCreation);
+       zRaw += 4;
+       /* Check for a collection schema */
+       pCol->nSchemaOfft = (sxu32)(zRaw - (unsigned char *)SyBlobData(pHeader));
+       if( zRaw < zEnd ){
+               /* Decode the FastJson value */
+               FastJsonDecode((const void *)zRaw,(sxu32)(zEnd-zRaw),&pCol->sSchema,0,0);
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Load or create a binary collection.
+ */
+static int unqliteVmLoadCollection(
+       unqlite_vm *pVm,    /* Target VM */
+       const char *zName,  /* Collection name */
+       sxu32 nByte,        /* zName length */
+       int iFlag,          /* Control flag */
+       unqlite_col **ppOut /* OUT: in-memory collection */
+       )
+{
+       unqlite_kv_methods *pMethods;
+       unqlite_kv_engine *pEngine;
+       unqlite_kv_cursor *pCursor;
+       unqlite *pDb = pVm->pDb;
+       unqlite_col *pCol = 0; /* cc warning */
+       int rc = SXERR_MEM;
+       char *zDup = 0;
+       /* Point to the underlying KV store */
+       pEngine = unqlitePagerGetKvEngine(pVm->pDb);
+       pMethods = pEngine->pIo->pMethods;
+       /* Allocate a new cursor */
+       rc = unqliteInitCursor(pDb,&pCursor);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       if( (iFlag & UNQLITE_VM_COLLECTION_CREATE) == 0 ){
+               /* Seek to the desired location */
+               rc = pMethods->xSeek(pCursor,(const void *)zName,(int)nByte,UNQLITE_CURSOR_MATCH_EXACT);
+               if( rc != UNQLITE_OK && (iFlag & UNQLITE_VM_COLLECTION_EXISTS) == 0){
+                       unqliteGenErrorFormat(pDb,"Collection '%.*s' not defined in the underlying database",nByte,zName);
+
+                       unqliteReleaseCursor(pDb,pCursor);
+                       return rc;
+               }
+               else if((iFlag & UNQLITE_VM_COLLECTION_EXISTS)){
+                       unqliteReleaseCursor(pDb,pCursor);
+                       return rc;
+               }
+       }
+       /* Allocate a new instance */
+       pCol = (unqlite_col *)SyMemBackendPoolAlloc(&pVm->sAlloc,sizeof(unqlite_col));
+       if( pCol == 0 ){
+               unqliteGenOutofMem(pDb);
+               rc = UNQLITE_NOMEM;
+               goto fail;
+       }
+       SyZero(pCol,sizeof(unqlite_col));
+       /* Fill in the structure */
+       SyBlobInit(&pCol->sWorker,&pVm->sAlloc);
+       SyBlobInit(&pCol->sHeader,&pVm->sAlloc);
+       pCol->pVm = pVm;
+       pCol->pCursor = pCursor;
+       /* Duplicate collection name */
+       zDup = SyMemBackendStrDup(&pVm->sAlloc,zName,nByte);
+       if( zDup == 0 ){
+               unqliteGenOutofMem(pDb);
+               rc = UNQLITE_NOMEM;
+               goto fail;
+       }
+       pCol->nRecSize = 64; /* Must be a power of two */
+       pCol->apRecord = (unqlite_col_record **)SyMemBackendAlloc(&pVm->sAlloc,pCol->nRecSize * sizeof(unqlite_col_record *));
+       if( pCol->apRecord == 0 ){
+               unqliteGenOutofMem(pDb);
+               rc = UNQLITE_NOMEM;
+               goto fail;
+       }
+       /* Zero the table */
+       SyZero((void *)pCol->apRecord,pCol->nRecSize * sizeof(unqlite_col_record *));
+       SyStringInitFromBuf(&pCol->sName,zDup,nByte);
+       jx9MemObjInit(pVm->pJx9Vm,&pCol->sSchema);
+       if( iFlag & UNQLITE_VM_COLLECTION_CREATE ){
+               /* Create a new collection */
+               if( pMethods->xReplace == 0 ){
+                       /* Read-only KV engine: Generate an error message and return */
+                       unqliteGenErrorFormat(pDb,
+                               "Cannot create new collection '%z' due to a read-only Key/Value storage engine",
+                               &pCol->sName
+                       );
+                       rc = UNQLITE_ABORT; /* Abort VM execution */
+                       goto fail;
+               }
+               /* Write the collection header */
+               rc = CollectionSetHeader(pEngine,pCol,0,0,0);
+               if( rc != UNQLITE_OK ){
+                       rc = UNQLITE_ABORT; /* Abort VM execution */
+                       goto fail;
+               }
+       }else{
+               /* Read the collection header */
+               rc = CollectionLoadHeader(pCol);
+               if( rc != UNQLITE_OK ){
+                       unqliteGenErrorFormat(pDb,"Corrupt collection '%z' header",&pCol->sName);
+                       goto fail;
+               }
+       }
+       /* Finally install the collection */
+       unqliteVmInstallCollection(pVm,pCol);
+       /* All done */
+       if( ppOut ){
+               *ppOut = pCol;
+       }
+       return UNQLITE_OK;
+fail:
+       unqliteReleaseCursor(pDb,pCursor);
+       if( zDup ){
+               SyMemBackendFree(&pVm->sAlloc,zDup);
+       }
+       if( pCol ){
+               if( pCol->apRecord ){
+                       SyMemBackendFree(&pVm->sAlloc,(void *)pCol->apRecord);
+               }
+               SyBlobRelease(&pCol->sHeader);
+               SyBlobRelease(&pCol->sWorker);
+               jx9MemObjRelease(&pCol->sSchema);
+               SyMemBackendPoolFree(&pVm->sAlloc,pCol);
+       }
+       return rc;
+}
+/*
+ * Fetch a collection.
+ */
+UNQLITE_PRIVATE unqlite_col * unqliteCollectionFetch(
+       unqlite_vm *pVm, /* Target VM */
+       SyString *pName, /* Lookup key */
+       int iFlag        /* Control flag */
+       )
+{
+       unqlite_col *pCol = 0; /* cc warning */
+       int rc;
+       /* Check if the collection is already loaded in memory */
+       pCol = unqliteVmFetchCollection(pVm,pName);
+       if( pCol ){
+               /* Already loaded in memory*/
+               return pCol;
+       }
+       if( (iFlag & UNQLITE_VM_AUTO_LOAD) == 0 ){
+               return 0;
+       }
+       /* Ask the storage engine for the collection */
+       rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,0,&pCol);
+       /* Return to the caller */
+       return rc == UNQLITE_OK ? pCol : 0;
+}
+/*
+ * Return the unique ID of the last inserted record.
+ */
+UNQLITE_PRIVATE jx9_int64 unqliteCollectionLastRecordId(unqlite_col *pCol)
+{
+       return pCol->nLastid == 0 ? 0 : (pCol->nLastid - 1);
+}
+/*
+ * Return the current record ID.
+ */
+UNQLITE_PRIVATE jx9_int64 unqliteCollectionCurrentRecordId(unqlite_col *pCol)
+{
+       return pCol->nCurid;
+}
+/*
+ * Return the total number of records in a given collection. 
+ */
+UNQLITE_PRIVATE jx9_int64 unqliteCollectionTotalRecords(unqlite_col *pCol)
+{
+       return pCol->nTotRec;
+}
+/*
+ * Reset the record cursor.
+ */
+UNQLITE_PRIVATE void unqliteCollectionResetRecordCursor(unqlite_col *pCol)
+{
+       pCol->nCurid = 0;
+}
+/*
+ * Fetch a record by its unique ID.
+ */
+UNQLITE_PRIVATE int unqliteCollectionFetchRecordById(
+       unqlite_col *pCol, /* Target collection */
+       jx9_int64 nId,     /* Unique record ID */
+       jx9_value *pValue  /* OUT: record value */
+       )
+{
+       SyBlob *pWorker = &pCol->sWorker;
+       unqlite_col_record *pRec;
+       int rc;
+       jx9_value_null(pValue);
+       /* Perform a cache lookup first */
+       pRec = CollectionCacheFetchRecord(pCol,nId);
+       if( pRec ){
+               /* Copy record value */
+               jx9MemObjStore(&pRec->sValue,pValue);
+               return UNQLITE_OK;
+       }
+       /* Reset the working buffer */
+       SyBlobReset(pWorker);
+       /* Generate the unique ID */
+       SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId);
+       /* Reset the cursor */
+       unqlite_kv_cursor_reset(pCol->pCursor);
+       /* Seek the cursor to the desired location */
+       rc = unqlite_kv_cursor_seek(pCol->pCursor,
+               SyBlobData(pWorker),SyBlobLength(pWorker),
+               UNQLITE_CURSOR_MATCH_EXACT
+               );
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Consume the binary JSON */
+       SyBlobReset(pWorker);
+       unqlite_kv_cursor_data_callback(pCol->pCursor,unqliteDataConsumer,pWorker);
+       if( SyBlobLength(pWorker) < 1 ){
+               unqliteGenErrorFormat(pCol->pVm->pDb,
+                       "Empty record '%qd'",nId
+                       );
+               jx9_value_null(pValue);
+       }else{
+               /* Decode the binary JSON */
+               rc = FastJsonDecode(SyBlobData(pWorker),SyBlobLength(pWorker),pValue,0,0);
+               if( rc == UNQLITE_OK ){
+                       /* Install the record in the cache */
+                       CollectionCacheInstallRecord(pCol,nId,pValue);
+               }
+       }
+       return rc;
+}
+/*
+ * Fetch the next record from a given collection.
+ */ 
+UNQLITE_PRIVATE int unqliteCollectionFetchNextRecord(unqlite_col *pCol,jx9_value *pValue)
+{
+       int rc;
+       for(;;){
+               if( pCol->nCurid >= pCol->nLastid ){
+                       /* No more records, reset the record cursor ID */
+                       pCol->nCurid = 0;
+                       /* Return to the caller */
+                       return SXERR_EOF;
+               }
+               rc = unqliteCollectionFetchRecordById(pCol,pCol->nCurid,pValue);
+               /* Increment the record ID */
+               pCol->nCurid++;
+               /* Lookup result */
+               if( rc == UNQLITE_OK || rc != UNQLITE_NOTFOUND ){
+                       break;
+               }
+       }
+       return rc;
+}
+/*
+ * Judge a collection whether exists
+ */
+UNQLITE_PRIVATE int unqliteExistsCollection(
+    unqlite_vm *pVm, /* Target VM */
+    SyString *pName  /* Collection name */
+    )
+{
+    unqlite_col *pCol;
+    int rc;
+    /* Perform a lookup first */
+    pCol = unqliteVmFetchCollection(pVm,pName);
+    if( pCol ){
+        /* Already loaded in memory*/
+        return UNQLITE_OK;
+    }
+    rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,UNQLITE_VM_COLLECTION_EXISTS,0);
+    return rc;
+}
+/*
+ * Create a new collection.
+ */
+UNQLITE_PRIVATE int unqliteCreateCollection(
+       unqlite_vm *pVm, /* Target VM */
+       SyString *pName  /* Collection name */
+       )
+{
+       unqlite_col *pCol;
+       int rc;
+       /* Perform a lookup first */
+       pCol = unqliteCollectionFetch(pVm,pName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               return UNQLITE_EXISTS;
+       }
+       /* Now, safely create the collection */
+       rc = unqliteVmLoadCollection(pVm,pName->zString,pName->nByte,UNQLITE_VM_COLLECTION_CREATE,0);
+       return rc;
+}
+/*
+ * Set a schema (JSON object) for a given collection.
+ */
+UNQLITE_PRIVATE int unqliteCollectionSetSchema(unqlite_col *pCol,jx9_value *pValue)
+{
+       int rc;
+       if( !jx9_value_is_json_object(pValue) ){
+               /* Must be a JSON object */
+               return SXERR_INVALID;
+       }
+       rc = CollectionSetHeader(0,pCol,-1,-1,pValue);
+       return rc;
+}
+/*
+ * Perform a store operation on a given collection.
+ */
+static int CollectionStore(
+       unqlite_col *pCol, /* Target collection */
+       jx9_value *pValue  /* JSON value to be stored */
+       )
+{
+       SyBlob *pWorker = &pCol->sWorker;
+       unqlite_kv_methods *pMethods;
+       unqlite_kv_engine *pEngine;
+       sxu32 nKeyLen;
+       int rc; 
+       /* Point to the underlying KV store */
+       pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
+       pMethods = pEngine->pIo->pMethods;
+       if( pCol->nTotRec >= SXI64_HIGH ){
+               /* Collection limit reached. No more records */
+               unqliteGenErrorFormat(pCol->pVm->pDb,
+                               "Collection '%z': Records limit reached",
+                               &pCol->sName
+                       );
+               return UNQLITE_LIMIT;
+       }
+       if( pMethods->xReplace == 0 ){
+               unqliteGenErrorFormat(pCol->pVm->pDb,
+                               "Cannot store record into collection '%z' due to a read-only Key/Value storage engine",
+                               &pCol->sName
+                       );
+               return UNQLITE_READ_ONLY;
+       }
+       /* Reset the working buffer */
+       SyBlobReset(pWorker);
+       if( jx9_value_is_json_object(pValue) ){
+               jx9_value sId;
+               /* If the given type is a JSON object, then add the special __id field */
+               jx9MemObjInitFromInt(pCol->pVm->pJx9Vm,&sId,pCol->nLastid);
+               jx9_array_add_strkey_elem(pValue,"__id",&sId);
+               jx9MemObjRelease(&sId);
+       }
+       /* Prepare the unique ID for this record */
+       SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,pCol->nLastid);
+       nKeyLen = SyBlobLength(pWorker);
+       if( nKeyLen < 1 ){
+               unqliteGenOutofMem(pCol->pVm->pDb);
+               return UNQLITE_NOMEM;
+       }
+       /* Turn to FastJson */
+       rc = FastJsonEncode(pValue,pWorker,0);
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Finally perform the insertion */
+       rc = pMethods->xReplace(
+               pEngine,
+               SyBlobData(pWorker),nKeyLen,
+               SyBlobDataAt(pWorker,nKeyLen),SyBlobLength(pWorker)-nKeyLen
+               );
+       if( rc == UNQLITE_OK ){
+               /* Save the value in the cache */
+               CollectionCacheInstallRecord(pCol,pCol->nLastid,pValue);
+               /* Increment the unique __id */
+               pCol->nLastid++;
+               pCol->nTotRec++;
+               /* Reflect the change */
+               rc = CollectionSetHeader(0,pCol,pCol->nLastid,pCol->nTotRec,0);
+       }
+       if( rc != UNQLITE_OK ){
+               unqliteGenErrorFormat(pCol->pVm->pDb,
+                               "IO error while storing record into collection '%z'",
+                               &pCol->sName
+                       );
+               return rc;
+       }
+       return UNQLITE_OK;
+}
+/*
+ * Perform a update operation on a given collection.
+ */
+static int CollectionUpdate(
+                            unqlite_col *pCol, /* Target collection */
+                            jx9_int64 nId,     /* Record ID */
+                            jx9_value *pValue  /* JSON value to be stored */
+)
+{
+    SyBlob *pWorker = &pCol->sWorker;
+    unqlite_kv_methods *pMethods;
+    unqlite_kv_engine *pEngine;
+    sxu32 nKeyLen;
+    int rc;
+    /* Point to the underlying KV store */
+    pEngine = unqlitePagerGetKvEngine(pCol->pVm->pDb);
+    pMethods = pEngine->pIo->pMethods;
+    if( pCol->nTotRec >= SXI64_HIGH ){
+        /* Collection limit reached. No more records */
+        unqliteGenErrorFormat(pCol->pVm->pDb,
+                              "Collection '%z': Records limit reached",
+                              &pCol->sName
+                              );
+        return UNQLITE_LIMIT;
+    }
+    if( pMethods->xReplace == 0 ){
+        unqliteGenErrorFormat(pCol->pVm->pDb,
+                              "Cannot store record into collection '%z' due to a read-only Key/Value storage engine",
+                              &pCol->sName
+                              );
+        return UNQLITE_READ_ONLY;
+    }
+    /* Reset the working buffer */
+    SyBlobReset(pWorker);
+    
+    /* Prepare the unique ID for this record */
+    SyBlobFormat(pWorker,"%z_%qd",&pCol->sName, nId);
+    
+    /* Reset the cursor */
+    unqlite_kv_cursor_reset(pCol->pCursor);
+    /* Seek the cursor to the desired location */
+    rc = unqlite_kv_cursor_seek(pCol->pCursor,
+                                SyBlobData(pWorker),SyBlobLength(pWorker),
+                                UNQLITE_CURSOR_MATCH_EXACT
+                                );
+    if( rc != UNQLITE_OK ){
+        unqliteGenErrorFormat(pCol->pVm->pDb,
+                              "No record to update in collection '%z'",
+                              &pCol->sName
+                              );
+        return rc;
+    }
+    
+    if( jx9_value_is_json_object(pValue) ){
+        jx9_value sId;
+        /* If the given type is a JSON object, then add the special __id field */
+        jx9MemObjInitFromInt(pCol->pVm->pJx9Vm,&sId,nId);
+        jx9_array_add_strkey_elem(pValue,"__id",&sId);
+        jx9MemObjRelease(&sId);
+    }
+    
+    nKeyLen = SyBlobLength(pWorker);
+    if( nKeyLen < 1 ){
+        unqliteGenOutofMem(pCol->pVm->pDb);
+        return UNQLITE_NOMEM;
+    }
+    /* Turn to FastJson */
+    rc = FastJsonEncode(pValue,pWorker,0);
+    if( rc != UNQLITE_OK ){
+        return rc;
+    }
+    /* Finally perform the insertion */
+    rc = pMethods->xReplace(
+                            pEngine,
+                            SyBlobData(pWorker),nKeyLen,
+                            SyBlobDataAt(pWorker,nKeyLen),SyBlobLength(pWorker)-nKeyLen
+                            );
+    if( rc == UNQLITE_OK ){
+        /* Save the value in the cache */
+        CollectionCacheInstallRecord(pCol,nId,pValue);
+    }
+    if( rc != UNQLITE_OK ){
+        unqliteGenErrorFormat(pCol->pVm->pDb,
+                              "IO error while storing record into collection '%z'",
+                              &pCol->sName
+                              );
+        return rc;
+    }
+    return UNQLITE_OK;
+}
+/*
+ * Array walker callback (Refer to jx9_array_walk()).
+ */
+static int CollectionRecordArrayWalker(jx9_value *pKey,jx9_value *pData,void *pUserData)
+{
+       unqlite_col *pCol = (unqlite_col *)pUserData;
+       int rc;
+       /* Perform the insertion */
+       rc = CollectionStore(pCol,pData);
+       if( rc != UNQLITE_OK ){
+               SXUNUSED(pKey); /* cc warning */
+       }
+       return rc;
+}
+/*
+ * Perform a store operation on a given collection.
+ */
+UNQLITE_PRIVATE int unqliteCollectionPut(unqlite_col *pCol,jx9_value *pValue,int iFlag)
+{
+       int rc;
+       if( !jx9_value_is_json_object(pValue) && jx9_value_is_json_array(pValue) ){
+               /* Iterate over the array and store its members in the collection */
+               rc = jx9_array_walk(pValue,CollectionRecordArrayWalker,pCol);
+               SXUNUSED(iFlag); /* cc warning */
+       }else{
+               rc = CollectionStore(pCol,pValue);
+       }
+       return rc;
+}
+/*
+ * Drop a record from a given collection.
+ */
+UNQLITE_PRIVATE int unqliteCollectionDropRecord(
+       unqlite_col *pCol,  /* Target collection */
+       jx9_int64 nId,      /* Unique ID of the record to be droped */
+       int wr_header,      /* True to alter collection header */
+       int log_err         /* True to log error */
+       )
+{
+       SyBlob *pWorker = &pCol->sWorker;
+       int rc;         
+       /* Reset the working buffer */
+       SyBlobReset(pWorker);
+       /* Prepare the unique ID for this record */
+       SyBlobFormat(pWorker,"%z_%qd",&pCol->sName,nId);
+       /* Reset the cursor */
+       unqlite_kv_cursor_reset(pCol->pCursor);
+       /* Seek the cursor to the desired location */
+       rc = unqlite_kv_cursor_seek(pCol->pCursor,
+               SyBlobData(pWorker),SyBlobLength(pWorker),
+               UNQLITE_CURSOR_MATCH_EXACT
+               );
+       if( rc != UNQLITE_OK ){
+               return rc;
+       }
+       /* Remove the record from the storage engine */
+       rc = unqlite_kv_cursor_delete_entry(pCol->pCursor);
+       /* Finally, Remove the record from the cache */
+       unqliteCollectionCacheRemoveRecord(pCol,nId);
+       if( rc == UNQLITE_OK ){
+               pCol->nTotRec--;
+               if( wr_header ){
+                       /* Relect in the collection header */
+                       rc = CollectionSetHeader(0,pCol,-1,pCol->nTotRec,0);
+               }
+       }else if( rc == UNQLITE_NOTIMPLEMENTED ){
+               if( log_err ){
+                       unqliteGenErrorFormat(pCol->pVm->pDb,
+                               "Cannot delete record from collection '%z' due to a read-only Key/Value storage engine",
+                               &pCol->sName
+                               );
+               }
+       }
+       return rc;
+}
+/*
+ * Update a given record with new data
+ */
+UNQLITE_PRIVATE int unqliteCollectionUpdateRecord(unqlite_col *pCol,jx9_int64 nId, jx9_value *pValue,int iFlag)
+{
+    int rc;
+    if( !jx9_value_is_json_object(pValue) && jx9_value_is_json_array(pValue) ){
+        /* Iterate over the array and store its members in the collection */
+        rc = jx9_array_walk(pValue,CollectionRecordArrayWalker,pCol);
+        SXUNUSED(iFlag); /* cc warning */
+    }else{
+        rc = CollectionUpdate(pCol,nId,pValue);
+    }
+    return rc;
+}
+/*
+ * Drop a collection from the KV storage engine and the underlying
+ * unqlite VM.
+ */
+UNQLITE_PRIVATE int unqliteDropCollection(unqlite_col *pCol)
+{
+       unqlite_vm *pVm = pCol->pVm;
+       jx9_int64 nId;
+       int rc;
+       /* Reset the cursor */
+       unqlite_kv_cursor_reset(pCol->pCursor);
+       /* Seek the cursor to the desired location */
+       rc = unqlite_kv_cursor_seek(pCol->pCursor,
+               SyStringData(&pCol->sName),SyStringLength(&pCol->sName),
+               UNQLITE_CURSOR_MATCH_EXACT
+               );
+       if( rc == UNQLITE_OK ){
+               /* Remove the record from the storage engine */
+               rc = unqlite_kv_cursor_delete_entry(pCol->pCursor);
+       }
+       if( rc != UNQLITE_OK ){
+               unqliteGenErrorFormat(pCol->pVm->pDb,
+                               "Cannot remove collection '%z' due to a read-only Key/Value storage engine",
+                               &pCol->sName
+                       );
+               return rc;
+       }
+       /* Drop collection records */
+       for( nId = 0 ; nId < pCol->nLastid ; ++nId ){
+               unqliteCollectionDropRecord(pCol,nId,0,0);
+       }
+       /* Cleanup */
+       CollectionCacheRelease(pCol);
+       SyBlobRelease(&pCol->sHeader);
+       SyBlobRelease(&pCol->sWorker);
+       SyMemBackendFree(&pVm->sAlloc,(void *)SyStringData(&pCol->sName));
+       unqliteReleaseCursor(pVm->pDb,pCol->pCursor);
+       /* Unlink */
+       if( pCol->pPrevCol ){
+               pCol->pPrevCol->pNextCol = pCol->pNextCol;
+       }else{
+               sxu32 iBucket = pCol->nHash & (pVm->iColSize - 1);
+               pVm->apCol[iBucket] = pCol->pNextCol;
+       }
+       if( pCol->pNextCol ){
+               pCol->pNextCol->pPrevCol = pCol->pPrevCol;
+       }
+       MACRO_LD_REMOVE(pVm->pCol,pCol);
+       pVm->iCol--;
+       SyMemBackendPoolFree(&pVm->sAlloc,pCol);
+       return UNQLITE_OK;
+}
+/*
+ * ----------------------------------------------------------
+ * File: unqlite_jx9.c
+ * MD5: 8fddc15b667e85d7b5df5367132518fb
+ * ----------------------------------------------------------
+ */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+ /* $SymiscID: unql_jx9.c v1.2 FreeBSD 2013-01-24 22:45 stable <chm@symisc.net> $ */
+#ifndef UNQLITE_AMALGAMATION
+#include "unqliteInt.h"
+#endif
+/* 
+ * This file implements UnQLite functions (db_exists(), db_create(), db_put(), db_get(), etc.) for the
+ * underlying Jx9 Virtual Machine. 
+ */
+/*
+ * string db_version(void)
+ *   Return the current version of the unQLite database engine.
+ * Parameter
+ *   None
+ * Return
+ *    unQLite version number (string).
+ */
+static int unqliteBuiltin_db_version(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       SXUNUSED(argc); /* cc warning */
+       SXUNUSED(argv);
+       jx9_result_string(pCtx,UNQLITE_VERSION,(int)sizeof(UNQLITE_VERSION)-1);
+       return JX9_OK;
+}
+/*
+ * string db_errlog(void)
+ *   Return the database error log.
+ * Parameter
+ *   None
+ * Return
+ *    Database error log (string).
+ */
+static int unqliteBuiltin_db_errlog(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_vm *pVm;
+       SyBlob *pErr;
+       
+       SXUNUSED(argc); /* cc warning */
+       SXUNUSED(argv);
+
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Point to the error log */
+       pErr = &pVm->pDb->sErr;
+       /* Return the log */
+       jx9_result_string(pCtx,(const char *)SyBlobData(pErr),(int)SyBlobLength(pErr));
+       return JX9_OK;
+}
+/*
+ * string db_copyright(void)
+ * string db_credits(void)
+ *   Return the unQLite database engine copyright notice.
+ * Parameter
+ *   None
+ * Return
+ *    Copyright notice.
+ */
+static int unqliteBuiltin_db_credits(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       SXUNUSED(argc); /* cc warning */
+       SXUNUSED(argv);
+       jx9_result_string(pCtx,UNQLITE_COPYRIGHT,(int)sizeof(UNQLITE_COPYRIGHT)-1);
+       return JX9_OK;
+}
+/*
+ * string db_sig(void)
+ *   Return the unQLite database engine unique signature.
+ * Parameter
+ *   None
+ * Return
+ *    unQLite signature.
+ */
+static int unqliteBuiltin_db_sig(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       SXUNUSED(argc); /* cc warning */
+       SXUNUSED(argv);
+       jx9_result_string(pCtx,UNQLITE_IDENT,sizeof(UNQLITE_IDENT)-1);
+       return JX9_OK;
+}
+/*
+ * bool collection_exists(string $name)
+ * bool db_exits(string $name)
+ *   Check if a given collection exists in the underlying database.
+ * Parameter
+ *   name: Lookup name
+ * Return
+ *    TRUE if the collection exits. FALSE otherwise.
+ */
+static int unqliteBuiltin_collection_exists(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       int rc;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Perform the lookup */
+       rc = unqliteExistsCollection(pVm, &sName);
+       /* Lookup result */
+       jx9_result_bool(pCtx, rc == UNQLITE_OK ? 1 : 0);
+       return JX9_OK;
+}
+/*
+ * bool collection_create(string $name)
+ * bool db_create(string $name)
+ *   Create a new collection.
+ * Parameter
+ *   name: Collection name
+ * Return
+ *    TRUE if the collection was successfuly created. FALSE otherwise.
+ */
+static int unqliteBuiltin_collection_create(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       int rc;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Try to create the collection */
+       rc = unqliteCreateCollection(pVm,&sName);
+       /* Return the result to the caller */
+       jx9_result_bool(pCtx,rc == UNQLITE_OK ? 1 : 0);
+       return JX9_OK;
+}
+/*
+ * value db_fetch(string $col_name)
+ * value db_get(string $col_name)
+ *   Fetch the current record from a given collection and advance
+ *   the record cursor.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    Record content success. NULL on failure (No more records to retrieve).
+ */
+static int unqliteBuiltin_db_fetch_next(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       int rc;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return null */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               /* Fetch the current record */
+               jx9_value *pValue;
+               pValue = jx9_context_new_scalar(pCtx);
+               if( pValue == 0 ){
+                       jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
+                       jx9_result_null(pCtx);
+                       return JX9_OK;
+               }else{
+                       rc = unqliteCollectionFetchNextRecord(pCol,pValue);
+                       if( rc == UNQLITE_OK ){
+                               jx9_result_value(pCtx,pValue);
+                               /* pValue will be automatically released as soon we return from this function */
+                       }else{
+                               /* Return null */
+                               jx9_result_null(pCtx);
+                       }
+               }
+       }else{
+               /* No such collection, return null */
+               jx9_result_null(pCtx);
+       }
+       return JX9_OK;
+}
+/*
+ * value db_fetch_by_id(string $col_name,int64 $record_id)
+ * value db_get_by_id(string $col_name,int64 $record_id)
+ *   Fetch a record using its unique ID from a given collection.
+ * Parameter
+ *   col_name:  Collection name
+ *   record_id: Record number (__id field of a JSON object)
+ * Return
+ *    Record content success. NULL on failure (No such record).
+ */
+static int unqliteBuiltin_db_fetch_by_id(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       jx9_int64 nId;
+       int nByte;
+       int rc;
+       /* Extract collection name */
+       if( argc < 2 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or record ID");
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       /* Extract the record ID */
+       nId = jx9_value_to_int(argv[1]);
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               /* Fetch the desired record */
+               jx9_value *pValue;
+               pValue = jx9_context_new_scalar(pCtx);
+               if( pValue == 0 ){
+                       jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
+                       jx9_result_null(pCtx);
+                       return JX9_OK;
+               }else{
+                       rc = unqliteCollectionFetchRecordById(pCol,nId,pValue);
+                       if( rc == UNQLITE_OK ){
+                               jx9_result_value(pCtx,pValue);
+                               /* pValue will be automatically released as soon we return from this function */
+                       }else{
+                               /* No such record, return null */
+                               jx9_result_null(pCtx);
+                       }
+               }
+       }else{
+               /* No such collection, return null */
+               jx9_result_null(pCtx);
+       }
+       return JX9_OK;
+}
+/*
+ * array db_fetch_all(string $col_name,[callback filter_callback])
+ * array db_get_all(string $col_name,[callback filter_callback])
+ *   Retrieve all records of a given collection and apply the given
+ *   callback if available to filter records.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    Contents of the collection (JSON array) on success. NULL on failure.
+ */
+static int unqliteBuiltin_db_fetch_all(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       int rc;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return NULL */
+               jx9_result_null(pCtx);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               jx9_value *pValue,*pArray,*pCallback = 0;
+               jx9_value sResult; /* Callback result */
+               /* Allocate an empty scalar value and an empty JSON array */
+               pArray = jx9_context_new_array(pCtx);
+               pValue = jx9_context_new_scalar(pCtx);
+               jx9MemObjInit(pCtx->pVm,&sResult);
+               if( pValue == 0 || pArray == 0 ){
+                       jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Jx9 is running out of memory");
+                       jx9_result_null(pCtx);
+                       return JX9_OK;
+               }
+               if( argc > 1 && jx9_value_is_callable(argv[1]) ){
+                       pCallback = argv[1];
+               }
+               unqliteCollectionResetRecordCursor(pCol);
+               /* Fetch collection records one after one */
+               while( UNQLITE_OK == unqliteCollectionFetchNextRecord(pCol,pValue) ){
+                       if( pCallback ){
+                               jx9_value *apArg[2];
+                               /* Invoke the filter callback */
+                               apArg[0] = pValue;
+                               rc = jx9VmCallUserFunction(pCtx->pVm,pCallback,1,apArg,&sResult);
+                               if( rc == JX9_OK ){
+                                       int iResult; /* Callback result */
+                                       /* Extract callback result */
+                                       iResult = jx9_value_to_bool(&sResult);
+                                       if( !iResult ){
+                                               /* Discard the result */
+                                               unqliteCollectionCacheRemoveRecord(pCol,unqliteCollectionCurrentRecordId(pCol) - 1);
+                                               continue;
+                                       }
+                               }
+                       }
+                       /* Put the value in the JSON array */
+                       jx9_array_add_elem(pArray,0,pValue);
+                       /* Release the value */
+                       jx9_value_null(pValue);
+               }
+               jx9MemObjRelease(&sResult);
+               /* Finally, return our array */
+               jx9_result_value(pCtx,pArray);
+               /* pValue will be automatically released as soon we return from
+                * this foreign function.
+                */
+       }else{
+               /* No such collection, return null */
+               jx9_result_null(pCtx);
+       }
+       return JX9_OK;
+}
+/*
+ * int64 db_last_record_id(string $col_name)
+ *   Return the ID of the last inserted record.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    Record ID (64-bit integer) on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_last_record_id(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               jx9_result_int64(pCtx,unqliteCollectionLastRecordId(pCol));
+       }else{
+               /* No such collection, return FALSE */
+               jx9_result_bool(pCtx,0);
+       }
+       return JX9_OK;
+}
+/*
+ * inr64 db_current_record_id(string $col_name)
+ *   Return the current record ID.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    Current record ID (64-bit integer) on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_current_record_id(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               jx9_result_int64(pCtx,unqliteCollectionCurrentRecordId(pCol));
+       }else{
+               /* No such collection, return FALSE */
+               jx9_result_bool(pCtx,0);
+       }
+       return JX9_OK;
+}
+/*
+ * bool db_reset_record_cursor(string $col_name)
+ *   Reset the record ID cursor.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    TRUE on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_reset_record_cursor(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               unqliteCollectionResetRecordCursor(pCol);
+               jx9_result_bool(pCtx,1);
+       }else{
+               /* No such collection */
+               jx9_result_bool(pCtx,0);
+       }
+       return JX9_OK;
+}
+/*
+ * int64 db_total_records(string $col_name)
+ *   Return the total number of inserted records in the given collection.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    Total number of records on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_total_records(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               unqlite_int64 nRec;
+               nRec = unqliteCollectionTotalRecords(pCol);
+               jx9_result_int64(pCtx,nRec);
+       }else{
+               /* No such collection */
+               jx9_result_bool(pCtx,0);
+       }
+       return JX9_OK;
+}
+/*
+ * string db_creation_date(string $col_name)
+ *   Return the creation date of the given collection.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    Creation date on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_creation_date(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               Sytm *pTm = &pCol->sCreation;
+               jx9_result_string_format(pCtx,"%d-%d-%d %02d:%02d:%02d",
+                       pTm->tm_year,pTm->tm_mon,pTm->tm_mday,
+                       pTm->tm_hour,pTm->tm_min,pTm->tm_sec
+                       );
+       }else{
+               /* No such collection */
+               jx9_result_bool(pCtx,0);
+       }
+       return JX9_OK;
+}
+/*
+ * bool db_store(string $col_name,...)
+ * bool db_put(string $col_name,...)
+ *   Store one or more JSON values in a given collection.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    TRUE on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_store(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       int rc;
+       int i;
+       /* Extract collection name */
+       if( argc < 2 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol == 0 ){
+               jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       /* Store the given values */
+       for( i = 1 ; i < argc ; ++i ){
+               rc = unqliteCollectionPut(pCol,argv[i],0);
+               if( rc != UNQLITE_OK){
+                       jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,
+                               "Error while storing record %d in collection '%z'",i,&sName
+                               );
+                       /* Return false */
+                       jx9_result_bool(pCtx,0);
+                       return JX9_OK;
+               }
+       }
+       /* All done, return TRUE */
+       jx9_result_bool(pCtx,1);
+       return JX9_OK;
+}
+
+/*
+ * bool db_update_record(string $col_name, int_64 record_id, object $json_object)
+ *   Update a given record with new json object
+ * Parameter
+ * col_name: Collection name
+ *   record_id: ID of the record
+ *   json_object: New Record data
+ * Return
+ *   TRUE on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_update_record(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+    unqlite_col *pCol;
+    const char *zName;
+    unqlite_vm *pVm;
+    SyString sName;
+    jx9_int64 nId;
+    int nByte;
+    int rc;
+    /* Extract collection name */
+    if( argc < 2 ){
+        /* Missing arguments */
+        jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
+        /* Return false */
+        jx9_result_bool(pCtx,0);
+        return JX9_OK;
+    }
+    zName = jx9_value_to_string(argv[0],&nByte);
+    if( nByte < 1){
+        jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+        /* Return false */
+        jx9_result_bool(pCtx,0);
+        return JX9_OK;
+    }
+    SyStringInitFromBuf(&sName,zName,nByte);
+    pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+    /* Fetch the collection */
+    pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+    if( pCol == 0 ){
+        jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
+        /* Return false */
+        jx9_result_bool(pCtx,0);
+        return JX9_OK;
+    }
+    /* Update a record with the given value */
+    nId = jx9_value_to_int64(argv[1]);
+    rc = unqliteCollectionUpdateRecord(pCol, nId, argv[2], 0);
+    /* All done, return TRUE */
+    jx9_result_bool(pCtx,rc == UNQLITE_OK);
+    return JX9_OK;
+}
+
+/*
+ * bool db_drop_collection(string $col_name)
+ * bool collection_delete(string $col_name)
+ *   Remove a given collection from the database.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    TRUE on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_drop_col(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       int rc;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol == 0 ){
+               jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       /* Drop the collection */
+       rc = unqliteDropCollection(pCol);
+       /* Processing result */
+       jx9_result_bool(pCtx,rc == UNQLITE_OK);
+       return JX9_OK;
+}
+/*
+ * bool db_drop_record(string $col_name,int64 record_id)
+ *   Remove a given record from a collection.
+ * Parameter
+ *   col_name: Collection name.
+ *   record_id: ID of the record.
+ * Return
+ *    TRUE on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_drop_record(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       jx9_int64 nId;
+       int nByte;
+       int rc;
+       /* Extract collection name */
+       if( argc < 2 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or records");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol == 0 ){
+               jx9_context_throw_error_format(pCtx,JX9_CTX_ERR,"No such collection '%z'",&sName);
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       /* Extract the record ID */
+       nId = jx9_value_to_int64(argv[1]);
+       /* Drop the record */
+       rc = unqliteCollectionDropRecord(pCol,nId,1,1);
+       /* Processing result */
+       jx9_result_bool(pCtx,rc == UNQLITE_OK);
+       return JX9_OK;
+}
+/*
+ * bool db_set_schema(string $col_name, object $json_object)
+ *   Set a schema for a given collection.
+ * Parameter
+ *   col_name: Collection name.
+ *   json_object: Collection schema (Must be a JSON object).
+ * Return
+ *    TRUE on success. FALSE on failure.
+ */
+static int unqliteBuiltin_db_set_schema(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       int rc;
+       /* Extract collection name */
+       if( argc < 2 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       if( !jx9_value_is_json_object(argv[1]) ){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection scheme");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       rc = UNQLITE_NOOP;
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               /* Set the collection scheme */
+               rc = unqliteCollectionSetSchema(pCol,argv[1]);
+       }else{
+               jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING,
+                       "No such collection '%z'",
+                       &sName
+                       );
+       }
+       /* Processing result */
+       jx9_result_bool(pCtx,rc == UNQLITE_OK);
+       return JX9_OK;
+}
+/*
+ * object db_get_schema(string $col_name)
+ *   Return the schema associated with a given collection.
+ * Parameter
+ *   col_name: Collection name
+ * Return
+ *    Collection schema on success. null otherwise.
+ */
+static int unqliteBuiltin_db_get_schema(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_col *pCol;
+       const char *zName;
+       unqlite_vm *pVm;
+       SyString sName;
+       int nByte;
+       /* Extract collection name */
+       if( argc < 1 ){
+               /* Missing arguments */
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Missing collection name and/or db scheme");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       zName = jx9_value_to_string(argv[0],&nByte);
+       if( nByte < 1){
+               jx9_context_throw_error(pCtx,JX9_CTX_ERR,"Invalid collection name");
+               /* Return false */
+               jx9_result_bool(pCtx,0);
+               return JX9_OK;
+       }
+       SyStringInitFromBuf(&sName,zName,nByte);
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Fetch the collection */
+       pCol = unqliteCollectionFetch(pVm,&sName,UNQLITE_VM_AUTO_LOAD);
+       if( pCol ){
+               /* Return the collection schema */
+               jx9_result_value(pCtx,&pCol->sSchema);
+       }else{
+               jx9_context_throw_error_format(pCtx,JX9_CTX_WARNING,
+                       "No such collection '%z'",
+                       &sName
+                       );
+               jx9_result_null(pCtx);
+       }
+       return JX9_OK;
+}
+/*
+ * bool db_begin(void)
+ *   Manually begin a write transaction.
+ * Parameter
+ *   None
+ * Return
+ *    TRUE on success. FALSE otherwise.
+ */
+static int unqliteBuiltin_db_begin(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_vm *pVm;
+       unqlite *pDb;
+       int rc;
+       SXUNUSED(argc); /* cc warning */
+       SXUNUSED(argv);
+       /* Point to the unqlite Vm */
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Point to the underlying database handle  */
+       pDb = pVm->pDb;
+       /* Begin the transaction */
+       rc = unqlitePagerBegin(pDb->sDB.pPager);
+       /* result */
+       jx9_result_bool(pCtx,rc == UNQLITE_OK );
+       return JX9_OK;
+}
+/*
+ * bool db_commit(void)
+ *   Manually commit a transaction.
+ * Parameter
+ *   None
+ * Return
+ *    TRUE if the transaction was successfuly commited. FALSE otherwise.
+ */
+static int unqliteBuiltin_db_commit(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_vm *pVm;
+       unqlite *pDb;
+       int rc;
+       SXUNUSED(argc); /* cc warning */
+       SXUNUSED(argv);
+       /* Point to the unqlite Vm */
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Point to the underlying database handle  */
+       pDb = pVm->pDb;
+       /* Commit the transaction if any */
+       rc = unqlitePagerCommit(pDb->sDB.pPager);
+       /* Commit result */
+       jx9_result_bool(pCtx,rc == UNQLITE_OK );
+       return JX9_OK;
+}
+/*
+ * bool db_rollback(void)
+ *   Manually rollback a transaction.
+ * Parameter
+ *   None
+ * Return
+ *    TRUE if the transaction was successfuly rolled back. FALSE otherwise
+ */
+static int unqliteBuiltin_db_rollback(jx9_context *pCtx,int argc,jx9_value **argv)
+{
+       unqlite_vm *pVm;
+       unqlite *pDb;
+       int rc;
+       SXUNUSED(argc); /* cc warning */
+       SXUNUSED(argv);
+       /* Point to the unqlite Vm */
+       pVm = (unqlite_vm *)jx9_context_user_data(pCtx);
+       /* Point to the underlying database handle  */
+       pDb = pVm->pDb;
+       /* Rollback the transaction if any */
+       rc = unqlitePagerRollback(pDb->sDB.pPager,TRUE);
+       /* Rollback result */
+       jx9_result_bool(pCtx,rc == UNQLITE_OK );
+       return JX9_OK;
+}
+/*
+ * Register all the UnQLite foreign functions defined above.
+ */
+UNQLITE_PRIVATE int unqliteRegisterJx9Functions(unqlite_vm *pVm)
+{
+       static const jx9_builtin_func aBuiltin[] = {
+               { "db_version" , unqliteBuiltin_db_version },
+               { "db_copyright", unqliteBuiltin_db_credits },
+               { "db_credits" , unqliteBuiltin_db_credits },
+               { "db_sig" ,     unqliteBuiltin_db_sig     },
+               { "db_errlog",   unqliteBuiltin_db_errlog  },
+               { "collection_exists", unqliteBuiltin_collection_exists },
+               { "db_exists",         unqliteBuiltin_collection_exists }, 
+               { "collection_create", unqliteBuiltin_collection_create },
+               { "db_create",         unqliteBuiltin_collection_create },
+               { "db_fetch",          unqliteBuiltin_db_fetch_next     },
+               { "db_get",            unqliteBuiltin_db_fetch_next     },
+               { "db_fetch_by_id",    unqliteBuiltin_db_fetch_by_id    },
+               { "db_get_by_id",      unqliteBuiltin_db_fetch_by_id    },
+               { "db_fetch_all",      unqliteBuiltin_db_fetch_all      },
+               { "db_get_all",        unqliteBuiltin_db_fetch_all      },
+               { "db_last_record_id", unqliteBuiltin_db_last_record_id },
+               { "db_current_record_id", unqliteBuiltin_db_current_record_id },
+               { "db_reset_record_cursor", unqliteBuiltin_db_reset_record_cursor },
+               { "db_total_records",  unqliteBuiltin_db_total_records  },
+               { "db_creation_date",  unqliteBuiltin_db_creation_date  },
+               { "db_store",          unqliteBuiltin_db_store          },
+        { "db_update_record",  unqliteBuiltin_db_update_record  },
+               { "db_put",            unqliteBuiltin_db_store          },
+               { "db_drop_collection", unqliteBuiltin_db_drop_col      },
+               { "collection_delete", unqliteBuiltin_db_drop_col       },
+               { "db_drop_record",    unqliteBuiltin_db_drop_record    },
+               { "db_set_schema",     unqliteBuiltin_db_set_schema     },
+               { "db_get_schema",     unqliteBuiltin_db_get_schema     },
+               { "db_begin",          unqliteBuiltin_db_begin          },
+               { "db_commit",         unqliteBuiltin_db_commit         },
+               { "db_rollback",       unqliteBuiltin_db_rollback       },
+       };
+       int rc = UNQLITE_OK;
+       sxu32 n;
+       /* Register the unQLite functions defined above in the Jx9 call table */
+       for( n = 0 ; n < SX_ARRAYSIZE(aBuiltin) ; ++n ){
+               rc = jx9_create_function(pVm->pJx9Vm,aBuiltin[n].zName,aBuiltin[n].xFunc,pVm);
+       }
+       return rc;
+}
+/* END-OF-IMPLEMENTATION: unqlite@embedded@symisc 34-09-46 */
+/*
+ * Symisc unQLite: An Embeddable NoSQL (Post Modern) Database Engine.
+ * Copyright (C) 2012-2013, Symisc Systems http://unqlite.org/
+ * Version 1.1.6
+ * 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://unqlite.org/licensing.html
+ */
+/*
+ * Copyright (C) 2012, 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine <chm@symisc.net>].
+ * 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.
+ *
+ * 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.
+ */
+
+#pragma GCC diagnostic pop